/**
 **     $Filename: PGPRMail.c $
 **     $Revision: 1.24 $
 **     $Date: 1993/09/22 23:08:20 $
 **
 **     This is a replacement for the UUCP command >rmail<. PGPRMail
 **     reads the incoming message from standard input, and prints
 **     the plaintext to a pipe, calling the original rmail with this
 **     set to standard input.
 **
 **     PGP-encrypted parts of the e-mail will be piped to PGP and will
 **     be replaced with the plaintext.
 **
 **     © Copyright 1993 Peter Simons, Germany
 **       All Rights Reserved
 **
 **     $Id: PGPRMail.c,v 1.24 1993/09/22 23:08:20 simons Exp simons $
 **
 ** ------------------------------ log history ----------------------------
 ** $Log: PGPRMail.c,v $
 ** Revision 1.24  1993/09/22  23:08:20  simons
 ** BUG: PGPRMail doesn't really support buffer-overflow checking!!
 **      I changed the buffer to 10k size, however, this is nasty and
 **      needs to be fixed ASAP!
 **
 ** Revision 1.23  1993/09/22  23:04:47  simons
 ** Removed the ArrayToLine() routine, because it isn't needed anymore.
 **
 ** Revision 1.22  1993/09/21  17:32:56  simons
 ** Added more debug-output at startup-time.
 **
 ** Revision 1.21  1993/09/21  17:27:17  simons
 ** "my_ulog" is extended to several lines and therefore requires brackets
 ** around the actual commands or it will break certain statements.
 **
 ** Revision 1.20  1993/09/19  13:25:27  simons
 ** Another major bug found: Due to wrong placed brackets, a loop was
 ** not executed completly.
 **
 ** Revision 1.19  1993/09/18  18:08:52  simons
 ** PGPRMail recognized a missing parameter, but didn't terminate. :-)
 **
 ** Revision 1.18  1993/09/14  23:16:39  simons
 ** Added log message when PGPRMail was started without parameters.
 **
 ** Revision 1.17  1993/09/14  22:43:20  simons
 ** Now using my modified startup code for less overhead. Therefore had to
 ** change a few things in the source, no `real' modification.
 **
 ** Revision 1.16  1993/09/13  17:40:04  simons
 ** Added logfile support and various log- and debug-messages.
 **
 ** Revision 1.15  1993/09/11  21:58:49  simons
 ** The command started by PGPRMail has been renamed to 'rmail'.
 **
 ** Revision 1.14  1993/08/25  01:11:28  simons
 ** Mega bug fixed: PGPRMail did not check if the receipient of the mail
 ** is located on this system. Though, it tried to decrypt mail, that is
 ** routed via the home-system!!
 **
 ** The current fix is more like a workaround, because I do not know for
 ** shure, how this is to determine easily. (See comments in the source.)
 **
 ** Revision 1.13  1993/08/22  17:39:54  simons
 ** PGPRMail used to call rmail with argv[0] as parameter also. This has
 ** been fixed.
 ** Known bugs: - currently does not correct the "Lines:" keyword
 **
 ** Revision 1.12  1993/08/22  17:26:58  simons
 ** PGPRMail didn't support multiple copies of one mail. It does now. :-)
 **
 ** Revision 1.11  1993/08/21  15:32:45  simons
 ** Added define for the internal IO buffer size and set it to BUFSIZ*10,
 ** what seemed to be the best value in several tests.
 **
 ** Revision 1.10  1993/08/21  15:15:34  simons
 ** Added buffering to getline(), what should result in a major speedup.
 **
 ** Revision 1.9  1993/08/21  13:40:27  simons
 ** The first copying-process has been rewritten to use the low-level IO
 ** routines, what gained a lot of speed. (25 seconds on a 800k file!)
 **
 ** Revision 1.8  1993/08/16  21:51:05  simons
 ** removed some debugging output
 **
 ** Revision 1.7  1993/08/10  17:02:30  simons
 ** Changed the commandline PGP is called with.
 **
 ** Revision 1.6  1993/08/10  16:31:52  simons
 ** If an disk-full error occurs after the standard input has been read
 ** completely, PGPRMail doe not terminate, but call rmail with the
 ** original file as parameter.
 ** Data-loss seems impossible now!
 ** Status turned to stable.
 **
 ** Revision 1.5  1993/08/10  16:13:05  simons
 ** PGPRMail now returns the code, rmail returnes.
 ** A 10 (instead of 5) is returned for fatal errors.
 ** Added security checks, just in case the disk holding the temporary
 ** files is full.
 ** Changed getline(): EOF is now only returned if no more characters are
 **                    available. The previous version returned EOF is no
 **                    complete (\n terminated) line was available.
 **
 ** Revision 1.4  1993/08/10  02:51:39  simons
 ** Added RCS Id string to the executable.
 ** The temporary file for PGP messages is now deleted every time, a
 ** message has been decoded successfully. Otherwise PGP would have
 ** decoded the previous message again.
 **
 ** Revision 1.3  1993/08/10  02:34:31  simons
 ** Changed filename to PGPRMail.
 ** Replaced all pipes with 'ordinary' temporary files. This should increase
 ** the programs security and make it easier for me to handle errors.
 ** Added several error messages and security checks.
 ** PGPRMail recognizes damaged PGP message blocks.
 ** PGPRMail recognizes errorcodes returned by PGP.
 **
 ** Revision 1.2  1993/08/10  00:10:28  simons
 ** The program works just fine, but I do a minimum of error-checking.
 ** This is dangerous and may cause data loss in the current revision.
 **
 ** Revision 1.1  1993/08/09  22:33:14  simons
 ** Initial revision
 **/



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

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

