1*8a16b7a1SPedro F. Giffuni /*- 2*8a16b7a1SPedro F. Giffuni * SPDX-License-Identifier: BSD-3-Clause 3*8a16b7a1SPedro F. Giffuni * 49b50d902SRodney W. Grimes * Copyright (c) 1980, 1993 59b50d902SRodney W. Grimes * The Regents of the University of California. All rights reserved. 69b50d902SRodney W. Grimes * 79b50d902SRodney W. Grimes * Redistribution and use in source and binary forms, with or without 89b50d902SRodney W. Grimes * modification, are permitted provided that the following conditions 99b50d902SRodney W. Grimes * are met: 109b50d902SRodney W. Grimes * 1. Redistributions of source code must retain the above copyright 119b50d902SRodney W. Grimes * notice, this list of conditions and the following disclaimer. 129b50d902SRodney W. Grimes * 2. Redistributions in binary form must reproduce the above copyright 139b50d902SRodney W. Grimes * notice, this list of conditions and the following disclaimer in the 149b50d902SRodney W. Grimes * documentation and/or other materials provided with the distribution. 15fbbd9655SWarner Losh * 3. Neither the name of the University nor the names of its contributors 169b50d902SRodney W. Grimes * may be used to endorse or promote products derived from this software 179b50d902SRodney W. Grimes * without specific prior written permission. 189b50d902SRodney W. Grimes * 199b50d902SRodney W. Grimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 209b50d902SRodney W. Grimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 219b50d902SRodney W. Grimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 229b50d902SRodney W. Grimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 239b50d902SRodney W. Grimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 249b50d902SRodney W. Grimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 259b50d902SRodney W. Grimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 269b50d902SRodney W. Grimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 279b50d902SRodney W. Grimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 289b50d902SRodney W. Grimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 299b50d902SRodney W. Grimes * SUCH DAMAGE. 309b50d902SRodney W. Grimes */ 319b50d902SRodney W. Grimes 329b50d902SRodney W. Grimes #ifndef lint 330c3a8314SMike Heffner #if 0 34856f23edSMike Heffner static char sccsid[] = "@(#)head.c 8.2 (Berkeley) 4/20/95"; 350c3a8314SMike Heffner #endif 369b50d902SRodney W. Grimes #endif /* not lint */ 37e026a48cSDavid E. O'Brien #include <sys/cdefs.h> 38e026a48cSDavid E. O'Brien __FBSDID("$FreeBSD$"); 399b50d902SRodney W. Grimes 409b50d902SRodney W. Grimes #include "rcv.h" 419b50d902SRodney W. Grimes #include "extern.h" 429b50d902SRodney W. Grimes 439b50d902SRodney W. Grimes /* 449b50d902SRodney W. Grimes * Mail -- a mail program 459b50d902SRodney W. Grimes * 469b50d902SRodney W. Grimes * Routines for processing and detecting headlines. 479b50d902SRodney W. Grimes */ 489b50d902SRodney W. Grimes 499b50d902SRodney W. Grimes /* 509b50d902SRodney W. Grimes * See if the passed line buffer is a mail header. 519b50d902SRodney W. Grimes * Return true if yes. Note the extreme pains to 52487ac9acSUlrich Spörlein * accommodate all funny formats. 539b50d902SRodney W. Grimes */ 549b50d902SRodney W. Grimes int 556d8484b0SPhilippe Charnier ishead(char linebuf[]) 569b50d902SRodney W. Grimes { 579b50d902SRodney W. Grimes struct headline hl; 589b50d902SRodney W. Grimes char parbuf[BUFSIZ]; 599b50d902SRodney W. Grimes 602e99d1d5SMike Heffner if (strncmp(linebuf, "From ", 5) != 0) 619b50d902SRodney W. Grimes return (0); 629b50d902SRodney W. Grimes parse(linebuf, &hl, parbuf); 632e99d1d5SMike Heffner if (hl.l_date == NULL) { 642e99d1d5SMike Heffner fail(linebuf, "No date field"); 659b50d902SRodney W. Grimes return (0); 669b50d902SRodney W. Grimes } 679b50d902SRodney W. Grimes if (!isdate(hl.l_date)) { 689b50d902SRodney W. Grimes fail(linebuf, "Date field not legal date"); 699b50d902SRodney W. Grimes return (0); 709b50d902SRodney W. Grimes } 719b50d902SRodney W. Grimes /* 729b50d902SRodney W. Grimes * I guess we got it! 739b50d902SRodney W. Grimes */ 749b50d902SRodney W. Grimes return (1); 759b50d902SRodney W. Grimes } 769b50d902SRodney W. Grimes 779b50d902SRodney W. Grimes void 786d8484b0SPhilippe Charnier fail(const char *linebuf __unused, const char *reason __unused) 799b50d902SRodney W. Grimes { 809b50d902SRodney W. Grimes 819b50d902SRodney W. Grimes /* 829ce73e90SMike Heffner if (value("debug") == NULL) 839b50d902SRodney W. Grimes return; 849b50d902SRodney W. Grimes fprintf(stderr, "\"%s\"\nnot a header because %s\n", linebuf, reason); 859b50d902SRodney W. Grimes */ 869b50d902SRodney W. Grimes } 879b50d902SRodney W. Grimes 889b50d902SRodney W. Grimes /* 899b50d902SRodney W. Grimes * Split a headline into its useful components. 909b50d902SRodney W. Grimes * Copy the line into dynamic string space, then set 919b50d902SRodney W. Grimes * pointers into the copied line in the passed headline 929b50d902SRodney W. Grimes * structure. Actually, it scans. 939b50d902SRodney W. Grimes */ 949b50d902SRodney W. Grimes void 956d8484b0SPhilippe Charnier parse(char line[], struct headline *hl, char pbuf[]) 969b50d902SRodney W. Grimes { 979ce73e90SMike Heffner char *cp, *sp; 989b50d902SRodney W. Grimes char word[LINESIZE]; 999b50d902SRodney W. Grimes 1009ce73e90SMike Heffner hl->l_from = NULL; 1019ce73e90SMike Heffner hl->l_tty = NULL; 1029ce73e90SMike Heffner hl->l_date = NULL; 1039b50d902SRodney W. Grimes cp = line; 1049b50d902SRodney W. Grimes sp = pbuf; 1059b50d902SRodney W. Grimes /* 1069b50d902SRodney W. Grimes * Skip over "From" first. 1079b50d902SRodney W. Grimes */ 1089b50d902SRodney W. Grimes cp = nextword(cp, word); 1092e99d1d5SMike Heffner /* 1102e99d1d5SMike Heffner * Check for missing return-path. 1112e99d1d5SMike Heffner */ 1122e99d1d5SMike Heffner if (isdate(cp)) { 1132e99d1d5SMike Heffner hl->l_date = copyin(cp, &sp); 1142e99d1d5SMike Heffner return; 1152e99d1d5SMike Heffner } 1169b50d902SRodney W. Grimes cp = nextword(cp, word); 1172e99d1d5SMike Heffner if (strlen(word) > 0) 1189b50d902SRodney W. Grimes hl->l_from = copyin(word, &sp); 1192e99d1d5SMike Heffner if (cp != NULL && strncmp(cp, "tty", 3) == 0) { 1209b50d902SRodney W. Grimes cp = nextword(cp, word); 1219b50d902SRodney W. Grimes hl->l_tty = copyin(word, &sp); 1229b50d902SRodney W. Grimes } 1239ce73e90SMike Heffner if (cp != NULL) 1249b50d902SRodney W. Grimes hl->l_date = copyin(cp, &sp); 1259b50d902SRodney W. Grimes } 1269b50d902SRodney W. Grimes 1279b50d902SRodney W. Grimes /* 1289b50d902SRodney W. Grimes * Copy the string on the left into the string on the right 1299b50d902SRodney W. Grimes * and bump the right (reference) string pointer by the length. 1309b50d902SRodney W. Grimes * Thus, dynamically allocate space in the right string, copying 1319b50d902SRodney W. Grimes * the left string into it. 1329b50d902SRodney W. Grimes */ 1339b50d902SRodney W. Grimes char * 1346d8484b0SPhilippe Charnier copyin(char *src, char **space) 1359b50d902SRodney W. Grimes { 1369ce73e90SMike Heffner char *cp, *top; 1379b50d902SRodney W. Grimes 1389b50d902SRodney W. Grimes top = cp = *space; 1399ce73e90SMike Heffner while ((*cp++ = *src++) != '\0') 1409b50d902SRodney W. Grimes ; 1419b50d902SRodney W. Grimes *space = cp; 1429b50d902SRodney W. Grimes return (top); 1439b50d902SRodney W. Grimes } 1449b50d902SRodney W. Grimes 1459b50d902SRodney W. Grimes /* 1469b50d902SRodney W. Grimes * Test to see if the passed string is a ctime(3) generated 1479b50d902SRodney W. Grimes * date string as documented in the manual. The template 1489b50d902SRodney W. Grimes * below is used as the criterion of correctness. 1499b50d902SRodney W. Grimes * Also, we check for a possible trailing time zone using 1509b50d902SRodney W. Grimes * the tmztype template. 151a9992a76SMike Heffner * 152a9992a76SMike Heffner * If the mail file is created by Sys V (Solaris), there are 153a9992a76SMike Heffner * no seconds in the time. If the mail is created by another 154a9992a76SMike Heffner * program such as imapd, it might have timezone as 155a9992a76SMike Heffner * <-|+>nnnn (-0800 for instance) at the end. 1569b50d902SRodney W. Grimes */ 1579b50d902SRodney W. Grimes 1589b50d902SRodney W. Grimes /* 1599b50d902SRodney W. Grimes * 'A' An upper case char 1609b50d902SRodney W. Grimes * 'a' A lower case char 1619b50d902SRodney W. Grimes * ' ' A space 1629b50d902SRodney W. Grimes * '0' A digit 163a9992a76SMike Heffner * 'O' A digit or space 164a9992a76SMike Heffner * 'p' A punctuation char 165a9992a76SMike Heffner * 'P' A punctuation char or space 1669b50d902SRodney W. Grimes * ':' A colon 1679b50d902SRodney W. Grimes * 'N' A new line 1689b50d902SRodney W. Grimes */ 169a9992a76SMike Heffner 170a9992a76SMike Heffner static char *date_formats[] = { 171a9992a76SMike Heffner "Aaa Aaa O0 00:00:00 0000", /* Mon Jan 01 23:59:59 2001 */ 172a9992a76SMike Heffner "Aaa Aaa O0 00:00:00 AAA 0000", /* Mon Jan 01 23:59:59 PST 2001 */ 173a9992a76SMike Heffner "Aaa Aaa O0 00:00:00 0000 p0000", /* Mon Jan 01 23:59:59 2001 -0800 */ 174a9992a76SMike Heffner "Aaa Aaa O0 00:00 0000", /* Mon Jan 01 23:59 2001 */ 175a9992a76SMike Heffner "Aaa Aaa O0 00:00 AAA 0000", /* Mon Jan 01 23:59 PST 2001 */ 176a9992a76SMike Heffner "Aaa Aaa O0 00:00 0000 p0000", /* Mon Jan 01 23:59 2001 -0800 */ 177a9992a76SMike Heffner NULL 178a9992a76SMike Heffner }; 1799b50d902SRodney W. Grimes 1809b50d902SRodney W. Grimes int 1816d8484b0SPhilippe Charnier isdate(char date[]) 1829b50d902SRodney W. Grimes { 183a9992a76SMike Heffner int i; 1849b50d902SRodney W. Grimes 185a9992a76SMike Heffner for(i = 0; date_formats[i] != NULL; i++) { 186a9992a76SMike Heffner if (cmatch(date, date_formats[i])) 187a9992a76SMike Heffner return (1); 188a9992a76SMike Heffner } 189a9992a76SMike Heffner return (0); 1909b50d902SRodney W. Grimes } 1919b50d902SRodney W. Grimes 1929b50d902SRodney W. Grimes /* 1939b50d902SRodney W. Grimes * Match the given string (cp) against the given template (tp). 1949b50d902SRodney W. Grimes * Return 1 if they match, 0 if they don't 1959b50d902SRodney W. Grimes */ 1969b50d902SRodney W. Grimes int 1976d8484b0SPhilippe Charnier cmatch(char *cp, char *tp) 1989b50d902SRodney W. Grimes { 1999b50d902SRodney W. Grimes 2009ce73e90SMike Heffner while (*cp != '\0' && *tp != '\0') 2019b50d902SRodney W. Grimes switch (*tp++) { 2029b50d902SRodney W. Grimes case 'a': 2036d48fa43SAndrey A. Chernov if (!islower((unsigned char)*cp++)) 2049ce73e90SMike Heffner return (0); 2059b50d902SRodney W. Grimes break; 2069b50d902SRodney W. Grimes case 'A': 2076d48fa43SAndrey A. Chernov if (!isupper((unsigned char)*cp++)) 2089ce73e90SMike Heffner return (0); 2099b50d902SRodney W. Grimes break; 2109b50d902SRodney W. Grimes case ' ': 2119b50d902SRodney W. Grimes if (*cp++ != ' ') 2129ce73e90SMike Heffner return (0); 2139b50d902SRodney W. Grimes break; 2149b50d902SRodney W. Grimes case '0': 2156d48fa43SAndrey A. Chernov if (!isdigit((unsigned char)*cp++)) 2169ce73e90SMike Heffner return (0); 2179b50d902SRodney W. Grimes break; 2189b50d902SRodney W. Grimes case 'O': 2196d48fa43SAndrey A. Chernov if (*cp != ' ' && !isdigit((unsigned char)*cp)) 2209ce73e90SMike Heffner return (0); 2219b50d902SRodney W. Grimes cp++; 2229b50d902SRodney W. Grimes break; 223a9992a76SMike Heffner case 'p': 2246d48fa43SAndrey A. Chernov if (!ispunct((unsigned char)*cp++)) 225a9992a76SMike Heffner return (0); 226a9992a76SMike Heffner break; 227a9992a76SMike Heffner case 'P': 2286d48fa43SAndrey A. Chernov if (*cp != ' ' && !ispunct((unsigned char)*cp)) 229a9992a76SMike Heffner return (0); 230a9992a76SMike Heffner cp++; 231a9992a76SMike Heffner break; 2329b50d902SRodney W. Grimes case ':': 2339b50d902SRodney W. Grimes if (*cp++ != ':') 2349ce73e90SMike Heffner return (0); 2359b50d902SRodney W. Grimes break; 2369b50d902SRodney W. Grimes case 'N': 2379b50d902SRodney W. Grimes if (*cp++ != '\n') 2389ce73e90SMike Heffner return (0); 2399b50d902SRodney W. Grimes break; 2409b50d902SRodney W. Grimes } 2419ce73e90SMike Heffner if (*cp != '\0' || *tp != '\0') 2429ce73e90SMike Heffner return (0); 2439b50d902SRodney W. Grimes return (1); 2449b50d902SRodney W. Grimes } 2459b50d902SRodney W. Grimes 2469b50d902SRodney W. Grimes /* 2479b50d902SRodney W. Grimes * Collect a liberal (space, tab delimited) word into the word buffer 2489b50d902SRodney W. Grimes * passed. Also, return a pointer to the next word following that, 2499ce73e90SMike Heffner * or NULL if none follow. 2509b50d902SRodney W. Grimes */ 2519b50d902SRodney W. Grimes char * 2526d8484b0SPhilippe Charnier nextword(char *wp, char *wbuf) 2539b50d902SRodney W. Grimes { 2549ce73e90SMike Heffner int c; 2559b50d902SRodney W. Grimes 2569ce73e90SMike Heffner if (wp == NULL) { 2579ce73e90SMike Heffner *wbuf = '\0'; 2589ce73e90SMike Heffner return (NULL); 2599b50d902SRodney W. Grimes } 2609ce73e90SMike Heffner while ((c = *wp++) != '\0' && c != ' ' && c != '\t') { 2619b50d902SRodney W. Grimes *wbuf++ = c; 2629b50d902SRodney W. Grimes if (c == '"') { 2639ce73e90SMike Heffner while ((c = *wp++) != '\0' && c != '"') 2649b50d902SRodney W. Grimes *wbuf++ = c; 2659b50d902SRodney W. Grimes if (c == '"') 2669b50d902SRodney W. Grimes *wbuf++ = c; 2679b50d902SRodney W. Grimes else 2689b50d902SRodney W. Grimes wp--; 2699b50d902SRodney W. Grimes } 2709b50d902SRodney W. Grimes } 2719b50d902SRodney W. Grimes *wbuf = '\0'; 2729b50d902SRodney W. Grimes for (; c == ' ' || c == '\t'; c = *wp++) 2739b50d902SRodney W. Grimes ; 2749ce73e90SMike Heffner if (c == '\0') 2759ce73e90SMike Heffner return (NULL); 2769b50d902SRodney W. Grimes return (wp - 1); 2779b50d902SRodney W. Grimes } 278