# Copyright (C) 2005 FishGrove Inc.
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
#
# $Id: Handshaker_s.pm,v 1.42 2006/03/07 14:39:11 slash5234 Exp $

########################################################################
#package Affelio::SNS::Handshaker_s::Util;
########################################################################
package Affelio::SNS::Handshaker_s::Util;
{
    BEGIN{
	use strict;
	use lib("../../");
	use Affelio;
	use Affelio::misc::Debug;

	use vars qw($AF_DIR);
    }

    sub af_new {
	my $cfg_dir = $AF_DIR . "config/";
	debug_print("Starting AF($cfg_dir)...");
	my $af = Affelio->new( ConfigDir => $cfg_dir )
	    or die "Cannot start Affelio";
	return($af);
    }
}

########################################################################
#Package Affelio::SNS::Handshaker_s;
########################################################################
package Affelio::SNS::Handshaker_s;
{
    BEGIN{
	use strict;
	use lib("../../../extlib/");
	use XMLRPC::Transport::HTTP;
	use DBI;
	use Crypt::RC5;
	use Crypt::DH;
	use MIME::Base64;
	use MIME::Base64;
	use Error qw(:try);
	use lib("../../");
	use Affelio;
	use Affelio::SNS::FriendManager;
	use Affelio::SNS::Handshaker_tmpDB;
	use Affelio::SNS::Handshaker_c qw(get_F2List);
	use Affelio::App::Standalone::Admin::MakeAffelioLink;
	use Affelio::Managing::MessageManager;
	use Affelio::misc::CGIError;
	use Affelio::misc::Debug;
	use Affelio::misc::MyCrypt;
	use Affelio::misc::NetMisc;
	use Affelio::misc::Time;
	use Affelio::misc::Sanitizer;

	use Exporter;
	@Affelio::SNS::Handshaker_s::ISA = "Exporter";
	@Affelio::SNS::Handshaker_s::EXPORT = qw (HandShake HandShakeReply F2List post_Message get_services get_news op_request_AFlink);

    }

    ##################################################################
    #affelio.HandShake
    # proto_ver:1.1
    # Accept HandShake from a client
    ##################################################################
    sub HandShake {
	my $self = shift;

        ##################################################
	#Distill args
        ##################################################
	my ($proto_ver, $timestamp, $peer_nickname, 
	    $peer_af_id, $peer_DH_pub_key_str, $MIMed_mesg) = @_;
	my $peer_domain = Affelio::misc::NetMisc::get_remote_domain(%ENV);
	my $sessionid = "$$" . "$timestamp";
	debug_print("server.HandShake: size=["
					  . length($MIMed_mesg));

	debug_print("server.HandShake: $proto_ver, $timestamp, $peer_domain, $peer_nickname, $peer_af_id, $peer_DH_pub_key_str [$MIMed_mesg]\n");
	##################################################
	#Version check
	##################################################
	if($proto_ver > 1.1){
	    return {
		flerror => XMLRPC::Data->type('boolean', 102),
		message => "ERR:102 UnsupportedProtoVer 1.1"
		};
	}

        ##################################################
	#Decode MIME
        ##################################################
	my $mesg = MIME::Base64::decode_base64($MIMed_mesg);

        ###########################################
        # Instantiate Affelio
        ###########################################
	my $af = Affelio::SNS::Handshaker_s::Util::af_new();

        ###########################################
        # Send a message to MessageManager
        ###########################################
	my $click_url = "$af->{site__user_afid}/bin/recv_mail_ack.cgi?id=$sessionid";
	my $mesg1 = "";
	$mesg1 .= "<AF_M text='_HANDSHAKE_REQUEST_MAIL_MESG1' param='$peer_nickname'>";
	$mesg1 .= "<AF_M text='_HANDSHAKE_REQUEST_MAIL_MESG2'>";
	$mesg1 .= "<AF_M text='_HANDSHAKE_REQUEST_MAIL_MESG3' param='$peer_nickname'>";
	$mesg1 .= "<AF_M text='_HANDSHAKE_REQUEST_MAIL_MESG4' param='$peer_af_id'>";
	$mesg1 .= "<AF_M text='_HANDSHAKE_REQUEST_MAIL_MESG5' param='$mesg'>";
	$mesg1 .= "<AF_M text='_HANDSHAKE_REQUEST_MAIL_MESG6' param='$click_url'>";
	my $translated = $af->translate_templateL10N($mesg1);
	$translated =~ s|\r\n|\n|g;
	$translated =~ s|\r|\n|g;
        $translated =~ s|\n|\<BR\>|g;

	my $message_body
	    = MIME::Base64::encode_base64($translated);
	$af->getMESGM->post_message("Your Affelio",
				   "Link Request from $peer_nickname",
				   "SystemToUser/LinkRequest/Encode-Base64/HTML",
				   $message_body);

        ###########################################
        # Save peer's info into "received_Handshake" DB
        ###########################################
	my $tmpdb= new Affelio::SNS::Handshaker_tmpDB($af);
	$tmpdb->add_received_Handshake($sessionid,
				       $peer_af_id,
				       $peer_nickname,
				       $timestamp,
				       $peer_DH_pub_key_str);

	debug_print("server.HandShake: DB(W) $sessionid\n");

	undef($af);
        ###########################################
        # Reply to client
        ###########################################
	my $msg = "OK: Thanks for your HandShake.";
	return {
	    flerror => XMLRPC::Data->type('boolean', 0),
	    message => $msg
	    };
	
    }#method

    ##################################################################
    #affelio.get_news
    # proto_ver: 1
    ##################################################################
    sub get_news{
	my $self = shift;
	my ($proto_ver,  $xwsse_mesg,  $format) = @_;
	my $af = Affelio::SNS::Handshaker_s::Util::af_new();

	if($proto_ver > 1.0){
	    return {
		flerror => XMLRPC::Data->type('boolean', 102),
		message => "ERR:102 UnsupportedProtoVer 1.0"
		};
	}

	#################################
	#authentication
	#################################
	#distill visiter_afid
	$xwsse_mesg =~ /Username="(.*)",(\s*)Password/;
	my $visitor_afid = $1;
	debug_print("S::get_news visitor_afid = [$visitor_afid]");

	#get password
	my $passAB = $af->getFM()->get_attribute_by_afid($visitor_afid, 
							 "password");
	debug_print("S::get_news passAB = [$passAB]");

	#WSSE authentication
	require Affelio::misc::WSSE;
	if(Affelio::misc::WSSE::authenticate($xwsse_mesg,
					     $visitor_afid, $passAB)){
	    debug_print("S::get_news WSSE authentication OK.");
	}else{
	    debug_print("S::get_news WSSE authentication NG.");
	    return {
		flerror => XMLRPC::Data->type('boolean', 104),
		message => "ERR:104 AuthenticationError"
		};
	}
	
	#################################
	#get list of apps the user can access
	#################################
	my @app_list=();
	my $this_app_ref;
	my $tmp_name;
	while ( ($tmp_name, $this_app_ref) = each( %{$af->getAM->{apps}} ) ){
	    my %this_app = %$this_app_ref;
	    
	    my $perm_to_tab=0;
	    $perm_to_tab
		= $af->getAM->get_summed_app_perm($visitor_afid,
						    "f1",
						    $this_app{'install_name'},
						    "DF_access");
	    if($perm_to_tab ==1 ){
		push(@app_list, $this_app{'install_title'});
	    }	
	    debug_print("S::get_news: [$tmp_name]");
	}
	#while
	debug_print("S::get_news: app_list = @app_list");

	#################################
	#get what's new contents
	#################################
	my $last_news_out = $af->getFM()->get_attribute_by_afid($visitor_afid,
							"last_news_out");
	debug_print("S::get_news last_news_out=[$last_news_out]");

	my $news = $af->getMyNews()->retrieve_news(app_list => \@app_list,
						   laterthan =>$last_news_out);

	my @row=();
	my $ret="";
	while(@row = $news->fetchrow_array){
	    $ret .= "$row[1]\n$row[2]\n$row[3]\n$row[4]\n$row[5]\n$row[6]\n";
	}
	debug_print("S::get_news NEWS=[$ret]");

	#################################
	#update last_news_out
	#################################
	my $uid = $af->getFM()->get_attribute_by_afid($visitor_afid, "uid");
	$af->getFM()->set_attribute_by_id($uid, "last_news_out", 
				  Affelio::misc::Time::get_timestamp());
	debug_print("S::get_news updated last_news_out.");

	#################################
	#send back return message
	#################################
	debug_print("S::get_news  done.");
	return {
	    flerror => XMLRPC::Data->type('boolean', 0),
	    message => $ret
	    };
    }

    ##################################################################
    #affelio.op_request_AFlink
    # proto_ver: 1
    ##################################################################
    sub op_request_AFlink{
	my $self = shift;
	my ($proto_ver,  $xwsse_mesg,  $to_uri) = @_;
	my $af = Affelio::SNS::Handshaker_s::Util::af_new();
	my $ret ="";

	debug_print("S::op_request_AFlink: start");
	if($proto_ver > 1.0){
	    return {
		flerror => XMLRPC::Data->type('boolean', 102),
		message => "ERR:102 UnsupportedProtoVer 1.0"
		};
	}

	############################
	debug_print("S::op_request_AFlink: do authentication...");
	require Affelio::misc::WSSE;
	if( !(Affelio::misc::WSSE::authenticate($xwsse_mesg, 
						$af->{site__username},
						$af->{userpref__password_remoteop})) ){
	    $ret="NG";
	}else{
	    $ret="OK";

	    debug_print("S::op_request_AFlink: send_request...");
	    Affelio::App::Standalone::Admin::MakeAffelioLink::_send_request($af, 
								$to_uri);

	    debug_print("S::op_request_AFlink: send_request...done.");
	}

	############################
	debug_print("S::op_request_AFlink: end.");
	return {
	    flerror => XMLRPC::Data->type('boolean', 0),
	    message => $ret
	    };
    }


    ##################################################################
    #affelio.get_services
    # proto_ver: 1
    ##################################################################
    sub get_services{
	my $self = shift;
	my ($proto_ver, 
	    $wsse_username, $wsse_pwdigest, $wsse_nonce, $wsse_created,
	    $format_req ) = @_;
	my $af = Affelio::SNS::Handshaker_s::Util::af_new();

	if($proto_ver > 1.0){
	    return {
		flerror => XMLRPC::Data->type('boolean', 102),
		message => "ERR:102 UnsupportedProtoVer 1.0"
		};
	}

	my $msg= <<EOT;
<?xml version="1.0"?>
<services type="core">
<service name="AffelioHandshaker">
<version>1.2</version>
</service>
<service name="AffelioMessaging">
<version>1.0</version>
</service>
<service name="AffelioFriendList">
<version>1.0</version>
</service>
<service name="AffelioNews">
<version>1.0</version>
</service>
</services>
EOT

	$msg .= '<services type="applications">' . "\n";

	while ( my ($tmp_name, $app) = each( %{$self->getAM->{apps}} ) ){
	    $msg .= "<service about=\"" . $app->{app_URI} . "\">\n";
	    $msg .= "<version>" . $app->{app_version} . "</version>\n";
	    $msg .= "<name>" . $app->{app_name} . "</name>\n";
	    $msg .= "<baseurl>" . $af->{site__user_afid} 
	    . "/apps/" . $apps->{install_name} .  "</baseurl>\n";
	    $msg .= "</service>\n";
	}

	my $msg .= "</services>\n";

	return {
	    flerror => XMLRPC::Data->type('boolean', 0),
	    message => $msg
	    };
    }

    ##################################################################
    #affelio.F2List
    # proto_ver: 1
    ##################################################################
    sub F2List {
	my $self = shift;
	my ($proto_ver, $req_timestamp) = @_;

	my $af = Affelio::SNS::Handshaker_s::Util::af_new();

	$req_timestamp 
	    = Affelio::misc::Sanitizer::sanitize_number($req_timestamp);

	debug_print("server.F2List: proto_ver=$proto_ver, timestamp=$req_timestamp");

	##################################
	#Retrieve friends whose
	#     timestamp > req_timestamp
	#Retrieve erased friends whose
	#     timestamp > req_timestamp
	my $retmsg = $af->getFM->get_updated_friends($req_timestamp);

	debug_print("server.F2List: ret=[$retmsg]");
	debug_print("server.F2List: end.");

	return {
	    flerror => XMLRPC::Data->type('boolean', 0),
	    message => $retmsg
	    };
    }

    ##################################################################
    #affelio.post_Message
    # proto_ver:1
    ##################################################################
    sub post_Message {
	my $self = shift;
	my $af = Affelio::SNS::Handshaker_s::Util::af_new();

	debug_print("server.postMesg: start.");

        ##################################################
	#Distill args
        ##################################################
	my ($proto_ver, 
	    $timestamp, 
	    $peer_afid, 
	    $MIMed_mesg) = @_;

	my $passAB = $af->getFM->get_attribute_by_afid($peer_afid, "password");
	if(!defined($passAB) || $passAB eq ""){
	    return {
		flerror => XMLRPC::Data->type('boolean', 100),
		message => "ERR:100 AFLinkNotFound"
	    };
	}

        ##################################################
	#Decode MIME
        ##################################################
	my $encrypted = MIME::Base64::decode_base64($MIMed_mesg);

        ##################################################
	#Decrypt
        ##################################################
	my $rc5 = Crypt::RC5->new($passAB, 12 );
	my $plain = $rc5->decrypt($encrypted);

        ##################################################
	#Parse XML
        ##################################################
	my $src;
	if($plain =~ /<src>(.+)<\/src>/){
	    $src = $1;
	}
	debug_print("server.postMesg: src: $src"); 

	my $msg_from;
	if($plain =~ /<from>(.+)<\/from>/){
	    $msg_from = $1;
	}
	debug_print("server.postMesg: from: $msg_from"); 

	my $msg_from_nickname;
	if($plain =~ /<from_nickname>(.+)<\/from_nickname>/){
	    $msg_from_nickname = $1;
	}
	debug_print("server.postMesg: nick: $msg_from_nickname"); 

	my $msg_to;
	if($plain =~ /<to>(.+)<\/to>/){
	    $msg_to = $1;
	}
	debug_print("server.postMesg: to: $msg_to");

	my $msg_timestamp;
	if($plain =~ /<timestamp>(.+)<\/timestamp>/){
	    $msg_timestamp = $1;
	}
	debug_print("server.postMesg: TIME: $msg_timestamp");

	my $msg_MIMed_title;
	if($plain =~ /<title>(.*)<\/title>/ms){
	    $msg_MIMed_title = $1;
	}
	debug_print("server.postMesg: title: $msg_MIMed_title");

	my $msg_MIMed_body;
	if($plain =~ /<text>(.*)<\/text>/ms){
	    $msg_MIMed_body = $1;
	}
	debug_print("server.postMesg: body: $msg_MIMed_body");

        ##################################################
	#Encryption integrity check
        ##################################################
	if($src ne $peer_afid){
	    return {
		flerror => XMLRPC::Data->type('boolean', 101),
		message => "ERR:101 InvalidEncryption"
		};
	}

        ##################################################
	#MIM decode for title and body
        ##################################################
	my $msg_title = MIME::Base64::decode_base64($msg_MIMed_title);
	my $msg_body  = MIME::Base64::decode_base64($msg_MIMed_body);

        ###########################################
        # Post this message to Message Manager
        ###########################################
	debug_print("server.PostMesg: $proto_ver, $msg_from $msg_from_nickname $msg_to $msg_timestamp $msg_title $msg_body");

	$msg_from = '<A HREF="' .  $msg_from . '">' . $msg_from_nickname . '</A>';

	$af->getMESGM->post_message($msg_from,
				   $msg_title,
				   "UserToUser/OneToOne",
				   $msg_body);
	undef($af);
	
        ###########################################
        # Reply to client
        ###########################################
	my $msg = "OK: Thanks for your message.";
	return {
	    flerror => XMLRPC::Data->type('boolean', 0),
	    message => $msg
	    };
    }


    ##################################################################
    #affelio.HandShakeReply
    # proto_ver:1.1
    # Accept HandshakeReply from a client
    ##################################################################
    sub HandShakeReply {
	my $self = shift;
	
        ###########################################
	#Distill args
        ###########################################
	my ($proto_ver, $timestamp, $peer_nickname, 
	    $peer_af_id, $peer_DH_pub_key_str, $mesg) = @_;
	debug_print("server.HandShakeReply: $proto_ver, $timestamp, $peer_nickname, $peer_af_id, $peer_DH_pub_key_str $mesg\n");
	
	##################################################
	#Version check
	##################################################
	if($proto_ver > 1.1){
	    return {
		flerror => XMLRPC::Data->type('boolean', 102),
		message => "ERR:102 UnsupportedProtoVer 1.1"
		};
	}

        ###########################################
        # Instantiate Affelio
        ###########################################
	my $af = Affelio::SNS::Handshaker_s::Util::af_new();

        ###########################################
        # check peer's info in "received_Handshake" DB
        ###########################################
	debug_print("server.HandShakeReply: searching... $peer_af_id => $timestamp\n");

	my $tmpdb= new Affelio::SNS::Handshaker_tmpDB($af);
	my @ret= $tmpdb->remove_sent_Handshake($timestamp);

	my $my_DH_pri_key_str;
	my $dummy1;
	my $dummy2;
	my $dummy3;
	my $dummy4;

	if(!defined(@ret)){
	    #No such session exists!!
	    debug_print("server.HandShakeReply: sent-Handshake session NOT Found!\n");
	      return {
		  flerror => XMLRPC::Data->type('boolean', 103),
		  message => "ERR:103 HandShakeReplyDenied"
		  };
	}else{
	    ($dummy1, $dummy2, $dummy3, $dummy4, $my_DH_pri_key_str) = @ret;
	    debug_print("server.HandShakeReply: my DH_pri_key = $my_DH_pri_key_str");
	    debug_print("server.HandShakeReply: sent-Handshake session Found. OK. Let's Move on.");
	}
	
        ###########################################
        #generate password
        ###########################################
	my $mydh = Crypt::DH->new;
        #RFC 2412 - The OAKLEY Key Determination Protocol
        #Group 1:  A 768 bit prime
	my $DH_g="2";
	my $DH_p="1552518092300708935130918131258481755631334049434514313202351194902966239949102107258669453876591642442910007680288864229150803718918046342632727613031282983744380820890196288509170691316593175367469551763119843371637221007210577919";
	$mydh->g($DH_g);
	$mydh->p($DH_p);

	$mydh->priv_key(Math::BigInt->new($my_DH_pri_key_str) );
	my $pass 
	    = $mydh->compute_key(Math::BigInt->new($peer_DH_pub_key_str))->bstr;
	debug_print("server.HandShakeReply: PASSWORD=[$pass]\n");

        ###########################################
        #Add peer to my friends list.
        ###########################################
	my $uid = $af->getFM->add_friend($peer_af_id, 
					$peer_nickname,
					$timestamp,
					$pass);
	debug_print("server.HandShakeReply: add_friend finished.\n");

	eval{
	    $af->getDB->commit;
	    $af->getDB->disconnect;
	    undef($af);
	};
	if($@){
	    debug_print($@);
	}

        ###########################################
        #Get peer's friends list.
        ###########################################
	# "peer's friends" = my F2 friends
	debug_print("server.HandshakeReply: Let's download peer's flist!");
	my $ret = Affelio::SNS::Handshaker_c::get_F2List(dest_uri =>  $peer_af_id, timestamp => 0);
	debug_print("server.HandshakeReply: get_F2List finished.");
	debug_print("server.HandshakeReply: List I've got is [$ret]");
	
        ###########################################
        #Save the F2 list into my DB
        ###########################################
	$af = Affelio::SNS::Handshaker_s::Util::af_new();
	debug_print("server.HandshakeReply: Let's save peer's flist!");
        $af->getFM->save_F2List($ret, $peer_af_id);
	debug_print("server.HandshakeReply: save_F2List finished.");
	#Make a new instance of Affelio

        ###########################################
        #Reply to client
        ###########################################
	return {
	    flerror => XMLRPC::Data->type('boolean', 0),
	    message => "OK: Thanks for your HandShakeReply." 
	    };
    }
}


########################################################################
#Package affelio
########################################################################
{    
    package affelio;
    BEGIN { @affelio::ISA = qw( Affelio::SNS::Handshaker_s ); }
}
########################################################################
1;
