view uip/pgpshow.c @ 0:bce86c4163a3

Initial revision
author kono
date Mon, 18 Apr 2005 23:46:02 +0900
parents
children 441a2190cfae
line wrap: on
line source

/* pgpshow.c - PGP viewer */
#ifndef lint
static char ident[] = "@(#)$Id$";
#endif /* lint */

#include "../h/mh.h"
#include <stdio.h>
#include <errno.h>
#include <signal.h>
#include <sys/stat.h>
#ifdef UNISTD
#include <unistd.h>
#endif /* UNISTD */
#ifdef LOCALE
#include <locale.h>
#endif /* LOCALE */

void pgp_show();
void pgp_store();
void pgp_mime();
int  tee();
static TYPESIG goodbye();

/*  */

static struct swit switches[] = {
#define PGPPROC 0
    {"pgpproc program", -7},

#define SHOWSW 1
    {"show", 0},
#define NSHOWSW 2
    {"noshow", 0},

#define STORESW 3
    {"store", 0},
#define NSTORESW 4
    {"nostore", 0},

#define MIMESW 5
    {"mime", 0},
#define NMIMESW 6
    {"nomime", 0},

#define HELPSW 7
    {"help", 4},

    {NULL, 0}
};

/*  */

static int showsw = 0;
static int storesw = 0;
static int mimesw = 0;

static char *pgpproc = ""; /* "pgp"; */
static char *showprog = "show"; /* not showproc */
extern char *moreproc;

static char tmpfil1[BUFSIZ];
static char tmpfil2[BUFSIZ];
static FILE *out = NULL;

#define PGP_SIGN    1
#define PGP_ENCRYPT 2

#define PGP_VER2 (int)1
#define PGP_VER5 (int)2

#define PGP2_GOOD_SIGNATURE "Good signature from user"
#define PGP5_GOOD_SIGNATURE "Good signature made"

#define NOT_INSTALLED 127

/*  */

main(argc, argv)
int argc;
char *argv[];
{
    int i, paramp = 0;
    char *cp, buf[BUFSIZ];
    char **ap, **argp, *arguments[MAXARGS], *param[MAXARGS];

#ifdef LOCALE
    setlocale(LC_ALL, "");
#endif /* LOCALE */
#ifdef JAPAN
    ml_init();
#endif /* JAPAN */
    invo_name = r1bindex(argv[0], '/');
    if ((cp = m_find(invo_name)) != NULL) {
	ap = brkstring(cp = getcpy(cp), " ", "\n");
	ap = copyip(ap, arguments);
    }
    else
	ap = arguments;
    (void) copyip(argv + 1, ap);
    argp = arguments;

/*  */

    while ((cp = *argp++)) {
	if (*cp == '-')
	    switch (smatch(++cp, switches)) {
	    case AMBIGSW: 
		ambigsw(cp, switches);
		done(1);
	    case UNKWNSW: 
		adios(NULLCP, "-%s unknown", cp);
	    case HELPSW: 
		(void) sprintf(buf, "%s [switches] file .. [parameters]",
			       invo_name);
		help(buf, switches);
		done(1);

	    case SHOWSW:
		showsw++;
		continue;
	    case NSHOWSW:
		showsw = 0;
		continue;

	    case STORESW:
		storesw++;
		continue;
	    case NSTORESW:
		storesw = 0;
		continue;

	    case MIMESW:
		mimesw++;
		continue;
	    case NMIMESW:
		mimesw = 0;
		continue;

	    case PGPPROC:
		if (!(pgpproc = *argp++) || *pgpproc == '-')
		    adios(NULLCP, "missing argument to %s", argp[-2]);
		continue;
	    }
	else
	    param[paramp++] = cp;
    }
    param[paramp] = NULLCP;
    if ((!showsw && !storesw) || paramp < (mimesw ? 2 : 1))
	adios(NULLCP, "usage: %s [switches] file .. [parameters]", invo_name);
    tmpfil1[0] = tmpfil2[0] = '\0';

/*  */

    if (mimesw) {
	int protocol = 0;
	char *micalg = NULLCP;
	for (i = 2; param[i]; i++) {
	    if (uprf(param[i], "protocol=")) {
		if (uleq(param[i] + 9, "application/pgp-signature"))
		    protocol = PGP_SIGN;
		else if (uleq(param[i] + 9, "application/pgp-encrypted"))
		    protocol = PGP_ENCRYPT;
		else
		    adios(NULLCP,
			  "protocol \"%s\" is unsupported", param[i] + 9);
	    }
	    if (uprf(param[i], "micalg=")) {
		micalg = param[i] + 7;
		if (!uleq(micalg, "pgp-md5") && !uleq(micalg, "pgp-sha1"))
		    adios(NULLCP, "micalg \"%s\" is unsupported", micalg);
	    }
	}
	if (!protocol)
	    adios(NULLCP, "protocol parameter is required");

	pgp_mime(param[0], param[1], protocol, micalg, showsw);
    } else {
	int fmtmime = 0;
	for (i = 1; param[i]; i++) {
	    if (uleq(param[i], "format=mime")) {
		fmtmime = 1;
		break;
	    }
	}
	if (showsw)
	    pgp_show(param[0], fmtmime);
	else
	    pgp_store(param[0], fmtmime);
    }

    done(0);
}

