long dummy_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data) { struct stat sbuf; int fd = -1; int e; char tmp[MAILTMPLEN]; MAILSTREAM *ts = default_proto (T); if (compare_cstring (mailbox,"INBOX") && dummy_file (tmp,mailbox) && ((fd = open (tmp,O_RDONLY,NIL)) < 0)) { if ((e = errno) == ENOENT) /* failed, was it no such file? */ mm_notify (stream,"[TRYCREATE] Must create mailbox before append", (long) NIL); sprintf (tmp,"%.80s: %.80s",strerror (e),mailbox); mm_log (tmp,ERROR); /* pass up error */ return NIL; /* always fails */ } if (fd >= 0) { /* found file? */ fstat (fd,&sbuf); /* get its size */ close (fd); /* toss out the fd */ if (sbuf.st_size) ts = NIL; /* non-empty file? */ } if (ts) return (*ts->dtb->append) (stream,mailbox,af,data); sprintf (tmp,"Indeterminate mailbox format: %.80s",mailbox); mm_log (tmp,ERROR); return NIL; }
long dummy_rename (MAILSTREAM *stream,char *old,char *newname) { struct stat sbuf; char c,*s,tmp[MAILTMPLEN],mbx[MAILTMPLEN],oldname[MAILTMPLEN]; long ret = NIL; /* no trailing \ allowed */ if (!dummy_file (oldname,old) || !(s = dummy_file (mbx,newname)) || ((s = strrchr (s,'\\')) && !s[1])) { sprintf (mbx,"Can't rename %.80s to %.80s: invalid name",old,newname); mm_log (mbx,ERROR); return NIL; } /* found superior to destination name? */ if (s && (s != mbx) && ((mbx[1] != ':') || (s != mbx + 2))) { c = s[1]; /* remember character after delimiter */ *s = s[1] = '\0'; /* tie off name at delimiter */ /* name doesn't exist, create it */ if (stat (mbx,&sbuf) || ((sbuf.st_mode & S_IFMT) != S_IFDIR)) { *s = '\\'; /* restore delimiter */ if (!dummy_create (stream,mbx)) return NIL; } else *s = '\\'; /* restore delimiter */ s[1] = c; /* restore character after delimiter */ } /* rename of non-ex INBOX creates dest */ if (!compare_cstring (old,"INBOX") && stat (oldname,&sbuf)) return dummy_create (NIL,mbx); if (rename (oldname,mbx)) { sprintf (tmp,"Can't rename mailbox %.80s to %.80s: %.80s",old,newname, strerror (errno)); mm_log (tmp,ERROR); return NIL; } return LONGT; /* return success */ }
MAILSTREAM *dummy_open (MAILSTREAM *stream) { int fd; char err[MAILTMPLEN],tmp[MAILTMPLEN]; struct stat sbuf; /* OP_PROTOTYPE call */ if (!stream) return &dummyproto; err[0] = '\0'; /* no error message yet */ /* can we open the file? */ if (!dummy_file (tmp,stream->mailbox)) sprintf (err,"Can't open this name: %.80s",stream->mailbox); else if ((fd = open (tmp,O_RDONLY,NIL)) < 0) { /* no, error unless INBOX */ if (compare_cstring (stream->mailbox,"INBOX")) sprintf (err,"%.80s: %.80s",strerror (errno),stream->mailbox); } else { /* file had better be empty then */ fstat (fd,&sbuf); /* sniff at its size */ close (fd); if (sbuf.st_size) /* bogus format if non-empty */ sprintf (err,"%.80s (file %.80s) is not in valid mailbox format", stream->mailbox,tmp); } if (err[0]) { /* if an error happened */ mm_log (err,stream->silent ? WARN : ERROR); return NIL; } else if (!stream->silent) { /* only if silence not requested */ mail_exists (stream,0); /* say there are 0 messages */ mail_recent (stream,0); /* and certainly no recent ones! */ stream->uid_validity = time (0); } stream->inbox = T; /* note that it's an INBOX */ return stream; /* return success */ }
long sm_unsubscribe (char *mailbox) { FILE *f,*tf; char *s,tmp[MAILTMPLEN],old[MAILTMPLEN],newname[MAILTMPLEN]; int found = NIL; /* canonicalize INBOX */ if (!compare_cstring (mailbox,"INBOX")) mailbox = "INBOX"; SUBSCRIPTIONFILE (old); /* make file names */ SUBSCRIPTIONTEMP (newname); if (!(f = fopen (old,"r"))) /* open subscription database */ MM_LOG ("No subscriptions",ERROR); else if (!(tf = fopen (newname,"w"))) { MM_LOG ("Can't create subscription temporary file",ERROR); fclose (f); } else { while (fgets (tmp,MAILTMPLEN,f)) { if (s = strchr (tmp,'\n')) *s = '\0'; if (strcmp (tmp,mailbox)) fprintf (tf,"%s\n",tmp); else found = T; /* found the name */ } fclose (f); if (fclose (tf) == EOF) MM_LOG ("Can't write subscription temporary file",ERROR); else if (!found) { sprintf (tmp,"Not subscribed to mailbox %.80s",mailbox); MM_LOG (tmp,ERROR); /* error if at end */ } else if (!unlink (old) && !rename (newname,old)) return LONGT; else MM_LOG ("Can't update subscription database",ERROR); } return NIL; }
long sm_subscribe (char *mailbox) { FILE *f; char *s,db[MAILTMPLEN],tmp[MAILTMPLEN]; /* canonicalize INBOX */ if (!compare_cstring (mailbox,"INBOX")) mailbox = "INBOX"; SUBSCRIPTIONFILE (db); /* open subscription database */ if (f = fopen (db,"r")) { /* make sure not already there */ while (fgets (tmp,MAILTMPLEN,f)) { if (s = strchr (tmp,'\n')) *s = '\0'; if (!strcmp (tmp,mailbox)) {/* already subscribed? */ sprintf (tmp,"Already subscribed to mailbox %.80s",mailbox); MM_LOG (tmp,ERROR); fclose (f); return NIL; } } fclose (f); } if (!(f = fopen (db,"a"))) { /* append new entry */ MM_LOG ("Can't append to subscription database",ERROR); return NIL; } fprintf (f,"%s\n",mailbox); return (fclose (f) == EOF) ? NIL : T; }
long dummy_create (MAILSTREAM *stream,char *mailbox) { char tmp[MAILTMPLEN]; if (compare_cstring (mailbox,"INBOX") && dummy_file (tmp,mailbox)) return dummy_create_path (stream,tmp,NIL); sprintf (tmp,"Can't create %.80s: invalid name",mailbox); mm_log (tmp,ERROR); return NIL; }
long smtp_ehlo (SENDSTREAM *stream,char *host,NETMBX *mb) { unsigned long i,j; long flags = (mb->secflag ? AU_SECURE : NIL) | (mb->authuser[0] ? AU_AUTHUSER : NIL); char *s,*t,*r,tmp[MAILTMPLEN]; /* clear ESMTP data */ memset (&ESMTP,0,sizeof (ESMTP)); if (mb->loser) return 500; /* never do EHLO if a loser */ sprintf (tmp,"EHLO %s",host); /* build the complete command */ if (stream->debug) mm_dlog (tmp); strcat (tmp,"\015\012"); /* send the command */ if (!net_soutr (stream->netstream,tmp)) return smtp_fake (stream,"SMTP connection broken (EHLO)"); /* got an OK reply? */ do if ((i = smtp_reply (stream)) == SMTPOK) { /* hack for AUTH= */ if (stream->reply[4] && stream->reply[5] && stream->reply[6] && stream->reply[7] && (stream->reply[8] == '=')) stream->reply[8] = ' '; /* get option code */ if (!(s = strtok_r (stream->reply+4," ",&r))); /* have option, does it have a value */ else if ((t = strtok_r (NIL," ",&r)) && *t) { /* EHLO options which take arguments */ if (!compare_cstring (s,"SIZE")) { if (isdigit (*t)) ESMTP.size.limit = strtoul (t,&t,10); ESMTP.size.ok = T; } else if (!compare_cstring (s,"DELIVERBY")) { if (isdigit (*t)) ESMTP.deliverby.minby = strtoul (t,&t,10); ESMTP.deliverby.ok = T; } else if (!compare_cstring (s,"ATRN")) { ESMTP.atrn.domains = cpystr (t); ESMTP.atrn.ok = T; } else if (!compare_cstring (s,"AUTH")) do if ((j = mail_lookup_auth_name (t,flags)) && (--j < MAXAUTHENTICATORS)) ESMTP.auth |= (1 << j); while ((t = strtok_r (NIL," ",&r)) && *t); } /* EHLO options which do not take arguments */ else if (!compare_cstring (s,"SIZE")) ESMTP.size.ok = T; else if (!compare_cstring (s,"8BITMIME")) ESMTP.eightbit.ok = T; else if (!compare_cstring (s,"DSN")) ESMTP.dsn.ok = T; else if (!compare_cstring (s,"ATRN")) ESMTP.atrn.ok = T; else if (!compare_cstring (s,"SEND")) ESMTP.service.send = T; else if (!compare_cstring (s,"SOML")) ESMTP.service.soml = T; else if (!compare_cstring (s,"SAML")) ESMTP.service.saml = T; else if (!compare_cstring (s,"EXPN")) ESMTP.service.expn = T; else if (!compare_cstring (s,"HELP")) ESMTP.service.help = T; else if (!compare_cstring (s,"TURN")) ESMTP.service.turn = T; else if (!compare_cstring (s,"ETRN")) ESMTP.service.etrn = T; else if (!compare_cstring (s,"STARTTLS")) ESMTP.service.starttls = T; else if (!compare_cstring (s,"RELAY")) ESMTP.service.relay = T; else if (!compare_cstring (s,"PIPELINING")) ESMTP.service.pipe = T; else if (!compare_cstring (s,"ENHANCEDSTATUSCODES")) ESMTP.service.ensc = T; else if (!compare_cstring (s,"BINARYMIME")) ESMTP.service.bmime = T; else if (!compare_cstring (s,"CHUNKING")) ESMTP.service.chunk = T; } while ((i < 100) || (stream->reply[3] == '-')); /* disable LOGIN if PLAIN also advertised */ if ((j = mail_lookup_auth_name ("PLAIN",NIL)) && (--j < MAXAUTHENTICATORS) && (ESMTP.auth & (1 << j)) && (j = mail_lookup_auth_name ("LOGIN",NIL)) && (--j < MAXAUTHENTICATORS)) ESMTP.auth &= ~(1 << j); return i; /* return the response code */ }
SENDSTREAM *smtp_open_full (NETDRIVER *dv,char **hostlist,char *service, unsigned long port,long options) { SENDSTREAM *stream = NIL; long reply; char *s,tmp[MAILTMPLEN]; NETSTREAM *netstream; NETMBX mb; if (!(hostlist && *hostlist)) mm_log ("Missing SMTP service host",ERROR); /* maximum domain name is 64 characters */ else do if (strlen (*hostlist) < SMTPMAXDOMAIN) { sprintf (tmp,"{%.1000s}",*hostlist); if (!mail_valid_net_parse_work (tmp,&mb,service ? service : "smtp") || mb.anoflag || mb.readonlyflag) { sprintf (tmp,"Invalid host specifier: %.80s",*hostlist); mm_log (tmp,ERROR); } else { /* light tryssl flag if requested */ mb.trysslflag = (options & SOP_TRYSSL) ? T : NIL; /* explicit port overrides all */ if (mb.port) port = mb.port; /* else /submit overrides port argument */ else if (!compare_cstring (mb.service,"submit")) { port = SUBMITTCPPORT; /* override port, use IANA name */ strcpy (mb.service,"submission"); } /* else port argument overrides SMTP port */ else if (!port) port = smtp_port ? smtp_port : SMTPTCPPORT; if (netstream = /* try to open ordinary connection */ net_open (&mb,dv,port, (NETDRIVER *) mail_parameters (NIL,GET_SSLDRIVER,NIL), "*smtps",smtp_sslport ? smtp_sslport : SMTPSSLPORT)) { stream = (SENDSTREAM *) memset (fs_get (sizeof (SENDSTREAM)),0, sizeof (SENDSTREAM)); stream->netstream = netstream; stream->host = cpystr ((long) mail_parameters (NIL,GET_TRUSTDNS,NIL) ? net_host (netstream) : mb.host); stream->debug = (mb.dbgflag || (options & OP_DEBUG)) ? T : NIL; if (options & SOP_SECURE) mb.secflag = T; /* get name of local host to use */ s = compare_cstring ("localhost",mb.host) ? net_localhost (netstream) : "localhost"; do reply = smtp_reply (stream); while ((reply < 100) || (stream->reply[3] == '-')); if (reply != SMTPGREET){/* get SMTP greeting */ sprintf (tmp,"SMTP greeting failure: %.80s",stream->reply); mm_log (tmp,ERROR); stream = smtp_close (stream); } /* try EHLO first, then HELO */ else if (((reply = smtp_ehlo (stream,s,&mb)) != SMTPOK) && ((reply = smtp_send (stream,"HELO",s)) != SMTPOK)) { sprintf (tmp,"SMTP hello failure: %.80s",stream->reply); mm_log (tmp,ERROR); stream = smtp_close (stream); } else { NETDRIVER *ssld =(NETDRIVER *)mail_parameters(NIL,GET_SSLDRIVER,NIL); sslstart_t stls = (sslstart_t) mail_parameters(NIL,GET_SSLSTART,NIL); ESMTP.ok = T; /* ESMTP server, start TLS if present */ if (!dv && stls && ESMTP.service.starttls && !mb.sslflag && !mb.notlsflag && (smtp_send (stream,"STARTTLS",NIL) == SMTPGREET)) { mb.tlsflag = T; /* TLS OK, get into TLS at this end */ stream->netstream->dtb = ssld; /* TLS started, negotiate it */ if (!(stream->netstream->stream = (*stls) (stream->netstream->stream,mb.host, (mb.tlssslv23 ? NIL : NET_TLSCLIENT) | (mb.novalidate ? NET_NOVALIDATECERT:NIL)))){ /* TLS negotiation failed after STARTTLS */ sprintf (tmp,"Unable to negotiate TLS with this server: %.80s", mb.host); mm_log (tmp,ERROR); /* close without doing QUIT */ if (stream->netstream) net_close (stream->netstream); stream->netstream = NIL; stream = smtp_close (stream); } /* TLS OK, re-negotiate EHLO */ else if ((reply = smtp_ehlo (stream,s,&mb)) != SMTPOK) { sprintf (tmp,"SMTP EHLO failure after STARTTLS: %.80s", stream->reply); mm_log (tmp,ERROR); stream = smtp_close (stream); } else ESMTP.ok = T; /* TLS OK and EHLO successful */ } else if (mb.tlsflag) {/* user specified /tls but can't do it */ sprintf (tmp,"TLS unavailable with this server: %.80s",mb.host); mm_log (tmp,ERROR); stream = smtp_close (stream); } /* remote name for authentication */ if (stream && ((mb.secflag || mb.user[0]))) { if (ESMTP.auth) { /* use authenticator? */ if ((long) mail_parameters (NIL,GET_TRUSTDNS,NIL)) { /* remote name for authentication */ strncpy (mb.host, (long) mail_parameters (NIL,GET_SASLUSESPTRNAME,NIL) ? net_remotehost (netstream) : net_host (netstream), NETMAXHOST-1); mb.host[NETMAXHOST-1] = '\0'; } if (!smtp_auth (stream,&mb,tmp)) stream = smtp_close (stream); } else { /* no available authenticators? */ sprintf (tmp,"%sSMTP authentication not available: %.80s", mb.secflag ? "Secure " : "",mb.host); mm_log (tmp,ERROR); stream = smtp_close (stream); } } } } } } while (!stream && *++hostlist); if (stream) { /* set stream options if have a stream */ if (options &(SOP_DSN | SOP_DSN_NOTIFY_FAILURE | SOP_DSN_NOTIFY_DELAY | SOP_DSN_NOTIFY_SUCCESS | SOP_DSN_RETURN_FULL)) { ESMTP.dsn.want = T; if (options & SOP_DSN_NOTIFY_FAILURE) ESMTP.dsn.notify.failure = T; if (options & SOP_DSN_NOTIFY_DELAY) ESMTP.dsn.notify.delay = T; if (options & SOP_DSN_NOTIFY_SUCCESS) ESMTP.dsn.notify.success = T; if (options & SOP_DSN_RETURN_FULL) ESMTP.dsn.full = T; } if (options & SOP_8BITMIME) ESMTP.eightbit.want = T; } return stream; }
int deliver (FILE *f,unsigned long msglen,char *user) { MAILSTREAM *ds = NIL; char *s,*mailbox,tmp[MAILTMPLEN],path[MAILTMPLEN]; STRING st; struct stat sbuf; /* have a mailbox specifier? */ if ((mailbox = strchr (user,'+')) != NULL) { *mailbox++ = '\0'; /* yes, tie off user name */ if (!*mailbox || !compare_cstring ((unsigned char *) mailbox,"INBOX")) mailbox = NIL; /* user+ and user+INBOX same as user */ } if (!*user) user = myusername (); else if (strcmp (user,myusername ())) return fail ("can't deliver to other user",EX_CANTCREAT); sprintf (tmp,"delivering to %.80s+%.80s",user,mailbox ? mailbox : "INBOX"); mm_dlog (tmp); /* prepare stringstruct */ INIT (&st,file_string,(void *) f,msglen); if (mailbox) { /* non-INBOX name */ switch (mailbox[0]) { /* make sure a valid name */ default: /* other names, try to deliver if not INBOX */ if ((strlen (mailbox) <= NETMAXMBX) && !strstr (mailbox,"..") && !strstr (mailbox,"//") && !strstr (mailbox,"/~") && mailboxfile (path,mailbox) && path[0] && !deliver_safely (NIL,&st,mailbox,path,tmp)) return NIL; case '%': case '*': /* wildcards not valid */ case '/': /* absolute path names not valid */ case '~': /* user names not valid */ sprintf (tmp,"invalid mailbox name %.80s+%.80s",user,mailbox); mm_log (tmp,WARN); break; } mm_dlog ("retrying delivery to INBOX"); SETPOS (&st,0); /* rewind stringstruct just in case */ } /* no -I, resolve "INBOX" into path */ if (mailboxfile (path,mailbox = "INBOX") && !path[0]) { /* clear box, get generic INBOX prototype */ if (!(ds = mail_open (NIL,"INBOX",OP_PROTOTYPE))) fatal ("no INBOX prototype"); /* standard system driver? */ if (!strcmp (ds->dtb->name,"unix") || !strcmp (ds->dtb->name,"mmdf")) { strcpy (path,sysinbox ());/* use system INBOX */ if (!lstat (path,&sbuf)) /* deliver to existing system INBOX */ return deliver_safely (ds,&st,mailbox,path,tmp); } else { /* other driver, try ~/INBOX */ if ((mailboxfile (path,"&&&&&") == path) && (s = strstr (path,"&&&&&")) && strcpy (s,"INBOX") && !lstat (path,&sbuf)){ /* deliver to existing ~/INBOX */ sprintf (tmp,"#driver.%s/INBOX",ds->dtb->name); return deliver_safely (ds,&st,cpystr (tmp),path,tmp); } } /* not dummy, deliver to driver imputed path */ if (strcmp (ds->dtb->name,"dummy")) return (ibxpath (ds,&mailbox,path) && !lstat (path,&sbuf)) ? deliver_safely (ds,&st,mailbox,path,tmp) : fail ("unable to resolve INBOX path",EX_CANTCREAT); /* dummy, empty imputed append path exist? */ if (ibxpath (ds = default_proto (T),&mailbox,path) && !lstat (path,&sbuf) && !sbuf.st_size) return deliver_safely (ds,&st,mailbox,path,tmp); /* impute path that we will create */ if (!ibxpath (ds = default_proto (NIL),&mailbox,path)) return fail ("unable to resolve INBOX",EX_CANTCREAT); } /* black box, must create, get create proto */ else if (lstat (path,&sbuf)) ds = default_proto (NIL); else { /* black box, existing file */ /* empty file, get append prototype */ if (!sbuf.st_size) ds = default_proto (T); /* non-empty, get prototype from its data */ else if (!(ds = mail_open (NIL,"INBOX",OP_PROTOTYPE))) fatal ("no INBOX prototype"); /* error if unknown format */ if (!strcmp (ds->dtb->name,"phile")) return fail ("unknown format INBOX",EX_UNAVAILABLE); /* otherwise can deliver to it */ return deliver_safely (ds,&st,mailbox,path,tmp); } sprintf (tmp,"attempting to create mailbox %.80s path %.80s",mailbox,path); mm_dlog (tmp); /* supplicate to the Evil One */ if (!path_create (ds,path)) return fail ("can't create INBOX",EX_CANTCREAT); sprintf (tmp,"created %.80s",path); mm_dlog (tmp); /* deliver the message */ return deliver_safely (ds,&st,mailbox,path,tmp); }
int main (int argc,char *argv[]) { FILE *f = NIL; int pid,c,ret = 0; unsigned long msglen,status = 0; char *s,tmp[MAILTMPLEN]; uid_t ruid = getuid (); struct passwd *pwd; openlog ("tmail",LOG_PID,LOG_MAIL); #include "linkage.c" /* make sure have some arguments */ if (--argc < 1) _exit (fail ("usage: tmail [-D] user[+folder]",EX_USAGE)); /* process all flags */ while (argc && (*(s = *++argv)) == '-') { argc--; /* gobble this argument */ switch (s[1]) { /* what is this flag? */ case 'D': /* debug */ debug = T; /* don't fork */ break; case 'I': /* inbox specifier */ if (inbox || format) _exit (fail ("duplicate -b or -I",EX_USAGE)); if (argc--) inbox = cpystr (*++argv); else _exit (fail ("missing argument to -I",EX_USAGE)); break; case 'f': /* new name for this flag */ case 'r': /* flag giving return path */ if (sender) _exit (fail ("duplicate -f or -r",EX_USAGE)); if (argc--) sender = cpystr (*++argv); else _exit (fail ("missing argument to -f or -r",EX_USAGE)); break; case 'b': /* create INBOX in this format */ if (inbox || format) _exit (fail ("duplicate -b or -I",EX_USAGE)); if (!argc--) _exit (fail ("missing argument to -b",EX_USAGE)); if (!(format = mail_parameters (NIL,GET_DRIVER,*++argv))) _exit (fail ("unknown format to -b",EX_USAGE)); else if (!(format->flags & DR_LOCAL) || !compare_cstring (format->name,"dummy")) _exit (fail ("invalid format to -b",EX_USAGE)); break; /* following flags are undocumented */ case 'p': /* precedence for quota */ if (s[2] && ((s[2] == '-') || isdigit (s[2]))) precedence = atol (s + 2); else if (argc-- && ((*(s = *++argv) == '-') || isdigit (*s))) precedence = atol (s); else _exit (fail ("missing argument to -p",EX_USAGE)); break; case 'd': /* obsolete flag meaning multiple users */ break; /* ignore silently */ /* -s has been deprecated and replaced by the -s and -k flags in dmail. * dmail's -k flag does what -s once did in tmail; dmail's -s flag * takes no argument and just sets \Seen. Flag setting is more properly * done in dmail which runs as the user and is clearly at the user's * behest. Since tmail runs privileged, -s would have to be disabled * unless the caller is also privileged. */ case 's': /* obsolete flag meaning delivery flags */ if (!argc--) /* takes an argument */ _exit (fail ("missing argument to deprecated flag",EX_USAGE)); syslog (LOG_INFO,"tmail called with deprecated flag: -s %.200s",*++argv); break; default: /* anything else */ _exit (fail ("unknown switch",EX_USAGE)); } } if (!argc) ret = fail ("no recipients",EX_USAGE); else if (!(f = tmpfile ())) ret = fail ("can't make temp file",EX_TEMPFAIL); else { /* build delivery headers */ if (sender) fprintf (f,"Return-Path: <%s>\015\012",sender); /* start Received line: */ fprintf (f,"Received: via tmail-%s.%s",CCLIENTVERSION,version); /* not root or daemon? */ if (ruid && !((pwd = getpwnam ("daemon")) && (ruid == pwd->pw_uid))) { pwd = getpwuid (ruid); /* get unprivileged user's information */ if (inbox || format) { if (pwd) sprintf (tmp,"user %.80s",pwd->pw_name); else sprintf (tmp,"UID %ld",(long) ruid); strcat (tmp," is not privileged to use -b or -I"); _exit (fail (tmp,EX_USAGE)); } fputs (" (invoked by ",f); if (pwd) fprintf (f,"user %s",pwd->pw_name); else fprintf (f,"UID %ld",(long) ruid); fputs (")",f); } /* write "for" if single recipient */ if (argc == 1) fprintf (f," for %s",*argv); fputs ("; ",f); rfc822_date (tmp); fputs (tmp,f); fputs ("\015\012",f); /* copy text from standard input */ if (!fgets (tmp,MAILTMPLEN-1,stdin) || !(s = strchr (tmp,'\n')) || (s == tmp) || s[1]) _exit (fail ("bad first message line",EX_USAGE)); if (s[-1] == '\015') { /* nuke leading "From " line */ if ((tmp[0] != 'F') || (tmp[1] != 'r') || (tmp[2] != 'o') || (tmp[3] != 'm') || (tmp[4] != ' ')) fputs (tmp,f); while ((c = getchar ()) != EOF) putc (c,f); } else { mm_log ("tmail called with LF-only newlines",WARN); if ((tmp[0] != 'F') || (tmp[1] != 'r') || (tmp[2] != 'o') || (tmp[3] != 'm') || (tmp[4] != ' ')) { *s++ = '\015'; /* overwrite NL with CRLF */ *s++ = '\012'; *s = '\0'; /* tie off string */ fputs (tmp,f); /* write line */ } /* copy text from standard input */ while ((c = getchar ()) != EOF) { /* add CR if needed */ if (c == '\012') putc ('\015',f); putc (c,f); } } msglen = ftell (f); /* size of message */ fflush (f); /* make sure all changes written out */ if (ferror (f)) ret = fail ("error writing temp file",EX_TEMPFAIL); else if (!msglen) ret = fail ("empty message",EX_TEMPFAIL); /* single delivery */ else if (argc == 1) ret = deliver (f,msglen,*argv); else do { /* multiple delivery uses daughter forks */ if ((pid = fork ()) < 0) ret = fail (strerror (errno),EX_OSERR); else if (pid) { /* mother process */ grim_pid_reap_status (pid,NIL,(void *) status); /* normal termination? */ if (!ret) ret = (status & 0xff) ? EX_SOFTWARE : (status & 0xff00) >> 8; } /* daughter process */ else _exit (deliver (f,msglen,*argv)); } while (--argc && *argv++); mm_dlog (ret ? "error in delivery" : "all recipients delivered"); }