2 5cdb1798 2005-10-29 devnull * this is a filter that changes mime types and names of
3 5cdb1798 2005-10-29 devnull * suspect executable attachments.
5 5cdb1798 2005-10-29 devnull #include "common.h"
6 5cdb1798 2005-10-29 devnull #include <ctype.h>
9 20bd3ca2 2006-04-09 devnull Accept = 0xA,
10 20bd3ca2 2006-04-09 devnull Discard = 0xD,
13 5cdb1798 2005-10-29 devnull Biobuf in;
14 5cdb1798 2005-10-29 devnull Biobuf out;
16 5cdb1798 2005-10-29 devnull typedef struct Mtype Mtype;
17 5cdb1798 2005-10-29 devnull typedef struct Hdef Hdef;
18 5cdb1798 2005-10-29 devnull typedef struct Hline Hline;
19 5cdb1798 2005-10-29 devnull typedef struct Part Part;
21 5cdb1798 2005-10-29 devnull static int badfile(char *name);
22 5cdb1798 2005-10-29 devnull static int badtype(char *type);
23 5cdb1798 2005-10-29 devnull static void ctype(Part*, Hdef*, char*);
24 5cdb1798 2005-10-29 devnull static void cencoding(Part*, Hdef*, char*);
25 5cdb1798 2005-10-29 devnull static void cdisposition(Part*, Hdef*, char*);
26 5cdb1798 2005-10-29 devnull static int decquoted(char *out, char *in, char *e);
27 5cdb1798 2005-10-29 devnull static char* getstring(char *p, String *s, int dolower);
28 5cdb1798 2005-10-29 devnull static void init_hdefs(void);
29 5cdb1798 2005-10-29 devnull static int isattribute(char **pp, char *attr);
30 5cdb1798 2005-10-29 devnull static int latin1toutf(char *out, char *in, char *e);
31 5cdb1798 2005-10-29 devnull static String* mkboundary(void);
32 5cdb1798 2005-10-29 devnull static Part* part(Part *pp);
33 5cdb1798 2005-10-29 devnull static Part* passbody(Part *p, int dobound);
34 5cdb1798 2005-10-29 devnull static void passnotheader(void);
35 5cdb1798 2005-10-29 devnull static void passunixheader(void);
36 5cdb1798 2005-10-29 devnull static Part* problemchild(Part *p);
37 5cdb1798 2005-10-29 devnull static void readheader(Part *p);
38 5cdb1798 2005-10-29 devnull static Hline* readhl(void);
39 5cdb1798 2005-10-29 devnull static void readmtypes(void);
40 5cdb1798 2005-10-29 devnull static int save(Part *p, char *file);
41 5cdb1798 2005-10-29 devnull static void setfilename(Part *p, char *name);
42 5cdb1798 2005-10-29 devnull static char* skiptosemi(char *p);
43 5cdb1798 2005-10-29 devnull static char* skipwhite(char *p);
44 5cdb1798 2005-10-29 devnull static String* tokenconvert(String *t);
45 5cdb1798 2005-10-29 devnull static void writeheader(Part *p, int);
49 cbeb0b26 2006-04-01 devnull /* encodings */
50 5cdb1798 2005-10-29 devnull Enone= 0,
54 cbeb0b26 2006-04-01 devnull /* disposition possibilities */
55 5cdb1798 2005-10-29 devnull Dnone= 0,
60 cbeb0b26 2006-04-01 devnull PAD64= '='
64 5cdb1798 2005-10-29 devnull * a message part; either the whole message or a subpart
66 5cdb1798 2005-10-29 devnull struct Part
68 5cdb1798 2005-10-29 devnull Part *pp; /* parent part */
69 5cdb1798 2005-10-29 devnull Hline *hl; /* linked list of header lines */
70 5cdb1798 2005-10-29 devnull int disposition;
71 5cdb1798 2005-10-29 devnull int encoding;
72 5cdb1798 2005-10-29 devnull int badfile;
73 5cdb1798 2005-10-29 devnull int badtype;
74 5cdb1798 2005-10-29 devnull String *boundary; /* boundary for multiparts */
75 5cdb1798 2005-10-29 devnull int blen;
76 5cdb1798 2005-10-29 devnull String *charset; /* character set */
77 5cdb1798 2005-10-29 devnull String *type; /* content type */
78 5cdb1798 2005-10-29 devnull String *filename; /* file name */
79 5cdb1798 2005-10-29 devnull Biobuf *tmpbuf; /* diversion input buffer */
83 5cdb1798 2005-10-29 devnull * a (multi)line header
85 5cdb1798 2005-10-29 devnull struct Hline
87 5cdb1798 2005-10-29 devnull Hline *next;
88 5cdb1798 2005-10-29 devnull String *s;
92 5cdb1798 2005-10-29 devnull * header definitions for parsing
94 5cdb1798 2005-10-29 devnull struct Hdef
96 5cdb1798 2005-10-29 devnull char *type;
97 5cdb1798 2005-10-29 devnull void (*f)(Part*, Hdef*, char*);
101 5cdb1798 2005-10-29 devnull Hdef hdefs[] =
103 5cdb1798 2005-10-29 devnull { "content-type:", ctype, },
104 5cdb1798 2005-10-29 devnull { "content-transfer-encoding:", cencoding, },
105 5cdb1798 2005-10-29 devnull { "content-disposition:", cdisposition, },
110 5cdb1798 2005-10-29 devnull * acceptable content types and their extensions
112 5cdb1798 2005-10-29 devnull struct Mtype {
113 5cdb1798 2005-10-29 devnull Mtype *next;
114 5cdb1798 2005-10-29 devnull char *ext; /* extension */
115 5cdb1798 2005-10-29 devnull char *gtype; /* generic content type */
116 5cdb1798 2005-10-29 devnull char *stype; /* specific content type */
117 5cdb1798 2005-10-29 devnull char class;
119 5cdb1798 2005-10-29 devnull Mtype *mtypes;
121 5cdb1798 2005-10-29 devnull int justreject;
122 5cdb1798 2005-10-29 devnull char *savefile;
125 b5f65921 2006-02-11 devnull usage(void)
127 b5f65921 2006-02-11 devnull fprint(2, "usage: upas/vf [-r] [-s savefile]\n");
128 b5f65921 2006-02-11 devnull exits("usage");
132 5cdb1798 2005-10-29 devnull main(int argc, char **argv)
134 5cdb1798 2005-10-29 devnull ARGBEGIN{
135 5cdb1798 2005-10-29 devnull case 'r':
136 5cdb1798 2005-10-29 devnull justreject = 1;
138 5cdb1798 2005-10-29 devnull case 's':
139 b5f65921 2006-02-11 devnull savefile = EARGF(usage());
141 b5f65921 2006-02-11 devnull default:
142 b5f65921 2006-02-11 devnull usage();
143 5cdb1798 2005-10-29 devnull }ARGEND;
145 b5f65921 2006-02-11 devnull if(argc)
146 b5f65921 2006-02-11 devnull usage();
148 5cdb1798 2005-10-29 devnull Binit(&in, 0, OREAD);
149 5cdb1798 2005-10-29 devnull Binit(&out, 1, OWRITE);
151 5cdb1798 2005-10-29 devnull init_hdefs();
152 5cdb1798 2005-10-29 devnull readmtypes();
154 5cdb1798 2005-10-29 devnull /* pass through our standard 'From ' line */
155 5cdb1798 2005-10-29 devnull passunixheader();
157 5cdb1798 2005-10-29 devnull /* parse with the top level part */
158 5cdb1798 2005-10-29 devnull part(nil);
160 5cdb1798 2005-10-29 devnull exits(0);
164 5cdb1798 2005-10-29 devnull refuse(void)
166 5cdb1798 2005-10-29 devnull postnote(PNGROUP, getpid(), "mail refused: we don't accept executable attachments");
167 5cdb1798 2005-10-29 devnull exits("mail refused: we don't accept executable attachments");
172 5cdb1798 2005-10-29 devnull * parse a part; returns the ancestor whose boundary terminated
173 5cdb1798 2005-10-29 devnull * this part or nil on EOF.
175 5cdb1798 2005-10-29 devnull static Part*
176 5cdb1798 2005-10-29 devnull part(Part *pp)
178 5cdb1798 2005-10-29 devnull Part *p, *np;
180 5cdb1798 2005-10-29 devnull p = mallocz(sizeof *p, 1);
181 5cdb1798 2005-10-29 devnull p->pp = pp;
182 5cdb1798 2005-10-29 devnull readheader(p);
184 5cdb1798 2005-10-29 devnull if(p->boundary != nil){
185 5cdb1798 2005-10-29 devnull /* the format of a multipart part is always:
186 5cdb1798 2005-10-29 devnull * header
187 5cdb1798 2005-10-29 devnull * null or ignored body
188 5cdb1798 2005-10-29 devnull * boundary
189 5cdb1798 2005-10-29 devnull * header
191 5cdb1798 2005-10-29 devnull * boundary
194 5cdb1798 2005-10-29 devnull writeheader(p, 1);
195 5cdb1798 2005-10-29 devnull np = passbody(p, 1);
196 5cdb1798 2005-10-29 devnull if(np != p)
197 5cdb1798 2005-10-29 devnull return np;
198 5cdb1798 2005-10-29 devnull for(;;){
199 5cdb1798 2005-10-29 devnull np = part(p);
200 5cdb1798 2005-10-29 devnull if(np != p)
201 5cdb1798 2005-10-29 devnull return np;
203 5cdb1798 2005-10-29 devnull } else {
204 5cdb1798 2005-10-29 devnull /* no boundary */
205 5cdb1798 2005-10-29 devnull /* may still be multipart if this is a forwarded message */
206 5cdb1798 2005-10-29 devnull if(p->type && cistrcmp(s_to_c(p->type), "message/rfc822") == 0){
207 5cdb1798 2005-10-29 devnull /* the format of forwarded message is:
208 5cdb1798 2005-10-29 devnull * header
209 5cdb1798 2005-10-29 devnull * header
212 5cdb1798 2005-10-29 devnull writeheader(p, 1);
213 5cdb1798 2005-10-29 devnull passnotheader();
214 5cdb1798 2005-10-29 devnull return part(p);
215 5cdb1798 2005-10-29 devnull } else {
217 5cdb1798 2005-10-29 devnull * This is the meat. This may be an executable.
218 5cdb1798 2005-10-29 devnull * if so, wrap it and change its type
220 5cdb1798 2005-10-29 devnull if(p->badtype || p->badfile){
221 5cdb1798 2005-10-29 devnull if(p->badfile == 2){
222 5cdb1798 2005-10-29 devnull if(savefile != nil)
223 5cdb1798 2005-10-29 devnull save(p, savefile);
224 5cdb1798 2005-10-29 devnull syslog(0, "vf", "vf rejected %s %s", p->type?s_to_c(p->type):"?",
225 5cdb1798 2005-10-29 devnull p->filename?s_to_c(p->filename):"?");
226 5cdb1798 2005-10-29 devnull fprint(2, "The mail contained an executable attachment.\n");
227 5cdb1798 2005-10-29 devnull fprint(2, "We refuse all mail containing such.\n");
228 5cdb1798 2005-10-29 devnull refuse();
230 5cdb1798 2005-10-29 devnull np = problemchild(p);
231 5cdb1798 2005-10-29 devnull if(np != p)
232 5cdb1798 2005-10-29 devnull return np;
233 5cdb1798 2005-10-29 devnull /* if problemchild returns p, it turns out p is okay: fall thru */
235 5cdb1798 2005-10-29 devnull writeheader(p, 1);
236 5cdb1798 2005-10-29 devnull return passbody(p, 1);
242 5cdb1798 2005-10-29 devnull * read and parse a complete header
244 5cdb1798 2005-10-29 devnull static void
245 5cdb1798 2005-10-29 devnull readheader(Part *p)
247 5cdb1798 2005-10-29 devnull Hline *hl, **l;
248 5cdb1798 2005-10-29 devnull Hdef *hd;
250 5cdb1798 2005-10-29 devnull l = &p->hl;
251 5cdb1798 2005-10-29 devnull for(;;){
252 5cdb1798 2005-10-29 devnull hl = readhl();
253 5cdb1798 2005-10-29 devnull if(hl == nil)
255 5cdb1798 2005-10-29 devnull *l = hl;
256 5cdb1798 2005-10-29 devnull l = &hl->next;
258 5cdb1798 2005-10-29 devnull for(hd = hdefs; hd->type != nil; hd++){
259 5cdb1798 2005-10-29 devnull if(cistrncmp(s_to_c(hl->s), hd->type, hd->len) == 0){
260 5cdb1798 2005-10-29 devnull (*hd->f)(p, hd, s_to_c(hl->s));
268 5cdb1798 2005-10-29 devnull * read a possibly multiline header line
270 5cdb1798 2005-10-29 devnull static Hline*
271 5cdb1798 2005-10-29 devnull readhl(void)
273 5cdb1798 2005-10-29 devnull Hline *hl;
274 5cdb1798 2005-10-29 devnull String *s;
275 5cdb1798 2005-10-29 devnull char *p;
278 5cdb1798 2005-10-29 devnull p = Brdline(&in, '\n');
279 5cdb1798 2005-10-29 devnull if(p == nil)
280 5cdb1798 2005-10-29 devnull return nil;
281 5cdb1798 2005-10-29 devnull n = Blinelen(&in);
282 5cdb1798 2005-10-29 devnull if(memchr(p, ':', n) == nil){
283 5cdb1798 2005-10-29 devnull Bseek(&in, -n, 1);
284 5cdb1798 2005-10-29 devnull return nil;
286 5cdb1798 2005-10-29 devnull s = s_nappend(s_new(), p, n);
287 5cdb1798 2005-10-29 devnull for(;;){
288 5cdb1798 2005-10-29 devnull p = Brdline(&in, '\n');
289 5cdb1798 2005-10-29 devnull if(p == nil)
291 5cdb1798 2005-10-29 devnull n = Blinelen(&in);
292 5cdb1798 2005-10-29 devnull if(*p != ' ' && *p != '\t'){
293 5cdb1798 2005-10-29 devnull Bseek(&in, -n, 1);
296 5cdb1798 2005-10-29 devnull s = s_nappend(s, p, n);
298 5cdb1798 2005-10-29 devnull hl = malloc(sizeof *hl);
299 5cdb1798 2005-10-29 devnull hl->s = s;
300 5cdb1798 2005-10-29 devnull hl->next = nil;
301 5cdb1798 2005-10-29 devnull return hl;
305 5cdb1798 2005-10-29 devnull * write out a complete header
307 5cdb1798 2005-10-29 devnull static void
308 5cdb1798 2005-10-29 devnull writeheader(Part *p, int xfree)
310 5cdb1798 2005-10-29 devnull Hline *hl, *next;
312 5cdb1798 2005-10-29 devnull for(hl = p->hl; hl != nil; hl = next){
313 5cdb1798 2005-10-29 devnull Bprint(&out, "%s", s_to_c(hl->s));
314 5cdb1798 2005-10-29 devnull if(xfree)
315 5cdb1798 2005-10-29 devnull s_free(hl->s);
316 5cdb1798 2005-10-29 devnull next = hl->next;
317 5cdb1798 2005-10-29 devnull if(xfree)
318 5cdb1798 2005-10-29 devnull free(hl);
320 5cdb1798 2005-10-29 devnull if(xfree)
321 5cdb1798 2005-10-29 devnull p->hl = nil;
325 5cdb1798 2005-10-29 devnull * pass a body through. return if we hit one of our ancestors'
326 5cdb1798 2005-10-29 devnull * boundaries or EOF. if we hit a boundary, return a pointer to
327 5cdb1798 2005-10-29 devnull * that ancestor. if we hit EOF, return nil.
329 5cdb1798 2005-10-29 devnull static Part*
330 5cdb1798 2005-10-29 devnull passbody(Part *p, int dobound)
332 5cdb1798 2005-10-29 devnull Part *pp;
333 5cdb1798 2005-10-29 devnull Biobuf *b;
334 5cdb1798 2005-10-29 devnull char *cp;
336 5cdb1798 2005-10-29 devnull for(;;){
337 5cdb1798 2005-10-29 devnull if(p->tmpbuf){
338 5cdb1798 2005-10-29 devnull b = p->tmpbuf;
339 5cdb1798 2005-10-29 devnull cp = Brdline(b, '\n');
340 5cdb1798 2005-10-29 devnull if(cp == nil){
341 5cdb1798 2005-10-29 devnull Bterm(b);
342 5cdb1798 2005-10-29 devnull p->tmpbuf = nil;
343 5cdb1798 2005-10-29 devnull goto Stdin;
347 5cdb1798 2005-10-29 devnull b = ∈
348 5cdb1798 2005-10-29 devnull cp = Brdline(b, '\n');
350 5cdb1798 2005-10-29 devnull if(cp == nil)
351 5cdb1798 2005-10-29 devnull return nil;
352 5cdb1798 2005-10-29 devnull for(pp = p; pp != nil; pp = pp->pp)
353 5cdb1798 2005-10-29 devnull if(pp->boundary != nil
354 5cdb1798 2005-10-29 devnull && strncmp(cp, s_to_c(pp->boundary), pp->blen) == 0){
355 5cdb1798 2005-10-29 devnull if(dobound)
356 5cdb1798 2005-10-29 devnull Bwrite(&out, cp, Blinelen(b));
358 5cdb1798 2005-10-29 devnull Bseek(b, -Blinelen(b), 1);
359 5cdb1798 2005-10-29 devnull return pp;
361 5cdb1798 2005-10-29 devnull Bwrite(&out, cp, Blinelen(b));
363 5cdb1798 2005-10-29 devnull return nil;
367 5cdb1798 2005-10-29 devnull * save the message somewhere
369 5cdb1798 2005-10-29 devnull static vlong bodyoff; /* clumsy hack */
370 5cdb1798 2005-10-29 devnull static int
371 5cdb1798 2005-10-29 devnull save(Part *p, char *file)
374 5cdb1798 2005-10-29 devnull char *cp;
376 5cdb1798 2005-10-29 devnull Bterm(&out);
377 5cdb1798 2005-10-29 devnull memset(&out, 0, sizeof(out));
379 5cdb1798 2005-10-29 devnull fd = open(file, OWRITE);
380 5cdb1798 2005-10-29 devnull if(fd < 0)
381 5cdb1798 2005-10-29 devnull return -1;
382 5cdb1798 2005-10-29 devnull seek(fd, 0, 2);
383 5cdb1798 2005-10-29 devnull Binit(&out, fd, OWRITE);
384 5cdb1798 2005-10-29 devnull cp = ctime(time(0));
385 5cdb1798 2005-10-29 devnull cp[28] = 0;
386 5cdb1798 2005-10-29 devnull Bprint(&out, "From virusfilter %s\n", cp);
387 5cdb1798 2005-10-29 devnull writeheader(p, 0);
388 5cdb1798 2005-10-29 devnull bodyoff = Boffset(&out);
389 5cdb1798 2005-10-29 devnull passbody(p, 1);
390 5cdb1798 2005-10-29 devnull Bprint(&out, "\n");
391 5cdb1798 2005-10-29 devnull Bterm(&out);
392 5cdb1798 2005-10-29 devnull close(fd);
394 5cdb1798 2005-10-29 devnull memset(&out, 0, sizeof out);
395 5cdb1798 2005-10-29 devnull Binit(&out, 1, OWRITE);
396 5cdb1798 2005-10-29 devnull return 0;
400 5cdb1798 2005-10-29 devnull * write to a file but save the fd for passbody.
402 5cdb1798 2005-10-29 devnull static char*
403 5cdb1798 2005-10-29 devnull savetmp(Part *p)
405 5cdb1798 2005-10-29 devnull char buf[40], *name;
408 20bd3ca2 2006-04-09 devnull strcpy(buf, "/var/tmp/vf.XXXXXXXXXXX");
409 20bd3ca2 2006-04-09 devnull if((fd = mkstemp(buf)) < 0){
410 5cdb1798 2005-10-29 devnull fprint(2, "error creating temporary file: %r\n");
411 5cdb1798 2005-10-29 devnull refuse();
413 20bd3ca2 2006-04-09 devnull name = buf;
414 5cdb1798 2005-10-29 devnull close(fd);
415 5cdb1798 2005-10-29 devnull if(save(p, name) < 0){
416 5cdb1798 2005-10-29 devnull fprint(2, "error saving temporary file: %r\n");
417 5cdb1798 2005-10-29 devnull refuse();
419 5cdb1798 2005-10-29 devnull if(p->tmpbuf){
420 5cdb1798 2005-10-29 devnull fprint(2, "error in savetmp: already have tmp file!\n");
421 5cdb1798 2005-10-29 devnull refuse();
423 5cdb1798 2005-10-29 devnull p->tmpbuf = Bopen(name, OREAD|ORCLOSE);
424 5cdb1798 2005-10-29 devnull if(p->tmpbuf == nil){
425 5cdb1798 2005-10-29 devnull fprint(2, "error reading tempoary file: %r\n");
426 5cdb1798 2005-10-29 devnull refuse();
428 5cdb1798 2005-10-29 devnull Bseek(p->tmpbuf, bodyoff, 0);
429 5cdb1798 2005-10-29 devnull return strdup(name);
433 b5f65921 2006-02-11 devnull * Run the external checker to do content-based checks.
435 5cdb1798 2005-10-29 devnull static int
436 5cdb1798 2005-10-29 devnull runchecker(Part *p)
438 5cdb1798 2005-10-29 devnull int pid;
439 5cdb1798 2005-10-29 devnull char *name;
440 5cdb1798 2005-10-29 devnull Waitmsg *w;
441 20bd3ca2 2006-04-09 devnull static char *val;
443 20bd3ca2 2006-04-09 devnull if(val == nil)
444 20bd3ca2 2006-04-09 devnull val = unsharp("#9/mail/lib/validateattachment");
445 20bd3ca2 2006-04-09 devnull if(val == nil || access(val, AEXEC) < 0)
446 20bd3ca2 2006-04-09 devnull return 0;
448 5cdb1798 2005-10-29 devnull name = savetmp(p);
449 5cdb1798 2005-10-29 devnull fprint(2, "run checker %s\n", name);
450 5cdb1798 2005-10-29 devnull switch(pid = fork()){
451 5cdb1798 2005-10-29 devnull case -1:
452 5cdb1798 2005-10-29 devnull sysfatal("fork: %r");
454 5cdb1798 2005-10-29 devnull dup(2, 1);
455 20bd3ca2 2006-04-09 devnull execl(val, "validateattachment", name, nil);
456 5cdb1798 2005-10-29 devnull _exits("exec failed");
460 5cdb1798 2005-10-29 devnull * Okay to return on error - will let mail through but wrapped.
462 5cdb1798 2005-10-29 devnull w = wait();
463 5cdb1798 2005-10-29 devnull if(w == nil){
464 5cdb1798 2005-10-29 devnull syslog(0, "mail", "vf wait failed: %r");
465 5cdb1798 2005-10-29 devnull return 0;
467 5cdb1798 2005-10-29 devnull if(w->pid != pid){
468 5cdb1798 2005-10-29 devnull syslog(0, "mail", "vf wrong pid %d != %d", w->pid, pid);
469 5cdb1798 2005-10-29 devnull return 0;
471 5cdb1798 2005-10-29 devnull if(p->filename)
472 5cdb1798 2005-10-29 devnull name = s_to_c(p->filename);
473 20bd3ca2 2006-04-09 devnull if(atoi(w->msg) == Discard){
474 5cdb1798 2005-10-29 devnull syslog(0, "mail", "vf validateattachment rejected %s", name);
475 5cdb1798 2005-10-29 devnull refuse();
477 20bd3ca2 2006-04-09 devnull if(atoi(w->msg) == Accept){
478 5cdb1798 2005-10-29 devnull syslog(0, "mail", "vf validateattachment accepted %s", name);
479 5cdb1798 2005-10-29 devnull return 1;
481 5cdb1798 2005-10-29 devnull free(w);
482 5cdb1798 2005-10-29 devnull return 0;
486 5cdb1798 2005-10-29 devnull * emit a multipart Part that explains the problem
488 5cdb1798 2005-10-29 devnull static Part*
489 5cdb1798 2005-10-29 devnull problemchild(Part *p)
491 5cdb1798 2005-10-29 devnull Part *np;
492 5cdb1798 2005-10-29 devnull Hline *hl;
493 5cdb1798 2005-10-29 devnull String *boundary;
494 5cdb1798 2005-10-29 devnull char *cp;
497 5cdb1798 2005-10-29 devnull * We don't know whether the attachment is okay.
498 5cdb1798 2005-10-29 devnull * If there's an external checker, let it have a crack at it.
500 5cdb1798 2005-10-29 devnull if(runchecker(p) > 0)
501 5cdb1798 2005-10-29 devnull return p;
503 b5f65921 2006-02-11 devnull if(justreject)
504 b5f65921 2006-02-11 devnull return p;
506 5cdb1798 2005-10-29 devnull syslog(0, "mail", "vf wrapped %s %s", p->type?s_to_c(p->type):"?",
507 5cdb1798 2005-10-29 devnull p->filename?s_to_c(p->filename):"?");
509 5cdb1798 2005-10-29 devnull boundary = mkboundary();
510 5cdb1798 2005-10-29 devnull /* print out non-mime headers */
511 5cdb1798 2005-10-29 devnull for(hl = p->hl; hl != nil; hl = hl->next)
512 5cdb1798 2005-10-29 devnull if(cistrncmp(s_to_c(hl->s), "content-", 8) != 0)
513 5cdb1798 2005-10-29 devnull Bprint(&out, "%s", s_to_c(hl->s));
515 5cdb1798 2005-10-29 devnull /* add in our own multipart headers and message */
516 5cdb1798 2005-10-29 devnull Bprint(&out, "Content-Type: multipart/mixed;\n");
517 5cdb1798 2005-10-29 devnull Bprint(&out, "\tboundary=\"%s\"\n", s_to_c(boundary));
518 5cdb1798 2005-10-29 devnull Bprint(&out, "Content-Disposition: inline\n");
519 5cdb1798 2005-10-29 devnull Bprint(&out, "\n");
520 5cdb1798 2005-10-29 devnull Bprint(&out, "This is a multi-part message in MIME format.\n");
521 5cdb1798 2005-10-29 devnull Bprint(&out, "--%s\n", s_to_c(boundary));
522 5cdb1798 2005-10-29 devnull Bprint(&out, "Content-Disposition: inline\n");
523 5cdb1798 2005-10-29 devnull Bprint(&out, "Content-Type: text/plain; charset=\"US-ASCII\"\n");
524 5cdb1798 2005-10-29 devnull Bprint(&out, "Content-Transfer-Encoding: 7bit\n");
525 5cdb1798 2005-10-29 devnull Bprint(&out, "\n");
526 5cdb1798 2005-10-29 devnull Bprint(&out, "from postmaster@%s:\n", sysname());
527 5cdb1798 2005-10-29 devnull Bprint(&out, "The following attachment had content that we can't\n");
528 5cdb1798 2005-10-29 devnull Bprint(&out, "prove to be harmless. To avoid possible automatic\n");
529 5cdb1798 2005-10-29 devnull Bprint(&out, "execution, we changed the content headers.\n");
530 5cdb1798 2005-10-29 devnull Bprint(&out, "The original header was:\n\n");
532 5cdb1798 2005-10-29 devnull /* print out original header lines */
533 5cdb1798 2005-10-29 devnull for(hl = p->hl; hl != nil; hl = hl->next)
534 5cdb1798 2005-10-29 devnull if(cistrncmp(s_to_c(hl->s), "content-", 8) == 0)
535 5cdb1798 2005-10-29 devnull Bprint(&out, "\t%s", s_to_c(hl->s));
536 5cdb1798 2005-10-29 devnull Bprint(&out, "--%s\n", s_to_c(boundary));
538 5cdb1798 2005-10-29 devnull /* change file name */
539 5cdb1798 2005-10-29 devnull if(p->filename)
540 5cdb1798 2005-10-29 devnull s_append(p->filename, ".suspect");
542 5cdb1798 2005-10-29 devnull p->filename = s_copy("file.suspect");
544 5cdb1798 2005-10-29 devnull /* print out new header */
545 5cdb1798 2005-10-29 devnull Bprint(&out, "Content-Type: application/octet-stream\n");
546 5cdb1798 2005-10-29 devnull Bprint(&out, "Content-Disposition: attachment; filename=\"%s\"\n", s_to_c(p->filename));
547 5cdb1798 2005-10-29 devnull switch(p->encoding){
548 5cdb1798 2005-10-29 devnull case Enone:
550 5cdb1798 2005-10-29 devnull case Ebase64:
551 5cdb1798 2005-10-29 devnull Bprint(&out, "Content-Transfer-Encoding: base64\n");
553 5cdb1798 2005-10-29 devnull case Equoted:
554 5cdb1798 2005-10-29 devnull Bprint(&out, "Content-Transfer-Encoding: quoted-printable\n");
558 5cdb1798 2005-10-29 devnull /* pass the body */
559 5cdb1798 2005-10-29 devnull np = passbody(p, 0);
561 5cdb1798 2005-10-29 devnull /* add the new boundary and the original terminator */
562 5cdb1798 2005-10-29 devnull Bprint(&out, "--%s--\n", s_to_c(boundary));
563 5cdb1798 2005-10-29 devnull if(np && np->boundary){
564 5cdb1798 2005-10-29 devnull cp = Brdline(&in, '\n');
565 5cdb1798 2005-10-29 devnull Bwrite(&out, cp, Blinelen(&in));
568 5cdb1798 2005-10-29 devnull return np;
571 5cdb1798 2005-10-29 devnull static int
572 5cdb1798 2005-10-29 devnull isattribute(char **pp, char *attr)
574 5cdb1798 2005-10-29 devnull char *p;
577 5cdb1798 2005-10-29 devnull n = strlen(attr);
578 5cdb1798 2005-10-29 devnull p = *pp;
579 5cdb1798 2005-10-29 devnull if(cistrncmp(p, attr, n) != 0)
580 5cdb1798 2005-10-29 devnull return 0;
582 5cdb1798 2005-10-29 devnull while(*p == ' ')
584 5cdb1798 2005-10-29 devnull if(*p++ != '=')
585 5cdb1798 2005-10-29 devnull return 0;
586 5cdb1798 2005-10-29 devnull while(*p == ' ')
588 5cdb1798 2005-10-29 devnull *pp = p;
589 5cdb1798 2005-10-29 devnull return 1;
593 5cdb1798 2005-10-29 devnull * parse content type header
595 5cdb1798 2005-10-29 devnull static void
596 5cdb1798 2005-10-29 devnull ctype(Part *p, Hdef *h, char *cp)
598 5cdb1798 2005-10-29 devnull String *s;
600 5cdb1798 2005-10-29 devnull cp += h->len;
601 5cdb1798 2005-10-29 devnull cp = skipwhite(cp);
603 5cdb1798 2005-10-29 devnull p->type = s_new();
604 5cdb1798 2005-10-29 devnull cp = getstring(cp, p->type, 1);
605 5cdb1798 2005-10-29 devnull if(badtype(s_to_c(p->type)))
606 5cdb1798 2005-10-29 devnull p->badtype = 1;
608 5cdb1798 2005-10-29 devnull while(*cp){
609 5cdb1798 2005-10-29 devnull if(isattribute(&cp, "boundary")){
610 5cdb1798 2005-10-29 devnull s = s_new();
611 5cdb1798 2005-10-29 devnull cp = getstring(cp, s, 0);
612 5cdb1798 2005-10-29 devnull p->boundary = s_reset(p->boundary);
613 5cdb1798 2005-10-29 devnull s_append(p->boundary, "--");
614 5cdb1798 2005-10-29 devnull s_append(p->boundary, s_to_c(s));
615 5cdb1798 2005-10-29 devnull p->blen = s_len(p->boundary);
616 5cdb1798 2005-10-29 devnull s_free(s);
617 5cdb1798 2005-10-29 devnull } else if(cistrncmp(cp, "multipart", 9) == 0){
619 5cdb1798 2005-10-29 devnull * the first unbounded part of a multipart message,
620 5cdb1798 2005-10-29 devnull * the preamble, is not displayed or saved
622 5cdb1798 2005-10-29 devnull } else if(isattribute(&cp, "name")){
623 5cdb1798 2005-10-29 devnull setfilename(p, cp);
624 5cdb1798 2005-10-29 devnull } else if(isattribute(&cp, "charset")){
625 5cdb1798 2005-10-29 devnull if(p->charset == nil)
626 5cdb1798 2005-10-29 devnull p->charset = s_new();
627 5cdb1798 2005-10-29 devnull cp = getstring(cp, s_reset(p->charset), 0);
630 5cdb1798 2005-10-29 devnull cp = skiptosemi(cp);
635 5cdb1798 2005-10-29 devnull * parse content encoding header
637 5cdb1798 2005-10-29 devnull static void
638 5cdb1798 2005-10-29 devnull cencoding(Part *m, Hdef *h, char *p)
640 5cdb1798 2005-10-29 devnull p += h->len;
641 5cdb1798 2005-10-29 devnull p = skipwhite(p);
642 5cdb1798 2005-10-29 devnull if(cistrncmp(p, "base64", 6) == 0)
643 5cdb1798 2005-10-29 devnull m->encoding = Ebase64;
644 5cdb1798 2005-10-29 devnull else if(cistrncmp(p, "quoted-printable", 16) == 0)
645 5cdb1798 2005-10-29 devnull m->encoding = Equoted;
649 5cdb1798 2005-10-29 devnull * parse content disposition header
651 5cdb1798 2005-10-29 devnull static void
652 5cdb1798 2005-10-29 devnull cdisposition(Part *p, Hdef *h, char *cp)
654 5cdb1798 2005-10-29 devnull cp += h->len;
655 5cdb1798 2005-10-29 devnull cp = skipwhite(cp);
656 5cdb1798 2005-10-29 devnull while(*cp){
657 5cdb1798 2005-10-29 devnull if(cistrncmp(cp, "inline", 6) == 0){
658 5cdb1798 2005-10-29 devnull p->disposition = Dinline;
659 5cdb1798 2005-10-29 devnull } else if(cistrncmp(cp, "attachment", 10) == 0){
660 5cdb1798 2005-10-29 devnull p->disposition = Dfile;
661 5cdb1798 2005-10-29 devnull } else if(cistrncmp(cp, "filename=", 9) == 0){
662 5cdb1798 2005-10-29 devnull cp += 9;
663 5cdb1798 2005-10-29 devnull setfilename(p, cp);
665 5cdb1798 2005-10-29 devnull cp = skiptosemi(cp);
670 5cdb1798 2005-10-29 devnull static void
671 5cdb1798 2005-10-29 devnull setfilename(Part *p, char *name)
673 5cdb1798 2005-10-29 devnull if(p->filename == nil)
674 5cdb1798 2005-10-29 devnull p->filename = s_new();
675 5cdb1798 2005-10-29 devnull getstring(name, s_reset(p->filename), 0);
676 5cdb1798 2005-10-29 devnull p->filename = tokenconvert(p->filename);
677 5cdb1798 2005-10-29 devnull p->badfile = badfile(s_to_c(p->filename));
680 5cdb1798 2005-10-29 devnull static char*
681 5cdb1798 2005-10-29 devnull skipwhite(char *p)
683 5cdb1798 2005-10-29 devnull while(isspace(*p))
685 5cdb1798 2005-10-29 devnull return p;
688 5cdb1798 2005-10-29 devnull static char*
689 5cdb1798 2005-10-29 devnull skiptosemi(char *p)
691 5cdb1798 2005-10-29 devnull while(*p && *p != ';')
693 5cdb1798 2005-10-29 devnull while(*p == ';' || isspace(*p))
695 5cdb1798 2005-10-29 devnull return p;
699 5cdb1798 2005-10-29 devnull * parse a possibly "'d string from a header. A
700 5cdb1798 2005-10-29 devnull * ';' terminates the string.
702 5cdb1798 2005-10-29 devnull static char*
703 5cdb1798 2005-10-29 devnull getstring(char *p, String *s, int dolower)
705 5cdb1798 2005-10-29 devnull s = s_reset(s);
706 5cdb1798 2005-10-29 devnull p = skipwhite(p);
707 5cdb1798 2005-10-29 devnull if(*p == '"'){
709 5cdb1798 2005-10-29 devnull for(;*p && *p != '"'; p++)
710 5cdb1798 2005-10-29 devnull if(dolower)
711 5cdb1798 2005-10-29 devnull s_putc(s, tolower(*p));
713 5cdb1798 2005-10-29 devnull s_putc(s, *p);
714 5cdb1798 2005-10-29 devnull if(*p == '"')
716 5cdb1798 2005-10-29 devnull s_terminate(s);
718 5cdb1798 2005-10-29 devnull return p;
721 5cdb1798 2005-10-29 devnull for(; *p && !isspace(*p) && *p != ';'; p++)
722 5cdb1798 2005-10-29 devnull if(dolower)
723 5cdb1798 2005-10-29 devnull s_putc(s, tolower(*p));
725 5cdb1798 2005-10-29 devnull s_putc(s, *p);
726 5cdb1798 2005-10-29 devnull s_terminate(s);
728 5cdb1798 2005-10-29 devnull return p;
731 5cdb1798 2005-10-29 devnull static void
732 5cdb1798 2005-10-29 devnull init_hdefs(void)
734 5cdb1798 2005-10-29 devnull Hdef *hd;
735 5cdb1798 2005-10-29 devnull static int already;
737 5cdb1798 2005-10-29 devnull if(already)
739 5cdb1798 2005-10-29 devnull already = 1;
741 5cdb1798 2005-10-29 devnull for(hd = hdefs; hd->type != nil; hd++)
742 5cdb1798 2005-10-29 devnull hd->len = strlen(hd->type);
746 5cdb1798 2005-10-29 devnull * create a new boundary
748 5cdb1798 2005-10-29 devnull static String*
749 5cdb1798 2005-10-29 devnull mkboundary(void)
751 5cdb1798 2005-10-29 devnull char buf[32];
753 5cdb1798 2005-10-29 devnull static int already;
755 5cdb1798 2005-10-29 devnull if(already == 0){
756 5cdb1798 2005-10-29 devnull srand((time(0)<<16)|getpid());
757 5cdb1798 2005-10-29 devnull already = 1;
759 5cdb1798 2005-10-29 devnull strcpy(buf, "upas-");
760 5cdb1798 2005-10-29 devnull for(i = 5; i < sizeof(buf)-1; i++)
761 5cdb1798 2005-10-29 devnull buf[i] = 'a' + nrand(26);
762 5cdb1798 2005-10-29 devnull buf[i] = 0;
763 5cdb1798 2005-10-29 devnull return s_copy(buf);
767 5cdb1798 2005-10-29 devnull * skip blank lines till header
769 5cdb1798 2005-10-29 devnull static void
770 5cdb1798 2005-10-29 devnull passnotheader(void)
772 5cdb1798 2005-10-29 devnull char *cp;
773 5cdb1798 2005-10-29 devnull int i, n;
775 5cdb1798 2005-10-29 devnull while((cp = Brdline(&in, '\n')) != nil){
776 5cdb1798 2005-10-29 devnull n = Blinelen(&in);
777 5cdb1798 2005-10-29 devnull for(i = 0; i < n-1; i++)
778 5cdb1798 2005-10-29 devnull if(cp[i] != ' ' && cp[i] != '\t' && cp[i] != '\r'){
779 5cdb1798 2005-10-29 devnull Bseek(&in, -n, 1);
782 5cdb1798 2005-10-29 devnull Bwrite(&out, cp, n);
787 5cdb1798 2005-10-29 devnull * pass unix header lines
789 5cdb1798 2005-10-29 devnull static void
790 5cdb1798 2005-10-29 devnull passunixheader(void)
792 5cdb1798 2005-10-29 devnull char *p;
795 5cdb1798 2005-10-29 devnull while((p = Brdline(&in, '\n')) != nil){
796 5cdb1798 2005-10-29 devnull n = Blinelen(&in);
797 5cdb1798 2005-10-29 devnull if(strncmp(p, "From ", 5) != 0){
798 5cdb1798 2005-10-29 devnull Bseek(&in, -n, 1);
801 5cdb1798 2005-10-29 devnull Bwrite(&out, p, n);
806 5cdb1798 2005-10-29 devnull * Read mime types
808 5cdb1798 2005-10-29 devnull static void
809 5cdb1798 2005-10-29 devnull readmtypes(void)
811 5cdb1798 2005-10-29 devnull Biobuf *b;
812 5cdb1798 2005-10-29 devnull char *p;
813 5cdb1798 2005-10-29 devnull char *f[6];
814 5cdb1798 2005-10-29 devnull Mtype *m;
815 5cdb1798 2005-10-29 devnull Mtype **l;
817 7ce2007c 2006-02-12 devnull b = Bopen(unsharp("#9/lib/mimetype"), OREAD);
818 5cdb1798 2005-10-29 devnull if(b == nil)
821 5cdb1798 2005-10-29 devnull l = &mtypes;
822 5cdb1798 2005-10-29 devnull while((p = Brdline(b, '\n')) != nil){
823 5cdb1798 2005-10-29 devnull if(*p == '#')
824 5cdb1798 2005-10-29 devnull continue;
825 5cdb1798 2005-10-29 devnull p[Blinelen(b)-1] = 0;
826 5cdb1798 2005-10-29 devnull if(tokenize(p, f, nelem(f)) < 5)
827 5cdb1798 2005-10-29 devnull continue;
828 5cdb1798 2005-10-29 devnull m = mallocz(sizeof *m, 1);
829 5cdb1798 2005-10-29 devnull if(m == nil)
830 5cdb1798 2005-10-29 devnull goto err;
831 5cdb1798 2005-10-29 devnull m->ext = strdup(f[0]);
832 5cdb1798 2005-10-29 devnull if(m->ext == 0)
833 5cdb1798 2005-10-29 devnull goto err;
834 5cdb1798 2005-10-29 devnull m->gtype = strdup(f[1]);
835 5cdb1798 2005-10-29 devnull if(m->gtype == 0)
836 5cdb1798 2005-10-29 devnull goto err;
837 5cdb1798 2005-10-29 devnull m->stype = strdup(f[2]);
838 5cdb1798 2005-10-29 devnull if(m->stype == 0)
839 5cdb1798 2005-10-29 devnull goto err;
840 5cdb1798 2005-10-29 devnull m->class = *f[4];
842 5cdb1798 2005-10-29 devnull l = &(m->next);
844 5cdb1798 2005-10-29 devnull Bterm(b);
847 5cdb1798 2005-10-29 devnull if(m == nil)
849 5cdb1798 2005-10-29 devnull free(m->ext);
850 5cdb1798 2005-10-29 devnull free(m->gtype);
851 5cdb1798 2005-10-29 devnull free(m->stype);
852 5cdb1798 2005-10-29 devnull free(m);
853 5cdb1798 2005-10-29 devnull Bterm(b);
857 5cdb1798 2005-10-29 devnull * if the class is 'm' or 'y', accept it
858 5cdb1798 2005-10-29 devnull * if the class is 'p' check a previous extension
859 5cdb1798 2005-10-29 devnull * otherwise, filename is bad
861 5cdb1798 2005-10-29 devnull static int
862 5cdb1798 2005-10-29 devnull badfile(char *name)
864 5cdb1798 2005-10-29 devnull char *p;
865 5cdb1798 2005-10-29 devnull Mtype *m;
868 5cdb1798 2005-10-29 devnull p = strrchr(name, '.');
869 5cdb1798 2005-10-29 devnull if(p == nil)
870 5cdb1798 2005-10-29 devnull return 0;
872 5cdb1798 2005-10-29 devnull for(m = mtypes; m != nil; m = m->next)
873 5cdb1798 2005-10-29 devnull if(cistrcmp(p, m->ext) == 0){
874 5cdb1798 2005-10-29 devnull switch(m->class){
875 5cdb1798 2005-10-29 devnull case 'm':
876 5cdb1798 2005-10-29 devnull case 'y':
877 5cdb1798 2005-10-29 devnull return 0;
878 5cdb1798 2005-10-29 devnull case 'p':
880 5cdb1798 2005-10-29 devnull rv = badfile(name);
881 5cdb1798 2005-10-29 devnull *p = '.';
882 5cdb1798 2005-10-29 devnull return rv;
883 5cdb1798 2005-10-29 devnull case 'r':
884 5cdb1798 2005-10-29 devnull return 2;
887 5cdb1798 2005-10-29 devnull return 1;
891 5cdb1798 2005-10-29 devnull * if the class is 'm' or 'y' or 'p', accept it
892 5cdb1798 2005-10-29 devnull * otherwise, filename is bad
894 5cdb1798 2005-10-29 devnull static int
895 5cdb1798 2005-10-29 devnull badtype(char *type)
897 5cdb1798 2005-10-29 devnull Mtype *m;
898 5cdb1798 2005-10-29 devnull char *s, *fix;
899 5cdb1798 2005-10-29 devnull int rv = 1;
901 5cdb1798 2005-10-29 devnull fix = s = strchr(type, '/');
902 5cdb1798 2005-10-29 devnull if(s != nil)
903 5cdb1798 2005-10-29 devnull *s++ = 0;
905 5cdb1798 2005-10-29 devnull s = "-";
907 5cdb1798 2005-10-29 devnull for(m = mtypes; m != nil; m = m->next){
908 5cdb1798 2005-10-29 devnull if(cistrcmp(type, m->gtype) != 0)
909 5cdb1798 2005-10-29 devnull continue;
910 5cdb1798 2005-10-29 devnull if(cistrcmp(s, m->stype) != 0)
911 5cdb1798 2005-10-29 devnull continue;
912 5cdb1798 2005-10-29 devnull switch(m->class){
913 5cdb1798 2005-10-29 devnull case 'y':
914 5cdb1798 2005-10-29 devnull case 'p':
915 5cdb1798 2005-10-29 devnull case 'm':
922 5cdb1798 2005-10-29 devnull if(fix != nil)
923 5cdb1798 2005-10-29 devnull *fix = '/';
924 5cdb1798 2005-10-29 devnull return rv;
927 5cdb1798 2005-10-29 devnull /* rfc2047 non-ascii */
928 5cdb1798 2005-10-29 devnull typedef struct Charset Charset;
929 5cdb1798 2005-10-29 devnull struct Charset {
930 5cdb1798 2005-10-29 devnull char *name;
931 5cdb1798 2005-10-29 devnull int len;
932 5cdb1798 2005-10-29 devnull int convert;
933 5cdb1798 2005-10-29 devnull } charsets[] =
935 5cdb1798 2005-10-29 devnull { "us-ascii", 8, 1, },
936 5cdb1798 2005-10-29 devnull { "utf-8", 5, 0, },
937 cbeb0b26 2006-04-01 devnull { "iso-8859-1", 10, 1, }
941 5cdb1798 2005-10-29 devnull * convert to UTF if need be
943 5cdb1798 2005-10-29 devnull static String*
944 5cdb1798 2005-10-29 devnull tokenconvert(String *t)
946 5cdb1798 2005-10-29 devnull String *s;
947 5cdb1798 2005-10-29 devnull char decoded[1024];
948 5cdb1798 2005-10-29 devnull char utfbuf[2*1024];
949 5cdb1798 2005-10-29 devnull int i, len;
950 5cdb1798 2005-10-29 devnull char *e;
951 5cdb1798 2005-10-29 devnull char *token;
953 5cdb1798 2005-10-29 devnull token = s_to_c(t);
954 5cdb1798 2005-10-29 devnull len = s_len(t);
956 5cdb1798 2005-10-29 devnull if(token[0] != '=' || token[1] != '?' ||
957 5cdb1798 2005-10-29 devnull token[len-2] != '?' || token[len-1] != '=')
958 5cdb1798 2005-10-29 devnull goto err;
959 5cdb1798 2005-10-29 devnull e = token+len-2;
960 5cdb1798 2005-10-29 devnull token += 2;
962 cbeb0b26 2006-04-01 devnull /* bail if we don't understand the character set */
963 5cdb1798 2005-10-29 devnull for(i = 0; i < nelem(charsets); i++)
964 5cdb1798 2005-10-29 devnull if(cistrncmp(charsets[i].name, token, charsets[i].len) == 0)
965 5cdb1798 2005-10-29 devnull if(token[charsets[i].len] == '?'){
966 5cdb1798 2005-10-29 devnull token += charsets[i].len + 1;
969 5cdb1798 2005-10-29 devnull if(i >= nelem(charsets))
970 5cdb1798 2005-10-29 devnull goto err;
972 cbeb0b26 2006-04-01 devnull /* bail if it doesn't fit */
973 5cdb1798 2005-10-29 devnull if(strlen(token) > sizeof(decoded)-1)
974 5cdb1798 2005-10-29 devnull goto err;
976 cbeb0b26 2006-04-01 devnull /* bail if we don't understand the encoding */
977 5cdb1798 2005-10-29 devnull if(cistrncmp(token, "b?", 2) == 0){
978 5cdb1798 2005-10-29 devnull token += 2;
979 5cdb1798 2005-10-29 devnull len = dec64((uchar*)decoded, sizeof(decoded), token, e-token);
980 5cdb1798 2005-10-29 devnull decoded[len] = 0;
981 5cdb1798 2005-10-29 devnull } else if(cistrncmp(token, "q?", 2) == 0){
982 5cdb1798 2005-10-29 devnull token += 2;
983 5cdb1798 2005-10-29 devnull len = decquoted(decoded, token, e);
984 5cdb1798 2005-10-29 devnull if(len > 0 && decoded[len-1] == '\n')
986 5cdb1798 2005-10-29 devnull decoded[len] = 0;
988 5cdb1798 2005-10-29 devnull goto err;
990 5cdb1798 2005-10-29 devnull s = nil;
991 5cdb1798 2005-10-29 devnull switch(charsets[i].convert){
993 5cdb1798 2005-10-29 devnull s = s_copy(decoded);
996 5cdb1798 2005-10-29 devnull s = s_new();
997 5cdb1798 2005-10-29 devnull latin1toutf(utfbuf, decoded, decoded+len);
998 5cdb1798 2005-10-29 devnull s_append(s, utfbuf);
1002 5cdb1798 2005-10-29 devnull return s;
1004 5cdb1798 2005-10-29 devnull return s_clone(t);
1008 5cdb1798 2005-10-29 devnull * decode quoted
1012 5cdb1798 2005-10-29 devnull Self= 1,
1015 5cdb1798 2005-10-29 devnull uchar tableqp[256];
1017 5cdb1798 2005-10-29 devnull static void
1018 5cdb1798 2005-10-29 devnull initquoted(void)
1022 5cdb1798 2005-10-29 devnull memset(tableqp, 0, 256);
1023 5cdb1798 2005-10-29 devnull for(c = ' '; c <= '<'; c++)
1024 5cdb1798 2005-10-29 devnull tableqp[c] = Self;
1025 5cdb1798 2005-10-29 devnull for(c = '>'; c <= '~'; c++)
1026 5cdb1798 2005-10-29 devnull tableqp[c] = Self;
1027 5cdb1798 2005-10-29 devnull tableqp['\t'] = Self;
1028 5cdb1798 2005-10-29 devnull tableqp['='] = Hex;
1031 5cdb1798 2005-10-29 devnull static int
1032 5cdb1798 2005-10-29 devnull hex2int(int x)
1034 5cdb1798 2005-10-29 devnull if(x >= '0' && x <= '9')
1035 5cdb1798 2005-10-29 devnull return x - '0';
1036 5cdb1798 2005-10-29 devnull if(x >= 'A' && x <= 'F')
1037 5cdb1798 2005-10-29 devnull return (x - 'A') + 10;
1038 5cdb1798 2005-10-29 devnull if(x >= 'a' && x <= 'f')
1039 5cdb1798 2005-10-29 devnull return (x - 'a') + 10;
1040 5cdb1798 2005-10-29 devnull return 0;
1043 5cdb1798 2005-10-29 devnull static char*
1044 5cdb1798 2005-10-29 devnull decquotedline(char *out, char *in, char *e)
1046 5cdb1798 2005-10-29 devnull int c, soft;
1048 5cdb1798 2005-10-29 devnull /* dump trailing white space */
1049 5cdb1798 2005-10-29 devnull while(e >= in && (*e == ' ' || *e == '\t' || *e == '\r' || *e == '\n'))
1052 5cdb1798 2005-10-29 devnull /* trailing '=' means no newline */
1053 5cdb1798 2005-10-29 devnull if(*e == '='){
1054 5cdb1798 2005-10-29 devnull soft = 1;
1057 5cdb1798 2005-10-29 devnull soft = 0;
1059 5cdb1798 2005-10-29 devnull while(in <= e){
1060 5cdb1798 2005-10-29 devnull c = (*in++) & 0xff;
1061 5cdb1798 2005-10-29 devnull switch(tableqp[c]){
1062 5cdb1798 2005-10-29 devnull case Self:
1063 5cdb1798 2005-10-29 devnull *out++ = c;
1065 5cdb1798 2005-10-29 devnull case Hex:
1066 5cdb1798 2005-10-29 devnull c = hex2int(*in++)<<4;
1067 5cdb1798 2005-10-29 devnull c |= hex2int(*in++);
1068 5cdb1798 2005-10-29 devnull *out++ = c;
1072 5cdb1798 2005-10-29 devnull if(!soft)
1073 5cdb1798 2005-10-29 devnull *out++ = '\n';
1074 5cdb1798 2005-10-29 devnull *out = 0;
1076 5cdb1798 2005-10-29 devnull return out;
1079 5cdb1798 2005-10-29 devnull static int
1080 5cdb1798 2005-10-29 devnull decquoted(char *out, char *in, char *e)
1082 5cdb1798 2005-10-29 devnull char *p, *nl;
1084 5cdb1798 2005-10-29 devnull if(tableqp[' '] == 0)
1085 5cdb1798 2005-10-29 devnull initquoted();
1087 5cdb1798 2005-10-29 devnull p = out;
1088 5cdb1798 2005-10-29 devnull while((nl = strchr(in, '\n')) != nil && nl < e){
1089 5cdb1798 2005-10-29 devnull p = decquotedline(p, in, nl);
1090 5cdb1798 2005-10-29 devnull in = nl + 1;
1092 5cdb1798 2005-10-29 devnull if(in < e)
1093 5cdb1798 2005-10-29 devnull p = decquotedline(p, in, e-1);
1095 cbeb0b26 2006-04-01 devnull /* make sure we end with a new line */
1096 5cdb1798 2005-10-29 devnull if(*(p-1) != '\n'){
1097 5cdb1798 2005-10-29 devnull *p++ = '\n';
1098 5cdb1798 2005-10-29 devnull *p = 0;
1101 5cdb1798 2005-10-29 devnull return p - out;
1104 5cdb1798 2005-10-29 devnull /* translate latin1 directly since it fits neatly in utf */
1105 5cdb1798 2005-10-29 devnull static int
1106 5cdb1798 2005-10-29 devnull latin1toutf(char *out, char *in, char *e)
1108 5cdb1798 2005-10-29 devnull Rune r;
1109 5cdb1798 2005-10-29 devnull char *p;
1111 5cdb1798 2005-10-29 devnull p = out;
1112 5cdb1798 2005-10-29 devnull for(; in < e; in++){
1113 5cdb1798 2005-10-29 devnull r = (*in) & 0xff;
1114 5cdb1798 2005-10-29 devnull p += runetochar(p, &r);
1116 5cdb1798 2005-10-29 devnull *p = 0;
1117 5cdb1798 2005-10-29 devnull return p - out;