/*
 *      $Filename: pgpsendmail.c $
 *      $Revision: 1.45 $
 *      $Date: 1994/03/21 14:54:52 $
 *
 *      Copyright (C) 1993 by Peter Simons <simons@peti.GUN.de>
 *
 *      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.
 *
 *      $Id: pgpsendmail.c,v 1.45 1994/03/21 14:54:52 simons Exp simons $
 *
 */


/**************************************************************************
 *                                                                        *
 * Section: Macros, Definitions, Includes, Structures                     *
 *                                                                        *
 **************************************************************************/

/************************************* Includes ***********/
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <string.h>
#include <ctype.h>

#include <exec/execbase.h>

#include "pgpsendmail.h"                /* standard defines */
#include "protos.h"                     /* protoypes of all available routines */

#include "pgpsendmail_rev.h"            /* bumprevision stuff (AmigaOS only) */

/************************************* Prototypes *********/
void my_exit(void);
void sendmail(void);

/************************************* Defines ************/
#define PGP             "PGP -fesa <%s >%s %s +batchmode"
#define PGPKEYLIST      "UULib:PGPKeys.lst"

#define BODY_ID         "Automatically encrypted message-body follows:\n\n"

/************************************* global Variables ***/
static const char __OSVer[] = VERSTAG;
static const char __RCSId[] = "$Id: pgpsendmail.c,v 1.45 1994/03/21 14:54:52 simons Exp simons $";
char PRGNAME[] = "PGPSendmail";

struct NetSupportLibrary *NetSupportBase;
FILE *mailfh, *tmpfh;
char mailfilename[L_tmpnam], tmpfilename[L_tmpnam], encryptedfilename[L_tmpnam], *argline, *tmpbuf;
int exitPipe = 1;

/**************************************************************************
 *                                                                        *
 * Section: main program                                                  *
 *                                                                        *
 **************************************************************************/

