marshal.c 32 KB

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