#include "SC:Sources/uucp.lib/include/config.h"
#include "SC:Sources/uucp.lib/include/log.h"

/************************************* Defines ************/
#define PGP "PGP -f <%s >>%s +batchmode"
#define START_RMAIL "rmail <%s %s"

#define MSG_BEGIN "-----BEGIN PGP MESSAGE----"
#define MSG_END "-----END PGP MESSAGE----"

#define DECRYPT_BEGIN "==== begin of decrypted PGP message ====\n"
#define DECRYPT_END "==== end of decrypted PGP message ====\n"

#define LINEBUFFER 1024*10      /* max. line length */
#define IOBUFSIZE BUFSIZ*10     /* size of internal IO buffer */

#define my_ulog(level, name, msg) { OpenLog(); ulog(level, name, msg); CloseLog(); }
#define my_ulog2(level, msg) { OpenLog(); ulog(level, msg); CloseLog(); }

/************************************* Prototypes *********/
void error(char *, int);
int getline(FILE *,char buffer[]);
int CopyUntilEOF(int in, int out);

        static const char __RCSId[] = "$Id: PGPRMail.c,v 1.24 1993/09/22 23:08:20 simons Exp simons $";

/**************************************************************************
 *                                                                        *
 * Sektion: Hauptprogramm                                                 *
 *                                                                        *
 **************************************************************************/

