/*  pgppipe.c

    Source file for  pgppipe  (decryption programme).

    Copyright (C) 1994-1997  Richard Gooch

    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., 675 Mass Ave, Cambridge, MA 02139, USA.

    Richard Gooch may be reached by email at  rgooch@atnf.csiro.au
    The postal address is:
      Richard Gooch, c/o ATNF, P. O. Box 76, Epping, N.S.W., 2121, Australia.
*/

/*  This programme will decrypt messages piped into it and will send the
    decrypted message to it's standard output. It uses PGPdaemon to perform
    the decryption.


    Written by      Richard Gooch   14-JUL-1994

    Updated by      Richard Gooch   15-JUL-1994: Added code to pass
  non-encrypted blocks of data straight through.

    Updated by      Richard Gooch   3-DEC-1994: Took account of changes to
  connect_to_pgpdaemon  .

    Updated by      Richard Gooch   7-DEC-1994: Inserted PGP message header
  line if PGPdaemon not contactable.

    Updated by      Richard Gooch   25-SEP-1997: Used new ERRSTRING macro.

    Updated by      Richard Gooch   1-OCT-1997: Added -binary and -force
  options.

    Last updated by Richard Gooch   2-OCT-1997: Fixed binary decrypt: to_fd
  was not closed hence programme would hang.


*/
#include <stdio.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <ctype.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <signal.h>
#include <string.h>
#include <pgpsendmail.h>
#include <version.h>


/*  Private functions  */
STATIC_FUNCTION (flag ascii_decrypt, (CONST char *pgppath) );
STATIC_FUNCTION (flag binary_decrypt, (CONST char *pgppath) );


/*  Public functions follow  */

void main (int argc, char **argv, char **envp)
{
    flag binary = FALSE;
    flag force = FALSE;
    char *pgppath;
    char *usage_string = "Usage:\tpgppipe [-binary] [-force]";

    for (--argc, ++argv; argc > 0; --argc, ++argv)
    {
	if (strcmp (*argv, "-binary") == 0) binary = TRUE;
	else if (strcmp (*argv, "-force") == 0) force = TRUE;
	else
	{
	    fprintf (stderr, "%s\n", usage_string);
	    exit (RV_BAD_PARAM);
	}
    }
    if ( ( pgppath = getenv ("PGPPATH") ) == NULL )
    {
	/*  Plain copy  */
	copy_data (OUTPUT_FD, INPUT_FD, FALSE);
	exit (RV_OK);
    }
    if (binary)
    {
	if ( binary_decrypt (pgppath) ) exit (RV_OK);
    }
    else
    {
	if ( ascii_decrypt (pgppath) ) exit (RV_OK);
    }
    /*  Some failure  */
    if (force)
    {
	copy_data (OUTPUT_FD, INPUT_FD, FALSE);
	exit (RV_OK);
    }
    else exit (RV_UNDEF_ERROR);
}   /*  End Function main  */


/*  Private functions follow  */