void
pgp_show(file, fmtmime)
char *file;
int fmtmime;
{
    int pid, nopgp = 0;
    char *ap[5];

    (void) signal(SIGHUP, goodbye);
    (void) signal(SIGINT, goodbye);
    (void) signal(SIGQUIT, goodbye);
    (void) signal(SIGPIPE, goodbye);
    (void) signal(SIGTERM, goodbye);

    (void) strcpy(tmpfil1, m_scratch("", m_maildir(invo_name)));
    if ((out = fopen(tmpfil1, "w")) == NULL) {
	(void) strcpy(tmpfil1, m_tmpfil(invo_name));
	if ((out = fopen(tmpfil1, "w")) == NULL) {
	    advise(tmpfil1, "unable to create");
	    goodbye();
	}
    }
    (void) chmod(tmpfil1, 0600);
    fclose(out);
    out = NULL;
    unlink(tmpfil1);

    if (! *pgpproc) {
	/* for PGP5 */
	ap[0] = pgpproc = "pgpv";
	ap[1] = file;
	ap[2] = "-o";
	ap[3] = tmpfil1;
	ap[4] = NULLCP;
	if (tee(ap, PGP_VER5) == NOTOK)
	    /* Let's try old PGP */
	    pgpproc = "pgp";
	else
	    pgpproc = "";
    }
    if (*pgpproc) {
	/* for PGP2 */
	ap[0] = r1bindex(pgpproc, '/');
	if (tee(ap, PGP_VER2) == NOTOK) {
	    advise(pgpproc, "unable to exec");
	    nopgp = 1;
	}
    }

    if (fmtmime) {
	ap[0] = r1bindex(showprog, '/');
	ap[1] = "-file";
	ap[2] = nopgp ? file : tmpfil1;
	ap[3] = NULLCP;
    } else {
	ap[0] = r1bindex(moreproc, '/');
	ap[1] = nopgp ? file : tmpfil1;
	ap[2] = NULLCP;
    }

    switch (pid = fork()) {
    case NOTOK:
	advise("fork", "unable to");
	goodbye();

    case OK:
	execvp(fmtmime ? showprog : moreproc, ap);
	adios(fmtmime ? showprog : moreproc, "unable to exec");

    default:
	if (pidXwait(pid, fmtmime ? showprog : r1bindex(moreproc, '/')))
	    goodbye();
	break;
    }
    unlink(tmpfil1);
}

void
pgp_store(file, fmtmime)
char *file;
int fmtmime;
{
    int pid, state;
    char *ap[5];

    (void) signal(SIGHUP, goodbye);
    (void) signal(SIGINT, goodbye);
    (void) signal(SIGQUIT, goodbye);
    (void) signal(SIGPIPE, goodbye);
    (void) signal(SIGTERM, goodbye);

    (void) strcpy(tmpfil1, m_scratch("", m_maildir(invo_name)));
    if ((out = fopen(tmpfil1, "w")) == NULL) {
	(void) strcpy(tmpfil1, m_tmpfil(invo_name));
	if ((out = fopen(tmpfil1, "w")) == NULL) {
	    advise(tmpfil1, "unable to create");
	    goodbye();
	}
    }
    (void) chmod(tmpfil1, 0600);
    cpydata(fileno(stdin), fileno(out), "stdin", tmpfil1);
    fclose(out);
    out = NULL;

    if (! *pgpproc) {
	/* for PGP5 */
	ap[0] = "pgpv";
	ap[1] = tmpfil1;
	ap[2] = "-o";
	ap[3] = file;
	ap[4] = NULLCP;

	switch (pid = fork()) {
	case NOTOK:
	    advise("fork", "unable to");
	    goodbye();

	case OK:
	    execvp("pgpv", ap);
	    /* maybe PGP5 is not installed. */
	    _exit(NOT_INSTALLED);

	default:
#if 0
	    state = pidwait(pid, OK);
	    if (WIFEXITED(state) && WEXITSTATUS(state) == NOT_INSTALLED)
#else
	    if ((state = pidwait(pid, OK)) == (NOT_INSTALLED << 8))
#endif
		/* Let's try old PGP */
		pgpproc = "pgp";
	    else if (pidstatus(state, stdout, "pgpv"))
		goodbye();
	    break;
	}
    }
    if (*pgpproc) {
	/* for PGP2 */
	ap[0] = r1bindex(pgpproc, '/');

	switch (pid = fork()) {
	case NOTOK:
	    advise("fork", "unable to");
	    goodbye();

	case OK:
	    execvp(pgpproc, ap);
	    adios(pgpproc, "unable to exec");

	default:
	    if (pidXwait(pid, r1bindex(pgpproc, '/')))
		goodbye();
	    break;
	}
    }
    unlink(tmpfil1);

    if (fmtmime) {
	if (!m_find("path"))
	    free(path("./", TFOLDER));
	if (!(ap[0] = m_find(inbox)))
	    ap[0] = defalt;
	if (*ap[0] != '+' && *ap[0] != '@')
	    ap[0] = add(ap[0], add("+", NULLCP));
	ap[1] = NULL;
	refile(ap, file);
    }
}

