head.c revision 216370
1/* 2 * Copyright (c) 1980, 1993 3 * The Regents of the University of California. All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 4. Neither the name of the University nor the names of its contributors 14 * may be used to endorse or promote products derived from this software 15 * without specific prior written permission. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 * SUCH DAMAGE. 28 */ 29 30#ifndef lint 31#if 0 32static char sccsid[] = "@(#)head.c 8.2 (Berkeley) 4/20/95"; 33#endif 34#endif /* not lint */ 35#include <sys/cdefs.h> 36__FBSDID("$FreeBSD: head/usr.bin/mail/head.c 216370 2010-12-11 08:32:16Z joel $"); 37 38#include "rcv.h" 39#include "extern.h" 40 41/* 42 * Mail -- a mail program 43 * 44 * Routines for processing and detecting headlines. 45 */ 46 47/* 48 * See if the passed line buffer is a mail header. 49 * Return true if yes. Note the extreme pains to 50 * accomodate all funny formats. 51 */ 52int 53ishead(linebuf) 54 char linebuf[]; 55{ 56 struct headline hl; 57 char parbuf[BUFSIZ]; 58 59 if (strncmp(linebuf, "From ", 5) != 0) 60 return (0); 61 parse(linebuf, &hl, parbuf); 62 if (hl.l_date == NULL) { 63 fail(linebuf, "No date field"); 64 return (0); 65 } 66 if (!isdate(hl.l_date)) { 67 fail(linebuf, "Date field not legal date"); 68 return (0); 69 } 70 /* 71 * I guess we got it! 72 */ 73 return (1); 74} 75 76/*ARGSUSED*/ 77void 78fail(linebuf, reason) 79 const char *linebuf, *reason; 80{ 81 82 /* 83 if (value("debug") == NULL) 84 return; 85 fprintf(stderr, "\"%s\"\nnot a header because %s\n", linebuf, reason); 86 */ 87} 88 89/* 90 * Split a headline into its useful components. 91 * Copy the line into dynamic string space, then set 92 * pointers into the copied line in the passed headline 93 * structure. Actually, it scans. 94 */ 95void 96parse(line, hl, pbuf) 97 char line[], pbuf[]; 98 struct headline *hl; 99{ 100 char *cp, *sp; 101 char word[LINESIZE]; 102 103 hl->l_from = NULL; 104 hl->l_tty = NULL; 105 hl->l_date = NULL; 106 cp = line; 107 sp = pbuf; 108 /* 109 * Skip over "From" first. 110 */ 111 cp = nextword(cp, word); 112 /* 113 * Check for missing return-path. 114 */ 115 if (isdate(cp)) { 116 hl->l_date = copyin(cp, &sp); 117 return; 118 } 119 cp = nextword(cp, word); 120 if (strlen(word) > 0) 121 hl->l_from = copyin(word, &sp); 122 if (cp != NULL && strncmp(cp, "tty", 3) == 0) { 123 cp = nextword(cp, word); 124 hl->l_tty = copyin(word, &sp); 125 } 126 if (cp != NULL) 127 hl->l_date = copyin(cp, &sp); 128} 129 130/* 131 * Copy the string on the left into the string on the right 132 * and bump the right (reference) string pointer by the length. 133 * Thus, dynamically allocate space in the right string, copying 134 * the left string into it. 135 */ 136char * 137copyin(src, space) 138 char *src; 139 char **space; 140{ 141 char *cp, *top; 142 143 top = cp = *space; 144 while ((*cp++ = *src++) != '\0') 145 ; 146 *space = cp; 147 return (top); 148} 149 150/* 151 * Test to see if the passed string is a ctime(3) generated 152 * date string as documented in the manual. The template 153 * below is used as the criterion of correctness. 154 * Also, we check for a possible trailing time zone using 155 * the tmztype template. 156 * 157 * If the mail file is created by Sys V (Solaris), there are 158 * no seconds in the time. If the mail is created by another 159 * program such as imapd, it might have timezone as 160 * <-|+>nnnn (-0800 for instance) at the end. 161 */ 162 163/* 164 * 'A' An upper case char 165 * 'a' A lower case char 166 * ' ' A space 167 * '0' A digit 168 * 'O' A digit or space 169 * 'p' A punctuation char 170 * 'P' A punctuation char or space 171 * ':' A colon 172 * 'N' A new line 173 */ 174 175static char *date_formats[] = { 176 "Aaa Aaa O0 00:00:00 0000", /* Mon Jan 01 23:59:59 2001 */ 177 "Aaa Aaa O0 00:00:00 AAA 0000", /* Mon Jan 01 23:59:59 PST 2001 */ 178 "Aaa Aaa O0 00:00:00 0000 p0000", /* Mon Jan 01 23:59:59 2001 -0800 */ 179 "Aaa Aaa O0 00:00 0000", /* Mon Jan 01 23:59 2001 */ 180 "Aaa Aaa O0 00:00 AAA 0000", /* Mon Jan 01 23:59 PST 2001 */ 181 "Aaa Aaa O0 00:00 0000 p0000", /* Mon Jan 01 23:59 2001 -0800 */ 182 NULL 183}; 184 185int 186isdate(date) 187 char date[]; 188{ 189 int i; 190 191 for(i = 0; date_formats[i] != NULL; i++) { 192 if (cmatch(date, date_formats[i])) 193 return (1); 194 } 195 return (0); 196} 197 198/* 199 * Match the given string (cp) against the given template (tp). 200 * Return 1 if they match, 0 if they don't 201 */ 202int 203cmatch(cp, tp) 204 char *cp, *tp; 205{ 206 207 while (*cp != '\0' && *tp != '\0') 208 switch (*tp++) { 209 case 'a': 210 if (!islower((unsigned char)*cp++)) 211 return (0); 212 break; 213 case 'A': 214 if (!isupper((unsigned char)*cp++)) 215 return (0); 216 break; 217 case ' ': 218 if (*cp++ != ' ') 219 return (0); 220 break; 221 case '0': 222 if (!isdigit((unsigned char)*cp++)) 223 return (0); 224 break; 225 case 'O': 226 if (*cp != ' ' && !isdigit((unsigned char)*cp)) 227 return (0); 228 cp++; 229 break; 230 case 'p': 231 if (!ispunct((unsigned char)*cp++)) 232 return (0); 233 break; 234 case 'P': 235 if (*cp != ' ' && !ispunct((unsigned char)*cp)) 236 return (0); 237 cp++; 238 break; 239 case ':': 240 if (*cp++ != ':') 241 return (0); 242 break; 243 case 'N': 244 if (*cp++ != '\n') 245 return (0); 246 break; 247 } 248 if (*cp != '\0' || *tp != '\0') 249 return (0); 250 return (1); 251} 252 253/* 254 * Collect a liberal (space, tab delimited) word into the word buffer 255 * passed. Also, return a pointer to the next word following that, 256 * or NULL if none follow. 257 */ 258char * 259nextword(wp, wbuf) 260 char *wp, *wbuf; 261{ 262 int c; 263 264 if (wp == NULL) { 265 *wbuf = '\0'; 266 return (NULL); 267 } 268 while ((c = *wp++) != '\0' && c != ' ' && c != '\t') { 269 *wbuf++ = c; 270 if (c == '"') { 271 while ((c = *wp++) != '\0' && c != '"') 272 *wbuf++ = c; 273 if (c == '"') 274 *wbuf++ = c; 275 else 276 wp--; 277 } 278 } 279 *wbuf = '\0'; 280 for (; c == ' ' || c == '\t'; c = *wp++) 281 ; 282 if (c == '\0') 283 return (NULL); 284 return (wp - 1); 285} 286