marshal.c 32 KB

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