#############################################################################
#
# Jarl - Chat Code
#
#   Perl code to handle chats in Jarl.  Core functions, non interface
# related.
#
##############################################################################

##############################################################################
#
#  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.
#
#  Jabber
#  Copyright (C) 1998-1999 The Jabber Team http://jabber.org/
#
##############################################################################

##############################################################################
#
# jarlChat_AddMessage - function to add the <message/> to the chat.
#
##############################################################################
sub jarlChat_AddMessage {
  my ($JID,$message,$server) = @_;

  $Debug->Log1("jarlChat_AddMessage: start");

  if (ref($message eq "Net::Jabber::Message") && ($message->GetBody() eq "")) {
    $Debug->Log1("jarlChat_AddMessage: no body...bailing");
    return;
  }

  $server = 0 unless defined($server);

  my $fromMe = 0;
  my $chatID;
  my $myID;
  my $nickID;
  my $myJID;
  my $chatJID;
  my $currentID;

  if ($JID == $jabber{myJID}) {
    $myJID = $JID;
    $chatJID = $message->GetTo("jid");
    $fromMe = 1;
  } else {
    $myJID = $jabber{myJID};
    $chatJID = $JID;
  }

  $Debug->Log3("jarlChat_Addmessage: chatJID($chatJID) ref(".ref($chatJID).")");

  $chatID = $Roster->GetValue($chatJID->GetJID(),"name");
  $chatID = $chatJID->GetUserID() if (!defined($chatID) || ($chatID eq ""));
  $myID = $myJID->GetUserID();
  $nickID = $chatJID->GetUserID();

  if (exists($groupchat{jids}->{$chatJID->GetJID()})) {
    my $channel = $chatID;
    ($channel) = ($chatID =~ /^([^\@]+)\@/) if ($channel =~ /\@/);
    $chatID = $channel."/".$chatJID->GetResource();
    $myID = $channel."/".$groupchat{tags}->{&jarlGroupChat_Tag($chatJID->GetJID())}->{nick};
    $nickID = $chatJID->GetResource();
  } else {
    $chatJID->SetResource();
  }

  if ($fromMe == 1) {
    $currentID = $myID;
  } else {
    $currentID = $chatID;
  }

  my $tag = "";

  my $reply = "";
  my $fullJID;
  if (exists($groupchat{jids}->{$chatJID->GetJID()})) {
    $fullJID = $chatJID->GetJID("full");
  } else {
    $fullJID = $chatJID->GetJID();	
  }

  $Debug->Log2("jarlChat_AddMessage: fullJID($fullJID)");

  if (ref($message) eq "Net::Jabber::Message") {
    if (!exists($chat{jids}->{$fullJID})) {
      $reply = $message->Reply(template=>"client");
      if ($message->GetThread() ne "") {
	$tag = "chat-$$-".$message->GetThread();
      } else {
	$tag = "chat-$$-jarl".$jabber{threadCount}.time;
	$jabber{threadCount}++;
      }
      $chat{jids}->{$fullJID} = $tag;
    } else {
      $tag = $chat{jids}->{$fullJID};
    }
  } else {
    if (!exists($chat{jids}->{$fullJID})) {
      $reply = new Net::Jabber::Message();
      $reply->SetMessage(from=>$jabber{myJID},
			 to=>$chatJID,
			 type=>"chat",
			 thread=>"jarl".$jabber{threadCount}.time);
      $tag = "chat-$$-".$reply->GetThread();
      $jabber{threadCount}++;

      $chat{jids}->{$fullJID} = $tag;
    } else {
      $tag = $chat{jids}->{$fullJID};
    }
  }

  if (&jarlChatIF_ChatExists($tag) == 0) {
    &jarlChat_ChatID($tag,$chatID);
    &jarlChat_NickID($tag,$nickID);
    &jarlChat_Reply($tag,$reply);
    &jarlChat_JID($tag,$reply->GetTo("jid"));
    &jarlChatIF_NewChat($tag);
    &jarlChat_UserSecure($tag,-1);
    &jarlChat_SecureChat($reply->GetTo("jid"));
    &jarlChat_UserSecure($tag,&jarlChat_Secure($tag));
  } else {
    if ($message eq "") {
      &jarlChatIF_RaiseChat($tag);
    }
  }

  $Debug->Log1("jarlChat_AddMessage: Use tag($tag)");

  if (ref($message) eq "Net::Jabber::Message") {

    if ($message->DefinedBody()) {
      my $timestamp;
      my @xDelays = $message->GetX("jabber:x:delay");
      if ($#xDelays == -1) {
	$timestamp = &Net::Jabber::GetTimeStamp("local",time,"shortest");
      } else {
	my $xTag = $xDelays[0];
	$timestamp = &Net::Jabber::GetTimeStamp("utcdelaylocal",$xTag->GetStamp(),"shortest");
      }

      my $body = $message->GetBody();
      my $encrypted = 0;
      my @xEncrypted = $message->GetX("jabber:x:encrypted");
      if ($#xEncrypted > -1) {
	if (($config{gpg}->{active} == 1) && ($fromMe == 0)) {
	  $body = &jarlGPG_DecryptMessage($config{gpg}->{passphrase},
					  $xEncrypted[0]->GetMessage());
	}
	$encrypted = 1;
      }

      my @body = &jarlParser_ParseText($body,$myID);

      my $type;
      if ($body =~ /^\/me/) {
	my $metype = shift(@body);
	my $me = shift(@body);
	$me =~ s/^\/me/\* $currentID/;
	unshift(@body,$me);
	unshift(@body,$metype);
	$type = "action";
      } else {
	if ($server == 1) {
	  unshift(@body,"%% ");
	  unshift(@body,"normal");
	  $type = "server";
	} else {
	  unshift(@body,"<$currentID> ");
	  unshift(@body,"me") if ($fromMe == 1);
	  unshift(@body,"notme") if ($fromMe == 0);
	  $type = "say";
	}
      }

      &jarlChatIF_AddMessage($tag,$type,$timestamp,$encrypted,$currentID,@body);
    }

    my @sxpms = $message->GetX("jabber:x:sxpm");
    &jarlSXPM_AddMessage(@_) if ($#sxpms > -1);
  }

  $Debug->Log1("jarlChat_AddMessage: finish");

  return $tag;
}


##############################################################################
#
# jarlChat_Reply - if $value is not defined then it returns the reply.
#                   Otherwise, it sets the reply.
#
##############################################################################
sub jarlChat_Reply {
  my ($tag,$value) = @_;
  if (defined($value)) {
    $chat{tags}->{$tag}->{reply} = $value;
  } else {
    return $chat{tags}->{$tag}->{reply};
  }
}


##############################################################################
#
# jarlChat_NickID - if $value is not defined then it returns the nickid.
#                   Otherwise, it sets the nickid.
#
##############################################################################
sub jarlChat_NickID {
  my ($tag,$value) = @_;
  if (defined($value)) {
    $chat{tags}->{$tag}->{nickid} = $value;
  } else {
    return $chat{tags}->{$tag}->{nickid};
  }
}


##############################################################################
#
# jarlChat_ChatID - if $value is not defined then it returns the chatid.
#                   Otherwise, it sets the chatid.
#
##############################################################################
sub jarlChat_ChatID {
  my ($tag,$value) = @_;
  if (defined($value)) {
    $chat{tags}->{$tag}->{chatid} = $value;
  } else {
    return $chat{tags}->{$tag}->{chatid};
  }
}


##############################################################################
#
# jarlChat_JID - if $value is not defined then it returns the jid.  Otherwise,
#                it sets the jid.
#
##############################################################################
sub jarlChat_JID {
  my ($tag,$value) = @_;
  if (defined($value)) {
    $chat{tags}->{$tag}->{jid} = $value;
  } else {
    return $chat{tags}->{$tag}->{jid};
  }
}


##############################################################################
#
# jarlChat_Secure - if $value is not defined then it returns 1 if the chat
#                   is secure, and 0 if not.  Otherwise, it sets the
#                   security of the chat to the $value (0 or 1 only).
#
##############################################################################
sub jarlChat_Secure {
  my ($tag,$value) = @_;
  if (defined($value)) {
    $chat{tags}->{$tag}->{secure} = $value;
    &jarlChatIF_Secure($tag,$value);
  } else {
    return $chat{tags}->{$tag}->{secure};
  }
}


##############################################################################
#
# jarlChat_UserSecure - if $value is not defined then it returns 1 if the chat
#                       is secure, and 0 if not.  Otherwise, it sets the
#                       security of the chat to the $value (0 or 1 only).
#
##############################################################################
sub jarlChat_UserSecure {
  my ($tag,$value) = @_;
  if (defined($value)) {
    $chat{tags}->{$tag}->{usersecure} = $value;
  } else {
    return $chat{tags}->{$tag}->{usersecure};
  }
}


##############################################################################
#
# jarlChat_IsSecure - quick check to see if the chat is secure or not.
#
##############################################################################
sub jarlChat_IsSecure {
  my ($fromJID) = @_;

  $fromJID = new Net::Jabber::JID($fromJID) if (ref($fromJID) eq "");

  my $chatJID;
  if (exists($groupchat{jids}->{$fromJID->GetJID()})) {
    $chatJID = $fromJID->GetJID("full");
  } else {
    $chatJID = $fromJID->GetJID();
  }

  my $tag = $chat{jids}->{$chatJID};

  return &jarlChat_Secure($tag);
}


##############################################################################
#
# jarlChat_SecureChat - make the chat with the specified JID secure
#                       (if possible)
#
##############################################################################
sub jarlChat_SecureChat {
  my ($fromJID) = @_;

  $Debug->Log3("jarlChat_SecureChat: fromJID($fromJID)");

  $fromJID = new Net::Jabber::JID($fromJID) if (ref($fromJID) eq "");

  my $chatJID;
  my $rosterObj;
  my $JID;
  my $tag;
  if (exists($groupchat{jids}->{$fromJID->GetJID()})) {
    $chatJID = $fromJID->GetJID("full");
    $rosterObj = $groupchatRosters{"groupchat-$$-".$fromJID->GetJID()};
    $tag = $chat{jids}->{$chatJID};
    $JID = &jarlChat_JID($tag)->GetJID("full");
  } else {
    $chatJID = $fromJID->GetJID();
    $rosterObj = $Roster;
    $tag = $chat{jids}->{$chatJID};
    $JID = &jarlChat_JID($tag)->GetJID();
  }

  $Debug->Log3("jarlChat_SecureChat: chatJID($chatJID) tag($tag) JID($JID)");

  $Debug->Log3("jarlChat_SecureChat: usersecure(".&jarlChat_UserSecure($tag).")");

  return if (&jarlChat_UserSecure($tag) == 0);

  my $pubKey = $rosterObj->GetValue($JID,"gpgkeyid");

  $Debug->Log3("jarlChat_SecureChat: pubKey($pubKey)");

  &jarlGPG_FetchAndTrustKey($pubKey)
    if (defined($pubKey) &&
	(&jarlGPG_CheckKeyRing($pubKey) == 0) &&
	($config{gpg}->{autofetchtrust} == 1));

  my $secure = 1
    if (($config{gpg}->{active} == 1) &&
	defined($pubKey) &&
	&jarlGPG_CheckKeyRing($pubKey) == 1);

  $Debug->Log3("jarlChat_SecureChat: secure?($secure)");

  if ($secure == 0) {
    return if (&jarlChat_Secure($tag) == 0);
    &jarlChat_UnsecureChat($fromJID);
    return;
  }

  return if &jarlChat_Secure($tag);

  &jarlChat_Secure($tag,1);

  my $serverMsg = new Net::Jabber::Message();
  $serverMsg->SetMessage(body=>"Securing chat");
  &jarlChat_AddMessage($fromJID,$serverMsg,1);
}


##############################################################################
#
# jarlChat_UnsecureChat - make the chat with the specified JID unsecure
#
##############################################################################
sub jarlChat_UnsecureChat {
  my ($fromJID) = @_;

  $fromJID = new Net::Jabber::JID($fromJID) if (ref($fromJID) eq "");

  my $chatJID;
  if (exists($groupchat{jids}->{$fromJID->GetJID()})) {
    $chatJID = $fromJID->GetJID("full");
  } else {
    $chatJID = $fromJID->GetJID();
  }

  my $tag = $chat{jids}->{$chatJID};

  &jarlChat_Secure($tag,0);

  my $serverMsg = new Net::Jabber::Message();
  $serverMsg->SetMessage(body=>"Unsecuring chat");
  &jarlChat_AddMessage($fromJID,$serverMsg,1);
}


##############################################################################
#
# jarlChat_HistoryUp -  function to provide the interfaces with a consistent
#                       way of supporting scrolling up through the history.
#
##############################################################################
sub jarlChat_HistoryUp {
  my ($tag) = @_;

  $Debug->Log3("jarlChat_HistoryUp: tag($tag)");

  my $current = &jarlChatIF_CurrentSay($tag);

  $Debug->Log3("jarlChat_HistoryUp: current($current)");

  return $current if ($#{$chat{tags}->{$tag}->{history}} < 0);
  return $current if ($chat{tags}->{$tag}->{historyPtr} ==
		      $#{$chat{tags}->{$tag}->{history}});

  $Debug->Log3("jarlChat_HistoryUp: pointer($chat{tags}->{$tag}->{historyPtr})");

  if ($chat{tags}->{$tag}->{historyPtr} == -1) {
    $chat{tags}->{$tag}->{historySaved} = $current;
    $Debug->Log3("jarlChat_HistoryUp: saved($chat{tags}->{$tag}->{historySaved})");
  }

  $chat{tags}->{$tag}->{historyPtr}++;

  $Debug->Log3("jarlChat_HistoryUp: pointer($chat{tags}->{$tag}->{historyPtr})");
  $Debug->Log3("jarlChat_HistoryUp: return($chat{tags}->{$tag}->{history}->[$chat{tags}->{$tag}->{historyPtr}])");

  return $chat{tags}->{$tag}->{history}->[$chat{tags}->{$tag}->{historyPtr}];
}


##############################################################################
#
# jarlChat_HistoryDown - function to provide the interfaces with a consistent
#                        way of supporting scrolling down through the history.
#
##############################################################################
sub jarlChat_HistoryDown {
  my ($tag) = @_;

  my $current = &jarlChatIF_CurrentSay($tag);

  return $current if ($#{$chat{tags}->{$tag}->{history}} < 0);
  return $current if ($chat{tags}->{$tag}->{historyPtr} == -1);

  $chat{tags}->{$tag}->{historyPtr}--;

  return $chat{tags}->{$tag}->{historySaved}
    if ($chat{tags}->{$tag}->{historyPtr} == -1 );

  return $chat{tags}->{$tag}->{history}->[$chat{tags}->{$tag}->{historyPtr}];
}


##############################################################################
#
# jarlChat_AddHistory - function to add a line to the chat history and reset
#                       the pointer.
#
##############################################################################
sub jarlChat_AddHistory {
  my ($tag,$text) = @_;

  unshift(@{$chat{tags}->{$tag}->{history}},$text);
  $chat{tags}->{$tag}->{historyPtr} = -1;
}


##############################################################################
#
# jarlChat_SendSay - function to provide the interfaces a consistent way to
#                    send the message to the chatee.
#
##############################################################################
sub jarlChat_SendSay {
  my ($tag) = @_;

  my $text = &jarlChatIF_CurrentSay($tag);

  return if ($text eq "");

  &jarlChat_AddHistory($tag,$text);

  &jarlChat_Reply($tag)->RemoveX("jabber:x:encrypted");

  if (&jarlChat_Secure($tag) == 0) {
    &jarlChat_Reply($tag)->SetMessage(body=>$text);
  } else {
    my $pubKey = "";

    if (exists($groupchat{jids}->{&jarlChat_JID($tag)->GetJID()})) {
      $pubKey = $groupchatRosters{"groupchat-$$-".&jarlChat_JID($tag)->GetJID()}->GetValue(&jarlChat_JID($tag)->GetJID("full"),"gpgkeyid");
    } else {
      $pubKey = $Roster->GetValue(&jarlChat_JID($tag)->GetJID(),"gpgkeyid");
    }

    my $xEncrypted = &jarlChat_Reply($tag)->NewX("jabber:x:encrypted");
    $xEncrypted->SetEncrypted(message=>&jarlGPG_EncryptMessage($config{gpg}->{passphrase},$pubKey,$text));
    &jarlChat_Reply($tag)->SetMessage(body=>"This message is encrypted.");
  }

  $jabber{client}->Send(&jarlChat_Reply($tag));
  &jarlChat_Reply($tag)->SetMessage(body=>$text);

  &jarlChat_AddMessage($jabber{myJID},&jarlChat_Reply($tag));
  &jarlChatIF_CurrentSay($tag,"");
}


##############################################################################
#
# jarlChat_CompleteNick - function to provide the interfaces with a consistent
#                         way to support nick completion.
#
##############################################################################
sub jarlChat_CompleteNick {
  my ($tag) = @_;

  my $current = &jarlChatIF_CurrentSay($tag);

  my $nick = &jarlParser_NickComplete(type=>"chat",
				      string=>$current,
				      chatid=>&jarlChat_NickID($tag));
  if ($nick ne "") {
    $nick .= ":"
      if ($current =~ /^\s*\S*$/);
    $nick .= " ";
    $current =~ s/(\s?)\S*$/$1$nick/;
  }
  &jarlChatIF_CurrentSay($tag,$current);
}


##############################################################################
#
# jarlChat_DeleteChat - delete the memory taken up by this chat session.
#
##############################################################################
sub jarlChat_DeleteChat {
  my ($tag) = @_;

  foreach my $jid (keys(%{$chat{jids}})) {
    delete($chat{jids}->{$jid}) if ($chat{jids}->{$jid} eq $tag);
  }
  delete($chat{tags}->{$tag});
}


##############################################################################
#
# jarlChat_Leave - do everything needed to leave a chat
#
##############################################################################
sub jarlChat_Leave {
  my $tag = shift;

  &jarlChat_DeleteChat($tag);
}


1;