static flag ascii_decrypt (CONST char *pgppath)
/*  [SUMMARY] Perform an ASCII decryption.
    <pgppath> The contents of the PGPPATH environment variable.
    [RETURNS] TRUE on success, else FALSE.
*/
{
    flag bool_val;
    flag pgp_data;
    int to_fd, from_fd;
    int child_pid, status;
    int len;
    char line[STRING_LENGTH];
    char *pgp_start_line = "-----BEGIN PGP MESSAGE-----\n";
    char *pgp_end_line = "-----END PGP MESSAGE-----\n";

    /*  ASCII: Loop through lines  */
    pgp_data = FALSE;
    while ( read_line_fd (INPUT_FD, line, STRING_LENGTH, FALSE) )
    {
	/*  Got a line  */
	len = strlen (line);
	if (pgp_data)
	{
	    /*  Copy straight out to PGPdaemon  */
	    if (write (to_fd, line, len) < len)
	    {
		fprintf (stderr, "Error writing data\t%s\n", ERRSTRING);
		waitpid (child_pid, &status, 0);
		close (to_fd);
		return (FALSE);
	    }
	    if (strcmp (line, pgp_end_line) == 0)
	    {
		/*  Tell PGPdaemon the end of the message has arrived  */
		if (write (to_fd, "\0", 1) < 1)
		{
		    fprintf (stderr, "Error copying data to PGPdaemon\t%s\n",
			     ERRSTRING);
		    kill (child_pid, SIGKILL);
		    waitpid (child_pid, &status, 0);
		    close (to_fd);
		    return (FALSE);
		}
		close (to_fd);
		/*  Reap child  */
		waitpid (child_pid, &status, 0);
		if (status != 0)
		{
		    fprintf (stderr,
			     "pgppipe child exited with status: %d\n", status);
		    return (FALSE);
		}
		fprintf (stdout, "\nEnd automatically decrypted message\n");
		fflush (stdout);
		pgp_data = FALSE;
	    }
	    continue;
	}
	/*  No existing decryption  */
	if (strcmp (line, pgp_start_line) != 0)
	{
	    /*  Not start of PGP message: plain copy  */
	    if (write (OUTPUT_FD, line, len) < len)
	    {
		fprintf (stderr, "Error writing data\t%s\n", ERRSTRING);
		return (FALSE);
	    }
	    continue;
	}
	/*  Start of PGP message  */
	if ( !connect_to_pgpdaemon (pgppath, &to_fd, &from_fd) )
	{
	    fprintf (stderr, "Could not connect to PGPdaemon\n");
	    fputs (pgp_start_line, stdout);
	    fflush (stdout);
	    return (FALSE);
	}
	if (write (to_fd, "DECRYPT\n", 8) < 8)
	{
	    fprintf (stderr, "Error writing request code\t%s\n", ERRSTRING);
	    return (FALSE);
	}
	/*  Check if OK so far  */
	if (read (from_fd, &bool_val, sizeof bool_val) < sizeof bool_val)
	{
	    fprintf (stderr,"Error reading response flag from PGPdaemon\t%s\n",
		     ERRSTRING);
	    return (FALSE);
	}
	if (!bool_val)
	{
	    fprintf (stderr,
		     "PGPdaemon refused to decrypt: probably has no passphrase\n");
	    return (FALSE);
	}
	fprintf (stdout, "pgppipe %s: automatically decrypted message:\n\n",
		 VERSION);
	fflush (stdout);
	if (write (to_fd, line, len) < len)
	{
	    fprintf (stderr, "Error writing data\t%s\n", ERRSTRING);
	    close (to_fd);
	    close (from_fd);
	    return (FALSE);
	}
	/*  Now have to copy stdin to PGPdaemon input and PGPdaemon output to
	    stdout  */
	/*  Setup a child process. Parent copies stdin to PGPdaemon.
	    Child copies PGPdaemon output to stdout  */
	switch ( child_pid = fork () )
	{
	  case 0:
	    /*  Child  */
	    close (to_fd);
	    if ( !copy_data (OUTPUT_FD, from_fd, FALSE) )
	    {
		fprintf (stderr, "Error copying data from PGPdaemon\n");
		exit (RV_UNDEF_ERROR);
	    }
	    /*  Done  */
	    close (from_fd);
	    exit (RV_OK);
	    /*break;*/
	  case -1:
	    /*  Error  */
	    fprintf (stderr, "Could not fork\t%s\n", ERRSTRING);
	    return (FALSE);
	    /*break;*/
	  default:
	    /*  Parent  */
	    pgp_data = TRUE;
	    close (from_fd);
	    break;
	}
    }
    if (pgp_data) close (to_fd);
    return (TRUE);
}   /*  End Function ascii_decrypt  */

static flag binary_decrypt (CONST char *pgppath)
/*  [SUMMARY] Perform a binary decryption.
    <pgppath> The contents of the PGPPATH environment variable.
    [RETURNS] TRUE on success, else FALSE.
*/
{
    flag bool_val;
    flag binary = FALSE;
    int to_fd, from_fd;
    int child_pid, status;
    int len;

    if ( !connect_to_pgpdaemon (pgppath, &to_fd, &from_fd) )
    {
	fprintf (stderr, "Could not connect to PGPdaemon\n");
	return (FALSE);
    }
    if (write (to_fd, "BINARY_DECRYPT\n", 15) < 15)
    {
	fprintf (stderr, "Error writing request code\t%s\n", ERRSTRING);
	return (FALSE);
    }
    /*  Check if OK so far  */
    if (read (from_fd, &bool_val, sizeof bool_val) < sizeof bool_val)
    {
	fprintf (stderr,"Error reading response flag from PGPdaemon\t%s\n",
		 ERRSTRING);
	return (FALSE);
    }
    if (!bool_val)
    {
	fprintf (stderr,
		 "PGPdaemon refused to decrypt: probably has no passphrase\n");
	return (FALSE);
    }
    /*  Now have to copy stdin to PGPdaemon input and PGPdaemon output to
	stdout  */
    /*  Setup a child process. Parent copies stdin to PGPdaemon.
	Child copies PGPdaemon output to stdout  */
    switch ( child_pid = fork () )
    {
      case 0:
	/*  Child  */
	close (to_fd);
	if ( !copy_data (OUTPUT_FD, from_fd, FALSE) )
	{
	    fprintf (stderr, "Error copying data from PGPdaemon\n");
	    copy_data (OUTPUT_FD, INPUT_FD, FALSE);
	    exit (RV_UNDEF_ERROR);
	}
	/*  Done  */
	close (from_fd);
	exit (RV_OK);
	/*break;*/
      case -1:
	/*  Error  */
	fprintf (stderr, "Could not fork\t%s\n", ERRSTRING);
	return (FALSE);
	/*break;*/
      default:
	/*  Parent  */
	close (from_fd);
	break;
    }
    if ( !copy_data (to_fd, INPUT_FD, FALSE) )
    {
	fprintf (stderr, "Error copying data from PGPdaemon\n");
	return (FALSE);
    }
    close (to_fd);
    /*  Reap child  */
    waitpid (child_pid, &status, 0);
    if (status != 0)
    {
	fprintf (stderr, "pgppipe child exited with status: %d\n", status);
	return (FALSE);
    }
    return (TRUE);
}   /*  End Function binary_decrypt  */
