#!/usr/bin/perl
# 改行コードに注意,LFにすること
use strict;
my $cfg = '/home/witchymail/wm_current/etc/wms.conf'; # location of configuration file
use lib('/home/witchymail/wm_current/lib');
use lib('/home/witchymail/wm_current/cpan');
use Jcode;
use Net::LDAP;
use WMS::SysConf;
our $s = WMS::SysConf->new();
use WMS::Ctrl;
our $d = WMS::Ctrl->new();
use Text::ParseWords;
use WMS::Crypt;
use WMS::Lock;
use utf8;

my $return_cd = "normal";

# openLog
unless( $d->openLog('aplog') ){
	# goto ERROR;	
}
# set config parameters
unless( $s->set_Config($cfg) ){
	goto ERROR;
}

my $param;
my @in = <STDIN>;
foreach (@in) {
	$_ =~ s/\n//go;
	my ($key, $value) = split(/=/,$_,2);
	$param->{$key} = $value;
	##$d->APLog("info","debug $key $value");
}
my $target = $param->{target};
my $keyword = $param->{keyword};
my $size_limit = $param->{size_limit};
my $user_id = $param->{user_id};

# arg check
unless( $target and defined($keyword) and $size_limit ){
	$return_cd = "csacmn00";
	$d->APLog("info","Argument error. target=$target, keyword=$keyword, size_limit=$size_limit");
	goto ERROR;
}
if ( ($size_limit < $WMS::SysConf::CFG->{shared_address_search_min}) && ($WMS::SysConf::CFG->{shared_address_search_max} < $size_limit) ) {
	$return_cd = "csacmn01";
	$d->APLog("info","Argument error. size_limit=$size_limit");
	goto ERROR;
}
# Zspace → space
utf8::decode( $keyword );
$keyword =~ s/^[\x{0020}\x{3000}]+//g; # ユニコード「U+0020」は半角スベース、ユニコード「U+3000」は全角スベース。
$keyword =~ s/[\x{0020}\x{3000}]+/ /g; # ユニコード「U+0020」は半角スベース、ユニコード「U+3000」は全角スベース。
utf8::encode( $keyword );
if ($keyword eq "") {
	$return_cd = "csacmn02";
	$d->APLog("info","Argument error. keyword is undefined." );
	goto ERROR;
}

# search from csv
my $result_csv;
my $count_csv = 0;
my $over_size_limit_flg_c = "false";
my $lock = WMS::Lock->new;
$lock->set_user_id( $user_id );
$lock->set_lock_file( $WMS::SysConf::CFG->{shared_address_lock} );
if ( -e "$WMS::SysConf::CFG->{shared_addressbook_dir}/$WMS::SysConf::CFG->{shared_addressbook_file}" ) {
	#$d->APLog( 'info', "Start Search from CSV");
	# read lock
	unless( $lock->read_lock ) {
		$return_cd = $lock->get_return_cd;
		$d->APLog( 'info', $lock->get_return_msg );
		goto ERROR;
	}

	($result_csv, $count_csv, $over_size_limit_flg_c, $return_cd) = &search_address_CSV($target, $keyword, $size_limit);

	# unlock
	if( $lock ){ $lock->unlock; }

	unless ($return_cd eq 'normal') {
		$d->APLog("info","Fail search shared addressbook(CSV). target=$target, keyword=$keyword, size_limit=$size_limit");
		goto ERROR;
	}
} else {
	%$result_csv = ();
}
#$d->APLog("info","debug CSV count:$count_csv overSizeLimit:$over_size_limit_flg_c $return_cd");
$d->APLog("info","CSV count:$count_csv overSizeLimit:$over_size_limit_flg_c");

# search from LDAP
# licence check
use WMS::License;
my $l = WMS::License->new;
unless ($l->decrypt_licensekey($WMS::SysConf::CFG->{license_key})) {
	$return_cd = "csacmn03";
	$d->APLog("info", "invalid licence key.");
	goto ERROR;
}
my $result_ldap;
my $count_ldap = 0;
my $over_size_limit_flg_l = "false";
if ($WMS::SysConf::CFG->{'licence_option_ldap'} eq "on" and $WMS::SysConf::CFG->{ldap_address_used} eq "true") {
	#$d->APLog( 'info', "Start Search from LDAP");
	($result_ldap, $count_ldap, $over_size_limit_flg_l, $return_cd) = &search_address_LDAP($target, $keyword, $size_limit);
	unless ($return_cd eq 'normal') {
		$d->APLog("info","Fail search shared addressbook(LDAP). target=$target, keyword=$keyword, size_limit=$size_limit");
		goto ERROR;
	}
} else {
	%$result_ldap = ();
}
#$d->APLog("info","debug LDAP count:$count_ldap overSizeLimit:$over_size_limit_flg_l $return_cd");
$d->APLog("info","LDAP count:$count_ldap overSizeLimit:$over_size_limit_flg_l");

