2 * Heirloom mailx - a mail user agent derived from Berkeley Mail.
4 * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany.
5 * Copyright (c) 2012 Steffen "Daode" Nurpmeso.
8 * Copyright (c) 1980, 1993
9 * The Regents of the University of California. All rights reserved.
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 * 3. All advertising materials mentioning features or use of this software
20 * must display the following acknowledgement:
21 * This product includes software developed by the University of
22 * California, Berkeley and its contributors.
23 * 4. Neither the name of the University nor the names of its contributors
24 * may be used to endorse or promote products derived from this software
25 * without specific prior written permission.
27 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
28 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
29 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
30 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
31 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
32 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
33 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
34 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
35 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
36 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
42 static char sccsid
[] = "@(#)head.c 2.17 (gritter) 3/4/06";
51 * Mail -- a mail program
53 * Routines for processing and detecting headlines.
56 static char *copyin(char *src
, char **space
);
57 static char *nextword(char *wp
, char *wbuf
);
58 static int gethfield(FILE *f
, char **linebuf
, size_t *linesize
, int rem
,
60 static int msgidnextc(const char **cp
, int *status
);
61 static int charcount(char *str
, int c
);
64 * See if the passed line buffer is a mail header.
65 * Return true if yes. POSIX.2 leaves the content
66 * following 'From ' unspecified, so don't care about
71 is_head(char *linebuf
, size_t linelen
)
77 if (*cp
++ != 'F' || *cp
++ != 'r' || *cp
++ != 'o' || *cp
++ != 'm' ||
84 * Split a headline into its useful components.
85 * Copy the line into dynamic string space, then set
86 * pointers into the copied line in the passed headline
87 * structure. Actually, it scans.
90 parse(char *line
, size_t linelen
, struct headline
*hl
, char *pbuf
)
101 word
= ac_alloc(linelen
+ 1);
103 * Skip over "From" first.
105 cp
= nextword(cp
, word
);
106 cp
= nextword(cp
, word
);
108 hl
->l_from
= copyin(word
, &sp
);
109 if (cp
!= NULL
&& cp
[0] == 't' && cp
[1] == 't' && cp
[2] == 'y') {
110 cp
= nextword(cp
, word
);
111 hl
->l_tty
= copyin(word
, &sp
);
114 hl
->l_date
= copyin(cp
, &sp
);
116 hl
->l_date
= catgets(catd
, CATSET
, 213, "<Unknown date>");
121 * Copy the string on the left into the string on the right
122 * and bump the right (reference) string pointer by the length.
123 * Thus, dynamically allocate space in the right string, copying
124 * the left string into it.
127 copyin(char *src
, char **space
)
133 while ((*cp
++ = *src
++) != '\0')
140 static int cmatch(char *, char *);
142 * Test to see if the passed string is a ctime(3) generated
143 * date string as documented in the manual. The template
144 * below is used as the criterion of correctness.
145 * Also, we check for a possible trailing time zone using
146 * the tmztype template.
150 * 'A' An upper case char
151 * 'a' A lower case char
154 * 'O' An optional digit or space
159 static char *tmztype
[] = {
160 "Aaa Aaa O0 00:00:00 0000",
161 "Aaa Aaa O0 00:00 0000",
162 "Aaa Aaa O0 00:00:00 AAA 0000",
163 "Aaa Aaa O0 00:00 AAA 0000",
165 * Sommer time, e.g. MET DST
167 "Aaa Aaa O0 00:00:00 AAA AAA 0000",
168 "Aaa Aaa O0 00:00 AAA AAA 0000",
170 * time zone offset, e.g.
171 * +0200 or +0200 MET or +0200 MET DST
173 "Aaa Aaa O0 00:00:00 +0000 0000",
174 "Aaa Aaa O0 00:00 +0000 0000",
175 "Aaa Aaa O0 00:00:00 +0000 AAA 0000",
176 "Aaa Aaa O0 00:00 +0000 AAA 0000",
177 "Aaa Aaa O0 00:00:00 +0000 AAA AAA 0000",
178 "Aaa Aaa O0 00:00 +0000 AAA AAA 0000",
180 * time zone offset without time zone specification (pine)
182 "Aaa Aaa O0 00:00:00 0000 +0000",
189 int ret
= 0, form
= 0;
191 while (tmztype
[form
]) {
192 if ( (ret
= cmatch(date
, tmztype
[form
])) == 1 )
201 * Match the given string (cp) against the given template (tp).
202 * Return 1 if they match, 0 if they don't
205 cmatch(char *cp
, char *tp
)
212 if (c
= *cp
++, !lowerchar(c
))
216 if (c
= *cp
++, !upperchar(c
))
224 if (c
= *cp
++, !digitchar(c
))
228 if (c
= *cp
, c
!= ' ' && !digitchar(c
))
237 if (*cp
!= '+' && *cp
!= '-')
253 * Collect a liberal (space, tab delimited) word into the word buffer
254 * passed. Also, return a pointer to the next word following that,
255 * or NULL if none follow.
258 nextword(char *wp
, char *wbuf
)
266 while ((c
= *wp
++) != '\0' && !blankchar(c
)) {
269 while ((c
= *wp
++) != '\0' && c
!= '"')
278 for (; blankchar(c
); c
= *wp
++)
286 extract_header(FILE *fp
, struct header
*hp
)
288 char *linebuf
= NULL
;
291 char *colon
, *cp
, *value
;
293 struct header
*hq
= &nh
;
296 memset(hq
, 0, sizeof *hq
);
297 for (lc
= 0; readline(fp
, &linebuf
, &linesize
) > 0; lc
++);
299 while ((lc
= gethfield(fp
, &linebuf
, &linesize
, lc
, &colon
)) >= 0) {
300 if ((value
= thisfield(linebuf
, "to")) != NULL
) {
302 hq
->h_to
= checkaddrs(cat(hq
->h_to
,
303 sextract(value
, GTO
|GFULL
)));
304 } else if ((value
= thisfield(linebuf
, "cc")) != NULL
) {
306 hq
->h_cc
= checkaddrs(cat(hq
->h_cc
,
307 sextract(value
, GCC
|GFULL
)));
308 } else if ((value
= thisfield(linebuf
, "bcc")) != NULL
) {
310 hq
->h_bcc
= checkaddrs(cat(hq
->h_bcc
,
311 sextract(value
, GBCC
|GFULL
)));
312 } else if ((value
= thisfield(linebuf
, "from")) != NULL
) {
314 hq
->h_from
= checkaddrs(cat(hq
->h_from
,
315 sextract(value
, GEXTRA
|GFULL
)));
316 } else if ((value
= thisfield(linebuf
, "reply-to")) != NULL
) {
318 hq
->h_replyto
= checkaddrs(cat(hq
->h_replyto
,
319 sextract(value
, GEXTRA
|GFULL
)));
320 } else if ((value
= thisfield(linebuf
, "sender")) != NULL
) {
322 hq
->h_sender
= checkaddrs(cat(hq
->h_sender
,
323 sextract(value
, GEXTRA
|GFULL
)));
324 } else if ((value
= thisfield(linebuf
,
325 "organization")) != NULL
) {
327 for (cp
= value
; blankchar(*cp
& 0377); cp
++);
328 hq
->h_organization
= hq
->h_organization
?
329 save2str(hq
->h_organization
, cp
) :
331 } else if ((value
= thisfield(linebuf
, "subject")) != NULL
||
332 (value
= thisfield(linebuf
, "subj")) != NULL
) {
334 for (cp
= value
; blankchar(*cp
& 0377); cp
++);
335 hq
->h_subject
= hq
->h_subject
?
336 save2str(hq
->h_subject
, cp
) :
339 fprintf(stderr
, catgets(catd
, CATSET
, 266,
340 "Ignoring header field \"%s\"\n"),
344 * In case the blank line after the header has been edited out.
345 * Otherwise, fetch the header separator.
348 if (linebuf
[0] != '\0') {
349 for (cp
= linebuf
; *(++cp
) != '\0'; );
350 fseek(fp
, (long)-(1 + cp
- linebuf
), SEEK_CUR
);
352 if ((c
= getc(fp
)) != '\n' && c
!= EOF
)
359 hp
->h_bcc
= hq
->h_bcc
;
360 hp
->h_from
= hq
->h_from
;
361 hp
->h_replyto
= hq
->h_replyto
;
362 hp
->h_sender
= hq
->h_sender
;
363 hp
->h_organization
= hq
->h_organization
;
364 hp
->h_subject
= hq
->h_subject
;
366 fprintf(stderr
, catgets(catd
, CATSET
, 267,
367 "Restoring deleted header lines\n"));
373 * Return the desired header line from the passed message
374 * pointer (or NULL if the desired header field is not available).
375 * If mult is zero, return the content of the first matching header
376 * field only, the content of all matching header fields else.
379 hfield_mult(char *field
, struct message
*mp
, int mult
)
382 char *linebuf
= NULL
;
386 char *colon
, *oldhfield
= NULL
;
388 if ((ibuf
= setinput(&mb
, mp
, NEED_HEADER
)) == NULL
)
390 if ((lc
= mp
->m_lines
- 1) < 0)
392 if ((mp
->m_flag
& MNOFROM
) == 0) {
393 if (readline(ibuf
, &linebuf
, &linesize
) < 0) {
400 if ((lc
= gethfield(ibuf
, &linebuf
, &linesize
, lc
, &colon
))
406 if ((hfield
= thisfield(linebuf
, field
)) != NULL
) {
407 oldhfield
= save2str(hfield
, oldhfield
);
418 * Return the next header field found in the given message.
419 * Return >= 0 if something found, < 0 elsewise.
420 * "colon" is set to point to the colon in the header.
421 * Must deal with \ continuations & other such fraud.
424 gethfield(FILE *f
, char **linebuf
, size_t *linesize
, int rem
, char **colon
)
427 size_t line2size
= 0;
431 if (*linebuf
== NULL
)
432 *linebuf
= srealloc(*linebuf
, *linesize
= 1);
437 if ((c
= readline(f
, linebuf
, linesize
)) <= 0)
439 for (cp
= *linebuf
; fieldnamechar(*cp
& 0377); cp
++);
441 while (blankchar(*cp
& 0377))
443 if (*cp
!= ':' || cp
== *linebuf
)
446 * I guess we got a headline.
447 * Handle wraparounding
453 while (--cp
>= *linebuf
&& blankchar(*cp
& 0377));
457 if (cp
-8 >= *linebuf
&& cp
[-1] == '=' && cp
[-2] == '?')
459 ungetc(c
= getc(f
), f
);
462 if ((c
= readline(f
, &line2
, &line2size
)) < 0)
465 for (cp2
= line2
; blankchar(*cp2
& 0377); cp2
++);
467 if (cp2
[0] == '=' && cp2
[1] == '?' && c
> 8)
469 if (cp
+ c
>= *linebuf
+ *linesize
- 2) {
470 size_t diff
= cp
- *linebuf
;
471 size_t colondiff
= *colon
- *linebuf
;
472 *linebuf
= srealloc(*linebuf
,
474 cp
= &(*linebuf
)[diff
];
475 *colon
= &(*linebuf
)[colondiff
];
491 * Check whether the passed line is a header line of
492 * the desired breed. Return the field body, or 0.
495 thisfield(const char *linebuf
, const char *field
)
497 while (lowerconv(*linebuf
&0377) == lowerconv(*field
&0377)) {
503 while (blankchar(*linebuf
&0377))
505 if (*linebuf
++ != ':')
507 while (blankchar(*linebuf
&0377))
509 return (char *)linebuf
;
513 * Get sender's name from this message. If the message has
514 * a bunch of arpanet stuff in it, we may have to skin the name
515 * before returning it.
518 nameof(struct message
*mp
, int reptype
)
522 cp
= skin(name1(mp
, reptype
));
523 if (reptype
!= 0 || charcount(cp
, '!') < 2)
525 cp2
= strrchr(cp
, '!');
527 while (cp2
> cp
&& *cp2
!= '!')
535 * Start of a "comment".
539 skip_comment(const char *cp
)
543 for (; nesting
> 0 && *cp
; cp
++) {
561 * Return the start of a route-addr (address in angle brackets),
565 routeaddr(const char *name
)
567 const char *np
, *rp
= NULL
;
569 for (np
= name
; *np
; np
++) {
572 np
= skip_comment(&np
[1]) - 1;
578 if (*np
== '\\' && np
[1])
593 * Skin an arpa net address according to the RFC 822 interpretation
607 if (strchr(name
, '(') == NULL
&& strchr(name
, '<') == NULL
608 && strchr(name
, ' ') == NULL
)
612 nbuf
= ac_alloc(strlen(name
) + 1);
614 for (cp
= name
, cp2
= bufend
; (c
= *cp
++) != '\0'; ) {
617 cp
= skip_comment(cp
);
623 * Start of a "quoted-string".
624 * Copy it in its entirety.
627 while ((c
= *cp
) != '\0') {
635 else if ((c
= *cp
) != '\0') {
644 if (cp
[0] == 'a' && cp
[1] == 't' && cp
[2] == ' ')
645 cp
+= 3, *cp2
++ = '@';
647 if (cp
[0] == '@' && cp
[1] == ' ')
648 cp
+= 2, *cp2
++ = '@';
651 * RFC 822 specifies spaces are STRIPPED when
652 * in an adress specifier.
668 while ((c
= *cp
) != '\0' && c
!= ',') {
671 cp
= skip_comment(cp
);
673 while ((c
= *cp
) != '\0') {
677 if (c
== '\\' && *cp
)
684 /* Fall into . . . */
692 if (c
== ',' && !gotlt
) {
694 for (; *cp
== ' '; cp
++)
708 * Fetch the real name from an internet mail address field.
713 char *cstart
= NULL
, *cend
= NULL
, *cp
, *cq
;
716 int quoted
, good
, nogood
;
720 for (cp
= name
; *cp
; cp
++) {
725 * More than one comment in address, doesn't
726 * make sense to display it without context.
727 * Return the entire field,
729 return mime_fromaddr(name
);
731 cp
= skip_comment(cp
);
734 cend
= cstart
= NULL
;
740 if (*cp
== '\\' && cp
[1])
752 * More than one address. Just use the first one.
757 brk
: if (cstart
== NULL
) {
760 * If name contains only a route-addr, the
761 * surrounding angle brackets don't serve any
762 * useful purpose when displaying, so they
765 return prstr(skin(name
));
766 return mime_fromaddr(name
);
768 rp
= rname
= ac_alloc(cend
- cstart
+ 1);
770 * Strip quotes. Note that quotes that appear within a MIME-
771 * encoded word are not stripped. The idea is to strip only
772 * syntactical relevant things (but this is not necessarily
773 * the most sensible way in practice).
776 for (cp
= cstart
; cp
< cend
; cp
++) {
777 if (*cp
== '(' && !quoted
) {
778 cq
= skip_comment(++cp
);
782 if (*cp
== '\\' && &cp
[1] < cq
)
786 } else if (*cp
== '\\' && &cp
[1] < cend
)
788 else if (*cp
== '"') {
797 mime_fromhdr(&in
, &out
, TD_ISPR
|TD_ICONV
);
799 rname
= savestr(out
.s
);
801 while (blankchar(*rname
& 0377))
803 for (rp
= rname
; *rp
; rp
++);
804 while (--rp
>= rname
&& blankchar(*rp
& 0377))
807 return mime_fromaddr(name
);
809 * mime_fromhdr() has converted all nonprintable characters to
810 * question marks now. These and blanks are considered uninteresting;
811 * if the displayed part of the real name contains more than 25% of
812 * them, it is probably better to display the plain email address
817 for (rp
= rname
; *rp
&& rp
< &rname
[20]; rp
++)
818 if (*rp
== '?' || blankchar(*rp
& 0377))
823 return prstr(skin(name
));
828 * Fetch the sender's name from the passed message.
830 * 0 -- get sender's name for display purposes
831 * 1 -- get sender's name for reply
832 * 2 -- get sender's name for Reply
835 name1(struct message
*mp
, int reptype
)
839 char *linebuf
= NULL
;
845 if ((cp
= hfield("from", mp
)) != NULL
&& *cp
!= '\0')
847 if (reptype
== 0 && (cp
= hfield("sender", mp
)) != NULL
&&
850 namebuf
= smalloc(namesize
= 1);
852 if (mp
->m_flag
& MNOFROM
)
854 if ((ibuf
= setinput(&mb
, mp
, NEED_HEADER
)) == NULL
)
856 if (readline(ibuf
, &linebuf
, &linesize
) < 0)
859 if (namesize
<= linesize
)
860 namebuf
= srealloc(namebuf
, namesize
= linesize
+ 1);
861 for (cp
= linebuf
; *cp
&& *cp
!= ' '; cp
++)
863 for (; blankchar(*cp
& 0377); cp
++);
864 for (cp2
= &namebuf
[strlen(namebuf
)];
865 *cp
&& !blankchar(*cp
& 0377) && cp2
< namebuf
+ namesize
- 1;)
868 if (readline(ibuf
, &linebuf
, &linesize
) < 0)
870 if ((cp
= strchr(linebuf
, 'F')) == NULL
)
872 if (strncmp(cp
, "From", 4) != 0)
874 if (namesize
<= linesize
)
875 namebuf
= srealloc(namebuf
, namesize
= linesize
+ 1);
876 while ((cp
= strchr(cp
, 'r')) != NULL
) {
877 if (strncmp(cp
, "remote", 6) == 0) {
878 if ((cp
= strchr(cp
, 'f')) == NULL
)
880 if (strncmp(cp
, "from", 4) != 0)
882 if ((cp
= strchr(cp
, ' ')) == NULL
)
886 strncpy(namebuf
, cp
, namesize
);
889 cp2
=strrchr(namebuf
, '!')+1;
890 strncpy(cp2
, cp
, (namebuf
+namesize
)-cp2
);
892 namebuf
[namesize
-2]='\0';
893 strcat(namebuf
, "!");
899 if (*namebuf
!= '\0' || ((cp
= hfield("return-path", mp
))) == NULL
||
901 cp
= savestr(namebuf
);
909 msgidnextc(const char **cp
, int *status
)
929 *cp
= skip_comment(&(*cp
)[1]);
945 return *status
& 02 ? lowerconv(c
) : c
;
951 msgidcmp(const char *s1
, const char *s2
)
957 c1
= msgidnextc(&s1
, &q1
);
958 c2
= msgidnextc(&s2
, &q2
);
966 * Count the occurances of c in str
969 charcount(char *str
, int c
)
974 for (i
= 0, cp
= str
; *cp
; cp
++)
981 * See if the given header field is supposed to be ignored.
984 is_ign(char *field
, size_t fieldlen
, struct ignoretab ignore
[2])
991 if (ignore
== allignore
)
994 * Lower-case the string, so that "Status" and "status"
995 * will hash to the same place.
997 realfld
= ac_alloc(fieldlen
+ 1);
998 i_strcpy(realfld
, field
, fieldlen
+ 1);
999 if (ignore
[1].i_count
> 0)
1000 ret
= !member(realfld
, ignore
+ 1);
1002 ret
= member(realfld
, ignore
);
1008 member(char *realfield
, struct ignoretab
*table
)
1012 for (igp
= table
->i_head
[hash(realfield
)]; igp
!= 0; igp
= igp
->i_link
)
1013 if (*igp
->i_field
== *realfield
&&
1014 equal(igp
->i_field
, realfield
))
1020 * Fake Sender for From_ lines if missing, e. g. with POP3.
1023 fakefrom(struct message
*mp
)
1027 if (((name
= skin(hfield("return-path", mp
))) == NULL
||
1029 ((name
= skin(hfield("from", mp
))) == NULL
||
1041 for (cq
= cp
; *cq
&& *cq
!= '\n'; cq
++);
1055 while (*cp
!= '\0') {
1067 } else if (blankchar(*cp
& 0377) || *cp
== ',')
1076 * From username Fri Jan 2 20:13:51 2004
1081 unixtime(char *from
)
1085 int i
, year
, month
, day
, hour
, minute
, second
;
1089 for (fp
= from
; *fp
&& *fp
!= '\n'; fp
++);
1095 for (i
= 0; month_names
[i
]; i
++)
1096 if (strncmp(&fp
[4], month_names
[i
], 3) == 0)
1098 if (month_names
[i
] == 0)
1103 day
= strtol(&fp
[8], &xp
, 10);
1104 if (*xp
!= ' ' || xp
!= &fp
[10])
1106 hour
= strtol(&fp
[11], &xp
, 10);
1107 if (*xp
!= ':' || xp
!= &fp
[13])
1109 minute
= strtol(&fp
[14], &xp
, 10);
1110 if (*xp
!= ':' || xp
!= &fp
[16])
1112 second
= strtol(&fp
[17], &xp
, 10);
1113 if (*xp
!= ' ' || xp
!= &fp
[19])
1115 year
= strtol(&fp
[20], &xp
, 10);
1118 if ((t
= combinetime(year
, month
, day
, hour
, minute
, second
)) ==
1121 tzdiff
= t
- mktime(gmtime(&t
));
1122 tmptr
= localtime(&t
);
1123 if (tmptr
->tm_isdst
> 0)
1135 char *cp
= date
, *x
;
1137 int i
, year
, month
, day
, hour
, minute
, second
;
1139 if ((cp
= nexttoken(cp
)) == NULL
)
1141 if (alphachar(cp
[0] & 0377) && alphachar(cp
[1] & 0377) &&
1142 alphachar(cp
[2] & 0377) && cp
[3] == ',') {
1143 if ((cp
= nexttoken(&cp
[4])) == NULL
)
1146 day
= strtol(cp
, &x
, 10);
1147 if ((cp
= nexttoken(x
)) == NULL
)
1149 for (i
= 0; month_names
[i
]; i
++) {
1150 if (strncmp(cp
, month_names
[i
], 3) == 0)
1153 if (month_names
[i
] == NULL
)
1156 if ((cp
= nexttoken(&cp
[3])) == NULL
)
1158 year
= strtol(cp
, &x
, 10);
1159 if ((cp
= nexttoken(x
)) == NULL
)
1161 hour
= strtol(cp
, &x
, 10);
1165 minute
= strtol(cp
, &x
, 10);
1168 second
= strtol(cp
, &x
, 10);
1171 if ((t
= combinetime(year
, month
, day
, hour
, minute
, second
)) ==
1174 if ((cp
= nexttoken(x
)) != NULL
) {
1185 if (digitchar(cp
[0] & 0377) && digitchar(cp
[1] & 0377) &&
1186 digitchar(cp
[2] & 0377) &&
1187 digitchar(cp
[3] & 0377)) {
1191 t
+= strtol(buf
, NULL
, 10) * sign
* 3600;
1194 t
+= strtol(buf
, NULL
, 10) * sign
* 60;
1202 #define leapyear(year) ((year % 100 ? year : year / 100) % 4 == 0)
1205 combinetime(int year
, int month
, int day
, int hour
, int minute
, int second
)
1209 if (second
< 0 || minute
< 0 || hour
< 0 || day
< 1)
1211 t
= second
+ minute
* 60 + hour
* 3600 + (day
- 1) * 86400;
1214 else if (year
< 1900)
1219 t
+= 86400 * (leapyear(year
) ? 29 : 28);
1239 t
+= (year
- 70) * 31536000 + ((year
- 69) / 4) * 86400 -
1240 ((year
- 1) / 100) * 86400 + ((year
+ 299) / 400) * 86400;
1245 substdate(struct message
*m
)
1251 * Determine the date to print in faked 'From ' lines. This is
1252 * traditionally the date the message was written to the mail
1253 * file. Try to determine this using RFC message header fields,
1254 * or fall back to current time.
1257 if ((cp
= hfield_mult("received", m
, 0)) != NULL
) {
1258 while ((cp
= nexttoken(cp
)) != NULL
&& *cp
!= ';') {
1261 while (alnumchar(*cp
& 0377));
1264 m
->m_time
= rfctime(cp
);
1266 if (m
->m_time
== 0 || m
->m_time
> now
)
1267 if ((cp
= hfield("date", m
)) != NULL
)
1268 m
->m_time
= rfctime(cp
);
1269 if (m
->m_time
== 0 || m
->m_time
> now
)
1274 check_from_and_sender(struct name
*fromfield
, struct name
*senderfield
)
1276 if (fromfield
&& fromfield
->n_flink
&& senderfield
== NULL
) {
1277 fprintf(stderr
, "A Sender: field is required with multiple "
1278 "addresses in From: field.\n");
1281 if (senderfield
&& senderfield
->n_flink
) {
1282 fprintf(stderr
, "The Sender: field may contain "
1283 "only one address.\n");
1290 getsender(struct message
*mp
)
1295 if ((cp
= hfield("from", mp
)) == NULL
||
1296 (np
= sextract(cp
, GEXTRA
|GSKIN
)) == NULL
)
1298 return np
->n_flink
!= NULL
? skin(hfield("sender", mp
)) : np
->n_name
;