2 * Heirloom mailx - a mail user agent derived from Berkeley Mail.
4 * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany.
7 * Copyright (c) 1980, 1993
8 * The Regents of the University of California. All rights reserved.
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 * 3. All advertising materials mentioning features or use of this software
19 * must display the following acknowledgement:
20 * This product includes software developed by the University of
21 * California, Berkeley and its contributors.
22 * 4. Neither the name of the University nor the names of its contributors
23 * may be used to endorse or promote products derived from this software
24 * without specific prior written permission.
26 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
27 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
28 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
29 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
30 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
31 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
32 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
33 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
34 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
35 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
41 static char sccsid
[] = "@(#)head.c 2.17 (gritter) 3/4/06";
50 * Mail -- a mail program
52 * Routines for processing and detecting headlines.
55 static char *copyin(char *src
, char **space
);
56 static char *nextword(char *wp
, char *wbuf
);
57 static int gethfield(FILE *f
, char **linebuf
, size_t *linesize
, int rem
,
59 static int msgidnextc(const char **cp
, int *status
);
60 static int charcount(char *str
, int c
);
63 * See if the passed line buffer is a mail header.
64 * Return true if yes. POSIX.2 leaves the content
65 * following 'From ' unspecified, so don't care about
70 is_head(char *linebuf
, size_t linelen
)
75 if (*cp
++ != 'F' || *cp
++ != 'r' || *cp
++ != 'o' || *cp
++ != 'm' ||
82 * Split a headline into its useful components.
83 * Copy the line into dynamic string space, then set
84 * pointers into the copied line in the passed headline
85 * structure. Actually, it scans.
88 parse(char *line
, size_t linelen
, struct headline
*hl
, char *pbuf
)
99 word
= ac_alloc(linelen
+ 1);
101 * Skip over "From" first.
103 cp
= nextword(cp
, word
);
104 cp
= nextword(cp
, word
);
106 hl
->l_from
= copyin(word
, &sp
);
107 if (cp
!= NULL
&& cp
[0] == 't' && cp
[1] == 't' && cp
[2] == 'y') {
108 cp
= nextword(cp
, word
);
109 hl
->l_tty
= copyin(word
, &sp
);
112 hl
->l_date
= copyin(cp
, &sp
);
114 hl
->l_date
= catgets(catd
, CATSET
, 213, "<Unknown date>");
119 * Copy the string on the left into the string on the right
120 * and bump the right (reference) string pointer by the length.
121 * Thus, dynamically allocate space in the right string, copying
122 * the left string into it.
125 copyin(char *src
, char **space
)
131 while ((*cp
++ = *src
++) != '\0')
138 static int cmatch(char *, char *);
140 * Test to see if the passed string is a ctime(3) generated
141 * date string as documented in the manual. The template
142 * below is used as the criterion of correctness.
143 * Also, we check for a possible trailing time zone using
144 * the tmztype template.
148 * 'A' An upper case char
149 * 'a' A lower case char
152 * 'O' An optional digit or space
157 static char *tmztype
[] = {
158 "Aaa Aaa O0 00:00:00 0000",
159 "Aaa Aaa O0 00:00 0000",
160 "Aaa Aaa O0 00:00:00 AAA 0000",
161 "Aaa Aaa O0 00:00 AAA 0000",
163 * Sommer time, e.g. MET DST
165 "Aaa Aaa O0 00:00:00 AAA AAA 0000",
166 "Aaa Aaa O0 00:00 AAA AAA 0000",
168 * time zone offset, e.g.
169 * +0200 or +0200 MET or +0200 MET DST
171 "Aaa Aaa O0 00:00:00 +0000 0000",
172 "Aaa Aaa O0 00:00 +0000 0000",
173 "Aaa Aaa O0 00:00:00 +0000 AAA 0000",
174 "Aaa Aaa O0 00:00 +0000 AAA 0000",
175 "Aaa Aaa O0 00:00:00 +0000 AAA AAA 0000",
176 "Aaa Aaa O0 00:00 +0000 AAA AAA 0000",
178 * time zone offset without time zone specification (pine)
180 "Aaa Aaa O0 00:00:00 0000 +0000",
187 int ret
= 0, form
= 0;
189 while (tmztype
[form
]) {
190 if ( (ret
= cmatch(date
, tmztype
[form
])) == 1 )
199 * Match the given string (cp) against the given template (tp).
200 * Return 1 if they match, 0 if they don't
203 cmatch(char *cp
, char *tp
)
210 if (c
= *cp
++, !lowerchar(c
))
214 if (c
= *cp
++, !upperchar(c
))
222 if (c
= *cp
++, !digitchar(c
))
226 if (c
= *cp
, c
!= ' ' && !digitchar(c
))
235 if (*cp
!= '+' && *cp
!= '-')
251 * Collect a liberal (space, tab delimited) word into the word buffer
252 * passed. Also, return a pointer to the next word following that,
253 * or NULL if none follow.
256 nextword(char *wp
, char *wbuf
)
264 while ((c
= *wp
++) != '\0' && !blankchar(c
)) {
267 while ((c
= *wp
++) != '\0' && c
!= '"')
276 for (; blankchar(c
); c
= *wp
++)
284 extract_header(FILE *fp
, struct header
*hp
)
286 char *linebuf
= NULL
;
289 char *colon
, *cp
, *value
;
291 struct header
*hq
= &nh
;
294 memset(hq
, 0, sizeof *hq
);
295 for (lc
= 0; readline(fp
, &linebuf
, &linesize
) > 0; lc
++);
297 while ((lc
= gethfield(fp
, &linebuf
, &linesize
, lc
, &colon
)) >= 0) {
298 if ((value
= thisfield(linebuf
, "to")) != NULL
) {
300 hq
->h_to
= checkaddrs(cat(hq
->h_to
,
301 sextract(value
, GTO
|GFULL
)));
302 } else if ((value
= thisfield(linebuf
, "cc")) != NULL
) {
304 hq
->h_cc
= checkaddrs(cat(hq
->h_cc
,
305 sextract(value
, GCC
|GFULL
)));
306 } else if ((value
= thisfield(linebuf
, "bcc")) != NULL
) {
308 hq
->h_bcc
= checkaddrs(cat(hq
->h_bcc
,
309 sextract(value
, GBCC
|GFULL
)));
310 } else if ((value
= thisfield(linebuf
, "from")) != NULL
) {
312 hq
->h_from
= checkaddrs(cat(hq
->h_from
,
313 sextract(value
, GEXTRA
|GFULL
)));
314 } else if ((value
= thisfield(linebuf
, "reply-to")) != NULL
) {
316 hq
->h_replyto
= checkaddrs(cat(hq
->h_replyto
,
317 sextract(value
, GEXTRA
|GFULL
)));
318 } else if ((value
= thisfield(linebuf
, "sender")) != NULL
) {
320 hq
->h_sender
= checkaddrs(cat(hq
->h_sender
,
321 sextract(value
, GEXTRA
|GFULL
)));
322 } else if ((value
= thisfield(linebuf
,
323 "organization")) != NULL
) {
325 for (cp
= value
; blankchar(*cp
& 0377); cp
++);
326 hq
->h_organization
= hq
->h_organization
?
327 save2str(hq
->h_organization
, cp
) :
329 } else if ((value
= thisfield(linebuf
, "subject")) != NULL
||
330 (value
= thisfield(linebuf
, "subj")) != NULL
) {
332 for (cp
= value
; blankchar(*cp
& 0377); cp
++);
333 hq
->h_subject
= hq
->h_subject
?
334 save2str(hq
->h_subject
, cp
) :
337 fprintf(stderr
, catgets(catd
, CATSET
, 266,
338 "Ignoring header field \"%s\"\n"),
342 * In case the blank line after the header has been edited out.
343 * Otherwise, fetch the header separator.
346 if (linebuf
[0] != '\0') {
347 for (cp
= linebuf
; *(++cp
) != '\0'; );
348 fseek(fp
, (long)-(1 + cp
- linebuf
), SEEK_CUR
);
350 if ((c
= getc(fp
)) != '\n' && c
!= EOF
)
357 hp
->h_bcc
= hq
->h_bcc
;
358 hp
->h_from
= hq
->h_from
;
359 hp
->h_replyto
= hq
->h_replyto
;
360 hp
->h_sender
= hq
->h_sender
;
361 hp
->h_organization
= hq
->h_organization
;
362 hp
->h_subject
= hq
->h_subject
;
364 fprintf(stderr
, catgets(catd
, CATSET
, 267,
365 "Restoring deleted header lines\n"));
371 * Return the desired header line from the passed message
372 * pointer (or NULL if the desired header field is not available).
373 * If mult is zero, return the content of the first matching header
374 * field only, the content of all matching header fields else.
377 hfield_mult(char *field
, struct message
*mp
, int mult
)
380 char *linebuf
= NULL
;
384 char *colon
, *oldhfield
= NULL
;
386 if ((ibuf
= setinput(&mb
, mp
, NEED_HEADER
)) == NULL
)
388 if ((lc
= mp
->m_lines
- 1) < 0)
390 if ((mp
->m_flag
& MNOFROM
) == 0) {
391 if (readline(ibuf
, &linebuf
, &linesize
) < 0) {
398 if ((lc
= gethfield(ibuf
, &linebuf
, &linesize
, lc
, &colon
))
404 if ((hfield
= thisfield(linebuf
, field
)) != NULL
) {
405 oldhfield
= save2str(hfield
, oldhfield
);
416 * Return the next header field found in the given message.
417 * Return >= 0 if something found, < 0 elsewise.
418 * "colon" is set to point to the colon in the header.
419 * Must deal with \ continuations & other such fraud.
422 gethfield(FILE *f
, char **linebuf
, size_t *linesize
, int rem
, char **colon
)
425 size_t line2size
= 0;
429 if (*linebuf
== NULL
)
430 *linebuf
= srealloc(*linebuf
, *linesize
= 1);
435 if ((c
= readline(f
, linebuf
, linesize
)) <= 0)
437 for (cp
= *linebuf
; fieldnamechar(*cp
& 0377); cp
++);
439 while (blankchar(*cp
& 0377))
441 if (*cp
!= ':' || cp
== *linebuf
)
444 * I guess we got a headline.
445 * Handle wraparounding
451 while (--cp
>= *linebuf
&& blankchar(*cp
& 0377));
455 if (cp
-8 >= *linebuf
&& cp
[-1] == '=' && cp
[-2] == '?')
457 ungetc(c
= getc(f
), f
);
460 if ((c
= readline(f
, &line2
, &line2size
)) < 0)
463 for (cp2
= line2
; blankchar(*cp2
& 0377); cp2
++);
465 if (cp2
[0] == '=' && cp2
[1] == '?' && c
> 8)
467 if (cp
+ c
>= *linebuf
+ *linesize
- 2) {
468 size_t diff
= cp
- *linebuf
;
469 size_t colondiff
= *colon
- *linebuf
;
470 *linebuf
= srealloc(*linebuf
,
472 cp
= &(*linebuf
)[diff
];
473 *colon
= &(*linebuf
)[colondiff
];
489 * Check whether the passed line is a header line of
490 * the desired breed. Return the field body, or 0.
493 thisfield(const char *linebuf
, const char *field
)
495 while (lowerconv(*linebuf
&0377) == lowerconv(*field
&0377)) {
501 while (blankchar(*linebuf
&0377))
503 if (*linebuf
++ != ':')
505 while (blankchar(*linebuf
&0377))
507 return (char *)linebuf
;
511 * Get sender's name from this message. If the message has
512 * a bunch of arpanet stuff in it, we may have to skin the name
513 * before returning it.
516 nameof(struct message
*mp
, int reptype
)
520 cp
= skin(name1(mp
, reptype
));
521 if (reptype
!= 0 || charcount(cp
, '!') < 2)
523 cp2
= strrchr(cp
, '!');
525 while (cp2
> cp
&& *cp2
!= '!')
533 * Start of a "comment".
537 skip_comment(const char *cp
)
541 for (; nesting
> 0 && *cp
; cp
++) {
559 * Return the start of a route-addr (address in angle brackets),
563 routeaddr(const char *name
)
565 const char *np
, *rp
= NULL
;
567 for (np
= name
; *np
; np
++) {
570 np
= skip_comment(&np
[1]) - 1;
576 if (*np
== '\\' && np
[1])
591 * Skin an arpa net address according to the RFC 822 interpretation
605 if (strchr(name
, '(') == NULL
&& strchr(name
, '<') == NULL
606 && strchr(name
, ' ') == NULL
)
610 nbuf
= ac_alloc(strlen(name
) + 1);
612 for (cp
= name
, cp2
= bufend
; (c
= *cp
++) != '\0'; ) {
615 cp
= skip_comment(cp
);
621 * Start of a "quoted-string".
622 * Copy it in its entirety.
625 while ((c
= *cp
) != '\0') {
633 else if ((c
= *cp
) != '\0') {
642 if (cp
[0] == 'a' && cp
[1] == 't' && cp
[2] == ' ')
643 cp
+= 3, *cp2
++ = '@';
645 if (cp
[0] == '@' && cp
[1] == ' ')
646 cp
+= 2, *cp2
++ = '@';
649 * RFC 822 specifies spaces are STRIPPED when
650 * in an adress specifier.
666 while ((c
= *cp
) != '\0' && c
!= ',') {
669 cp
= skip_comment(cp
);
671 while ((c
= *cp
) != '\0') {
675 if (c
== '\\' && *cp
)
682 /* Fall into . . . */
690 if (c
== ',' && !gotlt
) {
692 for (; *cp
== ' '; cp
++)
706 * Fetch the real name from an internet mail address field.
711 char *cstart
= NULL
, *cend
= NULL
, *cp
, *cq
;
714 int quoted
, good
, nogood
;
718 for (cp
= name
; *cp
; cp
++) {
723 * More than one comment in address, doesn't
724 * make sense to display it without context.
725 * Return the entire field,
727 return mime_fromaddr(name
);
729 cp
= skip_comment(cp
);
732 cend
= cstart
= NULL
;
738 if (*cp
== '\\' && cp
[1])
750 * More than one address. Just use the first one.
755 brk
: if (cstart
== NULL
) {
758 * If name contains only a route-addr, the
759 * surrounding angle brackets don't serve any
760 * useful purpose when displaying, so they
763 return prstr(skin(name
));
764 return mime_fromaddr(name
);
766 rp
= rname
= ac_alloc(cend
- cstart
+ 1);
768 * Strip quotes. Note that quotes that appear within a MIME-
769 * encoded word are not stripped. The idea is to strip only
770 * syntactical relevant things (but this is not necessarily
771 * the most sensible way in practice).
774 for (cp
= cstart
; cp
< cend
; cp
++) {
775 if (*cp
== '(' && !quoted
) {
776 cq
= skip_comment(++cp
);
780 if (*cp
== '\\' && &cp
[1] < cq
)
784 } else if (*cp
== '\\' && &cp
[1] < cend
)
786 else if (*cp
== '"') {
795 mime_fromhdr(&in
, &out
, TD_ISPR
|TD_ICONV
);
797 rname
= savestr(out
.s
);
799 while (blankchar(*rname
& 0377))
801 for (rp
= rname
; *rp
; rp
++);
802 while (--rp
>= rname
&& blankchar(*rp
& 0377))
805 return mime_fromaddr(name
);
807 * mime_fromhdr() has converted all nonprintable characters to
808 * question marks now. These and blanks are considered uninteresting;
809 * if the displayed part of the real name contains more than 25% of
810 * them, it is probably better to display the plain email address
815 for (rp
= rname
; *rp
&& rp
< &rname
[20]; rp
++)
816 if (*rp
== '?' || blankchar(*rp
& 0377))
821 return prstr(skin(name
));
826 * Fetch the sender's name from the passed message.
828 * 0 -- get sender's name for display purposes
829 * 1 -- get sender's name for reply
830 * 2 -- get sender's name for Reply
833 name1(struct message
*mp
, int reptype
)
837 char *linebuf
= NULL
;
843 if ((cp
= hfield("from", mp
)) != NULL
&& *cp
!= '\0')
845 if (reptype
== 0 && (cp
= hfield("sender", mp
)) != NULL
&&
848 namebuf
= smalloc(namesize
= 1);
850 if (mp
->m_flag
& MNOFROM
)
852 if ((ibuf
= setinput(&mb
, mp
, NEED_HEADER
)) == NULL
)
854 if (readline(ibuf
, &linebuf
, &linesize
) < 0)
857 if (namesize
<= linesize
)
858 namebuf
= srealloc(namebuf
, namesize
= linesize
+ 1);
859 for (cp
= linebuf
; *cp
&& *cp
!= ' '; cp
++)
861 for (; blankchar(*cp
& 0377); cp
++);
862 for (cp2
= &namebuf
[strlen(namebuf
)];
863 *cp
&& !blankchar(*cp
& 0377) && cp2
< namebuf
+ namesize
- 1;)
866 if (readline(ibuf
, &linebuf
, &linesize
) < 0)
868 if ((cp
= strchr(linebuf
, 'F')) == NULL
)
870 if (strncmp(cp
, "From", 4) != 0)
872 if (namesize
<= linesize
)
873 namebuf
= srealloc(namebuf
, namesize
= linesize
+ 1);
874 while ((cp
= strchr(cp
, 'r')) != NULL
) {
875 if (strncmp(cp
, "remote", 6) == 0) {
876 if ((cp
= strchr(cp
, 'f')) == NULL
)
878 if (strncmp(cp
, "from", 4) != 0)
880 if ((cp
= strchr(cp
, ' ')) == NULL
)
884 strncpy(namebuf
, cp
, namesize
);
887 cp2
=strrchr(namebuf
, '!')+1;
888 strncpy(cp2
, cp
, (namebuf
+namesize
)-cp2
);
890 namebuf
[namesize
-2]='\0';
891 strcat(namebuf
, "!");
897 if (*namebuf
!= '\0' || ((cp
= hfield("return-path", mp
))) == NULL
||
899 cp
= savestr(namebuf
);
907 msgidnextc(const char **cp
, int *status
)
927 *cp
= skip_comment(&(*cp
)[1]);
943 return *status
& 02 ? lowerconv(c
) : c
;
949 msgidcmp(const char *s1
, const char *s2
)
955 c1
= msgidnextc(&s1
, &q1
);
956 c2
= msgidnextc(&s2
, &q2
);
964 * Count the occurances of c in str
967 charcount(char *str
, int c
)
972 for (i
= 0, cp
= str
; *cp
; cp
++)
979 * See if the given header field is supposed to be ignored.
982 is_ign(char *field
, size_t fieldlen
, struct ignoretab ignore
[2])
989 if (ignore
== allignore
)
992 * Lower-case the string, so that "Status" and "status"
993 * will hash to the same place.
995 realfld
= ac_alloc(fieldlen
+ 1);
996 i_strcpy(realfld
, field
, fieldlen
+ 1);
997 if (ignore
[1].i_count
> 0)
998 ret
= !member(realfld
, ignore
+ 1);
1000 ret
= member(realfld
, ignore
);
1006 member(char *realfield
, struct ignoretab
*table
)
1010 for (igp
= table
->i_head
[hash(realfield
)]; igp
!= 0; igp
= igp
->i_link
)
1011 if (*igp
->i_field
== *realfield
&&
1012 equal(igp
->i_field
, realfield
))
1018 * Fake Sender for From_ lines if missing, e. g. with POP3.
1021 fakefrom(struct message
*mp
)
1025 if (((name
= skin(hfield("return-path", mp
))) == NULL
||
1027 ((name
= skin(hfield("from", mp
))) == NULL
||
1039 for (cq
= cp
; *cq
&& *cq
!= '\n'; cq
++);
1053 while (*cp
!= '\0') {
1065 } else if (blankchar(*cp
& 0377) || *cp
== ',')
1074 * From username Fri Jan 2 20:13:51 2004
1079 unixtime(char *from
)
1083 int i
, year
, month
, day
, hour
, minute
, second
;
1087 for (fp
= from
; *fp
&& *fp
!= '\n'; fp
++);
1093 for (i
= 0; month_names
[i
]; i
++)
1094 if (strncmp(&fp
[4], month_names
[i
], 3) == 0)
1096 if (month_names
[i
] == 0)
1101 day
= strtol(&fp
[8], &xp
, 10);
1102 if (*xp
!= ' ' || xp
!= &fp
[10])
1104 hour
= strtol(&fp
[11], &xp
, 10);
1105 if (*xp
!= ':' || xp
!= &fp
[13])
1107 minute
= strtol(&fp
[14], &xp
, 10);
1108 if (*xp
!= ':' || xp
!= &fp
[16])
1110 second
= strtol(&fp
[17], &xp
, 10);
1111 if (*xp
!= ' ' || xp
!= &fp
[19])
1113 year
= strtol(&fp
[20], &xp
, 10);
1116 if ((t
= combinetime(year
, month
, day
, hour
, minute
, second
)) ==
1119 tzdiff
= t
- mktime(gmtime(&t
));
1120 tmptr
= localtime(&t
);
1121 if (tmptr
->tm_isdst
> 0)
1133 char *cp
= date
, *x
;
1135 int i
, year
, month
, day
, hour
, minute
, second
;
1137 if ((cp
= nexttoken(cp
)) == NULL
)
1139 if (alphachar(cp
[0] & 0377) && alphachar(cp
[1] & 0377) &&
1140 alphachar(cp
[2] & 0377) && cp
[3] == ',') {
1141 if ((cp
= nexttoken(&cp
[4])) == NULL
)
1144 day
= strtol(cp
, &x
, 10);
1145 if ((cp
= nexttoken(x
)) == NULL
)
1147 for (i
= 0; month_names
[i
]; i
++) {
1148 if (strncmp(cp
, month_names
[i
], 3) == 0)
1151 if (month_names
[i
] == NULL
)
1154 if ((cp
= nexttoken(&cp
[3])) == NULL
)
1156 year
= strtol(cp
, &x
, 10);
1157 if ((cp
= nexttoken(x
)) == NULL
)
1159 hour
= strtol(cp
, &x
, 10);
1163 minute
= strtol(cp
, &x
, 10);
1166 second
= strtol(cp
, &x
, 10);
1169 if ((t
= combinetime(year
, month
, day
, hour
, minute
, second
)) ==
1172 if ((cp
= nexttoken(x
)) != NULL
) {
1183 if (digitchar(cp
[0] & 0377) && digitchar(cp
[1] & 0377) &&
1184 digitchar(cp
[2] & 0377) &&
1185 digitchar(cp
[3] & 0377)) {
1189 t
+= strtol(buf
, NULL
, 10) * sign
* 3600;
1192 t
+= strtol(buf
, NULL
, 10) * sign
* 60;
1200 #define leapyear(year) ((year % 100 ? year : year / 100) % 4 == 0)
1203 combinetime(int year
, int month
, int day
, int hour
, int minute
, int second
)
1207 if (second
< 0 || minute
< 0 || hour
< 0 || day
< 1)
1209 t
= second
+ minute
* 60 + hour
* 3600 + (day
- 1) * 86400;
1212 else if (year
< 1900)
1217 t
+= 86400 * (leapyear(year
) ? 29 : 28);
1237 t
+= (year
- 70) * 31536000 + ((year
- 69) / 4) * 86400 -
1238 ((year
- 1) / 100) * 86400 + ((year
+ 299) / 400) * 86400;
1243 substdate(struct message
*m
)
1249 * Determine the date to print in faked 'From ' lines. This is
1250 * traditionally the date the message was written to the mail
1251 * file. Try to determine this using RFC message header fields,
1252 * or fall back to current time.
1255 if ((cp
= hfield_mult("received", m
, 0)) != NULL
) {
1256 while ((cp
= nexttoken(cp
)) != NULL
&& *cp
!= ';') {
1259 while (alnumchar(*cp
& 0377));
1262 m
->m_time
= rfctime(cp
);
1264 if (m
->m_time
== 0 || m
->m_time
> now
)
1265 if ((cp
= hfield("date", m
)) != NULL
)
1266 m
->m_time
= rfctime(cp
);
1267 if (m
->m_time
== 0 || m
->m_time
> now
)
1272 check_from_and_sender(struct name
*fromfield
, struct name
*senderfield
)
1274 if (fromfield
&& fromfield
->n_flink
&& senderfield
== NULL
) {
1275 fprintf(stderr
, "A Sender: field is required with multiple "
1276 "addresses in From: field.\n");
1279 if (senderfield
&& senderfield
->n_flink
) {
1280 fprintf(stderr
, "The Sender: field may contain "
1281 "only one address.\n");
1288 getsender(struct message
*mp
)
1293 if ((cp
= hfield("from", mp
)) == NULL
||
1294 (np
= sextract(cp
, GEXTRA
|GSKIN
)) == NULL
)
1296 return np
->n_flink
!= NULL
? skin(hfield("sender", mp
)) : np
->n_name
;