marshal.c 32 KB

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