vf.c 20 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127
  1. /*
  2. * this is a filter that changes mime types and names of
  3. * suspect executable attachments.
  4. */
  5. #include "common.h"
  6. #include <ctype.h>
  7. Biobuf in;
  8. Biobuf out;
  9. typedef struct Mtype Mtype;
  10. typedef struct Hdef Hdef;
  11. typedef struct Hline Hline;
  12. typedef struct Part Part;
  13. static int badfile(char *name);
  14. static int badtype(char *type);
  15. static void ctype(Part*, Hdef*, char*);
  16. static void cencoding(Part*, Hdef*, char*);
  17. static void cdisposition(Part*, Hdef*, char*);
  18. static int decquoted(char *out, char *in, char *e);
  19. static char* getstring(char *p, String *s, int dolower);
  20. static void init_hdefs(void);
  21. static int isattribute(char **pp, char *attr);
  22. static int latin1toutf(char *out, char *in, char *e);
  23. static String* mkboundary(void);
  24. static Part* part(Part *pp);
  25. static Part* passbody(Part *p, int dobound);
  26. static void passnotheader(void);
  27. static void passunixheader(void);
  28. static Part* problemchild(Part *p);
  29. static void readheader(Part *p);
  30. static Hline* readhl(void);
  31. static void readmtypes(void);
  32. static int save(Part *p, char *file);
  33. static void setfilename(Part *p, char *name);
  34. static char* skiptosemi(char *p);
  35. static char* skipwhite(char *p);
  36. static String* tokenconvert(String *t);
  37. static void writeheader(Part *p, int);
  38. enum
  39. {
  40. /* encodings */
  41. Enone= 0,
  42. Ebase64,
  43. Equoted,
  44. /* disposition possibilities */
  45. Dnone= 0,
  46. Dinline,
  47. Dfile,
  48. Dignore,
  49. PAD64= '=',
  50. };
  51. /*
  52. * a message part; either the whole message or a subpart
  53. */
  54. struct Part
  55. {
  56. Part *pp; /* parent part */
  57. Hline *hl; /* linked list of header lines */
  58. int disposition;
  59. int encoding;
  60. int badfile;
  61. int badtype;
  62. String *boundary; /* boundary for multiparts */
  63. int blen;
  64. String *charset; /* character set */
  65. String *type; /* content type */
  66. String *filename; /* file name */
  67. Biobuf *tmpbuf; /* diversion input buffer */
  68. };
  69. /*
  70. * a (multi)line header
  71. */
  72. struct Hline
  73. {
  74. Hline *next;
  75. String *s;
  76. };
  77. /*
  78. * header definitions for parsing
  79. */
  80. struct Hdef
  81. {
  82. char *type;
  83. void (*f)(Part*, Hdef*, char*);
  84. int len;
  85. };
  86. Hdef hdefs[] =
  87. {
  88. { "content-type:", ctype, },
  89. { "content-transfer-encoding:", cencoding, },
  90. { "content-disposition:", cdisposition, },
  91. { 0, },
  92. };
  93. /*
  94. * acceptable content types and their extensions
  95. */
  96. struct Mtype {
  97. Mtype *next;
  98. char *ext; /* extension */
  99. char *gtype; /* generic content type */
  100. char *stype; /* specific content type */
  101. char class;
  102. };
  103. Mtype *mtypes;
  104. int justreject;
  105. char *savefile;
  106. void
  107. usage(void)
  108. {
  109. fprint(2, "usage: upas/vf [-r] [-s savefile]\n");
  110. exits("usage");
  111. }
  112. void
  113. main(int argc, char **argv)
  114. {
  115. ARGBEGIN{
  116. case 'r':
  117. justreject = 1;
  118. break;
  119. case 's':
  120. savefile = EARGF(usage());
  121. break;
  122. default:
  123. usage();
  124. }ARGEND
  125. if(argc)
  126. usage();
  127. Binit(&in, 0, OREAD);
  128. Binit(&out, 1, OWRITE);
  129. init_hdefs();
  130. readmtypes();
  131. /* pass through our standard 'From ' line */
  132. passunixheader();
  133. /* parse with the top level part */
  134. part(nil);
  135. exits(0);
  136. }
  137. void
  138. refuse(char *reason)
  139. {
  140. char *full;
  141. static char msg[] =
  142. "mail refused: we don't accept executable attachments";
  143. full = smprint("%s: %s", msg, reason);
  144. postnote(PNGROUP, getpid(), full);
  145. exits(full);
  146. }
  147. /*
  148. * parse a part; returns the ancestor whose boundary terminated
  149. * this part or nil on EOF.
  150. */
  151. static Part*
  152. part(Part *pp)
  153. {
  154. Part *p, *np;
  155. p = mallocz(sizeof *p, 1);
  156. p->pp = pp;
  157. readheader(p);
  158. if(p->boundary != nil){
  159. /* the format of a multipart part is always:
  160. * header
  161. * null or ignored body
  162. * boundary
  163. * header
  164. * body
  165. * boundary
  166. * ...
  167. */
  168. writeheader(p, 1);
  169. np = passbody(p, 1);
  170. if(np != p)
  171. return np;
  172. for(;;){
  173. np = part(p);
  174. if(np != p)
  175. return np;
  176. }
  177. } else {
  178. /* no boundary */
  179. /* may still be multipart if this is a forwarded message */
  180. if(p->type && cistrcmp(s_to_c(p->type), "message/rfc822") == 0){
  181. /* the format of forwarded message is:
  182. * header
  183. * header
  184. * body
  185. */
  186. writeheader(p, 1);
  187. passnotheader();
  188. return part(p);
  189. } else {
  190. /*
  191. * This is the meat. This may be an executable.
  192. * if so, wrap it and change its type
  193. */
  194. if(p->badtype || p->badfile){
  195. if(p->badfile == 2){
  196. if(savefile != nil)
  197. save(p, savefile);
  198. syslog(0, "vf", "vf rejected %s %s",
  199. p->type? s_to_c(p->type): "?",
  200. p->filename?s_to_c(p->filename):"?");
  201. fprint(2, "The mail contained an executable attachment.\n");
  202. fprint(2, "We refuse all mail containing such.\n");
  203. refuse(nil);
  204. }
  205. np = problemchild(p);
  206. if(np != p)
  207. return np;
  208. /* if problemchild returns p, it turns out p is okay: fall thru */
  209. }
  210. writeheader(p, 1);
  211. return passbody(p, 1);
  212. }
  213. }
  214. }
  215. /*
  216. * read and parse a complete header
  217. */
  218. static void
  219. readheader(Part *p)
  220. {
  221. Hline *hl, **l;
  222. Hdef *hd;
  223. l = &p->hl;
  224. for(;;){
  225. hl = readhl();
  226. if(hl == nil)
  227. break;
  228. *l = hl;
  229. l = &hl->next;
  230. for(hd = hdefs; hd->type != nil; hd++){
  231. if(cistrncmp(s_to_c(hl->s), hd->type, hd->len) == 0){
  232. (*hd->f)(p, hd, s_to_c(hl->s));
  233. break;
  234. }
  235. }
  236. }
  237. }
  238. /*
  239. * read a possibly multiline header line
  240. */
  241. static Hline*
  242. readhl(void)
  243. {
  244. Hline *hl;
  245. String *s;
  246. char *p;
  247. int n;
  248. p = Brdline(&in, '\n');
  249. if(p == nil)
  250. return nil;
  251. n = Blinelen(&in);
  252. if(memchr(p, ':', n) == nil){
  253. Bseek(&in, -n, 1);
  254. return nil;
  255. }
  256. s = s_nappend(s_new(), p, n);
  257. for(;;){
  258. p = Brdline(&in, '\n');
  259. if(p == nil)
  260. break;
  261. n = Blinelen(&in);
  262. if(*p != ' ' && *p != '\t'){
  263. Bseek(&in, -n, 1);
  264. break;
  265. }
  266. s = s_nappend(s, p, n);
  267. }
  268. hl = malloc(sizeof *hl);
  269. hl->s = s;
  270. hl->next = nil;
  271. return hl;
  272. }
  273. /*
  274. * write out a complete header
  275. */
  276. static void
  277. writeheader(Part *p, int xfree)
  278. {
  279. Hline *hl, *next;
  280. for(hl = p->hl; hl != nil; hl = next){
  281. Bprint(&out, "%s", s_to_c(hl->s));
  282. if(xfree)
  283. s_free(hl->s);
  284. next = hl->next;
  285. if(xfree)
  286. free(hl);
  287. }
  288. if(xfree)
  289. p->hl = nil;
  290. }
  291. /*
  292. * pass a body through. return if we hit one of our ancestors'
  293. * boundaries or EOF. if we hit a boundary, return a pointer to
  294. * that ancestor. if we hit EOF, return nil.
  295. */
  296. static Part*
  297. passbody(Part *p, int dobound)
  298. {
  299. Part *pp;
  300. Biobuf *b;
  301. char *cp;
  302. for(;;){
  303. if(p->tmpbuf){
  304. b = p->tmpbuf;
  305. cp = Brdline(b, '\n');
  306. if(cp == nil){
  307. Bterm(b);
  308. p->tmpbuf = nil;
  309. goto Stdin;
  310. }
  311. }else{
  312. Stdin:
  313. b = &in;
  314. cp = Brdline(b, '\n');
  315. }
  316. if(cp == nil)
  317. return nil;
  318. for(pp = p; pp != nil; pp = pp->pp)
  319. if(pp->boundary != nil
  320. && strncmp(cp, s_to_c(pp->boundary), pp->blen) == 0){
  321. if(dobound)
  322. Bwrite(&out, cp, Blinelen(b));
  323. else
  324. Bseek(b, -Blinelen(b), 1);
  325. return pp;
  326. }
  327. Bwrite(&out, cp, Blinelen(b));
  328. }
  329. }
  330. /*
  331. * save the message somewhere
  332. */
  333. static vlong bodyoff; /* clumsy hack */
  334. static int
  335. save(Part *p, char *file)
  336. {
  337. int fd;
  338. char *cp;
  339. Bterm(&out);
  340. memset(&out, 0, sizeof(out));
  341. fd = open(file, OWRITE);
  342. if(fd < 0)
  343. return -1;
  344. seek(fd, 0, 2);
  345. Binit(&out, fd, OWRITE);
  346. cp = ctime(time(0));
  347. cp[28] = 0;
  348. Bprint(&out, "From virusfilter %s\n", cp);
  349. writeheader(p, 0);
  350. bodyoff = Boffset(&out);
  351. passbody(p, 1);
  352. Bprint(&out, "\n");
  353. Bterm(&out);
  354. close(fd);
  355. memset(&out, 0, sizeof out);
  356. Binit(&out, 1, OWRITE);
  357. return 0;
  358. }
  359. /*
  360. * write to a file but save the fd for passbody.
  361. */
  362. static char*
  363. savetmp(Part *p)
  364. {
  365. char *name;
  366. int fd;
  367. name = mktemp(smprint("%s/vf.XXXXXXXXXXX", UPASTMP));
  368. if((fd = create(name, OWRITE|OEXCL, 0666)) < 0){
  369. fprint(2, "%s: error creating temporary file: %r\n", argv0);
  370. refuse("can't create temporary file");
  371. }
  372. close(fd);
  373. if(save(p, name) < 0){
  374. fprint(2, "%s: error saving temporary file: %r\n", argv0);
  375. refuse("can't write temporary file");
  376. }
  377. if(p->tmpbuf){
  378. fprint(2, "%s: error in savetmp: already have tmp file!\n",
  379. argv0);
  380. refuse("already have temporary file");
  381. }
  382. p->tmpbuf = Bopen(name, OREAD|ORCLOSE);
  383. if(p->tmpbuf == nil){
  384. fprint(2, "%s: error reading temporary file: %r\n", argv0);
  385. refuse("error reading temporary file");
  386. }
  387. Bseek(p->tmpbuf, bodyoff, 0);
  388. return name;
  389. }
  390. /*
  391. * Run the external checker to do content-based checks.
  392. */
  393. static int
  394. runchecker(Part *p)
  395. {
  396. int pid;
  397. char *name;
  398. Waitmsg *w;
  399. if(access("/mail/lib/validateattachment", AEXEC) < 0)
  400. return 0;
  401. name = savetmp(p);
  402. fprint(2, "run checker %s\n", name);
  403. switch(pid = fork()){
  404. case -1:
  405. sysfatal("fork: %r");
  406. case 0:
  407. dup(2, 1);
  408. execl("/mail/lib/validateattachment", "validateattachment",
  409. name, nil);
  410. _exits("exec failed");
  411. }
  412. /*
  413. * Okay to return on error - will let mail through but wrapped.
  414. */
  415. w = wait();
  416. if(w == nil){
  417. syslog(0, "mail", "vf wait failed: %r");
  418. return 0;
  419. }
  420. if(w->pid != pid){
  421. syslog(0, "mail", "vf wrong pid %d != %d", w->pid, pid);
  422. return 0;
  423. }
  424. if(p->filename) {
  425. free(name);
  426. name = strdup(s_to_c(p->filename));
  427. }
  428. if(strstr(w->msg, "discard")){
  429. syslog(0, "mail", "vf validateattachment rejected %s", name);
  430. refuse("rejected by validateattachment");
  431. }
  432. if(strstr(w->msg, "accept")){
  433. syslog(0, "mail", "vf validateattachment accepted %s", name);
  434. return 1;
  435. }
  436. free(w);
  437. free(name);
  438. return 0;
  439. }
  440. /*
  441. * emit a multipart Part that explains the problem
  442. */
  443. static Part*
  444. problemchild(Part *p)
  445. {
  446. Part *np;
  447. Hline *hl;
  448. String *boundary;
  449. char *cp;
  450. /*
  451. * We don't know whether the attachment is okay.
  452. * If there's an external checker, let it have a crack at it.
  453. */
  454. if(runchecker(p) > 0)
  455. return p;
  456. if(justreject)
  457. return p;
  458. fprint(2, "x\n");
  459. syslog(0, "mail", "vf wrapped %s %s", p->type?s_to_c(p->type):"?",
  460. p->filename?s_to_c(p->filename):"?");
  461. fprint(2, "x\n");
  462. boundary = mkboundary();
  463. fprint(2, "x\n");
  464. /* print out non-mime headers */
  465. for(hl = p->hl; hl != nil; hl = hl->next)
  466. if(cistrncmp(s_to_c(hl->s), "content-", 8) != 0)
  467. Bprint(&out, "%s", s_to_c(hl->s));
  468. fprint(2, "x\n");
  469. /* add in our own multipart headers and message */
  470. Bprint(&out, "Content-Type: multipart/mixed;\n");
  471. Bprint(&out, "\tboundary=\"%s\"\n", s_to_c(boundary));
  472. Bprint(&out, "Content-Disposition: inline\n");
  473. Bprint(&out, "\n");
  474. Bprint(&out, "This is a multi-part message in MIME format.\n");
  475. Bprint(&out, "--%s\n", s_to_c(boundary));
  476. Bprint(&out, "Content-Disposition: inline\n");
  477. Bprint(&out, "Content-Type: text/plain; charset=\"US-ASCII\"\n");
  478. Bprint(&out, "Content-Transfer-Encoding: 7bit\n");
  479. Bprint(&out, "\n");
  480. Bprint(&out, "from postmaster@%s:\n", sysname());
  481. Bprint(&out, "The following attachment had content that we can't\n");
  482. Bprint(&out, "prove to be harmless. To avoid possible automatic\n");
  483. Bprint(&out, "execution, we changed the content headers.\n");
  484. Bprint(&out, "The original header was:\n\n");
  485. /* print out original header lines */
  486. for(hl = p->hl; hl != nil; hl = hl->next)
  487. if(cistrncmp(s_to_c(hl->s), "content-", 8) == 0)
  488. Bprint(&out, "\t%s", s_to_c(hl->s));
  489. Bprint(&out, "--%s\n", s_to_c(boundary));
  490. /* change file name */
  491. if(p->filename)
  492. s_append(p->filename, ".suspect");
  493. else
  494. p->filename = s_copy("file.suspect");
  495. /* print out new header */
  496. Bprint(&out, "Content-Type: application/octet-stream\n");
  497. Bprint(&out, "Content-Disposition: attachment; filename=\"%s\"\n", s_to_c(p->filename));
  498. switch(p->encoding){
  499. case Enone:
  500. break;
  501. case Ebase64:
  502. Bprint(&out, "Content-Transfer-Encoding: base64\n");
  503. break;
  504. case Equoted:
  505. Bprint(&out, "Content-Transfer-Encoding: quoted-printable\n");
  506. break;
  507. }
  508. fprint(2, "z\n");
  509. /* pass the body */
  510. np = passbody(p, 0);
  511. fprint(2, "w\n");
  512. /* add the new boundary and the original terminator */
  513. Bprint(&out, "--%s--\n", s_to_c(boundary));
  514. if(np && np->boundary){
  515. cp = Brdline(&in, '\n');
  516. Bwrite(&out, cp, Blinelen(&in));
  517. }
  518. fprint(2, "a %p\n", np);
  519. return np;
  520. }
  521. static int
  522. isattribute(char **pp, char *attr)
  523. {
  524. char *p;
  525. int n;
  526. n = strlen(attr);
  527. p = *pp;
  528. if(cistrncmp(p, attr, n) != 0)
  529. return 0;
  530. p += n;
  531. while(*p == ' ')
  532. p++;
  533. if(*p++ != '=')
  534. return 0;
  535. while(*p == ' ')
  536. p++;
  537. *pp = p;
  538. return 1;
  539. }
  540. /*
  541. * parse content type header
  542. */
  543. static void
  544. ctype(Part *p, Hdef *h, char *cp)
  545. {
  546. String *s;
  547. cp += h->len;
  548. cp = skipwhite(cp);
  549. p->type = s_new();
  550. cp = getstring(cp, p->type, 1);
  551. if(badtype(s_to_c(p->type)))
  552. p->badtype = 1;
  553. while(*cp){
  554. if(isattribute(&cp, "boundary")){
  555. s = s_new();
  556. cp = getstring(cp, s, 0);
  557. p->boundary = s_reset(p->boundary);
  558. s_append(p->boundary, "--");
  559. s_append(p->boundary, s_to_c(s));
  560. p->blen = s_len(p->boundary);
  561. s_free(s);
  562. } else if(cistrncmp(cp, "multipart", 9) == 0){
  563. /*
  564. * the first unbounded part of a multipart message,
  565. * the preamble, is not displayed or saved
  566. */
  567. } else if(isattribute(&cp, "name")){
  568. setfilename(p, cp);
  569. } else if(isattribute(&cp, "charset")){
  570. if(p->charset == nil)
  571. p->charset = s_new();
  572. cp = getstring(cp, s_reset(p->charset), 0);
  573. }
  574. cp = skiptosemi(cp);
  575. }
  576. }
  577. /*
  578. * parse content encoding header
  579. */
  580. static void
  581. cencoding(Part *m, Hdef *h, char *p)
  582. {
  583. p += h->len;
  584. p = skipwhite(p);
  585. if(cistrncmp(p, "base64", 6) == 0)
  586. m->encoding = Ebase64;
  587. else if(cistrncmp(p, "quoted-printable", 16) == 0)
  588. m->encoding = Equoted;
  589. }
  590. /*
  591. * parse content disposition header
  592. */
  593. static void
  594. cdisposition(Part *p, Hdef *h, char *cp)
  595. {
  596. cp += h->len;
  597. cp = skipwhite(cp);
  598. while(*cp){
  599. if(cistrncmp(cp, "inline", 6) == 0){
  600. p->disposition = Dinline;
  601. } else if(cistrncmp(cp, "attachment", 10) == 0){
  602. p->disposition = Dfile;
  603. } else if(cistrncmp(cp, "filename=", 9) == 0){
  604. cp += 9;
  605. setfilename(p, cp);
  606. }
  607. cp = skiptosemi(cp);
  608. }
  609. }
  610. static void
  611. setfilename(Part *p, char *name)
  612. {
  613. if(p->filename == nil)
  614. p->filename = s_new();
  615. getstring(name, s_reset(p->filename), 0);
  616. p->filename = tokenconvert(p->filename);
  617. p->badfile = badfile(s_to_c(p->filename));
  618. }
  619. static char*
  620. skipwhite(char *p)
  621. {
  622. while(isspace(*p))
  623. p++;
  624. return p;
  625. }
  626. static char*
  627. skiptosemi(char *p)
  628. {
  629. while(*p && *p != ';')
  630. p++;
  631. while(*p == ';' || isspace(*p))
  632. p++;
  633. return p;
  634. }
  635. /*
  636. * parse a possibly "'d string from a header. A
  637. * ';' terminates the string.
  638. */
  639. static char*
  640. getstring(char *p, String *s, int dolower)
  641. {
  642. s = s_reset(s);
  643. p = skipwhite(p);
  644. if(*p == '"'){
  645. p++;
  646. for(;*p && *p != '"'; p++)
  647. if(dolower)
  648. s_putc(s, tolower(*p));
  649. else
  650. s_putc(s, *p);
  651. if(*p == '"')
  652. p++;
  653. s_terminate(s);
  654. return p;
  655. }
  656. for(; *p && !isspace(*p) && *p != ';'; p++)
  657. if(dolower)
  658. s_putc(s, tolower(*p));
  659. else
  660. s_putc(s, *p);
  661. s_terminate(s);
  662. return p;
  663. }
  664. static void
  665. init_hdefs(void)
  666. {
  667. Hdef *hd;
  668. static int already;
  669. if(already)
  670. return;
  671. already = 1;
  672. for(hd = hdefs; hd->type != nil; hd++)
  673. hd->len = strlen(hd->type);
  674. }
  675. /*
  676. * create a new boundary
  677. */
  678. static String*
  679. mkboundary(void)
  680. {
  681. char buf[32];
  682. int i;
  683. static int already;
  684. if(already == 0){
  685. srand((time(0)<<16)|getpid());
  686. already = 1;
  687. }
  688. strcpy(buf, "upas-");
  689. for(i = 5; i < sizeof(buf)-1; i++)
  690. buf[i] = 'a' + nrand(26);
  691. buf[i] = 0;
  692. return s_copy(buf);
  693. }
  694. /*
  695. * skip blank lines till header
  696. */
  697. static void
  698. passnotheader(void)
  699. {
  700. char *cp;
  701. int i, n;
  702. while((cp = Brdline(&in, '\n')) != nil){
  703. n = Blinelen(&in);
  704. for(i = 0; i < n-1; i++)
  705. if(cp[i] != ' ' && cp[i] != '\t' && cp[i] != '\r'){
  706. Bseek(&in, -n, 1);
  707. return;
  708. }
  709. Bwrite(&out, cp, n);
  710. }
  711. }
  712. /*
  713. * pass unix header lines
  714. */
  715. static void
  716. passunixheader(void)
  717. {
  718. char *p;
  719. int n;
  720. while((p = Brdline(&in, '\n')) != nil){
  721. n = Blinelen(&in);
  722. if(strncmp(p, "From ", 5) != 0){
  723. Bseek(&in, -n, 1);
  724. break;
  725. }
  726. Bwrite(&out, p, n);
  727. }
  728. }
  729. /*
  730. * Read mime types
  731. */
  732. static void
  733. readmtypes(void)
  734. {
  735. Biobuf *b;
  736. char *p;
  737. char *f[6];
  738. Mtype *m;
  739. Mtype **l;
  740. b = Bopen("/sys/lib/mimetype", OREAD);
  741. if(b == nil)
  742. return;
  743. l = &mtypes;
  744. while((p = Brdline(b, '\n')) != nil){
  745. if(*p == '#')
  746. continue;
  747. p[Blinelen(b)-1] = 0;
  748. if(tokenize(p, f, nelem(f)) < 5)
  749. continue;
  750. m = mallocz(sizeof *m, 1);
  751. if(m == nil)
  752. goto err;
  753. m->ext = strdup(f[0]);
  754. if(m->ext == 0)
  755. goto err;
  756. m->gtype = strdup(f[1]);
  757. if(m->gtype == 0)
  758. goto err;
  759. m->stype = strdup(f[2]);
  760. if(m->stype == 0)
  761. goto err;
  762. m->class = *f[4];
  763. *l = m;
  764. l = &(m->next);
  765. }
  766. Bterm(b);
  767. return;
  768. err:
  769. if(m == nil)
  770. return;
  771. free(m->ext);
  772. free(m->gtype);
  773. free(m->stype);
  774. free(m);
  775. Bterm(b);
  776. }
  777. /*
  778. * if the class is 'm' or 'y', accept it
  779. * if the class is 'p' check a previous extension
  780. * otherwise, filename is bad
  781. */
  782. static int
  783. badfile(char *name)
  784. {
  785. char *p;
  786. Mtype *m;
  787. int rv;
  788. p = strrchr(name, '.');
  789. if(p == nil)
  790. return 0;
  791. for(m = mtypes; m != nil; m = m->next)
  792. if(cistrcmp(p, m->ext) == 0){
  793. switch(m->class){
  794. case 'm':
  795. case 'y':
  796. return 0;
  797. case 'p':
  798. *p = 0;
  799. rv = badfile(name);
  800. *p = '.';
  801. return rv;
  802. case 'r':
  803. return 2;
  804. }
  805. }
  806. return 1;
  807. }
  808. /*
  809. * if the class is 'm' or 'y' or 'p', accept it
  810. * otherwise, filename is bad
  811. */
  812. static int
  813. badtype(char *type)
  814. {
  815. Mtype *m;
  816. char *s, *fix;
  817. int rv = 1;
  818. fix = s = strchr(type, '/');
  819. if(s != nil)
  820. *s++ = 0;
  821. else
  822. s = "-";
  823. for(m = mtypes; m != nil; m = m->next){
  824. if(cistrcmp(type, m->gtype) != 0)
  825. continue;
  826. if(cistrcmp(s, m->stype) != 0)
  827. continue;
  828. switch(m->class){
  829. case 'y':
  830. case 'p':
  831. case 'm':
  832. rv = 0;
  833. break;
  834. }
  835. break;
  836. }
  837. if(fix != nil)
  838. *fix = '/';
  839. return rv;
  840. }
  841. /* rfc2047 non-ascii */
  842. typedef struct Charset Charset;
  843. struct Charset {
  844. char *name;
  845. int len;
  846. int convert;
  847. } charsets[] =
  848. {
  849. { "us-ascii", 8, 1, },
  850. { "utf-8", 5, 0, },
  851. { "iso-8859-1", 10, 1, },
  852. };
  853. /*
  854. * convert to UTF if need be
  855. */
  856. static String*
  857. tokenconvert(String *t)
  858. {
  859. String *s;
  860. char decoded[1024];
  861. char utfbuf[2*1024];
  862. int i, len;
  863. char *e;
  864. char *token;
  865. token = s_to_c(t);
  866. len = s_len(t);
  867. if(token[0] != '=' || token[1] != '?' ||
  868. token[len-2] != '?' || token[len-1] != '=')
  869. goto err;
  870. e = token+len-2;
  871. token += 2;
  872. /* bail if we don't understand the character set */
  873. for(i = 0; i < nelem(charsets); i++)
  874. if(cistrncmp(charsets[i].name, token, charsets[i].len) == 0)
  875. if(token[charsets[i].len] == '?'){
  876. token += charsets[i].len + 1;
  877. break;
  878. }
  879. if(i >= nelem(charsets))
  880. goto err;
  881. /* bail if it doesn't fit */
  882. if(strlen(token) > sizeof(decoded)-1)
  883. goto err;
  884. /* bail if we don't understand the encoding */
  885. if(cistrncmp(token, "b?", 2) == 0){
  886. token += 2;
  887. len = dec64((uchar*)decoded, sizeof(decoded), token, e-token);
  888. decoded[len] = 0;
  889. } else if(cistrncmp(token, "q?", 2) == 0){
  890. token += 2;
  891. len = decquoted(decoded, token, e);
  892. if(len > 0 && decoded[len-1] == '\n')
  893. len--;
  894. decoded[len] = 0;
  895. } else
  896. goto err;
  897. s = nil;
  898. switch(charsets[i].convert){
  899. case 0:
  900. s = s_copy(decoded);
  901. break;
  902. case 1:
  903. s = s_new();
  904. latin1toutf(utfbuf, decoded, decoded+len);
  905. s_append(s, utfbuf);
  906. break;
  907. }
  908. return s;
  909. err:
  910. return s_clone(t);
  911. }
  912. /*
  913. * decode quoted
  914. */
  915. enum
  916. {
  917. Self= 1,
  918. Hex= 2,
  919. };
  920. uchar tableqp[256];
  921. static void
  922. initquoted(void)
  923. {
  924. int c;
  925. memset(tableqp, 0, 256);
  926. for(c = ' '; c <= '<'; c++)
  927. tableqp[c] = Self;
  928. for(c = '>'; c <= '~'; c++)
  929. tableqp[c] = Self;
  930. tableqp['\t'] = Self;
  931. tableqp['='] = Hex;
  932. }
  933. static int
  934. hex2int(int x)
  935. {
  936. if(x >= '0' && x <= '9')
  937. return x - '0';
  938. if(x >= 'A' && x <= 'F')
  939. return (x - 'A') + 10;
  940. if(x >= 'a' && x <= 'f')
  941. return (x - 'a') + 10;
  942. return 0;
  943. }
  944. static char*
  945. decquotedline(char *out, char *in, char *e)
  946. {
  947. int c, soft;
  948. /* dump trailing white space */
  949. while(e >= in && (*e == ' ' || *e == '\t' || *e == '\r' || *e == '\n'))
  950. e--;
  951. /* trailing '=' means no newline */
  952. if(*e == '='){
  953. soft = 1;
  954. e--;
  955. } else
  956. soft = 0;
  957. while(in <= e){
  958. c = (*in++) & 0xff;
  959. switch(tableqp[c]){
  960. case Self:
  961. *out++ = c;
  962. break;
  963. case Hex:
  964. c = hex2int(*in++)<<4;
  965. c |= hex2int(*in++);
  966. *out++ = c;
  967. break;
  968. }
  969. }
  970. if(!soft)
  971. *out++ = '\n';
  972. *out = 0;
  973. return out;
  974. }
  975. static int
  976. decquoted(char *out, char *in, char *e)
  977. {
  978. char *p, *nl;
  979. if(tableqp[' '] == 0)
  980. initquoted();
  981. p = out;
  982. while((nl = strchr(in, '\n')) != nil && nl < e){
  983. p = decquotedline(p, in, nl);
  984. in = nl + 1;
  985. }
  986. if(in < e)
  987. p = decquotedline(p, in, e-1);
  988. /* make sure we end with a new line */
  989. if(*(p-1) != '\n'){
  990. *p++ = '\n';
  991. *p = 0;
  992. }
  993. return p - out;
  994. }
  995. /* translate latin1 directly since it fits neatly in utf */
  996. static int
  997. latin1toutf(char *out, char *in, char *e)
  998. {
  999. Rune r;
  1000. char *p;
  1001. p = out;
  1002. for(; in < e; in++){
  1003. r = (*in) & 0xff;
  1004. p += runetochar(p, &r);
  1005. }
  1006. *p = 0;
  1007. return p - out;
  1008. }