Mercurial > hg > Applications > mh
view uip/mhn.c @ 0:bce86c4163a3
Initial revision
author | kono |
---|---|
date | Mon, 18 Apr 2005 23:46:02 +0900 |
parents | |
children | a6481689f99c |
line wrap: on
line source
/* mhn.c - multi-media MH */ #ifndef lint static char ident[] = "@(#)$Id$"; #endif /* lint */ #include "../h/mh.h" #include <ctype.h> #include <errno.h> #include <setjmp.h> #include <signal.h> #include <stdio.h> #include "../zotnet/mts.h" #include <sys/types.h> #include <sys/stat.h> #ifdef BSD42 #include <sys/wait.h> #endif #ifdef LOCALE #include <locale.h> #endif #ifdef UNISTD #include <unistd.h> #endif /* */ static struct swit switches[] = { #define AUTOSW 0 "auto", 0, #define NAUTOSW 1 "noauto", 0, #define CACHESW 2 "cache", 0, #define NCACHESW 3 "nocache", 0, #define CHECKSW 4 "check", 0, #define NCHECKSW 5 "nocheck", 0, #define DEBUGSW 6 "debug", -5, #define EBCDICSW 7 "ebcdicsafe", 0, #define NEBCDICSW 8 "noebcdicsafe", 0, #define FILESW 9 /* interface from show */ "file file", 0, #define FORMSW 10 "form formfile", 4, #define HEADSW 11 "headers", 0, #define NHEADSW 12 "noheaders", 0, #define LISTSW 13 "list", 0, #define NLISTSW 14 "nolist", 0, #define PARTSW 15 "part number", 0, #define PAUSESW 16 "pause", 0, #define NPAUSESW 17 "nopause", 0, #define RCACHESW 18 "rcache policy", 0, #define SIZESW 19 "realsize", 0, #define NSIZESW 20 "norealsize", 0, #define RFC934SW 21 "rfc934mode", 0, #define NRFC934SW 22 "norfc934mode", 0, #define SERIALSW 23 "serialonly", 0, #define NSERIALSW 24 "noserialonly", 0, #define SHOWSW 25 "show", 0, #define NSHOWSW 26 "noshow", 0, #define STORESW 27 "store", 0, #define NSTORESW 28 "nostore", 0, #define TYPESW 29 "type content", 0, #define VERBSW 30 "verbose", 0, #define NVERBSW 31 "noverbose", 0, #define WCACHESW 32 "wcache policy", 0, #define HELPSW 33 "help", 4, #define PROGSW 34 "moreproc program", -4, #define NPROGSW 35 "nomoreproc", -3, #define LENSW 36 "length lines", -4, #define WIDSW 37 "width columns", -4, #define VIAMSW 38 "viamail mailpath", -7, #define VIASSW 39 "viasubj subject", -7, #define VIAPSW 40 "viaparm arguments", -7, #define VIADSW 41 "viadesc text", -7, #define VIACSW 42 "viacmnt text", -7, #define VIAZSW 43 "viadelay seconds", -8, #define VIAFSW 44 "viafrom mailpath", -7, #ifdef JAPAN #define JUNETSW 45 "junet", 0, #define NJUNETSW 46 "nojunet", 0, #endif /* JAPAN */ #ifdef MH_PLUS #define EIGHTSW 47 "8bit", 0, #define NEIGHTSW 48 "no8bit", 0, #endif /* MH_PLUS */ NULL, 0 }; /* */ #define NPARTS 50 #define NTYPES 20 static struct swit caches[] = { #define CACHE_NEVER 0 "never", 0, #define CACHE_PRIVATE 1 "private", 0, #define CACHE_PUBLIC 2 "public", 0, #define CACHE_ASK 3 "ask", 0, NULL, 0 }; static int autosw = 0; static int cachesw = 0; static int checksw = 0; int debugsw = 0; static int ebcdicsw = 0; static char *formsw = NULLCP; static int headsw = 1; static int listsw = 0; static int nolist = 0; static int nomore = 0; static int npart = 0; static char *parts[NPARTS + 1]; static int pausesw = 1; static char *progsw = NULLCP; static int rcachesw = CACHE_ASK; 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 wcachesw = CACHE_ASK; #ifdef JAPAN static int junetsw = 0; #endif /* JAPAN */ #ifdef MH_PLUS static int eightsw = 0; #endif /* MH_PLUS */ static int endian = 0; static char *mm_charset = NULL; static int xpid = 0; static int userrs = 0; static char *cache_public; static char *cache_private; static int cwdlen; static char *cwd; static char *dir; static char *errs = NULL; static char *tmp; extern int errno; #if !defined(BSD44) && !defined(__GNU_LIBRARY__) 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; /* overrides profile entry, if any */ 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: */ #ifdef MH_PLUS char *m_subj; /* message/rfc822 Subject: */ #endif /* MH_PLUS */ int c_digested; /* Content-MD5: */ unsigned char c_digest[16]; /* .. */ 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_start; /* where headers starts in file */ 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 cache_content (); static int user_content (), compose_content (), output_content (); static void free_content (), flush_errors (), set_id (); #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 make_intermediates (); static int find_cache (), find_cache_aux (), find_cache_aux2 (); static int write7Bit (), writeBinary (); static int writeQuoted (), writeBase64 (), writeBase64aux (); static int writeDigest (), readDigest (); 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, *icachesw; 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 #ifdef JAPAN ml_init(); #endif /* JAPAN */ 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 CACHESW: cachesw++; continue; case NCACHESW: cachesw = 0; continue; case RCACHESW: icachesw = &rcachesw; goto do_cache; case WCACHESW: icachesw = &wcachesw; do_cache: ; if (!(cp = *argp++) || *cp == '-') adios (NULLCP, "missing argument to %s", argp[-2]); switch (*icachesw = smatch (cp, caches)) { case AMBIGSW: ambigsw (cp, caches); done (1); case UNKWNSW: adios (NULLCP, "%s unknown", cp); default: break; } continue; case CHECKSW: checksw++; continue; case NCHECKSW: checksw = 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 PAUSESW: pausesw++; continue; case NPAUSESW: pausesw = 0; 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; #ifdef JAPAN case JUNETSW: junetsw++; continue; case NJUNETSW: junetsw = 0; continue; #endif /* JAPAN */ #ifdef MH_PLUS case EIGHTSW: eightsw++; continue; case NEIGHTSW: eightsw = 0; continue; #endif /* MH_PLUS */ } 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 || f7) 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); if ((cache_public = m_find (buf)) && *cache_public != '/') cache_public = NULL; (void) sprintf (buf, "%s-private-cache", invo_name); if (!(cache_private = m_find (buf))) cache_private = ".cache"; cache_private = getcpy (m_maildir (cache_private)); 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 && !cachesw && !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 && !cachesw) 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, 1) && (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, 1) && 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; if (ct -> c_ceclosefnx) (*ct -> c_ceclosefnx) (ct); } flush_errors (); } if (storesw) { for (ctp = cts; ct = *ctp; ctp++) if (type_ok (ct, 1) && 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; if (ct -> c_ceclosefnx) (*ct -> c_ceclosefnx) (ct); } flush_errors (); } if (cachesw) { for (ctp = cts; ct = *ctp; ctp++) if (type_ok (ct, 1)) { cache_content (ct); if (ct -> c_fp) (void) fclose (ct -> c_fp), ct -> c_fp = NULL; if (ct -> c_ceclosefnx) (*ct -> c_ceclosefnx) (ct); } 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, 1) && 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; if (ct -> c_ceclosefnx) (*ct -> c_ceclosefnx) (ct); } flush_errors (); listsw = 0; } if (showsw) for (ctp = cts; ct = *ctp; ctp++) { #if defined(BSD42) && !defined(WAITINT) union wait status; #else int status; #endif TYPESIG (*hstat) (), (*istat) (), (*qstat) (), (*tstat) (); if (!type_ok (ct, 0)) 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; if (ct -> c_ceclosefnx) (*ct -> c_ceclosefnx) (ct); continue; } if (strcmp (formsw, "mhl.null")) { int child_id, i, vecp; char *vec[8]; 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; } } else xpid = 0; (void) (*ct -> c_ctshowfnx) (ct, 1, 0); if (ct -> c_fp) (void) fclose (ct -> c_fp), ct -> c_fp = NULL; if (ct -> c_ceclosefnx) (*ct -> c_ceclosefnx) (ct); 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" #undef si_value struct str2init { char *mhnsi_key; int mhnsi_value; int (*mhnsi_init) (); }; static int InitApplication (), InitMessage (), InitMultiPart (), InitText (), InitGeneric (); static struct str2init str2cts[] = { "application", CT_APPLICATION, InitApplication, "audio", CT_AUDIO, InitGeneric, "image", CT_IMAGE, InitGeneric, "message", CT_MESSAGE, InitMessage, "multipart", CT_MULTIPART, InitMultiPart, "text", CT_TEXT, InitText, "video", CT_VIDEO, InitGeneric, 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, Init7Bit, 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 = (ct -> c_start = 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)) { char c, *cp, *dp, *vrsn; cp = add (buf, NULLCP); while (state == FLDPLUS) { state = m_getfld (state, name, buf, sizeof buf, in); cp = add (buf, cp); } if (ct -> c_vrsn) { /* dp = trimcpy (cp); if (strlen(dp) >= BUFSIZ) strcpy(dp + BUFSIZ - 5, " ..."); advise (NULLCP, "message %s has multiple %s: fields (%s)", ct -> c_file, VRSN_FIELD, dp); free (dp); */ free (cp); /* goto out; */ goto got_header; } ct -> c_vrsn = cp; 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", VRSN_FIELD, cp); #if 0 if (*cp == '(' && get_comment (ct, &cp, 0) == NOTOK) goto out; for (dp = cp; istoken (*dp); dp++) continue; c = *dp, *dp = '\0'; ucmp = uleq (cp, VRSN_VALUE); *dp = c; if (!ucmp) { #else /* "MIME-Version: 1.(comment)0" is valid syntax. "1.0" is not one token. */ vrsn = NULLCP; while (*cp) { if (*cp == '(' && get_comment (ct, &cp, 0) == NOTOK) goto out; for (dp = cp; istoken (*dp); dp++) continue; c = *dp, *dp = '\0'; vrsn = add(cp, vrsn); *dp = c; if (*dp && !isspace(*dp) && *dp != '(') break; /* error */ while (isspace (*dp)) dp++; cp = dp; } if (!uleq (vrsn, VRSN_VALUE)) { #endif dp = trimcpy (cp); if (strlen(dp) >= BUFSIZ) strcpy(dp + BUFSIZ - 5, " ..."); admonish (NULLCP, "message %s has unknown value for %s: field (%s)", ct -> c_file, VRSN_FIELD, dp); } free(vrsn); 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); if (strlen(dp) >= BUFSIZ) strcpy(dp + BUFSIZ - 5, " ..."); 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 -> mhnsi_key; s2i++) if (uleq (ci -> ci_type, s2i -> mhnsi_key)) break; if (!s2i -> mhnsi_key && !uprf (ci -> ci_type, "X-")) s2i++; ct -> c_type = s2i -> mhnsi_value; ct -> c_ctinitfnx = s2i -> mhnsi_init; #ifdef MIME_HEADERS if (ci -> ci_comment) { char *buffer; buffer = add (ci -> ci_comment, NULLCP); exthdr_decode(buffer, ci -> ci_comment, name); free(buffer); } #endif /* MIME_HEADERS */ goto got_header; } if (uleq (name, ENCODING_FIELD)) { 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) { dp = trimcpy (cp); if (strlen(dp) >= BUFSIZ) strcpy(dp + BUFSIZ - 5, " ..."); advise (NULLCP, "message %s has multiple %s: fields (%s)", ct -> c_file, ENCODING_FIELD, dp); free (dp); free (cp); goto out; } ct -> c_celine = cp; while (isspace (*cp)) cp++; for (dp = index (cp, '\n'); dp; dp = index (dp, '\n')) *dp++ = ' '; if (*cp == '(' && get_comment (ct, &cp, 0) == NOTOK) goto out; for (dp = cp; istoken (*dp); dp++) continue; c = *dp, *dp = '\0'; for (s2i = str2ces; s2i -> mhnsi_key; s2i++) if (uleq (cp, s2i -> mhnsi_key)) break; if (!s2i -> mhnsi_key && !uprf (cp, "X-")) s2i++; *dp = c; ct -> c_encoding = s2i -> mhnsi_value; if (s2i -> mhnsi_init && (*s2i -> mhnsi_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); } #ifdef MIME_HEADERS { char *buffer; buffer = add (ct -> c_descr, NULLCP); exthdr_decode(buffer, ct -> c_descr, name); free(buffer); } #endif /* MIME_HEADERS */ goto got_header; } if (uleq (name, MD5_FIELD)) { char *cp, *dp, *ep; cp = add (buf, NULLCP); while (state == FLDPLUS) { state = m_getfld (state, name, buf, sizeof buf, in); cp = add (buf, cp); } if (!checksw) { free (cp); goto got_header; } if (ct -> c_digested) { dp = trimcpy (cp); if (strlen(dp) >= BUFSIZ) strcpy(dp + BUFSIZ - 5, " ..."); advise (NULLCP, "message %s has multiple %s: fields (%s)", ct -> c_file, MD5_FIELD, dp); free (dp); free (cp); goto out; } ep = cp; 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", MD5_FIELD, cp); if (*cp == '(' && get_comment (ct, &cp, 0) == NOTOK) { free (ep); goto out; } for (dp = cp; *dp && !isspace (*dp); dp++) continue; *dp = '\0'; (void) readDigest (ct, cp); free (ep); ct -> c_digested++; 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: /* binary file may contain '\0' inside "buf". Even if the current content is "7bit", it is possible that "buf" contains the next "binary" part. Never use strlen(). */ /* ct -> c_begin = ftell (in) - strlen (buf); */ 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; char *dp; register char **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); #ifdef JAPAN if (magic) (void) ml_conv(cp); #endif /* JAPAN */ if (*cp == '(' && get_comment (ct, &cp, 1) == 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) { if (magic) advise(NULLCP, "invalid # field (empty type)"); else advise(NULLCP, "invalid %s: field in message %s (empty type)", TYPE_FIELD, ct -> c_file); return NOTOK; } else if (strlen(ci -> ci_type) > NAMESZ) { if (magic) advise(NULLCP, "invalid # field (type too long)"); else advise(NULLCP, "invalid %s: field in message %s (type too long)", TYPE_FIELD, ct -> c_file); ci -> ci_type[NAMESZ] = '\0'; 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, 1) == 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, 1) == 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) { if (magic) advise(NULLCP, "invalid # field (empty subtype for \"%s\")", ci -> ci_type); else advise(NULLCP, "invalid %s: field in message %s (empty subtype for \"%s\")", TYPE_FIELD, ct -> c_file, ci -> ci_type); return NOTOK; } else if (strlen(ci -> ci_subtype) > NAMESZ) { if (magic) advise(NULLCP, "invalid # field (subtype for \"%s\" too long)", ci -> ci_type); else advise(NULLCP, "invalid %s: field in message %s (subtype for \"%s\" too long)", TYPE_FIELD, ct -> c_file, ci -> ci_type); ci -> ci_subtype[NAMESZ] = '\0'; 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, 1) == 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, 1) == 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 == '(' && get_comment (ct, &dp, 1) == NOTOK) return NOTOK; if (dp == cp || *dp != '=') { if (strlen(cp) >= BUFSIZ) strcpy(cp + BUFSIZ - 5, " ..."); 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'; if (strlen(*ap) > NAMESZ) { advise (NULLCP, "too long parameter in message %s's %s: field\n", ct -> c_file, TYPE_FIELD); return NOTOK; } for (dp++; isspace (*dp); ) dp++; if (*dp == '(' && get_comment (ct, &dp, 1) == NOTOK) return NOTOK; 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; } if (strlen(vp) > BUFSIZ / 4) { advise (NULLCP, "too long value 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, 1) == NOTOK) return NOTOK; } if (magic && *cp == '<') { if (ct -> c_id) free (ct -> c_id), ct -> c_id = NULL; if (!(dp = index (ct -> c_id = ++cp, '>'))) { advise (NULLCP, "invalid ID in message %s", ct -> c_file); return NOTOK; } c = *dp, *dp = '\0'; if (*ct -> c_id) ct -> c_id = concat ("<", ct -> c_id, ">\n", NULLCP); else ct -> c_id = NULL; *dp++ = c, cp = dp; while (isspace (*cp)) cp++; } 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 { if (strlen(cp) >= BUFSIZ) strcpy(cp + BUFSIZ - 5, " ..."); 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, istype) CT ct; char **ap; int istype; { register int i; register char *bp, *cp; char c, /* buffer[BUFSIZ], *//* fixed width buffer is dangerous. */ *dp; register CI ci = &ct -> c_ctinfo; cp = *ap; get_comment_again: ; /* bp = buffer; cp++; */ bp = ++cp; for (i = 0;;) { switch (c = *cp++) { case '\0': invalid: ; advise (NULLCP, "invalid comment in message %s's %s: field", ct -> c_file, istype ? TYPE_FIELD : VRSN_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'; */ c = *(cp - 1); *(cp - 1) = '\0'; if (istype) { if (dp = ci -> ci_comment) { ci -> ci_comment = concat (dp, " ", /* buffer */ bp, NULLCP); free (dp); } else ci -> ci_comment = add (/* buffer */ bp, NULLCP); } *(cp - 1) = c; while (isspace (*cp)) cp++; if (*cp == '(') goto get_comment_again; *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); } #ifdef MH_PLUS if (ct -> c_descr || ct -> m_subj) { #else /* MH_PLUS */ if (ct -> c_descr) { #endif /* MH_PLUS */ char *dp; #ifdef JAPAN char buf[BUFSIZ], *pp; #endif /* JAPAN */ #ifdef MH_PLUS dp = trimcpy (cp = add (ct -> c_descr ? ct -> c_descr : ct -> m_subj, NULLCP)); #else /* MH_PLUS */ dp = trimcpy (cp = add (ct -> c_descr, NULLCP)); #endif /* MH_PLUS */ free (cp); #ifdef JAPAN sprintf(buf, LSTFMT2d1, dp); for (pp = buf; *pp; pp++) if (ml_ismlchar(*pp)) { if (ml_ismlchar(*(pp+1))) pp++; else if (!*(pp+1)) *pp = '\0'; } ml_fputs(buf, stdout); #else printf (LSTFMT2d1, dp); #endif free (dp); } printf ("\n"); if (verbosw && ci -> ci_comment) { char *dp; #ifdef JAPAN char buf[BUFSIZ], *pp; #endif dp = trimcpy (cp = add (ci -> ci_comment, NULLCP)); free (cp); (void) sprintf (buffer, "(%.*s)", BUFSIZ - 3, dp); free (dp); #ifdef JAPAN sprintf(buf, LSTFMT2d2, buffer); for (pp = buf; *pp; pp++) if (ml_ismlchar(*pp)) { if (ml_ismlchar(*(pp+1))) pp++; else if (*(pp+1) == '\n' && !*(pp+2)) { *pp++ = '\n'; *pp = '\0'; } } ml_fputs(buf, stdout); #else printf (LSTFMT2d2, buffer); #endif } if (!debugsw) return OK; (void) fflush (stdout); fprintf (stderr, " partno \"%s\"\n", empty (ct -> c_partno)); if (ct -> c_vrsn) fprintf (stderr, " %s:%s\n", VRSN_FIELD, ct -> c_vrsn); if (ct -> c_ctline) fprintf (stderr, " %s:%s", TYPE_FIELD, ct -> c_ctline); #ifdef JAPAN fprintf (stderr, " type \"%s\" subtype \"%s\" comment \"", empty (ci -> ci_type), empty (ci -> ci_subtype)); ml_fputs(empty (ci -> ci_comment), stderr); fprintf (stderr, "\" magic \"%s\"\n", empty (ci -> ci_magic)); #else /* JAPAN */ 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)); #endif /* JAPAN */ 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) { #ifdef JAPAN fprintf (stderr, " %s:", DESCR_FIELD); ml_fputs(ct -> c_descr, stderr); #else /* JAPAN */ fprintf (stderr, " %s:%s", DESCR_FIELD, ct -> c_descr); #endif /* JAPAN */ } 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 * 2]; 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 (), show_content_aux2 (); static int escape_quotes(pp, bufend, noquote) char *pp, *bufend; int noquote; { char *endp = pp + strlen(pp); if (! noquote) pp++; while ((pp = index(pp, '\'')) && (noquote || *(pp + 1) != '\0')) { register char *xp; if (endp + 3 >= bufend) return -1; for (xp = endp; xp > pp; xp--) *(xp + 3) = *xp; pp++; *pp++ = '\\'; *pp++ = '\''; *pp++ = '\''; endp += 3; } return 0; } 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) if (ct -> c_type == CT_TEXT) { /* treat as text/plain (cf. RFC2046) */ (void) sprintf (buffer, "%%p%s '%%F'", progsw ? progsw : moreproc && *moreproc ? moreproc : "more"); cp = ct -> c_showproc = add (buffer, NULLCP); return show_content_aux (ct, serial, alternate, cp, NULLCP); } else 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 fd, xlist, xpause, xstdin, xtty, quoted; register char *bp; char *file, buffer[BUFSIZ], *bufend; 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'; bufend = buffer + sizeof(buffer) - NAMESZ; /* NAMESZ for c_termproc */ quoted = 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++) { if (bp + strlen(*ap) + strlen(*ep) + 4 >= bufend) break; /* quoted may have no meaning */ (void) sprintf (bp, "%s\'%s=%s\'", s, *ap, *ep); /* Escape existing quotes */ if (escape_quotes(bp + strlen(s), bufend, 0)) *bp = '\0'; bp += strlen (bp); s = " "; } } break; case 'd': /* content description */ if (ct -> c_descr) { char *s; if (bp + strlen(s = trimcpy (ct -> c_descr)) + 2 >= bufend) break; if (quoted) (void) strcpy (bp, s); else (void) sprintf (bp, "\'%s\'", s); free (s); if (escape_quotes(bp, bufend, quoted)) *bp = '\0'; } 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 */ if (bp + strlen(file) + 2 >= bufend) break; if (quoted) (void) strcpy (bp, file); else (void) sprintf (bp, "\'%s\'", file); if (escape_quotes(bp, bufend, quoted)) *bp = '\0'; break; case 'p': /* pause prior to displaying content */ xpause = pausesw; /* and fall... */ case 'l': /* display listing prior to displaying content */ xlist = !nolist; break; case 's': /* subtype */ if (bp + strlen(ci -> ci_subtype) + 2 >= bufend) break; if (quoted) (void) strcpy (bp, ci -> ci_subtype); else (void) sprintf (bp, "\'%s\'", ci -> ci_subtype); if (escape_quotes(bp, bufend, quoted)) *bp = '\0'; break; case '%': goto raw; default: if (bp + 1 >= bufend) break; *bp++ = *--cp; *bp = '\0'; continue; } bp += strlen (bp); } else { raw: ; if (bp + 1 >= bufend) break; *bp++ = *cp; *bp = '\0'; if (*cp == '\'') quoted = !quoted; } if (ct -> c_termproc) { char term[BUFSIZ]; (void) strcpy (term, buffer); (void) sprintf (buffer, ct -> c_termproc, term); } got_command: ; return show_content_aux2 (ct, serial, alternate, cracked, buffer, fd, xlist, xpause, xstdin, xtty); } static int show_content_aux2 (ct, serial, alternate, cracked, buffer, fd, xlist, xpause, xstdin, xtty) register CT ct; int serial, alternate; char *cracked, *buffer; int fd, xlist, xpause, xstdin, xtty; { int child_id, i; char *vec[4], exec[BUFSIZ + sizeof "exec "]; register CI ci = &ct -> c_ctinfo; 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 (xpid < 0 || (xtty && xpid)) { if (xpid < 0) xpid = -xpid; (void) pidcheck (pidwait (xpid, NOTOK)); xpid = 0; } if (xlist) { char prompt[BUFSIZ]; if (ct -> c_ctlistfnx) { if (ct -> c_type == CT_MULTIPART) (void) list_content (ct, -1); else (*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 (", BUFSIZ / 4, 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) { #ifdef JAPAN ml_fputs(prompt, stdout); fputs("\n", stdout); #else /* JAPAN */ printf ("%s\n", prompt); #endif /* JAPAN */ } else if (SOprintf ("Press <return> to show %s...", prompt)) { #ifdef JAPAN fputs("Press <return> to show ", stdout); ml_fputs(prompt, stdout); fputs("...", stdout); #else /* JAPAN */ printf ("Press <return> to show %s...", prompt); #endif /* JAPAN */ } } 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 || prompt[0] == 'q') { (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)); if (fd != NOTOK) (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], *bufend; register CI ci = &ct -> c_ctinfo; FILE *fp; int quoted; 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"; } } buffer[0] = '\0'; bufend = buffer + sizeof(buffer) - NAMESZ; quoted = 0; 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] != '!') { if (bp + 1 >= bufend) break; *bp++ = *--cp; *bp = '\0'; continue; } else { register char **ap, **ep; char *s = ""; for (ap = ci -> ci_attrs, ep = ci -> ci_values; *ap; ap++, ep++) { if (bp + strlen(*ap) + strlen(*ep) + 4 >= bufend) break; /* quoted may have no meaning */ (void) sprintf (bp, "%s\'%s=%s\'", s, *ap, *ep); /* Escape existing quotes */ if (escape_quotes(bp + strlen(s), bufend, 0)) *bp = '\0'; bp += strlen (bp); s = " "; } } break; case 'm': /* message */ { char *p = r1bindex (ct -> c_file, '/'); if (buffer[0] == '|' || buffer[0] == '!') { if (bp + strlen(p) + 2 >= bufend) break; if (quoted) (void) strcpy (bp, p); else (void) sprintf (bp, "\'%s\'", p); if (escape_quotes(bp, bufend, quoted)) *bp = '\0'; } else { if (bp + strlen(p) >= bufend) break; (void) strcpy (bp, p); } } break; case 'P': /* .part */ if (ct -> c_partno) { if (buffer[0] == '|' || buffer[0] == '!') { if (bp + strlen(ct -> c_partno) + 3 >= bufend) break; if (quoted) (void) sprintf (bp, ".%s", ct -> c_partno); else (void) sprintf (bp, "\'.%s\'", ct -> c_partno); if (escape_quotes(bp, bufend, quoted)) *bp = '\0'; } else { if (bp + strlen(ct -> c_partno) + 1 >= bufend) break; (void) sprintf (bp, ".%s", ct -> c_partno); } } break; case 'p': /* part */ if (ct -> c_partno) { if (buffer[0] == '|' || buffer[0] == '!') { if (bp + strlen(ct -> c_partno) + 2 >= bufend) break; if (quoted) (void) strcpy (bp, ct -> c_partno); else (void) sprintf (bp, "\'%s\'", ct -> c_partno); if (escape_quotes(bp, bufend, quoted)) *bp = '\0'; } else { if (bp + strlen(ct -> c_partno) >= bufend) break; (void) strcpy (bp, ct -> c_partno); } } break; case 't': /* type */ if (buffer[0] == '|' || buffer[0] == '!') { if (bp + strlen(ci -> ci_type) + 2 >= bufend) break; if (quoted) (void) strcpy (bp, ci -> ci_type); else (void) sprintf (bp, "\'%s\'", ci -> ci_type); if (escape_quotes(bp, bufend, quoted)) *bp = '\0'; } else { if (bp + strlen(ci -> ci_type) >= bufend) break; (void) strcpy (bp, ci -> ci_type); } break; case 's': /* subtype */ if (buffer[0] == '|' || buffer[0] == '!') { if (bp + strlen(ci -> ci_subtype) + 2 >= bufend) break; if (quoted) (void) strcpy (bp, ci -> ci_subtype); else (void) sprintf (bp, "\'%s\'", ci -> ci_subtype); if (escape_quotes(bp, bufend, quoted)) *bp = '\0'; } else { if (bp + strlen(ci -> ci_subtype) >= bufend) break; (void) strcpy (bp, ci -> ci_subtype); } break; case '%': goto raw; default: if (bp + 1 >= bufend) break; *bp++ = *--cp; *bp = '\0'; continue; } bp += strlen (bp); } else { raw: ; if (bp + 1 >= bufend) break; *bp++ = *cp; *bp = '\0'; if (*cp == '\'') quoted = !quoted; } 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); (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 (index (ct -> c_storage, '/') && make_intermediates (ct -> c_storage) == NOTOK) return NOTOK; if (ct -> c_encoding != CE_7BIT && ct -> c_encoding != CE_8BIT) { 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, "Encrypted:") && !uprf (buffer, "Message-ID:") && !uprf (buffer, "Subject:") && !uprf (buffer, VRSN_FIELD)) { 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, "Encrypted") || uleq (name, "Message-ID") || uleq (name, "Subject") || uleq (name, VRSN_FIELD)) { 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 int make_intermediates (file) char *file; { register char *cp; for (cp = file + 1; cp = index (cp, '/'); cp++) { struct stat st; *cp = '\0'; if (stat (file, &st) == NOTOK) { int answer; char *ep; if (errno != ENOENT) { advise (file, "error on directory"); losing_directory: ; *cp = '/'; return NOTOK; } ep = concat ("Create directory \"", file, "\"? ", NULLCP); answer = getanswer (ep); free (ep); if (!answer) goto losing_directory; if (!makedir (file)) { advise (NULLCP, "unable to create directory %s", file); goto losing_directory; } } *cp = '/'; } 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); ci -> ci_type = NULL; if (ci -> ci_subtype) free (ci -> ci_subtype); ci -> ci_subtype = NULL; for (ap = ci -> ci_attrs; *ap; ap++) free (*ap), *ap = NULL; if (ci -> ci_comment) free (ci -> ci_comment); ci -> ci_comment = NULL; if (ci -> ci_magic) free (ci -> ci_magic); ci -> ci_magic = NULL; } static void free_content (ct) register CT ct; { if (!ct) return; if (ct -> c_partno) free (ct -> c_partno); ct -> c_partno = NULL; if (ct -> c_vrsn) free (ct -> c_vrsn); ct -> c_vrsn = NULL; if (ct -> c_ctline) free (ct -> c_ctline); ct -> c_ctline = NULL; free_ctinfo (ct); if (ct -> c_ctfreefnx) (void) (*ct -> c_ctfreefnx) (ct); ct -> c_ctfreefnx = NULL; if (ct -> c_showproc) free (ct -> c_showproc); ct -> c_showproc = NULL; if (ct -> c_termproc) free (ct -> c_termproc); ct -> c_termproc = NULL; if (ct -> c_storeproc) free (ct -> c_storeproc); ct -> c_storeproc = NULL; if (ct -> c_celine) free (ct -> c_celine); ct -> c_celine = NULL; if (ct -> c_cefreefnx) (void) (*ct -> c_cefreefnx) (ct, 1); ct -> c_cefreefnx = NULL; if (ct -> c_id) free (ct -> c_id); ct -> c_id = NULL; if (ct -> c_descr) free (ct -> c_descr); ct -> c_descr = NULL; #ifdef MH_PLUS if (ct -> m_subj) free (ct -> m_subj); ct -> m_subj = NULL; #endif /* MH_PLUS */ if (ct -> c_file) { if (ct -> c_unlink) (void) unlink (ct -> c_file); free (ct -> c_file); ct -> c_file = NULL; } if (ct -> c_fp) (void) fclose (ct -> c_fp); ct -> c_fp = NULL; if (ct -> c_storage) (void) free (ct -> c_storage); ct -> c_storage = NULL; free ((char *) ct); } /* */ static int part_ok (ct, sP) register CT ct; int sP; { register char **ap; if ((ct -> c_type == CT_MULTIPART && (sP || ct -> c_subtype)) || 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, sP) register CT ct; int sP; { register char **ap; char buffer[BUFSIZ]; register CI ci = &ct -> c_ctinfo; if ((ct -> c_type == CT_MULTIPART && (sP || ct -> c_subtype)) || 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; }; static int InitGeneric (ct) register CT ct; { register char **ap, **ep; register CI ci = &ct -> c_ctinfo; for (ap = ci -> ci_attrs, ep = ci -> ci_values; *ap; ap++, ep++) if (autosw && !ct -> c_storeproc && uleq (*ap, "name")) { register char *cp; if (*(cp = *ep) != '/' && *cp != '.' && *cp != '|' && *cp != '!' && strlen(cp) <= NAMESZ && !index (cp, '%')) ct -> c_storeproc = add (cp, NULLCP); } return OK; } /* 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 #ifdef JAPAN #define CHARSET_ISO_2022_JP 0x03 #endif /* JAPAN */ }; static struct k2v Charset[] = { "us-ascii", CHARSET_USASCII, "iso-8859-1", CHARSET_LATIN, #ifdef JAPAN "iso-2022-jp", CHARSET_ISO_2022_JP, #endif /* JAPAN */ 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 (!ct -> c_ctparams && 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); } else if (autosw && !ct -> c_storeproc && uleq (*ap, "name")) { register char *cp; if (*(cp = *ep) != '/' && *cp != '.' && *cp != '|' && *cp != '!' && strlen(cp) <= NAMESZ && !index (cp, '%')) ct -> c_storeproc = add (cp, NULLCP); } 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; }; #ifdef MH_PLUS static int openRawMulti(); #endif /* MH_PLUS */ 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, 1) && type_ok (p, 1) && 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 = alternate ? NOTOK : OK; for (part = m -> mp_parts; part; part = part -> mp_next) { p = part -> mp_part; if (part_ok (p, 0) && type_ok (p, 0) && p -> c_ctshowfnx) { int inneresult; switch (inneresult = (*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; if (result == NOTOK) result = inneresult; } 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; } static int show_unknown_multi (ct, serial, alternate) register CT ct; int serial, alternate; { int xlist, xpause, xtty, quoted; register char *bp, *cp; char buffer[BUFSIZ], *bufend; register struct multipart *m = (struct multipart *) ct -> c_ctparams; register struct part *part; register CI ci = &ct -> c_ctinfo; register CT p; (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 (ct->c_subtype != MULTI_UNKNOWN) /* consult internal method. */ return show_multi (ct, serial, alternate); if (!alternate) /* treat as multipart/mixed (cf. RFC2046) */ return show_multi (ct, serial, alternate); /* content_error (NULLCP, ct, "don't know how to display content"); */ return NOTOK; } } for (part = m -> mp_parts; part; part = part -> mp_next) { p = part -> mp_part; if (!p -> c_ceopenfnx) { if (!alternate) content_error (NULLCP, p, "don't know how to decode content"); return NOTOK; } #ifdef MH_PLUS /* * We want raw contents for "show" (for multipart/signed etc.), * and want decoded contents for "store". Now re-store raw contents! */ { char *file = NULL; if (openRawMulti (p, &file) == NOTOK) return NOTOK; if (p -> c_storage) free (p -> c_storage); #else /* MH_PLUS */ if (p -> c_storage == NULL) { char *file = NULL; if ((*p -> c_ceopenfnx) (p, &file) == NOTOK) return NOTOK; #endif /* MH_PLUS */ p -> c_storage = add (file, NULLCP); if (p -> c_showproc && strcmp (p -> c_showproc, "true") == 0) return (alternate ? DONE : OK); (*p -> c_ceclosefnx) (p); } } xlist = xpause = xtty = 0; buffer[0] = '\0'; bufend = buffer + sizeof(buffer) - NAMESZ; /* NAMESZ for c_termproc */ quoted = 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++) { if (bp + strlen(*ap) + strlen(*ep) + 4 >= bufend) break; /* quoted may have no meaning */ (void) sprintf (bp, "%s\'%s=%s\'", s, *ap, *ep); /* Escape existing quotes */ if (escape_quotes(bp + strlen(s), bufend, 0)) *bp = '\0'; bp += strlen (bp); s = " "; } } break; case 'd': /* content description */ if (ct -> c_descr) { char *s; if (bp + strlen(s = trimcpy (ct -> c_descr)) + 2 >= bufend) break; if (quoted) (void) strcpy (bp, s); else (void) sprintf (bp, "\'%s\'", s); free (s); if (escape_quotes(bp, bufend, quoted)) *bp = '\0'; } break; case 'e': /* exclusive execution */ xtty = 1; break; case 'F': /* %e and %f */ xtty = 1; /* and fall... */ case 'f': /* filename(s) */ { char *s = ""; for (part = m -> mp_parts; part; part = part -> mp_next) { p = part -> mp_part; if (bp + strlen(p -> c_storage) + 3 >= bufend) break; /* quoted may have no meaning */ (void) sprintf (bp, "%s\'%s\'", s, p -> c_storage); /* Escape existing quotes */ if (escape_quotes(bp + strlen(s), bufend, 0)) *bp = '\0'; bp += strlen (bp); s = " "; } } break; case 'p': /* pause prior to displaying content */ xpause = pausesw; /* and fall... */ case 'l': /* display listing prior to displaying content */ xlist = !nolist; break; case 's': /* subtype */ if (bp + strlen(ci -> ci_subtype) + 2 >= bufend) break; if (quoted) (void) strcpy (bp, ci -> ci_subtype); else (void) sprintf (bp, "\'%s\'", ci -> ci_subtype); if (escape_quotes(bp, bufend, quoted)) *bp = '\0'; break; case '%': goto raw; default: if (bp + 1 >= bufend) break; *bp++ = *--cp; *bp = '\0'; continue; } bp += strlen (bp); } else { raw: ; if (bp + 1 >= bufend) break; *bp++ = *cp; *bp = '\0'; if (*cp == '\'') quoted = !quoted; } if (ct -> c_termproc) { char term[BUFSIZ]; (void) strcpy (term, buffer); (void) sprintf (buffer, ct -> c_termproc, term); } return show_content_aux2 (ct, serial, alternate, NULLCP, buffer, NOTOK, xlist, xpause, 0, xtty); } /* 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, 1) && type_ok (p, 1) && 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! */ }; /* tailing LWSP-chars of delimiter are ignored. (cf. RFC-2046) */ static int fuzzy_strcmp (str, substr) register char *str, *substr; { while (*str == *substr++) if (*str++ == 0) return 0; if (*(substr - 1) != '\n') return 1; for (; *str == ' ' || *str == '\t'; str++) continue; if (*str != '\n') return 1; return 0; } 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 && ct -> c_encoding != CE_8BIT && ct -> c_encoding != CE_BINARY) { admonish (NULLCP, "\"%s/%s\" type in message %s should be encoded in 7bit/8bit/binary", 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; #if 0 ct -> c_ctshowfnx = ct -> c_subtype != MULTI_UNKNOWN ? show_multi : show_unknown_multi; #else /* At first, consult mhn_default (eg. "mhn-show-multipart/mixed"). Next, consult internal method (ie. show_multi()). */ ct -> c_ctshowfnx = show_unknown_multi; #endif 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 (fuzzy_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 (fuzzy_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 (fuzzy_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 && 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); if (pp - partnam > NAMESZ) { advise(NULLCP, "multipart nest too deep in message %s", ct -> c_file); (void) fclose (ct -> c_fp), ct -> c_fp = NULL; return NOTOK; } } 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; unsigned long eb_size; char *eb_server; char *eb_subject; char *eb_body; }; static int openFile (); static int openFTP (); static int openMail (); /* NOTE WELL: mhnsi_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 -> mhnsi_key; s2i++) if (uleq (*ep, s2i -> mhnsi_key)) break; if (!s2i -> mhnsi_key) { e -> eb_access = *ep; e -> eb_flags = NOTOK; p -> c_encoding = CE_EXTERNAL; continue; } e -> eb_access = s2i -> mhnsi_key; e -> eb_flags = s2i -> mhnsi_value; p -> c_encoding = CE_EXTERNAL; if (init_encoding (p, s2i -> mhnsi_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 (uleq (*ap, "subject")) { e -> eb_subject = *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); if (e -> eb_subject) printf ("\t with subject %s\n", e -> eb_subject); 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, 0)) 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, 1)) 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 && ct -> c_encoding != CE_8BIT && ct -> c_encoding != CE_BINARY) { admonish (NULLCP, "\"%s/%s\" type in message %s should be encoded in 7bit/8bit/binary", 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); #ifdef MH_PLUS 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, ct -> c_begin, 0); { int state; char *cp, *dp, buf[BUFSIZ], name[NAMESZ]; for (state = FLD;;) { switch (state = m_getfld(state, name, buf, sizeof buf, ct -> c_fp)) { case FLD: case FLDPLUS: case FLDEOF: cp = add(buf, NULLCP); while (state == FLDPLUS) { state = m_getfld(state, name, buf, sizeof buf, ct -> c_fp); cp = add(buf, cp); } if (!uleq(name, "Subject")) { free(cp); break; } dp = trimcpy(cp); #ifdef MIME_HEADERS exthdr_decode(dp, cp); ct -> m_subj = cp; free(dp); #else /* MIME_HEADERS */ ct -> m_subj = dp; free(cp); #endif /* MIME_HEADERS */ /* fall */ default: fclose(ct -> c_fp); ct -> c_fp = NULL; return OK; } } } /* NOT REACHED */ #endif /* MH_PLUS */ 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) { if (!e -> eb_subject) 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 != '!' && strlen(cp) <= NAMESZ && !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_POSTSCRIPT 0x02 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, "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; for (ap = ci -> ci_attrs, ep = ci -> ci_values; *ap; ap++, ep++) if (autosw && !ct -> c_storeproc && uleq (*ap, "name")) { register char *cp; if (*(cp = *ep) != '/' && *cp != '.' && *cp != '|' && *cp != '!' && strlen(cp) <= NAMESZ && !index (cp, '%')) ct -> c_storeproc = add (cp, NULLCP); } 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 (*ap, "x-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 */ #include "md5.c" 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, digested, 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; MD5_CTX mdContext; 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; } if (digested = ct -> c_digested) MD5Init (&mdContext); (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 (digested) MD5Update (&mdContext, b1, 1); if (skip < 2) { (void) putc ((char) *b2, ce -> ce_fp); if (digested) MD5Update (&mdContext, b2, 1); if (skip < 1) { (void) putc ((char) *b3, ce -> ce_fp); if (digested) MD5Update (&mdContext, b3, 1); } } 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; } if (digested) { unsigned char digest[16]; MD5Final (digest, &mdContext); if (bcmp ((char *) digest, (char *) ct -> c_digest, sizeof digest / sizeof digest[0])) content_error (NULLCP, ct, "content integrity suspect (digest mismatch) -- continuing"); else if (debugsw) fprintf (stderr, "content integrity confirmed\n"); } 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 () { char *cp; 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"); if (!(mm_charset = getenv ("MM_CHARSET"))) mm_charset = m_find ("MM-Charset"); if ((cp = getenv ("MM_NOASK")) && strcmp (cp, "1") == 0) { nolist = 1, pausesw = 0; if (showsw) listsw = 0; } } /* 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, digested, len, quoted; register char *cp, *ep; char buffer[BUFSIZ]; unsigned char mask; register struct cefile *ce = (struct cefile *) ct -> c_ceparams; MD5_CTX mdContext; 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; } if (digested = ct -> c_digested) MD5Init (&mdContext); (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); if (digested) MD5Update (&mdContext, &mask, 1); } else switch (*cp) { case ':': (void) putc (*cp, ce -> ce_fp); if (digested) MD5Update (&mdContext, (unsigned char *) ":", 1); 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 (digested) { if (*cp == '\n') MD5Update (&mdContext, (unsigned char *) "\r\n",2); else MD5Update (&mdContext, (unsigned char *) cp, 1); } 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; } if (digested) { unsigned char digest[16]; MD5Final (digest, &mdContext); if (bcmp ((char *) digest, (char *) ct -> c_digest, sizeof digest / sizeof digest[0])) content_error (NULLCP, ct, "content integrity suspect (digest mismatch) -- continuing"); else if (debugsw) fprintf (stderr, "content integrity confirmed\n"); } 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); } #ifdef MH_PLUS /* raw multipart */ /* We want raw unencoded contents. (for multipart/signed etc.) */ static int openRawMulti (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) { fclose (ce -> ce_fp); ce -> ce_fp = NULL; } if (ce -> ce_file) { free(ce -> ce_file); ce -> ce_file = NULL; } 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_fp && (ct -> c_fp = fopen (ct -> c_file, "r")) == NULL) { content_error (ct -> c_file, ct, "unable to open for reading"); return NOTOK; } fseek(ct -> c_fp, ct -> c_start, 0); if ((len = ct -> c_end - ct -> c_start) < 0) adios (NULLCP, "internal error"); while (len > 0) switch (cc = fread(buffer, sizeof *buffer, sizeof buffer - 1, ct -> c_fp)) { case NOTOK: content_error(ct -> c_file, ct, "error reading from"); goto clean_up_raw; case OK: content_error(NULLCP, ct, "premature eof"); goto clean_up_raw; 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_raw; } } (void) fseek (ct -> c_fp, 0L, 0); if (fflush (ce -> ce_fp)) { content_error (ce -> ce_file, ct, "error writing to"); goto clean_up_raw; } (void) fseek (ce -> ce_fp, 0L, 0); *file = ce -> ce_file; return fileno (ce -> ce_fp); clean_up_raw: ; free_encoding (ct, 0); return NOTOK; } #endif /* MH_PLUS */ /* 7BIT */ static int open7Bit (ct, file) register CT ct; char **file; { int cc, fd, digested, len; char buffer[BUFSIZ]; register struct cefile *ce = (struct cefile *) ct -> c_ceparams; MD5_CTX mdContext; 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 (ct -> c_type == CT_MULTIPART) { register char **ap, **ep; register CI ci = &ct -> c_ctinfo; len = 0; fprintf (ce -> ce_fp, "%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); for (ap = ci -> ci_attrs, ep = ci -> ci_values; *ap; ap++, ep++) { (void) putc (';', ce -> ce_fp); len++; (void) sprintf (buffer, "%s=\"%s\"", *ap, *ep); if (len + 1 + (cc = strlen (buffer)) >= CPERLIN) { (void) fputs ("\n\t", ce -> ce_fp); len = 8; } else { (void) putc (' ', ce -> ce_fp); len++; } fprintf (ce -> ce_fp, "%s", buffer); len += cc; } if (ci -> ci_comment) { #ifdef MIME_HEADERS char *ep; if ((ep = malloc((unsigned)strlen(ci->ci_comment)*10)) == NULL) adios(NULLCP, "out of memory"); (void) exthdr_encode(ci -> ci_comment, ep, len+1+1, TYPE_FIELD); if (len + 1 + 2 + strlen(ep) >= CPERLIN) (void) exthdr_encode(ci -> ci_comment, ep, 8+1, TYPE_FIELD); ci -> ci_comment = ep; #endif /* MIME_HEADERS */ if (len + 1 + (cc = 2 + strlen (ci -> ci_comment)) >= CPERLIN) { (void) fputs ("\n\t", ce -> ce_fp); len = 8; } else { (void) putc (' ', ce -> ce_fp); len++; } fprintf (ce -> ce_fp, "(%s)", ci -> ci_comment); len += cc; } fprintf (ce -> ce_fp, "\n"); if (ct -> c_id) fprintf (ce -> ce_fp, "%s:%s", ID_FIELD, ct -> c_id); if (ct -> c_descr) { #ifdef MIME_HEADERS char *ep; if ((ep = malloc((unsigned)strlen(ct->c_descr)*10)) == NULL) adios(NULLCP, "out of memory"); (void) exthdr_encode(ct -> c_descr, ep, strlen(DESCR_FIELD)+1, DESCR_FIELD); free(ct -> c_descr); ct -> c_descr = ep; #endif /* MIME_HEADERS */ fprintf (ce -> ce_fp, "%s:%s", DESCR_FIELD, ct -> c_descr); } fprintf (ce -> ce_fp, "\n"); } 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; } if (digested = ct -> c_digested) MD5Init (&mdContext); (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; } if (digested) { unsigned char digest[16]; MD5Final (digest, &mdContext); if (bcmp ((char *) digest, (char *) ct -> c_digest, sizeof digest / sizeof digest[0])) content_error (NULLCP, ct, "content integrity suspect (digest mismatch) -- continuing"); else if (debugsw) fprintf (stderr, "content integrity confirmed\n"); } 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, cb, ce, file, fd) register CT ct; CT cb; struct cefile *ce; char **file; int *fd; { char cachefile[BUFSIZ]; 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 (find_cache (ct, rcachesw, (int *) 0, cb -> c_id, cachefile) != NOTOK) { if (ce -> ce_fp = fopen (cachefile, "r")) { ce -> ce_unlink = 0; ce -> ce_file = getcpy (cachefile); goto ready_already; } else admonish (cachefile, "unable to fopen for reading"); } return OK; } /* File */ static int openFile (ct, file) register CT ct; char **file; { int fd, cachetype; char cachefile[BUFSIZ]; register struct exbody *e = (struct exbody *) ct -> c_ctextern; register struct cefile *ce = (struct cefile *) ct -> c_ceparams; switch (openExternal (e -> eb_parent, e -> eb_content, 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 ((!e -> eb_permission || !uleq (e -> eb_permission, "read-write")) && find_cache (NULLCT, wcachesw, &cachetype, e -> eb_content -> c_id, cachefile) != NOTOK) { int mask; FILE *fp; mask = umask (cachetype ? ~m_gmprot () : 0222); if (fp = fopen (cachefile, "w")) { int cc; char buffer[BUFSIZ]; FILE *gp = ce -> ce_fp; (void) fseek (gp, 0L, 0); while ((cc = fread (buffer, sizeof *buffer, sizeof buffer, gp)) > 0) (void) fwrite (buffer, sizeof *buffer, cc, fp); (void) fflush (fp); if (ferror (gp)) { admonish (ce -> ce_file, "error reading"); (void) unlink (cachefile); } else 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 cachetype, caching, fd; char *bp, *ftp, *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, e -> eb_content, 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; } if (xpid) { if (xpid < 0) xpid = -xpid; (void) pidcheck (pidwait (xpid, NOTOK)); xpid = 0; } bp = buffer; (void) sprintf (bp, "Retrieve %s", e -> eb_name); bp += strlen (bp); if (e -> eb_partno) { (void) sprintf (bp, " (content %s)", e -> eb_partno); bp += strlen (bp); } (void) sprintf (bp, "\n using %sFTP from site %s", e -> eb_flags ? "anonymous " : "", e -> eb_site); bp += strlen (bp); if (e -> eb_size > 0) { (void) sprintf (bp, " (%lu octets)", e -> eb_size); bp += strlen (bp); } (void) sprintf (bp, "? "); 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 ((!e -> eb_permission || !uleq (e -> eb_permission, "read-write")) && find_cache (NULLCT, wcachesw, &cachetype, e -> eb_content -> c_id, cachefile) != NOTOK) { 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, cachetype ? m_gmprot () : 0444); else { int mask; FILE *fp; mask = umask (cachetype ? ~m_gmprot () : 0222); if (fp = fopen (cachefile, "w")) { int cc; FILE *gp = ce -> ce_fp; (void) fseek (gp, 0L, 0); while ((cc = fread (buffer, sizeof *buffer, sizeof buffer, gp)) > 0) (void) fwrite (buffer, sizeof *buffer, cc, fp); (void) fflush (fp); if (ferror (gp)) { admonish (ce -> ce_file, "error reading"); (void) unlink (cachefile); } else 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, vecp; char *bp, 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, e -> eb_content, 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; } if (xpid) { if (xpid < 0) xpid = -xpid; (void) pidcheck (pidwait (xpid, NOTOK)); xpid = 0; } bp = buffer; (void) sprintf (bp, "Retrieve content"); bp += strlen (bp); if (e -> eb_partno) { (void) sprintf (bp, " %s", e -> eb_partno); bp += strlen (bp); } (void) sprintf (bp, " by asking %s\n\n%s\n? ", e -> eb_server, e -> eb_subject ? e -> eb_subject : e -> eb_body); if (!getanswer (buffer)) return NOTOK; vecp = 0; vec[vecp++] = r1bindex (mailproc, '/'); vec[vecp++] = e -> eb_server; vec[vecp++] = "-subject"; vec[vecp++] = e -> eb_subject ? e -> eb_subject : "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); } /* CACHE */ static int find_cache (ct, policy, writing, id, buffer) CT ct; int policy, *writing; char *id, *buffer; { int status = NOTOK; if (id == NULL) return NOTOK; id = trimcpy (id); if (debugsw) fprintf (stderr, "find_cache %s(%d) %s %s\n", caches[policy].sw, policy, writing ? "writing" : "reading", id); switch (policy) { case CACHE_NEVER: default: break; case CACHE_ASK: case CACHE_PUBLIC: if (cache_private && !writing && find_cache_aux (writing ? 2 : 0, cache_private, id, buffer) == OK) { if (access (buffer, 04) != NOTOK) { got_private: ; if (writing) *writing = 1; got_it: ; status = OK; break; } } if (cache_public && find_cache_aux (writing ? 1 : 0, cache_public, id, buffer) == OK) { if (writing || access (buffer, 04) != NOTOK) { if (writing) *writing = 0; goto got_it; } } break; case CACHE_PRIVATE: if (cache_private && find_cache_aux (writing ? 2 : 0, cache_private, id, buffer) == OK) { if (writing || access (buffer, 04) != NOTOK) goto got_private; } break; } if (status == OK && policy == CACHE_ASK) { char *bp, query[BUFSIZ]; if (xpid) { if (xpid < 0) xpid = -xpid; (void) pidcheck (pidwait (xpid, NOTOK)); xpid = 0; } bp = query; if (writing) (void) sprintf (bp, "Make cached, publically-accessible copy"); else { struct stat st; (void) sprintf (bp, "Use cached copy"); bp += strlen (bp); if (ct -> c_partno) { (void) sprintf (bp, " of content %s", ct -> c_partno); bp += strlen (bp); } (void) stat (buffer, &st); (void) sprintf (bp, " (size %lu octets)", (unsigned long) st.st_size); } bp += strlen (bp); (void) sprintf (bp, "\n in file %s? ", buffer); if (!getanswer (query)) status = NOTOK; } if (status == OK && writing) { if (*writing && index (buffer, '/')) (void) make_intermediates (buffer); (void) unlink (buffer); } free (id); return status; } /* */ static int find_cache_aux (writing, directory, id, buffer) int writing; char *directory, *id, *buffer; { int mask; #ifdef BSD42 int usemap = (index (id, '/') || strlen(id) > BUFSIZ / 4) ? 1 : 0; #else int usemap = 1; #endif char mapfile[BUFSIZ], mapname[BUFSIZ]; FILE *fp; static int partno, pid; static time_t clock = 0L; if (debugsw) fprintf (stderr, "find_cache_aux %s usemap=%d\n", directory, usemap); (void) sprintf (mapfile, "%s/cache.map", directory); if (find_cache_aux2 (mapfile, id, mapname) == OK) goto done_map; if (!writing) { if (usemap) return NOTOK; use_raw: ; (void) sprintf (buffer, "%s/%s", directory, id); return OK; } if (!usemap && access (mapfile, 02) == NOTOK) goto use_raw; if (clock != 0L) { time_t now; (void) time (&now); if (now > clock) clock = 0L; } else pid = getpid (); if (clock == 0L) { (void) time (&clock); partno = 0; } else if (partno > 0xff) clock++, partno = 0; (void) sprintf (mapname, "%08x%04x%02x", clock & 0xffffffff, pid & 0xffff, partno++ & 0xff); if (debugsw) fprintf (stderr, "creating mapping %s -> %s\n", mapname, id); (void) make_intermediates (mapfile); mask = umask (writing == 2 ? 0077 : 0); if (!(fp = lkfopen (mapfile, "a")) && errno == ENOENT) { int fd = creat (mapfile, 0666); if (fd != NOTOK) { (void) close (fd); fp = lkfopen (mapfile, "a"); } } (void) umask (mask); if (!fp) return NOTOK; fprintf (fp, "%s: %s\n", mapname, id); (void) lkfclose (fp, mapfile); done_map: ; if (*mapname == '/') (void) strcpy (buffer, mapname); else (void) sprintf (buffer, "%s/%s", directory, mapname); if (debugsw) fprintf (stderr, "use %s\n", buffer); return OK; } /* */ static int find_cache_aux2 (mapfile, id, mapname) char *mapfile, *id, *mapname; { int state; char buf[BUFSIZ], name[NAMESZ]; FILE *fp; if (!(fp = lkfopen (mapfile, "r"))) return NOTOK; for (state = FLD;;) { int result; register char *cp, *dp; switch (state = m_getfld (state, name, buf, sizeof buf, fp)) { case FLD: case FLDPLUS: case FLDEOF: (void) strcpy (mapname, name); if (state != FLDPLUS) cp = buf; else { cp = add (buf, NULLCP); while (state == FLDPLUS) { state = m_getfld (state, name, buf, sizeof buf, fp); cp = add (buf, cp); } } dp = trimcpy (cp); if (cp != buf) free (cp); if (debugsw) fprintf (stderr, "compare %s to %s <- %s\n", id, dp, mapname); result = strcmp (id, dp); free (dp); if (result == 0) { (void) lkfclose (fp, mapfile); return OK; } if (state != FLDEOF) continue; /* else fall... */ case BODY: case BODYEOF: case FILEEOF: default: break; } break; } (void) lkfclose (fp, mapfile); return NOTOK; } /* */ static int cache_content (ct) register CT ct; { int cachetype; char *file, cachefile[BUFSIZ]; register struct cefile *ce = (struct cefile *) ct -> c_ceparams; if (!ct -> c_id) { advise (NULLCP, "no %s: field in %s", ID_FIELD, ct -> c_file); return; } if (!ce) { advise (NULLCP, "unable to decode %s", ct -> c_file); return; } if (ct -> c_ceopenfnx == openMail) { advise (NULLCP, "a radish may no know Greek, but I do..."); return; } if (find_cache (NULLCT, wcachesw != CACHE_NEVER ? wcachesw : CACHE_ASK, &cachetype, ct -> c_id, cachefile) == NOTOK) { advise (NULLCP, "unable to cache %s's contents", ct -> c_file); return; } if (wcachesw != CACHE_NEVER && wcachesw != CACHE_ASK) { (void) fflush (stdout); fprintf (stderr, "caching message %s as file %s\n", ct -> c_file, cachefile); } if (ce -> ce_file) { int mask = umask (cachetype ? ~m_gmprot () : 0222); FILE *fp; if (debugsw) fprintf (stderr, "caching by copying %s...\n", ce -> ce_file); file = NULL; if ((*ct -> c_ceopenfnx) (ct, &file) == NOTOK) goto reset_umask; if (fp = fopen (cachefile, "w")) { int cc; char buffer[BUFSIZ]; FILE *gp = ce -> ce_fp; (void) fseek (gp, 0L, 0); while ((cc = fread (buffer, sizeof *buffer, sizeof buffer, gp)) > 0) (void) fwrite (buffer, sizeof *buffer, cc, fp); (void) fflush (fp); if (ferror (gp)) { admonish (ce -> ce_file, "error reading"); (void) unlink (cachefile); } else if (ferror (fp)) { admonish (cachefile, "error writing"); (void) unlink (cachefile); } (void) fclose (fp); } else content_error (cachefile, ct, "unable to fopen for writing"); reset_umask: ; (void) umask (mask); } else { if (debugsw) fprintf (stderr, "in place caching...\n"); file = cachefile; if ((*ct -> c_ceopenfnx) (ct, &file) != NOTOK) (void) chmod (cachefile, cachetype ? m_gmprot () : 0444); } } /* 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) || uleq (name, ID_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); #if defined(JAPAN) || defined(MIME_HEADERS) { char *cp, *ep; cp = add(buf, NULLCP); while (state == FLDPLUS) { state = m_getfld (state, name, buf, sizeof buf, in); cp = add(buf, ep); } #ifdef MIME_HEADERS if ((ep = malloc((unsigned)strlen(cp)*10+1)) == NULL) adios(NULLCP, "out of memory"); #ifdef JAPAN (void) ml_conv(cp); #endif /* JAPAN */ (void) exthdr_encode(cp, ep, strlen(name)+1, name); fprintf(out, "%s:%s", name, ep); free(ep); #else /* MIME_HEADERS */ fprintf(out, "%s:%s", name, cp); #endif /* MIME_HEADERS */ free(cp); } #else /* JAPAN || MIME_HEADERS */ fprintf (out, "%s:%s", name, buf); while (state == FLDPLUS) { state = m_getfld (state, name, buf, sizeof buf, in); (void) fputs (buf, out); } #endif /* JAPAN || MIME_HEADERS */ finish_field: ; if (state != FLDEOF) continue; /* else fall... */ case FILEEOF: adios (NULLCP, "draft has empty body -- no directives!"); /* NOTREACHED */ case BODY: case BODYEOF: /* The draft file is not "binary". This strlen() is OK. */ (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; } else set_id (ct, 1); 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; set_id (ct, 0); 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 '#': if (buffer[1] != '#') adios (NULLCP, "#-directive after %s: field in plaintext", DESCR_FIELD); /* else fall... */ default: break; } } if (headers != 1 || buffer[0] != '\n') #ifdef JAPAN if (junetsw) { (void) ml_conv(buffer); junet_fputs(buffer, out); } else #endif /* JAPAN */ (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 -> mhnsi_key; s2i++) if (uleq (ci -> ci_type, s2i -> mhnsi_key)) break; if (!s2i -> mhnsi_key && !uprf (ci -> ci_type, "X-")) s2i++; switch (ct -> c_type = s2i -> mhnsi_value) { case CT_MESSAGE: if (uleq (ci -> ci_subtype, "rfc822")) { ct -> c_encoding = CE_7BIT; /* XXX */ goto call_init; } /* else fall... */ 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: call_init: ; if (ct -> c_ctinitfnx = s2i -> mhnsi_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 -> mhnsi_key; s2i++) if (uleq (ci -> ci_type, s2i -> mhnsi_key)) break; if (s2i -> mhnsi_key) { /* type/subtype [file] */ if (!ci -> ci_subtype) adios (NULLCP, "missing subtype in \"#%s\"", ci -> ci_type); switch (ct -> c_type = s2i -> mhnsi_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); use_forw: ; adios (NULLCP, "use \"#forw [+folder] [msgs]\" instead of \"#%s/%s\"", ci -> ci_type, ci -> ci_subtype); /* NOTREACHED */ default: if (ct -> c_ctinitfnx = s2i -> mhnsi_init) (void) (*ct -> c_ctinitfnx) (ct); break; } if (extrnal) { register struct exbody *e; CT p; 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); 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 (mp -> numsel > 1) { 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; } else free_content (ct); 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 (mp -> numsel > 1) { if ((part = (struct part *) calloc (1, sizeof *part)) == NULL) adios (NULLCP, "out of memory"); *pp = part, pp = &part -> mp_next; part -> mp_part = p; } else *ctp = ct = 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 ] */ if (!ci -> ci_magic) cp = SubMultiPart[(vrsn = MULTI_MIXED) - 1].kv_key; else if (uleq (ci -> ci_magic, "alternative")) cp = SubMultiPart[(vrsn = MULTI_ALTERNATE) - 1].kv_key; else if (uleq (ci -> ci_magic, "parallel")) cp = SubMultiPart[(vrsn = MULTI_PARALLEL) - 1].kv_key; else if (uprf (ci -> ci_magic, "digest")) goto use_forw; else cp = ci -> ci_magic, vrsn = MULTI_UNKNOWN; free_ctinfo (ct); (void) sprintf (buffer, "multipart/%s", cp); 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 void set_id (ct, top) CT ct; int top; { char msgid[BUFSIZ]; static int partno; static time_t clock = 0L; static char *msgfmt; if (clock == 0L) { (void) time (&clock); (void) sprintf (msgid, "<%d.%ld.%%d@%s>\n", getpid (), (long)clock, LocalName ()); partno = 0; msgfmt = getcpy (msgid); } (void) sprintf (msgid, msgfmt, top ? 0 : ++partno); ct -> c_id = getcpy (msgid); } /* */ static char ebcdicsafe[0x100] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 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, 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, 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 }; 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 (p -> c_encoding == CE_8BIT) { if (ct -> c_encoding != CE_BINARY) ct -> c_encoding = CE_8BIT; } else if (p -> c_encoding == CE_BINARY) ct -> c_encoding = CE_BINARY; } 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) break; /* else fall... */ case CT_MESSAGE: { int charset, len, linelen, nullch, 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 = ct -> c_subtype == TEXT_PLAIN ? -2 : -1; nullch = ct -> c_subtype == TEXT_PLAIN ? 0 : -1; break; case CT_APPLICATION: charset = linelen = nullch = ct -> c_encoding ? 0 : -1; break; default: /* "message" type is not always "7bit" */ charset = nullch = -1; linelen = -2; break; } while (fgets (buffer, sizeof buffer - 1, in)) { if (nullch == -2) nullch = 1; /* was not EOF, but contain NULL char */ if (charset == -1) { for (cp = buffer; *cp; cp++) { if (!isascii (*cp)) { charset = CHARSET_UNKNOWN; break; } #ifdef JAPAN if (*cp == '\033' && *(cp+1) == '$' && (*(cp+2) == 'B' || *(cp+2) == '@')) { charset = CHARSET_ISO_2022_JP; } #endif /* JAPAN */ if (linelen == -1 && ebcdicsw && !ebcdicsafe[*cp & 0xff]) linelen = 1; } for (; *cp; cp++) continue; if ((linelen == -1) && (cp - buffer > CPERLIN + 1)) linelen = 1; if ((linelen == -2) && (cp - buffer > MIMELIN + 1)) linelen = 1; if ((nullch == -1) && *(cp - 1) != '\n') nullch = -2; /* contain NULL char or EOF */ if (result == NOTOK) break; } else { cp = buffer + strlen (buffer); if ((linelen == -1) && (cp - buffer > CPERLIN + 1)) linelen = 1; if ((linelen == -2) && (cp - buffer > MIMELIN + 1)) linelen = 1; if ((nullch == -1) && *(cp - 1) != '\n') nullch = -2; /* contain NULL char or EOF */ } if (result == NOTOK) continue; if (linelen == -1) { if ((cp = buffer + strlen (buffer) - 2) > buffer && isspace (*cp)) linelen = 1; else if ((*(cp = buffer) == '.') || (strncmp (cp, "From ", sizeof "From " -1) == 0)) linelen = 1; } 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 && linelen != -2) break; } } } if (nullch == -2) nullch = -1; /* was EOF without last newline */ if (ct -> c_type == CT_MESSAGE) { ct -> c_encoding = (linelen == 1 || nullch == 1) ? CE_BINARY /* "message" type cannot be encoded */ : charset == CHARSET_UNKNOWN ? CE_8BIT : CE_7BIT; #ifdef JAPAN if (junetsw && ct -> c_subtype == MESSAGE_RFC822) ct -> c_encoding = CE_7BIT; #ifdef MH_PLUS if (ct -> c_encoding == CE_8BIT && uprf(mm_charset, "iso-2022-jp")) ct -> c_encoding = CE_7BIT; #endif /* MH_PLUS */ #endif /* JAPAN */ } if (ct -> c_type == CT_APPLICATION && !ct -> c_encoding) ct -> c_encoding = linelen == -1 && nullch == -1 && charset != CHARSET_UNKNOWN ? CE_7BIT : ct -> c_subtype != APPLICATION_POSTSCRIPT ? CE_BASE64 : 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 && *mm_charset) charset = -2; else if (charset == -1) /* MIME default charset must be US-ASCII */ /* charset = CHARSET_ISO_2022_JP; */ 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; #ifdef JAPAN case CHARSET_ISO_2022_JP: *ap = add ("charset=iso-2022-jp", NULLCP); break; #endif /* JAPAN */ case CHARSET_UNKNOWN: default: #if 0 /* We use "UNKNOWN-8BIT" charset value (cf. RFC-1428) */ *ap = add ("charset=x-unknown", NULLCP); #else *ap = add ("charset=unknown-8bit", NULLCP); #endif break; case -2: *ap = concat ("charset=", mm_charset, NULLCP); break; } cp = index (*ap++, '='); *ap = NULL; *cp++ = '\0'; *ep = cp; } if (ct -> c_type == CT_TEXT) { ct -> c_encoding = (linelen != -1 && linelen != -2) ? CE_QUOTED : nullch == 1 ? CE_QUOTED : (charset != CHARSET_UNKNOWN && charset != -2) #ifdef MH_PLUS ? CE_7BIT : eightsw ? CE_8BIT : CE_QUOTED; #else /* MH_PLUS */ ? CE_7BIT : CE_QUOTED; #endif /* MH_PLUS */ #if defined(JAPAN) && defined(MH_PLUS) if (ct -> c_encoding == CE_8BIT && uprf(mm_charset, "iso-2022-jp")) ct -> c_encoding = CE_7BIT; #endif /* JAPAN && MH_PLUS */ } (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++; } fputs (buffer, out); len += cc; } if (ci -> ci_comment) { #ifdef MIME_HEADERS char *ep; if ((ep = malloc((unsigned)strlen(ci->ci_comment)*10)) == NULL) adios(NULLCP, "out of memory"); (void) exthdr_encode(ci -> ci_comment, ep, len+1+1, TYPE_FIELD); if (len + 1 + 2 + strlen(ep) >= CPERLIN) (void) exthdr_encode(ci -> ci_comment, ep, 8+1, TYPE_FIELD); ci -> ci_comment = ep; #endif /* MIME_HEADERS */ 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; } (void) putc ('\n', out); if (ct -> c_id) fprintf (out, "%s: %s", ID_FIELD, ct -> c_id); if (ct -> c_descr) { #ifdef MIME_HEADERS char *ep; if ((ep = malloc((unsigned)strlen(ct->c_descr)*10)) == NULL) adios(NULLCP, "out of memory"); (void) exthdr_encode(ct -> c_descr, ep, strlen(DESCR_FIELD)+1, DESCR_FIELD); free(ct -> c_descr); ct -> c_descr = ep; #endif /* MIME_HEADERS */ 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_encoding == CE_8BIT) fprintf (out, "%s: %s\n", ENCODING_FIELD, "8bit"); else if (ct -> c_encoding == CE_BINARY) fprintf (out, "%s: %s\n", ENCODING_FIELD, "binary"); if (ct -> c_rfc934) (void) putc ('\n', out); 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]); (void) output_content (p, out); } fprintf (out, "\n--%s--\n", ci -> ci_values[0]); } break; case CT_TEXT: if (ct -> c_ctparams #ifdef MH_PLUS && !eightsw #endif /* MH_PLUS */ #ifdef JAPAN && ((struct text *) ct -> c_ctparams) -> tx_charset != CHARSET_ISO_2022_JP #endif /* JAPAN */ && ((struct text *) ct -> c_ctparams) -> tx_charset != CHARSET_USASCII) { quoted_printable: ; if (checksw) writeDigest (ct, out, 1); fprintf (out, "%s: %s\n\n", ENCODING_FIELD, "quoted-printable"); (void) writeQuoted (ct, out); break; } /* text/plain message that has too long line must be encoded. */ /* if ((ct -> c_subtype != TEXT_PLAIN && ct -> c_encoding != CE_7BIT) || checksw) */ if ((ct -> c_encoding != CE_7BIT && ct -> c_encoding != CE_8BIT) || checksw) goto quoted_printable; /* else fall... */ case CT_MESSAGE: seven_bit: ; #ifdef MH_PLUS if ((ct -> c_type == CT_TEXT || ct -> c_type == CT_APPLICATION) && (ct -> c_encoding == CE_7BIT)) fprintf (out, "%s: %s\n", ENCODING_FIELD, "7bit"); else #endif /* MH_PLUS */ if (ct -> c_encoding == CE_8BIT) fprintf (out, "%s: %s\n", ENCODING_FIELD, "8bit"); else if (ct -> c_encoding == CE_BINARY) fprintf (out, "%s: %s\n", ENCODING_FIELD, "binary"); (void) putc ('\n', out); 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) { register char *cp; putc ('\n', out); for (cp = e -> eb_body; *cp; cp++) { CT ct2 = e -> eb_content; CI ci2 = &ct2 -> c_ctinfo; if (*cp == '\\') switch (*++cp) { case 'I': if (ct2 -> c_id) { char *dp = trimcpy (ct2 -> c_id); (void) fputs (dp, out); free (dp); } continue; case 'N': for (ap = ci2 -> ci_attrs, ep = ci2 -> ci_values; *ap; ap++, ep++) if (uleq (*ap, "name")) { fprintf (out, "%s", *ep); break; } continue; case 'T': fprintf (out, "%s/%s", ci2 -> ci_type, ci2 -> ci_subtype); for (ap = ci2 -> ci_attrs, ep = ci2 -> ci_values; *ap; ap++, ep++) fprintf (out, "; %s=\"%s\"", *ap, *ep); continue; case 'n': (void) putc ('\n', out); continue; case 't': (void) putc ('\t', out); continue; case '\0': cp--; break; case '\\': case '"': break; default: (void) putc ('\\', out); break; } (void) putc (*cp, out); } putc ('\n', out); } } else if (ct -> c_encoding == CE_BINARY) (void) writeBinary (ct, out); else (void) write7Bit (ct, out); break; case CT_APPLICATION: switch (ct -> c_encoding) { case CE_7BIT: goto seven_bit; case CE_QUOTED: goto quoted_printable; default: break; } /* else fall... */ default: if (checksw) writeDigest (ct, out, 0); 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]; #ifdef JAPAN if (junetsw && ct -> c_type == CT_MESSAGE && ct -> c_subtype == MESSAGE_RFC822) { (void) ml_conv(buffer); junet_fputs(buffer, out); } else #endif /* JAPAN */ (void) fputs (buffer, out); } if (c != '\n') (void) putc ('\n', out); (void) fclose (in); return OK; } /* */ static int writeBinary (ct, out) register CT ct; FILE *out; { int c; char buffer[BUFSIZ]; FILE *in; if ((in = fopen (ct -> c_file, "r")) == NULL) adios (ct -> c_file, "unable to open for reading"); (void) fflush (out); while ((c = read (fileno(in), buffer, sizeof buffer - 1)) > 0) (void) write (fileno(out), buffer, c); (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 ((*(cp = buffer) == '.') || (strncmp (cp, "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; default: if (*cp < '!' || *cp > '~' || (ebcdicsw && !ebcdicsafe[*cp & 0xff])) goto three_print; (void) putc (*cp, out); n++; break; 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[0x40+1] = "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; } /* */ static int writeDigest (ct, out, asciiP) register CT ct; FILE *out; int asciiP; { int cc; char buffer[BUFSIZ]; register unsigned char *dp; unsigned char digest[16]; FILE *in; MD5_CTX mdContext; if ((in = fopen (ct -> c_file, "r")) == NULL) adios (ct -> c_file, "unable to open for reading"); MD5Init (&mdContext); if (asciiP) { while (fgets (buffer, sizeof buffer - 1, in)) { register char *cp; char c; cp = buffer + strlen (buffer) - 1; if ((c = *cp) == '\n') *cp = '\0'; MD5Update (&mdContext, (unsigned char *) buffer, (unsigned int) strlen (buffer)); if (c == '\n') MD5Update (&mdContext, (unsigned char *) "\r\n", 2); } } else while ((cc = fread (buffer, sizeof *buffer, sizeof buffer, in)) > 0) MD5Update (&mdContext, (unsigned char *) buffer, (unsigned int) cc); MD5Final (digest, &mdContext); if (debugsw) { unsigned char *ep; fprintf (stderr, "MD5 digest="); for (ep = (dp = digest) + sizeof digest / sizeof digest[0]; dp < ep; dp++) fprintf (stderr, "%02x", *dp & 0xff); fprintf (stderr, "\n"); } (void) fclose (in); fprintf (out, "%s: ", MD5_FIELD); for (dp = digest, cc = sizeof digest / sizeof digest[0]; cc > 0; cc -= 3) { unsigned long bits; register char *bp; char outbuf[4]; bits = (*dp++ & 0xff) << 16; if (cc > 1) { bits |= (*dp++ & 0xff) << 8; if (cc > 2) bits |= *dp++ & 0xff; } for (bp = outbuf + sizeof outbuf; bp > outbuf; bits >>= 6) *--bp = nib2b64[bits & 0x3f]; if (cc < 3) { outbuf[3] = '='; if (cc < 2) outbuf[2] = '='; } (void) fwrite (outbuf, sizeof *outbuf, sizeof outbuf, out); } fprintf (out, "\n"); } /* */ static int readDigest (ct, cp) register CT ct; register char *cp; { int bitno, skip; unsigned long bits; char *bp = cp; register unsigned char *dp; unsigned char value, *ep, *b = (unsigned char *) &bits, *b1 = &b[endian > 0 ? 1 : 2], *b2 = &b[endian > 0 ? 2 : 1], *b3 = &b[endian > 0 ? 3 : 0]; bitno = 18, bits = 0L, skip = 0; for (ep = (dp = ct -> c_digest) + sizeof ct -> c_digest / sizeof ct -> c_digest[0]; *cp; cp++) switch (*cp) { default: if (skip || (*cp & 0x80) || (value = b642nib[*cp & 0x7f]) > 0x3f) { if (debugsw) fprintf (stderr, "invalid BASE64 encoding\n"); return NOTOK; } bits |= value << bitno; test_end: ; if ((bitno -= 6) < 0) { if (dp + (3 - skip) > ep) goto invalid_digest; *dp++ = *b1; if (skip < 2) { *dp++ = *b2; if (skip < 1) *dp++ = *b3; } 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); return NOTOK; } self_delimiting: ; if (dp != ep) { invalid_digest: ; if (debugsw) { while (*cp) cp++; fprintf (stderr, "invalid MD5 digest (got %d octets)\n", cp - bp); } return NOTOK; } if (debugsw) { fprintf (stderr, "MD5 digest="); for (dp = ct -> c_digest; dp < ep; dp++) fprintf (stderr, "%02x", *dp & 0xff); fprintf (stderr, "\n"); } 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; long offset; 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++; offset = ftell (fp); 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, 0); else { int partno; time_t 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 (), (long)clock, LocalName ()); (void) fseek (fp, offset, 0); 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); } offset = ftell (fp); if (fflush (out)) adios (tmpdrf, "error writing to"); (void) fclose (out); status = via_post (tmpdrf, slowsw == 0); (void) unlink (tmpdrf); if (status) break; if (slowsw > 0 && partno < nparts) { if (verbosw) { printf ("pausing %d seconds before sending part %d...\n", slowsw, partno + 1); (void) fflush (stdout); } sleep ((unsigned) slowsw); } } } (void) fclose (fp); (void) unlink (tmpfil); done (status ? 1 : 0); } /* */ static int via_post (file, queued) char *file; int queued; { 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, queued ? "-queued" : NULLCP, 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 */ }