# merge & sort & print
my $Share_list;
my $count_share = 0;
my $over_size_limit_flg = "false";
if ( ($over_size_limit_flg_c eq "true") || ($over_size_limit_flg_l eq "true") ) {
	$over_size_limit_flg = "true";
}
%$Share_list = (%$result_csv, %$result_ldap);
$count_share = $count_csv + $count_ldap;
if ($count_share > $size_limit) {
	$count_share = $size_limit;
	$over_size_limit_flg = "true";
}
#$d->APLog("info","debug CSV&LDAP count:$count_share overSizeLimit:$over_size_limit_flg $return_cd");
$d->APLog("info","CSV&LDAP count:$count_share overSizeLimit:$over_size_limit_flg");
print << "EOS";
status=success
return_cd=$return_cd
count=$count_share
over_size_limit=$over_size_limit_flg
EOS

#昇順で最大sizelimit件のデータを取得し、降順で出力する(クライアントはidの大きいものから表示するので)
my $id = 0;
my $count_array = $count_csv + $count_ldap;
foreach (sort {$Share_list->{$b}[1] cmp $Share_list->{$a}[1]} keys %$Share_list) {
	if ($count_array > $size_limit) {
		$count_array--;
		#$d->APLog("info","debug skip $_ name=$Share_list->{$_}[1]\nmail=$Share_list->{$_}[2]\ngroup=$Share_list->{$_}[3]");
		next;
	}
	#$d->APLog("info","debug $_ name$id=$Share_list->{$_}[1]\nmail$id=$Share_list->{$_}[2]\ngroup$id=$Share_list->{$_}[3]");
	print "name$id=$Share_list->{$_}[1]\nmail$id=$Share_list->{$_}[2]\ngroup$id=$Share_list->{$_}[3]\n";
	$id++;
}
$d->closeLog();
exit;

ERROR:
#$d->APLog("info","debug $return_cd");
# unlock
if( $lock ){ $lock->unlock; }
print << "EOS";
status=error
return_cd=$return_cd
count=0
over_sizelimit=false
EOS

$d->closeLog();
exit;

