source: trunk/third/sendmail/sendmail/savemail.c @ 22421

Revision 22421, 40.1 KB checked in by zacheiss, 19 years ago (diff)
Apply patches from 3-22-06 CERT advisory.
Line 
1/*
2 * Copyright (c) 1998-2002 Sendmail, Inc. and its suppliers.
3 *      All rights reserved.
4 * Copyright (c) 1983, 1995-1997 Eric P. Allman.  All rights reserved.
5 * Copyright (c) 1988, 1993
6 *      The Regents of the University of California.  All rights reserved.
7 *
8 * By using this file, you agree to the terms and conditions set
9 * forth in the LICENSE file which can be found at the top level of
10 * the sendmail distribution.
11 *
12 */
13
14#include <sendmail.h>
15
16SM_RCSID("@(#)$Id: savemail.c,v 1.2 2006-03-23 21:02:46 zacheiss Exp $")
17
18static bool     errbody __P((MCI *, ENVELOPE *, char *));
19static bool     pruneroute __P((char *));
20
21/*
22**  SAVEMAIL -- Save mail on error
23**
24**      If mailing back errors, mail it back to the originator
25**      together with an error message; otherwise, just put it in
26**      dead.letter in the user's home directory (if he exists on
27**      this machine).
28**
29**      Parameters:
30**              e -- the envelope containing the message in error.
31**              sendbody -- if true, also send back the body of the
32**                      message; otherwise just send the header.
33**
34**      Returns:
35**              true if savemail panic'ed, (i.e., the data file should
36**              be preserved by dropenvelope())
37**
38**      Side Effects:
39**              Saves the letter, by writing or mailing it back to the
40**              sender, or by putting it in dead.letter in her home
41**              directory.
42*/
43
44/* defines for state machine */
45#define ESM_REPORT              0       /* report to sender's terminal */
46#define ESM_MAIL                1       /* mail back to sender */
47#define ESM_QUIET               2       /* mail has already been returned */
48#define ESM_DEADLETTER          3       /* save in ~/dead.letter */
49#define ESM_POSTMASTER          4       /* return to postmaster */
50#define ESM_DEADLETTERDROP      5       /* save in DeadLetterDrop */
51#define ESM_PANIC               6       /* call loseqfile() */
52#define ESM_DONE                7       /* message is successfully delivered */
53
54bool
55savemail(e, sendbody)
56        register ENVELOPE *e;
57        bool sendbody;
58{
59        register SM_FILE_T *fp;
60        bool panic = false;
61        int state;
62        auto ADDRESS *q = NULL;
63        register char *p;
64        MCI mcibuf;
65        int flags;
66        long sff;
67        char buf[MAXLINE + 1];
68        char dlbuf[MAXPATHLEN];
69        SM_MBDB_T user;
70
71
72        if (tTd(6, 1))
73        {
74                sm_dprintf("\nsavemail, errormode = %c, id = %s, ExitStat = %d\n  e_from=",
75                        e->e_errormode, e->e_id == NULL ? "NONE" : e->e_id,
76                        ExitStat);
77                printaddr(&e->e_from, false);
78        }
79
80        if (e->e_id == NULL)
81        {
82                /* can't return a message with no id */
83                return panic;
84        }
85
86        /*
87        **  In the unhappy event we don't know who to return the mail
88        **  to, make someone up.
89        */
90
91        if (e->e_from.q_paddr == NULL)
92        {
93                e->e_sender = "Postmaster";
94                if (parseaddr(e->e_sender, &e->e_from,
95                              RF_COPYPARSE|RF_SENDERADDR,
96                              '\0', NULL, e, false) == NULL)
97                {
98                        syserr("553 5.3.5 Cannot parse Postmaster!");
99                        finis(true, true, EX_SOFTWARE);
100                }
101        }
102        e->e_to = NULL;
103
104        /*
105        **  Basic state machine.
106        **
107        **      This machine runs through the following states:
108        **
109        **      ESM_QUIET       Errors have already been printed iff the
110        **                      sender is local.
111        **      ESM_REPORT      Report directly to the sender's terminal.
112        **      ESM_MAIL        Mail response to the sender.
113        **      ESM_DEADLETTER  Save response in ~/dead.letter.
114        **      ESM_POSTMASTER  Mail response to the postmaster.
115        **      ESM_DEADLETTERDROP
116        **                      If DeadLetterDrop set, save it there.
117        **      ESM_PANIC       Save response anywhere possible.
118        */
119
120        /* determine starting state */
121        switch (e->e_errormode)
122        {
123          case EM_WRITE:
124                state = ESM_REPORT;
125                break;
126
127          case EM_BERKNET:
128          case EM_MAIL:
129                state = ESM_MAIL;
130                break;
131
132          case EM_PRINT:
133          case '\0':
134                state = ESM_QUIET;
135                break;
136
137          case EM_QUIET:
138                /* no need to return anything at all */
139                return panic;
140
141          default:
142                syserr("554 5.3.0 savemail: bogus errormode x%x",
143                       e->e_errormode);
144                state = ESM_MAIL;
145                break;
146        }
147
148        /* if this is already an error response, send to postmaster */
149        if (bitset(EF_RESPONSE, e->e_flags))
150        {
151                if (e->e_parent != NULL &&
152                    bitset(EF_RESPONSE, e->e_parent->e_flags))
153                {
154                        /* got an error sending a response -- can it */
155                        return panic;
156                }
157                state = ESM_POSTMASTER;
158        }
159
160        while (state != ESM_DONE)
161        {
162                if (tTd(6, 5))
163                        sm_dprintf("  state %d\n", state);
164
165                switch (state)
166                {
167                  case ESM_QUIET:
168                        if (bitnset(M_LOCALMAILER, e->e_from.q_mailer->m_flags))
169                                state = ESM_DEADLETTER;
170                        else
171                                state = ESM_MAIL;
172                        break;
173
174                  case ESM_REPORT:
175
176                        /*
177                        **  If the user is still logged in on the same terminal,
178                        **  then write the error messages back to hir (sic).
179                        */
180
181                        p = ttypath();
182                        if (p == NULL || sm_io_reopen(SmFtStdio,
183                                                      SM_TIME_DEFAULT,
184                                                      p, SM_IO_WRONLY, NULL,
185                                                      smioout) == NULL)
186                        {
187                                state = ESM_MAIL;
188                                break;
189                        }
190
191                        expand("\201n", buf, sizeof buf, e);
192                        (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
193                                             "\r\nMessage from %s...\r\n", buf);
194                        (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
195                                             "Errors occurred while sending mail.\r\n");
196                        if (e->e_xfp != NULL)
197                        {
198                                (void) bfrewind(e->e_xfp);
199                                (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
200                                                     "Transcript follows:\r\n");
201                                while (sm_io_fgets(e->e_xfp, SM_TIME_DEFAULT,
202                                                   buf, sizeof buf) != NULL &&
203                                       !sm_io_error(smioout))
204                                        (void) sm_io_fputs(smioout,
205                                                           SM_TIME_DEFAULT,
206                                                           buf);
207                        }
208                        else
209                        {
210                                syserr("Cannot open %s",
211                                       queuename(e, XSCRPT_LETTER));
212                                (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
213                                                     "Transcript of session is unavailable.\r\n");
214                        }
215                        (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
216                                             "Original message will be saved in dead.letter.\r\n");
217                        state = ESM_DEADLETTER;
218                        break;
219
220                  case ESM_MAIL:
221                        /*
222                        **  If mailing back, do it.
223                        **      Throw away all further output.  Don't alias,
224                        **      since this could cause loops, e.g., if joe
225                        **      mails to joe@x, and for some reason the network
226                        **      for @x is down, then the response gets sent to
227                        **      joe@x, which gives a response, etc.  Also force
228                        **      the mail to be delivered even if a version of
229                        **      it has already been sent to the sender.
230                        **
231                        **  If this is a configuration or local software
232                        **      error, send to the local postmaster as well,
233                        **      since the originator can't do anything
234                        **      about it anyway.  Note that this is a full
235                        **      copy of the message (intentionally) so that
236                        **      the Postmaster can forward things along.
237                        */
238
239                        if (ExitStat == EX_CONFIG || ExitStat == EX_SOFTWARE)
240                        {
241                                (void) sendtolist("postmaster", NULLADDR,
242                                                  &e->e_errorqueue, 0, e);
243                        }
244                        if (!emptyaddr(&e->e_from))
245                        {
246                                char from[TOBUFSIZE];
247
248                                if (sm_strlcpy(from, e->e_from.q_paddr,
249                                                sizeof from) >= sizeof from)
250                                {
251                                        state = ESM_POSTMASTER;
252                                        break;
253                                }
254
255                                if (!DontPruneRoutes)
256                                        (void) pruneroute(from);
257
258                                (void) sendtolist(from, NULLADDR,
259                                                  &e->e_errorqueue, 0, e);
260                        }
261
262                        /*
263                        **  Deliver a non-delivery report to the
264                        **  Postmaster-designate (not necessarily
265                        **  Postmaster).  This does not include the
266                        **  body of the message, for privacy reasons.
267                        **  You really shouldn't need this.
268                        */
269
270                        e->e_flags |= EF_PM_NOTIFY;
271
272                        /* check to see if there are any good addresses */
273                        for (q = e->e_errorqueue; q != NULL; q = q->q_next)
274                        {
275                                if (QS_IS_SENDABLE(q->q_state))
276                                        break;
277                        }
278                        if (q == NULL)
279                        {
280                                /* this is an error-error */
281                                state = ESM_POSTMASTER;
282                                break;
283                        }
284                        if (returntosender(e->e_message, e->e_errorqueue,
285                                           sendbody ? RTSF_SEND_BODY
286                                                    : RTSF_NO_BODY,
287                                           e) == 0)
288                        {
289                                state = ESM_DONE;
290                                break;
291                        }
292
293                        /* didn't work -- return to postmaster */
294                        state = ESM_POSTMASTER;
295                        break;
296
297                  case ESM_POSTMASTER:
298                        /*
299                        **  Similar to previous case, but to system postmaster.
300                        */
301
302                        q = NULL;
303                        expand(DoubleBounceAddr, buf, sizeof buf, e);
304
305                        /*
306                        **  Just drop it on the floor if DoubleBounceAddr
307                        **  expands to an empty string.
308                        */
309
310                        if (*buf == '\0')
311                        {
312                                state = ESM_DONE;
313                                break;
314                        }
315                        if (sendtolist(buf, NULLADDR, &q, 0, e) <= 0)
316                        {
317                                syserr("553 5.3.0 cannot parse %s!", buf);
318                                ExitStat = EX_SOFTWARE;
319                                state = ESM_DEADLETTERDROP;
320                                break;
321                        }
322                        flags = RTSF_PM_BOUNCE;
323                        if (sendbody)
324                                flags |= RTSF_SEND_BODY;
325                        if (returntosender(e->e_message, q, flags, e) == 0)
326                        {
327                                state = ESM_DONE;
328                                break;
329                        }
330
331                        /* didn't work -- last resort */
332                        state = ESM_DEADLETTERDROP;
333                        break;
334
335                  case ESM_DEADLETTER:
336                        /*
337                        **  Save the message in dead.letter.
338                        **      If we weren't mailing back, and the user is
339                        **      local, we should save the message in
340                        **      ~/dead.letter so that the poor person doesn't
341                        **      have to type it over again -- and we all know
342                        **      what poor typists UNIX users are.
343                        */
344
345                        p = NULL;
346                        if (bitnset(M_HASPWENT, e->e_from.q_mailer->m_flags))
347                        {
348                                if (e->e_from.q_home != NULL)
349                                        p = e->e_from.q_home;
350                                else if (sm_mbdb_lookup(e->e_from.q_user, &user)
351                                         == EX_OK &&
352                                         *user.mbdb_homedir != '\0')
353                                        p = user.mbdb_homedir;
354                        }
355                        if (p == NULL || e->e_dfp == NULL)
356                        {
357                                /* no local directory or no data file */
358                                state = ESM_MAIL;
359                                break;
360                        }
361
362                        /* we have a home directory; write dead.letter */
363                        macdefine(&e->e_macro, A_TEMP, 'z', p);
364
365                        /* get the sender for the UnixFromLine */
366                        p = macvalue('g', e);
367                        macdefine(&e->e_macro, A_PERM, 'g', e->e_sender);
368
369                        expand("\201z/dead.letter", dlbuf, sizeof dlbuf, e);
370                        sff = SFF_CREAT|SFF_REGONLY|SFF_RUNASREALUID;
371                        if (RealUid == 0)
372                                sff |= SFF_ROOTOK;
373                        e->e_to = dlbuf;
374                        if (writable(dlbuf, NULL, sff) &&
375                            mailfile(dlbuf, FileMailer, NULL, sff, e) == EX_OK)
376                        {
377                                int oldverb = Verbose;
378
379                                if (OpMode != MD_DAEMON && OpMode != MD_SMTP)
380                                        Verbose = 1;
381                                if (Verbose > 0)
382                                        message("Saved message in %s", dlbuf);
383                                Verbose = oldverb;
384                                macdefine(&e->e_macro, A_PERM, 'g', p);
385                                state = ESM_DONE;
386                                break;
387                        }
388                        macdefine(&e->e_macro, A_PERM, 'g', p);
389                        state = ESM_MAIL;
390                        break;
391
392                  case ESM_DEADLETTERDROP:
393                        /*
394                        **  Log the mail in DeadLetterDrop file.
395                        */
396
397                        if (e->e_class < 0)
398                        {
399                                state = ESM_DONE;
400                                break;
401                        }
402
403                        if ((SafeFileEnv != NULL && SafeFileEnv[0] != '\0') ||
404                            DeadLetterDrop == NULL ||
405                            DeadLetterDrop[0] == '\0')
406                        {
407                                state = ESM_PANIC;
408                                break;
409                        }
410
411                        sff = SFF_CREAT|SFF_REGONLY|SFF_ROOTOK|SFF_OPENASROOT|SFF_MUSTOWN;
412                        if (!writable(DeadLetterDrop, NULL, sff) ||
413                            (fp = safefopen(DeadLetterDrop, O_WRONLY|O_APPEND,
414                                            FileMode, sff)) == NULL)
415                        {
416                                state = ESM_PANIC;
417                                break;
418                        }
419
420                        memset(&mcibuf, '\0', sizeof mcibuf);
421                        mcibuf.mci_out = fp;
422                        mcibuf.mci_mailer = FileMailer;
423                        if (bitnset(M_7BITS, FileMailer->m_flags))
424                                mcibuf.mci_flags |= MCIF_7BIT;
425
426                        /* get the sender for the UnixFromLine */
427                        p = macvalue('g', e);
428                        macdefine(&e->e_macro, A_PERM, 'g', e->e_sender);
429
430                        if (!putfromline(&mcibuf, e) ||
431                            !(*e->e_puthdr)(&mcibuf, e->e_header, e,
432                                        M87F_OUTER) ||
433                            !(*e->e_putbody)(&mcibuf, e, NULL) ||
434                            !putline("\n", &mcibuf) ||
435                            sm_io_flush(fp, SM_TIME_DEFAULT) == SM_IO_EOF ||
436                            sm_io_error(fp) ||
437                            sm_io_close(fp, SM_TIME_DEFAULT) < 0)
438                                state = ESM_PANIC;
439                        else
440                        {
441                                int oldverb = Verbose;
442
443                                if (OpMode != MD_DAEMON && OpMode != MD_SMTP)
444                                        Verbose = 1;
445                                if (Verbose > 0)
446                                        message("Saved message in %s",
447                                                DeadLetterDrop);
448                                Verbose = oldverb;
449                                if (LogLevel > 3)
450                                        sm_syslog(LOG_NOTICE, e->e_id,
451                                                  "Saved message in %s",
452                                                  DeadLetterDrop);
453                                state = ESM_DONE;
454                        }
455                        macdefine(&e->e_macro, A_PERM, 'g', p);
456                        break;
457
458                  default:
459                        syserr("554 5.3.5 savemail: unknown state %d", state);
460                        /* FALLTHROUGH */
461
462                  case ESM_PANIC:
463                        /* leave the locked queue & transcript files around */
464                        loseqfile(e, "savemail panic");
465                        panic = true;
466                        errno = 0;
467                        syserr("554 savemail: cannot save rejected email anywhere");
468                        state = ESM_DONE;
469                        break;
470                }
471        }
472        return panic;
473}
474/*
475**  RETURNTOSENDER -- return a message to the sender with an error.
476**
477**      Parameters:
478**              msg -- the explanatory message.
479**              returnq -- the queue of people to send the message to.
480**              flags -- flags tweaking the operation:
481**                      RTSF_SENDBODY -- include body of message (otherwise
482**                              just send the header).
483**                      RTSF_PMBOUNCE -- this is a postmaster bounce.
484**              e -- the current envelope.
485**
486**      Returns:
487**              zero -- if everything went ok.
488**              else -- some error.
489**
490**      Side Effects:
491**              Returns the current message to the sender via mail.
492*/
493
494#define MAXRETURNS      6       /* max depth of returning messages */
495#define ERRORFUDGE      1024    /* nominal size of error message text */
496
497int
498returntosender(msg, returnq, flags, e)
499        char *msg;
500        ADDRESS *returnq;
501        int flags;
502        register ENVELOPE *e;
503{
504        register ENVELOPE *ee;
505        ENVELOPE *oldcur = CurEnv;
506        ENVELOPE errenvelope;
507        static int returndepth = 0;
508        register ADDRESS *q;
509        char *p;
510        char buf[MAXNAME + 1];
511
512        if (returnq == NULL)
513                return -1;
514
515        if (msg == NULL)
516                msg = "Unable to deliver mail";
517
518        if (tTd(6, 1))
519        {
520                sm_dprintf("\n*** Return To Sender: msg=\"%s\", depth=%d, e=%p, returnq=",
521                        msg, returndepth, e);
522                printaddr(returnq, true);
523                if (tTd(6, 20))
524                {
525                        sm_dprintf("Sendq=");
526                        printaddr(e->e_sendqueue, true);
527                }
528        }
529
530        if (++returndepth >= MAXRETURNS)
531        {
532                if (returndepth != MAXRETURNS)
533                        syserr("554 5.3.0 returntosender: infinite recursion on %s",
534                               returnq->q_paddr);
535                /* don't "unrecurse" and fake a clean exit */
536                /* returndepth--; */
537                return 0;
538        }
539
540        macdefine(&e->e_macro, A_PERM, 'g', e->e_sender);
541        macdefine(&e->e_macro, A_PERM, 'u', NULL);
542
543        /* initialize error envelope */
544        ee = newenvelope(&errenvelope, e, sm_rpool_new_x(NULL));
545        macdefine(&ee->e_macro, A_PERM, 'a', "\201b");
546        macdefine(&ee->e_macro, A_PERM, 'r', "");
547        macdefine(&ee->e_macro, A_PERM, 's', "localhost");
548        macdefine(&ee->e_macro, A_PERM, '_', "localhost");
549        clrsessenvelope(ee);
550
551        ee->e_puthdr = putheader;
552        ee->e_putbody = errbody;
553        ee->e_flags |= EF_RESPONSE|EF_METOO;
554        if (!bitset(EF_OLDSTYLE, e->e_flags))
555                ee->e_flags &= ~EF_OLDSTYLE;
556        if (bitset(EF_DONT_MIME, e->e_flags))
557        {
558                ee->e_flags |= EF_DONT_MIME;
559
560                /*
561                **  If we can't convert to MIME and we don't pass
562                **  8-bit, we can't send the body.
563                */
564
565                if (bitset(EF_HAS8BIT, e->e_flags) &&
566                    !bitset(MM_PASS8BIT, MimeMode))
567                        flags &= ~RTSF_SEND_BODY;
568        }
569
570        ee->e_sendqueue = returnq;
571        ee->e_msgsize = 0;
572        if (bitset(RTSF_SEND_BODY, flags) &&
573            !bitset(PRIV_NOBODYRETN, PrivacyFlags))
574                ee->e_msgsize = ERRORFUDGE + e->e_msgsize;
575        else
576                ee->e_flags |= EF_NO_BODY_RETN;
577
578        if (!setnewqueue(ee))
579        {
580                syserr("554 5.3.0 returntosender: cannot select queue for %s",
581                               returnq->q_paddr);
582                ExitStat = EX_UNAVAILABLE;
583                returndepth--;
584                return -1;
585        }
586        initsys(ee);
587
588#if NAMED_BIND
589        _res.retry = TimeOuts.res_retry[RES_TO_FIRST];
590        _res.retrans = TimeOuts.res_retrans[RES_TO_FIRST];
591#endif /* NAMED_BIND */
592        for (q = returnq; q != NULL; q = q->q_next)
593        {
594                if (QS_IS_BADADDR(q->q_state))
595                        continue;
596
597                q->q_flags &= ~(QHASNOTIFY|Q_PINGFLAGS);
598                q->q_flags |= QPINGONFAILURE;
599
600                if (!QS_IS_DEAD(q->q_state))
601                        ee->e_nrcpts++;
602
603                if (q->q_alias == NULL)
604                        addheader("To", q->q_paddr, 0, ee);
605        }
606
607        if (LogLevel > 5)
608        {
609                if (bitset(EF_RESPONSE, e->e_flags))
610                        p = "return to sender";
611                else if (bitset(EF_WARNING, e->e_flags))
612                        p = "sender notify";
613                else if (bitset(RTSF_PM_BOUNCE, flags))
614                        p = "postmaster notify";
615                else
616                        p = "DSN";
617                sm_syslog(LOG_INFO, e->e_id, "%s: %s: %s",
618                          ee->e_id, p, shortenstring(msg, MAXSHORTSTR));
619        }
620
621        if (SendMIMEErrors)
622        {
623                addheader("MIME-Version", "1.0", 0, ee);
624                (void) sm_snprintf(buf, sizeof buf, "%s.%ld/%.100s",
625                                ee->e_id, (long)curtime(), MyHostName);
626                ee->e_msgboundary = sm_rpool_strdup_x(ee->e_rpool, buf);
627                (void) sm_snprintf(buf, sizeof buf,
628#if DSN
629                                "multipart/report; report-type=delivery-status;\n\tboundary=\"%s\"",
630#else /* DSN */
631                                "multipart/mixed; boundary=\"%s\"",
632#endif /* DSN */
633                                ee->e_msgboundary);
634                addheader("Content-Type", buf, 0, ee);
635
636                p = hvalue("Content-Transfer-Encoding", e->e_header);
637                if (p != NULL && sm_strcasecmp(p, "binary") != 0)
638                        p = NULL;
639                if (p == NULL && bitset(EF_HAS8BIT, e->e_flags))
640                        p = "8bit";
641                if (p != NULL)
642                        addheader("Content-Transfer-Encoding", p, 0, ee);
643        }
644        if (strncmp(msg, "Warning:", 8) == 0)
645        {
646                addheader("Subject", msg, 0, ee);
647                p = "warning-timeout";
648        }
649        else if (strncmp(msg, "Postmaster warning:", 19) == 0)
650        {
651                addheader("Subject", msg, 0, ee);
652                p = "postmaster-warning";
653        }
654        else if (strcmp(msg, "Return receipt") == 0)
655        {
656                addheader("Subject", msg, 0, ee);
657                p = "return-receipt";
658        }
659        else if (bitset(RTSF_PM_BOUNCE, flags))
660        {
661                (void) sm_snprintf(buf, sizeof buf,
662                         "Postmaster notify: see transcript for details");
663                addheader("Subject", buf, 0, ee);
664                p = "postmaster-notification";
665        }
666        else
667        {
668                (void) sm_snprintf(buf, sizeof buf,
669                         "Returned mail: see transcript for details");
670                addheader("Subject", buf, 0, ee);
671                p = "failure";
672        }
673        (void) sm_snprintf(buf, sizeof buf, "auto-generated (%s)", p);
674        addheader("Auto-Submitted", buf, 0, ee);
675
676        /* fake up an address header for the from person */
677        expand("\201n", buf, sizeof buf, e);
678        if (parseaddr(buf, &ee->e_from,
679                      RF_COPYALL|RF_SENDERADDR, '\0', NULL, e, false) == NULL)
680        {
681                syserr("553 5.3.5 Can't parse myself!");
682                ExitStat = EX_SOFTWARE;
683                returndepth--;
684                return -1;
685        }
686        ee->e_from.q_flags &= ~(QHASNOTIFY|Q_PINGFLAGS);
687        ee->e_from.q_flags |= QPINGONFAILURE;
688        ee->e_sender = ee->e_from.q_paddr;
689
690        /* push state into submessage */
691        CurEnv = ee;
692        macdefine(&ee->e_macro, A_PERM, 'f', "\201n");
693        macdefine(&ee->e_macro, A_PERM, 'x', "Mail Delivery Subsystem");
694        eatheader(ee, true, true);
695
696        /* mark statistics */
697        markstats(ee, NULLADDR, STATS_NORMAL);
698
699        /* actually deliver the error message */
700        sendall(ee, SM_DELIVER);
701
702        /* restore state */
703        dropenvelope(ee, true, false);
704        sm_rpool_free(ee->e_rpool);
705        CurEnv = oldcur;
706        returndepth--;
707
708        /* check for delivery errors */
709        if (ee->e_parent == NULL ||
710            !bitset(EF_RESPONSE, ee->e_parent->e_flags))
711                return 0;
712        for (q = ee->e_sendqueue; q != NULL; q = q->q_next)
713        {
714                if (QS_IS_ATTEMPTED(q->q_state))
715                        return 0;
716        }
717        return -1;
718}
719/*
720**  ERRBODY -- output the body of an error message.
721**
722**      Typically this is a copy of the transcript plus a copy of the
723**      original offending message.
724**
725**      Parameters:
726**              mci -- the mailer connection information.
727**              e -- the envelope we are working in.
728**              separator -- any possible MIME separator (unused).
729**
730**      Returns:
731**              success
732**
733**      Side Effects:
734**              Outputs the body of an error message.
735*/
736
737/* ARGSUSED2 */
738static bool
739errbody(mci, e, separator)
740        register MCI *mci;
741        register ENVELOPE *e;
742        char *separator;
743{
744        bool printheader;
745        bool sendbody;
746        bool pm_notify;
747        int save_errno;
748        register SM_FILE_T *xfile;
749        char *p;
750        register ADDRESS *q = NULL;
751        char actual[MAXLINE];
752        char buf[MAXLINE];
753
754        if (bitset(MCIF_INHEADER, mci->mci_flags))
755        {
756                if (!putline("", mci))
757                        goto writeerr;
758                mci->mci_flags &= ~MCIF_INHEADER;
759        }
760        if (e->e_parent == NULL)
761        {
762                syserr("errbody: null parent");
763                if (!putline("   ----- Original message lost -----\n", mci))
764                        goto writeerr;
765                return true;
766        }
767
768        /*
769        **  Output MIME header.
770        */
771
772        if (e->e_msgboundary != NULL)
773        {
774                (void) sm_strlcpyn(buf, sizeof buf, 2, "--", e->e_msgboundary);
775                if (!putline("This is a MIME-encapsulated message", mci) ||
776                    !putline("", mci) ||
777                    !putline(buf, mci) ||
778                    !putline("", mci))
779                        goto writeerr;
780        }
781
782        /*
783        **  Output introductory information.
784        */
785
786        pm_notify = false;
787        p = hvalue("subject", e->e_header);
788        if (p != NULL && strncmp(p, "Postmaster ", 11) == 0)
789                pm_notify = true;
790        else
791        {
792                for (q = e->e_parent->e_sendqueue; q != NULL; q = q->q_next)
793                {
794                        if (QS_IS_BADADDR(q->q_state))
795                                break;
796                }
797        }
798        if (!pm_notify && q == NULL &&
799            !bitset(EF_FATALERRS|EF_SENDRECEIPT, e->e_parent->e_flags))
800        {
801                if (!putline("    **********************************************",
802                        mci) ||
803                    !putline("    **      THIS IS A WARNING MESSAGE ONLY      **",
804                        mci) ||
805                    !putline("    **  YOU DO NOT NEED TO RESEND YOUR MESSAGE  **",
806                        mci) ||
807                    !putline("    **********************************************",
808                        mci) ||
809                    !putline("", mci))
810                        goto writeerr;
811        }
812        (void) sm_snprintf(buf, sizeof buf,
813                "The original message was received at %s",
814                arpadate(ctime(&e->e_parent->e_ctime)));
815        if (!putline(buf, mci))
816                goto writeerr;
817        expand("from \201_", buf, sizeof buf, e->e_parent);
818        if (!putline(buf, mci))
819                goto writeerr;
820
821        /* include id in postmaster copies */
822        if (pm_notify && e->e_parent->e_id != NULL)
823        {
824                (void) sm_strlcpyn(buf, sizeof buf, 2, "with id ",
825                        e->e_parent->e_id);
826                if (!putline(buf, mci))
827                        goto writeerr;
828        }
829        if (!putline("", mci))
830                goto writeerr;
831
832        /*
833        **  Output error message header (if specified and available).
834        */
835
836        if (ErrMsgFile != NULL &&
837            !bitset(EF_SENDRECEIPT, e->e_parent->e_flags))
838        {
839                if (*ErrMsgFile == '/')
840                {
841                        long sff = SFF_ROOTOK|SFF_REGONLY;
842
843                        if (DontLockReadFiles)
844                                sff |= SFF_NOLOCK;
845                        if (!bitnset(DBS_ERRORHEADERINUNSAFEDIRPATH,
846                                     DontBlameSendmail))
847                                sff |= SFF_SAFEDIRPATH;
848                        xfile = safefopen(ErrMsgFile, O_RDONLY, 0444, sff);
849                        if (xfile != NULL)
850                        {
851                                while (sm_io_fgets(xfile, SM_TIME_DEFAULT, buf,
852                                                   sizeof buf) != NULL)
853                                {
854                                        translate_dollars(buf);
855                                        expand(buf, buf, sizeof buf, e);
856                                        if (!putline(buf, mci))
857                                                goto writeerr;
858                                }
859                                (void) sm_io_close(xfile, SM_TIME_DEFAULT);
860                                if (!putline("\n", mci))
861                                        goto writeerr;
862                        }
863                }
864                else
865                {
866                        expand(ErrMsgFile, buf, sizeof buf, e);
867                        if (!putline(buf, mci) || !putline("", mci))
868                                goto writeerr;
869                }
870        }
871
872        /*
873        **  Output message introduction
874        */
875
876        /* permanent fatal errors */
877        printheader = true;
878        for (q = e->e_parent->e_sendqueue; q != NULL; q = q->q_next)
879        {
880                if (!QS_IS_BADADDR(q->q_state) ||
881                    !bitset(QPINGONFAILURE, q->q_flags))
882                        continue;
883
884                if (printheader)
885                {
886                        if (!putline("   ----- The following addresses had permanent fatal errors -----",
887                                        mci))
888                                goto writeerr;
889                        printheader = false;
890                }
891
892                (void) sm_strlcpy(buf, shortenstring(q->q_paddr, MAXSHORTSTR),
893                                  sizeof buf);
894                if (!putline(buf, mci))
895                        goto writeerr;
896                if (q->q_rstatus != NULL)
897                {
898                        (void) sm_snprintf(buf, sizeof buf,
899                                "    (reason: %s)",
900                                shortenstring(exitstat(q->q_rstatus),
901                                              MAXSHORTSTR));
902                        if (!putline(buf, mci))
903                                goto writeerr;
904                }
905                if (q->q_alias != NULL)
906                {
907                        (void) sm_snprintf(buf, sizeof buf,
908                                "    (expanded from: %s)",
909                                shortenstring(q->q_alias->q_paddr,
910                                              MAXSHORTSTR));
911                        if (!putline(buf, mci))
912                                goto writeerr;
913                }
914        }
915        if (!printheader && !putline("", mci))
916                goto writeerr;
917
918        /* transient non-fatal errors */
919        printheader = true;
920        for (q = e->e_parent->e_sendqueue; q != NULL; q = q->q_next)
921        {
922                if (QS_IS_BADADDR(q->q_state) ||
923                    !bitset(QPRIMARY, q->q_flags) ||
924                    !bitset(QBYNDELAY, q->q_flags) ||
925                    !bitset(QDELAYED, q->q_flags))
926                        continue;
927
928                if (printheader)
929                {
930                        if (!putline("   ----- The following addresses had transient non-fatal errors -----",
931                                        mci))
932                                goto writeerr;
933                        printheader = false;
934                }
935
936                (void) sm_strlcpy(buf, shortenstring(q->q_paddr, MAXSHORTSTR),
937                                  sizeof buf);
938                if (!putline(buf, mci))
939                        goto writeerr;
940                if (q->q_alias != NULL)
941                {
942                        (void) sm_snprintf(buf, sizeof buf,
943                                "    (expanded from: %s)",
944                                shortenstring(q->q_alias->q_paddr,
945                                              MAXSHORTSTR));
946                        if (!putline(buf, mci))
947                                goto writeerr;
948                }
949        }
950        if (!printheader && !putline("", mci))
951                goto writeerr;
952
953        /* successful delivery notifications */
954        printheader = true;
955        for (q = e->e_parent->e_sendqueue; q != NULL; q = q->q_next)
956        {
957                if (QS_IS_BADADDR(q->q_state) ||
958                    !bitset(QPRIMARY, q->q_flags) ||
959                    bitset(QBYNDELAY, q->q_flags) ||
960                    bitset(QDELAYED, q->q_flags))
961                        continue;
962                else if (bitset(QBYNRELAY, q->q_flags))
963                        p = "Deliver-By notify: relayed";
964                else if (bitset(QBYTRACE, q->q_flags))
965                        p = "Deliver-By trace: relayed";
966                else if (!bitset(QPINGONSUCCESS, q->q_flags))
967                        continue;
968                else if (bitset(QRELAYED, q->q_flags))
969                        p = "relayed to non-DSN-aware mailer";
970                else if (bitset(QDELIVERED, q->q_flags))
971                {
972                        if (bitset(QEXPANDED, q->q_flags))
973                                p = "successfully delivered to mailing list";
974                        else
975                                p = "successfully delivered to mailbox";
976                }
977                else if (bitset(QEXPANDED, q->q_flags))
978                        p = "expanded by alias";
979                else
980                        continue;
981
982                if (printheader)
983                {
984                        if (!putline("   ----- The following addresses had successful delivery notifications -----",
985                                        mci))
986                                goto writeerr;
987                        printheader = false;
988                }
989
990                (void) sm_snprintf(buf, sizeof buf, "%s  (%s)",
991                         shortenstring(q->q_paddr, MAXSHORTSTR), p);
992                if (!putline(buf, mci))
993                        goto writeerr;
994                if (q->q_alias != NULL)
995                {
996                        (void) sm_snprintf(buf, sizeof buf,
997                                "    (expanded from: %s)",
998                                shortenstring(q->q_alias->q_paddr,
999                                              MAXSHORTSTR));
1000                        if (!putline(buf, mci))
1001                                goto writeerr;
1002                }
1003        }
1004        if (!printheader && !putline("", mci))
1005                goto writeerr;
1006
1007        /*
1008        **  Output transcript of errors
1009        */
1010
1011        (void) sm_io_flush(smioout, SM_TIME_DEFAULT);
1012        if (e->e_parent->e_xfp == NULL)
1013        {
1014                if (!putline("   ----- Transcript of session is unavailable -----\n",
1015                                mci))
1016                        goto writeerr;
1017        }
1018        else
1019        {
1020                printheader = true;
1021                (void) bfrewind(e->e_parent->e_xfp);
1022                if (e->e_xfp != NULL)
1023                        (void) sm_io_flush(e->e_xfp, SM_TIME_DEFAULT);
1024                while (sm_io_fgets(e->e_parent->e_xfp, SM_TIME_DEFAULT, buf,
1025                                   sizeof buf) != NULL)
1026                {
1027                        if (printheader && !putline("   ----- Transcript of session follows -----\n",
1028                                                mci))
1029                                goto writeerr;
1030                        printheader = false;
1031                        if (!putline(buf, mci))
1032                                goto writeerr;
1033                }
1034        }
1035        errno = 0;
1036
1037#if DSN
1038        /*
1039        **  Output machine-readable version.
1040        */
1041
1042        if (e->e_msgboundary != NULL)
1043        {
1044                (void) sm_strlcpyn(buf, sizeof buf, 2, "--", e->e_msgboundary);
1045                if (!putline("", mci) ||
1046                    !putline(buf, mci) ||
1047                    !putline("Content-Type: message/delivery-status", mci) ||
1048                    !putline("", mci))
1049                        goto writeerr;
1050
1051                /*
1052                **  Output per-message information.
1053                */
1054
1055                /* original envelope id from MAIL FROM: line */
1056                if (e->e_parent->e_envid != NULL)
1057                {
1058                        (void) sm_snprintf(buf, sizeof buf,
1059                                        "Original-Envelope-Id: %.800s",
1060                                        xuntextify(e->e_parent->e_envid));
1061                        if (!putline(buf, mci))
1062                                goto writeerr;
1063                }
1064
1065                /* Reporting-MTA: is us (required) */
1066                (void) sm_snprintf(buf, sizeof buf,
1067                                   "Reporting-MTA: dns; %.800s", MyHostName);
1068                if (!putline(buf, mci))
1069                        goto writeerr;
1070
1071                /* DSN-Gateway: not relevant since we are not translating */
1072
1073                /* Received-From-MTA: shows where we got this message from */
1074                if (RealHostName != NULL)
1075                {
1076                        /* XXX use $s for type? */
1077                        if (e->e_parent->e_from.q_mailer == NULL ||
1078                            (p = e->e_parent->e_from.q_mailer->m_mtatype) == NULL)
1079                                p = "dns";
1080                        (void) sm_snprintf(buf, sizeof buf,
1081                                        "Received-From-MTA: %s; %.800s",
1082                                        p, RealHostName);
1083                        if (!putline(buf, mci))
1084                                goto writeerr;
1085                }
1086
1087                /* Arrival-Date: -- when it arrived here */
1088                (void) sm_strlcpyn(buf, sizeof buf, 2, "Arrival-Date: ",
1089                                arpadate(ctime(&e->e_parent->e_ctime)));
1090                if (!putline(buf, mci))
1091                        goto writeerr;
1092
1093                /* Deliver-By-Date: -- when it should have been delivered */
1094                if (IS_DLVR_BY(e->e_parent))
1095                {
1096                        time_t dbyd;
1097
1098                        dbyd = e->e_parent->e_ctime + e->e_parent->e_deliver_by;
1099                        (void) sm_strlcpyn(buf, sizeof buf, 2,
1100                                        "Deliver-By-Date: ",
1101                                        arpadate(ctime(&dbyd)));
1102                        if (!putline(buf, mci))
1103                                goto writeerr;
1104                }
1105
1106                /*
1107                **  Output per-address information.
1108                */
1109
1110                for (q = e->e_parent->e_sendqueue; q != NULL; q = q->q_next)
1111                {
1112                        char *action;
1113
1114                        if (QS_IS_BADADDR(q->q_state))
1115                        {
1116                                /* RFC 1891, 6.2.6 (b) */
1117                                if (bitset(QHASNOTIFY, q->q_flags) &&
1118                                    !bitset(QPINGONFAILURE, q->q_flags))
1119                                        continue;
1120                                action = "failed";
1121                        }
1122                        else if (!bitset(QPRIMARY, q->q_flags))
1123                                continue;
1124                        else if (bitset(QDELIVERED, q->q_flags))
1125                        {
1126                                if (bitset(QEXPANDED, q->q_flags))
1127                                        action = "delivered (to mailing list)";
1128                                else
1129                                        action = "delivered (to mailbox)";
1130                        }
1131                        else if (bitset(QRELAYED, q->q_flags))
1132                                action = "relayed (to non-DSN-aware mailer)";
1133                        else if (bitset(QEXPANDED, q->q_flags))
1134                                action = "expanded (to multi-recipient alias)";
1135                        else if (bitset(QDELAYED, q->q_flags))
1136                                action = "delayed";
1137                        else if (bitset(QBYTRACE, q->q_flags))
1138                                action = "relayed (Deliver-By trace mode)";
1139                        else if (bitset(QBYNDELAY, q->q_flags))
1140                                action = "delayed (Deliver-By notify mode)";
1141                        else if (bitset(QBYNRELAY, q->q_flags))
1142                                action = "relayed (Deliver-By notify mode)";
1143                        else
1144                                continue;
1145
1146                        if (!putline("", mci))
1147                                goto writeerr;
1148
1149                        /* Original-Recipient: -- passed from on high */
1150                        if (q->q_orcpt != NULL)
1151                        {
1152                                (void) sm_snprintf(buf, sizeof buf,
1153                                                "Original-Recipient: %.800s",
1154                                                q->q_orcpt);
1155                                if (!putline(buf, mci))
1156                                        goto writeerr;
1157                        }
1158
1159                        /* Figure out actual recipient */
1160                        actual[0] = '\0';
1161                        if (q->q_user[0] != '\0')
1162                        {
1163                                if (q->q_mailer != NULL &&
1164                                    q->q_mailer->m_addrtype != NULL)
1165                                        p = q->q_mailer->m_addrtype;
1166                                else
1167                                        p = "rfc822";
1168
1169                                if (sm_strcasecmp(p, "rfc822") == 0 &&
1170                                    strchr(q->q_user, '@') == NULL)
1171                                {
1172                                        (void) sm_snprintf(actual,
1173                                                           sizeof actual,
1174                                                           "%s; %.700s@%.100s",
1175                                                           p, q->q_user,
1176                                                           MyHostName);
1177                                }
1178                                else
1179                                {
1180                                        (void) sm_snprintf(actual,
1181                                                           sizeof actual,
1182                                                           "%s; %.800s",
1183                                                           p, q->q_user);
1184                                }
1185                        }
1186
1187                        /* Final-Recipient: -- the name from the RCPT command */
1188                        if (q->q_finalrcpt == NULL)
1189                        {
1190                                /* should never happen */
1191                                sm_syslog(LOG_ERR, e->e_id,
1192                                          "returntosender: q_finalrcpt is NULL");
1193
1194                                /* try to fall back to the actual recipient */
1195                                if (actual[0] != '\0')
1196                                        q->q_finalrcpt = sm_rpool_strdup_x(e->e_rpool,
1197                                                                           actual);
1198                        }
1199
1200                        if (q->q_finalrcpt != NULL)
1201                        {
1202                                (void) sm_snprintf(buf, sizeof buf,
1203                                                   "Final-Recipient: %s",
1204                                                   q->q_finalrcpt);
1205                                if (!putline(buf, mci))
1206                                        goto writeerr;
1207                        }
1208
1209                        /* X-Actual-Recipient: -- the real problem address */
1210                        if (actual[0] != '\0' &&
1211                            q->q_finalrcpt != NULL &&
1212                            strcmp(actual, q->q_finalrcpt) != 0)
1213                        {
1214                                (void) sm_snprintf(buf, sizeof buf,
1215                                                   "X-Actual-Recipient: %s",
1216                                                   actual);
1217                                if (!putline(buf, mci))
1218                                        goto writeerr;
1219                        }
1220
1221                        /* Action: -- what happened? */
1222                        (void) sm_strlcpyn(buf, sizeof buf, 2, "Action: ",
1223                                action);
1224                        if (!putline(buf, mci))
1225                                goto writeerr;
1226
1227                        /* Status: -- what _really_ happened? */
1228                        if (q->q_status != NULL)
1229                                p = q->q_status;
1230                        else if (QS_IS_BADADDR(q->q_state))
1231                                p = "5.0.0";
1232                        else if (QS_IS_QUEUEUP(q->q_state))
1233                                p = "4.0.0";
1234                        else
1235                                p = "2.0.0";
1236                        (void) sm_strlcpyn(buf, sizeof buf, 2, "Status: ", p);
1237                        if (!putline(buf, mci))
1238                                goto writeerr;
1239
1240                        /* Remote-MTA: -- who was I talking to? */
1241                        if (q->q_statmta != NULL)
1242                        {
1243                                if (q->q_mailer == NULL ||
1244                                    (p = q->q_mailer->m_mtatype) == NULL)
1245                                        p = "dns";
1246                                (void) sm_snprintf(buf, sizeof buf,
1247                                                "Remote-MTA: %s; %.800s",
1248                                                p, q->q_statmta);
1249                                p = &buf[strlen(buf) - 1];
1250                                if (*p == '.')
1251                                        *p = '\0';
1252                                if (!putline(buf, mci))
1253                                        goto writeerr;
1254                        }
1255
1256                        /* Diagnostic-Code: -- actual result from other end */
1257                        if (q->q_rstatus != NULL)
1258                        {
1259                                p = q->q_mailer->m_diagtype;
1260                                if (p == NULL)
1261                                        p = "smtp";
1262                                (void) sm_snprintf(buf, sizeof buf,
1263                                                "Diagnostic-Code: %s; %.800s",
1264                                                p, q->q_rstatus);
1265                                if (!putline(buf, mci))
1266                                        goto writeerr;
1267                        }
1268
1269                        /* Last-Attempt-Date: -- fine granularity */
1270                        if (q->q_statdate == (time_t) 0L)
1271                                q->q_statdate = curtime();
1272                        (void) sm_strlcpyn(buf, sizeof buf, 2,
1273                                        "Last-Attempt-Date: ",
1274                                        arpadate(ctime(&q->q_statdate)));
1275                        if (!putline(buf, mci))
1276                                goto writeerr;
1277
1278                        /* Will-Retry-Until: -- for delayed messages only */
1279                        if (QS_IS_QUEUEUP(q->q_state))
1280                        {
1281                                time_t xdate;
1282
1283                                xdate = e->e_parent->e_ctime +
1284                                        TimeOuts.to_q_return[e->e_parent->e_timeoutclass];
1285                                (void) sm_strlcpyn(buf, sizeof buf, 2,
1286                                         "Will-Retry-Until: ",
1287                                         arpadate(ctime(&xdate)));
1288                                if (!putline(buf, mci))
1289                                        goto writeerr;
1290                        }
1291                }
1292        }
1293#endif /* DSN */
1294
1295        /*
1296        **  Output text of original message
1297        */
1298
1299        if (!putline("", mci))
1300                goto writeerr;
1301        if (bitset(EF_HAS_DF, e->e_parent->e_flags))
1302        {
1303                sendbody = !bitset(EF_NO_BODY_RETN, e->e_parent->e_flags) &&
1304                           !bitset(EF_NO_BODY_RETN, e->e_flags);
1305
1306                if (e->e_msgboundary == NULL)
1307                {
1308                        if (!putline(
1309                                sendbody
1310                                ? "   ----- Original message follows -----\n"
1311                                : "   ----- Message header follows -----\n",
1312                                mci))
1313                        {
1314                                goto writeerr;
1315                        }
1316                }
1317                else
1318                {
1319                        (void) sm_strlcpyn(buf, sizeof buf, 2, "--",
1320                                        e->e_msgboundary);
1321
1322                        if (!putline(buf, mci))
1323                                goto writeerr;
1324                        (void) sm_strlcpyn(buf, sizeof buf, 2, "Content-Type: ",
1325                                        sendbody ? "message/rfc822"
1326                                                 : "text/rfc822-headers");
1327                        if (!putline(buf, mci))
1328                                goto writeerr;
1329
1330                        p = hvalue("Content-Transfer-Encoding",
1331                                   e->e_parent->e_header);
1332                        if (p != NULL && sm_strcasecmp(p, "binary") != 0)
1333                                p = NULL;
1334                        if (p == NULL &&
1335                            bitset(EF_HAS8BIT, e->e_parent->e_flags))
1336                                p = "8bit";
1337                        if (p != NULL)
1338                        {
1339                                (void) sm_snprintf(buf, sizeof buf,
1340                                                "Content-Transfer-Encoding: %s",
1341                                                p);
1342                                if (!putline(buf, mci))
1343                                        goto writeerr;
1344                        }
1345                }
1346                if (!putline("", mci))
1347                        goto writeerr;
1348                save_errno = errno;
1349                if (!putheader(mci, e->e_parent->e_header, e->e_parent,
1350                                M87F_OUTER))
1351                        goto writeerr;
1352                errno = save_errno;
1353                if (sendbody)
1354                {
1355                        if (!putbody(mci, e->e_parent, e->e_msgboundary))
1356                                goto writeerr;
1357                }
1358                else if (e->e_msgboundary == NULL)
1359                {
1360                        if (!putline("", mci) ||
1361                            !putline("   ----- Message body suppressed -----",
1362                                        mci))
1363                        {
1364                                goto writeerr;
1365                        }
1366                }
1367        }
1368        else if (e->e_msgboundary == NULL)
1369        {
1370                if (!putline("  ----- No message was collected -----\n", mci))
1371                        goto writeerr;
1372        }
1373
1374        if (e->e_msgboundary != NULL)
1375        {
1376                (void) sm_strlcpyn(buf, sizeof buf, 3, "--", e->e_msgboundary,
1377                                   "--");
1378                if (!putline("", mci) || !putline(buf, mci))
1379                        goto writeerr;
1380        }
1381        if (!putline("", mci) ||
1382            sm_io_flush(mci->mci_out, SM_TIME_DEFAULT) == SM_IO_EOF)
1383                        goto writeerr;
1384
1385        /*
1386        **  Cleanup and exit
1387        */
1388
1389        if (errno != 0)
1390        {
1391  writeerr:
1392                syserr("errbody: I/O error");
1393                return false;
1394        }
1395        return true;
1396}
1397
1398/*
1399**  SMTPTODSN -- convert SMTP to DSN status code
1400**
1401**      Parameters:
1402**              smtpstat -- the smtp status code (e.g., 550).
1403**
1404**      Returns:
1405**              The DSN version of the status code.
1406**
1407**      Storage Management:
1408**              smtptodsn() returns a pointer to a character string literal,
1409**              which will remain valid forever, and thus does not need to
1410**              be copied.  Current code relies on this property.
1411*/
1412
1413char *
1414smtptodsn(smtpstat)
1415        int smtpstat;
1416{
1417        if (smtpstat < 0)
1418                return "4.4.2";
1419
1420        switch (smtpstat)
1421        {
1422          case 450:     /* Req mail action not taken: mailbox unavailable */
1423                return "4.2.0";
1424
1425          case 451:     /* Req action aborted: local error in processing */
1426                return "4.3.0";
1427
1428          case 452:     /* Req action not taken: insufficient sys storage */
1429                return "4.3.1";
1430
1431          case 500:     /* Syntax error, command unrecognized */
1432                return "5.5.2";
1433
1434          case 501:     /* Syntax error in parameters or arguments */
1435                return "5.5.4";
1436
1437          case 502:     /* Command not implemented */
1438                return "5.5.1";
1439
1440          case 503:     /* Bad sequence of commands */
1441                return "5.5.1";
1442
1443          case 504:     /* Command parameter not implemented */
1444                return "5.5.4";
1445
1446          case 550:     /* Req mail action not taken: mailbox unavailable */
1447                return "5.2.0";
1448
1449          case 551:     /* User not local; please try <...> */
1450                return "5.1.6";
1451
1452          case 552:     /* Req mail action aborted: exceeded storage alloc */
1453                return "5.2.2";
1454
1455          case 553:     /* Req action not taken: mailbox name not allowed */
1456                return "5.1.0";
1457
1458          case 554:     /* Transaction failed */
1459                return "5.0.0";
1460        }
1461
1462        if ((smtpstat / 100) == 2)
1463                return "2.0.0";
1464        if ((smtpstat / 100) == 4)
1465                return "4.0.0";
1466        return "5.0.0";
1467}
1468/*
1469**  XTEXTIFY -- take regular text and turn it into DSN-style xtext
1470**
1471**      Parameters:
1472**              t -- the text to convert.
1473**              taboo -- additional characters that must be encoded.
1474**
1475**      Returns:
1476**              The xtext-ified version of the same string.
1477*/
1478
1479char *
1480xtextify(t, taboo)
1481        register char *t;
1482        char *taboo;
1483{
1484        register char *p;
1485        int l;
1486        int nbogus;
1487        static char *bp = NULL;
1488        static int bplen = 0;
1489
1490        if (taboo == NULL)
1491                taboo = "";
1492
1493        /* figure out how long this xtext will have to be */
1494        nbogus = l = 0;
1495        for (p = t; *p != '\0'; p++)
1496        {
1497                register int c = (*p & 0xff);
1498
1499                /* ASCII dependence here -- this is the way the spec words it */
1500                if (c < '!' || c > '~' || c == '+' || c == '\\' || c == '(' ||
1501                    strchr(taboo, c) != NULL)
1502                        nbogus++;
1503                l++;
1504        }
1505        if (nbogus < 0)
1506        {
1507                /* since nbogus is ssize_t and wrapped, 2 * size_t would wrap */
1508                syserr("!xtextify string too long");
1509        }
1510        if (nbogus == 0)
1511                return t;
1512        l += nbogus * 2 + 1;
1513
1514        /* now allocate space if necessary for the new string */
1515        if (l > bplen)
1516        {
1517                if (bp != NULL)
1518                        sm_free(bp); /* XXX */
1519                bp = sm_pmalloc_x(l);
1520                bplen = l;
1521        }
1522
1523        /* ok, copy the text with byte expansion */
1524        for (p = bp; *t != '\0'; )
1525        {
1526                register int c = (*t++ & 0xff);
1527
1528                /* ASCII dependence here -- this is the way the spec words it */
1529                if (c < '!' || c > '~' || c == '+' || c == '\\' || c == '(' ||
1530                    strchr(taboo, c) != NULL)
1531                {
1532                        *p++ = '+';
1533                        *p++ = "0123456789ABCDEF"[c >> 4];
1534                        *p++ = "0123456789ABCDEF"[c & 0xf];
1535                }
1536                else
1537                        *p++ = c;
1538        }
1539        *p = '\0';
1540        return bp;
1541}
1542/*
1543**  XUNTEXTIFY -- take xtext and turn it into plain text
1544**
1545**      Parameters:
1546**              t -- the xtextified text.
1547**
1548**      Returns:
1549**              The decoded text.  No attempt is made to deal with
1550**              null strings in the resulting text.
1551*/
1552
1553char *
1554xuntextify(t)
1555        register char *t;
1556{
1557        register char *p;
1558        int l;
1559        static char *bp = NULL;
1560        static int bplen = 0;
1561
1562        /* heuristic -- if no plus sign, just return the input */
1563        if (strchr(t, '+') == NULL)
1564                return t;
1565
1566        /* xtext is always longer than decoded text */
1567        l = strlen(t);
1568        if (l > bplen)
1569        {
1570                if (bp != NULL)
1571                        sm_free(bp); /* XXX */
1572                bp = xalloc(l);
1573                bplen = l;
1574        }
1575
1576        /* ok, copy the text with byte compression */
1577        for (p = bp; *t != '\0'; t++)
1578        {
1579                register int c = *t & 0xff;
1580
1581                if (c != '+')
1582                {
1583                        *p++ = c;
1584                        continue;
1585                }
1586
1587                c = *++t & 0xff;
1588                if (!isascii(c) || !isxdigit(c))
1589                {
1590                        /* error -- first digit is not hex */
1591                        usrerr("bogus xtext: +%c", c);
1592                        t--;
1593                        continue;
1594                }
1595                if (isdigit(c))
1596                        c -= '0';
1597                else if (isupper(c))
1598                        c -= 'A' - 10;
1599                else
1600                        c -= 'a' - 10;
1601                *p = c << 4;
1602
1603                c = *++t & 0xff;
1604                if (!isascii(c) || !isxdigit(c))
1605                {
1606                        /* error -- second digit is not hex */
1607                        usrerr("bogus xtext: +%x%c", *p >> 4, c);
1608                        t--;
1609                        continue;
1610                }
1611                if (isdigit(c))
1612                        c -= '0';
1613                else if (isupper(c))
1614                        c -= 'A' - 10;
1615                else
1616                        c -= 'a' - 10;
1617                *p++ |= c;
1618        }
1619        *p = '\0';
1620        return bp;
1621}
1622/*
1623**  XTEXTOK -- check if a string is legal xtext
1624**
1625**      Xtext is used in Delivery Status Notifications.  The spec was
1626**      taken from RFC 1891, ``SMTP Service Extension for Delivery
1627**      Status Notifications''.
1628**
1629**      Parameters:
1630**              s -- the string to check.
1631**
1632**      Returns:
1633**              true -- if 's' is legal xtext.
1634**              false -- if it has any illegal characters in it.
1635*/
1636
1637bool
1638xtextok(s)
1639        char *s;
1640{
1641        int c;
1642
1643        while ((c = *s++) != '\0')
1644        {
1645                if (c == '+')
1646                {
1647                        c = *s++;
1648                        if (!isascii(c) || !isxdigit(c))
1649                                return false;
1650                        c = *s++;
1651                        if (!isascii(c) || !isxdigit(c))
1652                                return false;
1653                }
1654                else if (c < '!' || c > '~' || c == '=')
1655                        return false;
1656        }
1657        return true;
1658}
1659/*
1660**  PRUNEROUTE -- prune an RFC-822 source route
1661**
1662**      Trims down a source route to the last internet-registered hop.
1663**      This is encouraged by RFC 1123 section 5.3.3.
1664**
1665**      Parameters:
1666**              addr -- the address
1667**
1668**      Returns:
1669**              true -- address was modified
1670**              false -- address could not be pruned
1671**
1672**      Side Effects:
1673**              modifies addr in-place
1674*/
1675
1676static bool
1677pruneroute(addr)
1678        char *addr;
1679{
1680#if NAMED_BIND
1681        char *start, *at, *comma;
1682        char c;
1683        int braclev;
1684        int rcode;
1685        int i;
1686        char hostbuf[BUFSIZ];
1687        char *mxhosts[MAXMXHOSTS + 1];
1688
1689        /* check to see if this is really a route-addr */
1690        if (*addr != '<' || addr[1] != '@' || addr[strlen(addr) - 1] != '>')
1691                return false;
1692
1693        /*
1694        **  Can't simply find the first ':' is the address might be in the
1695        **  form:  "<@[IPv6:::1]:user@host>" and the first ':' in inside
1696        **  the IPv6 address.
1697        */
1698
1699        start = addr;
1700        braclev = 0;
1701        while (*start != '\0')
1702        {
1703                if (*start == ':' && braclev <= 0)
1704                        break;
1705                else if (*start == '[')
1706                        braclev++;
1707                else if (*start == ']' && braclev > 0)
1708                        braclev--;
1709                start++;
1710        }
1711        if (braclev > 0 || *start != ':')
1712                return false;
1713
1714        at = strrchr(addr, '@');
1715        if (at == NULL || at < start)
1716                return false;
1717
1718        /* slice off the angle brackets */
1719        i = strlen(at + 1);
1720        if (i >= sizeof hostbuf)
1721                return false;
1722        (void) sm_strlcpy(hostbuf, at + 1, sizeof hostbuf);
1723        hostbuf[i - 1] = '\0';
1724
1725        while (start != NULL)
1726        {
1727                if (getmxrr(hostbuf, mxhosts, NULL, false,
1728                            &rcode, true, NULL) > 0)
1729                {
1730                        (void) sm_strlcpy(addr + 1, start + 1,
1731                                          strlen(addr) - 1);
1732                        return true;
1733                }
1734                c = *start;
1735                *start = '\0';
1736                comma = strrchr(addr, ',');
1737                if (comma != NULL && comma[1] == '@' &&
1738                    strlen(comma + 2) < sizeof hostbuf)
1739                        (void) sm_strlcpy(hostbuf, comma + 2, sizeof hostbuf);
1740                else
1741                        comma = NULL;
1742                *start = c;
1743                start = comma;
1744        }
1745#endif /* NAMED_BIND */
1746        return false;
1747}
Note: See TracBrowser for help on using the repository browser.