Mercurial > hg > Applications > mh
view uip/pgped.c @ 4:6bc439d68ff9 utf-8-support
*** empty log message ***
author | kono |
---|---|
date | Wed, 20 Apr 2005 14:39:40 +0900 |
parents | bce86c4163a3 |
children | 441a2190cfae |
line wrap: on
line source
/* pgped.c - PGP editor */ #ifndef lint static char ident[] = "@(#)$Id$"; #endif /* lint */ #include "../h/mh.h" #include "../h/mhn.h" #include "../h/addrsbr.h" #include "../h/aliasbr.h" #include <stdio.h> #include <ctype.h> #include <signal.h> #include <sys/stat.h> #ifdef UNISTD #include <unistd.h> #endif /* UNISTD */ #ifdef LOCALE #include <locale.h> #endif /* LOCALE */ struct addrlist { char *address; struct addrlist *next; }; struct addrlist *add_addrlist(); int exec_pgp5_for_keyid(); char *get_micalg(); static TYPESIG goodbye(); /* */ static struct swit switches[] = { #define ALIASW 0 {"alias aliasfile", 0}, #define MHNPROC 1 {"mhnproc program", 0}, #define PGPPROC 2 {"pgpproc program", -7}, #define SIGNSW 3 {"sign", 0}, #define NSIGNSW 4 {"nosign", 0}, #define CRYPTSW 5 {"encrypt", 0}, #define NCRPTSW 6 {"noencrypt", 0}, #define MIMESW 7 {"mime", 0}, #define NMIMESW 8 {"nomime", 0}, #define HELPSW 9 {"help", 4}, {NULL, 0} }; /* */ static int signsw = 1; static int encryptsw = 1; static int mimesw = 1; static char *mhnproc = "mhn"; static char *pgpproc = ""; /* "pgp"; */ static char prefix[] = "----- =_aaaaaaaaaa"; static char tmpfil1[BUFSIZ]; static char tmpfil2[BUFSIZ]; static char tmpfil3[BUFSIZ]; static FILE *out1 = NULL; static FILE *out2 = NULL; static struct addrlist *addrs = NULL; static struct addrlist *from_addrs = NULL; static struct addrlist *resent_addrs = NULL; char *addr_headers[] = { "Reply-To", "From", "Sender", "To", "Cc", "Bcc", "Dcc", NULLCP }; #define NOT_INSTALLED 127 /* */ main(argc, argv) int argc; char *argv[]; { int i, cnt, pid, tomhn, mimehdr, resent, compnum, state; char *cp, *file = NULL, *keyid, buf[BUFSIZ], name[NAMESZ]; char **ap, **argp, *arguments[MAXARGS]; FILE *in; struct addrlist *ip; #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", invo_name); help(buf, switches); done(1); case ALIASW: if (!(cp = *argp++) || *cp == '-') adios(NULLCP, "missing argument to %s", argp[-2]); if ((i = alias(cp)) != AK_OK) adios(NULLCP, "aliasing error in %s - %s", cp, akerror(i)); continue; case SIGNSW: signsw++; continue; case NSIGNSW: signsw = 0; continue; case CRYPTSW: encryptsw++; continue; case NCRPTSW: encryptsw = 0; continue; case MIMESW: mimesw++; continue; case NMIMESW: mimesw = 0; continue; case MHNPROC: if (!(mhnproc = *argp++) || *mhnproc == '-') adios(NULLCP, "missing argument to %s", argp[-2]); continue; case PGPPROC: if (!(pgpproc = *argp++) || *pgpproc == '-') adios(NULLCP, "missing argument to %s", argp[-2]); continue; } else { if (file) adios(NULLCP, "only one file at a time!"); else file = cp; } } /* for compatibility to "pgped.sh" */ if (argc == 3 && strcmp(argv[1], "-sign") == 0 && *argv[2] != '-') encryptsw = 0; /* */ if ((cp = m_find("Aliasfile"))) { /* allow Aliasfile: profile entry */ char *dp = NULL; for (ap = brkstring(dp = getcpy(cp), " ", "\n"); ap && *ap; ap++) if ((i = alias(*ap)) != AK_OK) adios (NULLCP, "aliasing error in %s - %s", *ap, akerror(i)); if (dp) free(dp); } (void) alias(AliasFile); if (!file || (!mimesw && !signsw && !encryptsw)) adios(NULLCP, "usage: %s [switches] file", invo_name); /* */ if (mimesw) { if ((in = fopen(file, "r")) == NULL) adios(file, "unable to open"); tomhn = 1; state = FLD; while ((state = m_getfld(state, name, buf, sizeof(buf), in)) == FLD || state == FLDPLUS || state == FLDEOF) { if (uleq(name, VRSN_FIELD)) { tomhn = 0; break; } } fclose(in); if (tomhn) { if (!getenv("mhdraft")) m_putenv("mhdraft", file); switch (pid = fork()) { case NOTOK: adios("fork", "unable to"); case OK: execlp(mhnproc, r1bindex(mhnproc, '/'), "-nocache", "-noshow", "-nostore", file, NULLCP); adios(mhnproc, "unable to exec"); default: if (pidXwait(pid, r1bindex(mhnproc, '/'))) done(1); break; } } if (!signsw && !encryptsw) done(0); } else tomhn = 0; /* */ if ((in = fopen(file, "r")) == NULL) adios(file, "unable to open"); tmpfil1[0] = tmpfil2[0] = tmpfil3[0] = '\0'; (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 ((out1 = fopen(tmpfil1, "w")) == NULL) { (void) strcpy(tmpfil1, m_tmpfil(invo_name)); if ((out1 = fopen(tmpfil1, "w")) == NULL) { advise(tmpfil1, "unable to create"); goodbye(); } } (void) chmod(tmpfil1, 0600); (void) strcpy(tmpfil2, m_scratch("", m_maildir(invo_name))); if ((out2 = fopen(tmpfil2, "w")) == NULL) { (void) strcpy(tmpfil2, m_tmpfil(invo_name)); if ((out2 = fopen(tmpfil2, "w")) == NULL) { advise(tmpfil2, "unable to create"); goodbye(); } } (void) chmod(tmpfil2, 0600); /* */ mimehdr = resent = 0; for (compnum = 1, state = FLD;;) { switch (state = m_getfld(state, name, buf, sizeof(buf), in)) { case FLD: case FLDPLUS: case FLDEOF: compnum++; cp = add(buf, NULLCP); while (state == FLDPLUS) { state = m_getfld(state, name, buf, sizeof(buf), in); cp = add(buf, cp); } if (uleq(name, VRSN_FIELD)) continue; if (uprf(name, XXX_FIELD_PRF)) { if (mimesw) { char *pp; pp = cp + strlen(cp); if (*--pp == '\n') *pp = '\0'; if (*--pp == '\r') *pp = '\0'; fprintf(out2, "%s:%s\r\n", name, cp); } else { mimehdr = 1; fprintf(out2, "%s:%s", name, cp); } } else { char **h; if (uprf(name, "Resent-")) { for (h = addr_headers; *h; h++) if (uleq(name+7, *h)) break; if (*h) resent_addrs = add_addrlist(resent_addrs, cp); if (uleq(name+7, "To")) /* Resent-To */ resent = 1; } else { for (h = addr_headers; *h; h++) if (uleq(name, *h)) break; if (*h) addrs = add_addrlist(addrs, cp); if (uleq(name, "From")) from_addrs = add_addrlist(from_addrs, cp); } fprintf (out1, "%s:%s", name, cp); } free(cp); continue; case BODY: case BODYEOF: (void) fseek(in, (long) (-strlen(buf)), 1); break; case FILEEOF: advise(NULLCP, "draft has empty body -- no directives!"); goodbye(); case LENERR: case FMTERR: advise(NULLCP, "message format error in component #%d", compnum); goodbye(); default: advise(NULLCP, "getfld() returned %d", state); goodbye(); } break; } if (mimesw) fputs("\r\n", out2); else if (mimehdr) fputs("\n", out2); /* */ fprintf (out1, "%s: %s\n", VRSN_FIELD, VRSN_VALUE); if (mimesw) { int badprefix, len = strlen(prefix); long pos; char *pp; if ((pp = index(prefix, 'a')) == NULL) { advise(NULLCP, "internal error"); goodbye(); } pos = ftell(in); badprefix = 0; 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, out2); if (badprefix || buf[0] != '-' || buf[1] != '-') continue; for (cp = buf + strlen(buf) - 1; cp >= buf; cp--) if (!isspace(*cp)) break; *++cp = '\0'; if (strncmp(buf + 2, prefix, len) == 0 && isdigit(buf[2 + len])) badprefix = 1; } while (badprefix) { if (*pp < 'z') (*pp)++; else if (*++pp == 0) { advise(NULLCP, "giving up trying to find a unique delimiter string" ); goodbye(); } else (*pp)++; fseek(in, pos, 0); badprefix = 0; while (fgets(buf, sizeof(buf) - 1, in)) { if (buf[0] != '-' || buf[1] != '-') continue; for (cp = buf + strlen(buf) - 1; cp >= buf; cp--) if (!isspace(*cp)) break; *++cp = '\0'; if (strncmp(buf + 2, prefix, len) == 0 && isdigit(buf[2 + len])) { badprefix = 1; break; } } } if (encryptsw) { fprintf(out1, "%s: multipart/encrypted; ", TYPE_FIELD); fprintf(out1, "protocol=\"application/pgp-encrypted\";\n"); fprintf(out1, "\tboundary=\"%s%d\"\n\n", prefix, 0); fprintf(out1, "--%s%d\n", prefix, 0); fprintf(out1, "%s: application/pgp-encrypted\n", TYPE_FIELD); fprintf(out1, "%s: 7bit\n\nVersion: 1\n\n", ENCODING_FIELD); fprintf(out1, "--%s%d\n", prefix, 0); fprintf(out1, "%s: application/octet-stream\n", TYPE_FIELD); fprintf(out1, "%s: 7bit\n\n", ENCODING_FIELD); } } else { while ((cnt = fread(buf, sizeof(char), sizeof(buf), in)) > 0) fwrite(buf, sizeof(char), cnt, out2); if (mimehdr) { fprintf(out1, "%s: application/pgp; format=mime\n\n", TYPE_FIELD); } else { if (encryptsw) fprintf(out1, "%s: application/pgp\n\n", TYPE_FIELD); else fprintf(out1, "%s: text/pgp\n\n", TYPE_FIELD); } } fclose(in); fclose(out2); out2 = NULL; /* */ (void) strcpy(tmpfil3, m_scratch("", m_maildir(invo_name))); if ((out2 = fopen(tmpfil3, "w")) == NULL) { (void) strcpy(tmpfil3, m_tmpfil(invo_name)); if ((out2 = fopen(tmpfil3, "w")) == NULL) { advise(tmpfil3, "unable to create"); goodbye(); } } (void) chmod(tmpfil3, 0600); fclose(out2); unlink(tmpfil3); if (resent) addrs = resent_addrs; i = 0; for (ip = addrs; ip; ip = ip->next) i++; if ((ap = (char **) malloc(sizeof(char *) * (i * 2 + 9))) == NULL) { advise(NULLCP, "out of memory"); goodbye(); } /* */ if (! *pgpproc) { /* for PGP5 */ ap[i = 0] = pgpproc = encryptsw ? "pgpe" : "pgps"; ap[++i] = encryptsw ? (signsw ? "-sat" : "-at") : (mimesw ? "-ab" : "-at"); ap[++i] = "+language=en"; ap[++i] = "+armorlines=0"; if (encryptsw) for (ip = addrs; ip; ip = ip->next) { ap[++i] = "-r"; ap[++i] = ip->address; } if (signsw && from_addrs) { ap[++i] = "-u"; ap[++i] = from_addrs->address; } ap[++i] = tmpfil2; ap[++i] = "-o"; ap[++i] = tmpfil3; ap[++i] = NULLCP; if (!mimesw || encryptsw) { switch (pid = fork()) { case NOTOK: advise("fork", "unable to"); goodbye(); case OK: execvp(pgpproc, 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, pgpproc)) goodbye(); else pgpproc = ""; break; } } else if (exec_pgp5_for_keyid(pgpproc, ap, &keyid) == NOTOK) /* Let's try old PGP */ pgpproc = "pgp"; else pgpproc = ""; } if (*pgpproc) { /* for PGP2 */ ap[i = 0] = r1bindex(pgpproc, '/'); ap[++i] = encryptsw ? (signsw ? "-esat" : "-eat") : (mimesw ? "-sab" : "-sat"); ap[++i] = "+language=en"; ap[++i] = "+armorlines=0"; ap[++i] = tmpfil2; for (ip = addrs; ip; ip = ip->next) ap[++i] = ip->address; if (from_addrs && from_addrs->address) { ap[++i] = "-u"; ap[++i] = from_addrs->address; } ap[++i] = "-o"; ap[++i] = tmpfil3; ap[++i] = NULLCP; 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; } } /* */ if (mimesw && !encryptsw) { fprintf(out1, "%s: multipart/signed;\n", TYPE_FIELD); fprintf(out1, "\tprotocol=\"application/pgp-signature\"; "); fprintf(out1, "micalg=\"pgp-%s\";\n", *pgpproc ? "md5" : get_micalg(from_addrs ? from_addrs->address : NULLCP, keyid)); fprintf(out1, "\tboundary=\"%s%d\"\n\n", prefix, 0); fprintf(out1, "--%s%d\n", prefix, 0); if ((in = fopen(tmpfil2, "r")) == NULL) { advise(tmpfil2, "unable to open"); goodbye(); } while (fgets(buf, sizeof(buf) - 1, in)) { cp = buf + strlen(buf) - 1; if (*cp-- == '\n' && *cp == '\r') { *cp++ = '\n'; *cp = '\0'; } fputs(buf, out1); } fclose(in); fprintf(out1, "\n--%s%d\n", prefix, 0); fprintf(out1, "%s: application/pgp-signature\n", TYPE_FIELD); fprintf(out1, "%s: 7bit\n\n", ENCODING_FIELD); } unlink(tmpfil2); if ((in = fopen(tmpfil3, "r")) == NULL) { advise(tmpfil3, "unable to open"); goodbye(); } while (fgets(buf, sizeof(buf) - 1, in)) fputs(buf, out1); fclose(in); unlink(tmpfil3); if (mimesw) fprintf(out1, "\n--%s%d--\n", prefix, 0); fclose(out1); if (!tomhn) { sprintf(buf, "%s.orig", m_backup(file)); if (rename(file, buf) == NOTOK) { advise(buf, "unable to rename %s to", file); goodbye(); } } unlink(file); if (rename(tmpfil1, file) == NOTOK) { int in, out; struct stat st; if (stat(file, &st) != NOTOK) { advise(file, "unable to rename %s to", tmpfil1); goodbye(); } if ((in = open(tmpfil1, 0)) == NOTOK) { advise(tmpfil1, "unable to open"); goodbye(); } if ((out = creat(file, 0600)) == NOTOK) { advise(file, "unable to create"); (void) close(in); goodbye(); } cpydata(in, out, tmpfil1, file); (void) close(in); (void) close(out); unlink(tmpfil1); } done(0); } struct addrlist * add_addrlist(addrp, str) struct addrlist *addrp; char *str; { char *cp; struct addrlist *ap, **vp; struct mailname *lp, *mp, *np, taddr; np = &taddr; while ((cp = getname(str))) { if (!(mp = getm(cp, NULLCP, 0, AD_HOST, NULLCP))) continue; np->m_next = mp; np = mp; } np->m_next = NULL; np = &taddr; for (lp = taddr.m_next; lp; lp = np->m_next) { if (lp->m_nohost) { char *pp; pp = akvalue(lp->m_mbox); while ((cp = getname(pp))) { if (!(mp = getm(cp, NULLCP, 0, AD_HOST, NULLCP))) continue; np->m_next = mp; np = mp; } np->m_next = lp->m_next; mnfree(lp); } else np = lp; } for (lp = taddr.m_next; lp; lp = np) { switch (lp->m_type) { case UUCPHOST: cp = add(lp->m_mbox, add("!", add(lp->m_host, NULLCP))); break; case LOCALHOST: cp = add(LocalName(), add("@", add(lp->m_mbox, NULLCP))); break; default: cp = add(lp->m_host, add("@", add(lp->m_mbox, NULLCP))); break; } vp = &addrp; for (ap = addrp; ap; ap = ap->next) { if (uleq(ap->address, cp)) break; vp = &(ap->next); } if (ap) free(cp); else { if ((*vp = (struct addrlist *) malloc(sizeof(struct addrlist))) == NULL) { advise(NULLCP, "out of memory"); goodbye(); } (*vp)->address = cp; (*vp)->next = NULL; } np = lp->m_next; mnfree(lp); } return addrp; } int exec_pgp5_for_keyid(prog, ap, keyid) char *prog, *ap[], **keyid; { 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(prog, ap); /* maybe PGP is not installed. */ _exit(NOT_INSTALLED); } close(pdes[1]); cp = ep = buf; *keyid = NULLCP; 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 (! *keyid) { for (xp = cp; *xp; xp++) if (strncmp(xp, "bits, Key ID ", 13) == 0) break; if (*xp && (yp = index(xp + 13, ','))) { *yp = '\0'; *keyid = add(xp + 13, NULLCP); } } } 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; } char * get_micalg(userid, keyid) char *userid, *keyid; { int pid, pdes[2]; char *ap[5], buf[BUFSIZ], *hash = "md5"; /* default */ FILE *in; 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]); ap[0] = "pgpk"; ap[1] = "-l"; ap[2] = "+language=en"; ap[3] = userid; /* might be NULLCP */ ap[4] = NULLCP; execvp("pgpk", ap); adios("pgpk", "unable to exec"); } close(pdes[1]); in = fdopen(pdes[0], "r"); while (fgets(buf, sizeof(buf), in)) { if (strncmp(buf, "sec", 3) == 0 /* secret key */ && strncmp(buf + 12, keyid, strlen(keyid)) == 0) { if (strncmp(buf + 43, "DSS", 3) == 0) hash = "sha1"; else if (strncmp(buf + 43, "RSA", 3) == 0) hash = "md5"; } } fclose(in); if (pidXwait(pid, "pgpk")) goodbye(); return hash; } static TYPESIG goodbye() { if (out1) fclose(out1); if (out2) fclose(out2); if (tmpfil1[0]) unlink(tmpfil1); if (tmpfil2[0]) unlink(tmpfil2); if (tmpfil3[0]) unlink(tmpfil3); done(1); /* NOT REACHED */ }