int main(char *argv)
{
        FILE *pipe1, *pipe2, *file;
        int flag, i;
        char *line, PIPE1[L_tmpnam], PIPE2[L_tmpnam];
        char FILENAME[L_tmpnam], *debuglevel;


        /* Determine loglevel. */

        if ((debuglevel = FindConfig(DEBUGNAME)) == NULL)
                LogLevel = 0;
        else
                LogLevel = atoi(debuglevel);


        /* Tell the world we are here! */

        my_ulog(4, "PGPRMail, started with commandline `%s'", argv);
        my_ulog(4, "PGPRMail, loglevel is %d", LogLevel);


        /* Allocate internal buffer */

        if (!(line = malloc(LINEBUFFER)))
                error("failed allocating internal buffer", 20);


        /* Make unique filenames for the temporary files and open them. */

        tmpnam(PIPE1);
        tmpnam(PIPE2);
        tmpnam(FILENAME);

        if ((pipe1 = fopen(PIPE1, "w")) == NULL)
                error("Can't open temporary file!", 10);
        if ((pipe2 = fopen(PIPE2, "w")) == NULL)
                error("Can't open temporary file!", 10);
        if ((file = fopen(FILENAME, "w+")) == NULL)
                error("Can't open temporary file!", 10);


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

        if (CopyUntilEOF(fileno(stdin), fileno(file)) == NULL)
                error("Can't write to my temporary file!", 10);
        my_ulog(2, "PGPRMail, original mail in '%s'", FILENAME);


        /*
         * Check is the receipient of the mail is on this system!!
         *
         * The specified addresses MUST NOT contain a '!' or '@', because
         * this means that the mail will be routed to another system.
         * Hence, we must not touch this message!
         */

        if ((flag = strlen(argv)) > 0) {
                for (i = 0; i < flag; i++) {
                        if (argv[i] == '!' || argv[i] == '@') {
                                my_ulog2(1, "PGPRMail, mail is routed to another machine and therefore not decrypted");
                                flag = 0;
                                break;
                        }
                }
        }
        else
                my_ulog2(1, "PGPRMail, no parameters provided --> TERMINATED");

        /* Browse through the file for PGP messages. */

        if (flag > 0) {
                fseek(file, 0L, SEEK_SET);
                while (flag != EOF) {
                        if ((flag = getline(file, line)) != EOF) {
                                if (!strncmp(MSG_BEGIN, line, strlen(MSG_BEGIN))) {
                                        my_ulog(3, "PGPRMail, found \"%s\"", MSG_BEGIN);
                                        if ((fprintf(pipe1, DECRYPT_BEGIN)) < 0) {
                                                flag = 0;
                                                break;
                                        }
                                        my_ulog(2, "PGPRMail, modified mail in '%s'", PIPE1);
                                        if ((fprintf(pipe2, "%s", line)) < 0) {
                                                flag = 0;
                                                break;
                                        }
                                        if ((flag = getline(file, line)) == EOF) {
                                                flag = 0;
                                                break;
                                        }
                                        while (strncmp(MSG_END, line, strlen(MSG_END))) {
                                                if ((fprintf(pipe2, "%s", line)) < 0) {
                                                        flag = 0;
                                                        break;
                                                }
                                                if ((flag = getline(file, line)) == EOF) {
                                                        flag = 0;
                                                        break;
                                                }
                                        }
                                        my_ulog(3, "PGPRMail, found \"%s\"", MSG_END);
                                        my_ulog(2, "PGPRMail, encrypted block in '%s'", PIPE2);

                                        /*
                                         * If an EOF occured during the PGP message
                                         * scan, the message seems to be damaged.
                                         * Break here and use the original!
                                         */

                                        if (flag == 0)
                                                break;


                                        /* Everything is fine! Call PGP. */

                                        if ((fprintf(pipe2, "%s", line)) < 0) {
                                                flag = 0;
                                                break;
                                        }
                                        sprintf(line, PGP, PIPE2, PIPE1);
                                        fprintf(stderr, "%s\n", line);
                                        fclose(pipe1); pipe1 = NULL;
                                        fclose(pipe2); pipe2 = NULL;
                                        my_ulog(1, "PGPRMail, starting '%s'", line);
                                        if (system(line)) {
                                                my_ulog2(1, "PGPRMail, PGP failed somehow");
                                                flag = 0;
                                                break;
                                        }

                                        if ((pipe1 = fopen(PIPE1, "a")) == NULL)
                                                break;


                                        /* Do not append new data to pipe2!! */

                                        if ((pipe2 = fopen(PIPE2, "w")) == NULL)
                                                break;
                                        if ((fprintf(pipe1, DECRYPT_END)) < 0) {
                                                flag = 0;
                                                break;
                                        }
                                        my_ulog2(2, "PGPRMail, decrypted text joined to mail-body");
                                }
                                else
                                        if ((fprintf(pipe1, "%s", line)) < 0) {
                                                flag = 0;
                                                break;
                                        }
                        }
                }
        }

        /* Close temporary files, so rmail can open them. */

        fclose(pipe1);
        fclose(pipe2);
        fclose(file);


        /*
         * If the loop has been interrupted due to an error, use
         * the original file for input.
         *
         * If everything went fine, pipe the generated textfile.
         */

        if (flag == 0 || pipe1 == NULL || pipe2 == NULL) {
                fprintf(stderr, "PGPRMail: Nonfatal error occured!\n");
                my_ulog2(-1, "PGPRMail, decrypting process failed");
                sprintf(line, START_RMAIL, FILENAME, argv);
                my_ulog(2, "PGPRMail, starting '%s'", line);
                flag = system(line);
        }
        else {
                sprintf(line, START_RMAIL, PIPE1, argv);
                my_ulog(2, "PGPRMail, starting '%s'", line);
                flag = system(line);
        }

        /* Delete temporary files. */

        remove(PIPE1);
        remove(PIPE2);
        remove(FILENAME);

        return flag;    /* flag contains the return code of rmail */
}


/**************************************************************************
 *                                                                        *
 *                              Unterprogramme                            *
 *                                                                        *
 **************************************************************************/

void error(char *message, int errorcode)
{
        my_ulog(-1, "PGPRMail, %s", message);
        fprintf(stderr, "PGPRMail: %s\n", message);
        exit(errorcode);
}

int getline(FILE *file, char *buffer)
{
        int i = 0;
        static char *gl_buffer = NULL;
        static int counter = 0, len = 0;

        /* If allocating buffer fails, exit immediatly. */

        if (gl_buffer == NULL)
                if ((gl_buffer = malloc(IOBUFSIZE)) == NULL)
                        error("Can't allocate my buffers!", 10);


        /*
         * Copy buffered characters until buffer is empty or and
         * end-of-line is reached.
         */

        while (1) {             /* Forever!*/
                while (counter < len) {
                        i++;
                        if ((*buffer++ = gl_buffer[counter++]) == '\n')
                                break;
                }

                if (counter < len) {
                        *buffer = '\0';         /* Everything went fine. */
                        return i;
                }


                /* Buffer went out of data before end-of-line was found. */

                if ((len = read(fileno(file), gl_buffer, IOBUFSIZE)) <= 0)
                        if (i > 0) {
                                *buffer = '\0';
                                return i;
                        }
                        else
                                return EOF;

                counter = 0;
        }
}

int CopyUntilEOF(int in, int out)
{
        char *buffer;
        int len;

        if ((buffer = malloc(IOBUFSIZE)) == NULL)
                return 0;

        while ((len = read(in, buffer, IOBUFSIZE)) > 0)
                if (write(out, buffer, len) == -1) {
                        free(buffer);
                        return 0;
                }

        free(buffer);
        return 1;
}

