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