sub search_address_CSV
{
	my $target = shift;
	my $keyword = shift;
	my $size_limit = shift;
	my $read_list;
	my $read_list_tmp;
	my $count_csv = 0;
	my $over_size_limit_flg = "false";
	my $return_cd = "normal";
	# arg check
	unless( $target and defined($keyword) and $size_limit ){
		$return_cd = "csacsv00";
		$d->APLog("info","Argument error. target=$target, keyword=$keyword, size_limit=$size_limit");
		goto ERROR;
	}

	#file open
	unless( open( FH, "< $WMS::SysConf::CFG->{shared_addressbook_dir}/$WMS::SysConf::CFG->{shared_addressbook_file}" ) ){
		$return_cd = "csacsv01";
		$d->APLog("info","Cannot open. $!. file=$WMS::SysConf::CFG->{shared_addressbook_dir}/$WMS::SysConf::CFG->{shared_addressbook_file}");
		goto ERROR;
	}

	#split keyword
	my @list_keyword;
	my $count_keyword = 0;
	foreach ( split(/\s/,$keyword) ) {
		$list_keyword[$count_keyword] = $_;
		$list_keyword[$count_keyword] =~ s/([\\|()[{^\$\*+?.\/@])/\\$1/g;	# escape regular expression	
		$count_keyword++;
		##$d->APLog("info","debug csv $_");
	}
	
	# read data.
	my $id = 0;
	my $cid;
	foreach( <FH> ){
		chomp $_;
		my( $address_id, $res1, $res2, $res3, $res4, $res5, $c_line ) = quotewords( ",", 0, $_ );
		$c_line = pack( "H*", $c_line );
		my $crypt= WMS::Crypt->new();
		$c_line = $crypt->Decrypt( $c_line );
		my( $name, $address, $group ) = quotewords( ",", 0, $c_line );
		
		my $count = 0;
		#$d->APLog("info","debug csv $name $address $group");
		for (my $j = 0; $j < $count_keyword; $j++) {
			if ($target eq "name") {
				if ($name !~ /$list_keyword[$j]/i) {
					last;
				}
			} elsif ($target eq "address") {
				if ($address !~ /$list_keyword[$j]/i) {
					last;
				}
			} elsif ($target eq "group") {
				if ($group !~ /$list_keyword[$j]/i) {
					last;
				}
			} elsif ($target eq "all") {
				if ( ($name !~ /$list_keyword[$j]/i) & ($address !~ /$list_keyword[$j]/i) & ($group !~ /$list_keyword[$j]/i) ) {
					last;
				}
			} else {
				$return_cd = "csacsv02";
				$d->APLog("info","Argument error. target=$target, keyword=$keyword, size_limit=$size_limit");
				goto ERROR;
			}
			$count++;
		}
		if ( $count == $count_keyword ) {
			$cid = "tmp_".$id;
			$read_list_tmp->{$cid}[0] = $id;
			$read_list_tmp->{$cid}[1] = $name;
			$read_list_tmp->{$cid}[2] = $address;
			$read_list_tmp->{$cid}[3] = $group;
			$id++;
			#$d->APLog("info","debug csv HIT$cid $read_list_tmp->{$cid}[1],$read_list_tmp->{$cid}[2],$read_list_tmp->{$cid}[3]");
		}
		
	}
	
	# sort
	$id = 0;
	foreach (sort {$read_list_tmp->{$a}[1] cmp $read_list_tmp->{$b}[1]} keys %$read_list_tmp) {
		$cid = "csv_".$id;
		$read_list->{$cid}[0] = $id;
		$read_list->{$cid}[1] = $read_list_tmp->{$_}[1];
		$read_list->{$cid}[2] = $read_list_tmp->{$_}[2];
		$read_list->{$cid}[3] = $read_list_tmp->{$_}[3];
		$id++;
		#$d->APLog("info","debug csv $cid $read_list->{$cid}[1],$read_list->{$cid}[2],$read_list->{$cid}[3]");
		if ( $id > $size_limit ) {
			$over_size_limit_flg = "true";
			last;
		}
	}
	$count_csv = $id;
	
	# file close.
	unless( close( FH ) ){
		$return_cd = "csacsv03";
		$d->APLog("info","Cannot close. $!. file=$WMS::SysConf::CFG->{shared_addressbook_file}");
		goto ERROR;
	}

	# end.
	unless ($count_csv) {
		%$read_list = ();
	}
	$d->APLog("info","Read address file end. $WMS::SysConf::CFG->{shasred_addressbook_file}");
	$return_cd = "normal";
	return( $read_list, $count_csv, $over_size_limit_flg, $return_cd );

ERROR:
#$d->APLog("info","debug $return_cd");
	return( undef, 0, "false", $return_cd );
}

sub search_address_LDAP
{
	my $target = shift;
	my $keyword = shift;
	my $size_limit = shift;
	my $LDAP_list;
	my $count_ldap = 0;
	my $over_size_limit_flg = "false";
	my $over_server_size_limit_flg = "false";
	my $return_cd = "normal";
	
	# arg check
	unless( $target and defined($keyword) and $size_limit ){
		$return_cd = "csaldp00";
		$d->APLog("info","Argument error. target=$target, keyword=$keyword, size_limit=$size_limit");
		goto ERROR;
	}

	# set filter
	my $filter;
	my $count_keyword = 0;
	my @list_keyword_utf8;
	my @list_attr = ($WMS::SysConf::CFG->{ldap_address_name}, $WMS::SysConf::CFG->{ldap_address_mail}, $WMS::SysConf::CFG->{ldap_address_group});
	my $num_attr = @list_attr;
	foreach ( split(/\s/,$keyword) ) {
		#$d->APLog("info","debug ldap $_");
		# 20060817 エスケープ処理追加　小石川
		$list_keyword_utf8[$count_keyword] = &escape_LDAP( $_ );
		$count_keyword++;
	}
	if ($count_keyword > $WMS::SysConf::CFG->{ldap_address_maxnum_keyword} ) {
		$return_cd = "csaldp08";
		$d->APLog("info","Argument error(too many Keywords). target=$target, keyword=$keyword, size_limit=$size_limit");
		goto ERROR;
	}
	if ($count_keyword == 1) {
		if ( $target eq "name" ) {
			$filter = "$WMS::SysConf::CFG->{ldap_address_name}=*$list_keyword_utf8[0]*";
		} elsif ( $target eq "address" ) {
			$filter = "$WMS::SysConf::CFG->{ldap_address_mail}=*$list_keyword_utf8[0]*";
		} elsif ( $target eq "group" ) {
			$filter = "$WMS::SysConf::CFG->{ldap_address_group}=*$list_keyword_utf8[0]*";
		} elsif ( $target eq "all") {
			$filter = "(|";
			$filter .= " ($WMS::SysConf::CFG->{ldap_address_name}=*$list_keyword_utf8[0]*) ";
			$filter .= " ($WMS::SysConf::CFG->{ldap_address_mail}=*$list_keyword_utf8[0]*) ";
			$filter .= " ($WMS::SysConf::CFG->{ldap_address_group}=*$list_keyword_utf8[0]*) ";
			$filter .= ")";
		} else {
			$return_cd = "csaldp01";
			$d->APLog("info","Argument error. target=$target, keyword=$keyword, size_limit=$size_limit");
			goto ERROR;
		}
	} else {
	# multi keyword
		if( $target eq "name" ) {
			$filter = "(&";
			for (my $i = 0; $i < $count_keyword; $i++) {
				$filter .= "($WMS::SysConf::CFG->{ldap_address_name}=*$list_keyword_utf8[$i]*)";
			}
			$filter .= ")";
		} elsif ( $target eq "address" ) {
			$filter = "(&";
			for (my $i = 0; $i < $count_keyword; $i++) {
				$filter .= "($WMS::SysConf::CFG->{ldap_address_mail}=*$list_keyword_utf8[$i]*)";
			}
			$filter .= ")";
		} elsif ( $target eq "group" ) {
			$filter = "(&";
			for (my $i = 0; $i < $count_keyword; $i++) {
				$filter .= "($WMS::SysConf::CFG->{ldap_address_group}=*$list_keyword_utf8[$i]*)";
			}
			$filter .= ")";
		} elsif ( $target eq "all") {
			$filter = "(|";
			for (my $i = 0; $i < $num_attr; $i++) {
				my $str_filter1 = "($list_attr[$i]=*$list_keyword_utf8[0]*)";
				for (my $j = 0; $j < $num_attr; $j++) {
					my $str_filter2 = "($list_attr[$j]=*$list_keyword_utf8[1]*)";
					$filter .= "(&$str_filter1$str_filter2)";
				}
			}
			$filter .= ")";
			my $pos_keyword = 2;
			while ($pos_keyword < $count_keyword) {
				my $tmp_filter = $filter;
				$filter =~ s/^\(\|//;
				$filter =~ s/\)$//;
				my @tmp_filter;
				for (my $i = 0; $i < $num_attr; $i++) {
					$tmp_filter[$i] = $filter;
					my $str_filter = "($list_attr[$i]=*$list_keyword_utf8[$pos_keyword]*)";
					$tmp_filter[$i] =~ s/\)\)/\)$str_filter\)/g;	
		
				}
				$filter = "(|";
				for (my $i = 0; $i < $num_attr; $i++) {
					$filter .= $tmp_filter[$i];
				}
				$filter .= ")";
				$pos_keyword++;
			}
		} else {
			$return_cd = "csaldp02";
			$d->APLog("info","Argument error. target=$target, keyword=$keyword, size_limit=$size_limit");
			goto ERROR;
		}
	}

	# connect
	my $ldap;
	unless ( $ldap = Net::LDAP->new( $WMS::SysConf::CFG->{ldap_address_server},
									 port=>$WMS::SysConf::CFG->{ldap_address_port},
									 onerror=>"warn",
								   ) ) {
		$return_cd = "csaldp03";
		$d->APLog("info","LDAP Connect error. ldap_server=$WMS::SysConf::CFG->{ldap_address_server}, ldap_port=$WMS::SysConf::CFG->{ldap_address_port}");
		goto ERROR;
	}

	
	# bind
	my $bind;
	if( $WMS::SysConf::CFG->{ldap_address_dn} ){
		$bind = $ldap->bind( $WMS::SysConf::CFG->{ldap_address_dn},
				             password => $WMS::SysConf::CFG->{ldap_address_pw},
				           );
	} else {
		$bind = $ldap->bind();
	}
	if ($bind->code()) {
		$return_cd = "csaldp04";
		$d->APLog("info","LDAP bind error. ldap_server=$WMS::SysConf::CFG->{ldap_address_dn}");
		goto ERROR;
	}

	# search
	my $result_utf8;
	#onerror=>"warn"の場合 正常な場合：$result_utf8->code()=0、$result_utf8->error()=Success
	$result_utf8 = $ldap->search(
						base      => $WMS::SysConf::CFG->{ldap_address_basedn},
						scope     => $WMS::SysConf::CFG->{ldap_address_scope},
						filter    => $filter,
						attrs     => [ "$WMS::SysConf::CFG->{ldap_address_name}", "$WMS::SysConf::CFG->{ldap_address_mail}", "$WMS::SysConf::CFG->{ldap_address_group}" ],
						timelimit => $WMS::SysConf::CFG->{ldap_address_timelimit},
					);
	if ($result_utf8->code()) {
		if ($result_utf8->error() eq "Sizelimit exceeded") {
			$d->APLog("info","Sizelimit exceeded(LDAP Search).");
			$over_server_size_limit_flg = "true";
		} elsif ($result_utf8->error() eq "Timelimit exceeded") {
			$return_cd = "csaldp07";
			$d->APLog("info","LDAP Search error(Timelimit exceeded).base=\"$WMS::SysConf::CFG->{ldap_address_basedn}\", scope=$WMS::SysConf::CFG->{ldap_address_scope}.");
			# unbind
			$ldap->unbind();
			goto ERROR;			
		} else {
			$return_cd = "csaldp05";
			$d->APLog("info","LDAP Search error.base=\"$WMS::SysConf::CFG->{ldap_address_basedn}\", scope=$WMS::SysConf::CFG->{ldap_address_scope}.");			# unbind
			$ldap->unbind();
			goto ERROR;
		}
	}

	if ($over_server_size_limit_flg eq "true") {
		$over_size_limit_flg = "true";
	}
	
	# sort
	my @list_result_sorted = $result_utf8->sorted("$WMS::SysConf::CFG->{ldap_address_name}");
	
	my $lid;
	my $result_count = 0;
	$d->APLog("info",$result_utf8->count());
	$result_count = $result_utf8->count() || 0;
	if ($size_limit < $result_count) {
		$count_ldap = $size_limit;
		$over_size_limit_flg = "true";
	} else {
		$count_ldap = $result_count;
	}
	for ( my $i = 0 ; $i < $count_ldap ; $i++ ) {
		my $entry_utf8 = $list_result_sorted[$i];
		$lid = "ldap_".$i;
		$LDAP_list->{$lid}[0] = $i;
		my $name_utf8 = $entry_utf8->get_value( $WMS::SysConf::CFG->{ldap_address_name});
		my $mail_utf8 = $entry_utf8->get_value( $WMS::SysConf::CFG->{ldap_address_mail});
		my $group_utf8 = $entry_utf8->get_value( $WMS::SysConf::CFG->{ldap_address_group});

		$LDAP_list->{$lid}[1] = $name_utf8;
		$LDAP_list->{$lid}[2] = $mail_utf8;
		$LDAP_list->{$lid}[3] = $group_utf8;
		#$d->APLog("info","debug ldap hit$lid $LDAP_list->{$lid}[1],$LDAP_list->{$lid}[2],$LDAP_list->{$lid}[3]");
	}


	# end
	unless ($count_ldap) {
		%$LDAP_list = ();
	}
	# unbind
	$ldap->unbind();
	$return_cd = "normal";
	return( $LDAP_list, $count_ldap, $over_size_limit_flg, $return_cd );

ERROR:
#$d->APLog("info","debug $return_cd");
	return( undef, 0, "false", ,$return_cd );
}

sub escape_LDAP
{
	my $str_filter = shift;
	
	# escape
	$str_filter =~ s/\\/\\\\/go;
	$str_filter =~ s/\*/\\\*/go;
	$str_filter =~ s/\(/\\\(/go;
	$str_filter =~ s/\)/\\\)/go;
	$str_filter =~ s/\0/\\00/go;
	
	return( $str_filter );
}
