source: trunk/third/sendmail/src/headers.c @ 12554

Revision 12554, 34.9 KB checked in by danw, 26 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r12553, which included commits to RCS files with non-trunk default branches.
Line 
1/*
2 * Copyright (c) 1998 Sendmail, Inc.  All rights reserved.
3 * Copyright (c) 1983, 1995-1997 Eric P. Allman.  All rights reserved.
4 * Copyright (c) 1988, 1993
5 *      The Regents of the University of California.  All rights reserved.
6 *
7 * By using this file, you agree to the terms and conditions set
8 * forth in the LICENSE file which can be found at the top level of
9 * the sendmail distribution.
10 *
11 */
12
13#ifndef lint
14static char sccsid[] = "@(#)headers.c   8.136 (Berkeley) 1/26/1999";
15#endif /* not lint */
16
17# include <errno.h>
18# include "sendmail.h"
19
20/*
21**  SETUPHEADERS -- initialize headers in symbol table
22**
23**      Parameters:
24**              none
25**
26**      Returns:
27**              none
28*/
29
30void
31setupheaders()
32{
33        struct hdrinfo *hi;
34        STAB *s;
35
36        for (hi = HdrInfo; hi->hi_field != NULL; hi++)
37        {
38                s = stab(hi->hi_field, ST_HEADER, ST_ENTER);
39                s->s_header.hi_flags = hi->hi_flags;
40                s->s_header.hi_ruleset = NULL;
41        }
42}
43/*
44**  CHOMPHEADER -- process and save a header line.
45**
46**      Called by collect and by readcf to deal with header lines.
47**
48**      Parameters:
49**              line -- header as a text line.
50**              def -- if set, this is a default value.
51**              hdrp -- a pointer to the place to save the header.
52**              e -- the envelope including this header.
53**
54**      Returns:
55**              flags for this header.
56**
57**      Side Effects:
58**              The header is saved on the header list.
59**              Contents of 'line' are destroyed.
60*/
61
62struct hdrinfo  NormalHeader =  { NULL, 0, NULL };
63
64int
65chompheader(line, def, hdrp, e)
66        char *line;
67        bool def;
68        HDR **hdrp;
69        register ENVELOPE *e;
70{
71        register char *p;
72        register HDR *h;
73        HDR **hp;
74        char *fname;
75        char *fvalue;
76        bool cond = FALSE;
77        bool headeronly;
78        STAB *s;
79        struct hdrinfo *hi;
80        bool nullheader = FALSE;
81        BITMAP mopts;
82
83        if (tTd(31, 6))
84        {
85                printf("chompheader: ");
86                xputs(line);
87                printf("\n");
88        }
89
90        headeronly = hdrp != NULL;
91        if (!headeronly)
92                hdrp = &e->e_header;
93
94        /* strip off options */
95        clrbitmap(mopts);
96        p = line;
97        if (*p == '?')
98        {
99                /* have some */
100                register char *q = strchr(p + 1, *p);
101               
102                if (q != NULL)
103                {
104                        *q++ = '\0';
105                        while (*++p != '\0')
106                                setbitn(*p, mopts);
107                        p = q;
108                }
109                else
110                        syserr("553 header syntax error, line \"%s\"", line);
111                cond = TRUE;
112        }
113
114        /* find canonical name */
115        fname = p;
116        while (isascii(*p) && isgraph(*p) && *p != ':')
117                p++;
118        fvalue = p;
119        while (isascii(*p) && isspace(*p))
120                p++;
121        if (*p++ != ':' || fname == fvalue)
122        {
123                syserr("553 header syntax error, line \"%s\"", line);
124                return 0;
125        }
126        *fvalue = '\0';
127
128        /* strip field value on front */
129        if (*p == ' ')
130                p++;
131        fvalue = p;
132
133        /* if the field is null, go ahead and use the default */
134        while (isascii(*p) && isspace(*p))
135                p++;
136        if (*p == '\0')
137                nullheader = TRUE;
138
139        /* security scan: long field names are end-of-header */
140        if (strlen(fname) > 100)
141                return H_EOH;
142
143        /* check to see if it represents a ruleset call */
144        if (def)
145        {
146                char hbuf[50];
147
148                (void) expand(fvalue, hbuf, sizeof hbuf, e);
149                for (p = hbuf; isascii(*p) && isspace(*p); )
150                        p++;
151                if ((*p++ & 0377) == CALLSUBR)
152                {
153                        auto char *endp;
154
155                        if (strtorwset(p, &endp, ST_ENTER) > 0)
156                        {
157                                *endp = '\0';
158                                s = stab(fname, ST_HEADER, ST_ENTER);
159                                s->s_header.hi_ruleset = newstr(p);
160                        }
161                        return 0;
162                }
163        }
164
165        /* see if it is a known type */
166        s = stab(fname, ST_HEADER, ST_FIND);
167        if (s != NULL)
168                hi = &s->s_header;
169        else
170                hi = &NormalHeader;
171
172        if (tTd(31, 9))
173        {
174                if (s == NULL)
175                        printf("no header flags match\n");
176                else
177                        printf("header match, flags=%x, ruleset=%s\n",
178                                hi->hi_flags,
179                                hi->hi_ruleset == NULL ? "<NULL>" : hi->hi_ruleset);
180        }
181
182        /* see if this is a resent message */
183        if (!def && !headeronly && bitset(H_RESENT, hi->hi_flags))
184                e->e_flags |= EF_RESENT;
185
186        /* if this is an Errors-To: header keep track of it now */
187        if (UseErrorsTo && !def && !headeronly &&
188            bitset(H_ERRORSTO, hi->hi_flags))
189                (void) sendtolist(fvalue, NULLADDR, &e->e_errorqueue, 0, e);
190
191        /* if this means "end of header" quit now */
192        if (!headeronly && bitset(H_EOH, hi->hi_flags))
193                return hi->hi_flags;
194
195        /*
196        **  Horrible hack to work around problem with Lotus Notes SMTP
197        **  mail gateway, which generates From: headers with newlines in
198        **  them and the <address> on the second line.  Although this is
199        **  legal RFC 822, many MUAs don't handle this properly and thus
200        **  never find the actual address.
201        */
202
203        if (bitset(H_FROM, hi->hi_flags) && SingleLineFromHeader)
204        {
205                while ((p = strchr(fvalue, '\n')) != NULL)
206                        *p = ' ';
207        }
208
209        /*
210        **  If there is a check ruleset, verify it against the header.
211        */
212
213        if (!def && hi->hi_ruleset != NULL)
214                (void) rscheck(hi->hi_ruleset, fvalue, NULL, e);
215
216        /*
217        **  Drop explicit From: if same as what we would generate.
218        **  This is to make MH (which doesn't always give a full name)
219        **  insert the full name information in all circumstances.
220        */
221
222        p = "resent-from";
223        if (!bitset(EF_RESENT, e->e_flags))
224                p += 7;
225        if (!def && !headeronly && !bitset(EF_QUEUERUN, e->e_flags) &&
226            strcasecmp(fname, p) == 0)
227        {
228                if (tTd(31, 2))
229                {
230                        printf("comparing header from (%s) against default (%s or %s)\n",
231                                fvalue, e->e_from.q_paddr, e->e_from.q_user);
232                }
233                if (e->e_from.q_paddr != NULL &&
234                    (strcmp(fvalue, e->e_from.q_paddr) == 0 ||
235                     strcmp(fvalue, e->e_from.q_user) == 0))
236                        return hi->hi_flags;
237        }
238
239        /* delete default value for this header */
240        for (hp = hdrp; (h = *hp) != NULL; hp = &h->h_link)
241        {
242                if (strcasecmp(fname, h->h_field) == 0 &&
243                    bitset(H_DEFAULT, h->h_flags) &&
244                    !bitset(H_FORCE, h->h_flags))
245                {
246                        if (nullheader)
247                        {
248                                /* user-supplied value was null */
249                                return 0;
250                        }
251                        h->h_value = NULL;
252                        if (!cond)
253                        {
254                                /* copy conditions from default case */
255                                bcopy((char *)h->h_mflags, (char *)mopts,
256                                                sizeof mopts);
257                        }
258                }
259        }
260
261        /* create a new node */
262        h = (HDR *) xalloc(sizeof *h);
263        h->h_field = newstr(fname);
264        h->h_value = newstr(fvalue);
265        h->h_link = NULL;
266        bcopy((char *) mopts, (char *) h->h_mflags, sizeof mopts);
267        *hp = h;
268        h->h_flags = hi->hi_flags;
269
270        /* strip EOH flag if parsing MIME headers */
271        if (headeronly)
272                h->h_flags &= ~H_EOH;
273        if (def)
274                h->h_flags |= H_DEFAULT;
275        if (cond)
276                h->h_flags |= H_CHECK;
277
278        /* hack to see if this is a new format message */
279        if (!def && !headeronly && bitset(H_RCPT|H_FROM, h->h_flags) &&
280            (strchr(fvalue, ',') != NULL || strchr(fvalue, '(') != NULL ||
281             strchr(fvalue, '<') != NULL || strchr(fvalue, ';') != NULL))
282        {
283                e->e_flags &= ~EF_OLDSTYLE;
284        }
285
286        return h->h_flags;
287}
288/*
289**  ADDHEADER -- add a header entry to the end of the queue.
290**
291**      This bypasses the special checking of chompheader.
292**
293**      Parameters:
294**              field -- the name of the header field.
295**              value -- the value of the field.
296**              hp -- an indirect pointer to the header structure list.
297**
298**      Returns:
299**              none.
300**
301**      Side Effects:
302**              adds the field on the list of headers for this envelope.
303*/
304
305void
306addheader(field, value, hdrlist)
307        char *field;
308        char *value;
309        HDR **hdrlist;
310{
311        register HDR *h;
312        STAB *s;
313        HDR **hp;
314
315        /* find info struct */
316        s = stab(field, ST_HEADER, ST_FIND);
317
318        /* find current place in list -- keep back pointer? */
319        for (hp = hdrlist; (h = *hp) != NULL; hp = &h->h_link)
320        {
321                if (strcasecmp(field, h->h_field) == 0)
322                        break;
323        }
324
325        /* allocate space for new header */
326        h = (HDR *) xalloc(sizeof *h);
327        h->h_field = field;
328        h->h_value = newstr(value);
329        h->h_link = *hp;
330        h->h_flags = H_DEFAULT;
331        if (s != NULL)
332                h->h_flags |= s->s_header.hi_flags;
333        clrbitmap(h->h_mflags);
334        *hp = h;
335}
336/*
337**  HVALUE -- return value of a header.
338**
339**      Only "real" fields (i.e., ones that have not been supplied
340**      as a default) are used.
341**
342**      Parameters:
343**              field -- the field name.
344**              header -- the header list.
345**
346**      Returns:
347**              pointer to the value part.
348**              NULL if not found.
349**
350**      Side Effects:
351**              none.
352*/
353
354char *
355hvalue(field, header)
356        char *field;
357        HDR *header;
358{
359        register HDR *h;
360
361        for (h = header; h != NULL; h = h->h_link)
362        {
363                if (!bitset(H_DEFAULT, h->h_flags) &&
364                    strcasecmp(h->h_field, field) == 0)
365                        return (h->h_value);
366        }
367        return (NULL);
368}
369/*
370**  ISHEADER -- predicate telling if argument is a header.
371**
372**      A line is a header if it has a single word followed by
373**      optional white space followed by a colon.
374**
375**      Header fields beginning with two dashes, although technically
376**      permitted by RFC822, are automatically rejected in order
377**      to make MIME work out.  Without this we could have a technically
378**      legal header such as ``--"foo:bar"'' that would also be a legal
379**      MIME separator.
380**
381**      Parameters:
382**              h -- string to check for possible headerness.
383**
384**      Returns:
385**              TRUE if h is a header.
386**              FALSE otherwise.
387**
388**      Side Effects:
389**              none.
390*/
391
392bool
393isheader(h)
394        char *h;
395{
396        register char *s = h;
397
398        if (s[0] == '-' && s[1] == '-')
399                return FALSE;
400
401        while (*s > ' ' && *s != ':' && *s != '\0')
402                s++;
403
404        if (h == s)
405                return FALSE;
406
407        /* following technically violates RFC822 */
408        while (isascii(*s) && isspace(*s))
409                s++;
410
411        return (*s == ':');
412}
413/*
414**  EATHEADER -- run through the stored header and extract info.
415**
416**      Parameters:
417**              e -- the envelope to process.
418**              full -- if set, do full processing (e.g., compute
419**                      message priority).  This should not be set
420**                      when reading a queue file because some info
421**                      needed to compute the priority is wrong.
422**
423**      Returns:
424**              none.
425**
426**      Side Effects:
427**              Sets a bunch of global variables from information
428**                      in the collected header.
429**              Aborts the message if the hop count is exceeded.
430*/
431
432void
433eatheader(e, full)
434        register ENVELOPE *e;
435        bool full;
436{
437        register HDR *h;
438        register char *p;
439        int hopcnt = 0;
440        char *msgid;
441        char buf[MAXLINE];
442        extern int priencode __P((char *));
443
444        /*
445        **  Set up macros for possible expansion in headers.
446        */
447
448        define('f', e->e_sender, e);
449        define('g', e->e_sender, e);
450        if (e->e_origrcpt != NULL && *e->e_origrcpt != '\0')
451                define('u', e->e_origrcpt, e);
452        else
453                define('u', NULL, e);
454
455        /* full name of from person */
456        p = hvalue("full-name", e->e_header);
457        if (p != NULL)
458        {
459                extern bool rfc822_string __P((char *));
460
461                if (!rfc822_string(p))
462                {
463                        extern char *addquotes __P((char *));
464
465                        /*
466                        **  Quote a full name with special characters
467                        **  as a comment so crackaddr() doesn't destroy
468                        **  the name portion of the address.
469                        */
470                        p = addquotes(p);
471                }
472                define('x', p, e);
473        }
474
475        if (tTd(32, 1))
476                printf("----- collected header -----\n");
477        msgid = NULL;
478        for (h = e->e_header; h != NULL; h = h->h_link)
479        {
480                if (tTd(32, 1))
481                        printf("%s: ", h->h_field);
482                if (h->h_value == NULL)
483                {
484                        if (tTd(32, 1))
485                                printf("<NULL>\n");
486                        continue;
487                }
488
489                /* do early binding */
490                if (bitset(H_DEFAULT, h->h_flags))
491                {
492                        if (tTd(32, 1))
493                        {
494                                printf("(");
495                                xputs(h->h_value);
496                                printf(") ");
497                        }
498                        expand(h->h_value, buf, sizeof buf, e);
499                        if (buf[0] != '\0')
500                        {
501                                if (bitset(H_FROM, h->h_flags))
502                                {
503                                        extern char *crackaddr __P((char *));
504
505                                        expand(crackaddr(buf), buf, sizeof buf, e);
506                                }
507                                h->h_value = newstr(buf);
508                                h->h_flags &= ~H_DEFAULT;
509                        }
510                }
511
512                if (tTd(32, 1))
513                {
514                        xputs(h->h_value);
515                        printf("\n");
516                }
517
518                /* count the number of times it has been processed */
519                if (bitset(H_TRACE, h->h_flags))
520                        hopcnt++;
521
522                /* send to this person if we so desire */
523                if (GrabTo && bitset(H_RCPT, h->h_flags) &&
524                    !bitset(H_DEFAULT, h->h_flags) &&
525                    (!bitset(EF_RESENT, e->e_flags) || bitset(H_RESENT, h->h_flags)))
526                {
527#if 0
528                        int saveflags = e->e_flags;
529#endif
530
531                        (void) sendtolist(h->h_value, NULLADDR,
532                                          &e->e_sendqueue, 0, e);
533
534#if 0
535                        /*
536                        **  Change functionality so a fatal error on an
537                        **  address doesn't affect the entire envelope.
538                        */
539                         
540                        /* delete fatal errors generated by this address */
541                        if (!bitset(EF_FATALERRS, saveflags))
542                                e->e_flags &= ~EF_FATALERRS;
543#endif
544                }
545
546                /* save the message-id for logging */
547                p = "resent-message-id";
548                if (!bitset(EF_RESENT, e->e_flags))
549                        p += 7;
550                if (strcasecmp(h->h_field, p) == 0)
551                {
552                        msgid = h->h_value;
553                        while (isascii(*msgid) && isspace(*msgid))
554                                msgid++;
555                }
556        }
557        if (tTd(32, 1))
558                printf("----------------------------\n");
559
560        /* if we are just verifying (that is, sendmail -t -bv), drop out now */
561        if (OpMode == MD_VERIFY)
562                return;
563
564        /* store hop count */
565        if (hopcnt > e->e_hopcount)
566                e->e_hopcount = hopcnt;
567
568        /* message priority */
569        p = hvalue("precedence", e->e_header);
570        if (p != NULL)
571                e->e_class = priencode(p);
572        if (e->e_class < 0)
573                e->e_timeoutclass = TOC_NONURGENT;
574        else if (e->e_class > 0)
575                e->e_timeoutclass = TOC_URGENT;
576        if (full)
577        {
578                e->e_msgpriority = e->e_msgsize
579                                 - e->e_class * WkClassFact
580                                 + e->e_nrcpts * WkRecipFact;
581        }
582
583        /* message timeout priority */
584        p = hvalue("priority", e->e_header);
585        if (p != NULL)
586        {
587                /* (this should be in the configuration file) */
588                if (strcasecmp(p, "urgent") == 0)
589                        e->e_timeoutclass = TOC_URGENT;
590                else if (strcasecmp(p, "normal") == 0)
591                        e->e_timeoutclass = TOC_NORMAL;
592                else if (strcasecmp(p, "non-urgent") == 0)
593                        e->e_timeoutclass = TOC_NONURGENT;
594        }
595
596        /* date message originated */
597        p = hvalue("posted-date", e->e_header);
598        if (p == NULL)
599                p = hvalue("date", e->e_header);
600        if (p != NULL)
601                define('a', p, e);
602
603        /* check to see if this is a MIME message */
604        if ((e->e_bodytype != NULL &&
605             strcasecmp(e->e_bodytype, "8BITMIME") == 0) ||
606            hvalue("MIME-Version", e->e_header) != NULL)
607        {
608                e->e_flags |= EF_IS_MIME;
609                if (HasEightBits)
610                        e->e_bodytype = "8BITMIME";
611        }
612        else if ((p = hvalue("Content-Type", e->e_header)) != NULL)
613        {
614                /* this may be an RFC 1049 message */
615                p = strpbrk(p, ";/");
616                if (p == NULL || *p == ';')
617                {
618                        /* yep, it is */
619                        e->e_flags |= EF_DONT_MIME;
620                }
621        }
622
623        /*
624        **  From person in antiquated ARPANET mode
625        **      required by UK Grey Book e-mail gateways (sigh)
626        */
627
628        if (OpMode == MD_ARPAFTP)
629        {
630                register struct hdrinfo *hi;
631
632                for (hi = HdrInfo; hi->hi_field != NULL; hi++)
633                {
634                        if (bitset(H_FROM, hi->hi_flags) &&
635                            (!bitset(H_RESENT, hi->hi_flags) ||
636                             bitset(EF_RESENT, e->e_flags)) &&
637                            (p = hvalue(hi->hi_field, e->e_header)) != NULL)
638                                break;
639                }
640                if (hi->hi_field != NULL)
641                {
642                        if (tTd(32, 2))
643                                printf("eatheader: setsender(*%s == %s)\n",
644                                        hi->hi_field, p);
645                        setsender(p, e, NULL, '\0', TRUE);
646                }
647        }
648
649        /*
650        **  Log collection information.
651        */
652
653        if (bitset(EF_LOGSENDER, e->e_flags) && LogLevel > 4)
654                logsender(e, msgid);
655        e->e_flags &= ~EF_LOGSENDER;
656}
657/*
658**  LOGSENDER -- log sender information
659**
660**      Parameters:
661**              e -- the envelope to log
662**              msgid -- the message id
663**
664**      Returns:
665**              none
666*/
667
668void
669logsender(e, msgid)
670        register ENVELOPE *e;
671        char *msgid;
672{
673        char *name;
674        register char *sbp;
675        register char *p;
676        int l;
677        char hbuf[MAXNAME + 1];
678        char sbuf[MAXLINE + 1];
679        char mbuf[MAXNAME + 1];
680
681        /* don't allow newlines in the message-id */
682        if (msgid != NULL)
683        {
684                l = strlen(msgid);
685                if (l > sizeof mbuf - 1)
686                        l = sizeof mbuf - 1;
687                bcopy(msgid, mbuf, l);
688                mbuf[l] = '\0';
689                p = mbuf;
690                while ((p = strchr(p, '\n')) != NULL)
691                        *p++ = ' ';
692        }
693
694        if (bitset(EF_RESPONSE, e->e_flags))
695                name = "[RESPONSE]";
696        else if ((name = macvalue('_', e)) != NULL)
697                ;
698        else if (RealHostName == NULL)
699                name = "localhost";
700        else if (RealHostName[0] == '[')
701                name = RealHostName;
702        else
703        {
704                name = hbuf;
705                (void) snprintf(hbuf, sizeof hbuf, "%.80s", RealHostName);
706                if (RealHostAddr.sa.sa_family != 0)
707                {
708                        p = &hbuf[strlen(hbuf)];
709                        (void) snprintf(p, SPACELEFT(hbuf, p), " (%.100s)",
710                                anynet_ntoa(&RealHostAddr));
711                }
712        }
713
714        /* some versions of syslog only take 5 printf args */
715#  if (SYSLOG_BUFSIZE) >= 256
716        sbp = sbuf;
717        snprintf(sbp, SPACELEFT(sbuf, sbp),
718            "from=%.200s, size=%ld, class=%d, pri=%ld, nrcpts=%d",
719            e->e_from.q_paddr == NULL ? "<NONE>" : e->e_from.q_paddr,
720            e->e_msgsize, e->e_class, e->e_msgpriority, e->e_nrcpts);
721        sbp += strlen(sbp);
722        if (msgid != NULL)
723        {
724                snprintf(sbp, SPACELEFT(sbuf, sbp), ", msgid=%.100s", mbuf);
725                sbp += strlen(sbp);
726        }
727        if (e->e_bodytype != NULL)
728        {
729                (void) snprintf(sbp, SPACELEFT(sbuf, sbp), ", bodytype=%.20s",
730                        e->e_bodytype);
731                sbp += strlen(sbp);
732        }
733        p = macvalue('r', e);
734        if (p != NULL)
735                (void) snprintf(sbp, SPACELEFT(sbuf, sbp), ", proto=%.20s", p);
736        sm_syslog(LOG_INFO, e->e_id,
737                "%.850s, relay=%.100s",
738                sbuf, name);
739
740#  else                 /* short syslog buffer */
741
742        sm_syslog(LOG_INFO, e->e_id,
743                "from=%s",
744                e->e_from.q_paddr == NULL ? "<NONE>"
745                                          : shortenstring(e->e_from.q_paddr, 83));
746        sm_syslog(LOG_INFO, e->e_id,
747                "size=%ld, class=%ld, pri=%ld, nrcpts=%d",
748                e->e_msgsize, e->e_class, e->e_msgpriority, e->e_nrcpts);
749        if (msgid != NULL)
750                sm_syslog(LOG_INFO, e->e_id,
751                        "msgid=%s",
752                        shortenstring(mbuf, 83));
753        sbp = sbuf;
754        *sbp = '\0';
755        if (e->e_bodytype != NULL)
756        {
757                snprintf(sbp, SPACELEFT(sbuf, sbp), "bodytype=%.20s, ", e->e_bodytype);
758                sbp += strlen(sbp);
759        }
760        p = macvalue('r', e);
761        if (p != NULL)
762        {
763                snprintf(sbp, SPACELEFT(sbuf, sbp), "proto=%.20s, ", p);
764                sbp += strlen(sbp);
765        }
766        sm_syslog(LOG_INFO, e->e_id,
767                "%.400srelay=%.100s", sbuf, name);
768#  endif
769}
770/*
771**  PRIENCODE -- encode external priority names into internal values.
772**
773**      Parameters:
774**              p -- priority in ascii.
775**
776**      Returns:
777**              priority as a numeric level.
778**
779**      Side Effects:
780**              none.
781*/
782
783int
784priencode(p)
785        char *p;
786{
787        register int i;
788
789        for (i = 0; i < NumPriorities; i++)
790        {
791                if (!strcasecmp(p, Priorities[i].pri_name))
792                        return (Priorities[i].pri_val);
793        }
794
795        /* unknown priority */
796        return (0);
797}
798/*
799**  CRACKADDR -- parse an address and turn it into a macro
800**
801**      This doesn't actually parse the address -- it just extracts
802**      it and replaces it with "$g".  The parse is totally ad hoc
803**      and isn't even guaranteed to leave something syntactically
804**      identical to what it started with.  However, it does leave
805**      something semantically identical.
806**
807**      This algorithm has been cleaned up to handle a wider range
808**      of cases -- notably quoted and backslash escaped strings.
809**      This modification makes it substantially better at preserving
810**      the original syntax.
811**
812**      Parameters:
813**              addr -- the address to be cracked.
814**
815**      Returns:
816**              a pointer to the new version.
817**
818**      Side Effects:
819**              none.
820**
821**      Warning:
822**              The return value is saved in local storage and should
823**              be copied if it is to be reused.
824*/
825
826char *
827crackaddr(addr)
828        register char *addr;
829{
830        register char *p;
831        register char c;
832        int cmtlev;
833        int realcmtlev;
834        int anglelev, realanglelev;
835        int copylev;
836        int bracklev;
837        bool qmode;
838        bool realqmode;
839        bool skipping;
840        bool putgmac = FALSE;
841        bool quoteit = FALSE;
842        bool gotangle = FALSE;
843        bool gotcolon = FALSE;
844        register char *bp;
845        char *buflim;
846        char *bufhead;
847        char *addrhead;
848        static char buf[MAXNAME + 1];
849
850        if (tTd(33, 1))
851                printf("crackaddr(%s)\n", addr);
852
853        /* strip leading spaces */
854        while (*addr != '\0' && isascii(*addr) && isspace(*addr))
855                addr++;
856
857        /*
858        **  Start by assuming we have no angle brackets.  This will be
859        **  adjusted later if we find them.
860        */
861
862        bp = bufhead = buf;
863        buflim = &buf[sizeof buf - 7];
864        p = addrhead = addr;
865        copylev = anglelev = realanglelev = cmtlev = realcmtlev = 0;
866        bracklev = 0;
867        qmode = realqmode = FALSE;
868
869        while ((c = *p++) != '\0')
870        {
871                /*
872                **  If the buffer is overful, go into a special "skipping"
873                **  mode that tries to keep legal syntax but doesn't actually
874                **  output things.
875                */
876
877                skipping = bp >= buflim;
878
879                if (copylev > 0 && !skipping)
880                        *bp++ = c;
881
882                /* check for backslash escapes */
883                if (c == '\\')
884                {
885                        /* arrange to quote the address */
886                        if (cmtlev <= 0 && !qmode)
887                                quoteit = TRUE;
888
889                        if ((c = *p++) == '\0')
890                        {
891                                /* too far */
892                                p--;
893                                goto putg;
894                        }
895                        if (copylev > 0 && !skipping)
896                                *bp++ = c;
897                        goto putg;
898                }
899
900                /* check for quoted strings */
901                if (c == '"' && cmtlev <= 0)
902                {
903                        qmode = !qmode;
904                        if (copylev > 0 && !skipping)
905                                realqmode = !realqmode;
906                        continue;
907                }
908                if (qmode)
909                        goto putg;
910
911                /* check for comments */
912                if (c == '(')
913                {
914                        cmtlev++;
915
916                        /* allow space for closing paren */
917                        if (!skipping)
918                        {
919                                buflim--;
920                                realcmtlev++;
921                                if (copylev++ <= 0)
922                                {
923                                        if (bp != bufhead)
924                                                *bp++ = ' ';
925                                        *bp++ = c;
926                                }
927                        }
928                }
929                if (cmtlev > 0)
930                {
931                        if (c == ')')
932                        {
933                                cmtlev--;
934                                copylev--;
935                                if (!skipping)
936                                {
937                                        realcmtlev--;
938                                        buflim++;
939                                }
940                        }
941                        continue;
942                }
943                else if (c == ')')
944                {
945                        /* syntax error: unmatched ) */
946                        if (copylev > 0 && !skipping)
947                                bp--;
948                }
949
950                /* count nesting on [ ... ] (for IPv6 domain literals) */
951                if (c == '[')
952                        bracklev++;
953                else if (c == ']')
954                        bracklev--;
955
956                /* check for group: list; syntax */
957                if (c == ':' && anglelev <= 0 && bracklev <= 0 &&
958                    !gotcolon && !ColonOkInAddr)
959                {
960                        register char *q;
961
962                        /*
963                        **  Check for DECnet phase IV ``::'' (host::user)
964                        **  or **  DECnet phase V ``:.'' syntaxes.  The latter
965                        **  covers ``user@DEC:.tay.myhost'' and
966                        **  ``DEC:.tay.myhost::user'' syntaxes (bletch).
967                        */
968
969                        if (*p == ':' || *p == '.')
970                        {
971                                if (cmtlev <= 0 && !qmode)
972                                        quoteit = TRUE;
973                                if (copylev > 0 && !skipping)
974                                {
975                                        *bp++ = c;
976                                        *bp++ = *p;
977                                }
978                                p++;
979                                goto putg;
980                        }
981
982                        gotcolon = TRUE;
983
984                        bp = bufhead;
985                        if (quoteit)
986                        {
987                                *bp++ = '"';
988
989                                /* back up over the ':' and any spaces */
990                                --p;
991                                while (isascii(*--p) && isspace(*p))
992                                        continue;
993                                p++;
994                        }
995                        for (q = addrhead; q < p; )
996                        {
997                                c = *q++;
998                                if (bp < buflim)
999                                {
1000                                        if (quoteit && c == '"')
1001                                                *bp++ = '\\';
1002                                        *bp++ = c;
1003                                }
1004                        }
1005                        if (quoteit)
1006                        {
1007                                if (bp == &bufhead[1])
1008                                        bp--;
1009                                else
1010                                        *bp++ = '"';
1011                                while ((c = *p++) != ':')
1012                                {
1013                                        if (bp < buflim)
1014                                                *bp++ = c;
1015                                }
1016                                *bp++ = c;
1017                        }
1018
1019                        /* any trailing white space is part of group: */
1020                        while (isascii(*p) && isspace(*p) && bp < buflim)
1021                                *bp++ = *p++;
1022                        copylev = 0;
1023                        putgmac = quoteit = FALSE;
1024                        bufhead = bp;
1025                        addrhead = p;
1026                        continue;
1027                }
1028
1029                if (c == ';' && copylev <= 0 && !ColonOkInAddr)
1030                {
1031                        if (bp < buflim)
1032                                *bp++ = c;
1033                }
1034
1035                /* check for characters that may have to be quoted */
1036                if (strchr(MustQuoteChars, c) != NULL)
1037                {
1038                        /*
1039                        **  If these occur as the phrase part of a <>
1040                        **  construct, but are not inside of () or already
1041                        **  quoted, they will have to be quoted.  Note that
1042                        **  now (but don't actually do the quoting).
1043                        */
1044
1045                        if (cmtlev <= 0 && !qmode)
1046                                quoteit = TRUE;
1047                }
1048
1049                /* check for angle brackets */
1050                if (c == '<')
1051                {
1052                        register char *q;
1053
1054                        /* assume first of two angles is bogus */
1055                        if (gotangle)
1056                                quoteit = TRUE;
1057                        gotangle = TRUE;
1058
1059                        /* oops -- have to change our mind */
1060                        anglelev = 1;
1061                        if (!skipping)
1062                                realanglelev = 1;
1063
1064                        bp = bufhead;
1065                        if (quoteit)
1066                        {
1067                                *bp++ = '"';
1068
1069                                /* back up over the '<' and any spaces */
1070                                --p;
1071                                while (isascii(*--p) && isspace(*p))
1072                                        continue;
1073                                p++;
1074                        }
1075                        for (q = addrhead; q < p; )
1076                        {
1077                                c = *q++;
1078                                if (bp < buflim)
1079                                {
1080                                        if (quoteit && c == '"')
1081                                                *bp++ = '\\';
1082                                        *bp++ = c;
1083                                }
1084                        }
1085                        if (quoteit)
1086                        {
1087                                if (bp == &buf[1])
1088                                        bp--;
1089                                else
1090                                        *bp++ = '"';
1091                                while ((c = *p++) != '<')
1092                                {
1093                                        if (bp < buflim)
1094                                                *bp++ = c;
1095                                }
1096                                *bp++ = c;
1097                        }
1098                        copylev = 0;
1099                        putgmac = quoteit = FALSE;
1100                        continue;
1101                }
1102
1103                if (c == '>')
1104                {
1105                        if (anglelev > 0)
1106                        {
1107                                anglelev--;
1108                                if (!skipping)
1109                                {
1110                                        realanglelev--;
1111                                        buflim++;
1112                                }
1113                        }
1114                        else if (!skipping)
1115                        {
1116                                /* syntax error: unmatched > */
1117                                if (copylev > 0)
1118                                        bp--;
1119                                quoteit = TRUE;
1120                                continue;
1121                        }
1122                        if (copylev++ <= 0)
1123                                *bp++ = c;
1124                        continue;
1125                }
1126
1127                /* must be a real address character */
1128        putg:
1129                if (copylev <= 0 && !putgmac)
1130                {
1131                        if (bp > bufhead && bp[-1] == ')')
1132                                *bp++ = ' ';
1133                        *bp++ = MACROEXPAND;
1134                        *bp++ = 'g';
1135                        putgmac = TRUE;
1136                }
1137        }
1138
1139        /* repair any syntactic damage */
1140        if (realqmode)
1141                *bp++ = '"';
1142        while (realcmtlev-- > 0)
1143                *bp++ = ')';
1144        while (realanglelev-- > 0)
1145                *bp++ = '>';
1146        *bp++ = '\0';
1147
1148        if (tTd(33, 1))
1149        {
1150                printf("crackaddr=>`");
1151                xputs(buf);
1152                printf("'\n");
1153        }
1154
1155        return (buf);
1156}
1157/*
1158**  PUTHEADER -- put the header part of a message from the in-core copy
1159**
1160**      Parameters:
1161**              mci -- the connection information.
1162**              h -- the header to put.
1163**              e -- envelope to use.
1164**              flags -- MIME conversion flags.
1165**
1166**      Returns:
1167**              none.
1168**
1169**      Side Effects:
1170**              none.
1171*/
1172
1173/*
1174 * Macro for fast max (not available in e.g. DG/UX, 386/ix).
1175 */
1176#ifndef MAX
1177# define MAX(a,b) (((a)>(b))?(a):(b))
1178#endif
1179
1180void
1181putheader(mci, hdr, e, flags)
1182        register MCI *mci;
1183        HDR *hdr;
1184        register ENVELOPE *e;
1185        int flags;
1186{
1187        register HDR *h;
1188        char buf[MAX(MAXLINE,BUFSIZ)];
1189        char obuf[MAXLINE];
1190
1191        if (tTd(34, 1))
1192                printf("--- putheader, mailer = %s ---\n",
1193                        mci->mci_mailer->m_name);
1194
1195        /*
1196        **  If we're in MIME mode, we're not really in the header of the
1197        **  message, just the header of one of the parts of the body of
1198        **  the message.  Therefore MCIF_INHEADER should not be turned on.
1199        */
1200
1201        if (!bitset(MCIF_INMIME, mci->mci_flags))
1202                mci->mci_flags |= MCIF_INHEADER;
1203
1204        for (h = hdr; h != NULL; h = h->h_link)
1205        {
1206                register char *p = h->h_value;
1207                extern bool bitintersect __P((BITMAP, BITMAP));
1208
1209                if (tTd(34, 11))
1210                {
1211                        printf("  %s: ", h->h_field);
1212                        xputs(p);
1213                }
1214
1215#if _FFR_MAX_MIME_HEADER_LENGTH
1216                /* heuristic shortening of MIME fields to avoid MUA overflows */
1217                if (MaxMimeFieldLength > 0 &&
1218                    wordinclass(h->h_field,
1219                                macid("{checkMIMEFieldHeaders}", NULL)))
1220                {
1221                        extern bool fix_mime_header __P((char *));
1222
1223                        if (fix_mime_header(h->h_value))
1224                        {
1225                                sm_syslog(LOG_ALERT, e->e_id,
1226                                        "Truncated MIME %s header due to field size (possible attack)",
1227                                        h->h_field);
1228                                if (tTd(34, 11))
1229                                        printf("  truncated MIME %s header due to field size (possible attack)\n",
1230                                                h->h_field);
1231                        }
1232                }
1233
1234                if (MaxMimeHeaderLength > 0 &&
1235                    wordinclass(h->h_field,
1236                                macid("{checkMIMETextHeaders}", NULL)))
1237                {
1238                        if (strlen(h->h_value) > MaxMimeHeaderLength)
1239                        {
1240                                h->h_value[MaxMimeHeaderLength - 1] = '\0';
1241                                sm_syslog(LOG_ALERT, e->e_id,
1242                                        "Truncated long MIME %s header (possible attack)",
1243                                        h->h_field);
1244                                if (tTd(34, 11))
1245                                        printf("  truncated long MIME %s header (possible attack)\n",
1246                                                h->h_field);
1247                        }
1248                }
1249
1250                if (MaxMimeHeaderLength > 0 &&
1251                    wordinclass(h->h_field,
1252                                macid("{checkMIMEHeaders}", NULL)))
1253                {
1254                        extern bool shorten_rfc822_string __P((char *, int));
1255
1256                        if (shorten_rfc822_string(h->h_value, MaxMimeHeaderLength))
1257                        {
1258                                sm_syslog(LOG_ALERT, e->e_id,
1259                                        "Truncated long MIME %s header (possible attack)",
1260                                        h->h_field);
1261                                if (tTd(34, 11))
1262                                        printf("  truncated long MIME %s header (possible attack)\n",
1263                                                h->h_field);
1264                        }
1265                }
1266#endif
1267
1268                /*
1269                **  Suppress Content-Transfer-Encoding: if we are MIMEing
1270                **  and we are potentially converting from 8 bit to 7 bit
1271                **  MIME.  If converting, add a new CTE header in
1272                **  mime8to7().
1273                */
1274                if (bitset(H_CTE, h->h_flags) &&
1275                    bitset(MCIF_CVT8TO7|MCIF_CVT7TO8|MCIF_INMIME,
1276                           mci->mci_flags) &&
1277                    !bitset(M87F_NO8TO7, flags))
1278                {
1279                        if (tTd(34, 11))
1280                                printf(" (skipped (content-transfer-encoding))\n");
1281                        continue;
1282                }
1283
1284                if (bitset(MCIF_INMIME, mci->mci_flags))
1285                {
1286                        if (tTd(34, 11))
1287                                printf("\n");
1288                        put_vanilla_header(h, p, mci);
1289                        continue;
1290                }
1291
1292                if (bitset(H_CHECK|H_ACHECK, h->h_flags) &&
1293                    !bitintersect(h->h_mflags, mci->mci_mailer->m_flags))
1294                {
1295                        if (tTd(34, 11))
1296                                printf(" (skipped)\n");
1297                        continue;
1298                }
1299
1300                /* handle Resent-... headers specially */
1301                if (bitset(H_RESENT, h->h_flags) && !bitset(EF_RESENT, e->e_flags))
1302                {
1303                        if (tTd(34, 11))
1304                                printf(" (skipped (resent))\n");
1305                        continue;
1306                }
1307
1308                /* suppress return receipts if requested */
1309                if (bitset(H_RECEIPTTO, h->h_flags) &&
1310#if _FFR_DSN_RRT_OPTION
1311                    (RrtImpliesDsn || bitset(EF_NORECEIPT, e->e_flags)))
1312#else
1313                    bitset(EF_NORECEIPT, e->e_flags))
1314#endif
1315                {
1316                        if (tTd(34, 11))
1317                                printf(" (skipped (receipt))\n");
1318                        continue;
1319                }
1320
1321                /* macro expand value if generated internally */
1322                if (bitset(H_DEFAULT, h->h_flags))
1323                {
1324                        expand(p, buf, sizeof buf, e);
1325                        p = buf;
1326                        if (*p == '\0')
1327                        {
1328                                if (tTd(34, 11))
1329                                        printf(" (skipped -- null value)\n");
1330                                continue;
1331                        }
1332                }
1333
1334                if (bitset(H_BCC, h->h_flags))
1335                {
1336                        /* Bcc: field -- either truncate or delete */
1337                        if (bitset(EF_DELETE_BCC, e->e_flags))
1338                        {
1339                                if (tTd(34, 11))
1340                                        printf(" (skipped -- bcc)\n");
1341                        }
1342                        else
1343                        {
1344                                /* no other recipient headers: truncate value */
1345                                (void) snprintf(obuf, sizeof obuf, "%s:",
1346                                        h->h_field);
1347                                putline(obuf, mci);
1348                        }
1349                        continue;
1350                }
1351
1352                if (tTd(34, 11))
1353                        printf("\n");
1354
1355                if (bitset(H_FROM|H_RCPT, h->h_flags))
1356                {
1357                        /* address field */
1358                        bool oldstyle = bitset(EF_OLDSTYLE, e->e_flags);
1359
1360                        if (bitset(H_FROM, h->h_flags))
1361                                oldstyle = FALSE;
1362                        commaize(h, p, oldstyle, mci, e);
1363                }
1364                else
1365                {
1366                        put_vanilla_header(h, p, mci);
1367                }
1368        }
1369
1370        /*
1371        **  If we are converting this to a MIME message, add the
1372        **  MIME headers.
1373        */
1374
1375#if MIME8TO7
1376        if (bitset(MM_MIME8BIT, MimeMode) &&
1377            bitset(EF_HAS8BIT, e->e_flags) &&
1378            !bitset(EF_DONT_MIME, e->e_flags) &&
1379            !bitnset(M_8BITS, mci->mci_mailer->m_flags) &&
1380            !bitset(MCIF_CVT8TO7|MCIF_CVT7TO8, mci->mci_flags))
1381        {
1382                if (hvalue("MIME-Version", e->e_header) == NULL)
1383                        putline("MIME-Version: 1.0", mci);
1384                if (hvalue("Content-Type", e->e_header) == NULL)
1385                {
1386                        snprintf(obuf, sizeof obuf,
1387                                "Content-Type: text/plain; charset=%s",
1388                                defcharset(e));
1389                        putline(obuf, mci);
1390                }
1391                if (hvalue("Content-Transfer-Encoding", e->e_header) == NULL)
1392                        putline("Content-Transfer-Encoding: 8bit", mci);
1393        }
1394#endif
1395}
1396/*
1397**  PUT_VANILLA_HEADER -- output a fairly ordinary header
1398**
1399**      Parameters:
1400**              h -- the structure describing this header
1401**              v -- the value of this header
1402**              mci -- the connection info for output
1403**
1404**      Returns:
1405**              none.
1406*/
1407
1408void
1409put_vanilla_header(h, v, mci)
1410        HDR *h;
1411        char *v;
1412        MCI *mci;
1413{
1414        register char *nlp;
1415        register char *obp;
1416        int putflags;
1417        char obuf[MAXLINE];
1418
1419        putflags = PXLF_HEADER;
1420#if _FFR_7BITHDRS
1421        if (bitnset(M_7BITHDRS, mci->mci_mailer->m_flags))
1422                putflags |= PXLF_STRIP8BIT;
1423#endif
1424        (void) snprintf(obuf, sizeof obuf, "%.200s: ", h->h_field);
1425        obp = obuf + strlen(obuf);
1426        while ((nlp = strchr(v, '\n')) != NULL)
1427        {
1428                int l;
1429
1430                l = nlp - v;
1431                if (SPACELEFT(obuf, obp) - 1 < l)
1432                        l = SPACELEFT(obuf, obp) - 1;
1433
1434                snprintf(obp, SPACELEFT(obuf, obp), "%.*s", l, v);
1435                putxline(obuf, strlen(obuf), mci, putflags);
1436                v += l + 1;
1437                obp = obuf;
1438                if (*v != ' ' && *v != '\t')
1439                        *obp++ = ' ';
1440        }
1441        snprintf(obp, SPACELEFT(obuf, obp), "%.*s",
1442                sizeof obuf - (obp - obuf) - 1, v);
1443        putxline(obuf, strlen(obuf), mci, putflags);
1444}
1445/*
1446**  COMMAIZE -- output a header field, making a comma-translated list.
1447**
1448**      Parameters:
1449**              h -- the header field to output.
1450**              p -- the value to put in it.
1451**              oldstyle -- TRUE if this is an old style header.
1452**              mci -- the connection information.
1453**              e -- the envelope containing the message.
1454**
1455**      Returns:
1456**              none.
1457**
1458**      Side Effects:
1459**              outputs "p" to file "fp".
1460*/
1461
1462void
1463commaize(h, p, oldstyle, mci, e)
1464        register HDR *h;
1465        register char *p;
1466        bool oldstyle;
1467        register MCI *mci;
1468        register ENVELOPE *e;
1469{
1470        register char *obp;
1471        int opos;
1472        int omax;
1473        bool firstone = TRUE;
1474        int putflags = PXLF_HEADER;
1475        char obuf[MAXLINE + 3];
1476
1477        /*
1478        **  Output the address list translated by the
1479        **  mailer and with commas.
1480        */
1481
1482        if (tTd(14, 2))
1483                printf("commaize(%s: %s)\n", h->h_field, p);
1484
1485#if _FFR_7BITHDRS
1486        if (bitnset(M_7BITHDRS, mci->mci_mailer->m_flags))
1487                putflags |= PXLF_STRIP8BIT;
1488#endif
1489
1490        obp = obuf;
1491        (void) snprintf(obp, SPACELEFT(obuf, obp), "%.200s: ", h->h_field);
1492        opos = strlen(h->h_field) + 2;
1493        if (opos > 202)
1494                opos = 202;
1495        obp += opos;
1496        omax = mci->mci_mailer->m_linelimit - 2;
1497        if (omax < 0 || omax > 78)
1498                omax = 78;
1499
1500        /*
1501        **  Run through the list of values.
1502        */
1503
1504        while (*p != '\0')
1505        {
1506                register char *name;
1507                register int c;
1508                char savechar;
1509                int flags;
1510                auto int stat;
1511
1512                /*
1513                **  Find the end of the name.  New style names
1514                **  end with a comma, old style names end with
1515                **  a space character.  However, spaces do not
1516                **  necessarily delimit an old-style name -- at
1517                **  signs mean keep going.
1518                */
1519
1520                /* find end of name */
1521                while ((isascii(*p) && isspace(*p)) || *p == ',')
1522                        p++;
1523                name = p;
1524                for (;;)
1525                {
1526                        auto char *oldp;
1527                        char pvpbuf[PSBUFSIZE];
1528
1529                        (void) prescan(p, oldstyle ? ' ' : ',', pvpbuf,
1530                                       sizeof pvpbuf, &oldp, NULL);
1531                        p = oldp;
1532
1533                        /* look to see if we have an at sign */
1534                        while (*p != '\0' && isascii(*p) && isspace(*p))
1535                                p++;
1536
1537                        if (*p != '@')
1538                        {
1539                                p = oldp;
1540                                break;
1541                        }
1542                        p += *p == '@' ? 1 : 2;
1543                        while (*p != '\0' && isascii(*p) && isspace(*p))
1544                                p++;
1545                }
1546                /* at the end of one complete name */
1547
1548                /* strip off trailing white space */
1549                while (p >= name &&
1550                       ((isascii(*p) && isspace(*p)) || *p == ',' || *p == '\0'))
1551                        p--;
1552                if (++p == name)
1553                        continue;
1554                savechar = *p;
1555                *p = '\0';
1556
1557                /* translate the name to be relative */
1558                flags = RF_HEADERADDR|RF_ADDDOMAIN;
1559                if (bitset(H_FROM, h->h_flags))
1560                        flags |= RF_SENDERADDR;
1561#if USERDB
1562                else if (e->e_from.q_mailer != NULL &&
1563                         bitnset(M_UDBRECIPIENT, e->e_from.q_mailer->m_flags))
1564                {
1565                        extern char *udbsender __P((char *));
1566                        char *q;
1567
1568                        q = udbsender(name);
1569                        if (q != NULL)
1570                                name = q;
1571                }
1572#endif
1573                stat = EX_OK;
1574                name = remotename(name, mci->mci_mailer, flags, &stat, e);
1575                if (*name == '\0')
1576                {
1577                        *p = savechar;
1578                        continue;
1579                }
1580                name = denlstring(name, FALSE, TRUE);
1581
1582                /* output the name with nice formatting */
1583                opos += strlen(name);
1584                if (!firstone)
1585                        opos += 2;
1586                if (opos > omax && !firstone)
1587                {
1588                        snprintf(obp, SPACELEFT(obuf, obp), ",\n");
1589                        putxline(obuf, strlen(obuf), mci, putflags);
1590                        obp = obuf;
1591                        (void) strcpy(obp, "        ");
1592                        opos = strlen(obp);
1593                        obp += opos;
1594                        opos += strlen(name);
1595                }
1596                else if (!firstone)
1597                {
1598                        snprintf(obp, SPACELEFT(obuf, obp), ", ");
1599                        obp += 2;
1600                }
1601
1602                while ((c = *name++) != '\0' && obp < &obuf[MAXLINE])
1603                        *obp++ = c;
1604                firstone = FALSE;
1605                *p = savechar;
1606        }
1607        *obp = '\0';
1608        putxline(obuf, strlen(obuf), mci, putflags);
1609}
1610/*
1611**  COPYHEADER -- copy header list
1612**
1613**      This routine is the equivalent of newstr for header lists
1614**
1615**      Parameters:
1616**              header -- list of header structures to copy.
1617**
1618**      Returns:
1619**              a copy of 'header'.
1620**
1621**      Side Effects:
1622**              none.
1623*/
1624
1625HDR *
1626copyheader(header)
1627        register HDR *header;
1628{
1629        register HDR *newhdr;
1630        HDR *ret;
1631        register HDR **tail = &ret;
1632
1633        while (header != NULL)
1634        {
1635                newhdr = (HDR *) xalloc(sizeof(HDR));
1636                STRUCTCOPY(*header, *newhdr);
1637                *tail = newhdr;
1638                tail = &newhdr->h_link;
1639                header = header->h_link;
1640        }
1641        *tail = NULL;
1642       
1643        return ret;
1644}
1645/*
1646**  FIX_MIME_HEADER -- possibly truncate/rebalance parameters in a MIME header
1647**
1648**      Run through all of the parameters of a MIME header and
1649**      possibly truncate and rebalance the parameter according
1650**      to MaxMimeFieldLength.
1651**
1652**      Parameters:
1653**              string -- the full header
1654**
1655**      Returns:
1656**              TRUE if the header was modified, FALSE otherwise
1657**
1658**      Side Effects:
1659**              string modified in place
1660*/
1661
1662bool
1663fix_mime_header(string)
1664        char *string;
1665{
1666        bool modified = FALSE;
1667        char *begin = string;
1668        char *end;
1669        extern char *find_character __P((char *, char));
1670        extern bool shorten_rfc822_string __P((char *, int));
1671       
1672        if (string == NULL || *string == '\0')
1673                return FALSE;
1674       
1675        /* Split on each ';' */
1676        while ((end = find_character(begin, ';')) != NULL)
1677        {
1678                char save = *end;
1679                char *bp;
1680               
1681                *end = '\0';
1682               
1683                /* Shorten individual parameter */
1684                if (shorten_rfc822_string(begin, MaxMimeFieldLength))
1685                        modified = TRUE;
1686               
1687                /* Collapse the possibly shortened string with rest */
1688                bp = begin + strlen(begin);
1689                if (bp != end)
1690                {
1691                        char *ep = end;
1692                       
1693                        *end = save;
1694                        end = bp;
1695                       
1696                        /* copy character by character due to overlap */
1697                        while (*ep != '\0')
1698                                *bp++ = *ep++;
1699                        *bp = '\0';
1700                }
1701                else
1702                        *end = save;
1703                if (*end == '\0')
1704                        break;
1705               
1706                /* Move past ';' */
1707                begin = end + 1;
1708        }
1709        return modified;
1710}
Note: See TracBrowser for help on using the repository browser.