vf.c 17 KB

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