marshal.c 33 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912
  1. /*
  2. * marshal - gather mail message for transmission
  3. */
  4. #include "common.h"
  5. #include <ctype.h>
  6. typedef struct Attach Attach;
  7. typedef struct Alias Alias;
  8. typedef struct Addr Addr;
  9. typedef struct Ctype Ctype;
  10. struct Attach {
  11. Attach *next;
  12. char *path;
  13. char *type;
  14. int ainline;
  15. Ctype *ctype;
  16. };
  17. struct Alias
  18. {
  19. Alias *next;
  20. int n;
  21. Addr *addr;
  22. };
  23. struct Addr
  24. {
  25. Addr *next;
  26. char *v;
  27. };
  28. enum {
  29. Hfrom,
  30. Hto,
  31. Hcc,
  32. Hbcc,
  33. Hsender,
  34. Hreplyto,
  35. Hinreplyto,
  36. Hdate,
  37. Hsubject,
  38. Hmime,
  39. Hpriority,
  40. Hmsgid,
  41. Hcontent,
  42. Hx,
  43. Hprecedence,
  44. Nhdr,
  45. };
  46. enum {
  47. PGPsign = 1,
  48. PGPencrypt = 2,
  49. };
  50. char *hdrs[Nhdr] = {
  51. [Hfrom] "from:",
  52. [Hto] "to:",
  53. [Hcc] "cc:",
  54. [Hbcc] "bcc:",
  55. [Hreplyto] "reply-to:",
  56. [Hinreplyto] "in-reply-to:",
  57. [Hsender] "sender:",
  58. [Hdate] "date:",
  59. [Hsubject] "subject:",
  60. [Hpriority] "priority:",
  61. [Hmsgid] "message-id:",
  62. [Hmime] "mime-",
  63. [Hcontent] "content-",
  64. [Hx] "x-",
  65. [Hprecedence] "precedence",
  66. };
  67. struct Ctype {
  68. char *type;
  69. char *ext;
  70. int display;
  71. };
  72. Ctype ctype[] = {
  73. { "text/plain", "txt", 1, },
  74. { "text/html", "html", 1, },
  75. { "text/html", "htm", 1, },
  76. { "text/tab-separated-values", "tsv", 1, },
  77. { "text/richtext", "rtx", 1, },
  78. { "message/rfc822", "txt", 1, },
  79. { "", 0, 0, },
  80. };
  81. Ctype *mimetypes;
  82. int pid = -1;
  83. int pgppid = -1;
  84. void Bdrain(Biobuf*);
  85. void attachment(Attach*, Biobuf*);
  86. void body(Biobuf*, Biobuf*, int);
  87. int cistrcmp(char*, char*);
  88. int cistrncmp(char*, char*, int);
  89. int doublequote(Fmt*);
  90. void* emalloc(int);
  91. int enc64(char*, int, uchar*, int);
  92. void* erealloc(void*, int);
  93. char* estrdup(char*);
  94. Addr* expand(int, char**);
  95. Addr* expandline(String**, Addr*);
  96. void freeaddr(Addr*);
  97. void freeaddr(Addr *);
  98. void freeaddrs(Addr*);
  99. void freealias(Alias*);
  100. void freealiases(Alias*);
  101. Attach* mkattach(char*, char*, int);
  102. char* mkboundary(void);
  103. char* mksubject(char*);
  104. int pgpfilter(int*, int, int);
  105. int pgpopts(char*);
  106. int printcc(Biobuf*, Addr*);
  107. int printdate(Biobuf*);
  108. int printfrom(Biobuf*);
  109. int printinreplyto(Biobuf*, char*);
  110. int printsubject(Biobuf*, char*);
  111. int printto(Biobuf*, Addr*);
  112. Alias* readaliases(void);
  113. int readheaders(Biobuf*, int*, String**, Addr**, int);
  114. void readmimetypes(void);
  115. int rfc2047fmt(Fmt*);
  116. int sendmail(Addr*, Addr*, int*, char*);
  117. char* waitforsubprocs(void);
  118. int rflag, lbflag, xflag, holding, nflag, Fflag, eightflag, dflag;
  119. int pgpflag = 0;
  120. char *user;
  121. char *login;
  122. Alias *aliases;
  123. int rfc822syntaxerror;
  124. char lastchar;
  125. char *replymsg;
  126. enum
  127. {
  128. Ok = 0,
  129. Nomessage = 1,
  130. Nobody = 2,
  131. Error = -1,
  132. };
  133. #pragma varargck type "Z" char*
  134. #pragma varargck type "U" char*
  135. void
  136. usage(void)
  137. {
  138. fprint(2, "usage: %s [-Fr#xn] [-s subject] [-c ccrecipient] [-t type]"
  139. " [-aA attachment] [-p[es]] [-R replymsg] -8 | recipient-list\n",
  140. argv0);
  141. exits("usage");
  142. }
  143. void
  144. fatal(char *fmt, ...)
  145. {
  146. char buf[1024];
  147. va_list arg;
  148. if(pid >= 0)
  149. postnote(PNPROC, pid, "die");
  150. if(pgppid >= 0)
  151. postnote(PNPROC, pgppid, "die");
  152. va_start(arg, fmt);
  153. vseprint(buf, buf+sizeof(buf), fmt, arg);
  154. va_end(arg);
  155. fprint(2, "%s: %s\n", argv0, buf);
  156. holdoff(holding);
  157. exits(buf);
  158. }
  159. static void
  160. bwritesfree(Biobuf *bp, String **str)
  161. {
  162. if(Bwrite(bp, s_to_c(*str), s_len(*str)) != s_len(*str))
  163. fatal("write error");
  164. s_free(*str);
  165. *str = nil;
  166. }
  167. void
  168. main(int argc, char **argv)
  169. {
  170. int ccargc, flags, fd, noinput, headersrv;
  171. char *subject, *type, *boundary;
  172. char *ccargv[32];
  173. Addr *cc, *to;
  174. Attach *first, **l, *a;
  175. Biobuf in, out, *b;
  176. String *file, *hdrstring;
  177. noinput = 0;
  178. subject = nil;
  179. first = nil;
  180. l = &first;
  181. type = nil;
  182. hdrstring = nil;
  183. ccargc = 0;
  184. quotefmtinstall();
  185. fmtinstall('Z', doublequote);
  186. fmtinstall('U', rfc2047fmt);
  187. ARGBEGIN{
  188. case 'a':
  189. flags = 0;
  190. goto aflag;
  191. case 'A':
  192. flags = 1;
  193. aflag:
  194. a = mkattach(EARGF(usage()), type, flags);
  195. if(a == nil)
  196. exits("bad args");
  197. type = nil;
  198. *l = a;
  199. l = &a->next;
  200. break;
  201. case 'C':
  202. if(ccargc >= nelem(ccargv)-1)
  203. sysfatal("too many cc's");
  204. ccargv[ccargc++] = EARGF(usage());
  205. break;
  206. case 'd':
  207. dflag = 1; /* for sendmail */
  208. break;
  209. case 'F':
  210. Fflag = 1; /* file message */
  211. break;
  212. case 'n': /* no standard input */
  213. nflag = 1;
  214. break;
  215. case 'p': /* pgp flag: encrypt, sign, or both */
  216. if(pgpopts(EARGF(usage())) < 0)
  217. sysfatal("bad pgp options");
  218. break;
  219. case 'r':
  220. rflag = 1; /* for sendmail */
  221. break;
  222. case 'R':
  223. replymsg = EARGF(usage());
  224. break;
  225. case 's':
  226. subject = EARGF(usage());
  227. break;
  228. case 't':
  229. type = EARGF(usage());
  230. break;
  231. case 'x':
  232. xflag = 1; /* for sendmail */
  233. break;
  234. case '8': /* read recipients from rfc822 header */
  235. eightflag = 1;
  236. break;
  237. case '#':
  238. lbflag = 1; /* for sendmail */
  239. break;
  240. default:
  241. usage();
  242. break;
  243. }ARGEND;
  244. login = getlog();
  245. user = getenv("upasname");
  246. if(user == nil || *user == 0)
  247. user = login;
  248. if(user == nil || *user == 0)
  249. sysfatal("can't read user name");
  250. if(Binit(&in, 0, OREAD) < 0)
  251. sysfatal("can't Binit 0: %r");
  252. if(nflag && eightflag)
  253. sysfatal("can't use both -n and -8");
  254. if(eightflag && argc >= 1)
  255. usage();
  256. else if(!eightflag && argc < 1)
  257. usage();
  258. aliases = readaliases();
  259. if(!eightflag){
  260. to = expand(argc, argv);
  261. cc = expand(ccargc, ccargv);
  262. } else
  263. to = cc = nil;
  264. flags = 0;
  265. headersrv = Nomessage;
  266. if(!nflag && !xflag && !lbflag &&!dflag) {
  267. /*
  268. * pass through headers, keeping track of which we've seen,
  269. * perhaps building to list.
  270. */
  271. holding = holdon();
  272. headersrv = readheaders(&in, &flags, &hdrstring,
  273. eightflag? &to: nil, 1);
  274. if(rfc822syntaxerror){
  275. Bdrain(&in);
  276. fatal("rfc822 syntax error, message not sent");
  277. }
  278. if(to == nil){
  279. Bdrain(&in);
  280. fatal("no addresses found, message not sent");
  281. }
  282. switch(headersrv){
  283. case Error: /* error */
  284. fatal("reading");
  285. break;
  286. case Nomessage: /* no message, just exit mimicking old behavior */
  287. noinput = 1;
  288. if(first == nil)
  289. exits(0);
  290. break;
  291. }
  292. }
  293. fd = sendmail(to, cc, &pid, Fflag ? argv[0] : nil);
  294. if(fd < 0)
  295. sysfatal("execing sendmail: %r\n:");
  296. if(xflag || lbflag || dflag){
  297. close(fd);
  298. exits(waitforsubprocs());
  299. }
  300. if(Binit(&out, fd, OWRITE) < 0)
  301. fatal("can't Binit 1: %r");
  302. if(!nflag)
  303. bwritesfree(&out, &hdrstring);
  304. /* read user's standard headers */
  305. file = s_new();
  306. mboxpath("headers", user, file, 0);
  307. b = Bopen(s_to_c(file), OREAD);
  308. if(b != nil){
  309. if (readheaders(b, &flags, &hdrstring, nil, 0) == Error)
  310. fatal("reading");
  311. Bterm(b);
  312. bwritesfree(&out, &hdrstring);
  313. }
  314. /* add any headers we need */
  315. if((flags & (1<<Hdate)) == 0)
  316. if(printdate(&out) < 0)
  317. fatal("writing");
  318. if((flags & (1<<Hfrom)) == 0)
  319. if(printfrom(&out) < 0)
  320. fatal("writing");
  321. if((flags & (1<<Hto)) == 0)
  322. if(printto(&out, to) < 0)
  323. fatal("writing");
  324. if((flags & (1<<Hcc)) == 0)
  325. if(printcc(&out, cc) < 0)
  326. fatal("writing");
  327. if((flags & (1<<Hsubject)) == 0 && subject != nil)
  328. if(printsubject(&out, subject) < 0)
  329. fatal("writing");
  330. if(replymsg != nil)
  331. if(printinreplyto(&out, replymsg) < 0)
  332. fatal("writing");
  333. Bprint(&out, "MIME-Version: 1.0\n");
  334. if(pgpflag){
  335. /* interpose pgp process between us and sendmail to handle body */
  336. Bflush(&out);
  337. Bterm(&out);
  338. fd = pgpfilter(&pgppid, fd, pgpflag);
  339. if(Binit(&out, fd, OWRITE) < 0)
  340. fatal("can't Binit 1: %r");
  341. }
  342. /* if attachments, stick in multipart headers */
  343. boundary = nil;
  344. if(first != nil){
  345. boundary = mkboundary();
  346. Bprint(&out, "Content-Type: multipart/mixed;\n");
  347. Bprint(&out, "\tboundary=\"%s\"\n\n", boundary);
  348. Bprint(&out, "This is a multi-part message in MIME format.\n");
  349. Bprint(&out, "--%s\n", boundary);
  350. Bprint(&out, "Content-Disposition: inline\n");
  351. }
  352. if(!nflag){
  353. if(!noinput && headersrv == Ok)
  354. body(&in, &out, 1);
  355. } else
  356. Bprint(&out, "\n");
  357. holdoff(holding);
  358. Bflush(&out);
  359. for(a = first; a != nil; a = a->next){
  360. if(lastchar != '\n')
  361. Bprint(&out, "\n");
  362. Bprint(&out, "--%s\n", boundary);
  363. attachment(a, &out);
  364. }
  365. if(first != nil){
  366. if(lastchar != '\n')
  367. Bprint(&out, "\n");
  368. Bprint(&out, "--%s--\n", boundary);
  369. }
  370. Bterm(&out);
  371. close(fd);
  372. exits(waitforsubprocs());
  373. }
  374. /* evaluate pgp option string */
  375. int
  376. pgpopts(char *s)
  377. {
  378. if(s == nil || s[0] == '\0')
  379. return -1;
  380. while(*s){
  381. switch(*s++){
  382. case 's': case 'S':
  383. pgpflag |= PGPsign;
  384. break;
  385. case 'e': case 'E':
  386. pgpflag |= PGPencrypt;
  387. break;
  388. default:
  389. return -1;
  390. }
  391. }
  392. return 0;
  393. }
  394. /*
  395. * read headers from stdin into a String, expanding local aliases,
  396. * keep track of which headers are there, which addresses we have
  397. * remove Bcc: line.
  398. */
  399. int
  400. readheaders(Biobuf *in, int *fp, String **sp, Addr **top, int strict)
  401. {
  402. int i, seen, hdrtype;
  403. char *p;
  404. Addr *to;
  405. String *s, *sline;
  406. s = s_new();
  407. sline = nil;
  408. to = nil;
  409. hdrtype = -1;
  410. seen = 0;
  411. for(;;) {
  412. if((p = Brdline(in, '\n')) != nil) {
  413. seen = 1;
  414. p[Blinelen(in)-1] = 0;
  415. /* coalesce multiline headers */
  416. if((*p == ' ' || *p == '\t') && sline){
  417. s_append(sline, "\n");
  418. s_append(sline, p);
  419. p[Blinelen(in)-1] = '\n';
  420. continue;
  421. }
  422. }
  423. /* process the current header, it's all been read */
  424. if(sline) {
  425. assert(hdrtype != -1);
  426. if(top){
  427. switch(hdrtype){
  428. case Hto:
  429. case Hcc:
  430. case Hbcc:
  431. to = expandline(&sline, to);
  432. break;
  433. }
  434. }
  435. if(hdrtype == Hsubject){
  436. s_append(s, mksubject(s_to_c(sline)));
  437. s_append(s, "\n");
  438. }else if(top==nil || hdrtype!=Hbcc){
  439. s_append(s, s_to_c(sline));
  440. s_append(s, "\n");
  441. }
  442. s_free(sline);
  443. sline = nil;
  444. }
  445. if(p == nil)
  446. break;
  447. /* if no :, it's not a header, seek back and break */
  448. if(strchr(p, ':') == nil){
  449. p[Blinelen(in)-1] = '\n';
  450. Bseek(in, -Blinelen(in), 1);
  451. break;
  452. }
  453. sline = s_copy(p);
  454. /*
  455. * classify the header. If we don't recognize it, break.
  456. * This is to take care of users who start messages with
  457. * lines that contain ':'s but that aren't headers.
  458. * This is a bit hokey. Since I decided to let users type
  459. * headers, I need some way to distinguish. Therefore,
  460. * marshal tries to know all likely headers and will indeed
  461. * screw up if the user types an unlikely one. -- presotto
  462. */
  463. hdrtype = -1;
  464. for(i = 0; i < nelem(hdrs); i++){
  465. if(cistrncmp(hdrs[i], p, strlen(hdrs[i])) == 0){
  466. *fp |= 1<<i;
  467. hdrtype = i;
  468. break;
  469. }
  470. }
  471. if(strict){
  472. if(hdrtype == -1){
  473. p[Blinelen(in)-1] = '\n';
  474. Bseek(in, -Blinelen(in), 1);
  475. break;
  476. }
  477. } else
  478. hdrtype = 0;
  479. p[Blinelen(in)-1] = '\n';
  480. }
  481. *sp = s;
  482. if(top)
  483. *top = to;
  484. if(seen == 0){
  485. if(Blinelen(in) == 0)
  486. return Nomessage;
  487. else
  488. return Ok;
  489. }
  490. if(p == nil)
  491. return Nobody;
  492. return Ok;
  493. }
  494. /* pass the body to sendmail, make sure body starts and ends with a newline */
  495. void
  496. body(Biobuf *in, Biobuf *out, int docontenttype)
  497. {
  498. char *buf, *p;
  499. int i, n, len;
  500. n = 0;
  501. len = 16*1024;
  502. buf = emalloc(len);
  503. /* first char must be newline */
  504. i = Bgetc(in);
  505. if(i > 0){
  506. if(i != '\n')
  507. buf[n++] = '\n';
  508. buf[n++] = i;
  509. } else
  510. buf[n++] = '\n';
  511. /* read into memory */
  512. if(docontenttype){
  513. while(docontenttype){
  514. if(n == len){
  515. len += len >> 2;
  516. buf = realloc(buf, len);
  517. if(buf == nil)
  518. sysfatal("%r");
  519. }
  520. p = buf+n;
  521. i = Bread(in, p, len - n);
  522. if(i < 0)
  523. fatal("input error2");
  524. if(i == 0)
  525. break;
  526. n += i;
  527. for(; i > 0; i--)
  528. if((*p++ & 0x80) && docontenttype){
  529. Bprint(out, "Content-Type: text/plain; charset=\"UTF-8\"\n");
  530. Bprint(out, "Content-Transfer-Encoding: 8bit\n");
  531. docontenttype = 0;
  532. break;
  533. }
  534. }
  535. if(docontenttype){
  536. Bprint(out, "Content-Type: text/plain; charset=\"US-ASCII\"\n");
  537. Bprint(out, "Content-Transfer-Encoding: 7bit\n");
  538. }
  539. }
  540. /* write what we already read */
  541. if(Bwrite(out, buf, n) < 0)
  542. fatal("output error");
  543. if(n > 0)
  544. lastchar = buf[n-1];
  545. else
  546. lastchar = '\n';
  547. /* pass the rest */
  548. for(;;){
  549. n = Bread(in, buf, len);
  550. if(n < 0)
  551. fatal("input error2");
  552. if(n == 0)
  553. break;
  554. if(Bwrite(out, buf, n) < 0)
  555. fatal("output error");
  556. lastchar = buf[n-1];
  557. }
  558. }
  559. /*
  560. * pass the body to sendmail encoding with base64
  561. *
  562. * the size of buf is very important to enc64. Anything other than
  563. * a multiple of 3 will cause enc64 to output a termination sequence.
  564. * To ensure that a full buf corresponds to a multiple of complete lines,
  565. * we make buf a multiple of 3*18 since that's how many enc64 sticks on
  566. * a single line. This avoids short lines in the output which is pleasing
  567. * but not necessary.
  568. */
  569. void
  570. body64(Biobuf *in, Biobuf *out)
  571. {
  572. int m, n;
  573. uchar buf[3*18*54];
  574. char obuf[3*18*54*2];
  575. Bprint(out, "\n");
  576. for(;;){
  577. n = Bread(in, buf, sizeof(buf));
  578. if(n < 0)
  579. fatal("input error");
  580. if(n == 0)
  581. break;
  582. m = enc64(obuf, sizeof(obuf), buf, n);
  583. if(Bwrite(out, obuf, m) < 0)
  584. fatal("output error");
  585. }
  586. lastchar = '\n';
  587. }
  588. /* pass message to sendmail, make sure body starts with a newline */
  589. void
  590. copy(Biobuf *in, Biobuf *out)
  591. {
  592. int n;
  593. char buf[4*1024];
  594. for(;;){
  595. n = Bread(in, buf, sizeof(buf));
  596. if(n < 0)
  597. fatal("input error");
  598. if(n == 0)
  599. break;
  600. if(Bwrite(out, buf, n) < 0)
  601. fatal("output error");
  602. }
  603. }
  604. void
  605. attachment(Attach *a, Biobuf *out)
  606. {
  607. Biobuf *f;
  608. char *p;
  609. /* if it's already mime encoded, just copy */
  610. if(strcmp(a->type, "mime") == 0){
  611. f = Bopen(a->path, OREAD);
  612. if(f == nil){
  613. /*
  614. * hack: give marshal time to stdin, before we kill it
  615. * (for dead.letter)
  616. */
  617. sleep(500);
  618. postnote(PNPROC, pid, "interrupt");
  619. sysfatal("opening %s: %r", a->path);
  620. }
  621. copy(f, out);
  622. Bterm(f);
  623. }
  624. /* if it's not already mime encoded ... */
  625. if(strcmp(a->type, "text/plain") != 0)
  626. Bprint(out, "Content-Type: %s\n", a->type);
  627. if(a->ainline)
  628. Bprint(out, "Content-Disposition: inline\n");
  629. else {
  630. p = strrchr(a->path, '/');
  631. if(p == nil)
  632. p = a->path;
  633. else
  634. p++;
  635. Bprint(out, "Content-Disposition: attachment; filename=%Z\n", p);
  636. }
  637. f = Bopen(a->path, OREAD);
  638. if(f == nil){
  639. /*
  640. * hack: give marshal time to stdin, before we kill it
  641. * (for dead.letter)
  642. */
  643. sleep(500);
  644. postnote(PNPROC, pid, "interrupt");
  645. sysfatal("opening %s: %r", a->path);
  646. }
  647. /* dump our local 'From ' line when passing along mail messages */
  648. if(strcmp(a->type, "message/rfc822") == 0){
  649. p = Brdline(f, '\n');
  650. if(strncmp(p, "From ", 5) != 0)
  651. Bseek(f, 0, 0);
  652. }
  653. if(a->ctype->display)
  654. body(f, out, strcmp(a->type, "text/plain") == 0);
  655. else {
  656. Bprint(out, "Content-Transfer-Encoding: base64\n");
  657. body64(f, out);
  658. }
  659. Bterm(f);
  660. }
  661. char *ascwday[] =
  662. {
  663. "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
  664. };
  665. char *ascmon[] =
  666. {
  667. "Jan", "Feb", "Mar", "Apr", "May", "Jun",
  668. "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
  669. };
  670. int
  671. printdate(Biobuf *b)
  672. {
  673. int tz;
  674. Tm *tm;
  675. tm = localtime(time(0));
  676. tz = (tm->tzoff/3600)*100 + (tm->tzoff/60)%60;
  677. return Bprint(b, "Date: %s, %d %s %d %2.2d:%2.2d:%2.2d %s%.4d\n",
  678. ascwday[tm->wday], tm->mday, ascmon[tm->mon], 1900 + tm->year,
  679. tm->hour, tm->min, tm->sec, tz>=0?"+":"", tz);
  680. }
  681. int
  682. printfrom(Biobuf *b)
  683. {
  684. return Bprint(b, "From: %s\n", user);
  685. }
  686. int
  687. printto(Biobuf *b, Addr *a)
  688. {
  689. int i;
  690. if(Bprint(b, "To: %s", a->v) < 0)
  691. return -1;
  692. i = 0;
  693. for(a = a->next; a != nil; a = a->next)
  694. if(Bprint(b, "%s%s", ((i++ & 7) == 7)?",\n\t":", ", a->v) < 0)
  695. return -1;
  696. if(Bprint(b, "\n") < 0)
  697. return -1;
  698. return 0;
  699. }
  700. int
  701. printcc(Biobuf *b, Addr *a)
  702. {
  703. int i;
  704. if(a == nil)
  705. return 0;
  706. if(Bprint(b, "CC: %s", a->v) < 0)
  707. return -1;
  708. i = 0;
  709. for(a = a->next; a != nil; a = a->next)
  710. if(Bprint(b, "%s%s", ((i++ & 7) == 7)?",\n\t":", ", a->v) < 0)
  711. return -1;
  712. if(Bprint(b, "\n") < 0)
  713. return -1;
  714. return 0;
  715. }
  716. int
  717. printsubject(Biobuf *b, char *subject)
  718. {
  719. return Bprint(b, "Subject: %U\n", subject);
  720. }
  721. int
  722. printinreplyto(Biobuf *out, char *dir)
  723. {
  724. int fd, n;
  725. char buf[256];
  726. String *s = s_copy(dir);
  727. s_append(s, "/messageid");
  728. fd = open(s_to_c(s), OREAD);
  729. s_free(s);
  730. if(fd < 0)
  731. return 0;
  732. n = read(fd, buf, sizeof(buf)-1);
  733. close(fd);
  734. if(n <= 0)
  735. return 0;
  736. buf[n] = 0;
  737. return Bprint(out, "In-Reply-To: %s\n", buf);
  738. }
  739. Attach*
  740. mkattach(char *file, char *type, int ainline)
  741. {
  742. int n, pfd[2];
  743. char *p;
  744. char ftype[64];
  745. Attach *a;
  746. Ctype *c;
  747. if(file == nil)
  748. return nil;
  749. if(access(file, 4) == -1){
  750. fprint(2, "%s: %s can't read file\n", argv0, file);
  751. return nil;
  752. }
  753. a = emalloc(sizeof(*a));
  754. a->path = file;
  755. a->next = nil;
  756. a->type = type;
  757. a->ainline = ainline;
  758. a->ctype = nil;
  759. if(type != nil){
  760. for(c = ctype; ; c++)
  761. if(strncmp(type, c->type, strlen(c->type)) == 0){
  762. a->ctype = c;
  763. break;
  764. }
  765. return a;
  766. }
  767. /* pick a type depending on extension */
  768. p = strchr(file, '.');
  769. if(p != nil)
  770. p++;
  771. /* check the builtin extensions */
  772. if(p != nil){
  773. for(c = ctype; c->ext != nil; c++)
  774. if(strcmp(p, c->ext) == 0){
  775. a->type = c->type;
  776. a->ctype = c;
  777. return a;
  778. }
  779. }
  780. /* try the mime types file */
  781. if(p != nil){
  782. if(mimetypes == nil)
  783. readmimetypes();
  784. for(c = mimetypes; c != nil && c->ext != nil; c++)
  785. if(strcmp(p, c->ext) == 0){
  786. a->type = c->type;
  787. a->ctype = c;
  788. return a;
  789. }
  790. }
  791. /* run file to figure out the type */
  792. a->type = "application/octet-stream"; /* safest default */
  793. if(pipe(pfd) < 0)
  794. return a;
  795. switch(fork()){
  796. case -1:
  797. break;
  798. case 0:
  799. close(pfd[1]);
  800. close(0);
  801. dup(pfd[0], 0);
  802. close(1);
  803. dup(pfd[0], 1);
  804. execl("/bin/file", "file", "-m", file, nil);
  805. exits(0);
  806. default:
  807. close(pfd[0]);
  808. n = read(pfd[1], ftype, sizeof(ftype));
  809. if(n > 0){
  810. ftype[n-1] = 0;
  811. a->type = estrdup(ftype);
  812. }
  813. close(pfd[1]);
  814. waitpid();
  815. break;
  816. }
  817. for(c = ctype; ; c++)
  818. if(strncmp(a->type, c->type, strlen(c->type)) == 0){
  819. a->ctype = c;
  820. break;
  821. }
  822. return a;
  823. }
  824. char*
  825. mkboundary(void)
  826. {
  827. int i;
  828. char buf[32];
  829. srand((time(0)<<16)|getpid());
  830. strcpy(buf, "upas-");
  831. for(i = 5; i < sizeof(buf)-1; i++)
  832. buf[i] = 'a' + nrand(26);
  833. buf[i] = 0;
  834. return estrdup(buf);
  835. }
  836. /* copy types to two fd's */
  837. static void
  838. tee(int in, int out1, int out2)
  839. {
  840. int n;
  841. char buf[8*1024];
  842. while ((n = read(in, buf, sizeof buf)) > 0)
  843. if (write(out1, buf, n) != n ||
  844. write(out2, buf, n) != n)
  845. break;
  846. }
  847. /* print the unix from line */
  848. int
  849. printunixfrom(int fd)
  850. {
  851. int tz;
  852. Tm *tm;
  853. tm = localtime(time(0));
  854. tz = (tm->tzoff/3600)*100 + (tm->tzoff/60)%60;
  855. return fprint(fd, "From %s %s %s %d %2.2d:%2.2d:%2.2d %s%.4d %d\n",
  856. user,
  857. ascwday[tm->wday], ascmon[tm->mon], tm->mday,
  858. tm->hour, tm->min, tm->sec, tz>=0?"+":"", tz, 1900 + tm->year);
  859. }
  860. char *specialfile[] =
  861. {
  862. "pipeto",
  863. "pipefrom",
  864. "L.mbox",
  865. "forward",
  866. "names"
  867. };
  868. /* return 1 if this is a special file */
  869. static int
  870. special(String *s)
  871. {
  872. int i;
  873. char *p;
  874. p = strrchr(s_to_c(s), '/');
  875. if(p == nil)
  876. p = s_to_c(s);
  877. else
  878. p++;
  879. for(i = 0; i < nelem(specialfile); i++)
  880. if(strcmp(p, specialfile[i]) == 0)
  881. return 1;
  882. return 0;
  883. }
  884. /* open the folder using the recipients account name */
  885. static int
  886. openfolder(char *rcvr)
  887. {
  888. int c, fd, scarey;
  889. char *p;
  890. Dir *d;
  891. String *file;
  892. file = s_new();
  893. mboxpath("f", user, file, 0);
  894. /* if $mail/f exists, store there, otherwise in $mail */
  895. d = dirstat(s_to_c(file));
  896. if(d == nil || d->qid.type != QTDIR){
  897. scarey = 1;
  898. file->ptr -= 1;
  899. } else {
  900. s_putc(file, '/');
  901. scarey = 0;
  902. }
  903. free(d);
  904. p = strrchr(rcvr, '!');
  905. if(p != nil)
  906. rcvr = p+1;
  907. while(*rcvr && *rcvr != '@'){
  908. c = *rcvr++;
  909. if(c == '/')
  910. c = '_';
  911. s_putc(file, c);
  912. }
  913. s_terminate(file);
  914. if(scarey && special(file)){
  915. fprint(2, "%s: won't overwrite %s\n", argv0, s_to_c(file));
  916. s_free(file);
  917. return -1;
  918. }
  919. fd = open(s_to_c(file), OWRITE);
  920. if(fd < 0)
  921. fd = create(s_to_c(file), OWRITE, 0660);
  922. s_free(file);
  923. return fd;
  924. }
  925. /* start up sendmail and return an fd to talk to it with */
  926. int
  927. sendmail(Addr *to, Addr *cc, int *pid, char *rcvr)
  928. {
  929. int ac, fd;
  930. int pfd[2];
  931. char **av, **v;
  932. Addr *a;
  933. String *cmd;
  934. fd = -1;
  935. if(rcvr != nil)
  936. fd = openfolder(rcvr);
  937. ac = 0;
  938. for(a = to; a != nil; a = a->next)
  939. ac++;
  940. for(a = cc; a != nil; a = a->next)
  941. ac++;
  942. v = av = emalloc(sizeof(char*)*(ac+20));
  943. ac = 0;
  944. v[ac++] = "sendmail";
  945. if(xflag)
  946. v[ac++] = "-x";
  947. if(rflag)
  948. v[ac++] = "-r";
  949. if(lbflag)
  950. v[ac++] = "-#";
  951. if(dflag)
  952. v[ac++] = "-d";
  953. for(a = to; a != nil; a = a->next)
  954. v[ac++] = a->v;
  955. for(a = cc; a != nil; a = a->next)
  956. v[ac++] = a->v;
  957. v[ac] = 0;
  958. if(pipe(pfd) < 0)
  959. fatal("%r");
  960. switch(*pid = rfork(RFFDG|RFREND|RFPROC|RFENVG)){
  961. case -1:
  962. fatal("%r");
  963. break;
  964. case 0:
  965. if(holding)
  966. close(holding);
  967. close(pfd[1]);
  968. dup(pfd[0], 0);
  969. close(pfd[0]);
  970. if(rcvr != nil){
  971. if(pipe(pfd) < 0)
  972. fatal("%r");
  973. switch(fork()){
  974. case -1:
  975. fatal("%r");
  976. break;
  977. case 0:
  978. close(pfd[0]);
  979. seek(fd, 0, 2);
  980. printunixfrom(fd);
  981. tee(0, pfd[1], fd);
  982. write(fd, "\n", 1);
  983. exits(0);
  984. default:
  985. close(fd);
  986. close(pfd[1]);
  987. dup(pfd[0], 0);
  988. break;
  989. }
  990. }
  991. if(replymsg != nil)
  992. putenv("replymsg", replymsg);
  993. cmd = mboxpath("pipefrom", login, s_new(), 0);
  994. exec(s_to_c(cmd), av);
  995. exec("/bin/myupassend", av);
  996. exec("/bin/upas/send", av);
  997. fatal("execing: %r");
  998. break;
  999. default:
  1000. if(rcvr != nil)
  1001. close(fd);
  1002. close(pfd[0]);
  1003. break;
  1004. }
  1005. return pfd[1];
  1006. }
  1007. /*
  1008. * start up pgp process and return an fd to talk to it with.
  1009. * its standard output will be the original fd, which goes to sendmail.
  1010. */
  1011. int
  1012. pgpfilter(int *pid, int fd, int pgpflag)
  1013. {
  1014. int ac;
  1015. int pfd[2];
  1016. char **av, **v;
  1017. v = av = emalloc(sizeof(char*)*8);
  1018. ac = 0;
  1019. v[ac++] = "pgp";
  1020. v[ac++] = "-fat"; /* operate as a filter, generate text */
  1021. if(pgpflag & PGPsign)
  1022. v[ac++] = "-s";
  1023. if(pgpflag & PGPencrypt)
  1024. v[ac++] = "-e";
  1025. v[ac] = 0;
  1026. if(pipe(pfd) < 0)
  1027. fatal("%r");
  1028. switch(*pid = fork()){
  1029. case -1:
  1030. fatal("%r");
  1031. break;
  1032. case 0:
  1033. close(pfd[1]);
  1034. dup(pfd[0], 0);
  1035. close(pfd[0]);
  1036. dup(fd, 1);
  1037. close(fd);
  1038. /* add newline to avoid confusing pgp output with 822 headers */
  1039. write(1, "\n", 1);
  1040. exec("/bin/pgp", av);
  1041. fatal("execing: %r");
  1042. break;
  1043. default:
  1044. close(pfd[0]);
  1045. break;
  1046. }
  1047. close(fd);
  1048. return pfd[1];
  1049. }
  1050. /* wait for sendmail and pgp to exit; exit here if either failed */
  1051. char*
  1052. waitforsubprocs(void)
  1053. {
  1054. Waitmsg *w;
  1055. char *err;
  1056. err = nil;
  1057. while((w = wait()) != nil){
  1058. if(w->pid == pid || w->pid == pgppid)
  1059. if(w->msg[0] != 0)
  1060. err = estrdup(w->msg);
  1061. free(w);
  1062. }
  1063. if(err)
  1064. exits(err);
  1065. return nil;
  1066. }
  1067. int
  1068. cistrncmp(char *a, char *b, int n)
  1069. {
  1070. while(n-- > 0)
  1071. if(tolower(*a++) != tolower(*b++))
  1072. return -1;
  1073. return 0;
  1074. }
  1075. int
  1076. cistrcmp(char *a, char *b)
  1077. {
  1078. for(;;){
  1079. if(tolower(*a) != tolower(*b++))
  1080. return -1;
  1081. if(*a++ == 0)
  1082. break;
  1083. }
  1084. return 0;
  1085. }
  1086. static uchar t64d[256];
  1087. static char t64e[64];
  1088. static void
  1089. init64(void)
  1090. {
  1091. int c, i;
  1092. memset(t64d, 255, 256);
  1093. memset(t64e, '=', 64);
  1094. i = 0;
  1095. for(c = 'A'; c <= 'Z'; c++){
  1096. t64e[i] = c;
  1097. t64d[c] = i++;
  1098. }
  1099. for(c = 'a'; c <= 'z'; c++){
  1100. t64e[i] = c;
  1101. t64d[c] = i++;
  1102. }
  1103. for(c = '0'; c <= '9'; c++){
  1104. t64e[i] = c;
  1105. t64d[c] = i++;
  1106. }
  1107. t64e[i] = '+';
  1108. t64d['+'] = i++;
  1109. t64e[i] = '/';
  1110. t64d['/'] = i;
  1111. }
  1112. int
  1113. enc64(char *out, int lim, uchar *in, int n)
  1114. {
  1115. int i;
  1116. ulong b24;
  1117. char *start = out;
  1118. char *e = out + lim;
  1119. if(t64e[0] == 0)
  1120. init64();
  1121. for(i = 0; i < n/3; i++){
  1122. b24 = (*in++)<<16;
  1123. b24 |= (*in++)<<8;
  1124. b24 |= *in++;
  1125. if(out + 5 >= e)
  1126. goto exhausted;
  1127. *out++ = t64e[(b24>>18)];
  1128. *out++ = t64e[(b24>>12)&0x3f];
  1129. *out++ = t64e[(b24>>6)&0x3f];
  1130. *out++ = t64e[(b24)&0x3f];
  1131. if((i%18) == 17)
  1132. *out++ = '\n';
  1133. }
  1134. switch(n%3){
  1135. case 2:
  1136. b24 = (*in++)<<16;
  1137. b24 |= (*in)<<8;
  1138. if(out + 4 >= e)
  1139. goto exhausted;
  1140. *out++ = t64e[(b24>>18)];
  1141. *out++ = t64e[(b24>>12)&0x3f];
  1142. *out++ = t64e[(b24>>6)&0x3f];
  1143. break;
  1144. case 1:
  1145. b24 = (*in)<<16;
  1146. if(out + 4 >= e)
  1147. goto exhausted;
  1148. *out++ = t64e[(b24>>18)];
  1149. *out++ = t64e[(b24>>12)&0x3f];
  1150. *out++ = '=';
  1151. break;
  1152. case 0:
  1153. if((i%18) != 0)
  1154. *out++ = '\n';
  1155. *out = 0;
  1156. return out - start;
  1157. }
  1158. exhausted:
  1159. *out++ = '=';
  1160. *out++ = '\n';
  1161. *out = 0;
  1162. return out - start;
  1163. }
  1164. void
  1165. freealias(Alias *a)
  1166. {
  1167. freeaddrs(a->addr);
  1168. free(a);
  1169. }
  1170. void
  1171. freealiases(Alias *a)
  1172. {
  1173. Alias *next;
  1174. while(a != nil){
  1175. next = a->next;
  1176. freealias(a);
  1177. a = next;
  1178. }
  1179. }
  1180. /*
  1181. * read alias file
  1182. */
  1183. Alias*
  1184. readaliases(void)
  1185. {
  1186. Addr *addr, **al;
  1187. Alias *a, **l, *first;
  1188. Sinstack *sp;
  1189. String *file, *line, *token;
  1190. static int already;
  1191. first = nil;
  1192. file = s_new();
  1193. line = s_new();
  1194. token = s_new();
  1195. /* open and get length */
  1196. mboxpath("names", login, file, 0);
  1197. sp = s_allocinstack(s_to_c(file));
  1198. if(sp == nil)
  1199. goto out;
  1200. l = &first;
  1201. /* read a line at a time. */
  1202. while(s_rdinstack(sp, s_restart(line))!=nil) {
  1203. s_restart(line);
  1204. a = emalloc(sizeof(Alias));
  1205. al = &a->addr;
  1206. while(s_parse(line, s_restart(token)) != 0) {
  1207. addr = emalloc(sizeof(Addr));
  1208. addr->v = strdup(s_to_c(token));
  1209. addr->next = 0;
  1210. *al = addr;
  1211. al = &addr->next;
  1212. }
  1213. if(a->addr == nil || a->addr->next == nil){
  1214. freealias(a);
  1215. continue;
  1216. }
  1217. a->next = nil;
  1218. *l = a;
  1219. l = &a->next;
  1220. }
  1221. s_freeinstack(sp);
  1222. out:
  1223. s_free(file);
  1224. s_free(line);
  1225. s_free(token);
  1226. return first;
  1227. }
  1228. Addr*
  1229. newaddr(char *name)
  1230. {
  1231. Addr *a;
  1232. a = emalloc(sizeof(*a));
  1233. a->next = nil;
  1234. a->v = estrdup(name);
  1235. if(a->v == nil)
  1236. sysfatal("%r");
  1237. return a;
  1238. }
  1239. /*
  1240. * expand personal aliases since the names are meaningless in
  1241. * other contexts
  1242. */
  1243. Addr*
  1244. _expand(Addr *old, int *changedp)
  1245. {
  1246. Addr *first, *next, **l, *a;
  1247. Alias *al;
  1248. *changedp = 0;
  1249. first = nil;
  1250. l = &first;
  1251. for(;old != nil; old = next){
  1252. next = old->next;
  1253. for(al = aliases; al != nil; al = al->next){
  1254. if(strcmp(al->addr->v, old->v) == 0){
  1255. for(a = al->addr->next; a != nil; a = a->next){
  1256. *l = newaddr(a->v);
  1257. if(*l == nil)
  1258. sysfatal("%r");
  1259. l = &(*l)->next;
  1260. *changedp = 1;
  1261. }
  1262. break;
  1263. }
  1264. }
  1265. if(al != nil){
  1266. freeaddr(old);
  1267. continue;
  1268. }
  1269. *l = old;
  1270. old->next = nil;
  1271. l = &(*l)->next;
  1272. }
  1273. return first;
  1274. }
  1275. Addr*
  1276. rexpand(Addr *old)
  1277. {
  1278. int i, changed;
  1279. changed = 0;
  1280. for(i = 0; i < 32; i++){
  1281. old = _expand(old, &changed);
  1282. if(changed == 0)
  1283. break;
  1284. }
  1285. return old;
  1286. }
  1287. Addr*
  1288. unique(Addr *first)
  1289. {
  1290. Addr *a, **l, *x;
  1291. for(a = first; a != nil; a = a->next){
  1292. for(l = &a->next; *l != nil;){
  1293. if(strcmp(a->v, (*l)->v) == 0){
  1294. x = *l;
  1295. *l = x->next;
  1296. freeaddr(x);
  1297. } else
  1298. l = &(*l)->next;
  1299. }
  1300. }
  1301. return first;
  1302. }
  1303. Addr*
  1304. expand(int ac, char **av)
  1305. {
  1306. int i;
  1307. Addr *first, **l;
  1308. first = nil;
  1309. /* make a list of the starting addresses */
  1310. l = &first;
  1311. for(i = 0; i < ac; i++){
  1312. *l = newaddr(av[i]);
  1313. if(*l == nil)
  1314. sysfatal("%r");
  1315. l = &(*l)->next;
  1316. }
  1317. /* recurse till we don't change any more */
  1318. return unique(rexpand(first));
  1319. }
  1320. Addr*
  1321. concataddr(Addr *a, Addr *b)
  1322. {
  1323. Addr *oa;
  1324. if(a == nil)
  1325. return b;
  1326. oa = a;
  1327. for(; a->next; a=a->next)
  1328. ;
  1329. a->next = b;
  1330. return oa;
  1331. }
  1332. void
  1333. freeaddr(Addr *ap)
  1334. {
  1335. free(ap->v);
  1336. free(ap);
  1337. }
  1338. void
  1339. freeaddrs(Addr *ap)
  1340. {
  1341. Addr *next;
  1342. for(; ap; ap=next) {
  1343. next = ap->next;
  1344. freeaddr(ap);
  1345. }
  1346. }
  1347. String*
  1348. s_copyn(char *s, int n)
  1349. {
  1350. return s_nappend(s_reset(nil), s, n);
  1351. }
  1352. /*
  1353. * fetch the next token from an RFC822 address string
  1354. * we assume the header is RFC822-conformant in that
  1355. * we recognize escaping anywhere even though it is only
  1356. * supposed to be in quoted-strings, domain-literals, and comments.
  1357. *
  1358. * i'd use yylex or yyparse here, but we need to preserve
  1359. * things like comments, which i think it tosses away.
  1360. *
  1361. * we're not strictly RFC822 compliant. we misparse such nonsense as
  1362. *
  1363. * To: gre @ (Grace) plan9 . (Emlin) bell-labs.com
  1364. *
  1365. * make sure there's no whitespace in your addresses and
  1366. * you'll be fine.
  1367. */
  1368. enum {
  1369. Twhite,
  1370. Tcomment,
  1371. Twords,
  1372. Tcomma,
  1373. Tleftangle,
  1374. Trightangle,
  1375. Terror,
  1376. Tend,
  1377. };
  1378. // char *ty82[] = {"white", "comment", "words", "comma", "<", ">", "err", "end"};
  1379. #define ISWHITE(p) ((p)==' ' || (p)=='\t' || (p)=='\n' || (p)=='\r')
  1380. int
  1381. get822token(String **tok, char *p, char **pp)
  1382. {
  1383. int type, quoting;
  1384. char *op;
  1385. op = p;
  1386. switch(*p){
  1387. case '\0':
  1388. *tok = nil;
  1389. *pp = nil;
  1390. return Tend;
  1391. case ' ': /* get whitespace */
  1392. case '\t':
  1393. case '\n':
  1394. case '\r':
  1395. type = Twhite;
  1396. while(ISWHITE(*p))
  1397. p++;
  1398. break;
  1399. case '(': /* get comment */
  1400. type = Tcomment;
  1401. for(p++; *p && *p != ')'; p++)
  1402. if(*p == '\\') {
  1403. if(*(p+1) == '\0') {
  1404. *tok = nil;
  1405. return Terror;
  1406. }
  1407. p++;
  1408. }
  1409. if(*p != ')') {
  1410. *tok = nil;
  1411. return Terror;
  1412. }
  1413. p++;
  1414. break;
  1415. case ',':
  1416. type = Tcomma;
  1417. p++;
  1418. break;
  1419. case '<':
  1420. type = Tleftangle;
  1421. p++;
  1422. break;
  1423. case '>':
  1424. type = Trightangle;
  1425. p++;
  1426. break;
  1427. default: /* bunch of letters, perhaps quoted strings tossed in */
  1428. type = Twords;
  1429. quoting = 0;
  1430. for (; *p && (quoting ||
  1431. (!ISWHITE(*p) && *p != '>' && *p != '<' && *p != ',')); p++) {
  1432. if(*p == '"')
  1433. quoting = !quoting;
  1434. if(*p == '\\') {
  1435. if(*(p+1) == '\0') {
  1436. *tok = nil;
  1437. return Terror;
  1438. }
  1439. p++;
  1440. }
  1441. }
  1442. break;
  1443. }
  1444. if(pp)
  1445. *pp = p;
  1446. *tok = s_copyn(op, p-op);
  1447. return type;
  1448. }
  1449. /*
  1450. * expand local aliases in an RFC822 mail line
  1451. * add list of expanded addresses to to.
  1452. */
  1453. Addr*
  1454. expandline(String **s, Addr *to)
  1455. {
  1456. int tok, inangle, hadangle, nword;
  1457. char *p;
  1458. Addr *na, *nto, *ap;
  1459. String *os, *ns, *stok, *lastword, *sinceword;
  1460. os = s_copy(s_to_c(*s));
  1461. p = strchr(s_to_c(*s), ':');
  1462. assert(p != nil);
  1463. p++;
  1464. ns = s_copyn(s_to_c(*s), p-s_to_c(*s));
  1465. stok = nil;
  1466. nto = nil;
  1467. /*
  1468. * the only valid mailbox namings are word
  1469. * and word* < addr >
  1470. * without comments this would be simple.
  1471. * we keep the following:
  1472. * lastword - current guess at the address
  1473. * sinceword - whitespace and comment seen since lastword
  1474. */
  1475. lastword = s_new();
  1476. sinceword = s_new();
  1477. inangle = 0;
  1478. nword = 0;
  1479. hadangle = 0;
  1480. for(;;) {
  1481. stok = nil;
  1482. switch(tok = get822token(&stok, p, &p)){
  1483. default:
  1484. abort();
  1485. case Tcomma:
  1486. case Tend:
  1487. if(inangle)
  1488. goto Error;
  1489. if(nword != 1)
  1490. goto Error;
  1491. na = rexpand(newaddr(s_to_c(lastword)));
  1492. s_append(ns, na->v);
  1493. s_append(ns, s_to_c(sinceword));
  1494. for(ap=na->next; ap; ap=ap->next) {
  1495. s_append(ns, ", ");
  1496. s_append(ns, ap->v);
  1497. }
  1498. nto = concataddr(na, nto);
  1499. if(tok == Tcomma){
  1500. s_append(ns, ",");
  1501. s_free(stok);
  1502. }
  1503. if(tok == Tend)
  1504. goto Break2;
  1505. inangle = 0;
  1506. nword = 0;
  1507. hadangle = 0;
  1508. s_reset(sinceword);
  1509. s_reset(lastword);
  1510. break;
  1511. case Twhite:
  1512. case Tcomment:
  1513. s_append(sinceword, s_to_c(stok));
  1514. s_free(stok);
  1515. break;
  1516. case Trightangle:
  1517. if(!inangle)
  1518. goto Error;
  1519. inangle = 0;
  1520. hadangle = 1;
  1521. s_append(sinceword, s_to_c(stok));
  1522. s_free(stok);
  1523. break;
  1524. case Twords:
  1525. case Tleftangle:
  1526. if(hadangle)
  1527. goto Error;
  1528. if(tok != Tleftangle && inangle && s_len(lastword))
  1529. goto Error;
  1530. if(tok == Tleftangle) {
  1531. inangle = 1;
  1532. nword = 1;
  1533. }
  1534. s_append(ns, s_to_c(lastword));
  1535. s_append(ns, s_to_c(sinceword));
  1536. s_reset(sinceword);
  1537. if(tok == Tleftangle) {
  1538. s_append(ns, "<");
  1539. s_reset(lastword);
  1540. } else {
  1541. s_free(lastword);
  1542. lastword = stok;
  1543. }
  1544. if(!inangle)
  1545. nword++;
  1546. break;
  1547. case Terror: /* give up, use old string, addrs */
  1548. Error:
  1549. ns = os;
  1550. os = nil;
  1551. freeaddrs(nto);
  1552. nto = nil;
  1553. werrstr("rfc822 syntax error");
  1554. rfc822syntaxerror = 1;
  1555. goto Break2;
  1556. }
  1557. }
  1558. Break2:
  1559. s_free(*s);
  1560. s_free(os);
  1561. *s = ns;
  1562. nto = concataddr(nto, to);
  1563. return nto;
  1564. }
  1565. void
  1566. Bdrain(Biobuf *b)
  1567. {
  1568. char buf[8192];
  1569. while(Bread(b, buf, sizeof buf) > 0)
  1570. ;
  1571. }
  1572. void
  1573. readmimetypes(void)
  1574. {
  1575. char *p;
  1576. char type[256];
  1577. char *f[6];
  1578. Biobuf *b;
  1579. static int alloced, inuse;
  1580. if(mimetypes == 0){
  1581. alloced = 256;
  1582. mimetypes = emalloc(alloced*sizeof(Ctype));
  1583. mimetypes[0].ext = "";
  1584. }
  1585. b = Bopen("/sys/lib/mimetype", OREAD);
  1586. if(b == nil)
  1587. return;
  1588. for(;;){
  1589. p = Brdline(b, '\n');
  1590. if(p == nil)
  1591. break;
  1592. p[Blinelen(b)-1] = 0;
  1593. if(tokenize(p, f, 6) < 4)
  1594. continue;
  1595. if (strcmp(f[0], "-") == 0 || strcmp(f[1], "-") == 0 ||
  1596. strcmp(f[2], "-") == 0)
  1597. continue;
  1598. if(inuse + 1 >= alloced){
  1599. alloced += 256;
  1600. mimetypes = erealloc(mimetypes, alloced*sizeof(Ctype));
  1601. }
  1602. snprint(type, sizeof(type), "%s/%s", f[1], f[2]);
  1603. mimetypes[inuse].type = estrdup(type);
  1604. mimetypes[inuse].ext = estrdup(f[0]+1);
  1605. mimetypes[inuse].display = !strcmp(type, "text/plain");
  1606. inuse++;
  1607. /* always make sure there's a terminator */
  1608. mimetypes[inuse].ext = 0;
  1609. }
  1610. Bterm(b);
  1611. }
  1612. char*
  1613. estrdup(char *x)
  1614. {
  1615. x = strdup(x);
  1616. if(x == nil)
  1617. fatal("memory");
  1618. return x;
  1619. }
  1620. void*
  1621. emalloc(int n)
  1622. {
  1623. void *x;
  1624. x = malloc(n);
  1625. if(x == nil)
  1626. fatal("%r");
  1627. return x;
  1628. }
  1629. void*
  1630. erealloc(void *x, int n)
  1631. {
  1632. x = realloc(x, n);
  1633. if(x == nil)
  1634. fatal("%r");
  1635. return x;
  1636. }
  1637. /*
  1638. * Formatter for %"
  1639. * Use double quotes to protect white space, frogs, \ and "
  1640. */
  1641. enum
  1642. {
  1643. Qok = 0,
  1644. Qquote,
  1645. Qbackslash,
  1646. };
  1647. static int
  1648. needtoquote(Rune r)
  1649. {
  1650. if(r >= Runeself)
  1651. return Qquote;
  1652. if(r <= ' ')
  1653. return Qquote;
  1654. if(r=='\\' || r=='"')
  1655. return Qbackslash;
  1656. return Qok;
  1657. }
  1658. int
  1659. doublequote(Fmt *f)
  1660. {
  1661. int w, quotes;
  1662. char *s, *t;
  1663. Rune r;
  1664. s = va_arg(f->args, char*);
  1665. if(s == nil || *s == '\0')
  1666. return fmtstrcpy(f, "\"\"");
  1667. quotes = 0;
  1668. for(t = s; *t; t += w){
  1669. w = chartorune(&r, t);
  1670. quotes |= needtoquote(r);
  1671. }
  1672. if(quotes == 0)
  1673. return fmtstrcpy(f, s);
  1674. fmtrune(f, '"');
  1675. for(t = s; *t; t += w){
  1676. w = chartorune(&r, t);
  1677. if(needtoquote(r) == Qbackslash)
  1678. fmtrune(f, '\\');
  1679. fmtrune(f, r);
  1680. }
  1681. return fmtrune(f, '"');
  1682. }
  1683. int
  1684. rfc2047fmt(Fmt *fmt)
  1685. {
  1686. char *s, *p;
  1687. s = va_arg(fmt->args, char*);
  1688. if(s == nil)
  1689. return fmtstrcpy(fmt, "");
  1690. for(p=s; *p; p++)
  1691. if((uchar)*p >= 0x80)
  1692. goto hard;
  1693. return fmtstrcpy(fmt, s);
  1694. hard:
  1695. fmtprint(fmt, "=?utf-8?q?");
  1696. for(p = s; *p; p++){
  1697. if(*p == ' ')
  1698. fmtrune(fmt, '_');
  1699. else if(*p == '_' || *p == '\t' || *p == '=' || *p == '?' ||
  1700. (uchar)*p >= 0x80)
  1701. fmtprint(fmt, "=%.2uX", (uchar)*p);
  1702. else
  1703. fmtrune(fmt, (uchar)*p);
  1704. }
  1705. fmtprint(fmt, "?=");
  1706. return 0;
  1707. }
  1708. char*
  1709. mksubject(char *line)
  1710. {
  1711. char *p, *q;
  1712. static char buf[1024];
  1713. p = strchr(line, ':') + 1;
  1714. while(*p == ' ')
  1715. p++;
  1716. for(q = p; *q; q++)
  1717. if((uchar)*q >= 0x80)
  1718. goto hard;
  1719. return line;
  1720. hard:
  1721. snprint(buf, sizeof buf, "Subject: %U", p);
  1722. return buf;
  1723. }