void
pgp_mime(file1, file2, protocol, micalg, showsw)
char *file1, *file2, *micalg;
int protocol, showsw;
{
    int pid;
    char *cp, *ap[6], buf[BUFSIZ];
    FILE *in;

    (void) signal(SIGHUP, goodbye);
    (void) signal(SIGINT, goodbye);
    (void) signal(SIGQUIT, goodbye);
    (void) signal(SIGPIPE, goodbye);
    (void) signal(SIGTERM, goodbye);

    (void) strcpy(tmpfil1, m_scratch("", m_maildir(invo_name)));
    if ((out = fopen(tmpfil1, "w")) == NULL) {
	(void) strcpy(tmpfil1, m_tmpfil(invo_name));
	if ((out = fopen(tmpfil1, "w")) == NULL) {
	    advise(tmpfil1, "unable to create");
	    goodbye();
	}
    }
    (void) chmod(tmpfil1, 0600);

    if (protocol == PGP_SIGN) {
	if ((in = fopen(file1, "r")) == NULL) {
	    advise(file1, "unable to open");
	    goodbye();
	}

	while (fgets(buf, sizeof(buf) - 2, in)) {
	    cp = buf + strlen(buf) - 1;
	    if (*cp-- == '\n' && *cp != '\r') {
		*++cp = '\r';
		*++cp = '\n';
		*++cp = '\0';
	    }
	    fputs(buf, out);
	}
	fclose(in);
	fclose(out);
	out = NULL;

	ap[1] = "+batchmode=on";
	ap[2] = file2;
	ap[3] = "-o";
	ap[4] = tmpfil1;
	ap[5] = NULLCP;
    } else {
	fclose(out);
	out = NULL;
	unlink(tmpfil1);

	ap[1] = file2;
	ap[2] = "-o";
	ap[3] = tmpfil1;
	ap[4] = NULLCP;
    }
    if (! *pgpproc) {
	/* for PGP5 */
	ap[0] = pgpproc = "pgpv";
	if (tee(ap, PGP_VER5) == NOTOK) {
	    if (protocol == PGP_SIGN && !uleq(micalg, "pgp-md5")) {
		/* PGP2 only support PGP-MD5 */
		advise(pgpproc, "unable to exec");
		pgpproc = "";
	    } else
		/* Let's try old PGP */
		pgpproc = "pgp";
	} else
	    pgpproc = "";
    }
    if (*pgpproc) {
	/* for PGP2 */
	ap[0] = r1bindex(pgpproc, '/');
	if (protocol == PGP_SIGN) {
	    ap[3] = tmpfil1;
	    ap[4] = NULLCP;
	}
	if (tee(ap, PGP_VER2) == NOTOK) {
	    advise(pgpproc, "unable to exec");
	    if (protocol != PGP_SIGN)
		goodbye();
	}
    }

    ap[0] = r1bindex(showprog, '/');
    ap[1] = "-file";
    if (protocol == PGP_SIGN)
	ap[2] = file1;
    else {
	if ((in = fopen(tmpfil1, "r")) == NULL) {
	    advise(tmpfil1, "unable to open");
	    goodbye();
	}

	(void) strcpy(tmpfil2, m_scratch("", m_maildir(invo_name)));
	if ((out = fopen(tmpfil2, "w")) == NULL) {
	    (void) strcpy(tmpfil2, m_tmpfil(invo_name));
	    if ((out = fopen(tmpfil2, "w")) == NULL) {
		advise(tmpfil2, "unable to create");
		goodbye();
	    }
	}
	(void) chmod(tmpfil2, 0600);

	while (fgets(buf, sizeof(buf) - 1, in)) {
	    cp = buf + strlen(buf) - 1;
	    if (*cp-- == '\n' && *cp == '\r') {
		*cp++ = '\n';
		*cp = '\0';
	    }
	    fputs(buf, out);
	}
	fclose(in);
	fclose(out);
	out = NULL;

	ap[2] = tmpfil2;
    }
    ap[3] = NULLCP;
    unlink(tmpfil1);

    if (!showsw) {
	if (!m_find("path"))
	    free(path("./", TFOLDER));
	if (!(ap[0] = m_find(inbox)))
	    ap[0] = defalt;
	if (*ap[0] != '+' && *ap[0] != '@')
	    ap[0] = add(ap[0], add("+", NULLCP));
	ap[1] = NULL;
	refile(ap, ap[2]);
	return;
    }

    switch (pid = fork()) {
    case NOTOK:
	advise("fork", "unable to");
	goodbye();

    case OK:
	execvp(showprog, ap);
	adios(showprog, "unable to exec");

    default:
	if (pidXwait(pid, r1bindex(showprog, '/')))
	    goodbye();
	break;
    }
    if (tmpfil2[0])
	unlink(tmpfil2);
}

