marshal.c 33 KB

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