Home | History | Annotate | Line # | Download | only in mail
head.c revision 1.21.34.1
      1  1.21.34.1       tls /*	$NetBSD: head.c,v 1.21.34.1 2013/02/25 00:30:36 tls 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.21.34.1       tls __RCSID("$NetBSD: head.c,v 1.21.34.1 2013/02/25 00:30:36 tls 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.21.34.1       tls 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.21.34.1       tls 		case '+':
     90  1.21.34.1       tls 			if (*cp != '+' && *cp != '-')
     91  1.21.34.1       tls 				return 0;
     92  1.21.34.1       tls 			cp++;
     93  1.21.34.1       tls 			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.21.34.1       tls  * '+'	A plus or minus sign
    117       1.20  christos  */
    118  1.21.34.1       tls static struct cmatch_data {
    119  1.21.34.1       tls 	size_t		tlen;
    120  1.21.34.1       tls 	char const	*tdata;
    121  1.21.34.1       tls } const	cmatch_data[] = {
    122  1.21.34.1       tls #define TSZ(a)	(sizeof(a) - 1), a
    123  1.21.34.1       tls 	{ TSZ("Aaa Aaa O0 00:00:00 0000") },		/* BSD ctype */
    124  1.21.34.1       tls 	{ TSZ("Aaa Aaa O0 00:00 0000") },		/* SysV ctype */
    125  1.21.34.1       tls 	{ TSZ("Aaa Aaa O0 00:00:00 AAA 0000") },	/* BSD tmztype */
    126  1.21.34.1       tls 	{ TSZ("Aaa Aaa O0 00:00 AAA 0000") },		/* SysV tmztype */
    127  1.21.34.1       tls 	/*
    128  1.21.34.1       tls 	 * RFC 822-alike From_ lines do not conform to RFC 4155, but seem to
    129  1.21.34.1       tls 	 * be used in the wild by UW-imap (MBX format plus)
    130  1.21.34.1       tls 	 */
    131  1.21.34.1       tls 	{ TSZ("Aaa Aaa O0 00:00:00 0000 +0000") },	/* RFC822, UT offset */
    132  1.21.34.1       tls 	/*
    133  1.21.34.1       tls 	 * RFC 822 with zone spec:
    134  1.21.34.1       tls 	 *    1. military,
    135  1.21.34.1       tls 	 *    2. UT,
    136  1.21.34.1       tls 	 *    3. north america time zone strings
    137  1.21.34.1       tls 	 * note that 1. is strictly speaking not correct as some letters are
    138  1.21.34.1       tls 	 * not used
    139  1.21.34.1       tls 	 */
    140  1.21.34.1       tls 	{ TSZ("Aaa Aaa O0 00:00:00 0000 A") },
    141  1.21.34.1       tls 	{ TSZ("Aaa Aaa O0 00:00:00 0000 AA") },
    142  1.21.34.1       tls         { TSZ("Aaa Aaa O0 00:00:00 0000 AAA") },
    143  1.21.34.1       tls 	{ 0, NULL },
    144  1.21.34.1       tls };
    145       1.20  christos 
    146       1.20  christos static int
    147       1.20  christos isdate(const char date[])
    148       1.20  christos {
    149  1.21.34.1       tls 	static size_t cmatch_minlen = 0;
    150  1.21.34.1       tls 	struct cmatch_data const *cmdp;
    151  1.21.34.1       tls 	size_t dl = strlen(date);
    152  1.21.34.1       tls 
    153  1.21.34.1       tls 	if (cmatch_minlen == 0)
    154  1.21.34.1       tls 		for (cmdp = cmatch_data; cmdp->tdata != NULL; ++cmdp)
    155  1.21.34.1       tls 			cmatch_minlen = MIN(cmatch_minlen, cmdp->tlen);
    156  1.21.34.1       tls 
    157  1.21.34.1       tls 	if (dl < cmatch_minlen)
    158  1.21.34.1       tls 		return 0;
    159  1.21.34.1       tls 
    160  1.21.34.1       tls 	for (cmdp = cmatch_data; cmdp->tdata != NULL; ++cmdp)
    161  1.21.34.1       tls 		if (dl == cmdp->tlen && cmatch(date, cmdp->tdata))
    162  1.21.34.1       tls 			return 1;
    163       1.20  christos 
    164  1.21.34.1       tls 	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.21.34.1       tls  * 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