int main(int argc, char **argv)
{
        extern struct ExecBase *SysBase;
        BPTR pipe, tmp_pipe;
        char **receipients, *cmdbuf, *header, *tmp, *ptr;
        int rc;


        /*
         * Initialize the requires resources and return if something fails!
         */

        if (SysBase->LibNode.lib_Version < 37) {
                fprintf(stderr, "%s: Kickstart 2.04 or later is required!", PRGNAME);
                return 20;
        }
        if (atexit(my_exit)) {
                fprintf(stderr, "%s: Could not install my exit handler!\n", PRGNAME);
                return 20;
        }
        if (!(NetSupportBase = (struct NetSupportLibrary *) OpenLibrary("netsupport.library", 0L))) {
                fprintf(stderr, "%s: Failed opening NetSupport.Library!", PRGNAME);
                return 20;
        }
        if (!(cmdbuf = malloc(MAX_CMDLINELEN)) || (!(tmpbuf = malloc(MY_BUFLEN)))) {
                MakeLogEntry(PRGNAME, MLE_FATAL_ERROR, "Failed allocating my buffers!");
                return 20;
        }
        tmpnam(mailfilename);
        tmpnam(tmpfilename);
        tmpnam(encryptedfilename);
        argline = GetOriginalCmdLine(argc, argv);       /* Re-build the original commandline, because
                                                         * the real sendmail will need it later.
                                                         */

        /*
         * Copy standard input to a temporary file for security reasons.
         */

        if (!(mailfh = fopen(mailfilename, "w"))) {
                MakeLogEntry(PRGNAME, MLE_FATAL_ERROR, "Failed opening %s!", mailfilename);
                return 20;
        }
        while (fgets(tmpbuf, MY_BUFLEN, stdin)) {
                if (fputs(tmpbuf, mailfh) == EOF) {
                        MakeLogEntry(PRGNAME, MLE_FATAL_ERROR, "Can't write to %s!", mailfilename);
                        return 20;
                }
        }
        fclose(mailfh); mailfh = NULL;
        MakeLogEntry(PRGNAME, MLE_DEBUG2, "original mail is now in '%s'.", mailfilename);


        /*
         * Now that the mail is safe, install an exit handler that pipes the
         * file to sendmail when we exit, to make sure the mail will not be
         * lost under error condition.
         */

        if (atexit(sendmail)) {
                fprintf(stderr, "%s: Could not install my exit handler!\n", PRGNAME);
                return 20;
        }



        /*
         * Load the mail header for RFC routines.
         */

        if (!(header = malloc(10*1024))) {      /* allocate 10k for the mail header */
                MakeLogEntry(PRGNAME, MLE_ERROR, "Failed allocating my buffers!");
                return 10;
        }
        if (!(mailfh = fopen(mailfilename, "r"))) {
                MakeLogEntry(PRGNAME, MLE_ERROR, "Failed opening %s!", mailfilename);
                return 10;
        }
        tmp = header;
        while (fgets(tmp, MY_BUFLEN, mailfh)) {
                if (strlen(tmp) == 1)           /* Have we reached the mail body? */
                        break;
                tmp += strlen(tmp);
        }



        /*
         * Determine receipients to decide whom to encrypt to.
         */

        receipients = FindReceipients(header);
        free(header);                                   /* Header isn't needed anymore. */

        if (!receipients || (!*receipients)) {
                MakeLogEntry(PRGNAME, MLE_ERROR, "Can't determine receipient of message.");
                return 10;
        }
        else
                MakeLogEntry(PRGNAME, MLE_DEBUG2, "receipients (including aliases): %s", ArrayToLine(receipients));
        receipients = ExpandAliases(receipients);
        if (!receipients || (!*receipients)) {
                MakeLogEntry(PRGNAME, MLE_ERROR, "Can't determine receipient of message.");
                return 10;
        }
        else
                MakeLogEntry(PRGNAME, MLE_DEBUG2, "receipients (aliases expanded): %s", ArrayToLine(receipients));
        MakeDomainAddress(receipients);



        /*
         * Check wether all keys are available or not.
         */

        if ((CheckKeyAvailability(receipients)) == 0) {
                MakeLogEntry(PRGNAME, MLE_DEBUG2, "Not all receipients have a valid public key.");
                return 0;
        }



        /*
         * Ask user wether this mail should be encrypted or not.
         */

        ptr = GetConfig(NULL, CONFIRMENCRYPTION, NULL, "y");
        if (ptr[0] == 'y' || ptr[0] == 'Y')
                if (!ConfirmEncryption()) {
                        MakeLogEntry(PRGNAME, MLE_INFO, "Encryption denied by user.");
                        return 0;
                }


        /*
         * Copy messagebody for encryption.
         */

        if (!(tmpfh = fopen(tmpfilename, "w"))) {
                MakeLogEntry(PRGNAME, MLE_ERROR, "Failed opening %s!", tmpfilename);
                return 5;
        }
        while (fgets(tmpbuf, MY_BUFLEN, mailfh)) {      /* The fileposition is at the begin of the
                                                         * mailbody because we read the header earlier.
                                                         */
                if (fputs(tmpbuf, tmpfh) == EOF) {
                        MakeLogEntry(PRGNAME, MLE_ERROR, "Can't write to %s!", tmpfilename);
                        return 5;
                }
        }
        fclose(mailfh); mailfh = NULL;
        fclose(tmpfh); tmpfh = NULL;



        /*
         * Encrypt message body.
         */

        sprintf(tmpbuf, "PGP -ftea <%s >%s %s", tmpfilename, encryptedfilename, ArrayToLine(receipients));
        MakeLogEntry(PRGNAME, MLE_DEBUG1, "calling '%s'", tmpbuf);
        if (rc = system(tmpbuf)) {
                MakeLogEntry(PRGNAME, MLE_ERROR, "PGP returned error code %ld", rc);
                return 5;
        }
        else
                MakeLogEntry(PRGNAME, MLE_INFO, "succesfully encrypted the mail for %s", ArrayToLine(receipients));

        remove(tmpfilename);            /* isn't required anymore */



        /*
         * Call SendMail to handle the message.
         */

        sprintf(tmpbuf, "%s %s", FindConfig(SENDMAIL), argline);
        MakeLogEntry(PRGNAME, MLE_DEBUG1, "Calling '%s'", tmpbuf);

        /*
         * PORT NOTE: Sorry, but I need POpen() again. To make this stuff work on
         * another platform, you'll have to re-write it completely.
         */

        if (pipe = POpen(tmpbuf, MODE_PIPETO)) {


                /* Pipe the mail header to sendmail. */

                if (tmp_pipe = Open(mailfilename, MODE_OLDFILE)) {
                        while (FGets(tmp_pipe, tmpbuf, MY_BUFLEN)) {
                                FPuts(pipe, tmpbuf);
                                if (strlen(tmpbuf) == 1)        /* header is copied completely */
                                        break;
                        }
                        Close(tmp_pipe);


                        /* Pipe the encrypted mail body to sendmail. */

                        FPuts(pipe, BODY_ID);
                        if (tmp_pipe = Open(encryptedfilename, MODE_OLDFILE)) {
                                while (FGets(tmp_pipe, tmpbuf, MY_BUFLEN))
                                        FPuts(pipe, tmpbuf);
                                Close(tmp_pipe);
                        }

                        exitPipe = 0;   /* The mail is sent already, we don't need the sendmail()
                                         * exit handler anymore.
                                         */
                }

                rc = PClose(pipe);
        }
        else {
                MakeLogEntry(PRGNAME, MLE_ERROR, "Can't send the encrypted mail!");
                return 5;
        }

        return rc;
}


/**************************************************************************
 *                                                                        *
 * SECTION: Subroutines                                                   *
 *                                                                        *
 **************************************************************************/

 /*
  * This exit handler will free all resources allocated by the program
  * when we exit to make sure nothing is wasted under error condition.
  *
  * PORT NOTE: Contains system-dependant stuff!
  */

