X-Git-Url: https://diplodocus.org/git/nmh/blobdiff_plain/2f689a1cb907a5de04e6d39ffd217a69af3216c7..d711510305:/uip/post.c?ds=inline diff --git a/uip/post.c b/uip/post.c index ba898a4c..81bd7b4a 100644 --- a/uip/post.c +++ b/uip/post.c @@ -1,8 +1,4 @@ - -/* - * post.c -- enter messages into the mail transport system - * - * $Id$ +/* post.c -- enter messages into the mail transport system * * This code is Copyright (c) 2002, by the authors of nmh. See the * COPYRIGHT file in the root directory of the nmh distribution for @@ -17,28 +13,17 @@ #include #include #include - #include #include +#include "../sbr/m_mktemp.h" +#include "../sbr/message_id.h" -#include -#include -#include - -#ifdef TIME_WITH_SYS_TIME +#ifdef HAVE_SYS_TIME_H # include -# include -#else -# ifdef TM_IN_SYS_TIME -# include -# else -# include -# endif #endif +#include -#ifdef SMTPMTS -# include -#endif +#include #ifndef CYRUS_SASL # define SASLminc(a) (a) @@ -46,9 +31,19 @@ # define SASLminc(a) 0 #endif /* CYRUS_SASL */ -#define FCCS 10 /* max number of fccs allowed */ +#ifndef TLS_SUPPORT +# define TLSminc(a) (a) +#else /* TLS_SUPPORT */ +# define TLSminc(a) 0 +#endif /* TLS_SUPPORT */ -#define uptolow(c) ((isalpha(c) && isupper (c)) ? tolower (c) : c) +#ifndef OAUTH_SUPPORT +# define OAUTHminc(a) (a) +#else /* OAUTH_SUPPORT */ +# define OAUTHminc(a) 0 +#endif /* OAUTH_SUPPORT */ + +#define FCCS 10 /* max number of fccs allowed */ /* In the following array of structures, the numeric second field of the structures (minchars) is apparently used like this: @@ -57,93 +52,90 @@ 0 : Switch can't be abbreviated; switch shown in -help. # : Switch can be abbreviated to # characters; switch shown in -help. */ -static struct swit switches[] = { -#define ALIASW 0 - { "alias aliasfile", 0 }, -#define CHKSW 1 - { "check", -5 }, /* interface from whom */ -#define NCHKSW 2 - { "nocheck", -7 }, /* interface from whom */ -#define DEBUGSW 3 - { "debug", -5 }, -#define DISTSW 4 - { "dist", -4 }, /* interface from dist */ -#define FILTSW 5 - { "filter filterfile", 0 }, -#define NFILTSW 6 - { "nofilter", 0 }, -#define FRMTSW 7 - { "format", 0 }, -#define NFRMTSW 8 - { "noformat", 0 }, -#define LIBSW 9 - { "library directory", -7 }, /* interface from send, whom */ -#define MIMESW 10 - { "mime", 0 }, -#define NMIMESW 11 - { "nomime", 0 }, -#define MSGDSW 12 - { "msgid", 0 }, -#define NMSGDSW 13 - { "nomsgid", 0 }, -#define VERBSW 14 - { "verbose", 0 }, -#define NVERBSW 15 - { "noverbose", 0 }, -#define WATCSW 16 - { "watch", 0 }, -#define NWATCSW 17 - { "nowatch", 0 }, -#define WHOMSW 18 - { "whom", -4 }, /* interface from whom */ -#define WIDTHSW 19 - { "width columns", 0 }, -#define VERSIONSW 20 - { "version", 0 }, -#define HELPSW 21 - { "help", 0 }, -#define BITSTUFFSW 22 - { "dashstuffing", -12 }, /* should we dashstuff BCC messages? */ -#define NBITSTUFFSW 23 - { "nodashstuffing", -14 }, -#define MAILSW 24 - { "mail", -4 }, /* specify MAIL smtp mode */ -#define SAMLSW 25 - { "saml", -4 }, /* specify SAML smtp mode */ -#define SENDSW 26 - { "send", -4 }, /* specify SEND smtp mode */ -#define SOMLSW 27 - { "soml", -4 }, /* specify SOML smtp mode */ -#define ANNOSW 28 - { "idanno number", -6 }, /* interface from send */ -#define DLVRSW 29 - { "deliver address-list", -7 }, -#define CLIESW 30 - { "client host", -6 }, -#define SERVSW 31 - { "server host", -6 }, /* specify alternate SMTP server */ -#define SNOOPSW 32 - { "snoop", -5 }, /* snoop the SMTP transaction */ -#define FILLSW 33 - { "fill-in file", -7 }, -#define FILLUSW 34 - { "fill-up", -7 }, -#define PARTSW 35 - { "partno", -6 }, -#define QUEUESW 36 - { "queued", -6 }, -#define SASLSW 37 - { "sasl", SASLminc(-4) }, -#define SASLMECHSW 38 - { "saslmech", SASLminc(-5) }, -#define USERSW 39 - { "user", SASLminc(-4) }, -#define PORTSW 40 - { "port server port name/number", 4 }, - { NULL, 0 } -}; +#define POST_SWITCHES \ + X("alias aliasfile", 0, ALIASW) \ + X("check", -5, CHKSW) /* interface from whom */ \ + X("nocheck", -7, NCHKSW) /* interface from whom */ \ + X("debug", -5, DEBUGSW) \ + X("dist", -4, DISTSW) /* interface from dist */ \ + X("filter filterfile", 0, FILTSW) \ + X("nofilter", 0, NFILTSW) \ + X("format", 0, FRMTSW) \ + X("noformat", 0, NFRMTSW) \ + X("library directory", -7, LIBSW) /* interface from send, whom */ \ + X("mime", 0, MIMESW) \ + X("nomime", 0, NMIMESW) \ + X("msgid", 0, MSGDSW) \ + X("nomsgid", 0, NMSGDSW) \ + X("verbose", 0, VERBSW) \ + X("noverbose", 0, NVERBSW) \ + X("watch", 0, WATCSW) \ + X("nowatch", 0, NWATCSW) \ + X("whom", -4, WHOMSW) /* interface from whom */ \ + X("width columns", 0, WIDTHSW) \ + X("version", 0, VERSIONSW) \ + X("help", 0, HELPSW) \ + X("dashstuffing", -12, BITSTUFFSW) /* should we dashstuff BCC messages? */ \ + X("nodashstuffing", -14, NBITSTUFFSW) \ + X("idanno number", -6, ANNOSW) /* interface from send */ \ + X("client host", -6, CLIESW) \ + X("server host", 6, SERVSW) /* specify alternate SMTP server */ \ + X("snoop", -5, SNOOPSW) /* snoop the SMTP transaction */ \ + X("partno", -6, PARTSW) \ + X("sasl", SASLminc(4), SASLSW) \ + X("nosasl", SASLminc(6), NOSASLSW) \ + X("saslmech", SASLminc(5), SASLMECHSW) \ + X("user", SASLminc(-4), USERSW) \ + X("port server submission port name/number", 4, PORTSW) \ + X("tls", TLSminc(-3), TLSSW) \ + X("initialtls", TLSminc(-10), INITTLSSW) \ + X("notls", TLSminc(-5), NTLSSW) \ + X("certverify", TLSminc(-10), CERTVERSW) \ + X("nocertverify", TLSminc(-12), NOCERTVERSW) \ + X("fileproc", -4, FILEPROCSW) \ + X("mhlproc", -3, MHLPROCSW) \ + X("sendmail program", 0, MTSSM) \ + X("mts smtp|sendmail/smtp|sendmail/pipe", 2, MTSSW) \ + X("credentials legacy|file:filename", 0, CREDENTIALSSW) \ + X("messageid localname|random", 2, MESSAGEIDSW) \ + X("authservice auth-service-name", OAUTHminc(-11), AUTHSERVICESW) \ + X("oauthcredfile credential-file", OAUTHminc(-7), OAUTHCREDFILESW) \ + X("oauthclientid client-id", OAUTHminc(-12), OAUTHCLIDSW) \ + X("oauthclientsecret client-secret", OAUTHminc(-12), OAUTHCLSECSW) \ + X("oauthauthendpoint authentication-endpoint", OAUTHminc(-6), OAUTHAUTHENDSW) \ + X("oauthredirect redirect-uri", OAUTHminc(-6), OAUTHREDIRSW) \ + X("oauthtokenendpoint token-endpoint", OAUTHminc(-6), OAUTHTOKENDSW) \ + X("oauthscope scope", OAUTHminc(-6), OAUTHSCOPESW) \ + +#define X(sw, minchars, id) id, +DEFINE_SWITCH_ENUM(POST); +#undef X + +#define X(sw, minchars, id) { sw, minchars, id }, +DEFINE_SWITCH_ARRAY(POST, switches); +#undef X +/* + * Mapping between command-line switches and profile entries, communicated + * from 'send'. We use a service name of 'post' internally. + */ + +static struct { + const char *profname; + int switchnum; + const char *value; +} oauthswitches[] = { + { "oauth-%s-credential-file", OAUTHCREDFILESW, NULL }, + { "oauth-%s-client_id", OAUTHCLIDSW, NULL }, + { "oauth-%s-client_secret", OAUTHCLSECSW, NULL }, + { "oauth-%s-auth_endpoint", OAUTHAUTHENDSW, NULL }, + { "oauth-%s-redirect_uri", OAUTHREDIRSW, NULL }, + { "oauth-%s-token_endpoint", OAUTHTOKENDSW, NULL }, + { "oauth-%s-scope", OAUTHSCOPESW, NULL }, + { NULL, 0, NULL } +}; + struct headers { char *value; unsigned int flags; @@ -153,50 +145,60 @@ struct headers { /* * flags for headers->flags */ -#define HNOP 0x0000 /* just used to keep .set around */ -#define HBAD 0x0001 /* bad header - don't let it through */ -#define HADR 0x0002 /* header has an address field */ -#define HSUB 0x0004 /* Subject: header */ -#define HTRY 0x0008 /* try to send to addrs on header */ -#define HBCC 0x0010 /* don't output this header */ -#define HMNG 0x0020 /* munge this header */ -#define HNGR 0x0040 /* no groups allowed in this header */ -#define HFCC 0x0080 /* FCC: type header */ -#define HNIL 0x0100 /* okay for this header not to have addrs */ -#define HIGN 0x0200 /* ignore this header */ -#define HDCC 0x0400 /* another undocumented feature */ +#define HNOP 0x0000 /* just used to keep .set around */ +#define HBAD 0x0001 /* bad header - don't let it through */ +#define HADR 0x0002 /* header has an address field */ +#define HSUB 0x0004 /* Subject: header */ +#define HTRY 0x0008 /* try to send to addrs on header */ +#define HBCC 0x0010 /* don't output this header, unless MTS_SENDMAIL_PIPE */ +#define HMNG 0x0020 /* munge this header */ +#define HNGR 0x0040 /* no groups allowed in this header */ +#define HFCC 0x0080 /* FCC: type header */ +#define HNIL 0x0100 /* okay for this header not to have addrs */ +#define HIGN 0x0200 /* ignore this header */ +#define HDCC 0x0400 /* another undocumented feature */ +#define HONE 0x0800 /* Only (zero or) one address allowed */ +#define HEFM 0x1000 /* Envelope-From: header */ +#define HMIM 0x2000 /* MIME-Version: header */ +#define HCTE 0x4000 /* Content-Transfer-Encoding: header */ /* * flags for headers->set */ -#define MFRM 0x0001 /* we've seen a From: */ -#define MDAT 0x0002 /* we've seen a Date: */ -#define MRFM 0x0004 /* we've seen a Resent-From: */ -#define MVIS 0x0008 /* we've seen sighted addrs */ -#define MINV 0x0010 /* we've seen blind addrs */ - +#define MFRM 0x0001 /* we've seen a From: */ +#define MDAT 0x0002 /* we've seen a Date: */ +#define MRFM 0x0004 /* we've seen a Resent-From: */ +#define MVIS 0x0008 /* we've seen sighted addrs */ +#define MINV 0x0010 /* we've seen blind addrs */ +#define MSND 0x0020 /* we've seen a Sender: */ +#define MRSN 0x0040 /* We've seen a Resent-Sender: */ +#define MEFM 0x0080 /* We've seen Envelope-From: */ +#define MMIM 0x0100 /* We've seen Mime-Version: */ static struct headers NHeaders[] = { - { "Return-Path", HBAD, 0 }, - { "Received", HBAD, 0 }, - { "Reply-To", HADR|HNGR, 0 }, - { "From", HADR|HNGR, MFRM }, - { "Sender", HADR|HBAD, 0 }, - { "Date", HBAD, 0 }, - { "Subject", HSUB, 0 }, - { "To", HADR|HTRY, MVIS }, - { "cc", HADR|HTRY, MVIS }, - { "Bcc", HADR|HTRY|HBCC|HNIL, MINV }, - { "Dcc", HADR|HTRY|HDCC|HNIL, MVIS }, /* sorta cc & bcc combined */ - { "Message-ID", HBAD, 0 }, - { "Fcc", HFCC, 0 }, - { NULL, 0, 0 } + { "Return-Path", HBAD, 0 }, + { "Received", HBAD, 0 }, + { "Reply-To", HADR|HNGR, 0 }, + { "From", HADR|HNGR, MFRM }, + { "Sender", HADR|HNGR|HONE, MSND }, + { "Date", HBAD, 0 }, + { "Subject", HSUB, 0 }, + { "To", HADR|HTRY, MVIS }, + { "cc", HADR|HTRY, MVIS }, + { "Bcc", HADR|HTRY|HBCC|HNIL, MINV }, + { "Dcc", HADR|HTRY|HDCC|HNIL, MVIS }, /* sorta cc & bcc combined */ + { "Message-ID", HBAD, 0 }, + { "Fcc", HFCC, 0 }, + { "Envelope-From", HADR|HONE|HEFM, MEFM }, + { "MIME-Version", HMIM, MMIM }, + { "Content-Transfer-Encoding", HCTE, 0 }, + { NULL, 0, 0 } }; static struct headers RHeaders[] = { { "Resent-Reply-To", HADR|HNGR, 0 }, { "Resent-From", HADR|HNGR, MRFM }, - { "Resent-Sender", HADR|HBAD, 0 }, + { "Resent-Sender", HADR|HNGR, MRSN }, { "Resent-Date", HBAD, 0 }, { "Resent-Subject", HSUB, 0 }, { "Resent-To", HADR|HTRY, MVIS }, @@ -206,12 +208,15 @@ static struct headers RHeaders[] = { { "Resent-Fcc", HFCC, 0 }, { "Reply-To", HADR, 0 }, { "From", HADR|HNGR, MFRM }, - { "Sender", HADR|HNGR, 0 }, + { "Sender", HADR|HNGR, MSND }, { "Date", HNOP, MDAT }, { "To", HADR|HNIL, 0 }, { "cc", HADR|HNIL, 0 }, { "Bcc", HADR|HTRY|HBCC|HNIL, 0 }, { "Fcc", HIGN, 0 }, + { "Envelope-From", HADR|HONE|HEFM, MEFM }, + { "MIME-Version", HMIM, MMIM }, + { "Content-Transfer-Encoding", HCTE, 0 }, { NULL, 0, 0 } }; @@ -219,8 +224,6 @@ static short fccind = 0; /* index into fccfold[] */ static short outputlinelen = OUTPUTLINELEN; static int pfd = NOTOK; /* fd to write annotation list to */ -static uid_t myuid= -1; /* my user id */ -static gid_t mygid= -1; /* my group id */ static int recipients = 0; /* how many people will get a copy */ static int unkadr = 0; /* how many of those were unknown */ static int badadr = 0; /* number of bad addrs */ @@ -238,7 +241,10 @@ static int nameoutput=0; /* putadr() has output header name */ static int sasl=0; /* Use SASL auth for SMTP */ static char *saslmech=NULL; /* Force use of particular SASL mech */ static char *user=NULL; /* Authenticate as this user */ -static char *port="smtp"; /* Name of server port for SMTP */ +static char *port="submission"; /* Name of server port for SMTP submission */ +static int tlsflag=0; /* Flags to control TLS settings */ +static int fromcount=0; /* Count of addresses on From: header */ +static int seensender=0; /* Have we seen a Sender: header? */ static unsigned msgflags = 0; /* what we've seen */ @@ -254,40 +260,34 @@ static char tmpfil[BUFSIZ]; static char bccfil[BUFSIZ]; static char from[BUFSIZ]; /* my network address */ -static char signature[BUFSIZ]; /* my signature */ +static char sender[BUFSIZ]; /* my Sender: header */ +static char efrom[BUFSIZ]; /* my Envelope-From: header */ +static char fullfrom[BUFSIZ]; /* full contents of From header */ static char *filter = NULL; /* the filter for BCC'ing */ static char *subject = NULL; /* the subject field for BCC'ing */ static char *fccfold[FCCS]; /* foldernames for FCC'ing */ +enum encoding { UNKNOWN = 0, BINARY = 1, SEVENBIT = 7, EIGHTBIT = 8 }; +static enum encoding cte = UNKNOWN; static struct headers *hdrtab; /* table for the message we're doing */ -static struct mailname localaddrs={NULL}; /* local addrs */ -static struct mailname netaddrs={NULL}; /* network addrs */ -static struct mailname uuaddrs={NULL}; /* uucp addrs */ -static struct mailname tmpaddrs={NULL}; /* temporary queue */ +static struct mailname localaddrs; /* local addrs */ +static struct mailname netaddrs; /* network addrs */ +static struct mailname uuaddrs; /* uucp addrs */ +static struct mailname tmpaddrs; /* temporary queue */ -#ifdef SMTPMTS static int snoop = 0; -static int smtpmode = S_MAIL; static char *clientsw = NULL; static char *serversw = NULL; -extern struct smtp sm_reply; -#endif /* SMTPMTS */ - static char prefix[] = "----- =_aaaaaaaaaa"; -static int fill_up = 0; -static char *fill_in = NULL; static char *partno = NULL; -static int queued = 0; - -extern boolean draft_from_masquerading; /* defined in mts.c */ /* * static prototypes */ -static void putfmt (char *, char *, FILE *); +static void putfmt (char *, char *, int *, FILE *); static void start_headers (void); static void finish_headers (FILE *); static int get_header (char *, struct headers *); @@ -297,16 +297,16 @@ static int insert (struct mailname *); static void pl (void); static void anno (void); static int annoaux (struct mailname *); -static void insert_fcc (struct headers *, unsigned char *); +static void insert_fcc (struct headers *, char *); static void make_bcc_file (int); -static void verify_all_addresses (int); +static void verify_all_addresses (int, int, char *, int, char *); static void chkadr (void); static void sigon (void); static void sigoff (void); static void p_refile (char *); static void fcc (char *, char *); static void die (char *, char *, ...); -static void post (char *, int, int); +static void post (char *, int, int, int, char *, int, char *); static void do_text (char *file, int fd); static void do_an_address (struct mailname *, int); static void do_addresses (int, int); @@ -316,27 +316,23 @@ static int find_prefix (void); int main (int argc, char **argv) { - int state, compnum, dashstuff = 0; - char *cp, *msg = NULL, **argp, **arguments; - char buf[BUFSIZ], name[NAMESZ]; + int state, compnum, dashstuff = 0, swnum, oauth_flag = 0, tls = -1; + int noverify = 0; + int eai = 0; /* use Email Address Internationalization (EAI) (SMTPUTF8) */ + char *cp, *msg = NULL, **argp, **arguments, *envelope; + char buf[NMH_BUFSIZ], name[NAMESZ], *auth_svc = NULL; FILE *in, *out; + m_getfld_state_t gstate = 0; -#ifdef LOCALE - setlocale(LC_ALL, ""); -#endif - invo_name = r1bindex (argv[0], '/'); - - /* foil search of user profile/context */ - if (context_foil (NULL) == -1) - done (1); + if (nmh_init(argv[0], 0 /* use context_foil() */)) { return 1; } - mts_init (invo_name); + mts_init (); arguments = getarguments (invo_name, argc, argv, 0); argp = arguments; while ((cp = *argp++)) { if (*cp == '-') { - switch (smatch (++cp, switches)) { + switch ((swnum = smatch (++cp, switches))) { case AMBIGSW: ambigsw (cp, switches); done (1); @@ -346,10 +342,10 @@ main (int argc, char **argv) case HELPSW: snprintf (buf, sizeof(buf), "%s [switches] file", invo_name); print_help (buf, switches, 0); - done (1); + done (0); case VERSIONSW: print_version(invo_name); - done (1); + done (0); case LIBSW: if (!(cp = *argp++) || *cp == '-') @@ -452,33 +448,6 @@ main (int argc, char **argv) adios (NULL, "bad argument %s %s", argp[-2], cp); continue; - case DLVRSW: - if (!(cp = *argp++) || *cp == '-') - adios (NULL, "missing argument to %s", argp[-2]); - continue; - -#ifndef SMTPMTS - case CLIESW: - case SERVSW: - if (!(cp = *argp++) || *cp == '-') - adios (NULL, "missing argument to %s", argp[-2]); - continue; - - case SNOOPSW: - continue; -#else /* SMTPMTS */ - case MAILSW: - smtpmode = S_MAIL; - continue; - case SAMLSW: - smtpmode = S_SAML; - continue; - case SOMLSW: - smtpmode = S_SOML; - continue; - case SENDSW: - smtpmode = S_SEND; - continue; case CLIESW: if (!(clientsw = *argp++) || *clientsw == '-') adios (NULL, "missing argument to %s", argp[-2]); @@ -490,33 +459,59 @@ main (int argc, char **argv) case SNOOPSW: snoop++; continue; -#endif /* SMTPMTS */ - case FILLSW: - if (!(fill_in = *argp++) || *fill_in == '-') - adios (NULL, "missing argument to %s", argp[-2]); - continue; - case FILLUSW: - fill_up++; - continue; case PARTSW: if (!(partno = *argp++) || *partno == '-') adios (NULL, "missing argument to %s", argp[-2]); continue; - case QUEUESW: - queued++; - continue; - case SASLSW: sasl++; continue; - + + case NOSASLSW: + sasl = 0; + continue; + case SASLMECHSW: if (!(saslmech = *argp++) || *saslmech == '-') adios (NULL, "missing argument to %s", argp[-2]); continue; - + + case AUTHSERVICESW: + if (!(auth_svc = *argp++) || *auth_svc == '-') + adios (NULL, "missing argument to %s", argp[-2]); + oauth_flag++; + continue; + + case OAUTHCREDFILESW: + case OAUTHCLIDSW: + case OAUTHCLSECSW: + case OAUTHAUTHENDSW: + case OAUTHREDIRSW: + case OAUTHTOKENDSW: + case OAUTHSCOPESW: + { + int i; + + if (!(cp = *argp++) || *cp == '-') + adios (NULL, "missing argument to %s", argp[-2]); + + for (i = 0; oauthswitches[i].profname != NULL; i++) { + if (oauthswitches[i].switchnum == swnum) { + oauthswitches[i].value = cp; + break; + } + } + + if (oauthswitches[i].profname == NULL) + adios (NULL, "internal error: cannot map switch %s " + "to profile entry", argp[-2]); + + oauth_flag++; + continue; + } + case USERSW: if (!(user = *argp++) || *user == '-') adios (NULL, "missing argument to %s", argp[-2]); @@ -526,6 +521,64 @@ main (int argc, char **argv) if (!(port = *argp++) || *port == '-') adios (NULL, "missing argument to %s", argp[-2]); continue; + + case TLSSW: + tls = 1; + continue; + + case INITTLSSW: + tls = 2; + continue; + + case NTLSSW: + tls = 0; + continue; + + case CERTVERSW: + noverify = 0; + continue; + + case NOCERTVERSW: + noverify++; + continue; + + case FILEPROCSW: + if (!(cp = *argp++) || *cp == '-') + adios (NULL, "missing argument to %s", argp[-2]); + fileproc = cp; + continue; + + case MHLPROCSW: + if (!(cp = *argp++) || *cp == '-') + adios (NULL, "missing argument to %s", argp[-2]); + mhlproc = cp; + continue; + + case MTSSM: + if (!(cp = *argp++) || *cp == '-') + adios (NULL, "missing argument to %s", argp[-2]); + sendmail = cp; + continue; + + case MTSSW: + if (!(cp = *argp++) || *cp == '-') + adios (NULL, "missing argument to %s", argp[-2]); + save_mts_method (cp); + continue; + + case CREDENTIALSSW: { + if (!(cp = *argp++) || *cp == '-') + adios (NULL, "missing argument to %s", argp[-2]); + add_profile_entry ("credentials", cp); + continue; + } + + case MESSAGEIDSW: + if (!(cp = *argp++) || *cp == '-') + adios (NULL, "missing argument to %s", argp[-2]); + if (save_message_id_style (cp) != 0) + adios (NULL, "unsupported messageid \"%s\"", cp); + continue; } } if (msg) @@ -548,52 +601,47 @@ main (int argc, char **argv) start_headers (); if (debug) { verbose++; - discard (out = stdout); /* XXX: reference discard() to help loader */ + out = stdout; } else { if (whomsw) { - if ((out = fopen (fill_in ? fill_in : "/dev/null", "w")) == NULL) + if ((out = fopen ("/dev/null", "w")) == NULL) adios ("/dev/null", "unable to open"); } else { - char *cp = m_mktemp(m_maildir(invo_name), NULL, &out); - if (cp == NULL) { - cp = m_mktemp2(NULL, invo_name, NULL, &out); - if (cp == NULL) { - adios ("post", "unable to create temporary file"); - } - } + char *cp = m_mktemp2(NULL, invo_name, NULL, &out); + if (cp == NULL) { + adios(NULL, "unable to create temporary file in %s", + get_temp_dir()); + } strncpy(tmpfil, cp, sizeof(tmpfil)); - chmod (tmpfil, 0600); } } hdrtab = msgstate == NORMAL ? NHeaders : RHeaders; - for (compnum = 1, state = FLD;;) { - switch (state = m_getfld (state, name, buf, sizeof(buf), in)) { + for (compnum = 1;;) { + int bufsz = sizeof buf; + switch (state = m_getfld (&gstate, name, buf, &bufsz, in)) { case FLD: - case FLDEOF: case FLDPLUS: - compnum++; - cp = add (buf, NULL); + compnum++; + cp = mh_xstrdup(buf); while (state == FLDPLUS) { - state = m_getfld (state, name, buf, sizeof(buf), in); + bufsz = sizeof buf; + state = m_getfld (&gstate, name, buf, &bufsz, in); cp = add (buf, cp); } - putfmt (name, cp, out); + putfmt (name, cp, &eai, out); free (cp); - if (state != FLDEOF) - continue; - finish_headers (out); - break; + continue; case BODY: - case BODYEOF: finish_headers (out); - if (whomsw && !fill_in) + if (whomsw) break; fprintf (out, "\n%s", buf); while (state == BODY) { - state = m_getfld (state, name, buf, sizeof(buf), in); + bufsz = sizeof buf; + state = m_getfld (&gstate, name, buf, &bufsz, in); fputs (buf, out); } break; @@ -611,6 +659,7 @@ main (int argc, char **argv) } break; } + m_getfld_state_destroy (&gstate); if (pfd != NOTOK) anno (); @@ -623,31 +672,109 @@ main (int argc, char **argv) fclose (out); } + /* + * Here's how we decide which address to use as the envelope-from + * address for SMTP. + * + * - If we were given an Envelope-From header, use that. + * - If we were given a Sender: address, use that. + * - Otherwise, use the address on the From: line + */ + + if (msgflags & MEFM) { + envelope = efrom; + } else if (seensender) { + envelope = sender; + } else { + envelope = from; + } + + if (tls == -1) { +#ifdef TLS_SUPPORT + /* + * The user didn't specify any of the tls switches. Try to + * help them by implying -initialtls if they're using port 465 + * (smtps, until IANA revoked that registration in 1998). + */ + tls = ! strcmp (port, "465") || ! strcasecmp (port, "smtps") + ? 2 + : 0; +#else /* ! TLS_SUPPORT */ + tls = 0; +#endif /* ! TLS_SUPPORT */ + } + + if (tls == 1) + tlsflag = S_STARTTLS; + else if (tls == 2) + tlsflag = S_INITTLS; + else + tlsflag = 0; + + if (noverify) + tlsflag |= S_NOVERIFY; + + /* + * If we were given any oauth flags, store the appropriate profile + * entries and make sure an authservice was given (we have to do this + * here because we aren't guaranteed the authservice will be given on + * the command line before the other OAuth flags are given). + */ + + if (oauth_flag) { + int i; + char sbuf[128]; + + if (auth_svc == NULL) { + adios(NULL, "No authentication service given with -authservice"); + } + + for (i = 0; oauthswitches[i].profname != NULL; i++) { + if (oauthswitches[i].value != NULL) { + snprintf(sbuf, sizeof(sbuf), + oauthswitches[i].profname, auth_svc); + sbuf[sizeof(sbuf) - 1] = '\0'; + add_profile_entry(sbuf, oauthswitches[i].value); + } + } + } + /* If we are doing a "whom" check */ if (whomsw) { - if (!fill_up) - verify_all_addresses (1); + /* This won't work with MTS_SENDMAIL_PIPE. */ + verify_all_addresses (1, eai, envelope, oauth_flag, auth_svc); done (0); } if (msgflags & MINV) { make_bcc_file (dashstuff); if (msgflags & MVIS) { - verify_all_addresses (verbose); - post (tmpfil, 0, verbose); + if (sm_mts != MTS_SENDMAIL_PIPE) { + /* It would be nice to have support to call + verify_all_addresses with MTS_SENDMAIL_PIPE, but + that might require running sendmail as root. Note + that spost didn't verify addresses. */ + verify_all_addresses (verbose, eai, envelope, oauth_flag, + auth_svc); + } + post (tmpfil, 0, verbose, eai, envelope, oauth_flag, auth_svc); } - post (bccfil, 1, verbose); - unlink (bccfil); + post (bccfil, 1, verbose, eai, envelope, oauth_flag, auth_svc); + (void) m_unlink (bccfil); } else { - post (tmpfil, 0, isatty (1)); + post (tmpfil, 0, isatty (1), eai, envelope, oauth_flag, auth_svc); } p_refile (tmpfil); - unlink (tmpfil); + (void) m_unlink (tmpfil); + + if (verbose) { + if (partno) + printf ("Partial Message #%s Processed\n", partno); + else + puts("Message Processed"); + } - if (verbose) - printf (partno ? "Partial Message #%s Processed\n" : "Message Processed\n", - partno); done (0); return 1; } @@ -658,11 +785,11 @@ main (int argc, char **argv) */ static void -putfmt (char *name, char *str, FILE *out) +putfmt (char *name, char *str, int *eai, FILE *out) { int count, grp, i, keep; char *cp, *pp, *qp; - char namep[BUFSIZ]; + char namep[BUFSIZ], error[BUFSIZ]; struct mailname *mp = NULL, *np = NULL; struct headers *hdr; @@ -670,86 +797,130 @@ putfmt (char *name, char *str, FILE *out) str++; if (msgstate == NORMAL && uprf (name, "resent")) { - advise (NULL, "illegal header line -- %s:", name); + inform("illegal header line -- %s:", name); badmsg++; return; } + if (! *eai) { + /* Check each header field value to see if it has any 8-bit characters. + If it does, enable EAI support. */ + if (contains8bit(str, NULL)) { + if (verbose) { + puts("EAI/SMTPUTF8 enabled"); + } + + /* Enable SMTPUTF8. */ + *eai = 1; + + /* Enable passing of utf-8 setting to getname()/getadrx(). */ + enable_eai(); + } + } + if ((i = get_header (name, hdrtab)) == NOTOK) { - fprintf (out, "%s: %s", name, str); + if (strncasecmp (name, "nmh-", 4)) { + fprintf (out, "%s: %s", name, str); + } else { + /* Filter out all Nmh-* headers, because Norm asked. They + should never have reached this point. Warn about any + that are non-empty. */ + if (strcmp (str, "\n")) { + trim_suffix_c(str, '\n'); + if (! whomsw) { + inform("ignoring header line -- %s: %s", name, str); + } + } + } + return; } hdr = &hdrtab[i]; if (hdr->flags & HIGN) { - if (fill_in) - fprintf (out, "%s: %s", name, str); return; } if (hdr->flags & HBAD) { - if (fill_in) - fprintf (out, "%s: %s", name, str); - else { - advise (NULL, "illegal header line -- %s:", name); - badmsg++; - } + inform("illegal header line -- %s:", name); + badmsg++; return; } msgflags |= (hdr->set & ~(MVIS | MINV)); if (hdr->flags & HSUB) - subject = subject ? add (str, add ("\t", subject)) : getcpy (str); + subject = subject ? add (str, add ("\t", subject)) : mh_xstrdup(str); if (hdr->flags & HFCC) { - if (fill_in) { - fprintf (out, "%s: %s", name, str); - return; - } - if ((cp = strrchr(str, '\n'))) *cp = 0; - for (cp = pp = str; (cp = strchr(pp, ',')); pp = cp) { + for (pp = str; (cp = strchr(pp, ',')); pp = cp) { *cp++ = 0; insert_fcc (hdr, pp); } insert_fcc (hdr, pp); return; } - + if (hdr->flags & HCTE) { + if (strncasecmp (str, "7bit", 4) == 0) { + cte = SEVENBIT; + } else if (strncasecmp (str, "8bit", 4) == 0) { + cte = EIGHTBIT; + } else if (strncasecmp (str, "binary", 6) == 0) { + cte = BINARY; + } + } if (!(hdr->flags & HADR)) { fprintf (out, "%s: %s", name, str); return; } tmpaddrs.m_next = NULL; - for (count = 0; (cp = getname (str)); count++) - if ((mp = getm (cp, NULL, 0, AD_HOST, NULL))) { + + for (count = 0; (cp = getname (str)); count++) { + if ((mp = getm (cp, NULL, 0, error, sizeof(error)))) { if (tmpaddrs.m_next) np->m_next = mp; else tmpaddrs.m_next = mp; np = mp; } - else + else { + admonish(cp, "%s", error); if (hdr->flags & HTRY) badadr++; else badmsg++; + } + } if (count < 1) { if (hdr->flags & HNIL) fprintf (out, "%s: %s", name, str); else { + /* + * Sender (or Resent-Sender) can have only one address + */ + if ((msgstate == RESENT) ? (hdr->set & MRSN) + : (hdr->set & MSND)) { + inform("%s: field requires one address", name); + badmsg++; + } #ifdef notdef - advise (NULL, "%s: field requires at least one address", name); + inform("%s: field requires at least one address", name); badmsg++; #endif /* notdef */ } return; } + if (count > 1 && (hdr->flags & HONE)) { + inform("%s: field only permits one address", name); + badmsg++; + return; + } + nameoutput = linepos = 0; snprintf (namep, sizeof(namep), "%s%s", - !fill_in && (hdr->flags & HMNG) ? "Original-" : "", name); + (hdr->flags & HMNG) ? "Original-" : "", name); for (grp = 0, mp = tmpaddrs.m_next; mp; mp = np) if (mp->m_nohost) { /* also used to test (hdr->flags & HTRY) */ @@ -760,22 +931,43 @@ putfmt (char *name, char *str, FILE *out) if (np->m_gname) putgrp (namep, np->m_gname, out, hdr->flags); while ((cp = getname (pp))) { - if (!(mp = getm (cp, NULL, 0, AD_HOST, NULL))) { + if (!(mp = getm (cp, NULL, 0, error, sizeof(error)))) { + admonish(cp, "%s", error); badadr++; continue; } - if (draft_from_masquerading && ((msgstate == RESENT) - ? (hdr->set & MRFM) - : (hdr->set & MFRM))) - /* The user manually specified a [Resent-]From: address in - their draft and the "masquerade:" line in mts.conf - doesn't contain "draft_from", so we'll set things up to - use the actual email address embedded in the draft - [Resent-]From: (after alias substitution, and without the - GECOS full name or angle brackets) as the envelope - From:. */ + /* + * If it's a From: or Resent-From: header, save the address + * for later possible use (as the envelope address for SMTP) + */ + + if ((msgstate == RESENT) ? (hdr->set & MRFM) + : (hdr->set & MFRM)) { strncpy(from, auxformat(mp, 0), sizeof(from) - 1); + from[sizeof(from) - 1] = '\0'; + fromcount = count; + } + + /* + * Also save the Sender: or Resent-Sender: header as well + */ + + if ((msgstate == RESENT) ? (hdr->set & MRSN) + : (hdr->set & MSND)) { + strncpy(sender, auxformat(mp, 0), sizeof(sender) - 1); + sender[sizeof(sender) - 1] = '\0'; + seensender++; + } + + /* + * ALSO ... save Envelope-From + */ + + if (hdr->set & MEFM) { + strncpy(efrom, auxformat(mp, 0), sizeof(efrom) - 1); + efrom[sizeof(efrom) - 1] = '\0'; + } if (hdr->flags & HBCC) mp->m_bcc++; @@ -784,8 +976,22 @@ putfmt (char *name, char *str, FILE *out) else if (mp->m_gname) putgrp (namep, mp->m_gname, out, hdr->flags); - if (mp->m_ingrp) + if (mp->m_ingrp) { + if (sm_mts == MTS_SENDMAIL_PIPE) { + /* Catch this before sendmail chokes with: + "553 List:; syntax illegal for recipient + addresses". + If we wanted to, we could expand out blind + aliases and put them in Bcc:, but then + they'd have the Blind-Carbon-Copy + indication. */ + adios (NULL, + "blind lists not compatible with" + " sendmail/pipe"); + } + grp++; + } if (putadr (namep, qp, mp, out, hdr->flags)) msgflags |= (hdr->set & (MVIS | MINV)); else @@ -797,17 +1003,37 @@ putfmt (char *name, char *str, FILE *out) } else { /* Address includes a host, so no alias substitution is needed. */ - if (draft_from_masquerading && ((msgstate == RESENT) - ? (hdr->set & MRFM) - : (hdr->set & MFRM))) - /* The user manually specified a [Resent-]From: address in - their draft and the "masquerade:" line in mts.conf - doesn't contain "draft_from", so we'll set things up to - use the actual email address embedded in the draft - [Resent-]From: (after alias substitution, and without the - GECOS full name or angle brackets) as the envelope - From:. */ + + /* + * If it's a From: or Resent-From header, save the address + * for later possible use (as the envelope address for SMTP) + */ + + if ((msgstate == RESENT) ? (hdr->set & MRFM) + : (hdr->set & MFRM)) { strncpy(from, auxformat(mp, 0), sizeof(from) - 1); + fromcount = count; + } + + /* + * Also save the Sender: header as well + */ + + if ((msgstate == RESENT) ? (hdr->set & MRSN) + : (hdr->set & MSND)) { + strncpy(sender, auxformat(mp, 0), sizeof(sender) - 1); + sender[sizeof(sender) - 1] = '\0'; + seensender++; + } + + /* + * ALSO ... save Envelope-From + */ + + if (hdr->set & MEFM) { + strncpy(efrom, auxformat(mp, 0), sizeof(efrom) - 1); + efrom[sizeof(efrom) - 1] = '\0'; + } if (hdr->flags & HBCC) mp->m_bcc++; @@ -825,13 +1051,28 @@ putfmt (char *name, char *str, FILE *out) mnfree (mp); } + /* + * If this is a From:/Resent-From: header, save the full thing for + * later in case we need it for use when constructing a Bcc draft message + */ + + if ((msgstate == RESENT) ? (hdr->set & MRFM) : (hdr->set & MFRM)) { + strncpy(fullfrom, str, sizeof(fullfrom)); + fullfrom[sizeof(fullfrom) - 1] = 0; + /* + * Strip off any trailing newlines + */ + + while (*fullfrom && fullfrom[strlen(fullfrom) - 1] == '\n') { + fullfrom[strlen(fullfrom) - 1] = '\0'; + } + } + if (grp > 0 && (hdr->flags & HNGR)) { - advise (NULL, "%s: field does not allow groups", name); + inform("%s: field does not allow groups", name); badmsg++; } if (linepos) { - if (fill_in && grp > 0) - putc (';', out); putc ('\n', out); } } @@ -840,34 +1081,16 @@ putfmt (char *name, char *str, FILE *out) static void start_headers (void) { - unsigned char *cp; - char myhost[BUFSIZ], sigbuf[BUFSIZ]; - struct mailname *mp; - - myuid = getuid (); - mygid = getgid (); time (&tclock); - strncpy (from, adrsprintf (NULL, NULL), sizeof(from)); - strncpy (myhost, LocalName (), sizeof(myhost)); - - for (cp = myhost; *cp; cp++) - *cp = uptolow (*cp); - - if ((cp = getfullname ()) && *cp) { - strncpy (sigbuf, cp, sizeof(sigbuf)); - snprintf (signature, sizeof(signature), "%s <%s>", - sigbuf, adrsprintf (NULL, NULL)); - if ((cp = getname (signature)) == NULL) - adios (NULL, "getname () failed -- you lose extraordinarily big"); - if ((mp = getm (cp, NULL, 0, AD_HOST, NULL)) == NULL) - adios (NULL, "bad signature '%s'", sigbuf); - mnfree (mp); - while (getname ("")) - continue; - } else { - strncpy (signature, adrsprintf (NULL, NULL), sizeof(signature)); - } + /* + * Probably not necessary, but just in case ... + */ + + from[0] = '\0'; + efrom[0] = '\0'; + sender[0] = '\0'; + fullfrom[0] = '\0'; } @@ -882,62 +1105,96 @@ finish_headers (FILE *out) { switch (msgstate) { case NORMAL: - if (whomsw && !fill_up) + if (!(msgflags & MFRM)) { + /* + * A From: header is now required in the draft. + */ + inform("message has no From: header"); + inform("See default components files for examples"); + badmsg++; break; + } - fprintf (out, "Date: %s\n", dtime (&tclock, 0)); - if (msgid) - fprintf (out, "Message-ID: <%d.%ld@%s>\n", - (int) getpid (), (long) tclock, LocalName ()); - if (msgflags & MFRM) { - /* There was already a From: in the draft. Don't add one. */ - if (!draft_from_masquerading) - /* mts.conf didn't contain "masquerade:[...]draft_from[...]" - so we'll reveal the user's actual account@thismachine - address in a Sender: header (and use it as the envelope - From: later). */ - fprintf (out, "Sender: %s\n", from); + if (fromcount > 1 && (seensender == 0 && !(msgflags & MEFM))) { + inform("A Sender: or Envelope-From: header is required " + "with multiple\nFrom: addresses"); + badmsg++; + break; } - else - /* Construct a From: header. */ - fprintf (out, "From: %s\n", signature); + if (whomsw) break; + fprintf (out, "Date: %s\n", dtime (&tclock, 0)); + if (msgid) + fprintf (out, "Message-ID: %s\n", message_id (tclock, 0)); + /* + * If we have multiple From: addresses, make sure we have an + * Sender: header. If we don't have one, then generate one + * from Envelope-From: (which in this case, cannot be blank) + */ + + if (fromcount > 1 && seensender == 0) { + if (efrom[0] == '\0') { + inform("Envelope-From cannot be blank when there " + "is multiple From: addresses\nand no Sender: " + "header"); + badmsg++; + } else { + fprintf (out, "Sender: %s\n", efrom); + } + } + if (!(msgflags & MVIS)) fprintf (out, "Bcc: Blind Distribution List: ;\n"); break; case RESENT: if (!(msgflags & MDAT)) { - advise (NULL, "message has no Date: header"); + inform("message has no Date: header"); badmsg++; } if (!(msgflags & MFRM)) { - advise (NULL, "message has no From: header"); + inform("message has no From: header"); + badmsg++; + } + if (!(msgflags & MRFM)) { + inform("message has no Resent-From: header"); + inform("See default components files for examples"); + badmsg++; + break; + } + if (fromcount > 1 && (seensender == 0 && !(msgflags & MEFM))) { + inform("A Resent-Sender: or Envelope-From: header is " + "required with multiple\nResent-From: addresses"); badmsg++; + break; } - if (whomsw && !fill_up) + + if (whomsw) break; fprintf (out, "Resent-Date: %s\n", dtime (&tclock, 0)); if (msgid) - fprintf (out, "Resent-Message-ID: <%d.%ld@%s>\n", - (int) getpid (), (long) tclock, LocalName ()); - if (msgflags & MRFM) { - /* There was already a Resent-From: in draft. Don't add one. */ - if (!draft_from_masquerading) - /* mts.conf didn't contain "masquerade:[...]draft_from[...]" - so we'll reveal the user's actual account@thismachine - address in a Sender: header (and use it as the envelope - From: later). */ - fprintf (out, "Resent-Sender: %s\n", from); + fprintf (out, "Resent-Message-ID: %s\n", + message_id (tclock, 0)); + /* + * If we have multiple Resent-From: addresses, make sure we have an + * Resent-Sender: header. If we don't have one, then generate one + * from Envelope-From (which in this case, cannot be blank) + */ + + if (fromcount > 1 && seensender == 0) { + if (efrom[0] == '\0') { + inform("Envelope-From cannot be blank when there " + "is multiple Resent-From: addresses and no " + "Resent-Sender: header"); + badmsg++; + } else { + fprintf (out, "Resent-Sender: %s\n", efrom); + } } - else - /* Construct a Resent-From: header. */ - fprintf (out, "Resent-From: %s\n", signature); - if (whomsw) - break; + if (!(msgflags & MVIS)) fprintf (out, "Resent-Bcc: Blind Re-Distribution List: ;\n"); break; @@ -956,7 +1213,7 @@ get_header (char *header, struct headers *table) struct headers *h; for (h = table; h->value; h++) - if (!mh_strcasecmp (header, h->value)) + if (!strcasecmp (FENDNULL(header), FENDNULL(h->value))) return (h - table); return NOTOK; @@ -972,7 +1229,8 @@ putadr (char *name, char *aka, struct mailname *mp, FILE *out, unsigned int flag if (mp->m_mbox == NULL || ((flags & HTRY) && !insert (mp))) return 0; - if ((!fill_in && (flags & (HBCC | HDCC))) || mp->m_ingrp) + if (sm_mts != MTS_SENDMAIL_PIPE && + ((flags & (HBCC | HDCC | HEFM)) || mp->m_ingrp)) return 1; if (!nameoutput) { @@ -981,9 +1239,9 @@ putadr (char *name, char *aka, struct mailname *mp, FILE *out, unsigned int flag } if (*aka && mp->m_type != UUCPHOST && !mp->m_pers) - mp->m_pers = getcpy (aka); + mp->m_pers = mh_xstrdup(aka); if (format) { - if (mp->m_gname && !fill_in) { + if (mp->m_gname) { snprintf (buffer, sizeof(buffer), "%s;", mp->m_gname); cp = buffer; } else { @@ -1016,17 +1274,15 @@ putgrp (char *name, char *group, FILE *out, unsigned int flags) int len; char *cp; - if (!fill_in && (flags & HBCC)) + if (sm_mts != MTS_SENDMAIL_PIPE && (flags & HBCC)) return; if (!nameoutput) { fprintf (out, "%s: ", name); linepos += (nameoutput = strlen (name) + 2); - if (fill_in) - linepos -= strlen (group); } - cp = fill_in ? group : concat (group, ";", NULL); + cp = concat (group, ";", NULL); len = strlen (cp); if (linepos > nameoutput) { @@ -1058,9 +1314,11 @@ insert (struct mailname *np) : &netaddrs; mp->m_next; mp = mp->m_next) - if (!mh_strcasecmp (np->m_host, mp->m_next->m_host) - && !mh_strcasecmp (np->m_mbox, mp->m_next->m_mbox) - && np->m_bcc == mp->m_next->m_bcc) + if (!strcasecmp (FENDNULL(np->m_host), + FENDNULL(mp->m_next->m_host)) && + !strcasecmp (FENDNULL(np->m_mbox), + FENDNULL(mp->m_next->m_mbox)) && + np->m_bcc == mp->m_next->m_bcc) return 0; mp->m_next = np; @@ -1083,7 +1341,7 @@ pl (void) printf ("\nnet:\t"); for (mp = netaddrs.m_next; mp; mp = mp->m_next) - printf ("%s%s@%s%s%s", mp->m_path ? mp->m_path : "", + printf ("%s%s@%s%s%s", FENDNULL(mp->m_path), mp->m_mbox, mp->m_host, mp->m_bcc ? "[BCC]" : "", mp->m_next ? ",\n\t" : ""); @@ -1097,7 +1355,7 @@ pl (void) printf ("\n\t-- Folder Copies --\nfcc:\t"); for (i = 0; i < fccind; i++) printf ("%s%s", fccfold[i], i + 1 < fccind ? ",\n\t" : ""); - printf ("\n"); + putchar('\n'); } @@ -1138,13 +1396,13 @@ annoaux (struct mailname *mp) static void -insert_fcc (struct headers *hdr, unsigned char *pp) +insert_fcc (struct headers *hdr, char *pp) { - unsigned char *cp; + char *cp; - for (cp = pp; isspace (*cp); cp++) + for (cp = pp; isspace ((unsigned char) *cp); cp++) continue; - for (pp += strlen (pp) - 1; pp > cp && isspace (*pp); pp--) + for (pp += strlen (pp) - 1; pp > cp && isspace ((unsigned char) *pp); pp--) continue; if (pp >= cp) *++pp = 0; @@ -1153,7 +1411,7 @@ insert_fcc (struct headers *hdr, unsigned char *pp) if (fccind >= FCCS) adios (NULL, "too many %ss", hdr->value); - fccfold[fccind++] = getcpy (cp); + fccfold[fccind++] = mh_xstrdup(cp); } /* @@ -1165,31 +1423,19 @@ make_bcc_file (int dashstuff) { int fd, i; pid_t child_id; - char *vec[6]; + char **vec; FILE *out; - char *tfile = NULL; + char *tfile = NULL, *program; - tfile = m_mktemp2(NULL, "bccs", NULL, &out); - if (tfile == NULL) adios("bcc", "unable to create temporary file"); - chmod (bccfil, 0600); + if ((tfile = m_mktemp2(NULL, "bccs", NULL, &out)) == NULL) { + adios(NULL, "unable to create temporary file in %s", get_temp_dir()); + } strncpy (bccfil, tfile, sizeof(bccfil)); + fprintf (out, "From: %s\n", fullfrom); fprintf (out, "Date: %s\n", dtime (&tclock, 0)); if (msgid) - fprintf (out, "Message-ID: <%d.%ld@%s>\n", - (int) getpid (), (long) tclock, LocalName ()); - if (msgflags & MFRM) { - /* There was already a From: in the draft. Don't add one. */ - if (!draft_from_masquerading) - /* mts.conf didn't contain "masquerade:[...]draft_from[...]" - so we'll reveal the user's actual account@thismachine - address in a Sender: header (and use it as the envelope - From: later). */ - fprintf (out, "Sender: %s\n", from); - } - else - /* Construct a From: header. */ - fprintf (out, "From: %s\n", signature); + fprintf (out, "Message-ID: %s\n", message_id (tclock, 0)); if (subject) fprintf (out, "Subject: %s", subject); fprintf (out, "BCC:\n"); @@ -1232,8 +1478,6 @@ make_bcc_file (int dashstuff) * of MIME encapsulation. */ if (filter != NULL) { - vec[0] = r1bindex (mhlproc, '/'); - for (i = 0; (child_id = fork()) == NOTOK && i < 5; i++) sleep (5); switch (child_id) { @@ -1243,7 +1487,7 @@ make_bcc_file (int dashstuff) case OK: dup2 (fileno (out), 1); - i = 1; + vec = argsplit(mhlproc, &program, &i); vec[i++] = "-forward"; vec[i++] = "-form"; vec[i++] = filter; @@ -1256,7 +1500,7 @@ make_bcc_file (int dashstuff) vec[i++] = "-nodashstuffing"; vec[i] = NULL; - execvp (mhlproc, vec); + execvp (program, vec); fprintf (stderr, "unable to exec "); perror (mhlproc); _exit (-1); @@ -1298,22 +1542,19 @@ make_bcc_file (int dashstuff) static int find_prefix (void) { - int len, result; - unsigned char buffer[BUFSIZ]; + int result = OK; + char buffer[BUFSIZ]; FILE *in; if ((in = fopen (tmpfil, "r")) == NULL) adios (tmpfil, "unable to re-open"); - len = strlen (prefix); - - result = OK; - while (fgets (buffer, sizeof(buffer) - 1, in)) + while (fgets (buffer, sizeof buffer, in)) if (buffer[0] == '-' && buffer[1] == '-') { - unsigned char *cp; + char *cp; for (cp = buffer + strlen (buffer) - 1; cp >= buffer; cp--) - if (!isspace (*cp)) + if (!isspace ((unsigned char) *cp)) break; *++cp = '\0'; if (strcmp (buffer + 2, prefix) == 0) { @@ -1327,18 +1568,16 @@ find_prefix (void) } -#define plural(x) (x == 1 ? "" : "s") - static void chkadr (void) { if (badadr && unkadr) die (NULL, "%d address%s unparsable, %d addressee%s undeliverable", - badadr, plural (badadr), unkadr, plural (badadr)); + badadr, PLURALS(badadr), unkadr, PLURALS(badadr)); if (badadr) - die (NULL, "%d address%s unparsable", badadr, plural (badadr)); + die (NULL, "%d address%s unparsable", badadr, PLURALS(badadr)); if (unkadr) - die (NULL, "%d addressee%s undeliverable", unkadr, plural (unkadr)); + die (NULL, "%d addressee%s undeliverable", unkadr, PLURALS(unkadr)); } @@ -1353,7 +1592,7 @@ do_addresses (int bccque, int talk) for (lp = localaddrs.m_next; lp; lp = lp->m_next) if (lp->m_bcc ? bccque : !bccque) { if (talk && !state) - printf (" -- Local Recipients --\n"); + puts(" -- Local Recipients --"); do_an_address (lp, talk); state++; } @@ -1362,7 +1601,7 @@ do_addresses (int bccque, int talk) for (lp = uuaddrs.m_next; lp; lp = lp->m_next) if (lp->m_bcc ? bccque : !bccque) { if (talk && !state) - printf (" -- UUCP Recipients --\n"); + puts(" -- UUCP Recipients --"); do_an_address (lp, talk); state++; } @@ -1371,17 +1610,15 @@ do_addresses (int bccque, int talk) for (lp = netaddrs.m_next; lp; lp = lp->m_next) if (lp->m_bcc ? bccque : !bccque) { if (talk && !state) - printf (" -- Network Recipients --\n"); + puts(" -- Network Recipients --"); do_an_address (lp, talk); state++; } chkadr (); -#ifdef SMTPMTS if (rp_isbad (retval = sm_waend ())) die (NULL, "problem ending addresses; %s", rp_string (retval)); -#endif /* SMTPMTS */ } @@ -1394,90 +1631,145 @@ do_addresses (int bccque, int talk) * SENDMAIL/SMTP routines */ -#ifdef SMTPMTS - static void -post (char *file, int bccque, int talk) +post (char *file, int bccque, int talk, int eai, char *envelope, + int oauth_flag, char *auth_svc) { - int fd, onex; - int retval; + int retval, i; + pid_t child_id; - onex = !(msgflags & MINV) || bccque; if (verbose) { if (msgflags & MINV) printf (" -- Posting for %s Recipients --\n", bccque ? "Blind" : "Sighted"); else - printf (" -- Posting for All Recipients --\n"); + puts(" -- Posting for All Recipients --"); } sigon (); - if (rp_isbad (retval = sm_init (clientsw, serversw, port, watch, verbose, - snoop, onex, queued, sasl, saslmech, - user)) - || rp_isbad (retval = sm_winit (smtpmode, from))) - die (NULL, "problem initializing server; %s", rp_string (retval)); - - do_addresses (bccque, talk && verbose); - if ((fd = open (file, O_RDONLY)) == NOTOK) - die (file, "unable to re-open"); - do_text (file, fd); - close (fd); - fflush (stdout); + if (sm_mts == MTS_SENDMAIL_PIPE) { + char **argp, *program; + int argc; - sm_end (onex ? OK : DONE); - sigoff (); + for (i = 0; (child_id = fork()) == NOTOK && i < 5; i++) + sleep (5); + switch (child_id) { + case NOTOK: + adios ("fork", "unable to"); - if (verbose) { - if (msgflags & MINV) - printf (" -- %s Recipient Copies Posted --\n", - bccque ? "Blind" : "Sighted"); - else - printf (" -- Recipient Copies Posted --\n"); - } + case OK: + if (freopen( file, "r", stdin) == NULL) { + adios (file, "can't reopen for sendmail"); + } - fflush (stdout); + argp = argsplit(sendmail, &program, &argc); + argp[argc++] = "-t"; /* read msg for recipients */ + argp[argc++] = "-i"; /* don't stop on "." */ + if (whomsw) + argp[argc++] = "-bv"; + if (snoop) + argp[argc++] = "-v"; + argp[argc] = NULL; + + execv (program, argp); + adios (sendmail, "can't exec"); + + default: + pidXwait (child_id, NULL); + break; + } + } else { + const int fd = open (file, O_RDONLY); + int eightbit = 0; + + if (fd == NOTOK) { + die (file, "unable to re-open"); + } + + if (msgflags & MMIM && cte != UNKNOWN) { + /* MIME message with C-T-E header. (BINARYMIME isn't + supported, use 8BITMIME instead for binary.) */ + eightbit = cte != SEVENBIT; + } else { + if (scan_input (fd, &eightbit) == NOTOK) { + close (fd); + die (file, "problem reading from"); + } + } + + if (rp_isbad (retval = sm_init (clientsw, serversw, port, watch, + verbose, snoop, sasl, saslmech, user, + oauth_flag ? auth_svc : NULL, tlsflag)) + || rp_isbad (retval = sm_winit (envelope, eai, eightbit))) { + close (fd); + die (NULL, "problem initializing server; %s", rp_string (retval)); + } + + do_addresses (bccque, talk && verbose); + do_text (file, fd); + close (fd); + fflush (stdout); + + sm_end (!(msgflags & MINV) || bccque ? OK : DONE); + sigoff (); + + if (verbose) { + if (msgflags & MINV) + printf (" -- %s Recipient Copies Posted --\n", + bccque ? "Blind" : "Sighted"); + else + puts(" -- Recipient Copies Posted --"); + } + + fflush (stdout); + } } /* Address Verification */ static void -verify_all_addresses (int talk) +verify_all_addresses (int talk, int eai, char *envelope, int oauth_flag, + char *auth_svc) { int retval; struct mailname *lp; sigon (); - if (!whomsw || checksw) + if (!whomsw || checksw) { + /* Not sending message body, so don't need to use 8BITMIME. */ + const int eightbit = 0; + if (rp_isbad (retval = sm_init (clientsw, serversw, port, watch, - verbose, snoop, 0, queued, sasl, - saslmech, user)) - || rp_isbad (retval = sm_winit (smtpmode, from))) + verbose, snoop, sasl, saslmech, user, + oauth_flag ? auth_svc : NULL, tlsflag)) + || rp_isbad (retval = sm_winit (envelope, eai, eightbit))) { die (NULL, "problem initializing server; %s", rp_string (retval)); + } + } if (talk && !whomsw) - printf (" -- Address Verification --\n"); + puts(" -- Address Verification --"); if (talk && localaddrs.m_next) - printf (" -- Local Recipients --\n"); + puts(" -- Local Recipients --"); for (lp = localaddrs.m_next; lp; lp = lp->m_next) do_an_address (lp, talk); if (talk && uuaddrs.m_next) - printf (" -- UUCP Recipients --\n"); + puts(" -- UUCP Recipients --"); for (lp = uuaddrs.m_next; lp; lp = lp->m_next) do_an_address (lp, talk); if (talk && netaddrs.m_next) - printf (" -- Network Recipients --\n"); + puts(" -- Network Recipients --"); for (lp = netaddrs.m_next; lp; lp = lp->m_next) do_an_address (lp, talk); chkadr (); if (talk && !whomsw) - printf (" -- Address Verification Successful --\n"); + puts(" -- Address Verification Successful --"); if (!whomsw || checksw) sm_end (DONE); @@ -1529,7 +1821,7 @@ do_an_address (struct mailname *lp, int talk) lp->m_type != UUCPHOST ? lp->m_path : NULL)) { case RP_OK: if (talk) - printf ("address ok\n"); + puts("address ok"); break; case RP_NO: @@ -1557,7 +1849,7 @@ do_text (char *file, int fd) int retval, state; char buf[BUFSIZ]; - lseek (fd, (off_t) 0, SEEK_SET); + lseek(fd, 0, SEEK_SET); while ((state = read (fd, buf, sizeof(buf))) > 0) { if (rp_isbad (retval = sm_wtxt (buf, state))) @@ -1574,34 +1866,29 @@ do_text (char *file, int fd) case RP_NO: case RP_NDEL: die (NULL, "posting failed; %s", rp_string (retval)); + break; default: die (NULL, "unexpected response; %s", rp_string (retval)); } } -#endif /* SMTPMTS */ - /* * SIGNAL HANDLING */ -static RETSIGTYPE +static void sigser (int i) { -#ifndef RELIABLE_SIGNALS - SIGNAL (i, SIG_IGN); -#endif + NMH_UNUSED (i); - unlink (tmpfil); + (void) m_unlink (tmpfil); if (msgflags & MINV) - unlink (bccfil); + (void) m_unlink (bccfil); -#ifdef SMTPMTS if (!whomsw || checksw) sm_end (NOTOK); -#endif /* SMTPMTS */ done (1); } @@ -1645,11 +1932,11 @@ p_refile (char *file) return; if (verbose) - printf (" -- Filing Folder Copies --\n"); + puts(" -- Filing Folder Copies --"); for (i = 0; i < fccind; i++) fcc (file, fccfold[i]); if (verbose) - printf (" -- Folder Copies Filed --\n"); + puts(" -- Folder Copies Filed --"); } @@ -1661,8 +1948,9 @@ static void fcc (char *file, char *folder) { pid_t child_id; - int i, status; + int i, status, argp; char fold[BUFSIZ]; + char **arglist, *program; if (verbose) printf (" %sFcc %s: ", msgstate == RESENT ? "Resent-" : "", folder); @@ -1685,8 +1973,14 @@ fcc (char *file, char *folder) *folder == '+' || *folder == '@' ? "" : "+", folder); /* now exec the fileproc */ - execlp (fileproc, r1bindex (fileproc, '/'), - "-link", "-file", file, fold, NULL); + + arglist = argsplit(fileproc, &program, &argp); + arglist[argp++] = "-link"; + arglist[argp++] = "-file"; + arglist[argp++] = file; + arglist[argp++] = fold; + arglist[argp] = NULL; + execvp (program, arglist); _exit (-1); default: @@ -1694,10 +1988,10 @@ fcc (char *file, char *folder) if (!verbose) fprintf (stderr, " %sFcc %s: ", msgstate == RESENT ? "Resent-" : "", folder); - pidstatus (status, verbose ? stdout : stderr, NULL); + pidstatus (status, verbose ? stdout : stderr, fileproc); } else { if (verbose) - printf ("folder ok\n"); + puts("folder ok"); } } @@ -1711,18 +2005,20 @@ fcc (char *file, char *folder) static void die (char *what, char *fmt, ...) { + int err; va_list ap; - unlink (tmpfil); + err = errno; + + (void) m_unlink (tmpfil); if (msgflags & MINV) - unlink (bccfil); + (void) m_unlink (bccfil); -#ifdef SMTPMTS if (!whomsw || checksw) sm_end (NOTOK); -#endif /* SMTPMTS */ va_start(ap, fmt); + errno = err; advertise (what, NULL, fmt, ap); va_end(ap); done (1);