int
tee(ap, pgpver)
char *ap[];
int pgpver;
{
    int cnt, state, pid, pdes[2];
    char *cp, *ep, *np, buf[BUFSIZ];

    if (pipe(pdes) == NOTOK) {
	advise(NULLCP, "unable to open pipe");
	goodbye();
    }

    switch (pid = fork()) {
    case NOTOK:
	close(pdes[0]);
	close(pdes[1]);
	advise("fork", "unable to");
	goodbye();

    case OK:
	if (pdes[1] != fileno(stdout))
	    dup2(pdes[1], fileno(stdout));
	if (pdes[1] != fileno(stderr)) {
	    dup2(pdes[1], fileno(stderr));
	    if (pdes[1] != fileno(stdout))
		close(pdes[1]);
	}
	close(pdes[0]);
	execvp(pgpproc, ap);
	/* maybe PGP is not installed. */
	_exit(NOT_INSTALLED);
    }
    close(pdes[1]);

    cp = ep = buf;
    while ((cnt = read(pdes[0], buf + (ep - cp), sizeof(buf) - (ep - cp) - 1))
	   > 0) {
	write(fileno(stdout), buf + (ep - cp), cnt);

	ep = buf + (ep - cp) + cnt;
	cp = buf;
	while (cp < ep) {
	    np = cp;
	    while (np < ep && *np != '\n')
		np++;
	    if (np < ep || (cp == buf && np == buf + sizeof(buf) - 1)) {
		char *xp, *yp;
		*np = '\0';
		if ((pgpver == PGP_VER5
		     && strncmp(cp, PGP5_GOOD_SIGNATURE,
				strlen(PGP5_GOOD_SIGNATURE)) == 0)
		    ||
		    (pgpver == PGP_VER2
		     && strncmp(cp, PGP2_GOOD_SIGNATURE,
				strlen(PGP2_GOOD_SIGNATURE)) == 0)) {
		    pgpver = -1;
		}
		if (pgpver < 0
		    && (xp = index(cp, '"')) && (yp = rindex(xp + 1, '"'))) {
		    *yp = '\0';
		    m_putenv("PGP_SIGNATURE", xp + 1);
		    pgpver = 0;
		}
	    } else {
		bcopy(cp, buf, ep - cp);
		break;
	    }
	    cp = np + 1;
	}
    }
    close(pdes[0]);

#if 0
    state = pidwait(pid, OK);
    if (WIFEXITED(state) && WEXITSTATUS(state) == NOT_INSTALLED)
#else
    if ((state = pidwait(pid, OK)) == (NOT_INSTALLED << 8))
#endif
	return NOTOK;

    if (pidstatus(state, stdout, r1bindex(pgpproc, '/')))
	goodbye();

    return OK;
}

static TYPESIG goodbye()
{
    if (out)
	fclose(out);
    if (tmpfil1[0])
	unlink(tmpfil1);
    if (tmpfil2[0])
	unlink(tmpfil2);
    done(1);
    /* NOT REACHED */
}