void my_exit(void)
{
        /* Close all open files and delete them afterwards. */

        if (mailfh)
                fclose(mailfh);
        if (tmpfh)
                fclose(tmpfh);

        remove(mailfilename);
        remove(tmpfilename);
        remove(encryptedfilename);


        /* Free all locked files and close the netsupport.library. */

        if (NetSupportBase) {
                UnLockFiles();
                CloseLibrary((struct Library *) NetSupportBase);
        }
}


 /*
  * Pipe the mailfile to sendmail when we exit.
  *
  * PORT NOTE: Because the AmigaOS doen't know popen(), we have to use
  * the routine from the netsupport.library. For other operating systems
  * it will be necessary to replace the whole routine!
  */

void sendmail(void)
{
        BPTR pipe, fh;
        int len;

        if (!exitPipe)
                return;

        sprintf(tmpbuf, "%s %s", FindConfig(SENDMAIL), argline);
        MakeLogEntry(PRGNAME, MLE_DEBUG1, "Calling '%s'", tmpbuf);
        if (pipe = POpen(tmpbuf, MODE_PIPETO)) {

                /* If the file holding the mail is still open, close it first
                 * to make sure we can access it.
                 */

                if (mailfh) {
                        fclose(mailfh); mailfh = NULL;
                }

                /* Pipe the whole file to sendmail. */

                if (fh = Open(mailfilename, MODE_OLDFILE)) {
                        while (len = Read(fh, tmpbuf, MY_BUFLEN-1)) {
                                if (len == -1L)
                                        break;          /* Read() failed */
                                if (Write(pipe, tmpbuf, len) == -1L)
                                        break;          /* Write() failed */
                        }
                        Close(fh);
                }
                else
                        MakeLogEntry(PRGNAME, MLE_FATAL_ERROR, "Couldn't open mailfile! Mail is lost!!");
                PClose(pipe);
        }
        else
                MakeLogEntry(PRGNAME, MLE_FATAL_ERROR, "Couldn't open pipe to sendmail! Mail is lost!!");
}



char *ArrayToLine(char *array[])
{
        char *line;

        if (!(line = malloc(MY_BUFLEN)))
                return NULL;
        line[0] = '\0';

        while (*array != NULL) {
                strcat(line, *array);
                strcat(line, " ");
                array++;
        }
        return line;
}


int CheckKeyAvailability(char *receipients[])
{
        FILE *fh;
        char *keylist;
        int filelength;

        if (*receipients == NULL)
                return 0;               /* No receipient found! */

        LockFile(PGPKEYLIST);

        if ((fh = fopen(PGPKEYLIST, "r")) == NULL) {
                UnLockFile(PGPKEYLIST);
                return 0;               /* Couldn't open keylist! */
        }
        fseek(fh, 0L, SEEK_END); filelength = ftell(fh);
                                        /* Determine filelength. */


        /* Allocate buffer and read keylist. */

        if (keylist = malloc(filelength+1)) {
                lseek(fileno(fh), 0, 0);
                if ((read(fileno(fh), keylist, filelength)) == -1) {
                        fclose(fh);     /* Couldn't read keylist. */
                        UnLockFile(PGPKEYLIST);
                        free(keylist);
                        return 0;
                }
                else {          /* Read went fine. */
                        fclose(fh);
                        UnLockFile(PGPKEYLIST);
                        keylist[filelength] = '\0';

                        while (*receipients != NULL) {
                                if (FindKey(*receipients, keylist) == 0) {
                                        free(keylist);
                                        MakeLogEntry(PRGNAME, MLE_DEBUG1, "No key available for <%s>.", *receipients);
                                        return 0;       /* Key is not in keylist. */
                                }
                                receipients++;
                        }
                }
        }
        free(keylist);
        return 1;                       /* Everything is fine. */
}

int FindKey(char *key, char *keylist)
{
        char line[MY_BUFLEN];
        int i;

        while (*keylist != '\0') {

                /* Is this a valid key-line? */

                if (!strnicmp(keylist, "pub ", strlen("pub "))
                        || !strnicmp(keylist, "    ", strlen("    ")))
                {
                        for(i = 0; keylist[i] != '\n'; i++)
                                line[i] = keylist[i];
                        line[i] = '\0';
                        if (stristr(line, key))
                                return 1;
                }


                /* Find end of line or end of buffer. */

                while (*keylist != '\n' && *keylist != '\0')
                        keylist++;
                keylist++;
        }
        return 0;
}

char *stristr(char *line, char *key)
{
        int i, cmplen = strlen(key), end = strlen(line)-cmplen;

        for (i = 0; i <= end; i++)
                if (strnicmp(&line[i], key, cmplen) == NULL)
                        return &line[i];
        return NULL;
}

