xref: /plan9/sys/src/cmd/upas/vf/vf.c (revision 238ca1fe340c2ddde96f1c246538fcd9a2198a9f)
15243b8d1SDavid du Colombier /*
25243b8d1SDavid du Colombier  *  this is a filter that changes mime types and names of
35243b8d1SDavid du Colombier  *  suspect executable attachments.
45243b8d1SDavid du Colombier  */
59a747e4fSDavid du Colombier #include "common.h"
69a747e4fSDavid du Colombier #include <ctype.h>
79a747e4fSDavid du Colombier 
89a747e4fSDavid du Colombier Biobuf in;
99a747e4fSDavid du Colombier Biobuf out;
109a747e4fSDavid du Colombier 
119a747e4fSDavid du Colombier typedef struct Mtype Mtype;
129a747e4fSDavid du Colombier typedef struct Hdef Hdef;
139a747e4fSDavid du Colombier typedef struct Hline Hline;
149a747e4fSDavid du Colombier typedef struct Part Part;
159a747e4fSDavid du Colombier 
169a747e4fSDavid du Colombier static int	badfile(char *name);
179a747e4fSDavid du Colombier static int	badtype(char *type);
189a747e4fSDavid du Colombier static void	ctype(Part*, Hdef*, char*);
199a747e4fSDavid du Colombier static void	cencoding(Part*, Hdef*, char*);
209a747e4fSDavid du Colombier static void	cdisposition(Part*, Hdef*, char*);
219a747e4fSDavid du Colombier static int	decquoted(char *out, char *in, char *e);
229a747e4fSDavid du Colombier static char*	getstring(char *p, String *s, int dolower);
239a747e4fSDavid du Colombier static void	init_hdefs(void);
249a747e4fSDavid du Colombier static int	isattribute(char **pp, char *attr);
259a747e4fSDavid du Colombier static int	latin1toutf(char *out, char *in, char *e);
269a747e4fSDavid du Colombier static String*	mkboundary(void);
279a747e4fSDavid du Colombier static Part*	part(Part *pp);
289a747e4fSDavid du Colombier static Part*	passbody(Part *p, int dobound);
299a747e4fSDavid du Colombier static void	passnotheader(void);
309a747e4fSDavid du Colombier static void	passunixheader(void);
319a747e4fSDavid du Colombier static Part*	problemchild(Part *p);
329a747e4fSDavid du Colombier static void	readheader(Part *p);
339a747e4fSDavid du Colombier static Hline*	readhl(void);
349a747e4fSDavid du Colombier static void	readmtypes(void);
35a21ce2d6SDavid du Colombier static int	save(Part *p, char *file);
369a747e4fSDavid du Colombier static void	setfilename(Part *p, char *name);
379a747e4fSDavid du Colombier static char*	skiptosemi(char *p);
389a747e4fSDavid du Colombier static char*	skipwhite(char *p);
399a747e4fSDavid du Colombier static String*	tokenconvert(String *t);
40a21ce2d6SDavid du Colombier static void	writeheader(Part *p, int);
419a747e4fSDavid du Colombier 
429a747e4fSDavid du Colombier enum
439a747e4fSDavid du Colombier {
44850dd0caSDavid du Colombier 	/* encodings */
459a747e4fSDavid du Colombier 	Enone=	0,
469a747e4fSDavid du Colombier 	Ebase64,
479a747e4fSDavid du Colombier 	Equoted,
489a747e4fSDavid du Colombier 
49850dd0caSDavid du Colombier 	/* disposition possibilities */
509a747e4fSDavid du Colombier 	Dnone=	0,
519a747e4fSDavid du Colombier 	Dinline,
529a747e4fSDavid du Colombier 	Dfile,
539a747e4fSDavid du Colombier 	Dignore,
549a747e4fSDavid du Colombier 
559a747e4fSDavid du Colombier 	PAD64=	'=',
569a747e4fSDavid du Colombier };
579a747e4fSDavid du Colombier 
589a747e4fSDavid du Colombier /*
599a747e4fSDavid du Colombier  *  a message part; either the whole message or a subpart
609a747e4fSDavid du Colombier  */
619a747e4fSDavid du Colombier struct Part
629a747e4fSDavid du Colombier {
639a747e4fSDavid du Colombier 	Part	*pp;		/* parent part */
649a747e4fSDavid du Colombier 	Hline	*hl;		/* linked list of header lines */
659a747e4fSDavid du Colombier 	int	disposition;
669a747e4fSDavid du Colombier 	int	encoding;
679a747e4fSDavid du Colombier 	int	badfile;
689a747e4fSDavid du Colombier 	int	badtype;
699a747e4fSDavid du Colombier 	String	*boundary;	/* boundary for multiparts */
709a747e4fSDavid du Colombier 	int	blen;
719a747e4fSDavid du Colombier 	String	*charset;	/* character set */
729a747e4fSDavid du Colombier 	String	*type;		/* content type */
73a21ce2d6SDavid du Colombier 	String	*filename;	/* file name */
74a21ce2d6SDavid du Colombier 	Biobuf	*tmpbuf;		/* diversion input buffer */
759a747e4fSDavid du Colombier };
769a747e4fSDavid du Colombier 
779a747e4fSDavid du Colombier /*
789a747e4fSDavid du Colombier  *  a (multi)line header
799a747e4fSDavid du Colombier  */
809a747e4fSDavid du Colombier struct Hline
819a747e4fSDavid du Colombier {
829a747e4fSDavid du Colombier 	Hline	*next;
839a747e4fSDavid du Colombier 	String		*s;
849a747e4fSDavid du Colombier };
859a747e4fSDavid du Colombier 
869a747e4fSDavid du Colombier /*
879a747e4fSDavid du Colombier  *  header definitions for parsing
889a747e4fSDavid du Colombier  */
899a747e4fSDavid du Colombier struct Hdef
909a747e4fSDavid du Colombier {
919a747e4fSDavid du Colombier 	char *type;
929a747e4fSDavid du Colombier 	void (*f)(Part*, Hdef*, char*);
939a747e4fSDavid du Colombier 	int len;
949a747e4fSDavid du Colombier };
959a747e4fSDavid du Colombier 
969a747e4fSDavid du Colombier Hdef hdefs[] =
979a747e4fSDavid du Colombier {
989a747e4fSDavid du Colombier 	{ "content-type:", ctype, },
999a747e4fSDavid du Colombier 	{ "content-transfer-encoding:", cencoding, },
1009a747e4fSDavid du Colombier 	{ "content-disposition:", cdisposition, },
1019a747e4fSDavid du Colombier 	{ 0, },
1029a747e4fSDavid du Colombier };
1039a747e4fSDavid du Colombier 
1049a747e4fSDavid du Colombier /*
1059a747e4fSDavid du Colombier  *  acceptable content types and their extensions
1069a747e4fSDavid du Colombier  */
1079a747e4fSDavid du Colombier struct Mtype {
1089a747e4fSDavid du Colombier 	Mtype	*next;
1099a747e4fSDavid du Colombier 	char 	*ext;		/* extension */
1109a747e4fSDavid du Colombier 	char	*gtype;		/* generic content type */
1119a747e4fSDavid du Colombier 	char	*stype;		/* specific content type */
1129a747e4fSDavid du Colombier 	char	class;
1139a747e4fSDavid du Colombier };
1149a747e4fSDavid du Colombier Mtype *mtypes;
1159a747e4fSDavid du Colombier 
116f06c651dSDavid du Colombier int justreject;
117a50688edSDavid du Colombier char *savefile;
118f06c651dSDavid du Colombier 
1199a747e4fSDavid du Colombier void
usage(void)12090faae21SDavid du Colombier usage(void)
12190faae21SDavid du Colombier {
12290faae21SDavid du Colombier 	fprint(2, "usage: upas/vf [-r] [-s savefile]\n");
12390faae21SDavid du Colombier 	exits("usage");
12490faae21SDavid du Colombier }
12590faae21SDavid du Colombier 
12690faae21SDavid du Colombier void
main(int argc,char ** argv)1279a747e4fSDavid du Colombier main(int argc, char **argv)
1289a747e4fSDavid du Colombier {
1299a747e4fSDavid du Colombier 	ARGBEGIN{
130f06c651dSDavid du Colombier 	case 'r':
131f06c651dSDavid du Colombier 		justreject = 1;
132f06c651dSDavid du Colombier 		break;
133a50688edSDavid du Colombier 	case 's':
13490faae21SDavid du Colombier 		savefile = EARGF(usage());
135a50688edSDavid du Colombier 		break;
13690faae21SDavid du Colombier 	default:
13790faae21SDavid du Colombier 		usage();
13890faae21SDavid du Colombier 	}ARGEND
13990faae21SDavid du Colombier 
14090faae21SDavid du Colombier 	if(argc)
14190faae21SDavid du Colombier 		usage();
1429a747e4fSDavid du Colombier 
1439a747e4fSDavid du Colombier 	Binit(&in, 0, OREAD);
1449a747e4fSDavid du Colombier 	Binit(&out, 1, OWRITE);
1459a747e4fSDavid du Colombier 
1469a747e4fSDavid du Colombier 	init_hdefs();
1479a747e4fSDavid du Colombier 	readmtypes();
1489a747e4fSDavid du Colombier 
1499a747e4fSDavid du Colombier 	/* pass through our standard 'From ' line */
1509a747e4fSDavid du Colombier 	passunixheader();
1519a747e4fSDavid du Colombier 
1529a747e4fSDavid du Colombier 	/* parse with the top level part */
1539a747e4fSDavid du Colombier 	part(nil);
1549a747e4fSDavid du Colombier 
1559a747e4fSDavid du Colombier 	exits(0);
1569a747e4fSDavid du Colombier }
1579a747e4fSDavid du Colombier 
158a21ce2d6SDavid du Colombier void
refuse(char * reason)159850dd0caSDavid du Colombier refuse(char *reason)
160a21ce2d6SDavid du Colombier {
161*238ca1feSDavid du Colombier 	char *full;
162850dd0caSDavid du Colombier 	static char msg[] =
163850dd0caSDavid du Colombier 		"mail refused: we don't accept executable attachments";
164850dd0caSDavid du Colombier 
165*238ca1feSDavid du Colombier 	full = smprint("%s: %s", msg, reason);
166*238ca1feSDavid du Colombier 	postnote(PNGROUP, getpid(), full);
167*238ca1feSDavid du Colombier 	exits(full);
168a21ce2d6SDavid du Colombier }
169a21ce2d6SDavid du Colombier 
170a21ce2d6SDavid du Colombier 
1719a747e4fSDavid du Colombier /*
1729a747e4fSDavid du Colombier  *  parse a part; returns the ancestor whose boundary terminated
1739a747e4fSDavid du Colombier  *  this part or nil on EOF.
1749a747e4fSDavid du Colombier  */
1759a747e4fSDavid du Colombier static Part*
part(Part * pp)1769a747e4fSDavid du Colombier part(Part *pp)
1779a747e4fSDavid du Colombier {
1789a747e4fSDavid du Colombier 	Part *p, *np;
1799a747e4fSDavid du Colombier 
1809a747e4fSDavid du Colombier 	p = mallocz(sizeof *p, 1);
1819a747e4fSDavid du Colombier 	p->pp = pp;
1829a747e4fSDavid du Colombier 	readheader(p);
1839a747e4fSDavid du Colombier 
1849a747e4fSDavid du Colombier 	if(p->boundary != nil){
1859a747e4fSDavid du Colombier 		/* the format of a multipart part is always:
1869a747e4fSDavid du Colombier 		 *   header
1879a747e4fSDavid du Colombier 		 *   null or ignored body
1889a747e4fSDavid du Colombier 		 *   boundary
1899a747e4fSDavid du Colombier 		 *   header
1909a747e4fSDavid du Colombier 		 *   body
1919a747e4fSDavid du Colombier 		 *   boundary
1929a747e4fSDavid du Colombier 		 *   ...
1939a747e4fSDavid du Colombier 		 */
194a21ce2d6SDavid du Colombier 		writeheader(p, 1);
1959a747e4fSDavid du Colombier 		np = passbody(p, 1);
1969a747e4fSDavid du Colombier 		if(np != p)
1979a747e4fSDavid du Colombier 			return np;
1989a747e4fSDavid du Colombier 		for(;;){
1999a747e4fSDavid du Colombier 			np = part(p);
2009a747e4fSDavid du Colombier 			if(np != p)
2019a747e4fSDavid du Colombier 				return np;
2029a747e4fSDavid du Colombier 		}
2039a747e4fSDavid du Colombier 	} else {
2049a747e4fSDavid du Colombier 		/* no boundary */
2059a747e4fSDavid du Colombier 		/* may still be multipart if this is a forwarded message */
2069a747e4fSDavid du Colombier 		if(p->type && cistrcmp(s_to_c(p->type), "message/rfc822") == 0){
2079a747e4fSDavid du Colombier 			/* the format of forwarded message is:
2089a747e4fSDavid du Colombier 			 *   header
2099a747e4fSDavid du Colombier 			 *   header
2109a747e4fSDavid du Colombier 			 *   body
2119a747e4fSDavid du Colombier 			 */
212a21ce2d6SDavid du Colombier 			writeheader(p, 1);
2139a747e4fSDavid du Colombier 			passnotheader();
2149a747e4fSDavid du Colombier 			return part(p);
2159a747e4fSDavid du Colombier 		} else {
216a21ce2d6SDavid du Colombier 			/*
217a21ce2d6SDavid du Colombier 			 * This is the meat.  This may be an executable.
2189a747e4fSDavid du Colombier 			 * if so, wrap it and change its type
2199a747e4fSDavid du Colombier 			 */
2209a747e4fSDavid du Colombier 			if(p->badtype || p->badfile){
221f06c651dSDavid du Colombier 				if(p->badfile == 2){
222a50688edSDavid du Colombier 					if(savefile != nil)
223a21ce2d6SDavid du Colombier 						save(p, savefile);
224850dd0caSDavid du Colombier 					syslog(0, "vf", "vf rejected %s %s",
225850dd0caSDavid du Colombier 						p->type? s_to_c(p->type): "?",
226f06c651dSDavid du Colombier 						p->filename?s_to_c(p->filename):"?");
227a50688edSDavid du Colombier 					fprint(2, "The mail contained an executable attachment.\n");
228a50688edSDavid du Colombier 					fprint(2, "We refuse all mail containing such.\n");
229850dd0caSDavid du Colombier 					refuse(nil);
230f06c651dSDavid du Colombier 				}
231a21ce2d6SDavid du Colombier 				np = problemchild(p);
232a21ce2d6SDavid du Colombier 				if(np != p)
233a21ce2d6SDavid du Colombier 					return np;
234a21ce2d6SDavid du Colombier 				/* if problemchild returns p, it turns out p is okay: fall thru */
235a21ce2d6SDavid du Colombier 			}
236a21ce2d6SDavid du Colombier 			writeheader(p, 1);
2379a747e4fSDavid du Colombier 			return passbody(p, 1);
2389a747e4fSDavid du Colombier 		}
2399a747e4fSDavid du Colombier 	}
2409a747e4fSDavid du Colombier }
2419a747e4fSDavid du Colombier 
2429a747e4fSDavid du Colombier /*
2439a747e4fSDavid du Colombier  *  read and parse a complete header
2449a747e4fSDavid du Colombier  */
2459a747e4fSDavid du Colombier static void
readheader(Part * p)2469a747e4fSDavid du Colombier readheader(Part *p)
2479a747e4fSDavid du Colombier {
2489a747e4fSDavid du Colombier 	Hline *hl, **l;
2499a747e4fSDavid du Colombier 	Hdef *hd;
2509a747e4fSDavid du Colombier 
2519a747e4fSDavid du Colombier 	l = &p->hl;
2529a747e4fSDavid du Colombier 	for(;;){
2539a747e4fSDavid du Colombier 		hl = readhl();
2549a747e4fSDavid du Colombier 		if(hl == nil)
2559a747e4fSDavid du Colombier 			break;
2569a747e4fSDavid du Colombier 		*l = hl;
2579a747e4fSDavid du Colombier 		l = &hl->next;
2589a747e4fSDavid du Colombier 
2599a747e4fSDavid du Colombier 		for(hd = hdefs; hd->type != nil; hd++){
2609a747e4fSDavid du Colombier 			if(cistrncmp(s_to_c(hl->s), hd->type, hd->len) == 0){
2619a747e4fSDavid du Colombier 				(*hd->f)(p, hd, s_to_c(hl->s));
2629a747e4fSDavid du Colombier 				break;
2639a747e4fSDavid du Colombier 			}
2649a747e4fSDavid du Colombier 		}
2659a747e4fSDavid du Colombier 	}
2669a747e4fSDavid du Colombier }
2679a747e4fSDavid du Colombier 
2689a747e4fSDavid du Colombier /*
2699a747e4fSDavid du Colombier  *  read a possibly multiline header line
2709a747e4fSDavid du Colombier  */
2719a747e4fSDavid du Colombier static Hline*
readhl(void)2729a747e4fSDavid du Colombier readhl(void)
2739a747e4fSDavid du Colombier {
2749a747e4fSDavid du Colombier 	Hline *hl;
2759a747e4fSDavid du Colombier 	String *s;
2769a747e4fSDavid du Colombier 	char *p;
2779a747e4fSDavid du Colombier 	int n;
2789a747e4fSDavid du Colombier 
2799a747e4fSDavid du Colombier 	p = Brdline(&in, '\n');
2809a747e4fSDavid du Colombier 	if(p == nil)
2819a747e4fSDavid du Colombier 		return nil;
2829a747e4fSDavid du Colombier 	n = Blinelen(&in);
2839a747e4fSDavid du Colombier 	if(memchr(p, ':', n) == nil){
2849a747e4fSDavid du Colombier 		Bseek(&in, -n, 1);
2859a747e4fSDavid du Colombier 		return nil;
2869a747e4fSDavid du Colombier 	}
2879a747e4fSDavid du Colombier 	s = s_nappend(s_new(), p, n);
2889a747e4fSDavid du Colombier 	for(;;){
2899a747e4fSDavid du Colombier 		p = Brdline(&in, '\n');
2909a747e4fSDavid du Colombier 		if(p == nil)
2919a747e4fSDavid du Colombier 			break;
2929a747e4fSDavid du Colombier 		n = Blinelen(&in);
2939a747e4fSDavid du Colombier 		if(*p != ' ' && *p != '\t'){
2949a747e4fSDavid du Colombier 			Bseek(&in, -n, 1);
2959a747e4fSDavid du Colombier 			break;
2969a747e4fSDavid du Colombier 		}
2979a747e4fSDavid du Colombier 		s = s_nappend(s, p, n);
2989a747e4fSDavid du Colombier 	}
2999a747e4fSDavid du Colombier 	hl = malloc(sizeof *hl);
3009a747e4fSDavid du Colombier 	hl->s = s;
3019a747e4fSDavid du Colombier 	hl->next = nil;
3029a747e4fSDavid du Colombier 	return hl;
3039a747e4fSDavid du Colombier }
3049a747e4fSDavid du Colombier 
3059a747e4fSDavid du Colombier /*
3069a747e4fSDavid du Colombier  *  write out a complete header
3079a747e4fSDavid du Colombier  */
3089a747e4fSDavid du Colombier static void
writeheader(Part * p,int xfree)309a21ce2d6SDavid du Colombier writeheader(Part *p, int xfree)
3109a747e4fSDavid du Colombier {
3119a747e4fSDavid du Colombier 	Hline *hl, *next;
3129a747e4fSDavid du Colombier 
3139a747e4fSDavid du Colombier 	for(hl = p->hl; hl != nil; hl = next){
3149a747e4fSDavid du Colombier 		Bprint(&out, "%s", s_to_c(hl->s));
315a21ce2d6SDavid du Colombier 		if(xfree)
3169a747e4fSDavid du Colombier 			s_free(hl->s);
3179a747e4fSDavid du Colombier 		next = hl->next;
318a21ce2d6SDavid du Colombier 		if(xfree)
3199a747e4fSDavid du Colombier 			free(hl);
3209a747e4fSDavid du Colombier 	}
321a21ce2d6SDavid du Colombier 	if(xfree)
3229a747e4fSDavid du Colombier 		p->hl = nil;
3239a747e4fSDavid du Colombier }
3249a747e4fSDavid du Colombier 
3259a747e4fSDavid du Colombier /*
3269a747e4fSDavid du Colombier  *  pass a body through.  return if we hit one of our ancestors'
3279a747e4fSDavid du Colombier  *  boundaries or EOF.  if we hit a boundary, return a pointer to
3289a747e4fSDavid du Colombier  *  that ancestor.  if we hit EOF, return nil.
3299a747e4fSDavid du Colombier  */
3309a747e4fSDavid du Colombier static Part*
passbody(Part * p,int dobound)3319a747e4fSDavid du Colombier passbody(Part *p, int dobound)
3329a747e4fSDavid du Colombier {
3339a747e4fSDavid du Colombier 	Part *pp;
334a21ce2d6SDavid du Colombier 	Biobuf *b;
3359a747e4fSDavid du Colombier 	char *cp;
3369a747e4fSDavid du Colombier 
3379a747e4fSDavid du Colombier 	for(;;){
338a21ce2d6SDavid du Colombier 		if(p->tmpbuf){
339a21ce2d6SDavid du Colombier 			b = p->tmpbuf;
340a21ce2d6SDavid du Colombier 			cp = Brdline(b, '\n');
341a21ce2d6SDavid du Colombier 			if(cp == nil){
342a21ce2d6SDavid du Colombier 				Bterm(b);
343a21ce2d6SDavid du Colombier 				p->tmpbuf = nil;
344a21ce2d6SDavid du Colombier 				goto Stdin;
345a21ce2d6SDavid du Colombier 			}
346a21ce2d6SDavid du Colombier 		}else{
347a21ce2d6SDavid du Colombier 		Stdin:
348a21ce2d6SDavid du Colombier 			b = &in;
349a21ce2d6SDavid du Colombier 			cp = Brdline(b, '\n');
350a21ce2d6SDavid du Colombier 		}
3519a747e4fSDavid du Colombier 		if(cp == nil)
3529a747e4fSDavid du Colombier 			return nil;
3539a747e4fSDavid du Colombier 		for(pp = p; pp != nil; pp = pp->pp)
3549a747e4fSDavid du Colombier 			if(pp->boundary != nil
3559a747e4fSDavid du Colombier 			&& strncmp(cp, s_to_c(pp->boundary), pp->blen) == 0){
3569a747e4fSDavid du Colombier 				if(dobound)
357a21ce2d6SDavid du Colombier 					Bwrite(&out, cp, Blinelen(b));
3589a747e4fSDavid du Colombier 				else
359a21ce2d6SDavid du Colombier 					Bseek(b, -Blinelen(b), 1);
3609a747e4fSDavid du Colombier 				return pp;
3619a747e4fSDavid du Colombier 			}
362a21ce2d6SDavid du Colombier 		Bwrite(&out, cp, Blinelen(b));
3639a747e4fSDavid du Colombier 	}
3649a747e4fSDavid du Colombier }
3659a747e4fSDavid du Colombier 
3669a747e4fSDavid du Colombier /*
367a50688edSDavid du Colombier  *  save the message somewhere
368a50688edSDavid du Colombier  */
369a21ce2d6SDavid du Colombier static vlong bodyoff;	/* clumsy hack */
370*238ca1feSDavid du Colombier 
371a21ce2d6SDavid du Colombier static int
save(Part * p,char * file)372a21ce2d6SDavid du Colombier save(Part *p, char *file)
373a50688edSDavid du Colombier {
374a50688edSDavid du Colombier 	int fd;
375a50688edSDavid du Colombier 	char *cp;
376a50688edSDavid du Colombier 
377a50688edSDavid du Colombier 	Bterm(&out);
378a50688edSDavid du Colombier 	memset(&out, 0, sizeof(out));
379a50688edSDavid du Colombier 
380a21ce2d6SDavid du Colombier 	fd = open(file, OWRITE);
381a50688edSDavid du Colombier 	if(fd < 0)
382a21ce2d6SDavid du Colombier 		return -1;
383a50688edSDavid du Colombier 	seek(fd, 0, 2);
384a50688edSDavid du Colombier 	Binit(&out, fd, OWRITE);
385a50688edSDavid du Colombier 	cp = ctime(time(0));
386b7327ca2SDavid du Colombier 	cp[28] = 0;
387a50688edSDavid du Colombier 	Bprint(&out, "From virusfilter %s\n", cp);
388a21ce2d6SDavid du Colombier 	writeheader(p, 0);
389a21ce2d6SDavid du Colombier 	bodyoff = Boffset(&out);
390a50688edSDavid du Colombier 	passbody(p, 1);
391a50688edSDavid du Colombier 	Bprint(&out, "\n");
392a50688edSDavid du Colombier 	Bterm(&out);
393a50688edSDavid du Colombier 	close(fd);
394a21ce2d6SDavid du Colombier 
395a21ce2d6SDavid du Colombier 	memset(&out, 0, sizeof out);
396a21ce2d6SDavid du Colombier 	Binit(&out, 1, OWRITE);
397a21ce2d6SDavid du Colombier 	return 0;
398a21ce2d6SDavid du Colombier }
399a21ce2d6SDavid du Colombier 
400a21ce2d6SDavid du Colombier /*
401a21ce2d6SDavid du Colombier  * write to a file but save the fd for passbody.
402a21ce2d6SDavid du Colombier  */
403a21ce2d6SDavid du Colombier static char*
savetmp(Part * p)404a21ce2d6SDavid du Colombier savetmp(Part *p)
405a21ce2d6SDavid du Colombier {
40694c38f21SDavid du Colombier 	char *name;
407a21ce2d6SDavid du Colombier 	int fd;
408a21ce2d6SDavid du Colombier 
40994c38f21SDavid du Colombier 	name = mktemp(smprint("%s/vf.XXXXXXXXXXX", UPASTMP));
410a21ce2d6SDavid du Colombier 	if((fd = create(name, OWRITE|OEXCL, 0666)) < 0){
411850dd0caSDavid du Colombier 		fprint(2, "%s: error creating temporary file: %r\n", argv0);
412850dd0caSDavid du Colombier 		refuse("can't create temporary file");
413a21ce2d6SDavid du Colombier 	}
414a21ce2d6SDavid du Colombier 	close(fd);
415a21ce2d6SDavid du Colombier 	if(save(p, name) < 0){
416850dd0caSDavid du Colombier 		fprint(2, "%s: error saving temporary file: %r\n", argv0);
417850dd0caSDavid du Colombier 		refuse("can't write temporary file");
418a21ce2d6SDavid du Colombier 	}
419a21ce2d6SDavid du Colombier 	if(p->tmpbuf){
420850dd0caSDavid du Colombier 		fprint(2, "%s: error in savetmp: already have tmp file!\n",
421850dd0caSDavid du Colombier 			argv0);
422850dd0caSDavid du Colombier 		refuse("already have temporary file");
423a21ce2d6SDavid du Colombier 	}
424a21ce2d6SDavid du Colombier 	p->tmpbuf = Bopen(name, OREAD|ORCLOSE);
425a21ce2d6SDavid du Colombier 	if(p->tmpbuf == nil){
426850dd0caSDavid du Colombier 		fprint(2, "%s: error reading temporary file: %r\n", argv0);
427850dd0caSDavid du Colombier 		refuse("error reading temporary file");
428a21ce2d6SDavid du Colombier 	}
429a21ce2d6SDavid du Colombier 	Bseek(p->tmpbuf, bodyoff, 0);
43094c38f21SDavid du Colombier 	return name;
431a21ce2d6SDavid du Colombier }
432a21ce2d6SDavid du Colombier 
433a21ce2d6SDavid du Colombier /*
43490faae21SDavid du Colombier  * Run the external checker to do content-based checks.
435a21ce2d6SDavid du Colombier  */
436a21ce2d6SDavid du Colombier static int
runchecker(Part * p)437a21ce2d6SDavid du Colombier runchecker(Part *p)
438a21ce2d6SDavid du Colombier {
439a21ce2d6SDavid du Colombier 	int pid;
440a21ce2d6SDavid du Colombier 	char *name;
441a21ce2d6SDavid du Colombier 	Waitmsg *w;
442a21ce2d6SDavid du Colombier 
443a21ce2d6SDavid du Colombier 	if(access("/mail/lib/validateattachment", AEXEC) < 0)
444a21ce2d6SDavid du Colombier 		return 0;
445a21ce2d6SDavid du Colombier 
446a21ce2d6SDavid du Colombier 	name = savetmp(p);
447a21ce2d6SDavid du Colombier 	fprint(2, "run checker %s\n", name);
448a21ce2d6SDavid du Colombier 	switch(pid = fork()){
449a21ce2d6SDavid du Colombier 	case -1:
450a21ce2d6SDavid du Colombier 		sysfatal("fork: %r");
451a21ce2d6SDavid du Colombier 	case 0:
452a21ce2d6SDavid du Colombier 		dup(2, 1);
453*238ca1feSDavid du Colombier 		execl("/mail/lib/validateattachment", "validateattachment",
454*238ca1feSDavid du Colombier 			name, nil);
455a21ce2d6SDavid du Colombier 		_exits("exec failed");
456a21ce2d6SDavid du Colombier 	}
457a21ce2d6SDavid du Colombier 
458a21ce2d6SDavid du Colombier 	/*
459a21ce2d6SDavid du Colombier 	 * Okay to return on error - will let mail through but wrapped.
460a21ce2d6SDavid du Colombier 	 */
461a21ce2d6SDavid du Colombier 	w = wait();
462a21ce2d6SDavid du Colombier 	if(w == nil){
463a21ce2d6SDavid du Colombier 		syslog(0, "mail", "vf wait failed: %r");
464a21ce2d6SDavid du Colombier 		return 0;
465a21ce2d6SDavid du Colombier 	}
466a21ce2d6SDavid du Colombier 	if(w->pid != pid){
467a21ce2d6SDavid du Colombier 		syslog(0, "mail", "vf wrong pid %d != %d", w->pid, pid);
468a21ce2d6SDavid du Colombier 		return 0;
469a21ce2d6SDavid du Colombier 	}
470*238ca1feSDavid du Colombier 	if(p->filename) {
471*238ca1feSDavid du Colombier 		free(name);
472*238ca1feSDavid du Colombier 		name = strdup(s_to_c(p->filename));
473*238ca1feSDavid du Colombier 	}
474a21ce2d6SDavid du Colombier 	if(strstr(w->msg, "discard")){
475a21ce2d6SDavid du Colombier 		syslog(0, "mail", "vf validateattachment rejected %s", name);
476850dd0caSDavid du Colombier 		refuse("rejected by validateattachment");
477a21ce2d6SDavid du Colombier 	}
478a21ce2d6SDavid du Colombier 	if(strstr(w->msg, "accept")){
479a21ce2d6SDavid du Colombier 		syslog(0, "mail", "vf validateattachment accepted %s", name);
480a21ce2d6SDavid du Colombier 		return 1;
481a21ce2d6SDavid du Colombier 	}
482a21ce2d6SDavid du Colombier 	free(w);
483*238ca1feSDavid du Colombier 	free(name);
484a21ce2d6SDavid du Colombier 	return 0;
485a50688edSDavid du Colombier }
486a50688edSDavid du Colombier 
487a50688edSDavid du Colombier /*
4889a747e4fSDavid du Colombier  *  emit a multipart Part that explains the problem
4899a747e4fSDavid du Colombier  */
4909a747e4fSDavid du Colombier static Part*
problemchild(Part * p)4919a747e4fSDavid du Colombier problemchild(Part *p)
4929a747e4fSDavid du Colombier {
4939a747e4fSDavid du Colombier 	Part *np;
4949a747e4fSDavid du Colombier 	Hline *hl;
4959a747e4fSDavid du Colombier 	String *boundary;
4969a747e4fSDavid du Colombier 	char *cp;
4979a747e4fSDavid du Colombier 
498a21ce2d6SDavid du Colombier 	/*
499a21ce2d6SDavid du Colombier 	 * We don't know whether the attachment is okay.
500a21ce2d6SDavid du Colombier 	 * If there's an external checker, let it have a crack at it.
501a21ce2d6SDavid du Colombier 	 */
502a21ce2d6SDavid du Colombier 	if(runchecker(p) > 0)
503a21ce2d6SDavid du Colombier 		return p;
504a21ce2d6SDavid du Colombier 
50590faae21SDavid du Colombier 	if(justreject)
50690faae21SDavid du Colombier 		return p;
50790faae21SDavid du Colombier 
508a21ce2d6SDavid du Colombier fprint(2, "x\n");
509f06c651dSDavid du Colombier 	syslog(0, "mail", "vf wrapped %s %s", p->type?s_to_c(p->type):"?",
5109a747e4fSDavid du Colombier 		p->filename?s_to_c(p->filename):"?");
511a21ce2d6SDavid du Colombier fprint(2, "x\n");
5129a747e4fSDavid du Colombier 
5139a747e4fSDavid du Colombier 	boundary = mkboundary();
514a21ce2d6SDavid du Colombier fprint(2, "x\n");
5159a747e4fSDavid du Colombier 	/* print out non-mime headers */
5169a747e4fSDavid du Colombier 	for(hl = p->hl; hl != nil; hl = hl->next)
5179a747e4fSDavid du Colombier 		if(cistrncmp(s_to_c(hl->s), "content-", 8) != 0)
5189a747e4fSDavid du Colombier 			Bprint(&out, "%s", s_to_c(hl->s));
5199a747e4fSDavid du Colombier 
520a21ce2d6SDavid du Colombier fprint(2, "x\n");
521a21ce2d6SDavid du Colombier 	/* add in our own multipart headers and message */
5229a747e4fSDavid du Colombier 	Bprint(&out, "Content-Type: multipart/mixed;\n");
5239a747e4fSDavid du Colombier 	Bprint(&out, "\tboundary=\"%s\"\n", s_to_c(boundary));
5249a747e4fSDavid du Colombier 	Bprint(&out, "Content-Disposition: inline\n");
5259a747e4fSDavid du Colombier 	Bprint(&out, "\n");
5269a747e4fSDavid du Colombier 	Bprint(&out, "This is a multi-part message in MIME format.\n");
5279a747e4fSDavid du Colombier 	Bprint(&out, "--%s\n", s_to_c(boundary));
5289a747e4fSDavid du Colombier 	Bprint(&out, "Content-Disposition: inline\n");
5299a747e4fSDavid du Colombier 	Bprint(&out, "Content-Type: text/plain; charset=\"US-ASCII\"\n");
5309a747e4fSDavid du Colombier 	Bprint(&out, "Content-Transfer-Encoding: 7bit\n");
5319a747e4fSDavid du Colombier 	Bprint(&out, "\n");
5325f6b17bbSDavid du Colombier 	Bprint(&out, "from postmaster@%s:\n", sysname());
5339a747e4fSDavid du Colombier 	Bprint(&out, "The following attachment had content that we can't\n");
5349a747e4fSDavid du Colombier 	Bprint(&out, "prove to be harmless.  To avoid possible automatic\n");
5359a747e4fSDavid du Colombier 	Bprint(&out, "execution, we changed the content headers.\n");
5369a747e4fSDavid du Colombier 	Bprint(&out, "The original header was:\n\n");
5379a747e4fSDavid du Colombier 
5389a747e4fSDavid du Colombier 	/* print out original header lines */
5399a747e4fSDavid du Colombier 	for(hl = p->hl; hl != nil; hl = hl->next)
5409a747e4fSDavid du Colombier 		if(cistrncmp(s_to_c(hl->s), "content-", 8) == 0)
5419a747e4fSDavid du Colombier 			Bprint(&out, "\t%s", s_to_c(hl->s));
5429a747e4fSDavid du Colombier 	Bprint(&out, "--%s\n", s_to_c(boundary));
5439a747e4fSDavid du Colombier 
5449a747e4fSDavid du Colombier 	/* change file name */
5459a747e4fSDavid du Colombier 	if(p->filename)
5469a747e4fSDavid du Colombier 		s_append(p->filename, ".suspect");
5479a747e4fSDavid du Colombier 	else
5489a747e4fSDavid du Colombier 		p->filename = s_copy("file.suspect");
5499a747e4fSDavid du Colombier 
5509a747e4fSDavid du Colombier 	/* print out new header */
5519a747e4fSDavid du Colombier 	Bprint(&out, "Content-Type: application/octet-stream\n");
5529a747e4fSDavid du Colombier 	Bprint(&out, "Content-Disposition: attachment; filename=\"%s\"\n", s_to_c(p->filename));
5539a747e4fSDavid du Colombier 	switch(p->encoding){
5549a747e4fSDavid du Colombier 	case Enone:
5559a747e4fSDavid du Colombier 		break;
5569a747e4fSDavid du Colombier 	case Ebase64:
5579a747e4fSDavid du Colombier 		Bprint(&out, "Content-Transfer-Encoding: base64\n");
5589a747e4fSDavid du Colombier 		break;
5599a747e4fSDavid du Colombier 	case Equoted:
5609a747e4fSDavid du Colombier 		Bprint(&out, "Content-Transfer-Encoding: quoted-printable\n");
5619a747e4fSDavid du Colombier 		break;
5629a747e4fSDavid du Colombier 	}
5639a747e4fSDavid du Colombier 
564a21ce2d6SDavid du Colombier fprint(2, "z\n");
5659a747e4fSDavid du Colombier 	/* pass the body */
5669a747e4fSDavid du Colombier 	np = passbody(p, 0);
5679a747e4fSDavid du Colombier 
568a21ce2d6SDavid du Colombier fprint(2, "w\n");
5699a747e4fSDavid du Colombier 	/* add the new boundary and the original terminator */
5709a747e4fSDavid du Colombier 	Bprint(&out, "--%s--\n", s_to_c(boundary));
5719a747e4fSDavid du Colombier 	if(np && np->boundary){
5729a747e4fSDavid du Colombier 		cp = Brdline(&in, '\n');
5739a747e4fSDavid du Colombier 		Bwrite(&out, cp, Blinelen(&in));
5749a747e4fSDavid du Colombier 	}
5759a747e4fSDavid du Colombier 
576a21ce2d6SDavid du Colombier fprint(2, "a %p\n", np);
5779a747e4fSDavid du Colombier 	return np;
5789a747e4fSDavid du Colombier }
5799a747e4fSDavid du Colombier 
5809a747e4fSDavid du Colombier static int
isattribute(char ** pp,char * attr)5819a747e4fSDavid du Colombier isattribute(char **pp, char *attr)
5829a747e4fSDavid du Colombier {
5839a747e4fSDavid du Colombier 	char *p;
5849a747e4fSDavid du Colombier 	int n;
5859a747e4fSDavid du Colombier 
5869a747e4fSDavid du Colombier 	n = strlen(attr);
5879a747e4fSDavid du Colombier 	p = *pp;
5889a747e4fSDavid du Colombier 	if(cistrncmp(p, attr, n) != 0)
5899a747e4fSDavid du Colombier 		return 0;
5909a747e4fSDavid du Colombier 	p += n;
5919a747e4fSDavid du Colombier 	while(*p == ' ')
5929a747e4fSDavid du Colombier 		p++;
5939a747e4fSDavid du Colombier 	if(*p++ != '=')
5949a747e4fSDavid du Colombier 		return 0;
5959a747e4fSDavid du Colombier 	while(*p == ' ')
5969a747e4fSDavid du Colombier 		p++;
5979a747e4fSDavid du Colombier 	*pp = p;
5989a747e4fSDavid du Colombier 	return 1;
5999a747e4fSDavid du Colombier }
6009a747e4fSDavid du Colombier 
6019a747e4fSDavid du Colombier /*
6029a747e4fSDavid du Colombier  *  parse content type header
6039a747e4fSDavid du Colombier  */
6049a747e4fSDavid du Colombier static void
ctype(Part * p,Hdef * h,char * cp)6059a747e4fSDavid du Colombier ctype(Part *p, Hdef *h, char *cp)
6069a747e4fSDavid du Colombier {
6079a747e4fSDavid du Colombier 	String *s;
6089a747e4fSDavid du Colombier 
6099a747e4fSDavid du Colombier 	cp += h->len;
6109a747e4fSDavid du Colombier 	cp = skipwhite(cp);
6119a747e4fSDavid du Colombier 
6129a747e4fSDavid du Colombier 	p->type = s_new();
6139a747e4fSDavid du Colombier 	cp = getstring(cp, p->type, 1);
6149a747e4fSDavid du Colombier 	if(badtype(s_to_c(p->type)))
6159a747e4fSDavid du Colombier 		p->badtype = 1;
6169a747e4fSDavid du Colombier 
6179a747e4fSDavid du Colombier 	while(*cp){
6189a747e4fSDavid du Colombier 		if(isattribute(&cp, "boundary")){
6199a747e4fSDavid du Colombier 			s = s_new();
6209a747e4fSDavid du Colombier 			cp = getstring(cp, s, 0);
6219a747e4fSDavid du Colombier 			p->boundary = s_reset(p->boundary);
6229a747e4fSDavid du Colombier 			s_append(p->boundary, "--");
6239a747e4fSDavid du Colombier 			s_append(p->boundary, s_to_c(s));
6249a747e4fSDavid du Colombier 			p->blen = s_len(p->boundary);
6259a747e4fSDavid du Colombier 			s_free(s);
6269a747e4fSDavid du Colombier 		} else if(cistrncmp(cp, "multipart", 9) == 0){
6279a747e4fSDavid du Colombier 			/*
6289a747e4fSDavid du Colombier 			 *  the first unbounded part of a multipart message,
6299a747e4fSDavid du Colombier 			 *  the preamble, is not displayed or saved
6309a747e4fSDavid du Colombier 			 */
6319a747e4fSDavid du Colombier 		} else if(isattribute(&cp, "name")){
6329a747e4fSDavid du Colombier 			setfilename(p, cp);
6339a747e4fSDavid du Colombier 		} else if(isattribute(&cp, "charset")){
6349a747e4fSDavid du Colombier 			if(p->charset == nil)
6359a747e4fSDavid du Colombier 				p->charset = s_new();
6369a747e4fSDavid du Colombier 			cp = getstring(cp, s_reset(p->charset), 0);
6379a747e4fSDavid du Colombier 		}
6389a747e4fSDavid du Colombier 
6399a747e4fSDavid du Colombier 		cp = skiptosemi(cp);
6409a747e4fSDavid du Colombier 	}
6419a747e4fSDavid du Colombier }
6429a747e4fSDavid du Colombier 
6439a747e4fSDavid du Colombier /*
6449a747e4fSDavid du Colombier  *  parse content encoding header
6459a747e4fSDavid du Colombier  */
6469a747e4fSDavid du Colombier static void
cencoding(Part * m,Hdef * h,char * p)6479a747e4fSDavid du Colombier cencoding(Part *m, Hdef *h, char *p)
6489a747e4fSDavid du Colombier {
6499a747e4fSDavid du Colombier 	p += h->len;
6509a747e4fSDavid du Colombier 	p = skipwhite(p);
6519a747e4fSDavid du Colombier 	if(cistrncmp(p, "base64", 6) == 0)
6529a747e4fSDavid du Colombier 		m->encoding = Ebase64;
6539a747e4fSDavid du Colombier 	else if(cistrncmp(p, "quoted-printable", 16) == 0)
6549a747e4fSDavid du Colombier 		m->encoding = Equoted;
6559a747e4fSDavid du Colombier }
6569a747e4fSDavid du Colombier 
6579a747e4fSDavid du Colombier /*
6589a747e4fSDavid du Colombier  *  parse content disposition header
6599a747e4fSDavid du Colombier  */
6609a747e4fSDavid du Colombier static void
cdisposition(Part * p,Hdef * h,char * cp)6619a747e4fSDavid du Colombier cdisposition(Part *p, Hdef *h, char *cp)
6629a747e4fSDavid du Colombier {
6639a747e4fSDavid du Colombier 	cp += h->len;
6649a747e4fSDavid du Colombier 	cp = skipwhite(cp);
6659a747e4fSDavid du Colombier 	while(*cp){
6669a747e4fSDavid du Colombier 		if(cistrncmp(cp, "inline", 6) == 0){
6679a747e4fSDavid du Colombier 			p->disposition = Dinline;
6689a747e4fSDavid du Colombier 		} else if(cistrncmp(cp, "attachment", 10) == 0){
6699a747e4fSDavid du Colombier 			p->disposition = Dfile;
6709a747e4fSDavid du Colombier 		} else if(cistrncmp(cp, "filename=", 9) == 0){
6719a747e4fSDavid du Colombier 			cp += 9;
6729a747e4fSDavid du Colombier 			setfilename(p, cp);
6739a747e4fSDavid du Colombier 		}
6749a747e4fSDavid du Colombier 		cp = skiptosemi(cp);
6759a747e4fSDavid du Colombier 	}
6769a747e4fSDavid du Colombier 
6779a747e4fSDavid du Colombier }
6789a747e4fSDavid du Colombier 
6799a747e4fSDavid du Colombier static void
setfilename(Part * p,char * name)6809a747e4fSDavid du Colombier setfilename(Part *p, char *name)
6819a747e4fSDavid du Colombier {
6829a747e4fSDavid du Colombier 	if(p->filename == nil)
6839a747e4fSDavid du Colombier 		p->filename = s_new();
6849a747e4fSDavid du Colombier 	getstring(name, s_reset(p->filename), 0);
6859a747e4fSDavid du Colombier 	p->filename = tokenconvert(p->filename);
686f06c651dSDavid du Colombier 	p->badfile = badfile(s_to_c(p->filename));
6879a747e4fSDavid du Colombier }
6889a747e4fSDavid du Colombier 
6899a747e4fSDavid du Colombier static char*
skipwhite(char * p)6909a747e4fSDavid du Colombier skipwhite(char *p)
6919a747e4fSDavid du Colombier {
6929a747e4fSDavid du Colombier 	while(isspace(*p))
6939a747e4fSDavid du Colombier 		p++;
6949a747e4fSDavid du Colombier 	return p;
6959a747e4fSDavid du Colombier }
6969a747e4fSDavid du Colombier 
6979a747e4fSDavid du Colombier static char*
skiptosemi(char * p)6989a747e4fSDavid du Colombier skiptosemi(char *p)
6999a747e4fSDavid du Colombier {
7009a747e4fSDavid du Colombier 	while(*p && *p != ';')
7019a747e4fSDavid du Colombier 		p++;
7029a747e4fSDavid du Colombier 	while(*p == ';' || isspace(*p))
7039a747e4fSDavid du Colombier 		p++;
7049a747e4fSDavid du Colombier 	return p;
7059a747e4fSDavid du Colombier }
7069a747e4fSDavid du Colombier 
7079a747e4fSDavid du Colombier /*
7089a747e4fSDavid du Colombier  *  parse a possibly "'d string from a header.  A
7099a747e4fSDavid du Colombier  *  ';' terminates the string.
7109a747e4fSDavid du Colombier  */
7119a747e4fSDavid du Colombier static char*
getstring(char * p,String * s,int dolower)7129a747e4fSDavid du Colombier getstring(char *p, String *s, int dolower)
7139a747e4fSDavid du Colombier {
7149a747e4fSDavid du Colombier 	s = s_reset(s);
7159a747e4fSDavid du Colombier 	p = skipwhite(p);
7169a747e4fSDavid du Colombier 	if(*p == '"'){
7179a747e4fSDavid du Colombier 		p++;
7189a747e4fSDavid du Colombier 		for(;*p && *p != '"'; p++)
7199a747e4fSDavid du Colombier 			if(dolower)
7209a747e4fSDavid du Colombier 				s_putc(s, tolower(*p));
7219a747e4fSDavid du Colombier 			else
7229a747e4fSDavid du Colombier 				s_putc(s, *p);
7239a747e4fSDavid du Colombier 		if(*p == '"')
7249a747e4fSDavid du Colombier 			p++;
7259a747e4fSDavid du Colombier 		s_terminate(s);
7269a747e4fSDavid du Colombier 
7279a747e4fSDavid du Colombier 		return p;
7289a747e4fSDavid du Colombier 	}
7299a747e4fSDavid du Colombier 
7309a747e4fSDavid du Colombier 	for(; *p && !isspace(*p) && *p != ';'; p++)
7319a747e4fSDavid du Colombier 		if(dolower)
7329a747e4fSDavid du Colombier 			s_putc(s, tolower(*p));
7339a747e4fSDavid du Colombier 		else
7349a747e4fSDavid du Colombier 			s_putc(s, *p);
7359a747e4fSDavid du Colombier 	s_terminate(s);
7369a747e4fSDavid du Colombier 
7379a747e4fSDavid du Colombier 	return p;
7389a747e4fSDavid du Colombier }
7399a747e4fSDavid du Colombier 
7409a747e4fSDavid du Colombier static void
init_hdefs(void)7419a747e4fSDavid du Colombier init_hdefs(void)
7429a747e4fSDavid du Colombier {
7439a747e4fSDavid du Colombier 	Hdef *hd;
7449a747e4fSDavid du Colombier 	static int already;
7459a747e4fSDavid du Colombier 
7469a747e4fSDavid du Colombier 	if(already)
7479a747e4fSDavid du Colombier 		return;
7489a747e4fSDavid du Colombier 	already = 1;
7499a747e4fSDavid du Colombier 
7509a747e4fSDavid du Colombier 	for(hd = hdefs; hd->type != nil; hd++)
7519a747e4fSDavid du Colombier 		hd->len = strlen(hd->type);
7529a747e4fSDavid du Colombier }
7539a747e4fSDavid du Colombier 
7549a747e4fSDavid du Colombier /*
7559a747e4fSDavid du Colombier  *  create a new boundary
7569a747e4fSDavid du Colombier  */
7579a747e4fSDavid du Colombier static String*
mkboundary(void)7589a747e4fSDavid du Colombier mkboundary(void)
7599a747e4fSDavid du Colombier {
7609a747e4fSDavid du Colombier 	char buf[32];
7619a747e4fSDavid du Colombier 	int i;
7629a747e4fSDavid du Colombier 	static int already;
7639a747e4fSDavid du Colombier 
7649a747e4fSDavid du Colombier 	if(already == 0){
7659a747e4fSDavid du Colombier 		srand((time(0)<<16)|getpid());
7669a747e4fSDavid du Colombier 		already = 1;
7679a747e4fSDavid du Colombier 	}
7689a747e4fSDavid du Colombier 	strcpy(buf, "upas-");
7699a747e4fSDavid du Colombier 	for(i = 5; i < sizeof(buf)-1; i++)
7709a747e4fSDavid du Colombier 		buf[i] = 'a' + nrand(26);
7719a747e4fSDavid du Colombier 	buf[i] = 0;
7729a747e4fSDavid du Colombier 	return s_copy(buf);
7739a747e4fSDavid du Colombier }
7749a747e4fSDavid du Colombier 
7759a747e4fSDavid du Colombier /*
7769a747e4fSDavid du Colombier  *  skip blank lines till header
7779a747e4fSDavid du Colombier  */
7789a747e4fSDavid du Colombier static void
passnotheader(void)7799a747e4fSDavid du Colombier passnotheader(void)
7809a747e4fSDavid du Colombier {
7819a747e4fSDavid du Colombier 	char *cp;
7829a747e4fSDavid du Colombier 	int i, n;
7839a747e4fSDavid du Colombier 
7849a747e4fSDavid du Colombier 	while((cp = Brdline(&in, '\n')) != nil){
7859a747e4fSDavid du Colombier 		n = Blinelen(&in);
7869a747e4fSDavid du Colombier 		for(i = 0; i < n-1; i++)
7879a747e4fSDavid du Colombier 			if(cp[i] != ' ' && cp[i] != '\t' && cp[i] != '\r'){
7889a747e4fSDavid du Colombier 				Bseek(&in, -n, 1);
7899a747e4fSDavid du Colombier 				return;
7909a747e4fSDavid du Colombier 			}
7919a747e4fSDavid du Colombier 		Bwrite(&out, cp, n);
7929a747e4fSDavid du Colombier 	}
7939a747e4fSDavid du Colombier }
7949a747e4fSDavid du Colombier 
7959a747e4fSDavid du Colombier /*
7969a747e4fSDavid du Colombier  *  pass unix header lines
7979a747e4fSDavid du Colombier  */
7989a747e4fSDavid du Colombier static void
passunixheader(void)7999a747e4fSDavid du Colombier passunixheader(void)
8009a747e4fSDavid du Colombier {
8019a747e4fSDavid du Colombier 	char *p;
8029a747e4fSDavid du Colombier 	int n;
8039a747e4fSDavid du Colombier 
8049a747e4fSDavid du Colombier 	while((p = Brdline(&in, '\n')) != nil){
8059a747e4fSDavid du Colombier 		n = Blinelen(&in);
8069a747e4fSDavid du Colombier 		if(strncmp(p, "From ", 5) != 0){
8079a747e4fSDavid du Colombier 			Bseek(&in, -n, 1);
8089a747e4fSDavid du Colombier 			break;
8099a747e4fSDavid du Colombier 		}
8109a747e4fSDavid du Colombier 		Bwrite(&out, p, n);
8119a747e4fSDavid du Colombier 	}
8129a747e4fSDavid du Colombier }
8139a747e4fSDavid du Colombier 
8149a747e4fSDavid du Colombier /*
8159a747e4fSDavid du Colombier  *  Read mime types
8169a747e4fSDavid du Colombier  */
8179a747e4fSDavid du Colombier static void
readmtypes(void)8189a747e4fSDavid du Colombier readmtypes(void)
8199a747e4fSDavid du Colombier {
8209a747e4fSDavid du Colombier 	Biobuf *b;
8219a747e4fSDavid du Colombier 	char *p;
8229a747e4fSDavid du Colombier 	char *f[6];
8239a747e4fSDavid du Colombier 	Mtype *m;
8249a747e4fSDavid du Colombier 	Mtype **l;
8259a747e4fSDavid du Colombier 
8269a747e4fSDavid du Colombier 	b = Bopen("/sys/lib/mimetype", OREAD);
8279a747e4fSDavid du Colombier 	if(b == nil)
8289a747e4fSDavid du Colombier 		return;
8299a747e4fSDavid du Colombier 
8309a747e4fSDavid du Colombier 	l = &mtypes;
8319a747e4fSDavid du Colombier 	while((p = Brdline(b, '\n')) != nil){
8329a747e4fSDavid du Colombier 		if(*p == '#')
8339a747e4fSDavid du Colombier 			continue;
8349a747e4fSDavid du Colombier 		p[Blinelen(b)-1] = 0;
8359a747e4fSDavid du Colombier 		if(tokenize(p, f, nelem(f)) < 5)
8369a747e4fSDavid du Colombier 			continue;
8379a747e4fSDavid du Colombier 		m = mallocz(sizeof *m, 1);
8389a747e4fSDavid du Colombier 		if(m == nil)
8399a747e4fSDavid du Colombier 			goto err;
8409a747e4fSDavid du Colombier 		m->ext = strdup(f[0]);
8419a747e4fSDavid du Colombier 		if(m->ext == 0)
8429a747e4fSDavid du Colombier 			goto err;
8439a747e4fSDavid du Colombier 		m->gtype = strdup(f[1]);
8449a747e4fSDavid du Colombier 		if(m->gtype == 0)
8459a747e4fSDavid du Colombier 			goto err;
8469a747e4fSDavid du Colombier 		m->stype = strdup(f[2]);
8479a747e4fSDavid du Colombier 		if(m->stype == 0)
8489a747e4fSDavid du Colombier 			goto err;
8499a747e4fSDavid du Colombier 		m->class = *f[4];
8509a747e4fSDavid du Colombier 		*l = m;
8519a747e4fSDavid du Colombier 		l = &(m->next);
8529a747e4fSDavid du Colombier 	}
8539a747e4fSDavid du Colombier 	Bterm(b);
8549a747e4fSDavid du Colombier 	return;
8559a747e4fSDavid du Colombier err:
8569a747e4fSDavid du Colombier 	if(m == nil)
8579a747e4fSDavid du Colombier 		return;
8589a747e4fSDavid du Colombier 	free(m->ext);
8599a747e4fSDavid du Colombier 	free(m->gtype);
8609a747e4fSDavid du Colombier 	free(m->stype);
8619a747e4fSDavid du Colombier 	free(m);
8629a747e4fSDavid du Colombier 	Bterm(b);
8639a747e4fSDavid du Colombier }
8649a747e4fSDavid du Colombier 
8659a747e4fSDavid du Colombier /*
8669a747e4fSDavid du Colombier  *  if the class is 'm' or 'y', accept it
8679a747e4fSDavid du Colombier  *  if the class is 'p' check a previous extension
8689a747e4fSDavid du Colombier  *  otherwise, filename is bad
8699a747e4fSDavid du Colombier  */
8709a747e4fSDavid du Colombier static int
badfile(char * name)8719a747e4fSDavid du Colombier badfile(char *name)
8729a747e4fSDavid du Colombier {
8739a747e4fSDavid du Colombier 	char *p;
8749a747e4fSDavid du Colombier 	Mtype *m;
8759a747e4fSDavid du Colombier 	int rv;
8769a747e4fSDavid du Colombier 
8779a747e4fSDavid du Colombier 	p = strrchr(name, '.');
8789a747e4fSDavid du Colombier 	if(p == nil)
8799a747e4fSDavid du Colombier 		return 0;
8809a747e4fSDavid du Colombier 
8819a747e4fSDavid du Colombier 	for(m = mtypes; m != nil; m = m->next)
8829a747e4fSDavid du Colombier 		if(cistrcmp(p, m->ext) == 0){
8839a747e4fSDavid du Colombier 			switch(m->class){
8849a747e4fSDavid du Colombier 			case 'm':
8859a747e4fSDavid du Colombier 			case 'y':
8869a747e4fSDavid du Colombier 				return 0;
8879a747e4fSDavid du Colombier 			case 'p':
8889a747e4fSDavid du Colombier 				*p = 0;
8899a747e4fSDavid du Colombier 				rv = badfile(name);
8909a747e4fSDavid du Colombier 				*p = '.';
8919a747e4fSDavid du Colombier 				return rv;
892f06c651dSDavid du Colombier 			case 'r':
893f06c651dSDavid du Colombier 				return 2;
8949a747e4fSDavid du Colombier 			}
8959a747e4fSDavid du Colombier 		}
8969a747e4fSDavid du Colombier 	return 1;
8979a747e4fSDavid du Colombier }
8989a747e4fSDavid du Colombier 
8999a747e4fSDavid du Colombier /*
9009a747e4fSDavid du Colombier  *  if the class is 'm' or 'y' or 'p', accept it
9019a747e4fSDavid du Colombier  *  otherwise, filename is bad
9029a747e4fSDavid du Colombier  */
9039a747e4fSDavid du Colombier static int
badtype(char * type)9049a747e4fSDavid du Colombier badtype(char *type)
9059a747e4fSDavid du Colombier {
9069a747e4fSDavid du Colombier 	Mtype *m;
9079a747e4fSDavid du Colombier 	char *s, *fix;
9089a747e4fSDavid du Colombier 	int rv = 1;
9099a747e4fSDavid du Colombier 
9109a747e4fSDavid du Colombier 	fix = s = strchr(type, '/');
9119a747e4fSDavid du Colombier 	if(s != nil)
9129a747e4fSDavid du Colombier 		*s++ = 0;
9139a747e4fSDavid du Colombier 	else
9149a747e4fSDavid du Colombier 		s = "-";
9159a747e4fSDavid du Colombier 
9169a747e4fSDavid du Colombier 	for(m = mtypes; m != nil; m = m->next){
9179a747e4fSDavid du Colombier 		if(cistrcmp(type, m->gtype) != 0)
9189a747e4fSDavid du Colombier 			continue;
9199a747e4fSDavid du Colombier 		if(cistrcmp(s, m->stype) != 0)
9209a747e4fSDavid du Colombier 			continue;
9219a747e4fSDavid du Colombier 		switch(m->class){
9229a747e4fSDavid du Colombier 		case 'y':
9239a747e4fSDavid du Colombier 		case 'p':
9249a747e4fSDavid du Colombier 		case 'm':
9259a747e4fSDavid du Colombier 			rv = 0;
9269a747e4fSDavid du Colombier 			break;
9279a747e4fSDavid du Colombier 		}
9289a747e4fSDavid du Colombier 		break;
9299a747e4fSDavid du Colombier 	}
9309a747e4fSDavid du Colombier 
9319a747e4fSDavid du Colombier 	if(fix != nil)
9329a747e4fSDavid du Colombier 		*fix = '/';
9339a747e4fSDavid du Colombier 	return rv;
9349a747e4fSDavid du Colombier }
9359a747e4fSDavid du Colombier 
9369a747e4fSDavid du Colombier /* rfc2047 non-ascii */
9379a747e4fSDavid du Colombier typedef struct Charset Charset;
9389a747e4fSDavid du Colombier struct Charset {
9399a747e4fSDavid du Colombier 	char *name;
9409a747e4fSDavid du Colombier 	int len;
9419a747e4fSDavid du Colombier 	int convert;
9429a747e4fSDavid du Colombier } charsets[] =
9439a747e4fSDavid du Colombier {
9449a747e4fSDavid du Colombier 	{ "us-ascii",		8,	1, },
9459a747e4fSDavid du Colombier 	{ "utf-8",		5,	0, },
9469a747e4fSDavid du Colombier 	{ "iso-8859-1",		10,	1, },
9479a747e4fSDavid du Colombier };
9489a747e4fSDavid du Colombier 
9499a747e4fSDavid du Colombier /*
9509a747e4fSDavid du Colombier  *  convert to UTF if need be
9519a747e4fSDavid du Colombier  */
9529a747e4fSDavid du Colombier static String*
tokenconvert(String * t)9539a747e4fSDavid du Colombier tokenconvert(String *t)
9549a747e4fSDavid du Colombier {
9559a747e4fSDavid du Colombier 	String *s;
9569a747e4fSDavid du Colombier 	char decoded[1024];
9579a747e4fSDavid du Colombier 	char utfbuf[2*1024];
9589a747e4fSDavid du Colombier 	int i, len;
9599a747e4fSDavid du Colombier 	char *e;
9609a747e4fSDavid du Colombier 	char *token;
9619a747e4fSDavid du Colombier 
9629a747e4fSDavid du Colombier 	token = s_to_c(t);
9639a747e4fSDavid du Colombier 	len = s_len(t);
9649a747e4fSDavid du Colombier 
9659a747e4fSDavid du Colombier 	if(token[0] != '=' || token[1] != '?' ||
9669a747e4fSDavid du Colombier 	   token[len-2] != '?' || token[len-1] != '=')
9679a747e4fSDavid du Colombier 		goto err;
9689a747e4fSDavid du Colombier 	e = token+len-2;
9699a747e4fSDavid du Colombier 	token += 2;
9709a747e4fSDavid du Colombier 
971850dd0caSDavid du Colombier 	/* bail if we don't understand the character set */
9729a747e4fSDavid du Colombier 	for(i = 0; i < nelem(charsets); i++)
9739a747e4fSDavid du Colombier 		if(cistrncmp(charsets[i].name, token, charsets[i].len) == 0)
9749a747e4fSDavid du Colombier 		if(token[charsets[i].len] == '?'){
9759a747e4fSDavid du Colombier 			token += charsets[i].len + 1;
9769a747e4fSDavid du Colombier 			break;
9779a747e4fSDavid du Colombier 		}
9789a747e4fSDavid du Colombier 	if(i >= nelem(charsets))
9799a747e4fSDavid du Colombier 		goto err;
9809a747e4fSDavid du Colombier 
981850dd0caSDavid du Colombier 	/* bail if it doesn't fit */
9829a747e4fSDavid du Colombier 	if(strlen(token) > sizeof(decoded)-1)
9839a747e4fSDavid du Colombier 		goto err;
9849a747e4fSDavid du Colombier 
985850dd0caSDavid du Colombier 	/* bail if we don't understand the encoding */
9869a747e4fSDavid du Colombier 	if(cistrncmp(token, "b?", 2) == 0){
9879a747e4fSDavid du Colombier 		token += 2;
9889a747e4fSDavid du Colombier 		len = dec64((uchar*)decoded, sizeof(decoded), token, e-token);
9899a747e4fSDavid du Colombier 		decoded[len] = 0;
9909a747e4fSDavid du Colombier 	} else if(cistrncmp(token, "q?", 2) == 0){
9919a747e4fSDavid du Colombier 		token += 2;
9929a747e4fSDavid du Colombier 		len = decquoted(decoded, token, e);
9939a747e4fSDavid du Colombier 		if(len > 0 && decoded[len-1] == '\n')
9949a747e4fSDavid du Colombier 			len--;
9959a747e4fSDavid du Colombier 		decoded[len] = 0;
9969a747e4fSDavid du Colombier 	} else
9979a747e4fSDavid du Colombier 		goto err;
9989a747e4fSDavid du Colombier 
9999a747e4fSDavid du Colombier 	s = nil;
10009a747e4fSDavid du Colombier 	switch(charsets[i].convert){
10019a747e4fSDavid du Colombier 	case 0:
10029a747e4fSDavid du Colombier 		s = s_copy(decoded);
10039a747e4fSDavid du Colombier 		break;
10049a747e4fSDavid du Colombier 	case 1:
10059a747e4fSDavid du Colombier 		s = s_new();
10069a747e4fSDavid du Colombier 		latin1toutf(utfbuf, decoded, decoded+len);
10079a747e4fSDavid du Colombier 		s_append(s, utfbuf);
10089a747e4fSDavid du Colombier 		break;
10099a747e4fSDavid du Colombier 	}
10109a747e4fSDavid du Colombier 
10119a747e4fSDavid du Colombier 	return s;
10129a747e4fSDavid du Colombier err:
10139a747e4fSDavid du Colombier 	return s_clone(t);
10149a747e4fSDavid du Colombier }
10159a747e4fSDavid du Colombier 
10169a747e4fSDavid du Colombier /*
10179a747e4fSDavid du Colombier  *  decode quoted
10189a747e4fSDavid du Colombier  */
10199a747e4fSDavid du Colombier enum
10209a747e4fSDavid du Colombier {
10219a747e4fSDavid du Colombier 	Self=	1,
10229a747e4fSDavid du Colombier 	Hex=	2,
10239a747e4fSDavid du Colombier };
10249a747e4fSDavid du Colombier uchar	tableqp[256];
10259a747e4fSDavid du Colombier 
10269a747e4fSDavid du Colombier static void
initquoted(void)10279a747e4fSDavid du Colombier initquoted(void)
10289a747e4fSDavid du Colombier {
10299a747e4fSDavid du Colombier 	int c;
10309a747e4fSDavid du Colombier 
10319a747e4fSDavid du Colombier 	memset(tableqp, 0, 256);
10329a747e4fSDavid du Colombier 	for(c = ' '; c <= '<'; c++)
10339a747e4fSDavid du Colombier 		tableqp[c] = Self;
10349a747e4fSDavid du Colombier 	for(c = '>'; c <= '~'; c++)
10359a747e4fSDavid du Colombier 		tableqp[c] = Self;
10369a747e4fSDavid du Colombier 	tableqp['\t'] = Self;
10379a747e4fSDavid du Colombier 	tableqp['='] = Hex;
10389a747e4fSDavid du Colombier }
10399a747e4fSDavid du Colombier 
10409a747e4fSDavid du Colombier static int
hex2int(int x)10419a747e4fSDavid du Colombier hex2int(int x)
10429a747e4fSDavid du Colombier {
10439a747e4fSDavid du Colombier 	if(x >= '0' && x <= '9')
10449a747e4fSDavid du Colombier 		return x - '0';
10459a747e4fSDavid du Colombier 	if(x >= 'A' && x <= 'F')
10469a747e4fSDavid du Colombier 		return (x - 'A') + 10;
10479a747e4fSDavid du Colombier 	if(x >= 'a' && x <= 'f')
10489a747e4fSDavid du Colombier 		return (x - 'a') + 10;
10499a747e4fSDavid du Colombier 	return 0;
10509a747e4fSDavid du Colombier }
10519a747e4fSDavid du Colombier 
10529a747e4fSDavid du Colombier static char*
decquotedline(char * out,char * in,char * e)10539a747e4fSDavid du Colombier decquotedline(char *out, char *in, char *e)
10549a747e4fSDavid du Colombier {
10559a747e4fSDavid du Colombier 	int c, soft;
10569a747e4fSDavid du Colombier 
10579a747e4fSDavid du Colombier 	/* dump trailing white space */
10589a747e4fSDavid du Colombier 	while(e >= in && (*e == ' ' || *e == '\t' || *e == '\r' || *e == '\n'))
10599a747e4fSDavid du Colombier 		e--;
10609a747e4fSDavid du Colombier 
10619a747e4fSDavid du Colombier 	/* trailing '=' means no newline */
10629a747e4fSDavid du Colombier 	if(*e == '='){
10639a747e4fSDavid du Colombier 		soft = 1;
10649a747e4fSDavid du Colombier 		e--;
10659a747e4fSDavid du Colombier 	} else
10669a747e4fSDavid du Colombier 		soft = 0;
10679a747e4fSDavid du Colombier 
10689a747e4fSDavid du Colombier 	while(in <= e){
10699a747e4fSDavid du Colombier 		c = (*in++) & 0xff;
10709a747e4fSDavid du Colombier 		switch(tableqp[c]){
10719a747e4fSDavid du Colombier 		case Self:
10729a747e4fSDavid du Colombier 			*out++ = c;
10739a747e4fSDavid du Colombier 			break;
10749a747e4fSDavid du Colombier 		case Hex:
10759a747e4fSDavid du Colombier 			c = hex2int(*in++)<<4;
10769a747e4fSDavid du Colombier 			c |= hex2int(*in++);
10779a747e4fSDavid du Colombier 			*out++ = c;
10789a747e4fSDavid du Colombier 			break;
10799a747e4fSDavid du Colombier 		}
10809a747e4fSDavid du Colombier 	}
10819a747e4fSDavid du Colombier 	if(!soft)
10829a747e4fSDavid du Colombier 		*out++ = '\n';
10839a747e4fSDavid du Colombier 	*out = 0;
10849a747e4fSDavid du Colombier 
10859a747e4fSDavid du Colombier 	return out;
10869a747e4fSDavid du Colombier }
10879a747e4fSDavid du Colombier 
10889a747e4fSDavid du Colombier static int
decquoted(char * out,char * in,char * e)10899a747e4fSDavid du Colombier decquoted(char *out, char *in, char *e)
10909a747e4fSDavid du Colombier {
10919a747e4fSDavid du Colombier 	char *p, *nl;
10929a747e4fSDavid du Colombier 
10939a747e4fSDavid du Colombier 	if(tableqp[' '] == 0)
10949a747e4fSDavid du Colombier 		initquoted();
10959a747e4fSDavid du Colombier 
10969a747e4fSDavid du Colombier 	p = out;
10979a747e4fSDavid du Colombier 	while((nl = strchr(in, '\n')) != nil && nl < e){
10989a747e4fSDavid du Colombier 		p = decquotedline(p, in, nl);
10999a747e4fSDavid du Colombier 		in = nl + 1;
11009a747e4fSDavid du Colombier 	}
11019a747e4fSDavid du Colombier 	if(in < e)
11029a747e4fSDavid du Colombier 		p = decquotedline(p, in, e-1);
11039a747e4fSDavid du Colombier 
1104850dd0caSDavid du Colombier 	/* make sure we end with a new line */
11059a747e4fSDavid du Colombier 	if(*(p-1) != '\n'){
11069a747e4fSDavid du Colombier 		*p++ = '\n';
11079a747e4fSDavid du Colombier 		*p = 0;
11089a747e4fSDavid du Colombier 	}
11099a747e4fSDavid du Colombier 
11109a747e4fSDavid du Colombier 	return p - out;
11119a747e4fSDavid du Colombier }
11129a747e4fSDavid du Colombier 
11139a747e4fSDavid du Colombier /* translate latin1 directly since it fits neatly in utf */
11149a747e4fSDavid du Colombier static int
latin1toutf(char * out,char * in,char * e)11159a747e4fSDavid du Colombier latin1toutf(char *out, char *in, char *e)
11169a747e4fSDavid du Colombier {
11179a747e4fSDavid du Colombier 	Rune r;
11189a747e4fSDavid du Colombier 	char *p;
11199a747e4fSDavid du Colombier 
11209a747e4fSDavid du Colombier 	p = out;
11219a747e4fSDavid du Colombier 	for(; in < e; in++){
11229a747e4fSDavid du Colombier 		r = (*in) & 0xff;
11239a747e4fSDavid du Colombier 		p += runetochar(p, &r);
11249a747e4fSDavid du Colombier 	}
11259a747e4fSDavid du Colombier 	*p = 0;
11269a747e4fSDavid du Colombier 	return p - out;
11279a747e4fSDavid du Colombier }
1128