/* mhn.c - multi-media MH */ #ifndef lint static char ident[] = "@(#)$Id: mhn.c,v 2.21 1992/12/15 00:20:22 jromine Exp $"; #endif /* lint */ #include "../h/mh.h" #include <ctype.h> #include <errno.h> #include <setjmp.h> #include <signal.h> #include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #ifdef BSD42 #include <sys/wait.h> #endif #ifdef LOCALE #include <locale.h> #endif /* */ static struct swit switches[] = { #define AUTOSW 0 "auto", 0, #define NAUTOSW 1 "noauto", 0, #define DEBUGSW 2 "debug", -5, #define EBCDICSW 3 "ebcdicsafe", 0, #define NEBCDICSW 4 "noebcdicsafe", 0, #define FORMSW 5 "form formfile", 4, #define HEADSW 6 "headers", 0, #define NHEADSW 7 "noheaders", 0, #define LISTSW 8 "list", 0, #define NLISTSW 9 "nolist", 0, #define PARTSW 10 "part number", 0, #define SIZESW 11 "realsize", 0, #define NSIZESW 12 "norealsize", 0, #define RFC934SW 13 "rfc934mode", 0, #define NRFC934SW 14 "norfc934mode", 0, #define SERIALSW 15 "serialonly", 0, #define NSERIALSW 16 "noserialonly", 0, #define SHOWSW 17 "show", 0, #define NSHOWSW 18 "noshow", 0, #define STORESW 19 "store", 0, #define NSTORESW 20 "nostore", 0, #define TYPESW 21 "type content", 0, #define VERBSW 22 "verbose", 0, #define NVERBSW 23 "noverbose", 0, #define HELPSW 24 "help", 4, #define PROGSW 25 "moreproc program", -4, #define NPROGSW 26 "nomoreproc", -3, #define LENSW 27 "length lines", -4, #define WIDSW 28 "width columns", -4, #define FILESW 29 /* interface from show */ "file file", -4, #define VIAMSW 30 "viamail mailpath", -7, #define VIASSW 31 "viasubj subject", -7, #define VIAPSW 32 "viaparm arguments", -7, #define VIADSW 33 "viadesc text", -7, #define VIACSW 34 "viacmnt text", -7, #define VIAZSW 35 "viadelay seconds", -8, #define VIAFSW 36 "viafrom mailpath", -7, NULL, 0 }; /* */ #define NPARTS 50 #define NTYPES 20 static int autosw = 0; int debugsw = 0; static int ebcdicsw = 0; static char *formsw = NULLCP; static int headsw = 1; static int listsw = 0; static int nomore = 0; static int npart = 0; static char *parts[NPARTS + 1]; static char *progsw = NULLCP; static int rfc934sw = 1; static int serialsw = 0; static int showsw = 0; static int sizesw = 1; static int storesw = 0; static int ntype = 0; static char *types[NTYPES + 1]; int verbosw = 0; static int endian = 0; static char *mm_charset = NULL; static int xpid = 0; static int userrs = 0; static char *cache; static int cwdlen; static char *cwd; static char *dir; static char *errs = NULL; static char *tmp; extern int errno; #ifndef BSD44 extern int sys_nerr; extern char *sys_errlist[]; #endif off_t lseek (); time_t time (); /* */ #define LSTFMT1 "%4s %-5s %-24s %5s %-36s\n" #define LSTFMT2a "%4d " #define LSTFMT2b "%-5s %-24.24s " #define LSTFMT2c1 "%5lu" #define LSTFMT2c2 "%4lu%c" #define LSTFMT2c3 "huge " #define LSTFMT2c4 " " #define LSTFMT2d1 " %-36.36s" #define LSTFMT2d2 "\t %-65.65s\n" static void build_comp (); typedef struct CTinfo { char *ci_type; char *ci_subtype; #define NPARMS 10 char *ci_attrs[NPARMS + 2]; char *ci_values[NPARMS]; char *ci_comment; char *ci_magic; } CTInfo, *CI; #define NULLCI ((CI) 0) static int get_ctinfo (); static int get_comment (); typedef struct Content { char *c_partno; /* within multipart content */ char *c_vrsn; /* Body-Version: */ char *c_ctline; /* Content-Type: */ CTInfo c_ctinfo; int c_type; /* internal form */ #define CT_UNKNOWN 0x00 #define CT_APPLICATION 0x01 #define CT_AUDIO 0x02 #define CT_IMAGE 0x03 #define CT_MESSAGE 0x04 #define CT_MULTIPART 0x05 #define CT_TEXT 0x06 #define CT_VIDEO 0x07 #define CT_EXTENSION 0x08 int c_subtype; /* filled-in by c_ctinitfnx */ caddr_t c_ctparams; /* .. */ caddr_t c_ctextern; /* .. */ char *c_showproc; /* default, if not in profile */ char *c_termproc; /* for charset madness... */ char *c_storeproc; /* default, if not in profile */ int (*c_ctinitfnx) (); /* parse content */ int (*c_ctlistfnx) (); /* list content */ int (*c_ctshowfnx) (); /* show content */ int (*c_ctstorefnx) (); /* store content */ int (*c_ctfreefnx) (); /* free content-specific structures */ char *c_celine; /* Content-Transfer-Encoding: */ int c_encoding; /* internal form */ #define CE_UNKNOWN 0x00 #define CE_BASE64 0x01 #define CE_QUOTED 0x02 #define CE_8BIT 0x03 #define CE_7BIT 0x04 #define CE_BINARY 0x05 #define CE_EXTENSION 0x06 #define CE_EXTERNAL 0x07 /* for external-body */ caddr_t c_ceparams; /* filled-in by encoding initfnx */ int (*c_ceopenfnx) (); /* get a stream to decoded contents */ int (*c_ceclosefnx) (); /* release stream */ int (*c_celistfnx) (); /* list decoding info */ unsigned long (*c_cesizefnx) (); /* size of decoded contents */ int (*c_cefreefnx) (); /* free encoding-specific structures */ char *c_id; /* Content-ID: */ char *c_descr; /* Content-Description: */ FILE *c_fp; /* read contents (stream) */ char *c_file; /* read contents (file) */ int c_unlink; /* remove file when done? */ int c_umask; /* associated umask */ long c_begin; /* where content starts in file */ long c_end; /* .. ends */ int c_pid; /* process doing display */ char *c_storage; /* write contents (file) */ int c_rfc934; /* rfc934 compatibility */ } Content, *CT; #define NULLCT ((CT) 0) static CT get_content (); static int list_content (), show_content (), store_content (); static int user_content(), compose_content(), output_content(); static void free_content (), flush_errors (); #if defined(__STDC__) && defined(VSPRINTF) static void content_error (char *, register CT, char *, ...); #else static void content_error (); #endif static int init_encoding(), type_ok(), copy_some_headers(), set_endian(); static int write7Bit(), writeQuoted(), writeBase64(), writeBase64aux(); static int via_mail(), via_post(), pidcheck(); static CT *cts = NULL; #define quitser pipeser static TYPESIG pipeser (); static char *fgetstr (); /* */ /* ARGSUSED */ main (argc, argv) int argc; char **argv; { int f6 = 0, msgp = 0, msgnum; char *cp, *f1 = NULL, *f2 = NULL, *f3 = NULL, *f4 = NULL, *f5 = NULL, *f7 = NULL, *file = NULL, *folder = NULL, *maildir, buf[100], **ap, **argp, *arguments[MAXARGS], *msgs[MAXARGS]; struct msgs *mp; register CT ct, *ctp; FILE *fp; #ifdef LOCALE setlocale(LC_ALL, ""); #endif invo_name = r1bindex (argv[0], '/'); if (argv[1] && uprf (argv[1], "-via")) m_foil (NULLCP); 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 [+folder] [msgs] [switches]", invo_name); help (buf, switches); done (1); case AUTOSW: autosw++; continue; case NAUTOSW: autosw = 0; continue; case DEBUGSW: debugsw++; continue; case EBCDICSW: ebcdicsw++; continue; case NEBCDICSW: ebcdicsw = 0; continue; case FORMSW: if (!(cp = *argp++) || *cp == '-') adios (NULLCP, "missing argument to %s", argp[-2]); if (formsw) free (formsw); formsw = getcpy (libpath (cp)); continue; case HEADSW: headsw++; continue; case NHEADSW: headsw = 0; continue; case LISTSW: listsw++; continue; case NLISTSW: listsw = 0; continue; case PARTSW: if (!(cp = *argp++) || *cp == '-') adios (NULLCP, "missing argument to %s", argp[-2]); if (npart >= NPARTS) adios (NULLCP, "too many parts (starting with %s), %d max", cp, NPARTS); parts[npart++] = cp; continue; case RFC934SW: rfc934sw++; continue; case NRFC934SW: rfc934sw = 0; continue; case SERIALSW: serialsw++; continue; case NSERIALSW: serialsw = 0; continue; case SHOWSW: showsw++; continue; case NSHOWSW: showsw = 0; continue; case SIZESW: sizesw++; continue; case NSIZESW: sizesw = 0; continue; case STORESW: storesw++; continue; case NSTORESW: storesw = 0; continue; case TYPESW: if (!(cp = *argp++) || *cp == '-') adios (NULLCP, "missing argument to %s", argp[-2]); if (ntype >= NTYPES) adios (NULLCP, "too many types (starting with %s), %d max", cp, NTYPES); types[ntype++] = cp; continue; case VERBSW: verbosw++; continue; case NVERBSW: verbosw = 0; continue; case PROGSW: if (!(progsw = *argp++) || *progsw == '-') adios (NULLCP, "missing argument to %s", argp[-2]); continue; case NPROGSW: nomore++; continue; case LENSW: case WIDSW: if (!(cp = *argp++) || *cp == '-') adios (NULLCP, "missing argument to %s", argp[-2]); continue; case FILESW: if (!(cp = *argp++) || (*cp == '-' && cp[1])) adios (NULLCP, "missing argument to %s", argp[-2]); file = *cp == '-' ? cp : path (cp, TFILE); continue; case VIAMSW: if (!(f1 = *argp++)) adios (NULLCP, "missing argument to %s", argp[-2]); continue; case VIASSW: if (!(f2 = *argp++)) adios (NULLCP, "missing argument to %s", argp[-2]); continue; case VIAPSW: if (!(f3 = *argp++)) adios (NULLCP, "missing argument to %s", argp[-2]); continue; case VIADSW: if (!(f4 = *argp++)) adios (NULLCP, "missing argument to %s", argp[-2]); continue; case VIACSW: if (!(f5 = *argp++)) adios (NULLCP, "missing argument to %s", argp[-2]); continue; case VIAZSW: if (!(cp = *argp++) || *cp == '-') adios (NULLCP, "missing argument to %s", argp[-2]); if (sscanf (cp, "%d", &f6) != 1 || f6 < 0) f6 = 300; continue; case VIAFSW: if (!(f7 = *argp++)) adios (NULLCP, "missing argument to %s", argp[-2]); continue; } if (*cp == '+' || *cp == '@') { if (folder) adios (NULLCP, "only one folder at a time!"); else folder = path (cp + 1, *cp == '+' ? TFOLDER : TSUBCWF); } else msgs[msgp++] = cp; } parts[npart] = NULL, types[ntype] = NULL; if (!formsw) formsw = getcpy (libpath ("mhl.headers")); set_endian (); /* */ if (f1) { via_mail (f1, f2, f3, f4, f5, f6, f7); /* NOTREACHED */ } else if (f2 || f3 || f4 || f5 || f6) adios (NULLCP, "missing -viamail \"mailpath\" switch"); if (cp = getenv ("MHN")) { if (fp = fopen (cp, "r")) { m_readefs ((struct node **) 0, fp, cp, 0); (void) fclose (fp); } else admonish ("", "unable to read $MHN profile (%s)", cp); } if (fp = fopen (cp = libpath ("mhn_defaults"), "r")) { m_readefs ((struct node **) 0, fp, cp, 0); (void) fclose (fp); } (void) sprintf (buf, "%s-cache", invo_name); cache = m_find (buf); cwdlen = strlen (cwd = getcpy (pwd ())); (void) sprintf (buf, "%s-storage", invo_name); dir = getcpy ((cp = m_find (buf)) && *cp ? cp : cwd); tmp = cp && *cp ? concat (cp, "/", invo_name, NULLCP) : add (m_maildir (invo_name), NULLCP); if (!m_find ("path")) free (path ("./", TFOLDER)); if (msgp == 1 && !folder && !npart && !showsw && !storesw && !ntype && !file && (cp = getenv ("mhdraft")) && strcmp (cp, msgs[0]) == 0) { build_comp (cp); /* NOTREACHED */ } if (file) { int stdinP; if (msgp) adios (NULLCP, "only one file at a time!"); if ((cts = (CT *) calloc ((unsigned) 2, sizeof *cts)) == NULL) adios (NULLCP, "out of memory"); ctp = cts; if (stdinP = (strcmp (file, "-") == 0)) { char buffer[BUFSIZ]; file = add (m_tmpfil (invo_name), NULLCP); if ((fp = fopen (file, "w+")) == NULL) adios (file, "unable to fopen for writing and reading"); (void) chmod (file, 0600); while (fgets (buffer, sizeof buffer, stdin)) (void) fputs (buffer, fp); (void) fflush (fp); if (ferror (stdin)) { (void) unlink (file); adios ("stdin", "error reading"); } if (ferror (fp)) { (void) unlink (file); adios (file, "error writing"); } (void) fseek (fp, 0L, 0); } else if ((fp = fopen (file, "r")) == NULL) adios (file, "unable to read"); if (ct = get_content (fp, file, 1)) { if (stdinP) ct -> c_unlink = 1; ct -> c_fp = NULL; if (ct -> c_end == 0L) { (void) fseek (fp, 0L, 2); ct -> c_end = ftell (fp); } if (ct -> c_ctinitfnx && (*ct -> c_ctinitfnx) (ct) == NOTOK) free_content (ct); else *ctp++ = ct; } else advise (NULLCP, "unable to decode %s", file); (void) fclose (fp); mp = NULL; goto go_to_it; } if (!msgp) msgs[msgp++] = "cur"; if (!folder) folder = m_getfolder (); maildir = m_maildir (folder); if (chdir (maildir) == NOTOK) adios (maildir, "unable to change directory to"); if (!(mp = m_gmsg (folder))) adios (NULLCP, "unable to read folder %s", folder); if (mp -> hghmsg == 0) adios (NULLCP, "no messages in %s", folder); for (msgnum = 0; msgnum < msgp; msgnum++) if (!m_convert (mp, msgs[msgnum])) done (1); m_setseq (mp); if ((cts = (CT *) calloc ((unsigned) (mp -> numsel + 1), sizeof *cts)) == NULL) adios (NULLCP, "out of memory"); ctp = cts; for (msgnum = mp -> lowsel; msgnum <= mp -> hghsel; msgnum++) if (mp -> msgstats[msgnum] & SELECTED) { char *msgnam; if ((fp = fopen (msgnam = m_name (msgnum), "r")) == NULL) adios (msgnam, "unable to read message"); if (ct = get_content (fp, msgnam, 1)) { ct -> c_fp = NULL; if (ct -> c_end == 0L) { (void) fseek (fp, 0L, 2); ct -> c_end = ftell (fp); } if (ct -> c_ctinitfnx && (*ct -> c_ctinitfnx) (ct) == NOTOK) free_content (ct); else *ctp++ = ct; } else advise (NULLCP, "unable to decode message %s", msgnam); (void) fclose (fp); } go_to_it: ; if (!*cts) done (1); if (!listsw && !showsw && !storesw) showsw++; /* listsw && showsw -> user wants per-message listing, so delay until showsw processing && storesw && sizesw -> evaluating size will cause more work, so delay until after storesw processing */ userrs = 1; (void) signal (SIGQUIT, quitser); (void) signal (SIGPIPE, pipeser); for (ctp = cts; ct = *ctp; ctp++) if (type_ok (ct) && (ct -> c_ctlistfnx || ct -> c_ctstorefnx || ct -> c_ctshowfnx)) { struct stat st; if (!ct -> c_umask) ct -> c_umask = ~(stat (ct -> c_file, &st) != NOTOK ? (st.st_mode & 0777) : m_gmprot ()); } if (listsw && !showsw && (!storesw || !sizesw)) { if (headsw) printf (LSTFMT1, "msg", "part", "type/subtype", "size", "description"); for (ctp = cts; ct = *ctp; ctp++) if (type_ok (ct) && ct -> c_ctlistfnx) { (void) umask (ct -> c_umask); (void) (*ct -> c_ctlistfnx) (ct, 1); if (ct -> c_fp) (void) fclose (ct -> c_fp), ct -> c_fp = NULL; } flush_errors (); } if (storesw) { for (ctp = cts; ct = *ctp; ctp++) if (type_ok (ct) && ct -> c_ctstorefnx) { (void) umask (ct -> c_umask); (void) (*ct -> c_ctstorefnx) (ct, NULLCP); if (ct -> c_fp) (void) fclose (ct -> c_fp), ct -> c_fp = NULL; } flush_errors (); } if (listsw && !showsw && storesw && sizesw) { if (headsw) printf (LSTFMT1, "msg", "part", "type/subtype", "size", "description"); for (ctp = cts; ct = *ctp; ctp++) if (type_ok (ct) && ct -> c_ctlistfnx) { (void) umask (ct -> c_umask); (void) (*ct -> c_ctlistfnx) (ct, 1); if (ct -> c_fp) (void) fclose (ct -> c_fp), ct -> c_fp = NULL; } flush_errors (); listsw = 0; } if (showsw) for (ctp = cts; ct = *ctp; ctp++) { int child_id, i, vecp; char *vec[8]; #if defined(BSD42) && !defined(WAITINT) union wait status; #else int status; #endif TYPESIG (*hstat) (), (*istat) (), (*qstat) (), (*tstat) (); if (!type_ok (ct)) continue; (void) umask (ct -> c_umask); if (listsw) { if (headsw) printf (LSTFMT1, "msg", "part", "type/subtype", "size", "description"); if (ct -> c_ctlistfnx) (void) (*ct -> c_ctlistfnx) (ct, 1); } if (!ct -> c_ctshowfnx) { if (ct -> c_fp) (void) fclose (ct -> c_fp), ct -> c_fp = NULL; continue; } vecp = 0; vec[vecp++] = r1bindex (mhlproc, '/'); vec[vecp++] = "-form"; vec[vecp++] = formsw; vec[vecp++] = "-nobody"; vec[vecp++] = ct -> c_file; if (nomore) vec[vecp++] = "-nomoreproc"; else if (progsw) { vec[vecp++] = "-moreproc"; vec[vecp++] = progsw; } vec[vecp] = NULL; (void) fflush (stdout); for (i = 0; (child_id = vfork ()) == NOTOK && i < 5; i++) sleep (5); switch (child_id) { case NOTOK: adios ("fork", "unable to"); /* NOTREACHED */ case OK: (void) execvp (mhlproc, vec); fprintf (stderr, "unable to exec "); perror (mhlproc); _exit (-1); /* NOTREACHED */ default: xpid = child_id; break; } (void) (*ct -> c_ctshowfnx) (ct, 1, 0); if (ct -> c_fp) (void) fclose (ct -> c_fp), ct -> c_fp = NULL; hstat = signal (SIGHUP, SIG_IGN); istat = signal (SIGINT, SIG_IGN); qstat = signal (SIGQUIT, SIG_IGN); tstat = signal (SIGTERM, SIG_IGN); while (wait (&status) != NOTOK) { #if defined(BSD42) && !defined(WAITINT) (void) pidcheck (status.w_status); #else (void) pidcheck (status); #endif continue; } (void) signal (SIGHUP, hstat); (void) signal (SIGINT, istat); (void) signal (SIGQUIT, qstat); (void) signal (SIGTERM, tstat); xpid = 0; flush_errors (); } for (ctp = cts; *ctp; ctp++) free_content (*ctp); free ((char *) cts); cts = NULL; if (mp) { m_replace (pfolder, folder); if (mp -> hghsel != mp -> curmsg) m_setcur (mp, mp -> hghsel); m_sync (mp); m_update (); } done (0); /* NOTREACHED */ } /* */ static TYPESIG pipeser (i) int i; { if (i == SIGQUIT) { (void) unlink ("core"); (void) fflush (stdout); fprintf (stderr, "\n"); (void) fflush (stderr); } done (1); /* NOTREACHED */ } /* */ #include "../h/mhn.h" struct str2init { char *si_key; int si_value; int (*si_init) (); }; static int InitApplication (), InitMessage (), InitMultiPart (), InitText (); static struct str2init str2cts[] = { "application", CT_APPLICATION, InitApplication, "audio", CT_AUDIO, NULL, "image", CT_IMAGE, NULL, "message", CT_MESSAGE, InitMessage, "multipart", CT_MULTIPART, InitMultiPart, "text", CT_TEXT, InitText, "video", CT_VIDEO, NULL, NULL, CT_EXTENSION, NULL, /* these two must be last! */ NULL, CT_UNKNOWN, NULL, }; static int InitBase64 (), InitQuoted (), Init7Bit (); static struct str2init str2ces[] = { "base64", CE_BASE64, InitBase64, "quoted-printable", CE_QUOTED, InitQuoted, "8bit", CE_8BIT, Init7Bit, "7bit", CE_7BIT, Init7Bit, "binary", CE_BINARY, NULL, NULL, CE_EXTENSION, NULL, /* these two must be last! */ NULL, CE_UNKNOWN, NULL, }; /* */ static CT get_content (in, file, toplevel) FILE *in; char *file; int toplevel; { int compnum, state; char buf[BUFSIZ], name[NAMESZ]; register CT ct; if ((ct = (CT) calloc (1, sizeof *ct)) == NULL) adios (NULLCP, "out of memory"); ct -> c_begin = ftell (ct -> c_fp = in) + 1; ct -> c_file = add (file, NULLCP); for (compnum = 1, state = FLD;;) { switch (state = m_getfld (state, name, buf, sizeof buf, in)) { case FLD: case FLDPLUS: case FLDEOF: compnum++; if (uleq (name, VRSN_FIELD)) { register char *cp; cp = add (buf, NULLCP); while (state == FLDPLUS) { state = m_getfld (state, name, buf, sizeof buf, in); cp = add (buf, cp); } if (ct -> c_vrsn) { char *dp = trimcpy (cp); advise (NULLCP, "message %s has multiple %s: fields (%s)", ct -> c_file, VRSN_FIELD, dp); free (dp); free (cp); goto out; } ct -> c_vrsn = cp; #ifdef whocares while (isspace (*cp)) cp++; for (dp = cp; istoken (*dp); dp++) continue; c = *dp, *dp = NULL; if (!uleq (cp, VRSN_VALUE)) { if (!isspace (c)) *dp = c; advise (NULLCP, "message %s has unsupported value for %s: field (%s)", ct -> c_file, VRSN_FIELD, cp); goto out; } *dp = c; #endif goto got_header; } if (uleq (name, TYPE_FIELD)) { register char *cp; register struct str2init *s2i; register CI ci = &ct -> c_ctinfo; cp = add (buf, NULLCP); while (state == FLDPLUS) { state = m_getfld (state, name, buf, sizeof buf, in); cp = add (buf, cp); } if (ct -> c_ctline) { char *dp = trimcpy (cp); advise (NULLCP, "message %s has multiple %s: fields (%s)", ct -> c_file, TYPE_FIELD, dp); free (dp); free (cp); goto out; } if (get_ctinfo (cp, ct, 0) == NOTOK) goto out; for (s2i = str2cts; s2i -> si_key; s2i++) if (uleq (ci -> ci_type, s2i -> si_key)) break; if (!s2i -> si_key && !uprf (ci -> ci_type, "X-")) s2i++; ct -> c_type = s2i -> si_value; ct -> c_ctinitfnx = s2i -> si_init; goto got_header; } if (uleq (name, ENCODING_FIELD)) { register char *cp, *dp; char c; register struct str2init *s2i; cp = add (buf, NULLCP); while (state == FLDPLUS) { state = m_getfld (state, name, buf, sizeof buf, in); cp = add (buf, cp); } if (ct -> c_celine) { advise (NULLCP, "message %s has multiple %s: fields (%s)", ct -> c_file, ENCODING_FIELD, dp = trimcpy (cp)); free (dp); free (cp); goto out; } ct -> c_celine = cp; while (isspace (*cp)) cp++; for (dp = cp; istoken (*dp); dp++) continue; c = *dp, *dp = '\0'; for (s2i = str2ces; s2i -> si_key; s2i++) if (uleq (cp, s2i -> si_key)) break; if (!s2i -> si_key && !uprf (cp, "X-")) s2i++; *dp = c; ct -> c_encoding = s2i -> si_value; if (s2i -> si_init && (*s2i -> si_init) (ct) == NOTOK) goto out; goto got_header; } if (uleq (name, ID_FIELD)) { ct -> c_id = add (buf, ct -> c_id); while (state == FLDPLUS) { state = m_getfld (state, name, buf, sizeof buf, in); ct -> c_id = add (buf, ct -> c_id); } goto got_header; } if (uleq (name, DESCR_FIELD)) { ct -> c_descr = add (buf, ct -> c_descr); while (state == FLDPLUS) { state = m_getfld (state, name, buf, sizeof buf, in); ct -> c_descr = add (buf, ct -> c_descr); } goto got_header; } #ifdef notdef if (uprf (name, XXX_FIELD_PRF)) advise (NULLCP, "unknown field (%s) in message %s", name, ct -> c_file); /* and fall... */ #endif while (state == FLDPLUS) state = m_getfld (state, name, buf, sizeof buf, in); got_header: ; if (state != FLDEOF) { ct -> c_begin = ftell (in) + 1; continue; } /* else fall... */ case BODY: case BODYEOF: break; case FILEEOF: ct -> c_begin = ftell (in); break; case LENERR: case FMTERR: adios (NULLCP, "message format error in component #%d", compnum); default: adios (NULLCP, "getfld() returned %d", state); } break; } if (!ct -> c_ctline) { if (toplevel < 0) { if (get_ctinfo ("message/rfc822", ct, 0) == NOTOK) goto out; ct -> c_type = CT_MESSAGE; ct -> c_ctinitfnx = InitMessage; } else { if (get_ctinfo ("text/plain", ct, 0) == NOTOK) goto out; ct -> c_type = CT_TEXT; ct -> c_ctinitfnx = InitText; } } if (!ct -> c_ctlistfnx) ct -> c_ctlistfnx = list_content; if (!ct -> c_ctshowfnx) ct -> c_ctshowfnx = show_content; if (!ct -> c_ctstorefnx) ct -> c_ctstorefnx = store_content; if (!ct -> c_celine) { ct -> c_encoding = CE_7BIT; (void) Init7Bit (ct); } return ct; out: free_content (ct); return NULLCT; } /* */ static int get_ctinfo (cp, ct, magic) char *cp; register CT ct; int magic; { int i = strlen (invo_name) + 2; register char *dp, **ap, **ep; char c; register CI ci = &ct -> c_ctinfo; cp = ct -> c_ctline = add (cp, NULLCP); while (isspace (*cp)) cp++; for (dp = index (cp, '\n'); dp; dp = index (dp, '\n')) *dp++ = ' '; for (dp = cp + strlen (cp) - 1; dp >= cp; dp--) if (!isspace (*dp)) break; *++dp = '\0'; if (debugsw) fprintf (stderr, "%s: %s\n", TYPE_FIELD, cp); if (*cp == '(' && get_comment (ct, &cp) == NOTOK) return NOTOK; for (dp = cp; istoken (*dp); dp++) continue; c = *dp, *dp = '\0'; ci -> ci_type = add (cp, NULLCP); *dp = c, cp = dp; if (!*ci -> ci_type) { advise (NULLCP, "invalid %s: field in message %s (empty type)", TYPE_FIELD, ct -> c_file); return NOTOK; } for (dp = ci -> ci_type; *dp; dp++) if (isalpha(*dp) && isupper (*dp)) *dp = tolower (*dp); while (isspace (*cp)) cp++; if (*cp == '(' && get_comment (ct, &cp) == NOTOK) return NOTOK; if (*cp != '/') { if (!magic) ci -> ci_subtype = add ("", NULLCP); goto magic_skip; } cp++; while (isspace (*cp)) cp++; if (*cp == '(' && get_comment (ct, &cp) == NOTOK) return NOTOK; for (dp = cp; istoken (*dp); dp++) continue; c = *dp, *dp = '\0'; ci -> ci_subtype = add (cp, NULLCP); *dp = c, cp = dp; if (!*ci -> ci_subtype) { advise (NULLCP, "invalid %s: field in message %s (empty subtype for \"%s\")", TYPE_FIELD, ct -> c_file, ci -> ci_type); return NOTOK; } for (dp = ci -> ci_subtype; *dp; dp++) if (isalpha(*dp) && isupper (*dp)) *dp = tolower (*dp); magic_skip: ; while (isspace (*cp)) cp++; if (*cp == '(' && get_comment (ct, &cp) == NOTOK) return NOTOK; ep = (ap = ci -> ci_attrs) + NPARMS; while (*cp == ';') { char *vp, *up; if (ap >= ep) { advise (NULLCP, "too many parameters in message %s's %s: field (%d max)", ct -> c_file, TYPE_FIELD, NPARMS); return NOTOK; } cp++; while (isspace (*cp)) cp++; if (*cp == '(' && get_comment (ct, &cp) == NOTOK) return NOTOK; if (*cp == 0) { advise (NULLCP, "extraneous trailing ';' in message %s's %s: parameter list", ct -> c_file, TYPE_FIELD); return OK; } for (dp = cp; istoken (*dp); dp++) if (isalpha(*dp) && isupper (*dp)) *dp = tolower (*dp); for (up = dp; isspace (*dp); ) dp++; if (dp == cp || *dp != '=') { advise (NULLCP, "invalid parameter in message %s's %s: field\n%*.*sparameter %s (error detected at offset %d)", ct -> c_file, TYPE_FIELD, i, i, "", cp, dp - cp); return NOTOK; } vp = (*ap = add (cp, NULLCP)) + (up - cp); *vp = '\0'; for (dp++; isspace (*dp); ) dp++; ci -> ci_values[ap - ci -> ci_attrs] = vp = *ap + (dp - cp); if (*dp == '"') { for (cp = ++dp, dp = vp;;) { switch (c = *cp++) { case '\0': bad_quote: ; advise (NULLCP, "invalid quoted-string in message %s's %s: field\n%*.*s(parameter %s)", ct -> c_file, TYPE_FIELD, i, i, "", *ap); return NOTOK; case '\\': *dp++ = c; if ((c = *cp++) == '\0') goto bad_quote; /* else fall... */ default: *dp++ = c; continue; case '"': *dp = '\0'; break; } break; } } else { for (cp = dp, dp = vp; istoken (*cp); cp++, dp++) continue; *dp = '\0'; } if (!*vp) { advise (NULLCP, "invalid parameter in message %s's %s: field\n%*.*s(parameter %s)", ct -> c_file, TYPE_FIELD, i, i, "", *ap); return NOTOK; } ap++; while (isspace (*cp)) cp++; if (*cp == '(' && get_comment (ct, &cp) == NOTOK) return NOTOK; } if (magic && *cp == '[') { ct -> c_descr = ++cp; for (dp = cp + strlen (cp) - 1; dp >= cp; dp--) if (*dp == ']') break; if (dp < cp) { advise (NULLCP, "invalid description in message %s", ct -> c_file); ct -> c_descr = NULL; return NOTOK; } c = *dp, *dp = '\0'; if (*ct -> c_descr) ct -> c_descr = concat (ct -> c_descr, "\n", NULLCP); else ct -> c_descr = NULL; *dp++ = c, cp = dp; while (isspace (*cp)) cp++; } if (*cp) { if (magic) ci -> ci_magic = add (cp, NULLCP); else advise (NULLCP, "extraneous information in message %s's %s: field\n%*.*s(%s)", ct -> c_file, TYPE_FIELD, i, i, "", cp); } return OK; } /* */ static int get_comment (ct, ap) CT ct; char **ap; { register int i; register char *bp, *cp; char c, buffer[BUFSIZ], *dp; register CI ci = &ct -> c_ctinfo; cp = *ap; bp = buffer; cp++; for (i = 0;;) { switch (c = *cp++) { case '\0': invalid: ; advise (NULLCP, "invalid comment in message %s's %s: field", ct -> c_file, TYPE_FIELD); return NOTOK; case '\\': *bp++ = c; if ((c = *cp++) == '\0') goto invalid; *bp++ = c; continue; case '(': i++; /* and fall... */ default: *bp++ = c; continue; case ')': if (--i < 0) break; *bp++ = c; continue; } break; } *bp = '\0'; if (dp = ci -> ci_comment) { ci -> ci_comment = concat (dp, " ", buffer, NULLCP); free (dp); } else ci -> ci_comment = add (buffer, NULLCP); while (isspace (*cp)) cp++; *ap = cp; return OK; } /* */ #define empty(s) ((s) ? (s) : "") static int list_content (ct, toplevel) register CT ct; int toplevel; { unsigned long size; register char **ap, **ep; char *cp, buffer[BUFSIZ]; register CI ci = &ct -> c_ctinfo; printf (toplevel > 0 ? LSTFMT2a : toplevel < 0 ? "part " : " ", atoi (r1bindex (empty (ct -> c_file), '/'))); (void) sprintf (buffer, "%s/%s", empty (ci -> ci_type), empty (ci -> ci_subtype)); printf (LSTFMT2b, empty (ct -> c_partno), buffer); size = ct -> c_cesizefnx && sizesw ? (*ct -> c_cesizefnx) (ct) : ct -> c_end - ct -> c_begin; for (cp = " KMGT"; size > 9999; size >>= 10) if (!*++cp) break; switch (*cp) { case ' ': if (size > 0 || ct -> c_encoding != CE_EXTERNAL) printf (LSTFMT2c1, size); else printf (LSTFMT2c4); break; default: printf (LSTFMT2c2, size, *cp); break; case '\0': printf (LSTFMT2c3); } if (ct -> c_descr) { char *dp; dp = trimcpy (cp = add (ct -> c_descr, NULLCP)); free (cp); printf (LSTFMT2d1, dp); free (dp); } printf ("\n"); if (verbosw && ci -> ci_comment) { char *dp; dp = trimcpy (cp = add (ci -> ci_comment, NULLCP)); free (cp); (void) sprintf (buffer, "(%s)", dp); free (dp); printf (LSTFMT2d2, buffer); } if (!debugsw) return OK; (void) fflush (stdout); fprintf (stderr, " partno \"%s\"\n", empty (ct -> c_partno)); if (ct -> c_vrsn) fprintf (stderr, " %s:%s", VRSN_FIELD, ct -> c_vrsn); if (ct -> c_ctline) fprintf (stderr, " %s:%s", TYPE_FIELD, ct -> c_ctline); fprintf (stderr, " type \"%s\" subtype \"%s\" comment \"%s\" magic \"%s\"\n", empty (ci -> ci_type), empty (ci -> ci_subtype), empty (ci -> ci_comment), empty (ci -> ci_magic)); for (ap = ci -> ci_attrs, ep = ci -> ci_values; *ap; ap++, ep++) fprintf (stderr, " parameter %s=\"%s\"\n", *ap, *ep); fprintf (stderr, " type 0x%x subtype 0x%x params 0x%x\n", ct -> c_type, ct -> c_subtype, ct -> c_ctparams); fprintf (stderr, " showproc \"%s\"\n", empty (ct -> c_showproc)); fprintf (stderr, " termproc \"%s\"\n", empty (ct -> c_termproc)); fprintf (stderr, " storeproc \"%s\"\n", empty (ct -> c_storeproc)); if (ct -> c_celine) fprintf (stderr, " %s:%s", ENCODING_FIELD, ct -> c_celine); fprintf (stderr, " encoding 0x%x params 0x%x\n", ct -> c_encoding, ct -> c_ceparams); if (ct -> c_id) fprintf (stderr, " %s:%s", ID_FIELD, ct -> c_id); if (ct -> c_descr) fprintf (stderr, " %s:%s", DESCR_FIELD, ct -> c_descr); fprintf (stderr, " fp 0x%x file \"%s\" begin %d end %d\n", ct -> c_fp, empty (ct -> c_file), ct -> c_begin, ct -> c_end); if (ct -> c_celistfnx) (void) (*ct -> c_celistfnx) (ct); return OK; } #undef empty /* */ #ifdef VSPRINTF #ifdef __STDC__ #include <stdarg.h> #else #include <varargs.h> #endif #endif #ifdef VSPRINTF #ifdef __STDC__ static void content_error (char *what, register CT ct, char *fmt, ...) #else static void content_error (va_alist) va_dcl #endif #else /* !VSPRINTF */ /* VARARGS3 */ static void content_error (what, ct, fmt, a, b, c, d, e, f) char *what, *fmt, *a, *b, *c, *d, *e, *f; register CT ct; #endif { #ifdef VSPRINTF va_list arglist; #endif #if defined(VSPRINTF) && !defined(__STDC__) char *what, *fmt; register CT ct; #endif int i; register char *bp; char buffer[BUFSIZ]; register CI ci; bp = buffer; if (userrs && invo_name && *invo_name) { (void) sprintf (bp, "%s: ", invo_name); bp += strlen (bp); } #ifdef VSPRINTF #ifdef __STDC__ va_start (arglist, fmt); #else va_start (arglist); what = va_arg(arglist, char *); ct = va_arg(arglist, CT); fmt = va_arg(arglist, char *); #endif (void) vsprintf (bp, fmt, arglist); bp += strlen (bp); #else (void) sprintf (bp, fmt, a, b, c, d, e, f); bp += strlen (bp); #endif ci = &ct -> c_ctinfo; if (what) { if (*what) { (void) sprintf (bp, " %s: ", what); bp += strlen (bp); } if (errno > 0 && errno < sys_nerr) (void) sprintf (bp, "%s", sys_errlist[errno]); else (void) sprintf (bp, "Error %d", errno); bp += strlen (bp); } i = strlen (invo_name) + 2; (void) sprintf (bp, "\n%*.*s(content %s/%s", i, i, "", ci -> ci_type, ci -> ci_subtype); bp += strlen (bp); if (ct -> c_file) { (void) sprintf (bp, " in message %s", ct -> c_file); bp += strlen (bp); if (ct -> c_partno) { (void) sprintf (bp, ", part %s", ct -> c_partno); bp += strlen (bp); } } (void) sprintf (bp, ")"); bp += strlen (bp); if (userrs) { *bp++ = '\n'; *bp = '\0'; errs = add (buffer, errs); } else advise (NULLCP, "%s", buffer); } static void flush_errors () { if (errs) { (void) fflush (stdout); fprintf (stderr, "%s", errs); free (errs); errs = NULL; } } /* */ static jmp_buf intrenv; /* ARGSUSED */ static TYPESIG intrser (i) int i; { #ifdef BSD42 (void) signal (SIGINT, intrser); #endif (void) putchar ('\n'); longjmp (intrenv, DONE); } /* */ static int show_content_aux (); static int show_content (ct, serial, alternate) register CT ct; int serial, alternate; { register char *cp; char buffer[BUFSIZ]; register CI ci = &ct -> c_ctinfo; (void) sprintf (buffer, "%s-show-%s/%s", invo_name, ci -> ci_type, ci -> ci_subtype); if ((cp = m_find (buffer)) == NULL || *cp == 0) { (void) sprintf (buffer, "%s-show-%s", invo_name, ci -> ci_type); if (((cp = m_find (buffer)) == NULL || *cp == 0) && (cp = ct -> c_showproc) == NULL) { if (!alternate) content_error (NULLCP, ct, "don't know how to display content"); return NOTOK; } } return show_content_aux (ct, serial, alternate, cp, NULLCP); } static int show_content_aux (ct, serial, alternate, cp, cracked) register CT ct; int serial, alternate; register char *cp; char *cracked; { int child_id, fd, i, xlist, xpause, xstdin, xtty; register char *bp; char *file, *vec[4], buffer[BUFSIZ], exec[BUFSIZ + sizeof "exec "]; register CI ci = &ct -> c_ctinfo; if (!ct -> c_ceopenfnx) { if (!alternate) content_error (NULLCP, ct, "don't know how to decode content"); return NOTOK; } file = NULL; if ((fd = (*ct -> c_ceopenfnx) (ct, &file)) == NOTOK) return NOTOK; if (ct -> c_showproc && strcmp (ct -> c_showproc, "true") == 0) return (alternate ? DONE : OK); xlist = xpause = xstdin = xtty = 0; if (cracked) { (void) strcpy (buffer, cp); goto got_command; } buffer[0] = '\0'; for (bp = buffer; *cp; cp++) if (*cp == '%') { switch (*++cp) { case 'a': /* additional arguments */ { register char **ap, **ep; char *s = ""; for (ap = ci -> ci_attrs, ep = ci -> ci_values; *ap; ap++, ep++) { (void) sprintf (bp, "%s%s=\"%s\"", s, *ap, *ep); bp += strlen (bp); s = " "; } } break; case 'e': /* exclusive execution */ xtty = 1; break; case 'F': /* %e, %f, and stdin is terminal not content */ xstdin = xtty = 1; /* and fall... */ case 'f': /* filename */ (void) sprintf (bp, "%s", file); break; case 'p': /* pause prior to displaying content */ xpause = 1; /* and fall... */ case 'l': /* display listing prior to displaying content */ xlist = 1; break; case 's': /* subtype */ (void) strcpy (bp, ci -> ci_subtype); break; case '%': goto raw; default: *bp++ = *--cp; *bp = '\0'; continue; } bp += strlen (bp); } else { raw: ; *bp++ = *cp; *bp = '\0'; } if (ct -> c_termproc) { char term[BUFSIZ]; (void) strcpy (term, buffer); (void) sprintf (buffer, ct -> c_termproc, term); } got_command: ; if (debugsw || cracked) { (void) fflush (stdout); fprintf (stderr, "%s msg %s", cracked ? "storing" : "show", ct -> c_file); if (ct -> c_partno) fprintf (stderr, " part %s", ct -> c_partno); if (cracked) fprintf (stderr, " using command (cd %s; %s)\n", cracked, buffer); else fprintf (stderr, " using command %s\n", buffer); } if (xtty && xpid) { (void) pidcheck (pidwait (xpid, NOTOK)); xpid = 0; } if (xlist) { char prompt[BUFSIZ]; if (ct -> c_ctlistfnx) { (*ct -> c_ctlistfnx) (ct, -1); if (xpause && SOprintf ("Press <return> to show content...")) printf ("Press <return> to show content..."); } else { register char *pp; pp = prompt; if (ct -> c_descr) { (void) sprintf (pp, "%s (", ct -> c_descr); pp += strlen (pp); } (void) sprintf (pp, "content %s/%s", ci -> ci_type, ci -> ci_subtype); pp += strlen (pp); if (ct -> c_file) { (void) sprintf (pp, " in message %s", ct -> c_file); pp += strlen (pp); if (ct -> c_partno) { (void) sprintf (pp, ", part %s", ct -> c_partno); pp += strlen (pp); } } if (ct -> c_descr) { (void) sprintf (pp, ")"); pp += strlen (pp); } if (!xpause) printf ("%s\n", prompt); else if (SOprintf ("Press <return> to show %s...", prompt)) printf ("Press <return> to show %s...", prompt); } if (xpause) { int intr; TYPESIG (*istat) (); istat = signal (SIGINT, intrser); if ((intr = setjmp (intrenv)) == OK) { (void) fflush (stdout); prompt[0] = 0; (void) read (fileno (stdout), prompt, sizeof prompt); } (void) signal (SIGINT, istat); if (intr != OK) { (void) (*ct -> c_ceclosefnx) (ct); return (alternate ? DONE : NOTOK); } } } (void) sprintf (exec, "exec %s", buffer); vec[0] = "/bin/sh"; vec[1] = "-c"; vec[2] = exec; vec[3] = NULL; (void) fflush (stdout); for (i = 0; (child_id = vfork ()) == NOTOK && i < 5; i++) sleep (5); switch (child_id) { case NOTOK: advise ("fork", "unable to"); (void) (*ct -> c_ceclosefnx) (ct); return NOTOK; case OK: if (cracked) (void) chdir (cracked); if (!xstdin) (void) dup2 (fd, 0); (void) close (fd); (void) execvp ("/bin/sh", vec); fprintf (stderr, "unable to exec "); perror ("/bin/sh"); _exit (-1); /* NOTREACHED */ default: if (!serial) { ct -> c_pid = child_id; if (xtty) xpid = child_id; } else (void) pidcheck (pidXwait (child_id, NULLCP)); (void) (*ct -> c_ceclosefnx) (ct); return (alternate ? DONE : OK); } } /* */ static int store_content (ct, append) register CT ct; char *append; { int appending = append && *append; long last, pos; register char *bp, *cp; char *file, buffer[BUFSIZ]; register CI ci = &ct -> c_ctinfo; FILE *fp; if (appending) { (void) strcpy (buffer, append); goto got_filename; } if ((cp = ct -> c_storeproc) == NULL || *cp == 0) { (void) sprintf (buffer, "%s-store-%s/%s", invo_name, ci -> ci_type, ci -> ci_subtype); if ((cp = m_find (buffer)) == NULL || *cp == 0) { (void) sprintf (buffer, "%s-store-%s", invo_name, ci -> ci_type); if ((cp = m_find (buffer)) == NULL || *cp == 0) cp = ct -> c_type == CT_MESSAGE ? "+" : "%m%P.%s"; } } switch (*cp) { case '+': case '@': { char *folder = cp[1] ? path (cp + 1, *cp == '+' ? TFOLDER : TSUBCWF) : m_getfolder (); struct msgs *mp = NULL; struct stat st; if (stat (bp = m_mailpath (folder), &st) == NOTOK) { int answer; char *ep; if (errno != ENOENT) { advise (bp, "error on folder"); goto losing_folder; } ep = concat ("Create folder \"", bp, "\"? ", NULLCP); answer = getanswer (ep); free (ep); if (!answer) goto losing_folder; if (!makedir (bp)) { advise (NULLCP, "unable to create folder %s", bp); goto losing_folder; } } if (mp = m_gmsg (folder)) (void) sprintf (buffer, "%s/%d", mp -> foldpath, mp -> hghmsg + 1); else advise (NULLCP, "unable to read folder %s", folder); losing_folder: ; if (cp[1]) free (folder); if (mp) m_fmsg (mp); else return NOTOK; } goto got_filename; case '/': case '|': case '!': bp = buffer; buffer[0] = '\0'; break; default: bp = autosw ? cwd : dir; (void) sprintf (buffer, "%s/", bp[1] ? bp : ""); bp = buffer + strlen (buffer); break; } for (; *cp; cp++) if (*cp == '%') { switch (*++cp) { case 'a': /* additional arguments */ if (buffer[0] != '|' && buffer[0] != '!') { *bp++ = *--cp; *bp = '\0'; continue; } else { register char **ap, **ep; char *s = ""; for (ap = ci -> ci_attrs, ep = ci -> ci_values; *ap; ap++, ep++) { (void) sprintf (bp, "%s%s=\"%s\"", s, *ap, *ep); bp += strlen (bp); s = " "; } } break; case 'm': /* message */ (void) sprintf (bp, "%s", r1bindex (ct -> c_file, '/')); break; case 'P': /* .part */ if (ct -> c_partno) (void) sprintf (bp, ".%s", ct -> c_partno); break; case 'p': /* part */ if (ct -> c_partno) (void) strcpy (bp, ct -> c_partno); break; case 't': /* type */ (void) strcpy (bp, ci -> ci_type); break; case 's': /* subtype */ (void) strcpy (bp, ci -> ci_subtype); break; case '%': goto raw; default: *bp++ = *--cp; *bp = '\0'; continue; } bp += strlen (bp); } else { raw: ; *bp++ = *cp; *bp = '\0'; } if (buffer[0] == '|' || buffer[0] == '!') return show_content_aux (ct, 1, 0, buffer + 1, autosw ? cwd : dir); got_filename: ; ct -> c_storage = add (buffer, NULLCP); /* if (debugsw) { */ (void) fflush (stdout); fprintf (stderr, "storing message %s", ct -> c_file); if (ct -> c_partno) fprintf (stderr, " part %s", ct -> c_partno); fprintf (stderr, " as file %s\n", strncmp (ct -> c_storage, cwd, cwdlen) || ct -> c_storage[cwdlen] != '/' ? ct -> c_storage : ct -> c_storage + cwdlen + 1); /* } */ if (ct -> c_encoding != CE_7BIT) { int cc, fd; if (!ct -> c_ceopenfnx) { advise (NULLCP, "don't know how to decode part %s of message %s", ct -> c_partno, ct -> c_file); return NOTOK; } file = appending || !strcmp (ct -> c_storage, "-") ? NULLCP : ct -> c_storage; if ((fd = (*ct -> c_ceopenfnx) (ct, &file)) == NOTOK) return NOTOK; if (strcmp (file, ct -> c_storage) == 0) { (void) (*ct -> c_ceclosefnx) (ct); return OK; } if (!strcmp (ct -> c_storage, "-")) { int gd; if ((gd = dup (fileno (stdout))) == NOTOK) { advise ("stdout", "unable to dup"); losing: ; (void) (*ct -> c_ceclosefnx) (ct); return NOTOK; } if ((fp = fdopen (gd, appending ? "a" : "w")) == NULL) { advise ("stdout", "unable to fdopen (%d, \"%s\") from", gd, appending ? "a" : "w"); (void) close (gd); goto losing; } } else if ((fp = fopen (ct -> c_storage, appending ? "a" : "w")) == NULL) { advise (ct -> c_storage, "unable to fopen for %s", appending ? "appending" : "writing"); goto losing; } if (append && !*append) (void) copy_some_headers (fp, ct); for (;;) { switch (cc = read (fd, buffer, sizeof buffer)) { case NOTOK: advise (file, "error reading content from"); break; case OK: break; default: (void) fwrite (buffer, sizeof *buffer, cc, fp); continue; } break; } (void) (*ct -> c_ceclosefnx) (ct); if (cc != NOTOK && fflush (fp)) advise (ct -> c_storage, "error writing to"); (void) fclose (fp); return (cc != NOTOK ? OK : NOTOK); } if (!ct -> c_fp && (ct -> c_fp = fopen (ct -> c_file, "r")) == NULL) { advise (ct -> c_file, "unable to open for reading"); return NOTOK; } (void) fseek (ct -> c_fp, pos = ct -> c_begin, 0); last = ct -> c_end; if (!strcmp (ct -> c_storage, "-")) { int gd; if ((gd = dup (fileno (stdout))) == NOTOK) { advise ("stdout", "unable to dup"); return NOTOK; } if ((fp = fdopen (gd, appending ? "a" : "w")) == NULL) { advise ("stdout", "unable to fdopen (%d, \"%s\") from", gd, appending ? "a" : "w"); (void) close (gd); return NOTOK; } } else if ((fp = fopen (ct -> c_storage, appending ? "a" : "w")) == NULL) { advise (ct -> c_storage, "unable to fopen for %s", appending ? "appending" : "writing"); return NOTOK; } if (append && !*append) { (void) copy_some_headers (fp, ct); appending = 1; } else appending = 0; while (fgets (buffer, sizeof buffer - 1, ct -> c_fp)) { if ((pos += strlen (buffer)) > last) { int diff = strlen (buffer) - (pos - last); if (diff >= 0) buffer[diff] = '\0'; } if (appending) switch (buffer[0]) { case ' ': case '\t': if (appending < 0) buffer[0] = 0; break; case '\n': appending = 0; break; default: if (!uprf (buffer, XXX_FIELD_PRF) && !uprf (buffer, "Message-ID:")) { appending = -1; buffer[0] = 0; break; } appending = 1; break; } (void) fputs (buffer, fp); if (pos >= last) break; } if (fflush (fp)) advise (ct -> c_storage, "error writing to"); (void) fclose (fp); (void) fclose (ct -> c_fp), ct -> c_fp = NULL; return OK; } static int copy_some_headers (out, ct) FILE *out; register CT ct; { int state; char buf[BUFSIZ], name[NAMESZ]; FILE *in; if ((in = fopen (ct -> c_file, "r")) == NULL) { advise (ct -> c_file, "unable to open for reading"); return NOTOK; } for (state = FLD;;) { switch (state = m_getfld (state, name, buf, sizeof buf, in)) { case FLD: case FLDPLUS: case FLDEOF: if (uprf (name, XXX_FIELD_PRF) || uleq (name, "Message-ID")) { while (state == FLDPLUS) state = m_getfld (state, name, buf, sizeof buf, in); continue; } fprintf (out, "%s:%s", name, buf); while (state == FLDPLUS) { state = m_getfld (state, name, buf, sizeof buf, in); (void) fputs (buf, out); } if (state != FLDEOF) continue; /* else fall... */ case BODY: case BODYEOF: case FILEEOF: break; case LENERR: case FMTERR: default: break; } break; } (void) fclose (in); return OK; } /* */ static void free_ctinfo (ct) register CT ct; { register char **ap; register CI ci = &ct -> c_ctinfo; if (ci -> ci_type) free (ci -> ci_type); if (ci -> ci_subtype) free (ci -> ci_subtype); for (ap = ci -> ci_attrs; *ap; ap++) free (*ap); if (ci -> ci_comment) free (ci -> ci_comment); if (ci -> ci_magic) free (ci -> ci_magic); } static void free_content (ct) register CT ct; { if (!ct) return; if (ct -> c_partno) free (ct -> c_partno); if (ct -> c_vrsn) free (ct -> c_vrsn); if (ct -> c_ctline) free (ct -> c_ctline); free_ctinfo (ct); if (ct -> c_ctfreefnx) (void) (*ct -> c_ctfreefnx) (ct); if (ct -> c_showproc) free (ct -> c_showproc); if (ct -> c_termproc) free (ct -> c_termproc); if (ct -> c_storeproc) free (ct -> c_storeproc); if (ct -> c_celine) free (ct -> c_celine); if (ct -> c_cefreefnx) (void) (*ct -> c_cefreefnx) (ct, 1); if (ct -> c_id) free (ct -> c_id); if (ct -> c_descr) free (ct -> c_descr); if (ct -> c_file) { if (ct -> c_unlink) (void) unlink (ct -> c_file); free (ct -> c_file); } if (ct -> c_fp) (void) fclose (ct -> c_fp); if (ct -> c_storage) (void) free (ct -> c_storage); free ((char *) ct); } /* */ static int part_ok (ct) register CT ct; { register char **ap; if (ct -> c_type == CT_MULTIPART || npart == 0) return 1; for (ap = parts; *ap; ap++) if (strcmp (*ap, ct -> c_partno) == 0) return 1; return 0; } static int type_ok (ct) register CT ct; { register char **ap; char buffer[BUFSIZ]; register CI ci = &ct -> c_ctinfo; if (ct -> c_type == CT_MULTIPART || ntype == 0) return 1; (void) sprintf (buffer, "%s/%s", ci -> ci_type, ci -> ci_subtype); for (ap = types; *ap; ap++) if (uleq (*ap, ci -> ci_type) || uleq (*ap, buffer)) return 1; return 0; } /* CONTENTS */ struct k2v { char *kv_key; int kv_value; }; /* TEXT */ #define TEXT_UNKNOWN 0x00 #define TEXT_PLAIN 0x01 #define TEXT_RICHTEXT 0x02 struct text { int tx_charset; #define CHARSET_UNKNOWN 0x00 #define CHARSET_USASCII 0x01 #define CHARSET_LATIN 0x02 }; static struct k2v Charset[] = { "us-ascii", CHARSET_USASCII, "iso-8859-1", CHARSET_LATIN, NULL, CHARSET_UNKNOWN /* this one must be last! */ }; static int free_text (ct) register CT ct; { register struct text *t = (struct text *) ct -> c_ctparams; if (!t) return; free ((char *) t); ct -> c_ctparams = NULL; } static struct k2v SubText[] = { "plain", TEXT_PLAIN, "richtext", TEXT_RICHTEXT, NULL, TEXT_UNKNOWN /* this one must be last! */ }; static int InitText (ct) register CT ct; { char buffer[BUFSIZ]; register char **ap, **ep; register struct k2v *kv; register CI ci = &ct -> c_ctinfo; if (!*ci -> ci_subtype) /* XXX: attmail bogosity! */ ci -> ci_subtype = add ("plain", ci -> ci_subtype); for (kv = SubText; kv -> kv_key; kv++) if (uleq (ci -> ci_subtype, kv -> kv_key)) break; if ((ct -> c_subtype = kv -> kv_value) == TEXT_PLAIN) { (void) sprintf (buffer, "%%p%s '%%F'", progsw ? progsw : moreproc && *moreproc ? moreproc : "more"); ct -> c_showproc = add (buffer, NULLCP); } for (ap = ci -> ci_attrs, ep = ci -> ci_values; *ap; ap++, ep++) if (uleq (*ap, "charset")) { char *cp; register struct text *t; if ((t = (struct text *) calloc (1, sizeof *t)) == NULL) adios (NULLCP, "out of memory"); ct -> c_ctparams = (caddr_t) t; ct -> c_ctfreefnx = free_text; for (kv = Charset; kv -> kv_key; kv++) if (uleq (*ep, kv -> kv_key)) { (void) sprintf (buffer, "%s-charset-%s", invo_name, kv -> kv_key); break; } t -> tx_charset = kv -> kv_value; if (!kv -> kv_key) (void) sprintf (buffer, "%s-charset-%s", invo_name, *ep); if ((!mm_charset || !uleq (mm_charset, *ep)) && (cp = m_find (buffer))) ct -> c_termproc = getcpy (cp); break; } return OK; } /* MULTIPART */ #define MULTI_UNKNOWN 0x00 #define MULTI_MIXED 0x01 #define MULTI_ALTERNATE 0x02 #define MULTI_DIGEST 0x03 #define MULTI_PARALLEL 0x04 struct multipart { char *mp_start; char *mp_stop; struct part { CT mp_part; struct part *mp_next; } *mp_parts; }; static int list_multi (ct, toplevel) register CT ct; int toplevel; { register struct multipart *m = (struct multipart *) ct -> c_ctparams; register struct part *part; (void) list_content (ct, toplevel); for (part = m -> mp_parts; part; part = part -> mp_next) { register CT p = part -> mp_part; if (part_ok (p) && type_ok (p) && p -> c_ctlistfnx) (void) (*p -> c_ctlistfnx) (p, 0); } return OK; } static int show_multi (ct, serial, alternate) register CT ct; int serial, alternate; { int alternating, nowalternate, nowserial, result; register struct multipart *m = (struct multipart *) ct -> c_ctparams; register struct part *part; register CT p; TYPESIG (*hstat) (), (*istat) (), (*qstat) (), (*tstat) (); alternating = 0; nowalternate = alternate; switch (ct -> c_subtype) { case MULTI_PARALLEL: if (!(nowserial = serialsw)) { set_signals: ; hstat = signal (SIGHUP, SIG_IGN); istat = signal (SIGINT, SIG_IGN); qstat = signal (SIGQUIT, SIG_IGN); tstat = signal (SIGTERM, SIG_IGN); } break; case MULTI_ALTERNATE: nowalternate = alternating = 1; /* and fall... */ default: if (!(nowserial = serial)) goto set_signals; break; } /* alternate -> we're inside an alternative alternating -> we are an alternative */ result = OK; for (part = m -> mp_parts; part; part = part -> mp_next) { p = part -> mp_part; if (part_ok (p) && type_ok (p) && p -> c_ctshowfnx) { switch ((*p -> c_ctshowfnx) (p, nowserial, nowalternate)) { case NOTOK: if (alternate && !alternating) { result = NOTOK; goto out; } continue; case OK: case DONE: if (alternating) { result = DONE; break; } if (alternate) alternate = nowalternate = 0; continue; } break; } } if (alternating && !part) { if (!alternate) content_error (NULLCP, ct, "don't know how to display any of the contents"); result = NOTOK; goto out; } if (serial && !nowserial) { int pid, kids; #if defined(BSD42) && !defined(WAITINT) union wait status; #else int status; #endif kids = 0; for (part = m -> mp_parts; part; part = part -> mp_next) { p = part -> mp_part; if (p -> c_pid > OK) if (kill (p -> c_pid, 0) == NOTOK) p -> c_pid = 0; else kids++; } while (kids > 0 && (pid = wait (&status)) != NOTOK) { #if defined(BSD42) && !defined(WAITINT) (void) pidcheck (status.w_status); #else (void) pidcheck (status); #endif for (part = m -> mp_parts; part; part = part -> mp_next) { p = part -> mp_part; if (xpid == pid) xpid = 0; if (p -> c_pid == pid) { p -> c_pid = 0; kids--; break; } } } } out: ; if (!nowserial) { (void) signal (SIGHUP, hstat); (void) signal (SIGINT, istat); (void) signal (SIGQUIT, qstat); (void) signal (SIGTERM, tstat); } return result; } /* ARGSUSED */ static int store_multi (ct, unused) register CT ct; char *unused; { int result; register struct multipart *m = (struct multipart *) ct -> c_ctparams; register struct part *part; result = NOTOK; for (part = m -> mp_parts; part; part = part -> mp_next) { register CT p = part -> mp_part; if (part_ok (p) && type_ok (p) && p -> c_ctstorefnx && (result = (*p -> c_ctstorefnx) (p, NULLCP)) == OK && ct -> c_subtype == MULTI_ALTERNATE) break; } return result; } static int free_multi (ct) register CT ct; { register struct multipart *m = (struct multipart *) ct -> c_ctparams; register struct part *part, *next; if (!m) return; if (m -> mp_start) free (m -> mp_start); if (m -> mp_stop) free (m -> mp_stop); for (part = m -> mp_parts; part; part = next) { next = part -> mp_next; free_content (part -> mp_part); free ((char *) part); } m -> mp_parts = NULL; free ((char *) m); ct -> c_ctparams = NULL; } static struct k2v SubMultiPart[] = { "mixed", MULTI_MIXED, "alternative", MULTI_ALTERNATE, "digest", MULTI_DIGEST, "parallel", MULTI_PARALLEL, NULL, MULTI_UNKNOWN /* this one must be last! */ }; static int InitMultiPart (ct) register CT ct; { int inout; long last, pos; register char *cp, *dp, **ap, **ep; char *bp, buffer[BUFSIZ]; register struct multipart *m; register struct k2v *kv; register struct part *part, **next; register CI ci = &ct -> c_ctinfo; register CT p; FILE *fp; ct -> c_ctshowfnx = NULL; ct -> c_ctstorefnx = NULL; if (ct -> c_encoding != CE_7BIT) { admonish (NULLCP, "\"%s/%s\" type in message %s should be encoded in 7bit", ci -> ci_type, ci -> ci_subtype, ct -> c_file); return NOTOK; } for (kv = SubMultiPart; kv -> kv_key; kv++) if (uleq (ci -> ci_subtype, kv -> kv_key)) break; ct -> c_subtype = kv -> kv_value; for (ap = ci -> ci_attrs, ep = ci -> ci_values; *ap; ap++, ep++) if (uleq (*ap, "boundary")) { bp = *ep; break; } if (!*ap) { advise (NULLCP, "a \"boundary\" parameter is mandatory for \"%s/%s\" type in message %s's %s: field", ci -> ci_type, ci -> ci_subtype, ct -> c_file, TYPE_FIELD); return NOTOK; } if ((m = (struct multipart *) calloc (1, sizeof *m)) == NULL) adios (NULLCP, "out of memory"); ct -> c_ctparams = (caddr_t) m; ct -> c_ctlistfnx = list_multi; ct -> c_ctshowfnx = show_multi; ct -> c_ctstorefnx = store_multi; ct -> c_ctfreefnx = free_multi; for (cp = bp; isspace (*cp); cp++) continue; if (!*cp) { advise (NULLCP, "invalid \"boundary\" parameter for \"%s/%s\" type in message %s's %s: field", ci -> ci_type, ci -> ci_subtype, ct -> c_file, TYPE_FIELD); return NOTOK; } for (cp = bp, dp = cp + strlen (cp) - 1; dp > cp; dp--) if (!isspace (*dp)) break; *++dp = '\0'; m -> mp_start = concat (bp, "\n", NULLCP); m -> mp_stop = concat (bp, "--\n", NULLCP); if (!ct -> c_fp && (ct -> c_fp = fopen (ct -> c_file, "r")) == NULL) { advise (ct -> c_file, "unable to open for reading"); return NOTOK; } (void) fseek (fp = ct -> c_fp, pos = ct -> c_begin, 0); last = ct -> c_end; next = &m -> mp_parts, part = NULL, inout = 1; while (fgets (buffer, sizeof buffer - 1, fp)) { if (pos > last) break; pos += strlen (buffer); if (buffer[0] != '-' || buffer[1] != '-') continue; if (inout) { if (strcmp (buffer + 2, m -> mp_start)) continue; next_part: ; if ((part = (struct part *) calloc (1, sizeof *part)) == NULL) adios (NULLCP, "out of memory"); *next = part, next = &part -> mp_next; if ((p = get_content (fp, ct -> c_file, rfc934sw && ct -> c_subtype == MULTI_DIGEST ? -1 : 0)) == NULLCT) { (void) fclose (ct -> c_fp), ct -> c_fp = NULL; return NOTOK; } p -> c_fp = NULL; part -> mp_part = p; (void) fseek (fp, pos = p -> c_begin, 0); inout = 0; } else if (strcmp (buffer + 2, m -> mp_start) == 0) { inout = 1; end_part: ; p = part -> mp_part; p -> c_end = ftell (fp) - (strlen (buffer) + 1); if (p -> c_end < p -> c_begin) p -> c_begin = p -> c_end; if (inout) goto next_part; goto last_part; } else if (strcmp (buffer + 2, m -> mp_stop) == 0) goto end_part; } advise (NULLCP, "bogus multipart content in message %s", ct -> c_file); if (!inout && part) { p = part -> mp_part; p -> c_end = ct -> c_end; if (p -> c_begin >= p -> c_end) { for (next = &m -> mp_parts; *next != part; next = &((*next) -> mp_next)) continue; *next = NULL; free_content (p); free ((char *) part); } } last_part: ; if (ct -> c_subtype == MULTI_ALTERNATE && m -> mp_parts -> mp_next) { register int i; register struct part **base, **bmp; i = 0; for (part = m -> mp_parts; part; part = part -> mp_next) i++; if ((base = (struct part **) calloc ((unsigned) (i + 1), sizeof *base)) == NULL) adios (NULLCP, "out of memory"); bmp = base; for (part = m -> mp_parts; part; part = part -> mp_next) *bmp++ = part; *bmp = NULL; next = &m -> mp_parts; for (bmp--; bmp >= base; bmp--) { part = *bmp; *next = part, next = &part -> mp_next; } *next = NULL; free ((char *) base); } { int partnum; register char *pp; char partnam[BUFSIZ]; if (ct -> c_partno) { (void) sprintf (partnam, "%s.", ct -> c_partno); pp = partnam + strlen (partnam); } else pp = partnam; for (part = m -> mp_parts, partnum = 1; part; part = part -> mp_next, partnum++) { p = part -> mp_part; (void) sprintf (pp, "%d", partnum); p -> c_partno = add (partnam, NULLCP); if (p -> c_ctinitfnx && (*p -> c_ctinitfnx) (p) == NOTOK) { (void) fclose (ct -> c_fp), ct -> c_fp = NULL; return NOTOK; } } } (void) fclose (ct -> c_fp), ct -> c_fp = NULL; return OK; } /* MESSAGE */ #define MESSAGE_UNKNOWN 0x00 #define MESSAGE_RFC822 0x01 #define MESSAGE_PARTIAL 0x02 #define MESSAGE_EXTERNAL 0x03 struct partial { char *pm_partid; int pm_partno; int pm_maxno; int pm_marked; int pm_stored; }; static int list_partial (ct, toplevel) register CT ct; int toplevel; { register struct partial *p = (struct partial *) ct -> c_ctparams; (void) list_content (ct, toplevel); if (verbosw) { printf ("\t [message %s, part %d", p -> pm_partid, p -> pm_partno); if (p -> pm_maxno) printf (" of %d", p -> pm_maxno); printf ("]\n"); } return OK; } static int ct_compar (a, b) CT *a, *b; { register struct partial *am = (struct partial *) ((*a) -> c_ctparams); register struct partial *bm = (struct partial *) ((*b) -> c_ctparams); return (am -> pm_marked - bm -> pm_marked); } /* ARGSUSED */ static int store_partial (ct, unused) register CT ct; char *unused; { int cur, hi, i; register CT p, *ctp, *ctq; CT *base; struct partial *qm = (struct partial *) ct -> c_ctparams; if (qm -> pm_stored) return OK; hi = i = 0; for (ctp = cts; p = *ctp; ctp++) if (p -> c_type == CT_MESSAGE && p -> c_subtype == ct -> c_subtype) { register struct partial *pm = (struct partial *) p -> c_ctparams; if (!pm -> pm_stored && strcmp (qm -> pm_partid, pm -> pm_partid) == 0) { pm -> pm_marked = pm -> pm_partno; if (pm -> pm_maxno) hi = pm -> pm_maxno; pm -> pm_stored = 1; i++; } else pm -> pm_marked = 0; } if (hi == 0) { advise (NULLCP, "missing (at least) last part of multipart message"); return NOTOK; } if ((base = (CT *) calloc ((unsigned) (i + 1), sizeof *base)) == NULL) adios (NULLCP, "out of memory"); ctq = base; for (ctp = cts; p = *ctp; ctp++) if (p -> c_type == CT_MESSAGE && p -> c_subtype == ct -> c_subtype) { register struct partial *pm = (struct partial *) p -> c_ctparams; if (pm -> pm_marked) *ctq++ = p; } *ctq = NULL; if (i > 1) qsort ((char *) base, i, sizeof *base, ct_compar); cur = 1; for (ctq = base; p = *ctq; ctq++) { register struct partial *pm = (struct partial *) p -> c_ctparams; if (pm -> pm_marked != cur) { if (pm -> pm_marked == cur - 1) { admonish (NULLCP, "duplicate part %d of %d part multipart message", pm -> pm_marked, hi); continue; } missing_part: ; advise (NULLCP, "missing %spart %d of %d part multipart message", cur != hi ? "(at least) " : "", cur, hi); goto losing; } else cur++; } if (hi != --cur) { cur = hi; goto missing_part; } ctq = base; ct = *ctq++; if (store_content (ct, "") == NOTOK) { losing: ; free ((char *) base); return NOTOK; } for (; p = *ctq; ctq++) if (store_content (p, ct -> c_storage) == NOTOK) goto losing; free ((char *) base); return OK; } static int free_partial (ct) register CT ct; { register struct partial *p = (struct partial *) ct -> c_ctparams; if (!p) return; if (p -> pm_partid) free (p -> pm_partid); free ((char *) p); ct -> c_ctparams = NULL; } struct exbody { CT eb_parent; CT eb_content; char *eb_partno; char *eb_access; int eb_flags; char *eb_name; char *eb_permission; char *eb_site; char *eb_dir; char *eb_mode; char *eb_server; char *eb_body; unsigned long eb_size; }; static int openFile (); static int openFTP (); static int openMail (); /* NOTE WELL: si_key MUST NOT have value of NOTOK */ static struct str2init str2methods[] = { "afs", 1, openFile, "anon-ftp", 1, openFTP, "ftp", 0, openFTP, "local-file", 0, openFile, "mail-server", 0, openMail, NULL }; static int params_external (ct, composing) register CT ct; int composing; { register char **ap, **ep; register struct exbody *e = (struct exbody *) ct -> c_ctparams; register CI ci = &ct -> c_ctinfo; for (ap = ci -> ci_attrs, ep = ci -> ci_values; *ap; ap++, ep++) { if (uleq (*ap, "access-type")) { register struct str2init *s2i; register CT p = e -> eb_content; for (s2i = str2methods; s2i -> si_key; s2i++) if (uleq (*ep, s2i -> si_key)) break; if (!s2i -> si_key) { e -> eb_access = *ep; e -> eb_flags = NOTOK; p -> c_encoding = CE_EXTERNAL; continue; } e -> eb_access = s2i -> si_key; e -> eb_flags = s2i -> si_value; p -> c_encoding = CE_EXTERNAL; if (init_encoding (p, s2i -> si_init) == NOTOK) return NOTOK; continue; } if (uleq (*ap, "name")) { e -> eb_name = *ep; continue; } if (uleq (*ap, "permission")) { e -> eb_permission = *ep; continue; } if (uleq (*ap, "site")) { e -> eb_site = *ep; continue; } if (uleq (*ap, "directory")) { e -> eb_dir = *ep; continue; } if (uleq (*ap, "mode")) { e -> eb_mode = *ep; continue; } if (uleq (*ap, "size")) { (void) sscanf (*ep, "%lu", &e -> eb_size); continue; } if (uleq (*ap, "server")) { e -> eb_server = *ep; continue; } if (composing && uleq (*ap, "body")) { e -> eb_body = getcpy (*ep); continue; } } if (!e -> eb_access) { advise (NULLCP, "invalid parameters for \"%s/%s\" type in message %s's %s field", ci -> ci_type, ci -> ci_subtype, ct -> c_file, TYPE_FIELD); return NOTOK; } return OK; } static int list_external (ct, toplevel) register CT ct; int toplevel; { register struct exbody *e = (struct exbody *) ct -> c_ctparams; (void) list_content (ct, toplevel); if (verbosw) { if (e -> eb_name) printf ("\t retrieve %s\n", e -> eb_name); if (e -> eb_dir) printf ("\t in directory %s\n", e -> eb_dir); if (e -> eb_site) printf ("\t from %s\n", e -> eb_site); if (e -> eb_server) printf ("\t from mailbox %s\n", e -> eb_server); printf ("\t using %s", e -> eb_access); if (e -> eb_mode) printf (" (in %s mode)", e -> eb_mode); if (e -> eb_permission) printf (" (permission %s)", e -> eb_permission); if (e -> eb_flags == NOTOK) printf (" [service unavailable]"); printf ("\n"); } (void) list_content (e -> eb_content, 0); return OK; } static int show_external (ct, serial, alternate) register CT ct; int serial, alternate; { register struct exbody *e = (struct exbody *) ct -> c_ctparams; register CT p = e -> eb_content; if (!type_ok (p)) return OK; if (p -> c_ctshowfnx) return (*p -> c_ctshowfnx) (p, serial, alternate); content_error (NULLCP, p, "don't know how to display content"); return NOTOK; } static int store_external (ct) register CT ct; { int result = NOTOK; register struct exbody *e = (struct exbody *) ct -> c_ctparams; register CT p = e -> eb_content; if (!type_ok (p)) return OK; p -> c_partno = ct -> c_partno; if (p -> c_ctstorefnx) result = (*p -> c_ctstorefnx) (p, NULLCP); p -> c_partno = NULL; return result; } static int free_external (ct) register CT ct; { register struct exbody *e = (struct exbody *) ct -> c_ctparams; if (!e) return; free_content (e -> eb_content); if (e -> eb_body) free (e -> eb_body); free ((char *) e); ct -> c_ctparams = NULL; } static struct k2v SubMessage[] = { "rfc822", MESSAGE_RFC822, "partial", MESSAGE_PARTIAL, "external-body", MESSAGE_EXTERNAL, NULL, MESSAGE_UNKNOWN /* this one must be last! */ }; static int InitMessage (ct) register CT ct; { register struct k2v *kv; register CI ci = &ct -> c_ctinfo; if (ct -> c_encoding != CE_7BIT) { admonish (NULLCP, "\"%s/%s\" type in message %s should be encoded in 7bit", ci -> ci_type, ci -> ci_subtype, ct -> c_file); return NOTOK; } if (!*ci -> ci_subtype) /* XXX: attmail bogosity! */ ci -> ci_subtype = add ("rfc822", ci -> ci_subtype); for (kv = SubMessage; kv -> kv_key; kv++) if (uleq (ci -> ci_subtype, kv -> kv_key)) break; switch (ct -> c_subtype = kv -> kv_value) { case MESSAGE_RFC822: ct -> c_showproc = add ("%pshow -file '%F'", NULLCP); break; case MESSAGE_PARTIAL: { register char **ap, **ep; register struct partial *p; ct -> c_ctshowfnx = NULL; ct -> c_ctstorefnx = NULL; if ((p = (struct partial *) calloc (1, sizeof *p)) == NULL) adios (NULLCP, "out of memory"); ct -> c_ctparams = (caddr_t) p; ct -> c_ctfreefnx = free_partial; for (ap = ci -> ci_attrs, ep = ci -> ci_values; *ap; ap++, ep++) { if (uleq (*ap, "id")) { p -> pm_partid = add (*ep, NULLCP); continue; } if (uleq (*ap, "number")) { if (sscanf (*ep, "%d", &p -> pm_partno) != 1 || p -> pm_partno < 1) { invalid_param: ; advise (NULLCP, "invalid %s parameter for \"%s/%s\" type in message %s's %s field", *ap, ci -> ci_type, ci -> ci_subtype, ct -> c_file, TYPE_FIELD); return NOTOK; } continue; } if (uleq (*ap, "total")) { if (sscanf (*ep, "%d", &p -> pm_maxno) != 1 || p -> pm_maxno < 1) goto invalid_param; continue; } } if (!p -> pm_partid || !p -> pm_partno || (p -> pm_maxno && p -> pm_partno > p -> pm_maxno)) { advise (NULLCP, "invalid parameters for \"%s/%s\" type in message %s's %s field", ci -> ci_type, ci -> ci_subtype, ct -> c_file, TYPE_FIELD); return NOTOK; } ct -> c_ctlistfnx = list_partial; ct -> c_ctstorefnx = store_partial; } break; case MESSAGE_EXTERNAL: { int exresult; register struct exbody *e; CT p; FILE *fp; ct -> c_ctshowfnx = NULL; ct -> c_ctstorefnx = NULL; if ((e = (struct exbody *) calloc (1, sizeof *e)) == NULL) adios (NULLCP, "out of memory"); ct -> c_ctparams = (caddr_t) e; ct -> c_ctfreefnx = free_external; if (!ct -> c_fp && (ct -> c_fp = fopen (ct -> c_file, "r")) == NULL) { advise (ct -> c_file, "unable to open for reading"); return NOTOK; } (void) fseek (fp = ct -> c_fp, ct -> c_begin, 0); if ((p = get_content (fp, ct -> c_file, 0)) == NULLCT) { (void) fclose (ct -> c_fp), ct -> c_fp = NULL; return NOTOK; } e -> eb_parent = ct; e -> eb_content = p; p -> c_ctextern = (caddr_t) e; if ((exresult = params_external (ct, 0)) != NOTOK && p -> c_ceopenfnx == openMail) { int cc, size; char *bp; if ((size = ct -> c_end - p -> c_begin) <= 0) { content_error (NULLCP, ct, "empty body for access-type=mail-server"); goto no_body; } if ((e -> eb_body = bp = malloc ((unsigned) size)) == NULL) adios (NULLCP, "out of memory"); (void) fseek (p -> c_fp, p -> c_begin, 0); while (size > 0) switch (cc = fread (bp, sizeof *bp, size, p -> c_fp)) { case NOTOK: adios ("failed", "fread"); case OK: adios (NULLCP, "unexpected EOF from fread"); default: bp += cc, size -= cc; break; } *bp = 0; no_body: ; } p -> c_fp = NULL; p -> c_end = p -> c_begin; (void) fclose (ct -> c_fp), ct -> c_fp = NULL; ct -> c_ctlistfnx = list_external; if (exresult == NOTOK) return NOTOK; if (e -> eb_flags == NOTOK) return OK; if (e -> eb_name && autosw) { char *cp = e -> eb_name; if (*cp != '/' && *cp != '.' && *cp != '|' && *cp != '!' && !index (cp, '%')) { if (!ct -> c_storeproc) ct -> c_storeproc = add (cp, NULLCP); if (!p -> c_storeproc) p -> c_storeproc = add (cp, NULLCP); } } ct -> c_ctshowfnx = show_external; ct -> c_ctstorefnx = store_external; switch (p -> c_type) { case CT_MULTIPART: break; case CT_MESSAGE: if (p -> c_subtype != MESSAGE_RFC822) break; /* else fall... */ default: e -> eb_partno = ct -> c_partno; if (p -> c_ctinitfnx) (void) (*p -> c_ctinitfnx) (p); break; } } break; default: break; } return OK; } /* APPLICATION */ #define APPLICATION_UNKNOWN 0x00 #define APPLICATION_OCTETS 0x01 #define APPLICATION_ODA 0x02 #define APPLICATION_POSTSCRIPT 0x03 static int list_application (ct, toplevel) register CT ct; int toplevel; { (void) list_content (ct, toplevel); if (verbosw) { register char **ap, **ep; register CI ci = &ct -> c_ctinfo; for (ap = ci -> ci_attrs, ep = ci -> ci_values; *ap; ap++, ep++) printf ("\t %s=\"%s\"\n", *ap, *ep); } return OK; } static struct k2v SubApplication[] = { "octet-stream", APPLICATION_OCTETS, "oda", APPLICATION_ODA, "postscript", APPLICATION_POSTSCRIPT, NULL, APPLICATION_UNKNOWN /* this one must be last! */ }; static int InitApplication (ct) register CT ct; { register char **ap, **ep; register struct k2v *kv; register CI ci = &ct -> c_ctinfo; ct -> c_ctlistfnx = list_application; for (kv = SubApplication; kv -> kv_key; kv++) if (uleq (ci -> ci_subtype, kv -> kv_key)) break; if (autosw && !ct -> c_storeproc) for (ap = ci -> ci_attrs, ep = ci -> ci_values; *ap; ap++, ep++) if (uleq (*ap, "name")) { register char *cp; if (*(cp = *ep) != '/' && *cp != '.' && *cp != '|' && *cp != '!' && !index (cp, '%')) ct -> c_storeproc = add (cp, NULLCP); break; } if ((ct -> c_subtype = kv -> kv_value) == APPLICATION_OCTETS) { int tarP, zP; tarP = zP = 0; for (ap = ci -> ci_attrs, ep = ci -> ci_values; *ap; ap++, ep++) { if (uleq (*ap, "type")) { if (!uleq (*ep, "tar")) break; tarP = 1; continue; } if (uleq (*ap, "conversions") && (uleq (*ep, "compress") || uleq (*ep, "x-compress"))) { zP = 1; continue; } } if (tarP) { ct -> c_showproc = add (zP ? "%euncompress | tar tvf -" : "%etar tvf -", NULLCP); if (!ct -> c_storeproc) if (autosw) { ct -> c_storeproc = add (zP ? "| uncompress | tar xvpf -" : "| tar xvpf -", NULLCP); ct -> c_umask = 0022; } else ct -> c_storeproc = add (zP ? "%m%P.tar.Z" : "%m%P.tar", NULLCP); } } return OK; } /* ENCODINGS */ struct cefile { char *ce_file; int ce_unlink; FILE *ce_fp; }; static int list_encoding (ct) register CT ct; { register struct cefile *ce = (struct cefile *) ct -> c_ceparams; if (ce) fprintf (stderr, " decoded fp 0x%x file \"%s\"\n", ce -> ce_fp, ce -> ce_file ? ce -> ce_file : ""); return OK; } static int close_encoding (ct) register CT ct; { register struct cefile *ce = (struct cefile *) ct -> c_ceparams; if (!ce) return; if (ce -> ce_fp) { (void) fclose (ce -> ce_fp); ce -> ce_fp = NULL; } } static unsigned long size_encoding (ct) register CT ct; { int fd; unsigned long size; char *file; register struct cefile *ce = (struct cefile *) ct -> c_ceparams; struct stat st; if (!ce) { estimate: ; return (ct -> c_end - ct -> c_begin); } if (ce -> ce_fp && fstat (fileno (ce -> ce_fp), &st) != NOTOK) return (long) st.st_size; if (ce -> ce_file) return stat (ce -> ce_file, &st) != NOTOK ? (long) st.st_size : 0L; if (ct -> c_encoding == CE_EXTERNAL) goto estimate; file = NULL; if ((fd = (*ct -> c_ceopenfnx) (ct, &file)) == NOTOK) goto estimate; size = fstat (fd, &st) != NOTOK ? (long) st.st_size : 0L; (*ct -> c_ceclosefnx) (ct); return size; } static int free_encoding (ct, toplevel) register CT ct; int toplevel; { register struct cefile *ce = (struct cefile *) ct -> c_ceparams; if (!ce) return; if (ce -> ce_fp) { (void) fclose (ce -> ce_fp); ce -> ce_fp = NULL; } if (ce -> ce_file) { if (ce -> ce_unlink) (void) unlink (ce -> ce_file); free (ce -> ce_file); } if (toplevel) { free ((char *) ce); ct -> c_ceparams = NULL; } else ct -> c_ceopenfnx = NULL; } static init_encoding (ct, openfnx) register CT ct; int (*openfnx) (); { register struct cefile *ce; if ((ce = (struct cefile *) calloc (1, sizeof *ce)) == NULL) adios (NULLCP, "out of memory"); ct -> c_ceparams = (caddr_t) ce; ct -> c_ceopenfnx = openfnx; ct -> c_ceclosefnx = close_encoding; ct -> c_cesizefnx = size_encoding; ct -> c_celistfnx = list_encoding; ct -> c_cefreefnx = free_encoding; return OK; } /* BASE64 */ static unsigned char b642nib[0x80] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3e, 0xff, 0xff, 0xff, 0x3f, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0xff, 0xff, 0xff, 0xff, 0xff }; static int openBase64 (ct, file) register CT ct; char **file; { int bitno, cc, fd, len, skip; unsigned long bits; register char *cp, *ep; unsigned char value, *b = (unsigned char *) &bits, *b1 = &b[endian > 0 ? 1 : 2], *b2 = &b[endian > 0 ? 2 : 1], *b3 = &b[endian > 0 ? 3 : 0]; char buffer[BUFSIZ]; register struct cefile *ce = (struct cefile *) ct -> c_ceparams; if (ce -> ce_fp) goto ready_to_go; if (ce -> ce_file) { if ((ce -> ce_fp = fopen (ce -> ce_file, "r")) == NULL) { content_error (ce -> ce_file, ct, "unable to fopen for reading"); return NOTOK; } *file = ce -> ce_file; return fileno (ce -> ce_fp); } ce -> ce_unlink = *file == NULL; if ((ce -> ce_fp = fopen (ce -> ce_file = add (*file ? *file : m_scratch ("", tmp), NULLCP), "w+")) == NULL) { content_error (ce -> ce_file, ct, "unable to fopen for writing and reading"); return NOTOK; } if ((len = ct -> c_end - ct -> c_begin) < 0) adios (NULLCP, "internal error(1)"); if (!ct -> c_fp && (ct -> c_fp = fopen (ct -> c_file, "r")) == NULL) { content_error (ct -> c_file, ct, "unable to open for reading"); return NOTOK; } (void) lseek (fd = fileno (ct -> c_fp), (off_t)ct -> c_begin, 0); bitno = 18, bits = 0L, skip = 0; while (len > 0) switch (cc = read (fd, buffer, sizeof buffer - 1)) { case NOTOK: content_error (ct -> c_file, ct, "error reading from"); goto clean_up; case OK: content_error (NULLCP, ct, "premature eof"); goto clean_up; default: if (cc > len) cc = len; len -= cc; for (ep = (cp = buffer) + cc; cp < ep; cp++) switch (*cp) { default: if (isspace (*cp)) break; if (skip || (*cp & 0x80) || (value = b642nib[*cp & 0x7f]) > 0x3f) { if (debugsw) fprintf (stderr, "*cp=0x%x pos=%ld skip=%d\n", *cp, (long) lseek (fd, (off_t)0, 1) - (ep - cp), skip); content_error (NULLCP, ct, "invalid BASE64 encoding -- continuing"); continue; } bits |= value << bitno; test_end: ; if ((bitno -= 6) < 0) { (void) putc ((char) *b1, ce -> ce_fp); if (skip < 2) { (void) putc ((char) *b2, ce -> ce_fp); if (skip < 1) (void) putc ((char) *b3, ce -> ce_fp); } if (ferror (ce -> ce_fp)) { content_error (ce -> ce_file, ct, "error writing to"); goto clean_up; } bitno = 18, bits = 0L, skip = 0; } break; case '=': if (++skip > 3) goto self_delimiting; goto test_end; } } if (bitno != 18) { if (debugsw) fprintf (stderr, "premature ending (bitno %d)\n", bitno); content_error (NULLCP, ct, "invalid BASE64 encoding"); goto clean_up; } self_delimiting: ; (void) fseek (ct -> c_fp, 0L, 0); if (fflush (ce -> ce_fp)) { content_error (ce -> ce_file, ct, "error writing to"); goto clean_up; } ready_to_go: ; (void) fseek (ce -> ce_fp, 0L, 0); *file = ce -> ce_file; return fileno (ce -> ce_fp); clean_up: ; free_encoding (ct, 0); return NOTOK; } static int InitBase64 (ct) register CT ct; { return init_encoding (ct, openBase64); } static int set_endian () { union { long l; char c[sizeof (long)]; } un; un.l = 1; endian = un.c[0] ? -1 : 1; if (debugsw) fprintf (stderr, "%s endian architecture\n", endian > 0 ? "big" : "little"); mm_charset = getenv ("MM_CHARSET"); } /* QUOTED */ static char hex2nib[0x80] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; static int openQuoted (ct, file) register CT ct; char **file; { int cc, len, quoted; register char *cp, *ep; char buffer[BUFSIZ]; unsigned char mask; register struct cefile *ce = (struct cefile *) ct -> c_ceparams; if (ce -> ce_fp) goto ready_to_go; if (ce -> ce_file) { if ((ce -> ce_fp = fopen (ce -> ce_file, "r")) == NULL) { content_error (ce -> ce_file, ct, "unable to fopen for reading"); return NOTOK; } *file = ce -> ce_file; return fileno (ce -> ce_fp); } ce -> ce_unlink = *file == NULL; if ((ce -> ce_fp = fopen (ce -> ce_file = add (*file ? *file : m_scratch ("", tmp), NULLCP), "w+")) == NULL) { content_error (ce -> ce_file, ct, "unable to fopen for writing and reading"); return NOTOK; } if ((len = ct -> c_end - ct -> c_begin) < 0) adios (NULLCP, "internal error(2)"); if (!ct -> c_fp && (ct -> c_fp = fopen (ct -> c_file, "r")) == NULL) { content_error (ct -> c_file, ct, "unable to open for reading"); return NOTOK; } (void) fseek (ct -> c_fp, ct -> c_begin, 0); quoted = 0; #ifdef lint mask = 0; #endif while (len > 0) { char *dp; if (fgets (buffer, sizeof buffer - 1, ct -> c_fp) == NULL) { content_error (NULLCP, ct, "premature eof"); goto clean_up; } if ((cc = strlen (buffer)) > len) cc = len; len -= cc; for (ep = (cp = buffer) + cc - 1; cp <= ep; ep--) if (!isspace (*ep)) break; *++ep = '\n', ep++; for (; cp < ep; cp++) { if (quoted) { if (quoted > 1) { if (!isxdigit (*cp)) { invalid_hex: ; dp = "expecting hexidecimal-digit"; goto invalid_encoding; } mask <<= 4; mask |= hex2nib[*cp & 0x7f]; (void) putc (mask, ce -> ce_fp); } else switch (*cp) { case ':': (void) putc (*cp, ce -> ce_fp); break; default: if (!isxdigit (*cp)) goto invalid_hex; mask = hex2nib[*cp & 0x7f]; quoted = 2; continue; } if (ferror (ce -> ce_fp)) { content_error (ce -> ce_file, ct, "error writing to"); goto clean_up; } quoted = 0; continue; } switch (*cp) { default: if (*cp < '!' || *cp > '~') { int i; dp = "expecting character in range [!..~]"; invalid_encoding: ; i = strlen (invo_name) + 2; content_error (NULLCP, ct, "invalid QUOTED-PRINTABLE encoding -- %s,\n%*.*sbut got char 0x%x", dp, i, i, "", *cp); goto clean_up; } /* and fall...*/ case ' ': case '\t': case '\n': (void) putc (*cp, ce -> ce_fp); if (ferror (ce -> ce_fp)) { content_error (ce -> ce_file, ct, "error writing to"); goto clean_up; } break; case '=': if (*++cp != '\n') { quoted = 1; cp--; } break; } } } if (quoted) { content_error (NULLCP, ct, "invalid QUOTED-PRINTABLE encoding -- end-of-content while still quoting"); goto clean_up; } (void) fseek (ct -> c_fp, 0L, 0); if (fflush (ce -> ce_fp)) { content_error (ce -> ce_file, ct, "error writing to"); goto clean_up; } ready_to_go: ; (void) fseek (ce -> ce_fp, 0L, 0); *file = ce -> ce_file; return fileno (ce -> ce_fp); clean_up: ; free_encoding (ct, 0); return NOTOK; } static int InitQuoted (ct) register CT ct; { return init_encoding (ct, openQuoted); } /* 7BIT */ static int open7Bit (ct, file) register CT ct; char **file; { int cc, fd, len; char buffer[BUFSIZ]; register struct cefile *ce = (struct cefile *) ct -> c_ceparams; if (ce -> ce_fp) goto ready_to_go; if (ce -> ce_file) { if ((ce -> ce_fp = fopen (ce -> ce_file, "r")) == NULL) { content_error (ce -> ce_file, ct, "unable to fopen for reading"); return NOTOK; } *file = ce -> ce_file; return fileno (ce -> ce_fp); } ce -> ce_unlink = *file == NULL; if ((ce -> ce_fp = fopen (ce -> ce_file = add (*file ? *file : m_scratch ("", tmp), NULLCP), "w+")) == NULL) { content_error (ce -> ce_file, ct, "unable to fopen for writing and reading"); return NOTOK; } if ((len = ct -> c_end - ct -> c_begin) < 0) adios (NULLCP, "internal error(3)"); if (!ct -> c_fp && (ct -> c_fp = fopen (ct -> c_file, "r")) == NULL) { content_error (ct -> c_file, ct, "unable to open for reading"); return NOTOK; } (void) lseek (fd = fileno (ct -> c_fp), (off_t) ct -> c_begin, 0); while (len > 0) switch (cc = read (fd, buffer, sizeof buffer - 1)) { case NOTOK: content_error (ct -> c_file, ct, "error reading from"); goto clean_up; case OK: content_error (NULLCP, ct, "premature eof"); goto clean_up; default: if (cc > len) cc = len; len -= cc; (void) fwrite (buffer, sizeof *buffer, cc, ce -> ce_fp); if (ferror (ce -> ce_fp)) { content_error (ce -> ce_file, ct, "error writing to"); goto clean_up; } } (void) fseek (ct -> c_fp, 0L, 0); if (fflush (ce -> ce_fp)) { content_error (ce -> ce_file, ct, "error writing to"); goto clean_up; } ready_to_go: ; (void) fseek (ce -> ce_fp, 0L, 0); *file = ce -> ce_file; return fileno (ce -> ce_fp); clean_up: ; free_encoding (ct, 0); return NOTOK; } static int Init7Bit (ct) register CT ct; { if (init_encoding (ct, open7Bit) == NOTOK) return NOTOK; ct -> c_cesizefnx = NULL; return OK; } /* External */ static int openExternal (ct, ce, file, fd) register CT ct; struct cefile *ce; char **file; int *fd; { char *id; if (ce -> ce_fp) { (void) fseek (ce -> ce_fp, 0L, 0); ready_already: ; *file = ce -> ce_file, *fd = fileno (ce -> ce_fp); return DONE; } if (ce -> ce_file) { if ((ce -> ce_fp = fopen (ce -> ce_file, "r")) == NULL) { content_error (ce -> ce_file, ct, "unable to fopen for reading"); return NOTOK; } goto ready_already; } if (xpid) { (void) pidcheck (pidwait (xpid, NOTOK)); xpid = 0; } if (cache && (id = ct -> c_id)) { char buffer[BUFSIZ]; (void) sprintf (buffer, "%s/%s", cache, id = trimcpy (id)); free (id); id = getcpy (buffer); if (ce -> ce_fp = fopen (id, "r")) { struct stat st; (void) fstat (fileno (ce -> ce_fp), &st); (void) sprintf (buffer, "Use cached copy %s of size %lu octets (content %s)? ", id, (unsigned long) st.st_size, ct -> c_partno); if (getanswer (buffer)) { ce -> ce_unlink = 0; ce -> ce_file = id; goto ready_already; } (void) fclose (ce -> ce_fp), ce -> ce_fp = NULL; } free (id); } return OK; } /* File */ static int openFile (ct, file) register CT ct; char **file; { int fd; char *id; register struct exbody *e = (struct exbody *) ct -> c_ctextern; register struct cefile *ce = (struct cefile *) ct -> c_ceparams; switch (openExternal (e -> eb_parent, ce, file, &fd)) { case NOTOK: return NOTOK; case OK: break; case DONE: return fd; } if (!e -> eb_name) { content_error (NULLCP, ct, "missing name parameter"); return NOTOK; } ce -> ce_unlink = 0; if ((ce -> ce_fp = fopen (ce -> ce_file = getcpy (e -> eb_name), "r")) == NULL) { content_error (ce -> ce_file, ct, "unable to fopen for reading"); return NOTOK; } if (cache && (id = e -> eb_parent -> c_id) && (!e -> eb_permission || !uleq (e -> eb_permission, "read-write"))) { char buffer[BUFSIZ]; (void) sprintf (buffer, "Make cached, publically-accessible copy of %s? ", e -> eb_name); if (getanswer (buffer)) { int mask; char cachefile[BUFSIZ]; FILE *fp; (void) sprintf (cachefile, "%s/%s", cache, id = trimcpy (id)); free (id); mask = umask (0022); if (fp = fopen (cachefile, "w")) { register FILE *gp = ce -> ce_fp; (void) fseek (gp, 0L, 0); while (fgets (buffer, sizeof buffer - 1, gp)) (void) fputs (buffer, fp); (void) fflush (fp); if (ferror (gp)) { admonish (ce -> ce_file, "error reading"); (void) unlink (cachefile); } if (ferror (fp)) { admonish (cachefile, "error writing"); (void) unlink (cachefile); } (void) fclose (fp); } (void) umask (mask); } } (void) fseek (ce -> ce_fp, 0L, 0); *file = ce -> ce_file; return fileno (ce -> ce_fp); } /* FTP */ static int openFTP (ct, file) register CT ct; char **file; { int caching, fd; char *ftp, *id, *user, *pass, buffer[BUFSIZ], cachefile[BUFSIZ]; register struct exbody *e = (struct exbody *) ct -> c_ctextern; register struct cefile *ce = (struct cefile *) ct -> c_ceparams; static char *username = NULL; static char *password = NULL; (void) sprintf (buffer, "%s-access-ftp", invo_name); if ((ftp = m_find (buffer)) && !*ftp) ftp = NULLCP; #ifndef FTP if (!ftp) return NOTOK; #endif switch (openExternal (e -> eb_parent, ce, file, &fd)) { case NOTOK: return NOTOK; case OK: break; case DONE: return fd; } if (!e -> eb_name || !e -> eb_site) { content_error (NULLCP, ct, "missing %s parameter", e -> eb_name ? "site": "name"); return NOTOK; } (void) sprintf (buffer, e -> eb_size > 0 ? "Retrieve %s (content %s)\n using %sFTP from site %s (%lu octets)? " : "Retrieve %s (content %s)\n using %sFTP from site %s? ", e -> eb_name, e -> eb_partno, e -> eb_flags ? "anonymous " : "", e -> eb_site, e -> eb_size); if (!getanswer (buffer)) return NOTOK; if (e -> eb_flags) { user = "anonymous"; (void) sprintf (pass = buffer, "%s@%s", getusr (), LocalName ()); } else { ruserpass (e -> eb_site, &username, &password); user = username, pass = password; } ce -> ce_unlink = *file == NULL, caching = 0, cachefile[0] = 0; if (cache && (id = e -> eb_parent -> c_id) && (!e -> eb_permission || !uleq (e -> eb_permission, "read-write"))) { (void) sprintf (buffer, "Make cached, publically-accessible copy? "); if (getanswer (buffer)) { (void) sprintf (cachefile, "%s/%s", cache, id = trimcpy (id)); free (id); if (*file == NULL) { ce -> ce_unlink = 0; caching = 1; } } } if ((ce -> ce_fp = fopen (ce -> ce_file = add (*file ? *file : caching ? cachefile : m_scratch ("", tmp), NULLCP), "w+")) == NULL) { content_error (ce -> ce_file, ct, "unable to fopen for writing and reading"); return NOTOK; } #ifdef FTP if (ftp) #endif { int child_id, i, vecp; char *vec[9]; vecp = 0; vec[vecp++] = r1bindex (ftp, '/'); vec[vecp++] = e -> eb_site; vec[vecp++] = user; vec[vecp++] = pass; vec[vecp++] = e -> eb_dir; vec[vecp++] = e -> eb_name; vec[vecp++] = ce -> ce_file, vec[vecp++] = e -> eb_mode && uleq (e -> eb_mode, "ascii") ? "ascii" : "binary"; vec[vecp] = NULL; (void) fflush (stdout); for (i = 0; (child_id = vfork ()) == NOTOK && i < 5; i++) sleep (5); switch (child_id) { case NOTOK: adios ("fork", "unable to"); /* NOTREACHED */ case OK: (void) close (fileno (ce -> ce_fp)); (void) execvp (ftp, vec); fprintf (stderr, "unable to exec "); perror (ftp); _exit (-1); /* NOTREACHED */ default: if (pidXwait (child_id, NULLCP)) { #ifdef FTP losing_ftp: ; #endif username = password = NULL; ce -> ce_unlink = 1; return NOTOK; } break; } } #ifdef FTP else if (ftp_get (e -> eb_site, user, pass, e -> eb_dir, e -> eb_name, ce -> ce_file, e -> eb_mode && uleq (e -> eb_mode, "ascii"), 0) == NOTOK) goto losing_ftp; #endif if (cachefile[0]) if (caching) (void) chmod (cachefile, 0644); else { int mask; register FILE *fp; mask = umask (0022); if (fp = fopen (cachefile, "w")) { register FILE *gp = ce -> ce_fp; (void) fseek (gp, 0L, 0); while (fgets (buffer, sizeof buffer - 1, gp)) (void) fputs (buffer, fp); (void) fflush (fp); if (ferror (gp)) { admonish (ce -> ce_file, "error reading"); (void) unlink (cachefile); } if (ferror (fp)) { admonish (cachefile, "error writing"); (void) unlink (cachefile); } (void) fclose (fp); } (void) umask (mask); } (void) fseek (ce -> ce_fp, 0L, 0); *file = ce -> ce_file; return fileno (ce -> ce_fp); } /* Mail */ static int openMail (ct, file) register CT ct; char **file; { int child_id, fd, i, result, vecp; char *bp, *id, buffer[BUFSIZ], *vec[7]; register struct exbody *e = (struct exbody *) ct -> c_ctextern; register struct cefile *ce = (struct cefile *) ct -> c_ceparams; switch (openExternal (e -> eb_parent, ce, file, &fd)) { case NOTOK: return NOTOK; case OK: break; case DONE: return fd; } if (!e -> eb_server) { content_error (NULLCP, ct, "missing server parameter"); return NOTOK; } bp = concat ("Retrieve content ", e -> eb_partno, " by asking mailbox ", e -> eb_server, "\n\n", e -> eb_body, "\n? ", NULLCP); result = getanswer (bp); free (bp); if (!result) return NOTOK; vecp = 0; vec[vecp++] = r1bindex (mailproc, '/'); vec[vecp++] = e -> eb_server; vec[vecp++] = "-subject"; if (cache && (id = e -> eb_parent -> c_id) && (!e -> eb_permission || !uleq (e -> eb_permission, "read-write"))) { (void) sprintf (buffer, "cache content as %s/%s", cache, id = trimcpy (id)); free (id); vec[vecp++] = buffer; } else vec[vecp++] = "mail-server request"; vec[vecp++] = "-body"; vec[vecp++] = e -> eb_body; vec[vecp] = NULL; for (i = 0; (child_id = vfork ()) == NOTOK && i < 5; i++) sleep (5); switch (child_id) { case NOTOK: advise ("fork", "unable to"); return NOTOK; case OK: (void) execvp (mailproc, vec); fprintf (stderr, "unable to exec "); perror (mailproc); _exit (-1); /* NOTREACHED */ default: if (pidXwait (child_id, NULLCP) == OK) advise (NULLCP, "request sent"); break; } ce -> ce_unlink = *file == NULL; if ((ce -> ce_fp = fopen (ce -> ce_file = add (*file ? *file : m_scratch ("", tmp), NULLCP), "w+")) == NULL) { content_error (ce -> ce_file, ct, "unable to fopen for writing and reading"); return NOTOK; } if (ct -> c_showproc) free (ct -> c_showproc); ct -> c_showproc = add ("true", NULLCP); (void) fseek (ce -> ce_fp, 0L, 0); *file = ce -> ce_file; return fileno (ce -> ce_fp); } /* COMPOSITION */ static char prefix[] = "----- =_aaaaaaaaaa"; static char *free_file = NULL; static CT free_ct = NULL; static void build_comp (file) char *file; { int compnum, state; char *cp, buf[BUFSIZ], name[NAMESZ], tmpfil[BUFSIZ]; struct multipart *m; register struct part **pp; CT ct; FILE *in, *out; if ((in = fopen (file, "r")) == NULL) adios (file, "unable to open for reading"); (void) umask (~m_gmprot ()); (void) strcpy (tmpfil, m_scratch (file, invo_name)); if ((out = fopen (tmpfil, "w")) == NULL) adios (tmpfil, "unable to open for writing"); free_file = tmpfil; for (compnum = 1, state = FLD;;) { switch (state = m_getfld (state, name, buf, sizeof buf, in)) { case FLD: case FLDPLUS: case FLDEOF: compnum++; if (uleq (name, VRSN_FIELD)) adios (NULLCP, "draft shouldn't contain %s: field", VRSN_FIELD); if (uleq (name, TYPE_FIELD)) { while (state == FLDPLUS) state = m_getfld (state, name, buf, sizeof buf, in); goto finish_field; } if (uleq (name, ENCODING_FIELD)) adios (NULLCP, "draft shouldn't contain %s: field", ENCODING_FIELD); fprintf (out, "%s:%s", name, buf); while (state == FLDPLUS) { state = m_getfld (state, name, buf, sizeof buf, in); (void) fputs (buf, out); } finish_field: ; if (state != FLDEOF) continue; /* else fall... */ case FILEEOF: adios (NULLCP, "draft has empty body -- no directives!"); /* NOTREACHED */ case BODY: case BODYEOF: (void) fseek (in, (long) (-strlen (buf)), 1); break; case LENERR: case FMTERR: adios (NULLCP, "message format error in component #%d", compnum); default: adios (NULLCP, "getfld() returned %d", state); } break; } if ((free_ct = ct = (CT) calloc (1, sizeof *ct)) == NULL) adios (NULLCP, "out of memory"); if (get_ctinfo ("multipart/mixed", ct, 0) == NOTOK) done (1); ct -> c_type = CT_MULTIPART; ct -> c_subtype = MULTI_MIXED; ct -> c_ctlistfnx = list_multi; ct -> c_ctfreefnx = free_multi; ct -> c_file = add (file, NULLCP); if ((m = (struct multipart *) calloc (1, sizeof *m)) == NULL) adios (NULLCP, "out of memory"); ct -> c_ctparams = (caddr_t) m; pp = &m -> mp_parts; while (fgetstr (buf, sizeof buf - 1, in)) { register struct part *part; CT p; if (user_content (in, file, buf, &p) == DONE) { admonish (NULLCP, "ignoring spurious #end"); continue; } if (!p) continue; if ((part = (struct part *) calloc (1, sizeof *part)) == NULL) adios (NULLCP, "out of memory"); *pp = part, pp = &part -> mp_next; part -> mp_part = p; } (void) fclose (in); if (!m -> mp_parts) adios (NULLCP, "no content directives found"); if (!m -> mp_parts -> mp_next) { CT p = m -> mp_parts -> mp_part; m -> mp_parts -> mp_part = NULL; free_content (ct); free_ct = ct = p; } if ((cp = index (prefix, 'a')) == NULL) adios (NULLCP, "internal error(4)"); while (compose_content (ct) == NOTOK) if (*cp < 'z') (*cp)++; else if (*++cp == 0) adios (NULLCP, "giving up trying to find a unique delimiter string"); else (*cp)++; fprintf (out, "%s: %s\n", VRSN_FIELD, VRSN_VALUE); (void) output_content (ct, out); if (fflush (out)) adios (tmpfil, "error writing to"); (void) fclose (out); if (listsw && ct -> c_ctlistfnx) { char *savfile; if (headsw) printf (LSTFMT1, "msg", "part", "type/subtype", "size", "description"); /* to get msgno */ savfile = ct -> c_file, ct -> c_file = file; (*ct -> c_ctlistfnx) (ct, 1); ct -> c_file = savfile; } free_content (ct); free_ct = NULL; (void) sprintf (buf, "%s.orig", m_backup (file)); if (rename (file, buf) == NOTOK) adios (buf, "unable to rename %s to", file); if (rename (tmpfil, file) == NOTOK) { advise (file, "unable to rename %s to", tmpfil); (void) rename (buf, file); done (1); } free_file = NULL; done (0); } /* */ static char *fgetstr (s, n, stream) char *s; int n; FILE *stream; { register char *cp, *ep; for (ep = (cp = s) + n; cp < ep; ) { register int i; if (!fgets (cp, n, stream)) return (cp != s ? s : NULL); if (cp == s && *cp != '#') return s; cp += (i = strlen (cp)) - 1; if (i <= 1 || *cp-- != '\n' || *cp != '\\') break; *cp = 0, n -= (i - 2); } return s; } /* */ static int user_content (in, file, buf, ctp) FILE *in; char *file, *buf; CT *ctp; { int extrnal, vrsn; register char *cp, **ap; char buffer[BUFSIZ]; struct multipart *m; register struct part **pp; struct stat st; register struct str2init *s2i; register CI ci; register CT ct; if (buf[0] == '\n' || strcmp (buf, "#\n") == 0) { *ctp = NULL; return OK; } if ((ct = (CT) calloc (1, sizeof *ct)) == NULL) adios (NULLCP, "out of memory"); *ctp = ct; ci = &ct -> c_ctinfo; ct -> c_ctlistfnx = list_content; if (buf[0] != '#' || buf[1] == '#' || buf[1] == '<') { int headers, inlineD; long pos; char content[BUFSIZ]; FILE *out; ct -> c_file = add (m_tmpfil (invo_name), NULLCP); ct -> c_unlink = 1; if ((out = fopen (ct -> c_file, "w")) == NULL) adios (ct -> c_file, "unable to open for writing"); if (buf[0] == '#' && buf[1] == '<') { (void) strcpy (content, buf + 2); inlineD = 1; goto rock_and_roll; } else inlineD = 0; (void) strcpy (content, "text/plain"); headers = 0; (void) strcpy (buffer, buf[0] != '#' ? buf : buf + 1); for (;;) { int i; if (headers >= 0 && uprf (buffer, DESCR_FIELD) && buffer[i = strlen (DESCR_FIELD)] == ':') { headers = 1; again_descr: ; ct -> c_descr = add (buffer + i + 1, ct -> c_descr); if (!fgetstr (buffer, sizeof buffer - 1, in)) adios (NULLCP, "end-of-file after %s: field in plaintext", DESCR_FIELD); switch (buffer[0]) { case ' ': case '\t': i = -1; goto again_descr; case '#': adios (NULLCP, "#-directive after %s: field in plaintext", DESCR_FIELD); /* NOTREACHED */ default: break; } } if (headers != 1 || buffer[0] != '\n') (void) fputs (buffer, out); rock_and_roll: ; headers = -1; pos = ftell (in); if ((cp = fgetstr (buffer, sizeof buffer - 1, in)) == NULL) break; if (buffer[0] == '#') { register char *bp; if (buffer[1] != '#') break; for (cp = (bp = buffer) + 1; *cp; cp++) *bp++ = *cp; *bp = '\0'; } } if (listsw) ct -> c_end = ftell (out); (void) fclose (out); if (get_ctinfo (content, ct, inlineD) == NOTOK) done (1); for (s2i = str2cts; s2i -> si_key; s2i++) if (uleq (ci -> ci_type, s2i -> si_key)) break; if (!s2i -> si_key && !uprf (ci -> ci_type, "X-")) s2i++; switch (ct -> c_type = s2i -> si_value) { case CT_MESSAGE: case CT_MULTIPART: adios (NULLCP, "it makes sense to define a in-line %s content... NOT!", ct -> c_type == CT_MESSAGE ? "message" : "multipart"); /* NOTREACHED */ default: if (ct -> c_ctinitfnx = s2i -> si_init) (void) (*ct -> c_ctinitfnx) (ct); break; } if (cp) (void) fseek (in, pos, 0); return OK; } extrnal = buf[1] == '@'; if (get_ctinfo (buf + (extrnal ? 2 : 1), ct, 1) == NOTOK) done (1); for (s2i = str2cts; s2i -> si_key; s2i++) if (uleq (ci -> ci_type, s2i -> si_key)) break; if (s2i -> si_key) { /* type/subtype [file] */ if (!ci -> ci_subtype) adios (NULLCP, "missing subtype in \"#%s\"", ci -> ci_type); switch (ct -> c_type = s2i -> si_value) { case CT_MULTIPART: adios (NULLCP, "use \"#begin ... #end\" instead of \"#%s/%s\"", ci -> ci_type, ci -> ci_subtype); /* NOTREACHED */ case CT_MESSAGE: if (uleq (ci -> ci_subtype, "partial")) adios (NULLCP, "sorry, \"#%s/%s\" isn't supported", ci -> ci_type, ci -> ci_subtype); if (uleq (ci -> ci_subtype, "external-body")) adios (NULLCP, "use \"#@type/subtype ... [] ...\" instead of \"#%s/%s\"", ci -> ci_type, ci -> ci_subtype); adios (NULLCP, "use \"#forw [+folder] [msgs]\" instead of \"#%s/%s\"", ci -> ci_type, ci -> ci_subtype); /* NOTREACHED */ default: if (ct -> c_ctinitfnx = s2i -> si_init) (void) (*ct -> c_ctinitfnx) (ct); break; } if (extrnal) { char msgid[BUFSIZ]; register struct exbody *e; CT p; static int partno; static long clock = 0L; static char *msgfmt; if (!ci -> ci_magic) adios (NULLCP, "need external information for \"#@%s/%s\"", ci -> ci_type, ci -> ci_subtype); p = ct; (void) sprintf (buffer, "message/external-body; %s", ci -> ci_magic); free (ci -> ci_magic), ci -> ci_magic = NULL; if ((ct = (CT) calloc (1, sizeof *ct)) == NULL) adios (NULLCP, "out of memory"); *ctp = ct; ci = &ct -> c_ctinfo; ct -> c_ctlistfnx = list_content; if (get_ctinfo (buffer, ct, 0) == NOTOK) done (1); ct -> c_type = CT_MESSAGE; ct -> c_subtype = MESSAGE_EXTERNAL; if ((e = (struct exbody *) calloc (1, sizeof *e)) == NULL) adios (NULLCP, "out of memory"); ct -> c_ctparams = (caddr_t) e; ct -> c_ctfreefnx = free_external; e -> eb_parent = ct; e -> eb_content = p; p -> c_ctextern = (caddr_t) e; ct -> c_ctlistfnx = list_external; if (params_external (ct, 1) == NOTOK) done (1); if (clock == 0L) { (void) time (&clock); (void) sprintf (msgid, "<%d.%ld.%%d@%s>\n", getpid (), clock, LocalName ()); partno = 0; msgfmt = getcpy (msgid); } (void) sprintf (msgid, msgfmt, ++partno); ct -> c_id = getcpy (msgid); return OK; } if (ci -> ci_magic) { if (*ci -> ci_magic == '|' || *ci -> ci_magic == '!') { for (cp = ci -> ci_magic + 1; isspace (*cp); cp++) continue; if (!*cp) adios (NULLCP, "empty pipe command for #%s directive", ci -> ci_type); cp = add (cp, NULLCP); free (ci -> ci_magic); ci -> ci_magic = cp; } else { if (access (ct -> c_file = ci -> ci_magic, 04) == NOTOK) adios ("reading", "unable to access %s for", ct -> c_file); if (listsw && stat (ct -> c_file, &st) != NOTOK) ct -> c_end = (long) st.st_size; ci -> ci_magic = NULL; } return OK; } (void) sprintf (buffer, "%s-compose-%s/%s", invo_name, ci -> ci_type, ci -> ci_subtype); if ((cp = m_find (buffer)) == NULL || *cp == 0) { (void) sprintf (buffer, "%s-compose-%s", invo_name, ci -> ci_type); if ((cp = m_find (buffer)) == NULL || *cp == 0) { content_error (NULLCP, ct, "don't know how to compose content"); done (1); } } ci -> ci_magic = add (cp, NULLCP); return OK; } if (extrnal) adios (NULLCP, "externally definition not allowed for \"#%s\"", ci -> ci_type); if (uleq (ci -> ci_type, "forw")) { /* #forw [+folder] [msgs] */ int msgnum; char *folder, *arguments[MAXARGS]; struct msgs *mp; if (ci -> ci_magic) { ap = brkstring (ci -> ci_magic, " ", "\n"); ap = copyip (ap, arguments); } else arguments[0] = "cur", arguments[1] = NULL; folder = NULL; for (ap = arguments; cp = *ap; ap++) if (*cp == '+' || *cp == '@') if (folder) adios (NULLCP, "only one folder per #forw directive"); else folder = path (cp + 1, *cp == '+' ? TFOLDER : TSUBCWF); if (!folder) folder = add (m_getfolder (), NULLCP); if ((mp = m_gmsg (folder)) == NULL) adios (NULLCP, "unable to read folder %s", folder); for (ap = arguments; cp = *ap; ap++) if (*cp != '+' && *cp != '@') if (!m_convert (mp, cp)) done (1); free (folder); free_ctinfo (ct); if (get_ctinfo ("multipart/digest", ct, 0) == NOTOK) done (1); ct -> c_type = CT_MULTIPART; ct -> c_subtype = MULTI_DIGEST; ct -> c_ctlistfnx = list_multi; ct -> c_ctfreefnx = free_multi; if ((m = (struct multipart *) calloc (1, sizeof *m)) == NULL) adios (NULLCP, "out of memory"); ct -> c_ctparams = (caddr_t) m; pp = &m -> mp_parts; for (msgnum = mp -> lowsel; msgnum <= mp -> hghsel; msgnum++) if (mp -> msgstats[msgnum] & SELECTED) { register struct part *part; register CT p; if ((p = (CT) calloc (1, sizeof *p)) == NULL) adios (NULLCP, "out of memory"); if (get_ctinfo ("message/rfc822", p, 0) == NOTOK) done (1); p -> c_type = CT_MESSAGE; p -> c_subtype = MESSAGE_RFC822; p -> c_ctlistfnx = list_content; (void) sprintf (buffer, "%s/%d", mp -> foldpath, msgnum); p -> c_file = add (buffer, NULLCP); if (listsw && stat (p -> c_file, &st) != NOTOK) p -> c_end = (long) st.st_size; if ((part = (struct part *) calloc (1, sizeof *part)) == NULL) adios (NULLCP, "out of memory"); *pp = part, pp = &part -> mp_next; part -> mp_part = p; } m_fmsg (mp); return OK; } if (uleq (ci -> ci_type, "end")) { free_content (ct); *ctp = NULL; return DONE; } if (!uleq (ci -> ci_type, "begin")) adios (NULLCP, "unknown directive \"#%s\"", ci -> ci_type); /* #begin [ alternative | parallel ] */ vrsn = !ci -> ci_magic ? MULTI_MIXED : uprf (ci -> ci_magic, "alt") ? MULTI_ALTERNATE : MULTI_PARALLEL; free_ctinfo (ct); (void) sprintf (buffer, "multipart/%s", SubMultiPart[vrsn - 1].kv_key); if (get_ctinfo (buffer, ct, 0) == NOTOK) done (1); ct -> c_type = CT_MULTIPART; ct -> c_subtype = vrsn; ct -> c_ctlistfnx = list_multi; ct -> c_ctfreefnx = free_multi; if ((m = (struct multipart *) calloc (1, sizeof *m)) == NULL) adios (NULLCP, "out of memory"); ct -> c_ctparams = (caddr_t) m; pp = &m -> mp_parts; while (fgetstr (buffer, sizeof buffer - 1, in)) { register struct part *part; CT p; if (user_content (in, file, buffer, &p) == DONE) { if (!m -> mp_parts) adios (NULLCP, "empty \"#begin ... #end\" sequence"); return OK; } if (!p) continue; if ((part = (struct part *) calloc (1, sizeof *part)) == NULL) adios (NULLCP, "out of memory"); *pp = part, pp = &part -> mp_next; part -> mp_part = p; } admonish (NULLCP, "premature end-of-file, missing #end"); return OK; } /* */ static int compose_content (ct) register CT ct; { register char *cp; char buffer[BUFSIZ]; register CI ci = &ct -> c_ctinfo; if (ct -> c_type == CT_MESSAGE && ct -> c_subtype == MESSAGE_EXTERNAL) return OK; switch (ct -> c_type) { case CT_MULTIPART: { int partnum; register char *pp; char partnam[BUFSIZ]; struct multipart *m = (struct multipart *) ct -> c_ctparams; register struct part *part; if (ct -> c_partno) { (void) sprintf (partnam, "%s.", ct -> c_partno); pp = partnam + strlen (partnam); } else pp = partnam; for (part = m -> mp_parts, partnum = 1; part; part = part -> mp_next, partnum++) { register CT p = part -> mp_part; (void) sprintf (pp, "%d", partnum); p -> c_partno = add (partnam, NULLCP); if (compose_content (p) == NOTOK) return NOTOK; } if (rfc934sw && ct -> c_subtype == MULTI_DIGEST) { int is934 = 1; for (part = m -> mp_parts; part; part = part -> mp_next) { register CT p = part -> mp_part; if (p -> c_subtype != MESSAGE_RFC822) { is934 = 0; break; } } ct -> c_rfc934 = is934; for (part = m -> mp_parts; part; part = part -> mp_next) { register CT p = part -> mp_part; if (p -> c_rfc934 = is934) p -> c_end++; } } if (listsw) { ct -> c_end = (partnum = strlen (prefix) + 2) + 2; if (ct -> c_rfc934) ct -> c_end += 1; for (part = m -> mp_parts; part; part = part -> mp_next) ct -> c_end += part -> mp_part -> c_end + partnum; } } break; default: if (!ct -> c_file) { int child_id, i, xstdout; register char *bp, **ap; char *vec[4]; FILE *out; if (!(cp = ci -> ci_magic)) adios (NULLCP, "internal error(5)"); ct -> c_file = add (m_tmpfil (invo_name), NULLCP); ct -> c_unlink = 1; xstdout = 0; buffer[0] = '\0'; for (bp = buffer; *cp; cp++) if (*cp == '%') { switch (*++cp) { case 'a': /* additional arguments */ { register char **ep; char *s = ""; for (ap = ci -> ci_attrs, ep = ci -> ci_values; *ap; ap++, ep++) { (void) sprintf (bp, "%s%s=\"%s\"", s, *ap, *ep); bp += strlen (bp); s = " "; } } break; case 'F': /* %f, and stdout is not-redirected */ xstdout = 1; /* and fall... */ case 'f': /* filename */ (void) sprintf (bp, "%s", ct -> c_file); break; case 's': /* subtype */ (void) strcpy (bp, ci -> ci_subtype); break; case '%': goto raw; default: *bp++ = *--cp; *bp = '\0'; continue; } bp += strlen (bp); } else { raw: ; *bp++ = *cp; *bp = '\0'; } printf ("composing content %s/%s from command\n\t%s\n", ci -> ci_type, ci -> ci_subtype, buffer); (void) fflush (stdout); vec[0] = "/bin/sh"; vec[1] = "-c"; vec[2] = buffer; vec[3] = NULL; if ((out = fopen (ct -> c_file, "w")) == NULL) adios (ct -> c_file, "unable to open for writing"); for (i = 0; (child_id = vfork ()) == NOTOK && i > 5; i++) sleep (5); switch (child_id) { case NOTOK: adios ("fork", "unable to fork"); /* NOTREACHED */ case OK: if (!xstdout) (void) dup2 (fileno (out), 1); (void) close (fileno (out)); (void) execvp ("/bin/sh", vec); fprintf (stderr, "unable to exec "); perror ("/bin/sh"); _exit (-1); /* NOTREACHED */ default: (void) fclose (out); if (pidXwait (child_id, NULLCP)) done (1); break; } } if (listsw && ct -> c_end == 0L) { struct stat st; if (stat (ct -> c_file, &st) != NOTOK) ct -> c_end = (long) st.st_size; } if (ct -> c_type != CT_TEXT && !(ct -> c_type == CT_APPLICATION && ct -> c_subtype == APPLICATION_POSTSCRIPT)) break; /* else fall... */ case CT_MESSAGE: { int charset, len, linelen, result; FILE *in; if ((in = fopen (ct -> c_file, "r")) == NULL) adios (ct -> c_file, "unable to open for reading"); len = strlen (prefix); result = OK; switch (ct -> c_type) { case CT_TEXT: charset = ct -> c_ctparams ? 0 : -1; linelen = 0; break; case CT_APPLICATION: charset = linelen = ct -> c_encoding ? 0 : -1; break; default: charset = linelen = 0; break; } while (fgets (buffer, sizeof buffer - 1, in)) { if (charset == -1) { for (cp = buffer; *cp; cp++) if (!isascii (*cp)) { charset = CHARSET_UNKNOWN; break; } if ((linelen == -1) && (cp - buffer > CPERLIN + 1)) linelen = 1; if (result == NOTOK) break; } else if ((linelen == -1) && (strlen (buffer) > CPERLIN + 1)) linelen = 1; if (result == NOTOK) continue; if (buffer[0] == '-' && buffer[1] == '-') { for (cp = buffer + strlen (buffer) - 1; cp >= buffer; cp--) if (!isspace (*cp)) break; *++cp = '\0'; if (strncmp (buffer + 2, prefix, len) == 0 && isdigit (buffer[2 + len])) { result = NOTOK; if (charset != -1 && linelen != -1) break; } } } if (ct -> c_type == CT_APPLICATION && !ct -> c_encoding) ct -> c_encoding = linelen == -1 && charset != CHARSET_UNKNOWN ? CE_7BIT : CE_QUOTED; if (ct -> c_type == CT_TEXT && !ct -> c_ctparams) { register char **ap, **ep; register struct text *t; if (charset == CHARSET_UNKNOWN && mm_charset) charset = -2; else if (charset == -1) charset = CHARSET_USASCII; if ((t = (struct text *) calloc (1, sizeof *t)) == NULL) adios (NULLCP, "out of memory"); ct -> c_ctparams = (caddr_t) t; for (ap = ci -> ci_attrs, ep = ci -> ci_values; *ap; ap++, ep++) continue; switch (t -> tx_charset = charset) { case CHARSET_USASCII: *ap = add ("charset=us-ascii", NULLCP); break; case CHARSET_UNKNOWN: default: *ap = add ("charset=x-unknown", NULLCP); break; case -2: *ap = concat ("charset=", mm_charset, NULLCP); break; } cp = index (*ap++, '='); *ap = NULL; *cp++ = '\0'; *ep = cp; } (void) fclose (in); return result; } } return OK; } /* */ static int output_content (ct, out) register CT ct; FILE *out; { int cc, mailbody, len; register char **ap, **ep; char buffer[BUFSIZ]; register CI ci = &ct -> c_ctinfo; if (ct -> c_type == CT_MULTIPART) { register char *cp; static int encl = 0; ap = ci -> ci_attrs, ep = ci -> ci_values; (void) sprintf (buffer, "boundary=%s%d", prefix, encl++); cp = index (*ap++ = add (buffer, NULLCP), '='); *ap = NULL; *cp++ = '\0'; *ep = cp; } else if (ct -> c_type == CT_MESSAGE && ct -> c_rfc934) goto rfc934_mode; len = 0; fprintf (out, "%s: %s/%s", TYPE_FIELD, ci -> ci_type, ci -> ci_subtype); len += strlen (TYPE_FIELD) + 2 + strlen (ci -> ci_type) + 1 + strlen (ci -> ci_subtype); mailbody = ct -> c_type == CT_MESSAGE && ct -> c_subtype == MESSAGE_EXTERNAL && ((struct exbody *) ct -> c_ctparams) -> eb_body; for (ap = ci -> ci_attrs, ep = ci -> ci_values; *ap; ap++, ep++) { if (mailbody && uleq (*ap, "body")) continue; (void) putc (';', out); len++; (void) sprintf (buffer, "%s=\"%s\"", *ap, *ep); if (len + 1 + (cc = strlen (buffer)) >= CPERLIN) { (void) fputs ("\n\t", out); len = 8; } else { (void) putc (' ', out); len++; } fprintf (out, "%s", buffer); len += cc; } if (ci -> ci_comment) { if (len + 1 + (cc = 2 + strlen (ci -> ci_comment)) >= CPERLIN) { (void) fputs ("\n\t", out); len = 8; } else { (void) putc (' ', out); len++; } fprintf (out, "(%s)", ci -> ci_comment); len += cc; } fprintf (out, "\n"); if (ct -> c_id) fprintf (out, "%s: %s", ID_FIELD, ct -> c_id); if (ct -> c_descr) fprintf (out, "%s: %s", DESCR_FIELD, ct -> c_descr); rfc934_mode: ; if (ct -> c_ctextern) return OK; switch (ct -> c_type) { case CT_MULTIPART: { struct multipart *m = (struct multipart *) ct -> c_ctparams; register struct part *part; if (ct -> c_rfc934) fprintf (out, "\n"); for (part = m -> mp_parts; part; part = part -> mp_next) { register CT p = part -> mp_part; fprintf (out, "\n--%s\n", ci -> ci_values[0]); if (p -> c_type == CT_MESSAGE && p -> c_subtype != MESSAGE_EXTERNAL && !p -> c_rfc934) fprintf (out, "%s: %s\n", VRSN_FIELD, VRSN_VALUE); (void) output_content (p, out); } fprintf (out, "\n--%s--\n", ci -> ci_values[0]); } break; case CT_TEXT: if (ct -> c_subtype != TEXT_PLAIN || (ct -> c_ctparams && ((struct text *) ct -> c_ctparams) -> tx_charset != CHARSET_USASCII)) { quoted_printable: ; fprintf (out, "%s: %s\n\n", ENCODING_FIELD, "quoted-printable"); (void) writeQuoted (ct, out); break; } /* else fall... */ case CT_MESSAGE: seven_bit: ; fprintf (out, "\n"); if (ct -> c_type == CT_MESSAGE && ct -> c_subtype == MESSAGE_EXTERNAL) { register struct exbody *e = (struct exbody *) ct -> c_ctparams; (void) output_content (e -> eb_content, out); if (e -> eb_body) fprintf (out, "\n%s\n", e -> eb_body); } else (void) write7Bit (ct, out); break; case CT_APPLICATION: if (ct -> c_subtype == APPLICATION_POSTSCRIPT) { if (ct -> c_encoding == CE_7BIT) goto seven_bit; goto quoted_printable; } /* else fall... */ default: fprintf (out, "%s: %s\n\n", ENCODING_FIELD, "base64"); (void) writeBase64 (ct, out); break; } return OK; } /* */ static int write7Bit (ct, out) register CT ct; FILE *out; { char c, buffer[BUFSIZ]; FILE *in; if ((in = fopen (ct -> c_file, "r")) == NULL) adios (ct -> c_file, "unable to open for reading"); c = '\n'; while (fgets (buffer, sizeof buffer - 1, in)) { c = buffer[strlen (buffer) - 1]; (void) fputs (buffer, out); } if (c != '\n') (void) putc ('\n', out); (void) fclose (in); return OK; } /* */ static int writeQuoted (ct, out) register CT ct; FILE *out; { register char *cp; char c, buffer[BUFSIZ]; FILE *in; if ((in = fopen (ct -> c_file, "r")) == NULL) adios (ct -> c_file, "unable to open for reading"); while (fgets (buffer, sizeof buffer - 1, in)) { register int n; cp = buffer + strlen (buffer) - 1; if ((c = *cp) == '\n') *cp = '\0'; if (strncmp (cp = buffer, "From ", sizeof "From " - 1) == 0) { (void) fprintf (out, "=%02X", *cp++ & 0xff); n = 3; } else n = 0; for (; *cp; cp++) { if (n > CPERLIN - 3) { (void) fputs ("=\n", out); n = 0; } switch (*cp) { case ' ': case '\t': (void) putc (*cp, out); n++; break; case '@': case '`': if (ebcdicsw) goto three_print; one_print: ; (void) putc (*cp, out); n++; break; default: if (('!' <= *cp && *cp <= '$') || ('[' <= *cp && *cp <= '^') || ('{' <= *cp && *cp <= '~')) { if (ebcdicsw) goto three_print; goto one_print; } if ('%' <= *cp && *cp <= 'z') goto one_print; /* else fall... */ case '=': three_print: ; (void) fprintf (out, "=%02X", *cp & 0xff); n += 3; break; } } if (c == '\n') { if (cp > buffer && (*--cp == ' ' || *cp == '\t')) (void) fputs ("=\n", out); (void) putc ('\n', out); } else (void) fputs ("=\n", out); } (void) fclose (in); return OK; } /* */ static char nib2b64[0x40f] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; static int writeBase64 (ct, out) register CT ct; FILE *out; { int result; FILE *in; if ((in = fopen (ct -> c_file, "r")) == NULL) adios (ct -> c_file, "unable to open for reading"); result = writeBase64aux (in, out); (void) fclose (in); return result; } static int writeBase64aux (in, out) FILE *in, *out; { int cc, n; char inbuf[3]; n = BPERLIN; while ((cc = fread (inbuf, sizeof *inbuf, sizeof inbuf, in)) > 0) { unsigned long bits; register char *bp; char outbuf[4]; if (cc < sizeof inbuf) { inbuf[2] = 0; if (cc < sizeof inbuf - 1) inbuf[1] = 0; } bits = (inbuf[0] & 0xff) << 16; bits |= (inbuf[1] & 0xff) << 8; bits |= inbuf[2] & 0xff; for (bp = outbuf + sizeof outbuf; bp > outbuf; bits >>= 6) *--bp = nib2b64[bits & 0x3f]; if (cc < sizeof inbuf) { outbuf[3] = '='; if (cc < sizeof inbuf - 1) outbuf[2] = '='; } (void) fwrite (outbuf, sizeof *outbuf, sizeof outbuf, out); if (cc < sizeof inbuf) { (void) putc ('\n', out); return OK; } if (--n <= 0) { n = BPERLIN; (void) putc ('\n', out); } } if (n != BPERLIN) (void) putc ('\n', out); return OK; } /* VIAMAIL */ #include "../zotnet/tws.h" static int via_mail (mailsw, subjsw, parmsw, descsw, cmntsw, slowsw, fromsw) char *mailsw, *subjsw, *parmsw, *descsw, *cmntsw, *fromsw; int slowsw; { int nlines, nparts, status; long pos; char tmpfil[BUFSIZ]; struct stat st; FILE *fp; (void) umask (~m_gmprot ()); (void) strcpy (tmpfil, m_tmpfil (invo_name)); if ((fp = fopen (tmpfil, "w+")) == NULL) adios (tmpfil, "unable to open for writing"); (void) chmod (tmpfil, 0600); if (!index (mailsw, '@')) mailsw = concat (mailsw, "@", LocalName (), NULLCP); fprintf (fp, "To: %s\n", mailsw); nlines = 1; if (subjsw) fprintf (fp, "Subject: %s\n", subjsw), nlines++; if (fromsw) { if (!index (fromsw, '@')) fromsw = concat (fromsw, "@", LocalName (), NULLCP); fprintf (fp, "From: %s\n", fromsw), nlines++; } fprintf (fp, "%s: %s\n", VRSN_FIELD, VRSN_VALUE), nlines++; fprintf (fp, "%s: application/octet-stream", TYPE_FIELD); if (parmsw) fprintf (fp, "; %s", parmsw); if (cmntsw) fprintf (fp, "\n\t(%s)", cmntsw), nlines++; if (descsw) fprintf (fp, "\n%s: %s", DESCR_FIELD, descsw), nlines++; fprintf (fp, "\n%s: %s\n\n", ENCODING_FIELD, "base64"), nlines += 2; if (fflush (fp)) adios (tmpfil, "error writing to"); pos = ftell (fp); (void) writeBase64aux (stdin, fp); if (fflush (fp)) adios (tmpfil, "error writing to"); if (fstat (fileno (fp), &st) == NOTOK) adios ("failed", "fstat of %s", tmpfil); nlines += (((long) st.st_size - pos) + CPERLIN) / (CPERLIN + 1); nparts = (nlines + (LPERMSG - 1)) / LPERMSG; if (nparts <= 1) status = via_post (tmpfil); else { int partno; long clock; char buffer[BUFSIZ], msgid[BUFSIZ]; if (verbosw) { printf ("sending binary image as %d partial messages\n", nparts); (void) fflush (stdout); } (void) time (&clock); (void) sprintf (msgid, "<%d.%ld@%s>", getpid (), clock, LocalName ()); (void) fseek (fp, 0L, 0); if (!fgets (buffer, sizeof buffer, fp) || !fgets (buffer, sizeof buffer, fp) || (subjsw && !fgets (buffer, sizeof buffer, fp))) adios (NULLCP, "premature eof"); for (partno = 1; partno <= nparts; partno++) { int lineno; char tmpdrf[BUFSIZ]; FILE *out; (void) strcpy (tmpdrf, m_tmpfil (invo_name)); if ((out = fopen (tmpdrf, "w")) == NULL) adios (tmpdrf, "unable to open for writing"); (void) chmod (tmpdrf, 0600); fprintf (out, "To: %s\n", mailsw); if (subjsw) fprintf (out, "Subject: %s\n", subjsw); fprintf (out, "%s: %s\n", VRSN_FIELD, VRSN_VALUE); fprintf (out, "%s: message/partial; id=\"%s\";\n\tnumber=%d; total=%d\n", TYPE_FIELD, msgid, partno, nparts); fprintf (out, "%s: part %d of %d\n\n", DESCR_FIELD, partno, nparts); if (partno == 1) fprintf (out, "Message-ID: %s\n", msgid); for (lineno = LPERMSG; lineno > 0; lineno--) { if (!fgets (buffer, sizeof buffer, fp)) { if (partno == nparts) break; adios (NULLCP, "premature eof"); } (void) fputs (buffer, out); } if (fflush (out)) adios (tmpdrf, "error writing to"); (void) fclose (out); if (slowsw > 0 && 1 < partno && partno < nparts) { if (verbosw) { printf ("pausing %d seconds before sending part %d...\n", slowsw, partno); (void) fflush (stdout); } sleep ((unsigned) slowsw); } status = via_post (tmpdrf); (void) unlink (tmpdrf); if (status) break; } } (void) fclose (fp); (void) unlink (tmpfil); done (status ? 1 : 0); } /* */ static int via_post (file) char *file; { int child_id, i; for (i = 0; (child_id = fork ()) == NOTOK && i < 5; i++) sleep (5); switch (child_id) { case NOTOK: adios ("fork", "unable to"); /* NOTREACHED */ case OK: (void) execlp (postproc, r1bindex (postproc, '/'), file, NULLCP); fprintf (stderr, "unable to exec "); perror (postproc); _exit (-1); /* NOTREACHED */ default: return pidXwait (child_id, postproc); } } /* */ void done (status) int status; { register CT *ctp; if (ctp = cts) for (; *ctp; ctp++) free_content (*ctp); if (free_ct) free_content (free_ct); if (free_file) (void) unlink (free_file); exit (status); } static int pidcheck (status) int status; { if ((status & 0xff00) == 0xff00 || (status & 0x007f) != SIGQUIT) return status; (void) unlink ("core"); (void) fflush (stdout); (void) fflush (stderr); done (1); /* NOTREACHED */ }