##############################################################################
#
# Jarl - GPG Access Functions
#   Perl code to handle GPG calls that Jarl needs to message encrypted style.
#
##############################################################################

##############################################################################
#
#  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/
#
##############################################################################

use IO::Handle;
use GnuPG::Interface;
use IPC::Open3;
$SIG{CHLD} = 'IGNORE';

##############################################################################
#
# jarlGPG_SignStatus - generate the CDATA for the jabber:x:signed presence
#
##############################################################################
sub jarlGPG_SignStatus {
  my $passphrase = shift;
  my $status = shift;

  $Debug->Log1("jarlGPG_SignStatus: status($status)\n");

  my $gnupg = GnuPG::Interface->new();
  my $stdin = IO::Handle->new();
  my $stdout = IO::Handle->new();
  my $stderr = IO::Handle->new();
  my $handles = GnuPG::Handles->new(stdin=>$stdin,
				    stdout=>$stdout,
				    stderr=>$stderr,
				   );
  $gnupg->options->meta_interactive(0);
  $gnupg->options->hash_init(armor=>1);
  $gnupg->passphrase($passphrase);
  $gnupg->clearsign(handles=>$handles);

  print $stdin "$status\n";
  close($stdin);

  my $body;
  while(($_ = <$stdout>) && !(/^-----BEGIN PGP SIGNATURE/)) { }
  while(($_ = <$stdout>) && ($_ ne "\n")) { }
  while(($_ = <$stdout>) && !(/^-----\S+ PGP/)) { 
    $body .= $_;
  }
  close($stdout);
  close($stderr);
  chomp($body);

  $Debug->Log1("jarlGPG_SignStatus: body($body)\n");

  return $body;
}


##############################################################################
#
# jarlGPG_GetKeyID - extract the key id from CDATA of a signed presence
#
##############################################################################
sub jarlGPG_GetKeyID {
  my $status = shift;
  my $sig = shift;

  $Debug->Log1("jarlGPG_GetKeyID: status($status) sig($sig)\n");

  my $sigHeader1 = "-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

";
  my $sigHeader2 = "
-----BEGIN PGP SIGNATURE-----
Version: Jarl

";
  my $sigFooter = "
-----END PGP SIGNATURE-----
";
  
  my $gnupg = GnuPG::Interface->new();
  my $stdin   = IO::Handle->new();
  my $stdout   = IO::Handle->new();
  my $stderr  = IO::Handle->new();
  my $handles = GnuPG::Handles->new( stdin  => $stdin,
				     stdout => $stdout,
				     stderr => $stderr );
  
  $gnupg->options->meta_interactive(0);
  $gnupg->verify(handles=>$handles);
  
  print $stdin $sigHeader1,$status,$sigHeader2,$sig,$sigFooter,"\n";
  close($stdin);
  
  my $key = "";
  foreach my $line (<$stderr>) {
    ($key) = ($line =~ /.*Signature made.*using.*key ID (\S+)/);
    last if ($key ne "");
  }
  close($stderr);
  close($stdout);
  
  $Debug->Log1("jarlGPG_GetKeyID: key($key)\n");

  return $key;
}


##############################################################################
#
# jarlGPG_DecryptMessage - get the body data from the encrypted message
#
##############################################################################
sub jarlGPG_DecryptMessage {
  my $passphrase = shift;
  my $message = shift;

  $Debug->Log1("jarlGPG_DecryptMessage: message($message)\n");

  my $messageHeader = "-----BEGIN PGP MESSAGE-----

";
  my $messageFooter = "-----END PGP MESSAGE-----
";

  my $gnupg = GnuPG::Interface->new();
  my $stdin = IO::Handle->new();
  my $stdout = IO::Handle->new();
  my $stderr   = IO::Handle->new();
  my $handles = GnuPG::Handles->new(stdin=>$stdin,
				    stdout=>$stdout,
				    stderr=>$stderr,
				   );
  $gnupg->options->meta_interactive(0);
  $gnupg->passphrase($passphrase);
  $gnupg->decrypt(handles=>$handles);

  print $stdin $messageHeader,$message,$messageFooter,"\n";
  close($stdin);
  
  my @decrypted = <$stdout>;

  my $signed = 0;
  foreach my $line (@decrypted) {
    $signed = 1 if ($line =~ /^-----BEGIN PGP/);
  }
  
  my $body;
  if ($signed == 1) {
    my $seenStart = 0;
    my $seenBody = 0;
    foreach my $line (@decrypted) {
      next if (($seenStart == 0) && !($line =~ /^-----BEGIN PGP/));
      if ($seenStart == 0) {
	$seenStart = 1;
	next;
      }
      next if (($seenBody == 0) && ($line ne "\n"));
      if ($seenBody == 0) {
	$seenBody = 1;
	next;
      }
      last if ($line =~ /^-----\S+ PGP/);
      $body .= $line;
    }
  } else {
    foreach my $line (@decrypted) {
      $body .= $line;
    }
  }

  close($stdout);
  close($stderr);
  chomp($body);

  $Debug->Log1("jarlGPG_DecryptMessage: body($body)\n");

  return $body;
}


