nedmail.c 42 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419
  1. #include "common.h"
  2. #include <ctype.h>
  3. #include <plumb.h>
  4. typedef struct Message Message;
  5. typedef struct Ctype Ctype;
  6. typedef struct Cmd Cmd;
  7. char root[Pathlen];
  8. char mbname[Elemlen];
  9. int rootlen;
  10. int didopen;
  11. char *user;
  12. char wd[2048];
  13. String *mbpath;
  14. int natural;
  15. int doflush;
  16. int interrupted;
  17. struct Message {
  18. Message *next;
  19. Message *prev;
  20. Message *cmd;
  21. Message *child;
  22. Message *parent;
  23. String *path;
  24. int id;
  25. int len;
  26. int fileno; // number of directory
  27. String *info;
  28. char *from;
  29. char *to;
  30. char *cc;
  31. char *replyto;
  32. char *date;
  33. char *subject;
  34. char *type;
  35. char *disposition;
  36. char *filename;
  37. char deleted;
  38. char stored;
  39. };
  40. Message top;
  41. struct Ctype {
  42. char *type;
  43. char *ext;
  44. int display;
  45. char *plumbdest;
  46. };
  47. Ctype ctype[] = {
  48. { "text/plain", "txt", 1, 0 },
  49. { "text/html", "htm", 1, 0 },
  50. { "text/html", "html", 1, 0 },
  51. { "text/tab-separated-values", "tsv", 1, 0 },
  52. { "text/richtext", "rtx", 1, 0 },
  53. { "text", "txt", 1, 0 },
  54. { "message/rfc822", "msg", 0, 0 },
  55. { "image/jpeg", "jpg", 0, "image" },
  56. { "image/gif", "gif", 0, "image" },
  57. { "application/octet-stream", "bin", 0, 0 },
  58. { "application/pdf", "pdf", 0, "postscript" },
  59. { "application/postscript", "ps", 0, "postscript" },
  60. { "application/", 0, 0, 0 },
  61. { "image/", 0, 0, 0 },
  62. { "multipart/", "mul", 0, 0 },
  63. { "", 0, 0, 0 },
  64. };
  65. Message* acmd(Cmd*, Message*);
  66. Message* bcmd(Cmd*, Message*);
  67. Message* dcmd(Cmd*, Message*);
  68. Message* eqcmd(Cmd*, Message*);
  69. Message* hcmd(Cmd*, Message*);
  70. Message* Hcmd(Cmd*, Message*);
  71. Message* helpcmd(Cmd*, Message*);
  72. Message* icmd(Cmd*, Message*);
  73. Message* pcmd(Cmd*, Message*);
  74. Message* qcmd(Cmd*, Message*);
  75. Message* rcmd(Cmd*, Message*);
  76. Message* scmd(Cmd*, Message*);
  77. Message* ucmd(Cmd*, Message*);
  78. Message* wcmd(Cmd*, Message*);
  79. Message* xcmd(Cmd*, Message*);
  80. Message* ycmd(Cmd*, Message*);
  81. Message* pipecmd(Cmd*, Message*);
  82. Message* rpipecmd(Cmd*, Message*);
  83. Message* bangcmd(Cmd*, Message*);
  84. Message* Pcmd(Cmd*, Message*);
  85. Message* mcmd(Cmd*, Message*);
  86. Message* fcmd(Cmd*, Message*);
  87. Message* quotecmd(Cmd*, Message*);
  88. struct {
  89. char *cmd;
  90. int args;
  91. Message* (*f)(Cmd*, Message*);
  92. char *help;
  93. } cmdtab[] = {
  94. { "a", 1, acmd, "a reply to sender and recipients" },
  95. { "A", 1, acmd, "A reply to sender and recipients with copy" },
  96. { "b", 0, bcmd, "b print the next 10 headers" },
  97. { "d", 0, dcmd, "d mark for deletion" },
  98. { "f", 0, fcmd, "f file message by from address" },
  99. { "h", 0, hcmd, "h print elided message summary (,h for all)" },
  100. { "help", 0, helpcmd, "help print this info" },
  101. { "H", 0, Hcmd, "H print message's MIME structure " },
  102. { "i", 0, icmd, "i incorporate new mail" },
  103. { "m", 1, mcmd, "m addr forward mail" },
  104. { "M", 1, mcmd, "M addr forward mail with message" },
  105. { "p", 0, pcmd, "p print the processed message" },
  106. { "P", 0, Pcmd, "P print the raw message" },
  107. { "\"", 0, quotecmd, "\" print a quoted version of msg" },
  108. { "q", 0, qcmd, "q exit and remove all deleted mail" },
  109. { "r", 1, rcmd, "r [addr] reply to sender plus any addrs specified" },
  110. { "rf", 1, rcmd, "rf [addr]file message and reply" },
  111. { "R", 1, rcmd, "R [addr] reply including copy of message" },
  112. { "Rf", 1, rcmd, "Rf [addr]file message and reply with copy" },
  113. { "s", 1, scmd, "s file append raw message to file" },
  114. { "u", 0, ucmd, "u remove deletion mark" },
  115. { "w", 1, wcmd, "w file store message contents as file" },
  116. { "x", 0, xcmd, "x exit without flushing deleted messages" },
  117. { "y", 0, ycmd, "y synchronize with mail box" },
  118. { "=", 1, eqcmd, "= print current message number" },
  119. { "|", 1, pipecmd, "|cmd pipe message body to a command" },
  120. { "||", 1, rpipecmd, "|cmd pipe raw message to a command" },
  121. { "!", 1, bangcmd, "!cmd run a command" },
  122. { nil, 0, nil, nil },
  123. };
  124. enum
  125. {
  126. NARG= 32,
  127. };
  128. struct Cmd {
  129. Message *msgs;
  130. Message *(*f)(Cmd*, Message*);
  131. int an;
  132. char *av[NARG];
  133. int delete;
  134. };
  135. Biobuf out;
  136. int startedfs;
  137. int reverse;
  138. int longestfrom = 12;
  139. String* file2string(String*, char*);
  140. int dir2message(Message*, int);
  141. int filelen(String*, char*);
  142. String* extendpath(String*, char*);
  143. void snprintheader(char*, int, Message*);
  144. void cracktime(char*, char*, int);
  145. int cistrncmp(char*, char*, int);
  146. int cistrcmp(char*, char*);
  147. Reprog* parsesearch(char**);
  148. char* parseaddr(char**, Message*, Message*, Message*, Message**);
  149. char* parsecmd(char*, Cmd*, Message*, Message*);
  150. char* readline(char*, char*, int);
  151. void messagecount(Message*);
  152. void system(char*, char**, int);
  153. void mkid(String*, Message*);
  154. int switchmb(char*, char*);
  155. void closemb(void);
  156. int lineize(char*, char**, int);
  157. int rawsearch(Message*, Reprog*);
  158. Message* dosingleton(Message*, char*);
  159. String* rooted(String*);
  160. int plumb(Message*, Ctype*);
  161. String* addrecolon(char*);
  162. void exitfs(char*);
  163. Message* flushdeleted(Message*);
  164. void
  165. usage(void)
  166. {
  167. fprint(2, "usage: %s [-cr] [-f mboxdir] [-s singleton]\n", argv0);
  168. exits("usage");
  169. }
  170. void
  171. catchnote(void*, char *note)
  172. {
  173. if(strstr(note, "interrupt") != nil){
  174. interrupted = 1;
  175. noted(NCONT);
  176. }
  177. noted(NDFLT);
  178. }
  179. char *
  180. plural(int n)
  181. {
  182. if (n == 1)
  183. return "";
  184. return "s";
  185. }
  186. void
  187. main(int argc, char **argv)
  188. {
  189. Message *cur, *m, *x;
  190. char cmdline[4*1024];
  191. Cmd cmd;
  192. char *err;
  193. int n, cflag;
  194. char *av[4];
  195. String *prompt;
  196. char *file, *singleton;
  197. Binit(&out, 1, OWRITE);
  198. file = nil;
  199. singleton = nil;
  200. reverse = 1;
  201. cflag = 0;
  202. ARGBEGIN {
  203. case 'c':
  204. cflag = 1;
  205. break;
  206. case 'f':
  207. file = EARGF(usage());
  208. break;
  209. case 's':
  210. singleton = EARGF(usage());
  211. break;
  212. case 'r':
  213. reverse = 0;
  214. break;
  215. case 'n':
  216. natural = 1;
  217. reverse = 0;
  218. break;
  219. default:
  220. usage();
  221. break;
  222. } ARGEND;
  223. user = getlog();
  224. if(user == nil || *user == 0)
  225. sysfatal("can't read user name");
  226. if(cflag){
  227. if(argc > 0)
  228. creatembox(user, argv[0]);
  229. else
  230. creatembox(user, nil);
  231. exits(0);
  232. }
  233. if(argc)
  234. usage();
  235. if(access("/mail/fs/ctl", 0) < 0){
  236. startedfs = 1;
  237. av[0] = "fs";
  238. av[1] = "-p";
  239. av[2] = 0;
  240. system("/bin/upas/fs", av, -1);
  241. }
  242. switchmb(file, singleton);
  243. top.path = s_copy(root);
  244. if(singleton != nil){
  245. cur = dosingleton(&top, singleton);
  246. if(cur == nil){
  247. Bprint(&out, "no message\n");
  248. exitfs(0);
  249. }
  250. pcmd(nil, cur);
  251. } else {
  252. cur = &top;
  253. n = dir2message(&top, reverse);
  254. if(n < 0)
  255. sysfatal("can't read %s", s_to_c(top.path));
  256. Bprint(&out, "%d message%s\n", n, plural(n));
  257. }
  258. notify(catchnote);
  259. prompt = s_new();
  260. for(;;){
  261. s_reset(prompt);
  262. if(cur == &top)
  263. s_append(prompt, ": ");
  264. else {
  265. mkid(prompt, cur);
  266. s_append(prompt, ": ");
  267. }
  268. // leave space at the end of cmd line in case parsecmd needs to
  269. // add a space after a '|' or '!'
  270. if(readline(s_to_c(prompt), cmdline, sizeof(cmdline)-1) == nil)
  271. break;
  272. err = parsecmd(cmdline, &cmd, top.child, cur);
  273. if(err != nil){
  274. Bprint(&out, "!%s\n", err);
  275. continue;
  276. }
  277. if(singleton != nil && cmd.f == icmd){
  278. Bprint(&out, "!illegal command\n");
  279. continue;
  280. }
  281. interrupted = 0;
  282. if(cmd.msgs == nil || cmd.msgs == &top){
  283. x = (*cmd.f)(&cmd, &top);
  284. if(x != nil)
  285. cur = x;
  286. } else for(m = cmd.msgs; m != nil; m = m->cmd){
  287. x = m;
  288. if(cmd.delete){
  289. dcmd(&cmd, x);
  290. // dp acts differently than all other commands
  291. // since its an old lesk idiom that people love.
  292. // it deletes the current message, moves the current
  293. // pointer ahead one and prints.
  294. if(cmd.f == pcmd){
  295. if(x->next == nil){
  296. Bprint(&out, "!address\n");
  297. cur = x;
  298. break;
  299. } else
  300. x = x->next;
  301. }
  302. }
  303. x = (*cmd.f)(&cmd, x);
  304. if(x != nil)
  305. cur = x;
  306. if(interrupted)
  307. break;
  308. if(singleton != nil && (cmd.delete || cmd.f == dcmd))
  309. qcmd(nil, nil);
  310. }
  311. if(doflush)
  312. cur = flushdeleted(cur);
  313. }
  314. qcmd(nil, nil);
  315. }
  316. //
  317. // read the message info
  318. //
  319. Message*
  320. file2message(Message *parent, char *name)
  321. {
  322. Message *m;
  323. String *path;
  324. char *f[10];
  325. m = mallocz(sizeof(Message), 1);
  326. if(m == nil)
  327. return nil;
  328. m->path = path = extendpath(parent->path, name);
  329. m->fileno = atoi(name);
  330. m->info = file2string(path, "info");
  331. lineize(s_to_c(m->info), f, nelem(f));
  332. m->from = f[0];
  333. m->to = f[1];
  334. m->cc = f[2];
  335. m->replyto = f[3];
  336. m->date = f[4];
  337. m->subject = f[5];
  338. m->type = f[6];
  339. m->disposition = f[7];
  340. m->filename = f[8];
  341. m->len = filelen(path, "raw");
  342. if(strstr(m->type, "multipart") != nil || strcmp(m->type, "message/rfc822") == 0)
  343. dir2message(m, 0);
  344. m->parent = parent;
  345. return m;
  346. }
  347. void
  348. freemessage(Message *m)
  349. {
  350. Message *nm, *next;
  351. for(nm = m->child; nm != nil; nm = next){
  352. next = nm->next;
  353. freemessage(nm);
  354. }
  355. s_free(m->path);
  356. s_free(m->info);
  357. free(m);
  358. }
  359. //
  360. // read a directory into a list of messages
  361. //
  362. int
  363. dir2message(Message *parent, int reverse)
  364. {
  365. int i, n, fd, highest, newmsgs;
  366. Dir *d;
  367. Message *first, *last, *m;
  368. fd = open(s_to_c(parent->path), OREAD);
  369. if(fd < 0)
  370. return -1;
  371. // count current entries
  372. first = parent->child;
  373. highest = newmsgs = 0;
  374. for(last = parent->child; last != nil && last->next != nil; last = last->next)
  375. if(last->fileno > highest)
  376. highest = last->fileno;
  377. if(last != nil)
  378. if(last->fileno > highest)
  379. highest = last->fileno;
  380. n = dirreadall(fd, &d);
  381. for(i = 0; i < n; i++){
  382. if((d[i].qid.type & QTDIR) == 0)
  383. continue;
  384. if(atoi(d[i].name) <= highest)
  385. continue;
  386. m = file2message(parent, d[i].name);
  387. if(m == nil)
  388. break;
  389. newmsgs++;
  390. if(reverse){
  391. m->next = first;
  392. if(first != nil)
  393. first->prev = m;
  394. first = m;
  395. } else {
  396. if(first == nil)
  397. first = m;
  398. else
  399. last->next = m;
  400. m->prev = last;
  401. last = m;
  402. }
  403. }
  404. free(d);
  405. close(fd);
  406. parent->child = first;
  407. // renumber and file longest from
  408. i = 1;
  409. longestfrom = 12;
  410. for(m = first; m != nil; m = m->next){
  411. m->id = natural ? m->fileno : i++;
  412. n = strlen(m->from);
  413. if(n > longestfrom)
  414. longestfrom = n;
  415. }
  416. return newmsgs;
  417. }
  418. //
  419. // point directly to a message
  420. //
  421. Message*
  422. dosingleton(Message *parent, char *path)
  423. {
  424. char *p, *np;
  425. Message *m;
  426. // walk down to message and read it
  427. if(strlen(path) < rootlen)
  428. return nil;
  429. if(path[rootlen] != '/')
  430. return nil;
  431. p = path+rootlen+1;
  432. np = strchr(p, '/');
  433. if(np != nil)
  434. *np = 0;
  435. m = file2message(parent, p);
  436. if(m == nil)
  437. return nil;
  438. parent->child = m;
  439. m->id = 1;
  440. // walk down to requested component
  441. while(np != nil){
  442. *np = '/';
  443. np = strchr(np+1, '/');
  444. if(np != nil)
  445. *np = 0;
  446. for(m = m->child; m != nil; m = m->next)
  447. if(strcmp(path, s_to_c(m->path)) == 0)
  448. return m;
  449. if(m == nil)
  450. return nil;
  451. }
  452. return m;
  453. }
  454. //
  455. // read a file into a string
  456. //
  457. String*
  458. file2string(String *dir, char *file)
  459. {
  460. String *s;
  461. int fd, n, m;
  462. s = extendpath(dir, file);
  463. fd = open(s_to_c(s), OREAD);
  464. s_grow(s, 512); /* avoid multiple reads on info files */
  465. s_reset(s);
  466. if(fd < 0)
  467. return s;
  468. for(;;){
  469. n = s->end - s->ptr;
  470. if(n == 0){
  471. s_grow(s, 128);
  472. continue;
  473. }
  474. m = read(fd, s->ptr, n);
  475. if(m <= 0)
  476. break;
  477. s->ptr += m;
  478. if(m < n)
  479. break;
  480. }
  481. s_terminate(s);
  482. close(fd);
  483. return s;
  484. }
  485. //
  486. // get the length of a file
  487. //
  488. int
  489. filelen(String *dir, char *file)
  490. {
  491. String *path;
  492. Dir *d;
  493. int rv;
  494. path = extendpath(dir, file);
  495. d = dirstat(s_to_c(path));
  496. if(d == nil){
  497. s_free(path);
  498. return -1;
  499. }
  500. s_free(path);
  501. rv = d->length;
  502. free(d);
  503. return rv;
  504. }
  505. //
  506. // walk the path name an element
  507. //
  508. String*
  509. extendpath(String *dir, char *name)
  510. {
  511. String *path;
  512. if(strcmp(s_to_c(dir), ".") == 0)
  513. path = s_new();
  514. else {
  515. path = s_copy(s_to_c(dir));
  516. s_append(path, "/");
  517. }
  518. s_append(path, name);
  519. return path;
  520. }
  521. int
  522. cistrncmp(char *a, char *b, int n)
  523. {
  524. while(n-- > 0){
  525. if(tolower(*a++) != tolower(*b++))
  526. return -1;
  527. }
  528. return 0;
  529. }
  530. int
  531. cistrcmp(char *a, char *b)
  532. {
  533. for(;;){
  534. if(tolower(*a) != tolower(*b++))
  535. return -1;
  536. if(*a++ == 0)
  537. break;
  538. }
  539. return 0;
  540. }
  541. char*
  542. nosecs(char *t)
  543. {
  544. char *p;
  545. p = strchr(t, ':');
  546. if(p == nil)
  547. return t;
  548. p = strchr(p+1, ':');
  549. if(p != nil)
  550. *p = 0;
  551. return t;
  552. }
  553. char *months[12] =
  554. {
  555. "jan", "feb", "mar", "apr", "may", "jun",
  556. "jul", "aug", "sep", "oct", "nov", "dec"
  557. };
  558. int
  559. month(char *m)
  560. {
  561. int i;
  562. for(i = 0; i < 12; i++)
  563. if(cistrcmp(m, months[i]) == 0)
  564. return i+1;
  565. return 1;
  566. }
  567. enum
  568. {
  569. Yearsecs= 365*24*60*60
  570. };
  571. void
  572. cracktime(char *d, char *out, int len)
  573. {
  574. char in[64];
  575. char *f[6];
  576. int n;
  577. Tm tm;
  578. long now, then;
  579. char *dtime;
  580. *out = 0;
  581. if(d == nil)
  582. return;
  583. strncpy(in, d, sizeof(in));
  584. in[sizeof(in)-1] = 0;
  585. n = getfields(in, f, 6, 1, " \t\r\n");
  586. if(n != 6){
  587. // unknown style
  588. snprint(out, 16, "%10.10s", d);
  589. return;
  590. }
  591. now = time(0);
  592. memset(&tm, 0, sizeof tm);
  593. if(strchr(f[0], ',') != nil && strchr(f[4], ':') != nil){
  594. // 822 style
  595. tm.year = atoi(f[3])-1900;
  596. tm.mon = month(f[2]);
  597. tm.mday = atoi(f[1]);
  598. dtime = nosecs(f[4]);
  599. then = tm2sec(&tm);
  600. } else if(strchr(f[3], ':') != nil){
  601. // unix style
  602. tm.year = atoi(f[5])-1900;
  603. tm.mon = month(f[1]);
  604. tm.mday = atoi(f[2]);
  605. dtime = nosecs(f[3]);
  606. then = tm2sec(&tm);
  607. } else {
  608. then = now;
  609. tm = *localtime(now);
  610. dtime = "";
  611. }
  612. if(now - then < Yearsecs/2)
  613. snprint(out, len, "%2d/%2.2d %s", tm.mon, tm.mday, dtime);
  614. else
  615. snprint(out, len, "%2d/%2.2d %4d", tm.mon, tm.mday, tm.year+1900);
  616. }
  617. Ctype*
  618. findctype(char *t)
  619. {
  620. Ctype *cp;
  621. for(cp = ctype; ; cp++)
  622. if(strncmp(cp->type, t, strlen(cp->type)) == 0)
  623. break;
  624. return cp;
  625. }
  626. void
  627. mkid(String *s, Message *m)
  628. {
  629. char buf[32];
  630. if(m->parent != &top){
  631. mkid(s, m->parent);
  632. s_append(s, ".");
  633. }
  634. sprint(buf, "%d", m->id);
  635. s_append(s, buf);
  636. }
  637. void
  638. snprintheader(char *buf, int len, Message *m)
  639. {
  640. char timebuf[32];
  641. String *id;
  642. char *p, *q;;
  643. // create id
  644. id = s_new();
  645. mkid(id, m);
  646. if(*m->from == 0){
  647. // no from
  648. snprint(buf, len, "%-3s %s %6d %s",
  649. s_to_c(id),
  650. m->type,
  651. m->len,
  652. m->filename);
  653. } else if(*m->subject){
  654. q = p = strdup(m->subject);
  655. while(*p == ' ')
  656. p++;
  657. if(strlen(p) > 50)
  658. p[50] = 0;
  659. cracktime(m->date, timebuf, sizeof(timebuf));
  660. snprint(buf, len, "%-3s %c%c%c %6d %11.11s %-*.*s %s",
  661. s_to_c(id),
  662. m->child ? 'H' : ' ',
  663. m->deleted ? 'd' : ' ',
  664. m->stored ? 's' : ' ',
  665. m->len,
  666. timebuf,
  667. longestfrom, longestfrom, m->from,
  668. p);
  669. free(q);
  670. } else {
  671. cracktime(m->date, timebuf, sizeof(timebuf));
  672. snprint(buf, len, "%-3s %c%c%c %6d %11.11s %s",
  673. s_to_c(id),
  674. m->child ? 'H' : ' ',
  675. m->deleted ? 'd' : ' ',
  676. m->stored ? 's' : ' ',
  677. m->len,
  678. timebuf,
  679. m->from);
  680. }
  681. s_free(id);
  682. }
  683. char *spaces = " ";
  684. void
  685. snprintHeader(char *buf, int len, int indent, Message *m)
  686. {
  687. String *id;
  688. char typeid[64];
  689. char *p, *e;
  690. // create id
  691. id = s_new();
  692. mkid(id, m);
  693. e = buf + len;
  694. snprint(typeid, sizeof typeid, "%s %s", s_to_c(id), m->type);
  695. if(indent < 6)
  696. p = seprint(buf, e, "%-32s %-6d ", typeid, m->len);
  697. else
  698. p = seprint(buf, e, "%-64s %-6d ", typeid, m->len);
  699. if(m->filename && *m->filename)
  700. p = seprint(p, e, "(file,%s)", m->filename);
  701. if(m->from && *m->from)
  702. p = seprint(p, e, "(from,%s)", m->from);
  703. if(m->subject && *m->subject)
  704. seprint(p, e, "(subj,%s)", m->subject);
  705. s_free(id);
  706. }
  707. char sstring[256];
  708. // cmd := range cmd ' ' arg-list ;
  709. // range := address
  710. // | address ',' address
  711. // | 'g' search ;
  712. // address := msgno
  713. // | search ;
  714. // msgno := number
  715. // | number '/' msgno ;
  716. // search := '/' string '/'
  717. // | '%' string '%' ;
  718. //
  719. Reprog*
  720. parsesearch(char **pp)
  721. {
  722. char *p, *np;
  723. int c, n;
  724. p = *pp;
  725. c = *p++;
  726. np = strchr(p, c);
  727. if(np != nil){
  728. *np++ = 0;
  729. *pp = np;
  730. } else {
  731. n = strlen(p);
  732. *pp = p + n;
  733. }
  734. if(*p == 0)
  735. p = sstring;
  736. else{
  737. strncpy(sstring, p, sizeof(sstring));
  738. sstring[sizeof(sstring)-1] = 0;
  739. }
  740. return regcomp(p);
  741. }
  742. char*
  743. parseaddr(char **pp, Message *first, Message *cur, Message *unspec, Message **mp)
  744. {
  745. int n;
  746. Message *m;
  747. char *p;
  748. Reprog *prog;
  749. int c, sign;
  750. char buf[256];
  751. *mp = nil;
  752. p = *pp;
  753. if(*p == '+'){
  754. sign = 1;
  755. p++;
  756. *pp = p;
  757. } else if(*p == '-'){
  758. sign = -1;
  759. p++;
  760. *pp = p;
  761. } else
  762. sign = 0;
  763. switch(*p){
  764. default:
  765. if(sign){
  766. n = 1;
  767. goto number;
  768. }
  769. *mp = unspec;
  770. break;
  771. case '0': case '1': case '2': case '3': case '4':
  772. case '5': case '6': case '7': case '8': case '9':
  773. n = strtoul(p, pp, 10);
  774. if(n == 0){
  775. if(sign)
  776. *mp = cur;
  777. else
  778. *mp = &top;
  779. break;
  780. }
  781. number:
  782. m = nil;
  783. switch(sign){
  784. case 0:
  785. for(m = first; m != nil; m = m->next)
  786. if(m->id == n)
  787. break;
  788. break;
  789. case -1:
  790. if(cur != &top)
  791. for(m = cur; m != nil && n > 0; n--)
  792. m = m->prev;
  793. break;
  794. case 1:
  795. if(cur == &top){
  796. n--;
  797. cur = first;
  798. }
  799. for(m = cur; m != nil && n > 0; n--)
  800. m = m->next;
  801. break;
  802. }
  803. if(m == nil)
  804. return "address";
  805. *mp = m;
  806. break;
  807. case '%':
  808. case '/':
  809. case '?':
  810. c = *p;
  811. prog = parsesearch(pp);
  812. if(prog == nil)
  813. return "badly formed regular expression";
  814. m = nil;
  815. switch(c){
  816. case '%':
  817. for(m = cur == &top ? first : cur->next; m != nil; m = m->next){
  818. if(rawsearch(m, prog))
  819. break;
  820. }
  821. break;
  822. case '/':
  823. for(m = cur == &top ? first : cur->next; m != nil; m = m->next){
  824. snprintheader(buf, sizeof(buf), m);
  825. if(regexec(prog, buf, nil, 0))
  826. break;
  827. }
  828. break;
  829. case '?':
  830. for(m = cur == &top ? nil : cur->prev; m != nil; m = m->prev){
  831. snprintheader(buf, sizeof(buf), m);
  832. if(regexec(prog, buf, nil, 0))
  833. break;
  834. }
  835. break;
  836. }
  837. if(m == nil)
  838. return "search";
  839. *mp = m;
  840. free(prog);
  841. break;
  842. case '$':
  843. for(m = first; m != nil && m->next != nil; m = m->next)
  844. ;
  845. *mp = m;
  846. *pp = p+1;
  847. break;
  848. case '.':
  849. *mp = cur;
  850. *pp = p+1;
  851. break;
  852. case ',':
  853. *mp = first;
  854. *pp = p;
  855. break;
  856. }
  857. if(*mp != nil && **pp == '.'){
  858. (*pp)++;
  859. if((*mp)->child == nil)
  860. return "no sub parts";
  861. return parseaddr(pp, (*mp)->child, (*mp)->child, (*mp)->child, mp);
  862. }
  863. if(**pp == '+' || **pp == '-' || **pp == '/' || **pp == '%')
  864. return parseaddr(pp, first, *mp, *mp, mp);
  865. return nil;
  866. }
  867. //
  868. // search a message for a regular expression match
  869. //
  870. int
  871. rawsearch(Message *m, Reprog *prog)
  872. {
  873. char buf[4096+1];
  874. int i, fd, rv;
  875. String *path;
  876. path = extendpath(m->path, "raw");
  877. fd = open(s_to_c(path), OREAD);
  878. if(fd < 0)
  879. return 0;
  880. // march through raw message 4096 bytes at a time
  881. // with a 128 byte overlap to chain the re search.
  882. rv = 0;
  883. for(;;){
  884. i = read(fd, buf, sizeof(buf)-1);
  885. if(i <= 0)
  886. break;
  887. buf[i] = 0;
  888. if(regexec(prog, buf, nil, 0)){
  889. rv = 1;
  890. break;
  891. }
  892. if(i < sizeof(buf)-1)
  893. break;
  894. if(seek(fd, -128LL, 1) < 0)
  895. break;
  896. }
  897. close(fd);
  898. s_free(path);
  899. return rv;
  900. }
  901. char*
  902. parsecmd(char *p, Cmd *cmd, Message *first, Message *cur)
  903. {
  904. Reprog *prog;
  905. Message *m, *s, *e, **l, *last;
  906. char buf[256];
  907. char *err;
  908. int i, c;
  909. char *q;
  910. static char errbuf[Errlen];
  911. cmd->delete = 0;
  912. l = &cmd->msgs;
  913. *l = nil;
  914. // eat white space
  915. while(*p == ' ')
  916. p++;
  917. // null command is a special case (advance and print)
  918. if(*p == 0){
  919. if(cur == &top){
  920. // special case
  921. m = first;
  922. } else {
  923. // walk to the next message even if we have to go up
  924. m = cur->next;
  925. while(m == nil && cur->parent != nil){
  926. cur = cur->parent;
  927. m = cur->next;
  928. }
  929. }
  930. if(m == nil)
  931. return "address";
  932. *l = m;
  933. m->cmd = nil;
  934. cmd->an = 0;
  935. cmd->f = pcmd;
  936. return nil;
  937. }
  938. // global search ?
  939. if(*p == 'g'){
  940. p++;
  941. // no search string means all messages
  942. if(*p != '/' && *p != '%'){
  943. for(m = first; m != nil; m = m->next){
  944. *l = m;
  945. l = &m->cmd;
  946. *l = nil;
  947. }
  948. } else {
  949. // mark all messages matching this search string
  950. c = *p;
  951. prog = parsesearch(&p);
  952. if(prog == nil)
  953. return "badly formed regular expression";
  954. if(c == '%'){
  955. for(m = first; m != nil; m = m->next){
  956. if(rawsearch(m, prog)){
  957. *l = m;
  958. l = &m->cmd;
  959. *l = nil;
  960. }
  961. }
  962. } else {
  963. for(m = first; m != nil; m = m->next){
  964. snprintheader(buf, sizeof(buf), m);
  965. if(regexec(prog, buf, nil, 0)){
  966. *l = m;
  967. l = &m->cmd;
  968. *l = nil;
  969. }
  970. }
  971. }
  972. free(prog);
  973. }
  974. } else {
  975. // parse an address
  976. s = e = nil;
  977. err = parseaddr(&p, first, cur, cur, &s);
  978. if(err != nil)
  979. return err;
  980. if(*p == ','){
  981. // this is an address range
  982. if(s == &top)
  983. s = first;
  984. p++;
  985. for(last = s; last != nil && last->next != nil; last = last->next)
  986. ;
  987. err = parseaddr(&p, first, cur, last, &e);
  988. if(err != nil)
  989. return err;
  990. // select all messages in the range
  991. for(; s != nil; s = s->next){
  992. *l = s;
  993. l = &s->cmd;
  994. *l = nil;
  995. if(s == e)
  996. break;
  997. }
  998. if(s == nil)
  999. return "null address range";
  1000. } else {
  1001. // single address
  1002. if(s != &top){
  1003. *l = s;
  1004. s->cmd = nil;
  1005. }
  1006. }
  1007. }
  1008. // insert a space after '!'s and '|'s
  1009. for(q = p; *q; q++)
  1010. if(*q != '!' && *q != '|')
  1011. break;
  1012. if(q != p && *q != ' '){
  1013. memmove(q+1, q, strlen(q)+1);
  1014. *q = ' ';
  1015. }
  1016. cmd->an = getfields(p, cmd->av, nelem(cmd->av) - 1, 1, " \t\r\n");
  1017. if(cmd->an == 0 || *cmd->av[0] == 0)
  1018. cmd->f = pcmd;
  1019. else {
  1020. // hack to allow all messages to start with 'd'
  1021. if(*(cmd->av[0]) == 'd' && *(cmd->av[0]+1) != 0){
  1022. cmd->delete = 1;
  1023. cmd->av[0]++;
  1024. }
  1025. // search command table
  1026. for(i = 0; cmdtab[i].cmd != nil; i++)
  1027. if(strcmp(cmd->av[0], cmdtab[i].cmd) == 0)
  1028. break;
  1029. if(cmdtab[i].cmd == nil)
  1030. return "illegal command";
  1031. if(cmdtab[i].args == 0 && cmd->an > 1){
  1032. snprint(errbuf, sizeof(errbuf), "%s doesn't take an argument", cmdtab[i].cmd);
  1033. return errbuf;
  1034. }
  1035. cmd->f = cmdtab[i].f;
  1036. }
  1037. return nil;
  1038. }
  1039. // inefficient read from standard input
  1040. char*
  1041. readline(char *prompt, char *line, int len)
  1042. {
  1043. char *p, *e;
  1044. int n;
  1045. retry:
  1046. interrupted = 0;
  1047. Bprint(&out, "%s", prompt);
  1048. Bflush(&out);
  1049. e = line + len;
  1050. for(p = line; p < e; p++){
  1051. n = read(0, p, 1);
  1052. if(n < 0){
  1053. if(interrupted)
  1054. goto retry;
  1055. return nil;
  1056. }
  1057. if(n == 0)
  1058. return nil;
  1059. if(*p == '\n')
  1060. break;
  1061. }
  1062. *p = 0;
  1063. return line;
  1064. }
  1065. void
  1066. messagecount(Message *m)
  1067. {
  1068. int i;
  1069. i = 0;
  1070. for(; m != nil; m = m->next)
  1071. i++;
  1072. Bprint(&out, "%d message%s\n", i, plural(i));
  1073. }
  1074. Message*
  1075. aichcmd(Message *m, int indent)
  1076. {
  1077. char hdr[256];
  1078. if(m == &top)
  1079. return nil;
  1080. snprintHeader(hdr, sizeof(hdr), indent, m);
  1081. Bprint(&out, "%s\n", hdr);
  1082. for(m = m->child; m != nil; m = m->next)
  1083. aichcmd(m, indent+1);
  1084. return nil;
  1085. }
  1086. Message*
  1087. Hcmd(Cmd*, Message *m)
  1088. {
  1089. if(m == &top)
  1090. return nil;
  1091. aichcmd(m, 0);
  1092. return nil;
  1093. }
  1094. Message*
  1095. hcmd(Cmd*, Message *m)
  1096. {
  1097. char hdr[256];
  1098. if(m == &top)
  1099. return nil;
  1100. snprintheader(hdr, sizeof(hdr), m);
  1101. Bprint(&out, "%s\n", hdr);
  1102. return nil;
  1103. }
  1104. Message*
  1105. bcmd(Cmd*, Message *m)
  1106. {
  1107. int i;
  1108. Message *om = m;
  1109. if(m == &top)
  1110. m = top.child;
  1111. for(i = 0; i < 10 && m != nil; i++){
  1112. hcmd(nil, m);
  1113. om = m;
  1114. m = m->next;
  1115. }
  1116. return om;
  1117. }
  1118. Message*
  1119. ncmd(Cmd*, Message *m)
  1120. {
  1121. if(m == &top)
  1122. return m->child;
  1123. return m->next;
  1124. }
  1125. int
  1126. printpart(String *s, char *part)
  1127. {
  1128. char buf[4096];
  1129. int n, fd, tot;
  1130. String *path;
  1131. path = extendpath(s, part);
  1132. fd = open(s_to_c(path), OREAD);
  1133. s_free(path);
  1134. if(fd < 0){
  1135. fprint(2, "!message dissappeared\n");
  1136. return 0;
  1137. }
  1138. tot = 0;
  1139. while((n = read(fd, buf, sizeof(buf))) > 0){
  1140. if(interrupted)
  1141. break;
  1142. if(Bwrite(&out, buf, n) <= 0)
  1143. break;
  1144. tot += n;
  1145. }
  1146. close(fd);
  1147. return tot;
  1148. }
  1149. int
  1150. printhtml(Message *m)
  1151. {
  1152. Cmd c;
  1153. c.an = 3;
  1154. c.av[1] = "/bin/htmlfmt";
  1155. c.av[2] = "-l 40 -cutf-8";
  1156. Bprint(&out, "!%s\n", c.av[1]);
  1157. Bflush(&out);
  1158. pipecmd(&c, m);
  1159. return 0;
  1160. }
  1161. Message*
  1162. Pcmd(Cmd*, Message *m)
  1163. {
  1164. if(m == &top)
  1165. return &top;
  1166. if(m->parent == &top)
  1167. printpart(m->path, "unixheader");
  1168. printpart(m->path, "raw");
  1169. return m;
  1170. }
  1171. void
  1172. compress(char *p)
  1173. {
  1174. char *np;
  1175. int last;
  1176. last = ' ';
  1177. for(np = p; *p; p++){
  1178. if(*p != ' ' || last != ' '){
  1179. last = *p;
  1180. *np++ = last;
  1181. }
  1182. }
  1183. *np = 0;
  1184. }
  1185. Message*
  1186. pcmd(Cmd*, Message *m)
  1187. {
  1188. Message *nm;
  1189. Ctype *cp;
  1190. String *s;
  1191. char buf[128];
  1192. if(m == &top)
  1193. return &top;
  1194. if(m->parent == &top)
  1195. printpart(m->path, "unixheader");
  1196. if(printpart(m->path, "header") > 0)
  1197. Bprint(&out, "\n");
  1198. cp = findctype(m->type);
  1199. if(cp->display){
  1200. if(strcmp(m->type, "text/html") == 0)
  1201. printhtml(m);
  1202. else
  1203. printpart(m->path, "body");
  1204. } else if(strcmp(m->type, "multipart/alternative") == 0){
  1205. for(nm = m->child; nm != nil; nm = nm->next){
  1206. cp = findctype(nm->type);
  1207. if(cp->ext != nil && strncmp(cp->ext, "txt", 3) == 0)
  1208. break;
  1209. }
  1210. if(nm == nil)
  1211. for(nm = m->child; nm != nil; nm = nm->next){
  1212. cp = findctype(nm->type);
  1213. if(cp->display)
  1214. break;
  1215. }
  1216. if(nm != nil)
  1217. pcmd(nil, nm);
  1218. else
  1219. hcmd(nil, m);
  1220. } else if(strncmp(m->type, "multipart/", 10) == 0){
  1221. nm = m->child;
  1222. if(nm != nil){
  1223. // always print first part
  1224. pcmd(nil, nm);
  1225. for(nm = nm->next; nm != nil; nm = nm->next){
  1226. s = rooted(s_clone(nm->path));
  1227. cp = findctype(nm->type);
  1228. snprintHeader(buf, sizeof buf, -1, nm);
  1229. compress(buf);
  1230. if(strcmp(nm->disposition, "inline") == 0){
  1231. if(cp->ext != nil)
  1232. Bprint(&out, "\n--- %s %s/body.%s\n\n",
  1233. buf, s_to_c(s), cp->ext);
  1234. else
  1235. Bprint(&out, "\n--- %s %s/body\n\n",
  1236. buf, s_to_c(s));
  1237. pcmd(nil, nm);
  1238. } else {
  1239. if(cp->ext != nil)
  1240. Bprint(&out, "\n!--- %s %s/body.%s\n",
  1241. buf, s_to_c(s), cp->ext);
  1242. else
  1243. Bprint(&out, "\n!--- %s %s/body\n",
  1244. buf, s_to_c(s));
  1245. }
  1246. s_free(s);
  1247. }
  1248. } else {
  1249. hcmd(nil, m);
  1250. }
  1251. } else if(strcmp(m->type, "message/rfc822") == 0){
  1252. pcmd(nil, m->child);
  1253. } else if(plumb(m, cp) >= 0)
  1254. Bprint(&out, "\n!--- using plumber to display message of type %s\n", m->type);
  1255. else
  1256. Bprint(&out, "\n!--- cannot display messages of type %s\n", m->type);
  1257. return m;
  1258. }
  1259. void
  1260. printpartindented(String *s, char *part, char *indent)
  1261. {
  1262. char *p;
  1263. String *path;
  1264. Biobuf *b;
  1265. path = extendpath(s, part);
  1266. b = Bopen(s_to_c(path), OREAD);
  1267. s_free(path);
  1268. if(b == nil){
  1269. fprint(2, "!message dissappeared\n");
  1270. return;
  1271. }
  1272. while((p = Brdline(b, '\n')) != nil){
  1273. if(interrupted)
  1274. break;
  1275. p[Blinelen(b)-1] = 0;
  1276. if(Bprint(&out, "%s%s\n", indent, p) <= 0)
  1277. break;
  1278. }
  1279. Bprint(&out, "\n");
  1280. Bterm(b);
  1281. }
  1282. Message*
  1283. quotecmd(Cmd*, Message *m)
  1284. {
  1285. Message *nm;
  1286. Ctype *cp;
  1287. if(m == &top)
  1288. return &top;
  1289. Bprint(&out, "\n");
  1290. if(m->from != nil && *m->from)
  1291. Bprint(&out, "On %s, %s wrote:\n", m->date, m->from);
  1292. cp = findctype(m->type);
  1293. if(cp->display){
  1294. printpartindented(m->path, "body", "> ");
  1295. } else if(strcmp(m->type, "multipart/alternative") == 0){
  1296. for(nm = m->child; nm != nil; nm = nm->next){
  1297. cp = findctype(nm->type);
  1298. if(cp->ext != nil && strncmp(cp->ext, "txt", 3) == 0)
  1299. break;
  1300. }
  1301. if(nm == nil)
  1302. for(nm = m->child; nm != nil; nm = nm->next){
  1303. cp = findctype(nm->type);
  1304. if(cp->display)
  1305. break;
  1306. }
  1307. if(nm != nil)
  1308. quotecmd(nil, nm);
  1309. } else if(strncmp(m->type, "multipart/", 10) == 0){
  1310. nm = m->child;
  1311. if(nm != nil){
  1312. cp = findctype(nm->type);
  1313. if(cp->display || strncmp(m->type, "multipart/", 10) == 0)
  1314. quotecmd(nil, nm);
  1315. }
  1316. }
  1317. return m;
  1318. }
  1319. // really delete messages
  1320. Message*
  1321. flushdeleted(Message *cur)
  1322. {
  1323. Message *m, **l;
  1324. char buf[1024], *p, *e, *msg;
  1325. int deld, n, fd;
  1326. int i;
  1327. doflush = 0;
  1328. deld = 0;
  1329. fd = open("/mail/fs/ctl", ORDWR);
  1330. if(fd < 0){
  1331. fprint(2, "!can't delete mail, opening /mail/fs/ctl: %r\n");
  1332. exitfs(0);
  1333. }
  1334. e = &buf[sizeof(buf)];
  1335. p = seprint(buf, e, "delete %s", mbname);
  1336. n = 0;
  1337. for(l = &top.child; *l != nil;){
  1338. m = *l;
  1339. if(!m->deleted){
  1340. l = &(*l)->next;
  1341. continue;
  1342. }
  1343. // don't return a pointer to a deleted message
  1344. if(m == cur)
  1345. cur = m->next;
  1346. deld++;
  1347. msg = strrchr(s_to_c(m->path), '/');
  1348. if(msg == nil)
  1349. msg = s_to_c(m->path);
  1350. else
  1351. msg++;
  1352. if(e-p < 10){
  1353. write(fd, buf, p-buf);
  1354. n = 0;
  1355. p = seprint(buf, e, "delete %s", mbname);
  1356. }
  1357. p = seprint(p, e, " %s", msg);
  1358. n++;
  1359. // unchain and free
  1360. *l = m->next;
  1361. if(m->next)
  1362. m->next->prev = m->prev;
  1363. freemessage(m);
  1364. }
  1365. if(n)
  1366. write(fd, buf, p-buf);
  1367. close(fd);
  1368. if(deld)
  1369. Bprint(&out, "!%d message%s deleted\n", deld, plural(deld));
  1370. // renumber
  1371. i = 1;
  1372. for(m = top.child; m != nil; m = m->next)
  1373. m->id = natural ? m->fileno : i++;
  1374. // if we're out of messages, go back to first
  1375. // if no first, return the fake first
  1376. if(cur == nil){
  1377. if(top.child)
  1378. return top.child;
  1379. else
  1380. return &top;
  1381. }
  1382. return cur;
  1383. }
  1384. Message*
  1385. qcmd(Cmd*, Message*)
  1386. {
  1387. flushdeleted(nil);
  1388. if(didopen)
  1389. closemb();
  1390. Bflush(&out);
  1391. exitfs(0);
  1392. return nil; // not reached
  1393. }
  1394. Message*
  1395. ycmd(Cmd*, Message *m)
  1396. {
  1397. doflush = 1;
  1398. return icmd(nil, m);
  1399. }
  1400. Message*
  1401. xcmd(Cmd*, Message*)
  1402. {
  1403. exitfs(0);
  1404. return nil; // not reached
  1405. }
  1406. Message*
  1407. eqcmd(Cmd*, Message *m)
  1408. {
  1409. if(m == &top)
  1410. Bprint(&out, "0\n");
  1411. else
  1412. Bprint(&out, "%d\n", m->id);
  1413. return nil;
  1414. }
  1415. Message*
  1416. dcmd(Cmd*, Message *m)
  1417. {
  1418. if(m == &top){
  1419. Bprint(&out, "!address\n");
  1420. return nil;
  1421. }
  1422. while(m->parent != &top)
  1423. m = m->parent;
  1424. m->deleted = 1;
  1425. return m;
  1426. }
  1427. Message*
  1428. ucmd(Cmd*, Message *m)
  1429. {
  1430. if(m == &top)
  1431. return nil;
  1432. while(m->parent != &top)
  1433. m = m->parent;
  1434. if(m->deleted < 0)
  1435. Bprint(&out, "!can't undelete, already flushed\n");
  1436. m->deleted = 0;
  1437. return m;
  1438. }
  1439. Message*
  1440. icmd(Cmd*, Message *m)
  1441. {
  1442. int n;
  1443. n = dir2message(&top, reverse);
  1444. if(n > 0)
  1445. Bprint(&out, "%d new message%s\n", n, plural(n));
  1446. return m;
  1447. }
  1448. Message*
  1449. helpcmd(Cmd*, Message *m)
  1450. {
  1451. int i;
  1452. Bprint(&out, "Commands are of the form [<range>] <command> [args]\n");
  1453. Bprint(&out, "<range> := <addr> | <addr>','<addr>| 'g'<search>\n");
  1454. Bprint(&out, "<addr> := '.' | '$' | '^' | <number> | <search> | <addr>'+'<addr> | <addr>'-'<addr>\n");
  1455. Bprint(&out, "<search> := '/'<regexp>'/' | '?'<regexp>'?' | '%%'<regexp>'%%'\n");
  1456. Bprint(&out, "<command> :=\n");
  1457. for(i = 0; cmdtab[i].cmd != nil; i++)
  1458. Bprint(&out, "%s\n", cmdtab[i].help);
  1459. return m;
  1460. }
  1461. int
  1462. tomailer(char **av)
  1463. {
  1464. Waitmsg *w;
  1465. int pid, i;
  1466. // start the mailer and get out of the way
  1467. switch(pid = fork()){
  1468. case -1:
  1469. fprint(2, "can't fork: %r\n");
  1470. return -1;
  1471. case 0:
  1472. Bprint(&out, "!/bin/upas/marshal");
  1473. for(i = 1; av[i]; i++){
  1474. if(strchr(av[i], ' ') != nil)
  1475. Bprint(&out, " '%s'", av[i]);
  1476. else
  1477. Bprint(&out, " %s", av[i]);
  1478. }
  1479. Bprint(&out, "\n");
  1480. Bflush(&out);
  1481. av[0] = "marshal";
  1482. chdir(wd);
  1483. exec("/bin/upas/marshal", av);
  1484. fprint(2, "couldn't exec /bin/upas/marshal\n");
  1485. exits(0);
  1486. default:
  1487. w = wait();
  1488. if(w == nil){
  1489. if(interrupted)
  1490. postnote(PNPROC, pid, "die");
  1491. waitpid();
  1492. return -1;
  1493. }
  1494. if(w->msg[0]){
  1495. fprint(2, "mailer failed: %s\n", w->msg);
  1496. free(w);
  1497. return -1;
  1498. }
  1499. free(w);
  1500. Bprint(&out, "!\n");
  1501. break;
  1502. }
  1503. return 0;
  1504. }
  1505. //
  1506. // like tokenize but obey "" quoting
  1507. //
  1508. int
  1509. tokenize822(char *str, char **args, int max)
  1510. {
  1511. int na;
  1512. int intok = 0, inquote = 0;
  1513. if(max <= 0)
  1514. return 0;
  1515. for(na=0; ;str++)
  1516. switch(*str) {
  1517. case ' ':
  1518. case '\t':
  1519. if(inquote)
  1520. goto Default;
  1521. /* fall through */
  1522. case '\n':
  1523. *str = 0;
  1524. if(!intok)
  1525. continue;
  1526. intok = 0;
  1527. if(na < max)
  1528. continue;
  1529. /* fall through */
  1530. case 0:
  1531. return na;
  1532. case '"':
  1533. inquote ^= 1;
  1534. /* fall through */
  1535. Default:
  1536. default:
  1537. if(intok)
  1538. continue;
  1539. args[na++] = str;
  1540. intok = 1;
  1541. }
  1542. return 0; /* can't get here; silence compiler */
  1543. }
  1544. Message*
  1545. rcmd(Cmd *c, Message *m)
  1546. {
  1547. char *av[128];
  1548. int i, ai = 1;
  1549. Message *nm;
  1550. char *addr;
  1551. String *path = nil;
  1552. String *rpath;
  1553. String *subject = nil;
  1554. String *from;
  1555. if(m == &top){
  1556. Bprint(&out, "!address\n");
  1557. return nil;
  1558. }
  1559. addr = nil;
  1560. for(nm = m; nm != &top; nm = nm->parent){
  1561. if(*nm->replyto != 0){
  1562. addr = nm->replyto;
  1563. break;
  1564. }
  1565. }
  1566. if(addr == nil){
  1567. Bprint(&out, "!no reply address\n");
  1568. return nil;
  1569. }
  1570. if(nm == &top){
  1571. print("!noone to reply to\n");
  1572. return nil;
  1573. }
  1574. for(nm = m; nm != &top; nm = nm->parent){
  1575. if(*nm->subject){
  1576. av[ai++] = "-s";
  1577. subject = addrecolon(nm->subject);
  1578. av[ai++] = s_to_c(subject);;
  1579. break;
  1580. }
  1581. }
  1582. av[ai++] = "-R";
  1583. rpath = rooted(s_clone(m->path));
  1584. av[ai++] = s_to_c(rpath);
  1585. if(strchr(c->av[0], 'f') != nil){
  1586. fcmd(c, m);
  1587. av[ai++] = "-F";
  1588. }
  1589. if(strchr(c->av[0], 'R') != nil){
  1590. av[ai++] = "-t";
  1591. av[ai++] = "message/rfc822";
  1592. av[ai++] = "-A";
  1593. path = rooted(extendpath(m->path, "raw"));
  1594. av[ai++] = s_to_c(path);
  1595. }
  1596. for(i = 1; i < c->an && ai < nelem(av)-1; i++)
  1597. av[ai++] = c->av[i];
  1598. from = s_copy(addr);
  1599. ai += tokenize822(s_to_c(from), &av[ai], nelem(av) - ai);
  1600. av[ai] = 0;
  1601. if(tomailer(av) < 0)
  1602. m = nil;
  1603. s_free(path);
  1604. s_free(rpath);
  1605. s_free(subject);
  1606. s_free(from);
  1607. return m;
  1608. }
  1609. Message*
  1610. mcmd(Cmd *c, Message *m)
  1611. {
  1612. char **av;
  1613. int i, ai;
  1614. String *path;
  1615. if(m == &top){
  1616. Bprint(&out, "!address\n");
  1617. return nil;
  1618. }
  1619. if(c->an < 2){
  1620. fprint(2, "!usage: M list-of addresses\n");
  1621. return nil;
  1622. }
  1623. ai = 1;
  1624. av = malloc(sizeof(char*)*(c->an + 8));
  1625. av[ai++] = "-t";
  1626. if(m->parent == &top)
  1627. av[ai++] = "message/rfc822";
  1628. else
  1629. av[ai++] = "mime";
  1630. av[ai++] = "-A";
  1631. path = rooted(extendpath(m->path, "raw"));
  1632. av[ai++] = s_to_c(path);
  1633. if(strchr(c->av[0], 'M') == nil)
  1634. av[ai++] = "-n";
  1635. for(i = 1; i < c->an; i++)
  1636. av[ai++] = c->av[i];
  1637. av[ai] = 0;
  1638. if(tomailer(av) < 0)
  1639. m = nil;
  1640. if(path != nil)
  1641. s_free(path);
  1642. free(av);
  1643. return m;
  1644. }
  1645. Message*
  1646. acmd(Cmd *c, Message *m)
  1647. {
  1648. char *av[128];
  1649. int i, ai;
  1650. String *from, *to, *cc, *path = nil, *subject = nil;
  1651. if(m == &top){
  1652. Bprint(&out, "!address\n");
  1653. return nil;
  1654. }
  1655. ai = 1;
  1656. if(*m->subject){
  1657. av[ai++] = "-s";
  1658. subject = addrecolon(m->subject);
  1659. av[ai++] = s_to_c(subject);
  1660. }
  1661. if(strchr(c->av[0], 'A') != nil){
  1662. av[ai++] = "-t";
  1663. av[ai++] = "message/rfc822";
  1664. av[ai++] = "-A";
  1665. path = rooted(extendpath(m->path, "raw"));
  1666. av[ai++] = s_to_c(path);
  1667. }
  1668. for(i = 1; i < c->an && ai < nelem(av)-1; i++)
  1669. av[ai++] = c->av[i];
  1670. from = s_copy(m->from);
  1671. ai += tokenize822(s_to_c(from), &av[ai], nelem(av) - ai);
  1672. to = s_copy(m->to);
  1673. ai += tokenize822(s_to_c(to), &av[ai], nelem(av) - ai);
  1674. cc = s_copy(m->cc);
  1675. ai += tokenize822(s_to_c(cc), &av[ai], nelem(av) - ai);
  1676. av[ai] = 0;
  1677. if(tomailer(av) < 0)
  1678. return nil;
  1679. s_free(from);
  1680. s_free(to);
  1681. s_free(cc);
  1682. s_free(subject);
  1683. s_free(path);
  1684. return m;
  1685. }
  1686. String *
  1687. relpath(char *path, String *to)
  1688. {
  1689. if (*path=='/' || strncmp(path, "./", 2) == 0
  1690. || strncmp(path, "../", 3) == 0) {
  1691. to = s_append(to, path);
  1692. } else if(mbpath) {
  1693. to = s_append(to, s_to_c(mbpath));
  1694. to->ptr = strrchr(to->base, '/')+1;
  1695. s_append(to, path);
  1696. }
  1697. return to;
  1698. }
  1699. int
  1700. appendtofile(Message *m, char *part, char *base, int mbox)
  1701. {
  1702. String *file, *h;
  1703. int in, out, rv;
  1704. file = extendpath(m->path, part);
  1705. in = open(s_to_c(file), OREAD);
  1706. if(in < 0){
  1707. fprint(2, "!message disappeared\n");
  1708. return -1;
  1709. }
  1710. s_reset(file);
  1711. relpath(base, file);
  1712. if(sysisdir(s_to_c(file))){
  1713. s_append(file, "/");
  1714. if(m->filename && strchr(m->filename, '/') == nil)
  1715. s_append(file, m->filename);
  1716. else {
  1717. s_append(file, "att.XXXXXXXXXXX");
  1718. mktemp(s_to_c(file));
  1719. }
  1720. }
  1721. if(mbox)
  1722. out = open(s_to_c(file), OWRITE);
  1723. else
  1724. out = open(s_to_c(file), OWRITE|OTRUNC);
  1725. if(out < 0){
  1726. out = create(s_to_c(file), OWRITE, 0666);
  1727. if(out < 0){
  1728. fprint(2, "!can't open %s: %r\n", s_to_c(file));
  1729. close(in);
  1730. s_free(file);
  1731. return -1;
  1732. }
  1733. }
  1734. if(mbox)
  1735. seek(out, 0, 2);
  1736. // put on a 'From ' line
  1737. if(mbox){
  1738. while(m->parent != &top)
  1739. m = m->parent;
  1740. h = file2string(m->path, "unixheader");
  1741. fprint(out, "%s", s_to_c(h));
  1742. s_free(h);
  1743. }
  1744. // copy the message escaping what we have to ad adding newlines if we have to
  1745. if(mbox)
  1746. rv = appendfiletombox(in, out);
  1747. else
  1748. rv = appendfiletofile(in, out);
  1749. close(in);
  1750. close(out);
  1751. if(rv >= 0)
  1752. print("!saved in %s\n", s_to_c(file));
  1753. s_free(file);
  1754. return rv;
  1755. }
  1756. Message*
  1757. scmd(Cmd *c, Message *m)
  1758. {
  1759. char *file;
  1760. if(m == &top){
  1761. Bprint(&out, "!address\n");
  1762. return nil;
  1763. }
  1764. switch(c->an){
  1765. case 1:
  1766. file = "stored";
  1767. break;
  1768. case 2:
  1769. file = c->av[1];
  1770. break;
  1771. default:
  1772. fprint(2, "!usage: s filename\n");
  1773. return nil;
  1774. }
  1775. if(appendtofile(m, "raw", file, 1) < 0)
  1776. return nil;
  1777. m->stored = 1;
  1778. return m;
  1779. }
  1780. Message*
  1781. wcmd(Cmd *c, Message *m)
  1782. {
  1783. char *file;
  1784. if(m == &top){
  1785. Bprint(&out, "!address\n");
  1786. return nil;
  1787. }
  1788. switch(c->an){
  1789. case 2:
  1790. file = c->av[1];
  1791. break;
  1792. case 1:
  1793. if(*m->filename == 0){
  1794. fprint(2, "!usage: w filename\n");
  1795. return nil;
  1796. }
  1797. file = strrchr(m->filename, '/');
  1798. if(file != nil)
  1799. file++;
  1800. else
  1801. file = m->filename;
  1802. break;
  1803. default:
  1804. fprint(2, "!usage: w filename\n");
  1805. return nil;
  1806. }
  1807. if(appendtofile(m, "body", file, 0) < 0)
  1808. return nil;
  1809. m->stored = 1;
  1810. return m;
  1811. }
  1812. char *specialfile[] =
  1813. {
  1814. "pipeto",
  1815. "pipefrom",
  1816. "L.mbox",
  1817. "forward",
  1818. "names"
  1819. };
  1820. // return 1 if this is a special file
  1821. static int
  1822. special(String *s)
  1823. {
  1824. char *p;
  1825. int i;
  1826. p = strrchr(s_to_c(s), '/');
  1827. if(p == nil)
  1828. p = s_to_c(s);
  1829. else
  1830. p++;
  1831. for(i = 0; i < nelem(specialfile); i++)
  1832. if(strcmp(p, specialfile[i]) == 0)
  1833. return 1;
  1834. return 0;
  1835. }
  1836. // open the folder using the recipients account name
  1837. static String*
  1838. foldername(char *rcvr)
  1839. {
  1840. char *p;
  1841. int c;
  1842. String *file;
  1843. Dir *d;
  1844. int scarey;
  1845. file = s_new();
  1846. mboxpath("f", user, file, 0);
  1847. d = dirstat(s_to_c(file));
  1848. // if $mail/f exists, store there, otherwise in $mail
  1849. s_restart(file);
  1850. if(d && d->qid.type == QTDIR){
  1851. scarey = 0;
  1852. s_append(file, "f/");
  1853. } else {
  1854. scarey = 1;
  1855. }
  1856. free(d);
  1857. p = strrchr(rcvr, '!');
  1858. if(p != nil)
  1859. rcvr = p+1;
  1860. while(*rcvr && *rcvr != '@'){
  1861. c = *rcvr++;
  1862. if(c == '/')
  1863. c = '_';
  1864. s_putc(file, c);
  1865. }
  1866. s_terminate(file);
  1867. if(scarey && special(file)){
  1868. fprint(2, "!won't overwrite %s\n", s_to_c(file));
  1869. s_free(file);
  1870. return nil;
  1871. }
  1872. return file;
  1873. }
  1874. Message*
  1875. fcmd(Cmd *c, Message *m)
  1876. {
  1877. String *folder;
  1878. if(c->an > 1){
  1879. fprint(2, "!usage: f takes no arguments\n");
  1880. return nil;
  1881. }
  1882. if(m == &top){
  1883. Bprint(&out, "!address\n");
  1884. return nil;
  1885. }
  1886. folder = foldername(m->from);
  1887. if(folder == nil)
  1888. return nil;
  1889. if(appendtofile(m, "raw", s_to_c(folder), 1) < 0){
  1890. s_free(folder);
  1891. return nil;
  1892. }
  1893. s_free(folder);
  1894. m->stored = 1;
  1895. return m;
  1896. }
  1897. void
  1898. system(char *cmd, char **av, int in)
  1899. {
  1900. int pid;
  1901. switch(pid=fork()){
  1902. case -1:
  1903. return;
  1904. case 0:
  1905. if(in >= 0){
  1906. close(0);
  1907. dup(in, 0);
  1908. close(in);
  1909. }
  1910. if(wd[0] != 0)
  1911. chdir(wd);
  1912. exec(cmd, av);
  1913. fprint(2, "!couldn't exec %s\n", cmd);
  1914. exits(0);
  1915. default:
  1916. if(in >= 0)
  1917. close(in);
  1918. while(waitpid() < 0){
  1919. if(!interrupted)
  1920. break;
  1921. postnote(PNPROC, pid, "die");
  1922. continue;
  1923. }
  1924. break;
  1925. }
  1926. }
  1927. Message*
  1928. bangcmd(Cmd *c, Message *m)
  1929. {
  1930. char cmd[4*1024];
  1931. char *p, *e;
  1932. char *av[4];
  1933. int i;
  1934. cmd[0] = 0;
  1935. p = cmd;
  1936. e = cmd+sizeof(cmd);
  1937. for(i = 1; i < c->an; i++)
  1938. p = seprint(p, e, "%s ", c->av[i]);
  1939. av[0] = "rc";
  1940. av[1] = "-c";
  1941. av[2] = cmd;
  1942. av[3] = 0;
  1943. system("/bin/rc", av, -1);
  1944. Bprint(&out, "!\n");
  1945. return m;
  1946. }
  1947. Message*
  1948. xpipecmd(Cmd *c, Message *m, char *part)
  1949. {
  1950. char cmd[128];
  1951. char *p, *e;
  1952. char *av[4];
  1953. String *path;
  1954. int i, fd;
  1955. if(c->an < 2){
  1956. Bprint(&out, "!usage: | cmd\n");
  1957. return nil;
  1958. }
  1959. if(m == &top){
  1960. Bprint(&out, "!address\n");
  1961. return nil;
  1962. }
  1963. path = extendpath(m->path, part);
  1964. fd = open(s_to_c(path), OREAD);
  1965. s_free(path);
  1966. if(fd < 0){ // compatibility with older upas/fs
  1967. path = extendpath(m->path, "raw");
  1968. fd = open(s_to_c(path), OREAD);
  1969. s_free(path);
  1970. }
  1971. if(fd < 0){
  1972. fprint(2, "!message disappeared\n");
  1973. return nil;
  1974. }
  1975. p = cmd;
  1976. e = cmd+sizeof(cmd);
  1977. cmd[0] = 0;
  1978. for(i = 1; i < c->an; i++)
  1979. p = seprint(p, e, "%s ", c->av[i]);
  1980. av[0] = "rc";
  1981. av[1] = "-c";
  1982. av[2] = cmd;
  1983. av[3] = 0;
  1984. system("/bin/rc", av, fd); /* system closes fd */
  1985. Bprint(&out, "!\n");
  1986. return m;
  1987. }
  1988. Message*
  1989. pipecmd(Cmd *c, Message *m)
  1990. {
  1991. return xpipecmd(c, m, "body");
  1992. }
  1993. Message*
  1994. rpipecmd(Cmd *c, Message *m)
  1995. {
  1996. return xpipecmd(c, m, "rawunix");
  1997. }
  1998. void
  1999. closemb(void)
  2000. {
  2001. int fd;
  2002. fd = open("/mail/fs/ctl", ORDWR);
  2003. if(fd < 0)
  2004. sysfatal("can't open /mail/fs/ctl: %r");
  2005. // close current mailbox
  2006. if(*mbname && strcmp(mbname, "mbox") != 0)
  2007. fprint(fd, "close %s", mbname);
  2008. close(fd);
  2009. }
  2010. int
  2011. switchmb(char *file, char *singleton)
  2012. {
  2013. char *p;
  2014. int n, fd;
  2015. String *path;
  2016. char buf[256];
  2017. // if the user didn't say anything and there
  2018. // is an mbox mounted already, use that one
  2019. // so that the upas/fs -fdefault default is honored.
  2020. if(file
  2021. || (singleton && access(singleton, 0)<0)
  2022. || (!singleton && access("/mail/fs/mbox", 0)<0)){
  2023. if(file == nil)
  2024. file = "mbox";
  2025. // close current mailbox
  2026. closemb();
  2027. didopen = 1;
  2028. fd = open("/mail/fs/ctl", ORDWR);
  2029. if(fd < 0)
  2030. sysfatal("can't open /mail/fs/ctl: %r");
  2031. path = s_new();
  2032. // get an absolute path to the mail box
  2033. if(strncmp(file, "./", 2) == 0){
  2034. // resolve path here since upas/fs doesn't know
  2035. // our working directory
  2036. if(getwd(buf, sizeof(buf)-strlen(file)) == nil){
  2037. fprint(2, "!can't get working directory: %s\n", buf);
  2038. return -1;
  2039. }
  2040. s_append(path, buf);
  2041. s_append(path, file+1);
  2042. } else {
  2043. mboxpath(file, user, path, 0);
  2044. }
  2045. // make up a handle to use when talking to fs
  2046. p = strrchr(file, '/');
  2047. if(p == nil){
  2048. // if its in the mailbox directory, just use the name
  2049. strncpy(mbname, file, sizeof(mbname));
  2050. mbname[sizeof(mbname)-1] = 0;
  2051. } else {
  2052. // make up a mailbox name
  2053. p = strrchr(s_to_c(path), '/');
  2054. p++;
  2055. if(*p == 0){
  2056. fprint(2, "!bad mbox name");
  2057. return -1;
  2058. }
  2059. strncpy(mbname, p, sizeof(mbname));
  2060. mbname[sizeof(mbname)-1] = 0;
  2061. n = strlen(mbname);
  2062. if(n > Elemlen-12)
  2063. n = Elemlen-12;
  2064. sprint(mbname+n, "%ld", time(0));
  2065. }
  2066. if(fprint(fd, "open %s %s", s_to_c(path), mbname) < 0){
  2067. fprint(2, "!can't 'open %s %s': %r\n", file, mbname);
  2068. s_free(path);
  2069. return -1;
  2070. }
  2071. close(fd);
  2072. }else{
  2073. path = s_reset(nil);
  2074. mboxpath("mbox", user, path, 0);
  2075. strcpy(mbname, "mbox");
  2076. }
  2077. sprint(root, "/mail/fs/%s", mbname);
  2078. if(getwd(wd, sizeof(wd)) == 0)
  2079. wd[0] = 0;
  2080. if(singleton == nil && chdir(root) >= 0)
  2081. strcpy(root, ".");
  2082. rootlen = strlen(root);
  2083. if(mbpath != nil)
  2084. s_free(mbpath);
  2085. mbpath = path;
  2086. return 0;
  2087. }
  2088. // like tokenize but for into lines
  2089. int
  2090. lineize(char *s, char **f, int n)
  2091. {
  2092. int i;
  2093. for(i = 0; *s && i < n; i++){
  2094. f[i] = s;
  2095. s = strchr(s, '\n');
  2096. if(s == nil)
  2097. break;
  2098. *s++ = 0;
  2099. }
  2100. return i;
  2101. }
  2102. String*
  2103. rooted(String *s)
  2104. {
  2105. static char buf[256];
  2106. if(strcmp(root, ".") != 0)
  2107. return s;
  2108. snprint(buf, sizeof(buf), "/mail/fs/%s/%s", mbname, s_to_c(s));
  2109. s_free(s);
  2110. return s_copy(buf);
  2111. }
  2112. int
  2113. plumb(Message *m, Ctype *cp)
  2114. {
  2115. String *s;
  2116. Plumbmsg *pm;
  2117. static int fd = -2;
  2118. if(cp->plumbdest == nil)
  2119. return -1;
  2120. if(fd < -1)
  2121. fd = plumbopen("send", OWRITE);
  2122. if(fd < 0)
  2123. return -1;
  2124. pm = mallocz(sizeof(Plumbmsg), 1);
  2125. pm->src = strdup("mail");
  2126. if(*cp->plumbdest)
  2127. pm->dst = strdup(cp->plumbdest);
  2128. pm->wdir = nil;
  2129. pm->type = strdup("text");
  2130. pm->ndata = -1;
  2131. s = rooted(extendpath(m->path, "body"));
  2132. if(cp->ext != nil){
  2133. s_append(s, ".");
  2134. s_append(s, cp->ext);
  2135. }
  2136. pm->data = strdup(s_to_c(s));
  2137. s_free(s);
  2138. plumbsend(fd, pm);
  2139. plumbfree(pm);
  2140. return 0;
  2141. }
  2142. void
  2143. regerror(char*)
  2144. {
  2145. }
  2146. String*
  2147. addrecolon(char *s)
  2148. {
  2149. String *str;
  2150. if(cistrncmp(s, "re:", 3) != 0){
  2151. str = s_copy("Re: ");
  2152. s_append(str, s);
  2153. } else
  2154. str = s_copy(s);
  2155. return str;
  2156. }
  2157. void
  2158. exitfs(char *rv)
  2159. {
  2160. if(startedfs)
  2161. unmount(nil, "/mail/fs");
  2162. chdir("/sys/src/cmd/upas/ned");
  2163. exits(rv);
  2164. }