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