##############################################################################
#
# jarlGPG_EncryptMessage - generate the GPG version of the data
#
##############################################################################
sub jarlGPG_EncryptMessage {
  my $passphrase = shift;
  my $recipient = shift;
  my $body = shift;

  $Debug->Log1("jarlGPG_EncryptMessage: recipient($recipient) body($body)\n");

  my $gnupg = GnuPG::Interface->new();
  my $stdin = IO::Handle->new();
  my $stdout = IO::Handle->new();
  my $stderr   = IO::Handle->new();
  my $handles = GnuPG::Handles->new(stdin=>$stdin,
				    stdout=>$stdout,
				    stderr=>$stderr,
				   );
  $gnupg->options->meta_interactive(0);
  $gnupg->options->hash_init(armor=>1,
			     recipients=>[ $recipient ]
			    );
  $gnupg->passphrase($passphrase);
  $gnupg->clearsign(handles=>$handles);
  print $stdin "$body";
  close($stdin);

  my @signed = <$stdout>;
  close($stdout);
  close($stderr);

  $gnupg = GnuPG::Interface->new();
  $stdin = IO::Handle->new();
  $stdout = IO::Handle->new();
  $stderr   = IO::Handle->new();
  $handles = GnuPG::Handles->new(stdin=>$stdin,
				 stdout=>$stdout,
				 stderr=>$stderr,
				);
  $gnupg->options->meta_interactive(0);
  $gnupg->options->hash_init(armor=>1,
			     recipients=>[ $recipient ]
			    );
  $gnupg->encrypt(handles=>$handles);
  
  print $stdin @signed;
  close($stdin);
  
  my $newBody;
  while(($_ = <$stdout>) && !(/^-----BEGIN PGP/)) { }
  while(($_ = <$stdout>) && ($_ ne "\n")) { } 
  while(($_ = <$stdout>) && !(/^-----\S+ PGP/)) {
    $newBody .= $_;
  }
  close($stdout);
  close($stderr);

  $Debug->Log1("jarlGPG_EncryptMessage: output($newBody)\n");

  return $newBody;
}


##############################################################################
#
# jarlGPG_CheckKeyRing - check if the specified user is in your pub key ring.
#
##############################################################################
sub jarlGPG_CheckKeyRing {
  my $keyid = shift;

  $Debug->Log1("jarlGPG_CheckKeyRing: keyid($keyid)\n");

  my $gnupg = GnuPG::Interface->new();
  my $stdin = IO::Handle->new();
  my $stdout = IO::Handle->new();
  my $stderr   = IO::Handle->new();
  my $handles = GnuPG::Handles->new(stdin=>$stdin,
				    stdout=>$stdout,
				    stderr=>$stderr,
				   );
  $gnupg->options->meta_interactive(0);
  $gnupg->list_public_keys(handles=>$handles);

  my @keys = <$stdout>;

  $Debug->Log1("jarlGPG_CheckKeyRing: keys(@keys)\n");

  close($stdin);
  close($stdout);
  close($stderr);

  my $haveKey = 0;
  foreach my $key (@keys) {
    $haveKey = 1 if ($key =~ /$keyid/);
  }

  $Debug->Log1("jarlGPG_CheckKeyRing: in public ring($haveKey)\n");

  $haveKey = &jarlGPG_KeyTrusted($keyid) if ($haveKey == 1);

  $Debug->Log1("jarlGPG_CheckKeyRing: trusted($haveKey)\n");

  return $haveKey;
}


##############################################################################
#
# jarlGPG_TestPassphrase - test that the specified passphrase works
#
##############################################################################
sub jarlGPG_TestPassphrase {
  my $passphrase = shift;

  my $gnupg = GnuPG::Interface->new();
  $gnupg->options->meta_interactive(0);
  $gnupg->passphrase($passphrase);
  my $test = $gnupg->test_default_key_passphrase();

  $Debug->Log1("jarlGPG_TestPassphrase: pass($test)\n");

  return $test;
}


##############################################################################
#
# jarlGPG_KeyTrusted - is the key trusted?
#
##############################################################################
sub jarlGPG_KeyTrusted {
  my $key = shift;

  $Debug->Log1("jarlGPG_KeyTrusted: key($key)\n");

  my $gnupg = GnuPG::Interface->new();
  $gnupg->options->meta_interactive(0);
	       my @keys = $gnupg->get_public_keys($key);
	       
  if ($#keys == -1) {
    $Debug->Log1("jarlGPG_KeyTrusted: no key\n");
    return 0;
  }

  my $trust = $keys[0]->owner_trust();
  if (($trust eq "m") || ($trust eq "f") || ($trust eq "u")) {
    $Debug->Log1("jarlGPG_KeyTrusted: we trust them\n");
    return 1;
  }

  $Debug->Log1("jarlGPG_KeyTrusted: they can't even hold the door open for me....\n");
  return 0;
}


##############################################################################
#
# jarlGPG_FetchAndTrustKey - fetch and trust the key
#
##############################################################################
sub jarlGPG_FetchAndTrustKey {
  my $key = shift;

  $Debug->Log1("jarlGPG_FetchAndTrustKey: key($key)\n");

  $Debug->Log1("jarlGPG_FetchAndTrustKey: fetch\n");
  open3(STDIN,STDOUT,"","gpg --keyserver $config{gpg}->{keyserver} --recv-key $key");
  close(STDIN);
  close(STDOUT);
  
  $Debug->Log1("jarlGPG_FetchAndTrustKey: trust\n");
  open3(STDIN,STDOUT,"","gpg --command-fd 0 --edit-key $key");
  print STDIN "trust\n";
  print STDIN "$config{gpg}->{trust}\n";
  print STDIN "quit\n";
  close(STDIN);
  close(STDOUT);

  $Debug->Log1("jarlGPG_FetchAndTrustKey: done\n");
}


1;
