2 * S-nail - 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
45 * Mail -- a mail program
47 * Routines for processing and detecting headlines.
50 static char *copyin(char *src
, char **space
);
51 static char *nextword(char *wp
, char *wbuf
);
52 static int gethfield(FILE *f
, char **linebuf
, size_t *linesize
, int rem
,
54 static int msgidnextc(const char **cp
, int *status
);
55 static int charcount(char *str
, int c
);
58 * See if the passed line buffer is a mail header.
59 * Return true if yes. POSIX.2 leaves the content
60 * following 'From ' unspecified, so don't care about
65 is_head(char *linebuf
, size_t linelen
)
71 if (*cp
++ != 'F' || *cp
++ != 'r' || *cp
++ != 'o' || *cp
++ != 'm' ||
78 * Split a headline into its useful components.
79 * Copy the line into dynamic string space, then set
80 * pointers into the copied line in the passed headline
81 * structure. Actually, it scans.
84 parse(char *line
, size_t linelen
, struct headline
*hl
, char *pbuf
)
95 word
= ac_alloc(linelen
+ 1);
97 * Skip over "From" first.
99 cp
= nextword(cp
, word
);
100 cp
= nextword(cp
, word
);
102 hl
->l_from
= copyin(word
, &sp
);
103 if (cp
!= NULL
&& cp
[0] == 't' && cp
[1] == 't' && cp
[2] == 'y') {
104 cp
= nextword(cp
, word
);
105 hl
->l_tty
= copyin(word
, &sp
);
108 hl
->l_date
= copyin(cp
, &sp
);
110 hl
->l_date
= catgets(catd
, CATSET
, 213, "<Unknown date>");
115 * Copy the string on the left into the string on the right
116 * and bump the right (reference) string pointer by the length.
117 * Thus, dynamically allocate space in the right string, copying
118 * the left string into it.
121 copyin(char *src
, char **space
)
127 while ((*cp
++ = *src
++) != '\0')
134 static int cmatch(char *, char *);
136 * Test to see if the passed string is a ctime(3) generated
137 * date string as documented in the manual. The template
138 * below is used as the criterion of correctness.
139 * Also, we check for a possible trailing time zone using
140 * the tmztype template.
144 * 'A' An upper case char
145 * 'a' A lower case char
148 * 'O' An optional digit or space
153 static char *tmztype
[] = {
154 "Aaa Aaa O0 00:00:00 0000",
155 "Aaa Aaa O0 00:00 0000",
156 "Aaa Aaa O0 00:00:00 AAA 0000",
157 "Aaa Aaa O0 00:00 AAA 0000",
159 * Sommer time, e.g. MET DST
161 "Aaa Aaa O0 00:00:00 AAA AAA 0000",
162 "Aaa Aaa O0 00:00 AAA AAA 0000",
164 * time zone offset, e.g.
165 * +0200 or +0200 MET or +0200 MET DST
167 "Aaa Aaa O0 00:00:00 +0000 0000",
168 "Aaa Aaa O0 00:00 +0000 0000",
169 "Aaa Aaa O0 00:00:00 +0000 AAA 0000",
170 "Aaa Aaa O0 00:00 +0000 AAA 0000",
171 "Aaa Aaa O0 00:00:00 +0000 AAA AAA 0000",
172 "Aaa Aaa O0 00:00 +0000 AAA AAA 0000",
174 * time zone offset without time zone specification (pine)
176 "Aaa Aaa O0 00:00:00 0000 +0000",
183 int ret
= 0, form
= 0;
185 while (tmztype
[form
]) {
186 if ( (ret
= cmatch(date
, tmztype
[form
])) == 1 )
195 * Match the given string (cp) against the given template (tp).
196 * Return 1 if they match, 0 if they don't
199 cmatch(char *cp
, char *tp
)
206 if (c
= *cp
++, !lowerchar(c
))
210 if (c
= *cp
++, !upperchar(c
))
218 if (c
= *cp
++, !digitchar(c
))
222 if (c
= *cp
, c
!= ' ' && !digitchar(c
))
231 if (*cp
!= '+' && *cp
!= '-')
247 * Collect a liberal (space, tab delimited) word into the word buffer
248 * passed. Also, return a pointer to the next word following that,
249 * or NULL if none follow.
252 nextword(char *wp
, char *wbuf
)
260 while ((c
= *wp
++) != '\0' && !blankchar(c
)) {
263 while ((c
= *wp
++) != '\0' && c
!= '"')
272 for (; blankchar(c
); c
= *wp
++)
280 extract_header(FILE *fp
, struct header
*hp
)
282 char *linebuf
= NULL
;
285 char *colon
, *cp
, *value
;
287 struct header
*hq
= &nh
;
290 memset(hq
, 0, sizeof *hq
);
291 for (lc
= 0; readline(fp
, &linebuf
, &linesize
) > 0; lc
++);
293 while ((lc
= gethfield(fp
, &linebuf
, &linesize
, lc
, &colon
)) >= 0) {
294 if ((value
= thisfield(linebuf
, "to")) != NULL
) {
296 hq
->h_to
= checkaddrs(cat(hq
->h_to
,
297 sextract(value
, GTO
|GFULL
)));
298 } else if ((value
= thisfield(linebuf
, "cc")) != NULL
) {
300 hq
->h_cc
= checkaddrs(cat(hq
->h_cc
,
301 sextract(value
, GCC
|GFULL
)));
302 } else if ((value
= thisfield(linebuf
, "bcc")) != NULL
) {
304 hq
->h_bcc
= checkaddrs(cat(hq
->h_bcc
,
305 sextract(value
, GBCC
|GFULL
)));
306 } else if ((value
= thisfield(linebuf
, "from")) != NULL
) {
308 hq
->h_from
= checkaddrs(cat(hq
->h_from
,
309 sextract(value
, GEXTRA
|GFULL
)));
310 } else if ((value
= thisfield(linebuf
, "reply-to")) != NULL
) {
312 hq
->h_replyto
= checkaddrs(cat(hq
->h_replyto
,
313 sextract(value
, GEXTRA
|GFULL
)));
314 } else if ((value
= thisfield(linebuf
, "sender")) != NULL
) {
316 hq
->h_sender
= checkaddrs(cat(hq
->h_sender
,
317 sextract(value
, GEXTRA
|GFULL
)));
318 } else if ((value
= thisfield(linebuf
,
319 "organization")) != NULL
) {
321 for (cp
= value
; blankchar(*cp
& 0377); cp
++);
322 hq
->h_organization
= hq
->h_organization
?
323 save2str(hq
->h_organization
, cp
) :
325 } else if ((value
= thisfield(linebuf
, "subject")) != NULL
||
326 (value
= thisfield(linebuf
, "subj")) != NULL
) {
328 for (cp
= value
; blankchar(*cp
& 0377); cp
++);
329 hq
->h_subject
= hq
->h_subject
?
330 save2str(hq
->h_subject
, cp
) :
333 fprintf(stderr
, catgets(catd
, CATSET
, 266,
334 "Ignoring header field \"%s\"\n"),
338 * In case the blank line after the header has been edited out.
339 * Otherwise, fetch the header separator.
342 if (linebuf
[0] != '\0') {
343 for (cp
= linebuf
; *(++cp
) != '\0'; );
344 fseek(fp
, (long)-(1 + cp
- linebuf
), SEEK_CUR
);
346 if ((c
= getc(fp
)) != '\n' && c
!= EOF
)
353 hp
->h_bcc
= hq
->h_bcc
;
354 hp
->h_from
= hq
->h_from
;
355 hp
->h_replyto
= hq
->h_replyto
;
356 hp
->h_sender
= hq
->h_sender
;
357 hp
->h_organization
= hq
->h_organization
;
358 hp
->h_subject
= hq
->h_subject
;
360 fprintf(stderr
, catgets(catd
, CATSET
, 267,
361 "Restoring deleted header lines\n"));
367 * Return the desired header line from the passed message
368 * pointer (or NULL if the desired header field is not available).
369 * If mult is zero, return the content of the first matching header
370 * field only, the content of all matching header fields else.
373 hfield_mult(char *field
, struct message
*mp
, int mult
)
376 char *linebuf
= NULL
;
380 char *colon
, *oldhfield
= NULL
;
382 if ((ibuf
= setinput(&mb
, mp
, NEED_HEADER
)) == NULL
)
384 if ((lc
= mp
->m_lines
- 1) < 0)
386 if ((mp
->m_flag
& MNOFROM
) == 0) {
387 if (readline(ibuf
, &linebuf
, &linesize
) < 0) {
394 if ((lc
= gethfield(ibuf
, &linebuf
, &linesize
, lc
, &colon
))
400 if ((hfield
= thisfield(linebuf
, field
)) != NULL
) {
401 oldhfield
= save2str(hfield
, oldhfield
);
412 * Return the next header field found in the given message.
413 * Return >= 0 if something found, < 0 elsewise.
414 * "colon" is set to point to the colon in the header.
415 * Must deal with \ continuations & other such fraud.
418 gethfield(FILE *f
, char **linebuf
, size_t *linesize
, int rem
, char **colon
)
421 size_t line2size
= 0;
425 if (*linebuf
== NULL
)
426 *linebuf
= srealloc(*linebuf
, *linesize
= 1);
431 if ((c
= readline(f
, linebuf
, linesize
)) <= 0)
433 for (cp
= *linebuf
; fieldnamechar(*cp
& 0377); cp
++);
435 while (blankchar(*cp
& 0377))
437 if (*cp
!= ':' || cp
== *linebuf
)
440 * I guess we got a headline.
441 * Handle wraparounding
447 while (--cp
>= *linebuf
&& blankchar(*cp
& 0377));
451 if (cp
-8 >= *linebuf
&& cp
[-1] == '=' && cp
[-2] == '?')
453 ungetc(c
= getc(f
), f
);
456 if ((c
= readline(f
, &line2
, &line2size
)) < 0)
459 for (cp2
= line2
; blankchar(*cp2
& 0377); cp2
++);
461 if (cp2
[0] == '=' && cp2
[1] == '?' && c
> 8)
463 if (cp
+ c
>= *linebuf
+ *linesize
- 2) {
464 size_t diff
= cp
- *linebuf
;
465 size_t colondiff
= *colon
- *linebuf
;
466 *linebuf
= srealloc(*linebuf
,
468 cp
= &(*linebuf
)[diff
];
469 *colon
= &(*linebuf
)[colondiff
];
485 * Check whether the passed line is a header line of
486 * the desired breed. Return the field body, or 0.
489 thisfield(const char *linebuf
, const char *field
)
491 while (lowerconv(*linebuf
&0377) == lowerconv(*field
&0377)) {
497 while (blankchar(*linebuf
&0377))
499 if (*linebuf
++ != ':')
501 while (blankchar(*linebuf
&0377))
503 return (char *)linebuf
;
507 * Get sender's name from this message. If the message has
508 * a bunch of arpanet stuff in it, we may have to skin the name
509 * before returning it.
512 nameof(struct message
*mp
, int reptype
)
516 cp
= skin(name1(mp
, reptype
));
517 if (reptype
!= 0 || charcount(cp
, '!') < 2)
519 cp2
= strrchr(cp
, '!');
521 while (cp2
> cp
&& *cp2
!= '!')
529 * Start of a "comment".
533 skip_comment(const char *cp
)
537 for (; nesting
> 0 && *cp
; cp
++) {
555 * Return the start of a route-addr (address in angle brackets),
559 routeaddr(const char *name
)
561 const char *np
, *rp
= NULL
;
563 for (np
= name
; *np
; np
++) {
566 np
= skip_comment(&np
[1]) - 1;
572 if (*np
== '\\' && np
[1])
587 * Returned the skinned n_name, use the cached value if available.
588 * Note well that it may *not* create a duplicate.
591 skinned_name(struct name
*np
)
593 return ((np
->n_flags
& NAME_SKINNED
) ? np
->n_name
: skin(np
->n_name
));
597 * Skin an arpa net address according to the RFC 822 interpretation
611 if (strchr(name
, '(') == NULL
&& strchr(name
, '<') == NULL
612 && strchr(name
, ' ') == NULL
)
616 nbuf
= ac_alloc(strlen(name
) + 1);
618 for (cp
= name
, cp2
= bufend
; (c
= *cp
++) != '\0'; ) {
621 cp
= skip_comment(cp
);
627 * Start of a "quoted-string".
628 * Copy it in its entirety.
631 while ((c
= *cp
) != '\0') {
639 else if ((c
= *cp
) != '\0') {
648 if (cp
[0] == 'a' && cp
[1] == 't' && cp
[2] == ' ')
649 cp
+= 3, *cp2
++ = '@';
651 if (cp
[0] == '@' && cp
[1] == ' ')
652 cp
+= 2, *cp2
++ = '@';
655 * RFC 822 specifies spaces are STRIPPED when
656 * in an adress specifier.
672 while ((c
= *cp
) != '\0' && c
!= ',') {
675 cp
= skip_comment(cp
);
677 while ((c
= *cp
) != '\0') {
681 if (c
== '\\' && *cp
)
688 /* Fall into . . . */
696 if (c
== ',' && !gotlt
) {
698 for (; *cp
== ' '; cp
++)
712 * Fetch the real name from an internet mail address field.
717 char *cstart
= NULL
, *cend
= NULL
, *cp
, *cq
;
720 int quoted
, good
, nogood
;
724 for (cp
= name
; *cp
; cp
++) {
729 * More than one comment in address, doesn't
730 * make sense to display it without context.
731 * Return the entire field,
733 return mime_fromaddr(name
);
735 cp
= skip_comment(cp
);
738 cend
= cstart
= NULL
;
744 if (*cp
== '\\' && cp
[1])
756 * More than one address. Just use the first one.
761 brk
: if (cstart
== NULL
) {
764 * If name contains only a route-addr, the
765 * surrounding angle brackets don't serve any
766 * useful purpose when displaying, so they
769 return prstr(skin(name
));
770 return mime_fromaddr(name
);
772 rp
= rname
= ac_alloc(cend
- cstart
+ 1);
774 * Strip quotes. Note that quotes that appear within a MIME-
775 * encoded word are not stripped. The idea is to strip only
776 * syntactical relevant things (but this is not necessarily
777 * the most sensible way in practice).
780 for (cp
= cstart
; cp
< cend
; cp
++) {
781 if (*cp
== '(' && !quoted
) {
782 cq
= skip_comment(++cp
);
786 if (*cp
== '\\' && &cp
[1] < cq
)
790 } else if (*cp
== '\\' && &cp
[1] < cend
)
792 else if (*cp
== '"') {
801 mime_fromhdr(&in
, &out
, TD_ISPR
|TD_ICONV
);
803 rname
= savestr(out
.s
);
805 while (blankchar(*rname
& 0377))
807 for (rp
= rname
; *rp
; rp
++);
808 while (--rp
>= rname
&& blankchar(*rp
& 0377))
811 return mime_fromaddr(name
);
813 * mime_fromhdr() has converted all nonprintable characters to
814 * question marks now. These and blanks are considered uninteresting;
815 * if the displayed part of the real name contains more than 25% of
816 * them, it is probably better to display the plain email address
821 for (rp
= rname
; *rp
&& rp
< &rname
[20]; rp
++)
822 if (*rp
== '?' || blankchar(*rp
& 0377))
827 return prstr(skin(name
));
832 * Fetch the sender's name from the passed message.
834 * 0 -- get sender's name for display purposes
835 * 1 -- get sender's name for reply
836 * 2 -- get sender's name for Reply
839 name1(struct message
*mp
, int reptype
)
843 char *linebuf
= NULL
;
849 if ((cp
= hfield("from", mp
)) != NULL
&& *cp
!= '\0')
851 if (reptype
== 0 && (cp
= hfield("sender", mp
)) != NULL
&&
854 namebuf
= smalloc(namesize
= 1);
856 if (mp
->m_flag
& MNOFROM
)
858 if ((ibuf
= setinput(&mb
, mp
, NEED_HEADER
)) == NULL
)
860 if (readline(ibuf
, &linebuf
, &linesize
) < 0)
863 if (namesize
<= linesize
)
864 namebuf
= srealloc(namebuf
, namesize
= linesize
+ 1);
865 for (cp
= linebuf
; *cp
&& *cp
!= ' '; cp
++)
867 for (; blankchar(*cp
& 0377); cp
++);
868 for (cp2
= &namebuf
[strlen(namebuf
)];
869 *cp
&& !blankchar(*cp
& 0377) && cp2
< namebuf
+ namesize
- 1;)
872 if (readline(ibuf
, &linebuf
, &linesize
) < 0)
874 if ((cp
= strchr(linebuf
, 'F')) == NULL
)
876 if (strncmp(cp
, "From", 4) != 0)
878 if (namesize
<= linesize
)
879 namebuf
= srealloc(namebuf
, namesize
= linesize
+ 1);
880 while ((cp
= strchr(cp
, 'r')) != NULL
) {
881 if (strncmp(cp
, "remote", 6) == 0) {
882 if ((cp
= strchr(cp
, 'f')) == NULL
)
884 if (strncmp(cp
, "from", 4) != 0)
886 if ((cp
= strchr(cp
, ' ')) == NULL
)
890 strncpy(namebuf
, cp
, namesize
);
893 cp2
=strrchr(namebuf
, '!')+1;
894 strncpy(cp2
, cp
, (namebuf
+namesize
)-cp2
);
896 namebuf
[namesize
-2]='\0';
897 strcat(namebuf
, "!");
903 if (*namebuf
!= '\0' || ((cp
= hfield("return-path", mp
))) == NULL
||
905 cp
= savestr(namebuf
);
913 msgidnextc(const char **cp
, int *status
)
933 *cp
= skip_comment(&(*cp
)[1]);
949 return *status
& 02 ? lowerconv(c
) : c
;
955 msgidcmp(const char *s1
, const char *s2
)
961 c1
= msgidnextc(&s1
, &q1
);
962 c2
= msgidnextc(&s2
, &q2
);
970 * Count the occurances of c in str
973 charcount(char *str
, int c
)
978 for (i
= 0, cp
= str
; *cp
; cp
++)
985 * See if the given header field is supposed to be ignored.
988 is_ign(char *field
, size_t fieldlen
, struct ignoretab ignore
[2])
995 if (ignore
== allignore
)
998 * Lower-case the string, so that "Status" and "status"
999 * will hash to the same place.
1001 realfld
= ac_alloc(fieldlen
+ 1);
1002 i_strcpy(realfld
, field
, fieldlen
+ 1);
1003 if (ignore
[1].i_count
> 0)
1004 ret
= !member(realfld
, ignore
+ 1);
1006 ret
= member(realfld
, ignore
);
1012 member(char *realfield
, struct ignoretab
*table
)
1016 for (igp
= table
->i_head
[hash(realfield
)]; igp
!= 0; igp
= igp
->i_link
)
1017 if (*igp
->i_field
== *realfield
&&
1018 strcmp(igp
->i_field
, realfield
) == 0)
1024 * Fake Sender for From_ lines if missing, e. g. with POP3.
1027 fakefrom(struct message
*mp
)
1031 if (((name
= skin(hfield("return-path", mp
))) == NULL
||
1033 ((name
= skin(hfield("from", mp
))) == NULL
||
1045 for (cq
= cp
; *cq
&& *cq
!= '\n'; cq
++);
1059 while (*cp
!= '\0') {
1071 } else if (blankchar(*cp
& 0377) || *cp
== ',')
1080 * From username Fri Jan 2 20:13:51 2004
1085 unixtime(char *from
)
1089 int i
, year
, month
, day
, hour
, minute
, second
;
1093 for (fp
= from
; *fp
&& *fp
!= '\n'; fp
++);
1099 for (i
= 0; month_names
[i
]; i
++)
1100 if (strncmp(&fp
[4], month_names
[i
], 3) == 0)
1102 if (month_names
[i
] == 0)
1107 day
= strtol(&fp
[8], &xp
, 10);
1108 if (*xp
!= ' ' || xp
!= &fp
[10])
1110 hour
= strtol(&fp
[11], &xp
, 10);
1111 if (*xp
!= ':' || xp
!= &fp
[13])
1113 minute
= strtol(&fp
[14], &xp
, 10);
1114 if (*xp
!= ':' || xp
!= &fp
[16])
1116 second
= strtol(&fp
[17], &xp
, 10);
1117 if (*xp
!= ' ' || xp
!= &fp
[19])
1119 year
= strtol(&fp
[20], &xp
, 10);
1122 if ((t
= combinetime(year
, month
, day
, hour
, minute
, second
)) ==
1125 tzdiff
= t
- mktime(gmtime(&t
));
1126 tmptr
= localtime(&t
);
1127 if (tmptr
->tm_isdst
> 0)
1139 char *cp
= date
, *x
;
1141 int i
, year
, month
, day
, hour
, minute
, second
;
1143 if ((cp
= nexttoken(cp
)) == NULL
)
1145 if (alphachar(cp
[0] & 0377) && alphachar(cp
[1] & 0377) &&
1146 alphachar(cp
[2] & 0377) && cp
[3] == ',') {
1147 if ((cp
= nexttoken(&cp
[4])) == NULL
)
1150 day
= strtol(cp
, &x
, 10);
1151 if ((cp
= nexttoken(x
)) == NULL
)
1153 for (i
= 0; month_names
[i
]; i
++) {
1154 if (strncmp(cp
, month_names
[i
], 3) == 0)
1157 if (month_names
[i
] == NULL
)
1160 if ((cp
= nexttoken(&cp
[3])) == NULL
)
1162 year
= strtol(cp
, &x
, 10);
1163 if ((cp
= nexttoken(x
)) == NULL
)
1165 hour
= strtol(cp
, &x
, 10);
1169 minute
= strtol(cp
, &x
, 10);
1172 second
= strtol(cp
, &x
, 10);
1175 if ((t
= combinetime(year
, month
, day
, hour
, minute
, second
)) ==
1178 if ((cp
= nexttoken(x
)) != NULL
) {
1189 if (digitchar(cp
[0] & 0377) && digitchar(cp
[1] & 0377) &&
1190 digitchar(cp
[2] & 0377) &&
1191 digitchar(cp
[3] & 0377)) {
1195 t
+= strtol(buf
, NULL
, 10) * sign
* 3600;
1198 t
+= strtol(buf
, NULL
, 10) * sign
* 60;
1206 #define leapyear(year) ((year % 100 ? year : year / 100) % 4 == 0)
1209 combinetime(int year
, int month
, int day
, int hour
, int minute
, int second
)
1213 if (second
< 0 || minute
< 0 || hour
< 0 || day
< 1)
1215 t
= second
+ minute
* 60 + hour
* 3600 + (day
- 1) * 86400;
1218 else if (year
< 1900)
1223 t
+= 86400 * (leapyear(year
) ? 29 : 28);
1243 t
+= (year
- 70) * 31536000 + ((year
- 69) / 4) * 86400 -
1244 ((year
- 1) / 100) * 86400 + ((year
+ 299) / 400) * 86400;
1249 substdate(struct message
*m
)
1255 * Determine the date to print in faked 'From ' lines. This is
1256 * traditionally the date the message was written to the mail
1257 * file. Try to determine this using RFC message header fields,
1258 * or fall back to current time.
1261 if ((cp
= hfield_mult("received", m
, 0)) != NULL
) {
1262 while ((cp
= nexttoken(cp
)) != NULL
&& *cp
!= ';') {
1265 while (alnumchar(*cp
& 0377));
1268 m
->m_time
= rfctime(cp
);
1270 if (m
->m_time
== 0 || m
->m_time
> now
)
1271 if ((cp
= hfield("date", m
)) != NULL
)
1272 m
->m_time
= rfctime(cp
);
1273 if (m
->m_time
== 0 || m
->m_time
> now
)
1278 check_from_and_sender(struct name
*fromfield
, struct name
*senderfield
)
1280 if (fromfield
&& fromfield
->n_flink
&& senderfield
== NULL
) {
1281 fprintf(stderr
, "A Sender: field is required with multiple "
1282 "addresses in From: field.\n");
1285 if (senderfield
&& senderfield
->n_flink
) {
1286 fprintf(stderr
, "The Sender: field may contain "
1287 "only one address.\n");
1294 getsender(struct message
*mp
)
1299 if ((cp
= hfield("from", mp
)) == NULL
||
1300 (np
= sextract(cp
, GEXTRA
|GSKIN
)) == NULL
)
1302 return np
->n_flink
!= NULL
? skin(hfield("sender", mp
)) : np
->n_name
;