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 827018c4 2006-07-23 devnull remove(name);
464 5cdb1798 2005-10-29 devnull if(w == nil){
465 5cdb1798 2005-10-29 devnull syslog(0, "mail", "vf wait failed: %r");
466 5cdb1798 2005-10-29 devnull return 0;
468 5cdb1798 2005-10-29 devnull if(w->pid != pid){
469 5cdb1798 2005-10-29 devnull syslog(0, "mail", "vf wrong pid %d != %d", w->pid, pid);
470 5cdb1798 2005-10-29 devnull return 0;
472 5cdb1798 2005-10-29 devnull if(p->filename)
473 5cdb1798 2005-10-29 devnull name = s_to_c(p->filename);
474 20bd3ca2 2006-04-09 devnull if(atoi(w->msg) == Discard){
475 5cdb1798 2005-10-29 devnull syslog(0, "mail", "vf validateattachment rejected %s", name);
476 5cdb1798 2005-10-29 devnull refuse();
478 20bd3ca2 2006-04-09 devnull if(atoi(w->msg) == Accept){
479 5cdb1798 2005-10-29 devnull syslog(0, "mail", "vf validateattachment accepted %s", name);
480 5cdb1798 2005-10-29 devnull return 1;
482 5cdb1798 2005-10-29 devnull free(w);
483 5cdb1798 2005-10-29 devnull return 0;
487 5cdb1798 2005-10-29 devnull * emit a multipart Part that explains the problem
489 5cdb1798 2005-10-29 devnull static Part*
490 5cdb1798 2005-10-29 devnull problemchild(Part *p)
492 5cdb1798 2005-10-29 devnull Part *np;
493 5cdb1798 2005-10-29 devnull Hline *hl;
494 5cdb1798 2005-10-29 devnull String *boundary;
495 5cdb1798 2005-10-29 devnull char *cp;
498 5cdb1798 2005-10-29 devnull * We don't know whether the attachment is okay.
499 5cdb1798 2005-10-29 devnull * If there's an external checker, let it have a crack at it.
501 5cdb1798 2005-10-29 devnull if(runchecker(p) > 0)
502 5cdb1798 2005-10-29 devnull return p;
504 b5f65921 2006-02-11 devnull if(justreject)
505 b5f65921 2006-02-11 devnull return p;
507 5cdb1798 2005-10-29 devnull syslog(0, "mail", "vf wrapped %s %s", p->type?s_to_c(p->type):"?",
508 5cdb1798 2005-10-29 devnull p->filename?s_to_c(p->filename):"?");
510 5cdb1798 2005-10-29 devnull boundary = mkboundary();
511 5cdb1798 2005-10-29 devnull /* print out non-mime headers */
512 5cdb1798 2005-10-29 devnull for(hl = p->hl; hl != nil; hl = hl->next)
513 5cdb1798 2005-10-29 devnull if(cistrncmp(s_to_c(hl->s), "content-", 8) != 0)
514 5cdb1798 2005-10-29 devnull Bprint(&out, "%s", s_to_c(hl->s));
516 5cdb1798 2005-10-29 devnull /* add in our own multipart headers and message */
517 5cdb1798 2005-10-29 devnull Bprint(&out, "Content-Type: multipart/mixed;\n");
518 5cdb1798 2005-10-29 devnull Bprint(&out, "\tboundary=\"%s\"\n", s_to_c(boundary));
519 5cdb1798 2005-10-29 devnull Bprint(&out, "Content-Disposition: inline\n");
520 5cdb1798 2005-10-29 devnull Bprint(&out, "\n");
521 5cdb1798 2005-10-29 devnull Bprint(&out, "This is a multi-part message in MIME format.\n");
522 5cdb1798 2005-10-29 devnull Bprint(&out, "--%s\n", s_to_c(boundary));
523 5cdb1798 2005-10-29 devnull Bprint(&out, "Content-Disposition: inline\n");
524 5cdb1798 2005-10-29 devnull Bprint(&out, "Content-Type: text/plain; charset=\"US-ASCII\"\n");
525 5cdb1798 2005-10-29 devnull Bprint(&out, "Content-Transfer-Encoding: 7bit\n");
526 5cdb1798 2005-10-29 devnull Bprint(&out, "\n");
527 5cdb1798 2005-10-29 devnull Bprint(&out, "from postmaster@%s:\n", sysname());
528 5cdb1798 2005-10-29 devnull Bprint(&out, "The following attachment had content that we can't\n");
529 5cdb1798 2005-10-29 devnull Bprint(&out, "prove to be harmless. To avoid possible automatic\n");
530 5cdb1798 2005-10-29 devnull Bprint(&out, "execution, we changed the content headers.\n");
531 5cdb1798 2005-10-29 devnull Bprint(&out, "The original header was:\n\n");
533 5cdb1798 2005-10-29 devnull /* print out original header lines */
534 5cdb1798 2005-10-29 devnull for(hl = p->hl; hl != nil; hl = hl->next)
535 5cdb1798 2005-10-29 devnull if(cistrncmp(s_to_c(hl->s), "content-", 8) == 0)
536 5cdb1798 2005-10-29 devnull Bprint(&out, "\t%s", s_to_c(hl->s));
537 5cdb1798 2005-10-29 devnull Bprint(&out, "--%s\n", s_to_c(boundary));
539 5cdb1798 2005-10-29 devnull /* change file name */
540 5cdb1798 2005-10-29 devnull if(p->filename)
541 5cdb1798 2005-10-29 devnull s_append(p->filename, ".suspect");
543 5cdb1798 2005-10-29 devnull p->filename = s_copy("file.suspect");
545 5cdb1798 2005-10-29 devnull /* print out new header */
546 5cdb1798 2005-10-29 devnull Bprint(&out, "Content-Type: application/octet-stream\n");
547 5cdb1798 2005-10-29 devnull Bprint(&out, "Content-Disposition: attachment; filename=\"%s\"\n", s_to_c(p->filename));
548 5cdb1798 2005-10-29 devnull switch(p->encoding){
549 5cdb1798 2005-10-29 devnull case Enone:
551 5cdb1798 2005-10-29 devnull case Ebase64:
552 5cdb1798 2005-10-29 devnull Bprint(&out, "Content-Transfer-Encoding: base64\n");
554 5cdb1798 2005-10-29 devnull case Equoted:
555 5cdb1798 2005-10-29 devnull Bprint(&out, "Content-Transfer-Encoding: quoted-printable\n");
559 5cdb1798 2005-10-29 devnull /* pass the body */
560 5cdb1798 2005-10-29 devnull np = passbody(p, 0);
562 5cdb1798 2005-10-29 devnull /* add the new boundary and the original terminator */
563 5cdb1798 2005-10-29 devnull Bprint(&out, "--%s--\n", s_to_c(boundary));
564 5cdb1798 2005-10-29 devnull if(np && np->boundary){
565 5cdb1798 2005-10-29 devnull cp = Brdline(&in, '\n');
566 5cdb1798 2005-10-29 devnull Bwrite(&out, cp, Blinelen(&in));
569 5cdb1798 2005-10-29 devnull return np;
572 5cdb1798 2005-10-29 devnull static int
573 5cdb1798 2005-10-29 devnull isattribute(char **pp, char *attr)
575 5cdb1798 2005-10-29 devnull char *p;
578 5cdb1798 2005-10-29 devnull n = strlen(attr);
579 5cdb1798 2005-10-29 devnull p = *pp;
580 5cdb1798 2005-10-29 devnull if(cistrncmp(p, attr, n) != 0)
581 5cdb1798 2005-10-29 devnull return 0;
583 5cdb1798 2005-10-29 devnull while(*p == ' ')
585 5cdb1798 2005-10-29 devnull if(*p++ != '=')
586 5cdb1798 2005-10-29 devnull return 0;
587 5cdb1798 2005-10-29 devnull while(*p == ' ')
589 5cdb1798 2005-10-29 devnull *pp = p;
590 5cdb1798 2005-10-29 devnull return 1;
594 5cdb1798 2005-10-29 devnull * parse content type header
596 5cdb1798 2005-10-29 devnull static void
597 5cdb1798 2005-10-29 devnull ctype(Part *p, Hdef *h, char *cp)
599 5cdb1798 2005-10-29 devnull String *s;
601 5cdb1798 2005-10-29 devnull cp += h->len;
602 5cdb1798 2005-10-29 devnull cp = skipwhite(cp);
604 5cdb1798 2005-10-29 devnull p->type = s_new();
605 5cdb1798 2005-10-29 devnull cp = getstring(cp, p->type, 1);
606 5cdb1798 2005-10-29 devnull if(badtype(s_to_c(p->type)))
607 5cdb1798 2005-10-29 devnull p->badtype = 1;
609 5cdb1798 2005-10-29 devnull while(*cp){
610 5cdb1798 2005-10-29 devnull if(isattribute(&cp, "boundary")){
611 5cdb1798 2005-10-29 devnull s = s_new();
612 5cdb1798 2005-10-29 devnull cp = getstring(cp, s, 0);
613 5cdb1798 2005-10-29 devnull p->boundary = s_reset(p->boundary);
614 5cdb1798 2005-10-29 devnull s_append(p->boundary, "--");
615 5cdb1798 2005-10-29 devnull s_append(p->boundary, s_to_c(s));
616 5cdb1798 2005-10-29 devnull p->blen = s_len(p->boundary);
617 5cdb1798 2005-10-29 devnull s_free(s);
618 5cdb1798 2005-10-29 devnull } else if(cistrncmp(cp, "multipart", 9) == 0){
620 5cdb1798 2005-10-29 devnull * the first unbounded part of a multipart message,
621 5cdb1798 2005-10-29 devnull * the preamble, is not displayed or saved
623 5cdb1798 2005-10-29 devnull } else if(isattribute(&cp, "name")){
624 5cdb1798 2005-10-29 devnull setfilename(p, cp);
625 5cdb1798 2005-10-29 devnull } else if(isattribute(&cp, "charset")){
626 5cdb1798 2005-10-29 devnull if(p->charset == nil)
627 5cdb1798 2005-10-29 devnull p->charset = s_new();
628 5cdb1798 2005-10-29 devnull cp = getstring(cp, s_reset(p->charset), 0);
631 5cdb1798 2005-10-29 devnull cp = skiptosemi(cp);
636 5cdb1798 2005-10-29 devnull * parse content encoding header
638 5cdb1798 2005-10-29 devnull static void
639 5cdb1798 2005-10-29 devnull cencoding(Part *m, Hdef *h, char *p)
641 5cdb1798 2005-10-29 devnull p += h->len;
642 5cdb1798 2005-10-29 devnull p = skipwhite(p);
643 5cdb1798 2005-10-29 devnull if(cistrncmp(p, "base64", 6) == 0)
644 5cdb1798 2005-10-29 devnull m->encoding = Ebase64;
645 5cdb1798 2005-10-29 devnull else if(cistrncmp(p, "quoted-printable", 16) == 0)
646 5cdb1798 2005-10-29 devnull m->encoding = Equoted;
650 5cdb1798 2005-10-29 devnull * parse content disposition header
652 5cdb1798 2005-10-29 devnull static void
653 5cdb1798 2005-10-29 devnull cdisposition(Part *p, Hdef *h, char *cp)
655 5cdb1798 2005-10-29 devnull cp += h->len;
656 5cdb1798 2005-10-29 devnull cp = skipwhite(cp);
657 5cdb1798 2005-10-29 devnull while(*cp){
658 5cdb1798 2005-10-29 devnull if(cistrncmp(cp, "inline", 6) == 0){
659 5cdb1798 2005-10-29 devnull p->disposition = Dinline;
660 5cdb1798 2005-10-29 devnull } else if(cistrncmp(cp, "attachment", 10) == 0){
661 5cdb1798 2005-10-29 devnull p->disposition = Dfile;
662 5cdb1798 2005-10-29 devnull } else if(cistrncmp(cp, "filename=", 9) == 0){
663 5cdb1798 2005-10-29 devnull cp += 9;
664 5cdb1798 2005-10-29 devnull setfilename(p, cp);
666 5cdb1798 2005-10-29 devnull cp = skiptosemi(cp);
671 5cdb1798 2005-10-29 devnull static void
672 5cdb1798 2005-10-29 devnull setfilename(Part *p, char *name)
674 5cdb1798 2005-10-29 devnull if(p->filename == nil)
675 5cdb1798 2005-10-29 devnull p->filename = s_new();
676 5cdb1798 2005-10-29 devnull getstring(name, s_reset(p->filename), 0);
677 5cdb1798 2005-10-29 devnull p->filename = tokenconvert(p->filename);
678 5cdb1798 2005-10-29 devnull p->badfile = badfile(s_to_c(p->filename));
681 5cdb1798 2005-10-29 devnull static char*
682 5cdb1798 2005-10-29 devnull skipwhite(char *p)
684 5cdb1798 2005-10-29 devnull while(isspace(*p))
686 5cdb1798 2005-10-29 devnull return p;
689 5cdb1798 2005-10-29 devnull static char*
690 5cdb1798 2005-10-29 devnull skiptosemi(char *p)
692 5cdb1798 2005-10-29 devnull while(*p && *p != ';')
694 5cdb1798 2005-10-29 devnull while(*p == ';' || isspace(*p))
696 5cdb1798 2005-10-29 devnull return p;
700 5cdb1798 2005-10-29 devnull * parse a possibly "'d string from a header. A
701 5cdb1798 2005-10-29 devnull * ';' terminates the string.
703 5cdb1798 2005-10-29 devnull static char*
704 5cdb1798 2005-10-29 devnull getstring(char *p, String *s, int dolower)
706 5cdb1798 2005-10-29 devnull s = s_reset(s);
707 5cdb1798 2005-10-29 devnull p = skipwhite(p);
708 5cdb1798 2005-10-29 devnull if(*p == '"'){
710 5cdb1798 2005-10-29 devnull for(;*p && *p != '"'; p++)
711 5cdb1798 2005-10-29 devnull if(dolower)
712 5cdb1798 2005-10-29 devnull s_putc(s, tolower(*p));
714 5cdb1798 2005-10-29 devnull s_putc(s, *p);
715 5cdb1798 2005-10-29 devnull if(*p == '"')
717 5cdb1798 2005-10-29 devnull s_terminate(s);
719 5cdb1798 2005-10-29 devnull return p;
722 5cdb1798 2005-10-29 devnull for(; *p && !isspace(*p) && *p != ';'; p++)
723 5cdb1798 2005-10-29 devnull if(dolower)
724 5cdb1798 2005-10-29 devnull s_putc(s, tolower(*p));
726 5cdb1798 2005-10-29 devnull s_putc(s, *p);
727 5cdb1798 2005-10-29 devnull s_terminate(s);
729 5cdb1798 2005-10-29 devnull return p;
732 5cdb1798 2005-10-29 devnull static void
733 5cdb1798 2005-10-29 devnull init_hdefs(void)
735 5cdb1798 2005-10-29 devnull Hdef *hd;
736 5cdb1798 2005-10-29 devnull static int already;
738 5cdb1798 2005-10-29 devnull if(already)
740 5cdb1798 2005-10-29 devnull already = 1;
742 5cdb1798 2005-10-29 devnull for(hd = hdefs; hd->type != nil; hd++)
743 5cdb1798 2005-10-29 devnull hd->len = strlen(hd->type);
747 5cdb1798 2005-10-29 devnull * create a new boundary
749 5cdb1798 2005-10-29 devnull static String*
750 5cdb1798 2005-10-29 devnull mkboundary(void)
752 5cdb1798 2005-10-29 devnull char buf[32];
754 5cdb1798 2005-10-29 devnull static int already;
756 5cdb1798 2005-10-29 devnull if(already == 0){
757 5cdb1798 2005-10-29 devnull srand((time(0)<<16)|getpid());
758 5cdb1798 2005-10-29 devnull already = 1;
760 5cdb1798 2005-10-29 devnull strcpy(buf, "upas-");
761 5cdb1798 2005-10-29 devnull for(i = 5; i < sizeof(buf)-1; i++)
762 5cdb1798 2005-10-29 devnull buf[i] = 'a' + nrand(26);
763 5cdb1798 2005-10-29 devnull buf[i] = 0;
764 5cdb1798 2005-10-29 devnull return s_copy(buf);
768 5cdb1798 2005-10-29 devnull * skip blank lines till header
770 5cdb1798 2005-10-29 devnull static void
771 5cdb1798 2005-10-29 devnull passnotheader(void)
773 5cdb1798 2005-10-29 devnull char *cp;
774 5cdb1798 2005-10-29 devnull int i, n;
776 5cdb1798 2005-10-29 devnull while((cp = Brdline(&in, '\n')) != nil){
777 5cdb1798 2005-10-29 devnull n = Blinelen(&in);
778 5cdb1798 2005-10-29 devnull for(i = 0; i < n-1; i++)
779 5cdb1798 2005-10-29 devnull if(cp[i] != ' ' && cp[i] != '\t' && cp[i] != '\r'){
780 5cdb1798 2005-10-29 devnull Bseek(&in, -n, 1);
783 5cdb1798 2005-10-29 devnull Bwrite(&out, cp, n);
788 5cdb1798 2005-10-29 devnull * pass unix header lines
790 5cdb1798 2005-10-29 devnull static void
791 5cdb1798 2005-10-29 devnull passunixheader(void)
793 5cdb1798 2005-10-29 devnull char *p;
796 5cdb1798 2005-10-29 devnull while((p = Brdline(&in, '\n')) != nil){
797 5cdb1798 2005-10-29 devnull n = Blinelen(&in);
798 5cdb1798 2005-10-29 devnull if(strncmp(p, "From ", 5) != 0){
799 5cdb1798 2005-10-29 devnull Bseek(&in, -n, 1);
802 5cdb1798 2005-10-29 devnull Bwrite(&out, p, n);
807 5cdb1798 2005-10-29 devnull * Read mime types
809 5cdb1798 2005-10-29 devnull static void
810 5cdb1798 2005-10-29 devnull readmtypes(void)
812 5cdb1798 2005-10-29 devnull Biobuf *b;
813 5cdb1798 2005-10-29 devnull char *p;
814 5cdb1798 2005-10-29 devnull char *f[6];
815 5cdb1798 2005-10-29 devnull Mtype *m;
816 5cdb1798 2005-10-29 devnull Mtype **l;
818 7ce2007c 2006-02-12 devnull b = Bopen(unsharp("#9/lib/mimetype"), OREAD);
819 5cdb1798 2005-10-29 devnull if(b == nil)
822 5cdb1798 2005-10-29 devnull l = &mtypes;
823 5cdb1798 2005-10-29 devnull while((p = Brdline(b, '\n')) != nil){
824 5cdb1798 2005-10-29 devnull if(*p == '#')
825 5cdb1798 2005-10-29 devnull continue;
826 5cdb1798 2005-10-29 devnull p[Blinelen(b)-1] = 0;
827 5cdb1798 2005-10-29 devnull if(tokenize(p, f, nelem(f)) < 5)
828 5cdb1798 2005-10-29 devnull continue;
829 5cdb1798 2005-10-29 devnull m = mallocz(sizeof *m, 1);
830 5cdb1798 2005-10-29 devnull if(m == nil)
831 5cdb1798 2005-10-29 devnull goto err;
832 5cdb1798 2005-10-29 devnull m->ext = strdup(f[0]);
833 5cdb1798 2005-10-29 devnull if(m->ext == 0)
834 5cdb1798 2005-10-29 devnull goto err;
835 5cdb1798 2005-10-29 devnull m->gtype = strdup(f[1]);
836 5cdb1798 2005-10-29 devnull if(m->gtype == 0)
837 5cdb1798 2005-10-29 devnull goto err;
838 5cdb1798 2005-10-29 devnull m->stype = strdup(f[2]);
839 5cdb1798 2005-10-29 devnull if(m->stype == 0)
840 5cdb1798 2005-10-29 devnull goto err;
841 5cdb1798 2005-10-29 devnull m->class = *f[4];
843 5cdb1798 2005-10-29 devnull l = &(m->next);
845 5cdb1798 2005-10-29 devnull Bterm(b);
848 5cdb1798 2005-10-29 devnull if(m == nil)
850 5cdb1798 2005-10-29 devnull free(m->ext);
851 5cdb1798 2005-10-29 devnull free(m->gtype);
852 5cdb1798 2005-10-29 devnull free(m->stype);
853 5cdb1798 2005-10-29 devnull free(m);
854 5cdb1798 2005-10-29 devnull Bterm(b);
858 5cdb1798 2005-10-29 devnull * if the class is 'm' or 'y', accept it
859 5cdb1798 2005-10-29 devnull * if the class is 'p' check a previous extension
860 5cdb1798 2005-10-29 devnull * otherwise, filename is bad
862 5cdb1798 2005-10-29 devnull static int
863 5cdb1798 2005-10-29 devnull badfile(char *name)
865 5cdb1798 2005-10-29 devnull char *p;
866 5cdb1798 2005-10-29 devnull Mtype *m;
869 5cdb1798 2005-10-29 devnull p = strrchr(name, '.');
870 5cdb1798 2005-10-29 devnull if(p == nil)
871 5cdb1798 2005-10-29 devnull return 0;
873 5cdb1798 2005-10-29 devnull for(m = mtypes; m != nil; m = m->next)
874 5cdb1798 2005-10-29 devnull if(cistrcmp(p, m->ext) == 0){
875 5cdb1798 2005-10-29 devnull switch(m->class){
876 5cdb1798 2005-10-29 devnull case 'm':
877 5cdb1798 2005-10-29 devnull case 'y':
878 5cdb1798 2005-10-29 devnull return 0;
879 5cdb1798 2005-10-29 devnull case 'p':
881 5cdb1798 2005-10-29 devnull rv = badfile(name);
882 5cdb1798 2005-10-29 devnull *p = '.';
883 5cdb1798 2005-10-29 devnull return rv;
884 5cdb1798 2005-10-29 devnull case 'r':
885 5cdb1798 2005-10-29 devnull return 2;
888 5cdb1798 2005-10-29 devnull return 1;
892 5cdb1798 2005-10-29 devnull * if the class is 'm' or 'y' or 'p', accept it
893 5cdb1798 2005-10-29 devnull * otherwise, filename is bad
895 5cdb1798 2005-10-29 devnull static int
896 5cdb1798 2005-10-29 devnull badtype(char *type)
898 5cdb1798 2005-10-29 devnull Mtype *m;
899 5cdb1798 2005-10-29 devnull char *s, *fix;
900 5cdb1798 2005-10-29 devnull int rv = 1;
902 5cdb1798 2005-10-29 devnull fix = s = strchr(type, '/');
903 5cdb1798 2005-10-29 devnull if(s != nil)
904 5cdb1798 2005-10-29 devnull *s++ = 0;
906 5cdb1798 2005-10-29 devnull s = "-";
908 5cdb1798 2005-10-29 devnull for(m = mtypes; m != nil; m = m->next){
909 5cdb1798 2005-10-29 devnull if(cistrcmp(type, m->gtype) != 0)
910 5cdb1798 2005-10-29 devnull continue;
911 5cdb1798 2005-10-29 devnull if(cistrcmp(s, m->stype) != 0)
912 5cdb1798 2005-10-29 devnull continue;
913 5cdb1798 2005-10-29 devnull switch(m->class){
914 5cdb1798 2005-10-29 devnull case 'y':
915 5cdb1798 2005-10-29 devnull case 'p':
916 5cdb1798 2005-10-29 devnull case 'm':
923 5cdb1798 2005-10-29 devnull if(fix != nil)
924 5cdb1798 2005-10-29 devnull *fix = '/';
925 5cdb1798 2005-10-29 devnull return rv;
928 5cdb1798 2005-10-29 devnull /* rfc2047 non-ascii */
929 5cdb1798 2005-10-29 devnull typedef struct Charset Charset;
930 5cdb1798 2005-10-29 devnull struct Charset {
931 5cdb1798 2005-10-29 devnull char *name;
932 5cdb1798 2005-10-29 devnull int len;
933 5cdb1798 2005-10-29 devnull int convert;
934 5cdb1798 2005-10-29 devnull } charsets[] =
936 5cdb1798 2005-10-29 devnull { "us-ascii", 8, 1, },
937 5cdb1798 2005-10-29 devnull { "utf-8", 5, 0, },
938 cbeb0b26 2006-04-01 devnull { "iso-8859-1", 10, 1, }
942 5cdb1798 2005-10-29 devnull * convert to UTF if need be
944 5cdb1798 2005-10-29 devnull static String*
945 5cdb1798 2005-10-29 devnull tokenconvert(String *t)
947 5cdb1798 2005-10-29 devnull String *s;
948 5cdb1798 2005-10-29 devnull char decoded[1024];
949 5cdb1798 2005-10-29 devnull char utfbuf[2*1024];
950 5cdb1798 2005-10-29 devnull int i, len;
951 5cdb1798 2005-10-29 devnull char *e;
952 5cdb1798 2005-10-29 devnull char *token;
954 5cdb1798 2005-10-29 devnull token = s_to_c(t);
955 5cdb1798 2005-10-29 devnull len = s_len(t);
957 5cdb1798 2005-10-29 devnull if(token[0] != '=' || token[1] != '?' ||
958 5cdb1798 2005-10-29 devnull token[len-2] != '?' || token[len-1] != '=')
959 5cdb1798 2005-10-29 devnull goto err;
960 5cdb1798 2005-10-29 devnull e = token+len-2;
961 5cdb1798 2005-10-29 devnull token += 2;
963 cbeb0b26 2006-04-01 devnull /* bail if we don't understand the character set */
964 5cdb1798 2005-10-29 devnull for(i = 0; i < nelem(charsets); i++)
965 5cdb1798 2005-10-29 devnull if(cistrncmp(charsets[i].name, token, charsets[i].len) == 0)
966 5cdb1798 2005-10-29 devnull if(token[charsets[i].len] == '?'){
967 5cdb1798 2005-10-29 devnull token += charsets[i].len + 1;
970 5cdb1798 2005-10-29 devnull if(i >= nelem(charsets))
971 5cdb1798 2005-10-29 devnull goto err;
973 cbeb0b26 2006-04-01 devnull /* bail if it doesn't fit */
974 5cdb1798 2005-10-29 devnull if(strlen(token) > sizeof(decoded)-1)
975 5cdb1798 2005-10-29 devnull goto err;
977 cbeb0b26 2006-04-01 devnull /* bail if we don't understand the encoding */
978 5cdb1798 2005-10-29 devnull if(cistrncmp(token, "b?", 2) == 0){
979 5cdb1798 2005-10-29 devnull token += 2;
980 5cdb1798 2005-10-29 devnull len = dec64((uchar*)decoded, sizeof(decoded), token, e-token);
981 5cdb1798 2005-10-29 devnull decoded[len] = 0;
982 5cdb1798 2005-10-29 devnull } else if(cistrncmp(token, "q?", 2) == 0){
983 5cdb1798 2005-10-29 devnull token += 2;
984 5cdb1798 2005-10-29 devnull len = decquoted(decoded, token, e);
985 5cdb1798 2005-10-29 devnull if(len > 0 && decoded[len-1] == '\n')
987 5cdb1798 2005-10-29 devnull decoded[len] = 0;
989 5cdb1798 2005-10-29 devnull goto err;
991 5cdb1798 2005-10-29 devnull s = nil;
992 5cdb1798 2005-10-29 devnull switch(charsets[i].convert){
994 5cdb1798 2005-10-29 devnull s = s_copy(decoded);
997 5cdb1798 2005-10-29 devnull s = s_new();
998 5cdb1798 2005-10-29 devnull latin1toutf(utfbuf, decoded, decoded+len);
999 5cdb1798 2005-10-29 devnull s_append(s, utfbuf);
1003 5cdb1798 2005-10-29 devnull return s;
1005 5cdb1798 2005-10-29 devnull return s_clone(t);
1009 5cdb1798 2005-10-29 devnull * decode quoted
1013 5cdb1798 2005-10-29 devnull Self= 1,
1016 5cdb1798 2005-10-29 devnull uchar tableqp[256];
1018 5cdb1798 2005-10-29 devnull static void
1019 5cdb1798 2005-10-29 devnull initquoted(void)
1023 5cdb1798 2005-10-29 devnull memset(tableqp, 0, 256);
1024 5cdb1798 2005-10-29 devnull for(c = ' '; c <= '<'; c++)
1025 5cdb1798 2005-10-29 devnull tableqp[c] = Self;
1026 5cdb1798 2005-10-29 devnull for(c = '>'; c <= '~'; c++)
1027 5cdb1798 2005-10-29 devnull tableqp[c] = Self;
1028 5cdb1798 2005-10-29 devnull tableqp['\t'] = Self;
1029 5cdb1798 2005-10-29 devnull tableqp['='] = Hex;
1032 5cdb1798 2005-10-29 devnull static int
1033 5cdb1798 2005-10-29 devnull hex2int(int x)
1035 5cdb1798 2005-10-29 devnull if(x >= '0' && x <= '9')
1036 5cdb1798 2005-10-29 devnull return x - '0';
1037 5cdb1798 2005-10-29 devnull if(x >= 'A' && x <= 'F')
1038 5cdb1798 2005-10-29 devnull return (x - 'A') + 10;
1039 5cdb1798 2005-10-29 devnull if(x >= 'a' && x <= 'f')
1040 5cdb1798 2005-10-29 devnull return (x - 'a') + 10;
1041 5cdb1798 2005-10-29 devnull return 0;
1044 5cdb1798 2005-10-29 devnull static char*
1045 5cdb1798 2005-10-29 devnull decquotedline(char *out, char *in, char *e)
1047 5cdb1798 2005-10-29 devnull int c, soft;
1049 5cdb1798 2005-10-29 devnull /* dump trailing white space */
1050 5cdb1798 2005-10-29 devnull while(e >= in && (*e == ' ' || *e == '\t' || *e == '\r' || *e == '\n'))
1053 5cdb1798 2005-10-29 devnull /* trailing '=' means no newline */
1054 5cdb1798 2005-10-29 devnull if(*e == '='){
1055 5cdb1798 2005-10-29 devnull soft = 1;
1058 5cdb1798 2005-10-29 devnull soft = 0;
1060 5cdb1798 2005-10-29 devnull while(in <= e){
1061 5cdb1798 2005-10-29 devnull c = (*in++) & 0xff;
1062 5cdb1798 2005-10-29 devnull switch(tableqp[c]){
1063 5cdb1798 2005-10-29 devnull case Self:
1064 5cdb1798 2005-10-29 devnull *out++ = c;
1066 5cdb1798 2005-10-29 devnull case Hex:
1067 5cdb1798 2005-10-29 devnull c = hex2int(*in++)<<4;
1068 5cdb1798 2005-10-29 devnull c |= hex2int(*in++);
1069 5cdb1798 2005-10-29 devnull *out++ = c;
1073 5cdb1798 2005-10-29 devnull if(!soft)
1074 5cdb1798 2005-10-29 devnull *out++ = '\n';
1075 5cdb1798 2005-10-29 devnull *out = 0;
1077 5cdb1798 2005-10-29 devnull return out;
1080 5cdb1798 2005-10-29 devnull static int
1081 5cdb1798 2005-10-29 devnull decquoted(char *out, char *in, char *e)
1083 5cdb1798 2005-10-29 devnull char *p, *nl;
1085 5cdb1798 2005-10-29 devnull if(tableqp[' '] == 0)
1086 5cdb1798 2005-10-29 devnull initquoted();
1088 5cdb1798 2005-10-29 devnull p = out;
1089 5cdb1798 2005-10-29 devnull while((nl = strchr(in, '\n')) != nil && nl < e){
1090 5cdb1798 2005-10-29 devnull p = decquotedline(p, in, nl);
1091 5cdb1798 2005-10-29 devnull in = nl + 1;
1093 5cdb1798 2005-10-29 devnull if(in < e)
1094 5cdb1798 2005-10-29 devnull p = decquotedline(p, in, e-1);
1096 cbeb0b26 2006-04-01 devnull /* make sure we end with a new line */
1097 5cdb1798 2005-10-29 devnull if(*(p-1) != '\n'){
1098 5cdb1798 2005-10-29 devnull *p++ = '\n';
1099 5cdb1798 2005-10-29 devnull *p = 0;
1102 5cdb1798 2005-10-29 devnull return p - out;
1105 5cdb1798 2005-10-29 devnull /* translate latin1 directly since it fits neatly in utf */
1106 5cdb1798 2005-10-29 devnull static int
1107 5cdb1798 2005-10-29 devnull latin1toutf(char *out, char *in, char *e)
1109 5cdb1798 2005-10-29 devnull Rune r;
1110 5cdb1798 2005-10-29 devnull char *p;
1112 5cdb1798 2005-10-29 devnull p = out;
1113 5cdb1798 2005-10-29 devnull for(; in < e; in++){
1114 5cdb1798 2005-10-29 devnull r = (*in) & 0xff;
1115 5cdb1798 2005-10-29 devnull p += runetochar(p, &r);
1117 5cdb1798 2005-10-29 devnull *p = 0;
1118 5cdb1798 2005-10-29 devnull return p - out;