122711Sdist /* 234921Sbostic * Copyright (c) 1983 Eric P. Allman 362531Sbostic * Copyright (c) 1988, 1993 462531Sbostic * The Regents of the University of California. All rights reserved. 533731Sbostic * 642829Sbostic * %sccs.include.redist.c% 733731Sbostic */ 822711Sdist 922711Sdist #ifndef lint 10*68693Seric static char sccsid[] = "@(#)savemail.c 8.62 (Berkeley) 03/31/95"; 1133731Sbostic #endif /* not lint */ 1222711Sdist 1363937Seric # include "sendmail.h" 14297Seric 15297Seric /* 16297Seric ** SAVEMAIL -- Save mail on error 17297Seric ** 189375Seric ** If mailing back errors, mail it back to the originator 19297Seric ** together with an error message; otherwise, just put it in 20297Seric ** dead.letter in the user's home directory (if he exists on 21297Seric ** this machine). 22297Seric ** 23297Seric ** Parameters: 249337Seric ** e -- the envelope containing the message in error. 2567981Seric ** sendbody -- if TRUE, also send back the body of the 2667981Seric ** message; otherwise just send the header. 27297Seric ** 28297Seric ** Returns: 29297Seric ** none 30297Seric ** 31297Seric ** Side Effects: 32297Seric ** Saves the letter, by writing or mailing it back to the 33297Seric ** sender, or by putting it in dead.letter in her home 34297Seric ** directory. 35297Seric */ 36297Seric 3724942Seric /* defines for state machine */ 3824942Seric # define ESM_REPORT 0 /* report to sender's terminal */ 3924942Seric # define ESM_MAIL 1 /* mail back to sender */ 4024942Seric # define ESM_QUIET 2 /* messages have already been returned */ 4124942Seric # define ESM_DEADLETTER 3 /* save in ~/dead.letter */ 4224942Seric # define ESM_POSTMASTER 4 /* return to postmaster */ 4324942Seric # define ESM_USRTMP 5 /* save in /usr/tmp/dead.letter */ 4424942Seric # define ESM_PANIC 6 /* leave the locked queue/transcript files */ 4524942Seric # define ESM_DONE 7 /* the message is successfully delivered */ 4624942Seric 4765174Seric # ifndef _PATH_VARTMP 4865174Seric # define _PATH_VARTMP "/usr/tmp/" 4965174Seric # endif 5024942Seric 5165174Seric 5267981Seric savemail(e, sendbody) 539337Seric register ENVELOPE *e; 5467981Seric bool sendbody; 55297Seric { 56297Seric register struct passwd *pw; 5724942Seric register FILE *fp; 5824942Seric int state; 5959290Seric auto ADDRESS *q = NULL; 6065870Seric register char *p; 6165870Seric MCI mcibuf; 62297Seric char buf[MAXLINE+1]; 63297Seric extern char *ttypath(); 645846Seric typedef int (*fnptr)(); 6564945Seric extern bool writable(); 66297Seric 677676Seric if (tTd(6, 1)) 6858680Seric { 6966317Seric printf("\nsavemail, errormode = %c, id = %s, ExitStat = %d\n e_from=", 7066317Seric e->e_errormode, e->e_id == NULL ? "NONE" : e->e_id, 7166317Seric ExitStat); 7258680Seric printaddr(&e->e_from, FALSE); 7358680Seric } 747361Seric 7559101Seric if (e->e_id == NULL) 7659101Seric { 7759101Seric /* can't return a message with no id */ 7859101Seric return; 7959101Seric } 8059101Seric 81297Seric /* 82297Seric ** In the unhappy event we don't know who to return the mail 83297Seric ** to, make someone up. 84297Seric */ 85297Seric 869337Seric if (e->e_from.q_paddr == NULL) 87297Seric { 8858733Seric e->e_sender = "Postmaster"; 8964284Seric if (parseaddr(e->e_sender, &e->e_from, 9064284Seric RF_COPYPARSE|RF_SENDERADDR, '\0', NULL, e) == NULL) 91297Seric { 9258733Seric syserr("553 Cannot parse Postmaster!"); 93297Seric ExitStat = EX_SOFTWARE; 94297Seric finis(); 95297Seric } 96297Seric } 979337Seric e->e_to = NULL; 98297Seric 99297Seric /* 10024942Seric ** Basic state machine. 10124942Seric ** 10224942Seric ** This machine runs through the following states: 10324942Seric ** 10424942Seric ** ESM_QUIET Errors have already been printed iff the 10524942Seric ** sender is local. 10624942Seric ** ESM_REPORT Report directly to the sender's terminal. 10724942Seric ** ESM_MAIL Mail response to the sender. 10824942Seric ** ESM_DEADLETTER Save response in ~/dead.letter. 10924942Seric ** ESM_POSTMASTER Mail response to the postmaster. 11024942Seric ** ESM_PANIC Save response anywhere possible. 111297Seric */ 112297Seric 11324942Seric /* determine starting state */ 11458734Seric switch (e->e_errormode) 115297Seric { 11624942Seric case EM_WRITE: 11724942Seric state = ESM_REPORT; 11824942Seric break; 11924942Seric 12024942Seric case EM_BERKNET: 12124942Seric /* mail back, but return o.k. exit status */ 122401Seric ExitStat = EX_OK; 12324942Seric 12424942Seric /* fall through.... */ 12524942Seric 12624942Seric case EM_MAIL: 12724942Seric state = ESM_MAIL; 12824942Seric break; 12924942Seric 13024942Seric case EM_PRINT: 13124979Seric case '\0': 13224942Seric state = ESM_QUIET; 13324942Seric break; 13424942Seric 13524942Seric case EM_QUIET: 13624942Seric /* no need to return anything at all */ 13724942Seric return; 13824979Seric 13924979Seric default: 14058734Seric syserr("554 savemail: bogus errormode x%x\n", e->e_errormode); 14124979Seric state = ESM_MAIL; 14224979Seric break; 143297Seric } 144297Seric 14559094Seric /* if this is already an error response, send to postmaster */ 14659094Seric if (bitset(EF_RESPONSE, e->e_flags)) 14759094Seric { 14859094Seric if (e->e_parent != NULL && 14959094Seric bitset(EF_RESPONSE, e->e_parent->e_flags)) 15059094Seric { 15159094Seric /* got an error sending a response -- can it */ 15259094Seric return; 15359094Seric } 15459094Seric state = ESM_POSTMASTER; 15559094Seric } 15659094Seric 15724942Seric while (state != ESM_DONE) 158297Seric { 15924979Seric if (tTd(6, 5)) 16024979Seric printf(" state %d\n", state); 16124979Seric 16224942Seric switch (state) 163297Seric { 16424979Seric case ESM_QUIET: 16567473Seric if (bitnset(M_LOCALMAILER, e->e_from.q_mailer->m_flags)) 16624979Seric state = ESM_DEADLETTER; 16724979Seric else 16824979Seric state = ESM_MAIL; 16924979Seric break; 17024979Seric 17124942Seric case ESM_REPORT: 17224942Seric 17324942Seric /* 17424942Seric ** If the user is still logged in on the same terminal, 17524942Seric ** then write the error messages back to hir (sic). 17624942Seric */ 17724942Seric 17824942Seric p = ttypath(); 17924942Seric if (p == NULL || freopen(p, "w", stdout) == NULL) 18024942Seric { 18124942Seric state = ESM_MAIL; 18224942Seric break; 18324942Seric } 18424942Seric 18568529Seric expand("\201n", buf, sizeof buf, e); 1869375Seric printf("\r\nMessage from %s...\r\n", buf); 1879375Seric printf("Errors occurred while sending mail.\r\n"); 1889542Seric if (e->e_xfp != NULL) 1899375Seric { 1909542Seric (void) fflush(e->e_xfp); 19124942Seric fp = fopen(queuename(e, 'x'), "r"); 1929375Seric } 1939375Seric else 19424942Seric fp = NULL; 19524942Seric if (fp == NULL) 1969375Seric { 1979337Seric syserr("Cannot open %s", queuename(e, 'x')); 1989375Seric printf("Transcript of session is unavailable.\r\n"); 1999375Seric } 2009375Seric else 2019375Seric { 2029375Seric printf("Transcript follows:\r\n"); 20324942Seric while (fgets(buf, sizeof buf, fp) != NULL && 2049375Seric !ferror(stdout)) 2059375Seric fputs(buf, stdout); 20658680Seric (void) xfclose(fp, "savemail transcript", e->e_id); 2079375Seric } 20824942Seric printf("Original message will be saved in dead.letter.\r\n"); 20924942Seric state = ESM_DEADLETTER; 21024942Seric break; 211297Seric 21224942Seric case ESM_MAIL: 21324942Seric /* 21424942Seric ** If mailing back, do it. 21524942Seric ** Throw away all further output. Don't alias, 21624942Seric ** since this could cause loops, e.g., if joe 21724942Seric ** mails to joe@x, and for some reason the network 21824942Seric ** for @x is down, then the response gets sent to 21924942Seric ** joe@x, which gives a response, etc. Also force 22024942Seric ** the mail to be delivered even if a version of 22124942Seric ** it has already been sent to the sender. 22263841Seric ** 22363841Seric ** If this is a configuration or local software 22463841Seric ** error, send to the local postmaster as well, 22563841Seric ** since the originator can't do anything 22663841Seric ** about it anyway. Note that this is a full 22763841Seric ** copy of the message (intentionally) so that 22863841Seric ** the Postmaster can forward things along. 22924942Seric */ 230297Seric 23163841Seric if (ExitStat == EX_CONFIG || ExitStat == EX_SOFTWARE) 23263841Seric { 23363841Seric (void) sendtolist("postmaster", 23467982Seric NULLADDR, &e->e_errorqueue, 0, e); 23563841Seric } 23667939Seric if (!emptyaddr(&e->e_from)) 23763841Seric { 23858680Seric (void) sendtolist(e->e_from.q_paddr, 23967982Seric NULLADDR, &e->e_errorqueue, 0, e); 24063841Seric } 24158680Seric 24263841Seric /* 24363841Seric ** Deliver a non-delivery report to the 24463841Seric ** Postmaster-designate (not necessarily 24563841Seric ** Postmaster). This does not include the 24663841Seric ** body of the message, for privacy reasons. 24763841Seric ** You really shouldn't need this. 24863841Seric */ 24963841Seric 25063849Seric e->e_flags |= EF_PM_NOTIFY; 25158178Seric 25264792Seric /* check to see if there are any good addresses */ 25364792Seric for (q = e->e_errorqueue; q != NULL; q = q->q_next) 25464792Seric if (!bitset(QBADADDR|QDONTSEND, q->q_flags)) 25564792Seric break; 25658680Seric if (q == NULL) 25758680Seric { 25858680Seric /* this is an error-error */ 25958680Seric state = ESM_POSTMASTER; 26058680Seric break; 26158680Seric } 26266303Seric if (returntosender(e->e_message, e->e_errorqueue, 26367981Seric sendbody, e) == 0) 26458680Seric { 26558680Seric state = ESM_DONE; 26658680Seric break; 26758680Seric } 26824981Seric 26958680Seric /* didn't work -- return to postmaster */ 27058680Seric state = ESM_POSTMASTER; 27158680Seric break; 27258306Seric 27358680Seric case ESM_POSTMASTER: 27458680Seric /* 27558680Seric ** Similar to previous case, but to system postmaster. 27658680Seric */ 27758680Seric 27859432Seric q = NULL; 27967982Seric if (sendtolist("postmaster", NULL, &q, 0, e) <= 0) 28024942Seric { 28158680Seric syserr("553 cannot parse postmaster!"); 28258680Seric ExitStat = EX_SOFTWARE; 28358680Seric state = ESM_USRTMP; 28458680Seric break; 28524942Seric } 28667981Seric if (returntosender(e->e_message, q, sendbody, e) == 0) 28724942Seric { 28824942Seric state = ESM_DONE; 28924942Seric break; 29024942Seric } 291297Seric 29258680Seric /* didn't work -- last resort */ 29358680Seric state = ESM_USRTMP; 29424942Seric break; 295297Seric 29624942Seric case ESM_DEADLETTER: 29724942Seric /* 29824942Seric ** Save the message in dead.letter. 29924942Seric ** If we weren't mailing back, and the user is 30024942Seric ** local, we should save the message in 30124942Seric ** ~/dead.letter so that the poor person doesn't 30224942Seric ** have to type it over again -- and we all know 30324942Seric ** what poor typists UNIX users are. 30424942Seric */ 3055315Seric 30624942Seric p = NULL; 30767473Seric if (bitnset(M_HASPWENT, e->e_from.q_mailer->m_flags)) 30824942Seric { 30924942Seric if (e->e_from.q_home != NULL) 31024942Seric p = e->e_from.q_home; 311*68693Seric else if ((pw = sm_getpwnam(e->e_from.q_user)) != NULL) 31224942Seric p = pw->pw_dir; 31324942Seric } 31424942Seric if (p == NULL) 31524942Seric { 31658865Seric /* no local directory */ 31724942Seric state = ESM_MAIL; 31824942Seric break; 31924942Seric } 32024942Seric if (e->e_dfp != NULL) 32124942Seric { 32224942Seric bool oldverb = Verbose; 32324942Seric 32424942Seric /* we have a home directory; open dead.letter */ 32524942Seric define('z', p, e); 32668529Seric expand("\201z/dead.letter", buf, sizeof buf, e); 32724942Seric Verbose = TRUE; 32858151Seric message("Saving message in %s", buf); 32924942Seric Verbose = oldverb; 33024942Seric e->e_to = buf; 33124942Seric q = NULL; 33267982Seric (void) sendtolist(buf, &e->e_from, &q, 0, e); 33364354Seric if (q != NULL && 33464354Seric !bitset(QBADADDR, q->q_flags) && 33564354Seric deliver(e, q) == 0) 33624942Seric state = ESM_DONE; 33724942Seric else 33824942Seric state = ESM_MAIL; 33924942Seric } 34025569Seric else 34125569Seric { 34225569Seric /* no data file -- try mailing back */ 34325569Seric state = ESM_MAIL; 34425569Seric } 34524942Seric break; 34624942Seric 34724942Seric case ESM_USRTMP: 34824942Seric /* 34924942Seric ** Log the mail in /usr/tmp/dead.letter. 35024942Seric */ 35124942Seric 35257438Seric if (e->e_class < 0) 35357438Seric { 35457438Seric state = ESM_DONE; 35557438Seric break; 35657438Seric } 35757438Seric 35868490Seric if (SafeFileEnv != NULL && SafeFileEnv[0] != '\0') 35968490Seric { 36068490Seric state = ESM_PANIC; 36168490Seric break; 36268490Seric } 36368490Seric 36465174Seric strcpy(buf, _PATH_VARTMP); 36565174Seric strcat(buf, "dead.letter"); 36668494Seric if (!writable(buf, NULLADDR, SFF_NOSLINK|SFF_CREAT)) 36764945Seric { 36864945Seric state = ESM_PANIC; 36964945Seric break; 37064945Seric } 37168494Seric fp = safefopen(buf, O_WRONLY|O_CREAT|O_APPEND, 37268513Seric FileMode, SFF_NOSLINK|SFF_REGONLY); 37324942Seric if (fp == NULL) 37424942Seric { 37524942Seric state = ESM_PANIC; 37624942Seric break; 37724942Seric } 37824942Seric 37965870Seric bzero(&mcibuf, sizeof mcibuf); 38065870Seric mcibuf.mci_out = fp; 38165870Seric mcibuf.mci_mailer = FileMailer; 38265870Seric if (bitnset(M_7BITS, FileMailer->m_flags)) 38365870Seric mcibuf.mci_flags |= MCIF_7BIT; 38465870Seric 38565870Seric putfromline(&mcibuf, e); 38668228Seric (*e->e_puthdr)(&mcibuf, e->e_header, e); 38768228Seric (*e->e_putbody)(&mcibuf, e, NULL); 38865870Seric putline("\n", &mcibuf); 38924942Seric (void) fflush(fp); 39024942Seric state = ferror(fp) ? ESM_PANIC : ESM_DONE; 39167809Seric (void) xfclose(fp, "savemail", buf); 39224942Seric break; 39324942Seric 39424942Seric default: 39558151Seric syserr("554 savemail: unknown state %d", state); 39624942Seric 39724942Seric /* fall through ... */ 39824942Seric 39924942Seric case ESM_PANIC: 40024942Seric /* leave the locked queue & transcript files around */ 40168490Seric loseqfile(e, "savemail panic"); 40264949Seric syserr("!554 savemail: cannot save rejected email anywhere"); 40324942Seric } 404297Seric } 405297Seric } 406297Seric /* 4074633Seric ** RETURNTOSENDER -- return a message to the sender with an error. 4084633Seric ** 4094633Seric ** Parameters: 4104633Seric ** msg -- the explanatory message. 41116479Seric ** returnq -- the queue of people to send the message to. 4125984Seric ** sendbody -- if TRUE, also send back the body of the 4135984Seric ** message; otherwise just send the header. 41455012Seric ** e -- the current envelope. 4154633Seric ** 4164633Seric ** Returns: 4174633Seric ** zero -- if everything went ok. 4184633Seric ** else -- some error. 4194633Seric ** 4204633Seric ** Side Effects: 4214633Seric ** Returns the current message to the sender via 4224633Seric ** mail. 4234633Seric */ 4244633Seric 4257045Seric #define MAXRETURNS 6 /* max depth of returning messages */ 42658559Seric #define ERRORFUDGE 100 /* nominal size of error message text */ 4277045Seric 42855012Seric returntosender(msg, returnq, sendbody, e) 4294633Seric char *msg; 43016479Seric ADDRESS *returnq; 4315984Seric bool sendbody; 43255012Seric register ENVELOPE *e; 4334633Seric { 43468528Seric char buf[MAXNAME + 1]; 4356978Seric extern putheader(), errbody(); 4366978Seric register ENVELOPE *ee; 43758680Seric ENVELOPE *oldcur = CurEnv; 4386978Seric ENVELOPE errenvelope; 4397045Seric static int returndepth; 4409375Seric register ADDRESS *q; 44168228Seric char *p; 4424633Seric 44360010Seric if (returnq == NULL) 44460010Seric return (-1); 44560010Seric 44658966Seric if (msg == NULL) 44758966Seric msg = "Unable to deliver mail"; 44858966Seric 4497676Seric if (tTd(6, 1)) 4507287Seric { 45167987Seric printf("\n*** Return To Sender: msg=\"%s\", depth=%d, e=%x, returnq=", 45255012Seric msg, returndepth, e); 45316479Seric printaddr(returnq, TRUE); 45467987Seric if (tTd(6, 20)) 45567987Seric { 45667987Seric printf("Sendq="); 45767987Seric printaddr(e->e_sendqueue, TRUE); 45867987Seric } 4597287Seric } 4607287Seric 4617045Seric if (++returndepth >= MAXRETURNS) 4627045Seric { 4637045Seric if (returndepth != MAXRETURNS) 46458151Seric syserr("554 returntosender: infinite recursion on %s", returnq->q_paddr); 4657045Seric /* don't "unrecurse" and fake a clean exit */ 4667045Seric /* returndepth--; */ 4677045Seric return (0); 4687045Seric } 4697045Seric 47058680Seric define('g', e->e_from.q_paddr, e); 47164940Seric define('u', NULL, e); 47267880Seric 47367880Seric /* initialize error envelope */ 47458179Seric ee = newenvelope(&errenvelope, e); 47558050Seric define('a', "\201b", ee); 47659057Seric define('r', "internal", ee); 47759057Seric define('s', "localhost", ee); 47859057Seric define('_', "localhost", ee); 4796978Seric ee->e_puthdr = putheader; 4806978Seric ee->e_putbody = errbody; 48164120Seric ee->e_flags |= EF_RESPONSE|EF_METOO; 48255012Seric if (!bitset(EF_OLDSTYLE, e->e_flags)) 48345155Seric ee->e_flags &= ~EF_OLDSTYLE; 48416479Seric ee->e_sendqueue = returnq; 48564655Seric ee->e_msgsize = ERRORFUDGE; 48668559Seric if (sendbody) 48764655Seric ee->e_msgsize += e->e_msgsize; 48864737Seric initsys(ee); 48916479Seric for (q = returnq; q != NULL; q = q->q_next) 4909375Seric { 49159989Seric if (bitset(QBADADDR, q->q_flags)) 49258559Seric continue; 49358559Seric 49468141Seric if (!DontPruneRoutes && pruneroute(q->q_paddr)) 49568141Seric { 49668141Seric register ADDRESS *p; 49768141Seric 49868141Seric parseaddr(q->q_paddr, q, RF_COPYPARSE, '\0', NULL, e); 49968141Seric for (p = returnq; p != NULL; p = p->q_next) 50068141Seric { 50168141Seric if (p != q && sameaddr(p, q)) 50268141Seric q->q_flags |= QDONTSEND; 50368141Seric } 50468141Seric } 50568141Seric 50659989Seric if (!bitset(QDONTSEND, q->q_flags)) 50759989Seric ee->e_nrcpts++; 50858559Seric 5099375Seric if (q->q_alias == NULL) 51067546Seric addheader("To", q->q_paddr, &ee->e_header); 5119375Seric } 51224942Seric 51357642Seric # ifdef LOG 51458020Seric if (LogLevel > 5) 51565054Seric syslog(LOG_INFO, "%s: %s: return to sender: %s", 51657642Seric e->e_id, ee->e_id, msg); 51757642Seric # endif 51857642Seric 51968003Seric if (SendMIMEErrors) 52067261Seric { 52168003Seric addheader("MIME-Version", "1.0", &ee->e_header); 52268003Seric (void) sprintf(buf, "%s.%ld/%s", 52368003Seric ee->e_id, curtime(), MyHostName); 52468003Seric ee->e_msgboundary = newstr(buf); 52568003Seric (void) sprintf(buf, 52668028Seric #ifdef DSN 52768085Seric "multipart/report; report-type=X-delivery-status-1; boundary=\"%s\"", 52868028Seric #else 52968028Seric "multipart/mixed; boundary=\"%s\"", 53068028Seric #endif 53168003Seric ee->e_msgboundary); 53268003Seric addheader("Content-Type", buf, &ee->e_header); 53368003Seric } 53468228Seric if (strncmp(msg, "Warning:", 8) == 0) 53568003Seric { 53667261Seric addheader("Subject", msg, ee); 53768228Seric p = "warning-timeout"; 53867261Seric } 53967429Seric else if (strcmp(msg, "Return receipt") == 0) 54067429Seric { 54167429Seric addheader("Subject", msg, ee); 54268228Seric p = "return-receipt"; 54367429Seric } 54467261Seric else 54567261Seric { 54667261Seric sprintf(buf, "Returned mail: %.*s", sizeof buf - 20, msg); 54767546Seric addheader("Subject", buf, &ee->e_header); 54868228Seric p = "failure"; 54967261Seric } 55068228Seric (void) sprintf(buf, "auto-generated (%s)", p); 55168228Seric addheader("Auto-Submitted", buf, &ee->e_header); 5524633Seric 5534633Seric /* fake up an address header for the from person */ 55468529Seric expand("\201n", buf, sizeof buf, e); 55564284Seric if (parseaddr(buf, &ee->e_from, RF_COPYALL|RF_SENDERADDR, '\0', NULL, e) == NULL) 5564633Seric { 55758151Seric syserr("553 Can't parse myself!"); 5584633Seric ExitStat = EX_SOFTWARE; 5597045Seric returndepth--; 5604633Seric return (-1); 5614633Seric } 56258704Seric ee->e_sender = ee->e_from.q_paddr; 5635984Seric 5646978Seric /* push state into submessage */ 5656978Seric CurEnv = ee; 56658050Seric define('f', "\201n", ee); 5679375Seric define('x', "Mail Delivery Subsystem", ee); 56858929Seric eatheader(ee, TRUE); 5695984Seric 57063753Seric /* mark statistics */ 57164284Seric markstats(ee, NULLADDR); 57263753Seric 5736978Seric /* actually deliver the error message */ 57414876Seric sendall(ee, SM_DEFAULT); 5756978Seric 5766978Seric /* restore state */ 5777811Seric dropenvelope(ee); 57858680Seric CurEnv = oldcur; 5797045Seric returndepth--; 5806978Seric 5817045Seric /* should check for delivery errors here */ 5824633Seric return (0); 5834633Seric } 5844633Seric /* 5856978Seric ** ERRBODY -- output the body of an error message. 5866978Seric ** 5876978Seric ** Typically this is a copy of the transcript plus a copy of the 5886978Seric ** original offending message. 5896978Seric ** 590297Seric ** Parameters: 59165870Seric ** mci -- the mailer connection information. 5929542Seric ** e -- the envelope we are working in. 59367546Seric ** separator -- any possible MIME separator. 59467936Seric ** flags -- to modify the behaviour. 595297Seric ** 596297Seric ** Returns: 597297Seric ** none 598297Seric ** 599297Seric ** Side Effects: 6006978Seric ** Outputs the body of an error message. 601297Seric */ 602297Seric 60368228Seric errbody(mci, e, separator) 60465870Seric register MCI *mci; 6059542Seric register ENVELOPE *e; 60667546Seric char *separator; 607297Seric { 6086978Seric register FILE *xfile; 60959082Seric char *p; 61059082Seric register ADDRESS *q; 61159082Seric bool printheader; 61268559Seric bool sendbody; 6133189Seric char buf[MAXLINE]; 61468228Seric extern char *xtextify(); 615297Seric 61667546Seric if (bitset(MCIF_INHEADER, mci->mci_flags)) 61767546Seric { 61867546Seric putline("", mci); 61967546Seric mci->mci_flags &= ~MCIF_INHEADER; 62067546Seric } 62158680Seric if (e->e_parent == NULL) 62258680Seric { 62358680Seric syserr("errbody: null parent"); 62465870Seric putline(" ----- Original message lost -----\n", mci); 62558680Seric return; 62658680Seric } 62758680Seric 6289057Seric /* 62959730Seric ** Output MIME header. 63059730Seric */ 63159730Seric 63259730Seric if (e->e_msgboundary != NULL) 63359730Seric { 63465870Seric putline("This is a MIME-encapsulated message", mci); 63565870Seric putline("", mci); 63659730Seric (void) sprintf(buf, "--%s", e->e_msgboundary); 63765870Seric putline(buf, mci); 63865870Seric putline("", mci); 63959730Seric } 64059730Seric 64159730Seric /* 64263852Seric ** Output introductory information. 64363852Seric */ 64463852Seric 64564718Seric for (q = e->e_parent->e_sendqueue; q != NULL; q = q->q_next) 64664718Seric if (bitset(QBADADDR, q->q_flags)) 64764718Seric break; 64865054Seric if (q == NULL && 64965054Seric !bitset(EF_FATALERRS|EF_SENDRECEIPT, e->e_parent->e_flags)) 65064718Seric { 65164718Seric putline(" **********************************************", 65265870Seric mci); 65364718Seric putline(" ** THIS IS A WARNING MESSAGE ONLY **", 65465870Seric mci); 65564718Seric putline(" ** YOU DO NOT NEED TO RESEND YOUR MESSAGE **", 65665870Seric mci); 65764718Seric putline(" **********************************************", 65865870Seric mci); 65965870Seric putline("", mci); 66064718Seric } 66164718Seric sprintf(buf, "The original message was received at %s", 66264718Seric arpadate(ctime(&e->e_parent->e_ctime))); 66365870Seric putline(buf, mci); 66468529Seric expand("from \201_", buf, sizeof buf, e->e_parent); 66565870Seric putline(buf, mci); 66665870Seric putline("", mci); 66763852Seric 66863852Seric /* 66955372Seric ** Output error message header (if specified and available). 67055372Seric */ 67155372Seric 67267682Seric if (ErrMsgFile != NULL && !bitset(EF_SENDRECEIPT, e->e_parent->e_flags)) 67355372Seric { 67455372Seric if (*ErrMsgFile == '/') 67555372Seric { 67655372Seric xfile = fopen(ErrMsgFile, "r"); 67755372Seric if (xfile != NULL) 67855372Seric { 67955372Seric while (fgets(buf, sizeof buf, xfile) != NULL) 68055425Seric { 68168529Seric expand(buf, buf, sizeof buf, e); 68265870Seric putline(buf, mci); 68355425Seric } 68455372Seric (void) fclose(xfile); 68565870Seric putline("\n", mci); 68655372Seric } 68755372Seric } 68855372Seric else 68955372Seric { 69068529Seric expand(ErrMsgFile, buf, sizeof buf, e); 69165870Seric putline(buf, mci); 69265870Seric putline("", mci); 69355372Seric } 69455372Seric } 69555372Seric 69655372Seric /* 69759082Seric ** Output message introduction 69859082Seric */ 69959082Seric 70059082Seric printheader = TRUE; 70159082Seric for (q = e->e_parent->e_sendqueue; q != NULL; q = q->q_next) 70259082Seric { 70368603Seric if (bitset(QBADADDR|QREPORT|QRELAYED|QEXPLODED, q->q_flags)) 70459082Seric { 70563849Seric strcpy(buf, q->q_paddr); 70663787Seric if (bitset(QBADADDR, q->q_flags)) 70763849Seric strcat(buf, " (unrecoverable error)"); 70868603Seric else if (!bitset(QPRIMARY, q->q_flags)) 70968603Seric continue; 71067981Seric else if (bitset(QRELAYED, q->q_flags)) 71167981Seric strcat(buf, " (relayed to non-DSN-aware mailer)"); 71267880Seric else if (bitset(QSENT, q->q_flags)) 71367880Seric strcat(buf, " (successfully delivered)"); 71468603Seric else if (bitset(QEXPLODED, q->q_flags)) 71568603Seric strcat(buf, " (expanded by mailing list)"); 71663787Seric else 71763849Seric strcat(buf, " (transient failure)"); 71868603Seric if (printheader) 71968603Seric { 72068603Seric putline(" ----- The following addresses have delivery notifications -----", 72168603Seric mci); 72268603Seric printheader = FALSE; 72368603Seric } 72465870Seric putline(buf, mci); 72563849Seric if (q->q_alias != NULL) 72663849Seric { 72763849Seric strcpy(buf, " (expanded from: "); 72863849Seric strcat(buf, q->q_alias->q_paddr); 72963849Seric strcat(buf, ")"); 73065870Seric putline(buf, mci); 73163849Seric } 73259082Seric } 73359082Seric } 73459082Seric if (!printheader) 73565870Seric putline("\n", mci); 73659082Seric 73759082Seric /* 7389057Seric ** Output transcript of errors 7399057Seric */ 7409057Seric 7414086Seric (void) fflush(stdout); 7429542Seric p = queuename(e->e_parent, 'x'); 7439337Seric if ((xfile = fopen(p, "r")) == NULL) 7449057Seric { 7459337Seric syserr("Cannot open %s", p); 74665870Seric putline(" ----- Transcript of session is unavailable -----\n", mci); 7479057Seric } 7489057Seric else 7499057Seric { 75065870Seric putline(" ----- Transcript of session follows -----\n", mci); 7519542Seric if (e->e_xfp != NULL) 7529542Seric (void) fflush(e->e_xfp); 7539057Seric while (fgets(buf, sizeof buf, xfile) != NULL) 75465870Seric putline(buf, mci); 75558680Seric (void) xfclose(xfile, "errbody xscript", p); 7569057Seric } 757297Seric errno = 0; 7584318Seric 75968028Seric #ifdef DSN 7604318Seric /* 76167880Seric ** Output machine-readable version. 76267880Seric */ 76367880Seric 76467880Seric if (e->e_msgboundary != NULL) 76567880Seric { 76667880Seric putline("", mci); 76767880Seric (void) sprintf(buf, "--%s", e->e_msgboundary); 76867880Seric putline(buf, mci); 76968606Seric putline("Content-Type: message/X-delivery-status-04 (Draft of 20 January 1995)", mci); 77067880Seric putline("", mci); 77167880Seric 77267880Seric /* 77367880Seric ** Output per-message information. 77467880Seric */ 77567880Seric 77667880Seric /* original envelope id from MAIL FROM: line */ 77767880Seric if (e->e_parent->e_envid != NULL) 77867880Seric { 77967880Seric (void) sprintf(buf, "Original-Envelope-Id: %s", 78068228Seric xtextify(e->e_parent->e_envid)); 78167880Seric putline(buf, mci); 78267880Seric } 78367880Seric 78468228Seric /* Reporting-MTA: is us (required) */ 78568228Seric p = e->e_parent->e_from.q_mailer->m_mtatype; 78668228Seric if (p == NULL) 78768228Seric p = "dns"; 78868583Seric (void) sprintf(buf, "Reporting-MTA: %s; %s", p, 78968583Seric xtextify(MyHostName)); 79067880Seric putline(buf, mci); 79167880Seric 79268228Seric /* Received-From-MTA: shows where we got this message from */ 79367990Seric if (RealHostName != NULL) 79467990Seric { 79568228Seric /* XXX use $s for type? */ 79668228Seric p = e->e_parent->e_from.q_mailer->m_mtatype; 79768228Seric if (p == NULL) 79868228Seric p = "dns"; 79968228Seric (void) sprintf(buf, "Received-From-MTA: %s; %s", 80068583Seric p, xtextify(RealHostName)); 80167990Seric putline(buf, mci); 80267990Seric } 80367963Seric 80467963Seric /* Arrival-Date: -- when it arrived here */ 80567963Seric (void) sprintf(buf, "Arrival-Date: %s", 80667963Seric arpadate(ctime(&e->e_parent->e_ctime))); 80767963Seric putline(buf, mci); 80867963Seric 80967880Seric /* 81067880Seric ** Output per-address information. 81167880Seric */ 81267880Seric 81367880Seric for (q = e->e_parent->e_sendqueue; q != NULL; q = q->q_next) 81467880Seric { 81567880Seric register ADDRESS *r; 81668603Seric char *action; 81767880Seric 81868603Seric if (bitset(QBADADDR, q->q_flags)) 81968603Seric action = "failure"; 82068603Seric else if (!bitset(QPRIMARY, q->q_flags)) 82167880Seric continue; 82268603Seric else if (bitset(QRELAYED, q->q_flags)) 82368603Seric action = "relayed"; 82468603Seric else if (bitset(QEXPLODED, q->q_flags)) 82568603Seric action = "delivered (to mailing list)"; 82668603Seric else if (bitset(QSENT, q->q_flags) && 82768603Seric bitnset(M_LOCALMAILER, q->q_mailer->m_flags)) 82868603Seric action = "delivered (final delivery)"; 82968603Seric else if (bitset(QREPORT, q->q_flags)) 83068603Seric action = "delayed"; 83168603Seric else 83268603Seric continue; 83368603Seric 83467880Seric putline("", mci); 83567880Seric 83668228Seric /* Original-Recipient: -- passed from on high */ 83768228Seric if (q->q_orcpt != NULL) 83868228Seric { 83968228Seric (void) sprintf(buf, "Original-Recipient: %s", 84068228Seric xtextify(q->q_orcpt)); 84168228Seric putline(buf, mci); 84268228Seric } 84368228Seric 84468228Seric /* Final-Recipient: -- the name from the RCPT command */ 84568228Seric p = e->e_parent->e_from.q_mailer->m_addrtype; 84668228Seric if (p == NULL) 84768228Seric p = "rfc822"; 84868228Seric for (r = q; r->q_alias != NULL; r = r->q_alias) 84968228Seric continue; 85068228Seric if (strchr(r->q_user, '@') == NULL) 85168583Seric { 85268583Seric (void) sprintf(buf, "Final-Recipient: %s; %s@", 85368583Seric p, xtextify(r->q_user)); 85468583Seric strcat(buf, xtextify(MyHostName)); 85568583Seric } 85667998Seric else 85768583Seric { 85868228Seric (void) sprintf(buf, "Final-Recipient: %s; %s", 85968228Seric p, xtextify(r->q_user)); 86068583Seric } 86167880Seric putline(buf, mci); 86267880Seric 86368603Seric /* X-Actual-Recipient: -- the real problem address */ 86468603Seric if (r != q) 86568603Seric { 86668603Seric if (strchr(q->q_user, '@') == NULL) 86768603Seric { 86868603Seric (void) sprintf(buf, "X-Actual-Recipient: %s; %s@", 86968603Seric p, xtextify(q->q_user)); 87068603Seric strcat(buf, xtextify(MyHostName)); 87168603Seric } 87268603Seric else 87368603Seric { 87468603Seric (void) sprintf(buf, "X-Actual-Recipient: %s; %s", 87568603Seric p, xtextify(q->q_user)); 87668603Seric } 87768603Seric putline(buf, mci); 87868603Seric } 87968603Seric 88067880Seric /* Action: -- what happened? */ 88168603Seric sprintf(buf, "Action: %s", action); 88268603Seric putline(buf, mci); 88367880Seric 88467880Seric /* Status: -- what _really_ happened? */ 88567880Seric strcpy(buf, "Status: "); 88667880Seric if (q->q_status != NULL) 88767880Seric strcat(buf, q->q_status); 88867880Seric else if (bitset(QBADADDR, q->q_flags)) 88968228Seric strcat(buf, "5.0.0"); 89067880Seric else if (bitset(QQUEUEUP, q->q_flags)) 89168228Seric strcat(buf, "4.0.0"); 89267880Seric else 89368228Seric strcat(buf, "2.0.0"); 89467880Seric putline(buf, mci); 89567880Seric 89668228Seric /* Remote-MTA: -- who was I talking to? */ 89768228Seric p = q->q_mailer->m_mtatype; 89868228Seric if (p == NULL) 89968228Seric p = "dns"; 90068228Seric (void) sprintf(buf, "Remote-MTA: %s; ", p); 90168228Seric if (q->q_statmta != NULL) 90268228Seric p = q->q_statmta; 90368603Seric else if (q->q_host != NULL && q->q_host[0] != '\0') 90468228Seric p = q->q_host; 90568228Seric else 90668228Seric p = NULL; 90768228Seric if (p != NULL) 90868228Seric { 90968228Seric strcat(buf, p); 91068228Seric p = &buf[strlen(buf) - 1]; 91168228Seric if (*p == '.') 91268228Seric *p = '\0'; 91368228Seric putline(buf, mci); 91468228Seric } 91568228Seric 91668228Seric /* Diagnostic-Code: -- actual result from other end */ 91768228Seric if (q->q_rstatus != NULL) 91868228Seric { 91968228Seric p = q->q_mailer->m_diagtype; 92068228Seric if (p == NULL) 92168228Seric p = "smtp"; 92268228Seric (void) sprintf(buf, "Diagnostic-Code: %s; %s", 92368603Seric p, q->q_rstatus); 92468228Seric putline(buf, mci); 92568228Seric } 92668228Seric 92768228Seric /* Last-Attempt-Date: -- fine granularity */ 92867880Seric if (q->q_statdate == (time_t) 0L) 92967880Seric q->q_statdate = curtime(); 93068228Seric (void) sprintf(buf, "Last-Attempt-Date: %s", 93167880Seric arpadate(ctime(&q->q_statdate))); 93267880Seric putline(buf, mci); 93367880Seric 93467963Seric /* Expiry-Date: -- for delayed messages only */ 93567963Seric if (bitset(QQUEUEUP, q->q_flags) && 93667963Seric !bitset(QBADADDR, q->q_flags)) 93767963Seric { 93867963Seric time_t xdate; 93967963Seric 94067963Seric xdate = e->e_ctime + TimeOuts.to_q_return[e->e_timeoutclass]; 94167963Seric sprintf(buf, "Expiry-Date: %s", 94267963Seric arpadate(ctime(&xdate))); 94367963Seric putline(buf, mci); 94467963Seric } 94567880Seric } 94667880Seric } 94768028Seric #endif 94867880Seric 94967880Seric /* 9504318Seric ** Output text of original message 9514318Seric */ 9524318Seric 95365870Seric putline("", mci); 95468564Seric if (bitset(EF_HAS_DF, e->e_parent->e_flags)) 9554199Seric { 95668559Seric sendbody = !bitset(EF_NO_BODY_RETN, e->e_parent->e_flags); 95768559Seric 95867880Seric if (e->e_msgboundary == NULL) 95967880Seric { 96068559Seric if (sendbody) 96167880Seric putline(" ----- Original message follows -----\n", mci); 96267880Seric else 96367880Seric putline(" ----- Message header follows -----\n", mci); 96467880Seric (void) fflush(mci->mci_out); 96567880Seric } 9665984Seric else 9675984Seric { 96859730Seric (void) sprintf(buf, "--%s", e->e_msgboundary); 96965870Seric putline(buf, mci); 97068228Seric (void) sprintf(buf, "Content-Type: message/rfc822%s", 97168559Seric mci, sendbody ? "" : "-headers"); 97268228Seric putline(buf, mci); 9735984Seric } 97467981Seric putline("", mci); 97568228Seric putheader(mci, e->e_parent->e_header, e->e_parent); 97668559Seric if (sendbody) 97768228Seric putbody(mci, e->e_parent, e->e_msgboundary); 97868228Seric else if (e->e_msgboundary == NULL) 97967546Seric { 98067546Seric putline("", mci); 98165870Seric putline(" ----- Message body suppressed -----", mci); 98267546Seric } 9834199Seric } 98468228Seric else if (e->e_msgboundary == NULL) 98510170Seric { 98665870Seric putline(" ----- No message was collected -----\n", mci); 98710170Seric } 9884318Seric 98960010Seric if (e->e_msgboundary != NULL) 99060010Seric { 99165870Seric putline("", mci); 99260010Seric (void) sprintf(buf, "--%s--", e->e_msgboundary); 99365870Seric putline(buf, mci); 99460010Seric } 99565870Seric putline("", mci); 99659730Seric 9974318Seric /* 9984318Seric ** Cleanup and exit 9994318Seric */ 10004318Seric 1001297Seric if (errno != 0) 10026978Seric syserr("errbody: I/O error"); 1003297Seric } 100458144Seric /* 100568228Seric ** SMTPTODSN -- convert SMTP to DSN status code 100668228Seric ** 100768228Seric ** Parameters: 100868228Seric ** smtpstat -- the smtp status code (e.g., 550). 100968228Seric ** 101068228Seric ** Returns: 101168228Seric ** The DSN version of the status code. 101268228Seric */ 101368228Seric 101468228Seric char * 101568228Seric smtptodsn(smtpstat) 101668228Seric int smtpstat; 101768228Seric { 101868228Seric switch (smtpstat) 101968228Seric { 102068228Seric case 450: /* Req mail action not taken: mailbox unavailable */ 102168228Seric return "4.2.0"; 102268228Seric 102368228Seric case 451: /* Req action aborted: local error in processing */ 102468228Seric return "4.3.0"; 102568228Seric 102668228Seric case 452: /* Req action not taken: insufficient sys storage */ 102768228Seric return "4.3.1"; 102868228Seric 102968228Seric case 500: /* Syntax error, command unrecognized */ 103068228Seric return "5.5.2"; 103168228Seric 103268228Seric case 501: /* Syntax error in parameters or arguments */ 103368228Seric return "5.5.4"; 103468228Seric 103568228Seric case 502: /* Command not implemented */ 103668228Seric return "5.5.1"; 103768228Seric 103868228Seric case 503: /* Bad sequence of commands */ 103968228Seric return "5.5.1"; 104068228Seric 104168228Seric case 504: /* Command parameter not implemented */ 104268228Seric return "5.5.4"; 104368228Seric 104468228Seric case 550: /* Req mail action not taken: mailbox unavailable */ 104568228Seric return "5.2.0"; 104668228Seric 104768228Seric case 551: /* User not local; please try <...> */ 104868228Seric return "5.1.6"; 104968228Seric 105068228Seric case 552: /* Req mail action aborted: exceeded storage alloc */ 105168228Seric return "5.2.2"; 105268228Seric 105368228Seric case 553: /* Req action not taken: mailbox name not allowed */ 105468228Seric return "5.1.3"; 105568228Seric 105668228Seric case 554: /* Transaction failed */ 105768228Seric return "5.0.0"; 105868228Seric } 105968228Seric 106068228Seric if ((smtpstat / 100) == 2) 106168228Seric return "2.0.0"; 106268228Seric if ((smtpstat / 100) == 4) 106368228Seric return "4.0.0"; 106468228Seric return "5.0.0"; 106568228Seric } 106668228Seric /* 106768228Seric ** XTEXTIFY -- take regular text and turn it into DSN-style xtext 106868228Seric ** 106968228Seric ** Parameters: 107068228Seric ** t -- the text to convert. 107168228Seric ** 107268228Seric ** Returns: 107368228Seric ** The xtext-ified version of the same string. 107468228Seric */ 107568228Seric 107668228Seric char * 107768228Seric xtextify(t) 107868228Seric register char *t; 107968228Seric { 108068228Seric register char *p; 108168228Seric int l; 108268228Seric int nbogus; 108368228Seric static char *bp = NULL; 108468228Seric static int bplen = 0; 108568228Seric 108668228Seric /* figure out how long this xtext will have to be */ 108768228Seric nbogus = l = 0; 108868228Seric for (p = t; *p != '\0'; p++) 108968228Seric { 109068228Seric register int c = (*p & 0xff); 109168228Seric 109268228Seric /* ASCII dependence here -- this is the way the spec words it */ 109368603Seric if ((c < ' ' || c > '~' || c == '+' || c == '\\' || c == '(') && 109468603Seric c != '\t') 109568228Seric nbogus++; 109668228Seric l++; 109768228Seric } 109868228Seric if (nbogus == 0) 109968228Seric return t; 110068228Seric l += nbogus * 2 + 1; 110168228Seric 110268228Seric /* now allocate space if necessary for the new string */ 110368228Seric if (l > bplen) 110468228Seric { 110568228Seric if (bp != NULL) 110668228Seric free(bp); 110768228Seric bp = xalloc(l); 110868228Seric bplen = l; 110968228Seric } 111068228Seric 111168228Seric /* ok, copy the text with byte expansion */ 111268228Seric for (p = bp; *t != '\0'; ) 111368228Seric { 111468228Seric register int c = (*t++ & 0xff); 111568228Seric 111668228Seric /* ASCII dependence here -- this is the way the spec words it */ 111768228Seric if (c < '!' || c > '~' || c == '+' || c == '\\' || c == '(') 111868228Seric { 111968228Seric *p++ = '+'; 112068228Seric *p++ = "0123456789abcdef"[c >> 4]; 112168228Seric *p++ = "0123456789abcdef"[c & 0xf]; 112268228Seric } 112368228Seric else 112468228Seric *p++ = c; 112568228Seric } 112668228Seric *p = '\0'; 112768228Seric return bp; 112868228Seric } 112968228Seric /* 113068583Seric ** XTEXTOK -- check if a string is legal xtext 113168583Seric ** 113268583Seric ** Xtext is used in Delivery Status Notifications. The spec was 113368583Seric ** taken from draft-ietf-notary-mime-delivery-04.txt. 113468583Seric ** 113568583Seric ** Parameters: 113668583Seric ** s -- the string to check. 113768583Seric ** 113868583Seric ** Returns: 113968583Seric ** TRUE -- if 's' is legal xtext. 114068583Seric ** FALSE -- if it has any illegal characters in it. 114168583Seric */ 114268583Seric 114368583Seric bool 114468583Seric xtextok(s) 114568583Seric char *s; 114668583Seric { 114768583Seric int c; 114868583Seric 114968583Seric while ((c = *s++) != '\0') 115068583Seric { 115168583Seric if (c == '+') 115268583Seric { 115368583Seric c = *s++; 115468583Seric if (!isascii(c) || !isxdigit(c)) 115568583Seric return FALSE; 115668583Seric c = *s++; 115768583Seric if (!isascii(c) || !isxdigit(c)) 115868583Seric return FALSE; 115968583Seric } 116068583Seric else if (c < '!' || c > '~' || c == '\\' || c == '(') 116168583Seric return FALSE; 116268583Seric } 116368583Seric return TRUE; 116468583Seric } 116568583Seric /* 116658144Seric ** PRUNEROUTE -- prune an RFC-822 source route 116758144Seric ** 116858144Seric ** Trims down a source route to the last internet-registered hop. 116958144Seric ** This is encouraged by RFC 1123 section 5.3.3. 117058144Seric ** 117158144Seric ** Parameters: 117258144Seric ** addr -- the address 117358144Seric ** 117458144Seric ** Returns: 117558144Seric ** TRUE -- address was modified 117658144Seric ** FALSE -- address could not be pruned 117758144Seric ** 117858144Seric ** Side Effects: 117958144Seric ** modifies addr in-place 118058144Seric */ 118158144Seric 118258144Seric pruneroute(addr) 118358144Seric char *addr; 118458144Seric { 118566334Seric #if NAMED_BIND 118658144Seric char *start, *at, *comma; 118758144Seric char c; 118858144Seric int rcode; 118958144Seric char hostbuf[BUFSIZ]; 119058144Seric char *mxhosts[MAXMXHOSTS + 1]; 119158144Seric 119258144Seric /* check to see if this is really a route-addr */ 119358144Seric if (*addr != '<' || addr[1] != '@' || addr[strlen(addr) - 1] != '>') 119458144Seric return FALSE; 119558144Seric start = strchr(addr, ':'); 119658144Seric at = strrchr(addr, '@'); 119758144Seric if (start == NULL || at == NULL || at < start) 119858144Seric return FALSE; 119958144Seric 120058144Seric /* slice off the angle brackets */ 120158144Seric strcpy(hostbuf, at + 1); 120258144Seric hostbuf[strlen(hostbuf) - 1] = '\0'; 120358144Seric 120458144Seric while (start) 120558144Seric { 120659273Seric if (getmxrr(hostbuf, mxhosts, FALSE, &rcode) > 0) 120758144Seric { 120858144Seric strcpy(addr + 1, start + 1); 120958144Seric return TRUE; 121058144Seric } 121158144Seric c = *start; 121258144Seric *start = '\0'; 121358144Seric comma = strrchr(addr, ','); 121458144Seric if (comma && comma[1] == '@') 121558144Seric strcpy(hostbuf, comma + 2); 121658144Seric else 121758144Seric comma = 0; 121858144Seric *start = c; 121958144Seric start = comma; 122058144Seric } 122158144Seric #endif 122258144Seric return FALSE; 122358144Seric } 1224