imap4d.c 40 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110
  1. /*
  2. * This file is part of the UCB release of Plan 9. It is subject to the license
  3. * terms in the LICENSE file found in the top-level directory of this
  4. * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
  5. * part of the UCB release of Plan 9, including this file, may be copied,
  6. * modified, propagated, or distributed except according to the terms contained
  7. * in the LICENSE file.
  8. */
  9. #include <u.h>
  10. #include <libc.h>
  11. #include <auth.h>
  12. #include <bio.h>
  13. #include "imap4d.h"
  14. /*
  15. * these should be in libraries
  16. */
  17. char *csquery(char *attr, char *val, char *rattr);
  18. /*
  19. * /lib/rfc/rfc2060 imap4rev1
  20. * /lib/rfc/rfc2683 is implementation advice
  21. * /lib/rfc/rfc2342 is namespace capability
  22. * /lib/rfc/rfc2222 is security protocols
  23. * /lib/rfc/rfc1731 is security protocols
  24. * /lib/rfc/rfc2221 is LOGIN-REFERRALS
  25. * /lib/rfc/rfc2193 is MAILBOX-REFERRALS
  26. * /lib/rfc/rfc2177 is IDLE capability
  27. * /lib/rfc/rfc2195 is CRAM-MD5 authentication
  28. * /lib/rfc/rfc2088 is LITERAL+ capability
  29. * /lib/rfc/rfc1760 is S/Key authentication
  30. *
  31. * outlook uses "Secure Password Authentication" aka ntlm authentication
  32. *
  33. * capabilities from nslocum
  34. * CAPABILITY IMAP4 IMAP4REV1 NAMESPACE IDLE SCAN SORT MAILBOX-REFERRALS LOGIN-REFERRALS AUTH=LOGIN THREAD=ORDEREDSUBJECT
  35. */
  36. typedef struct ParseCmd ParseCmd;
  37. enum
  38. {
  39. UlongMax = 4294967295,
  40. };
  41. struct ParseCmd
  42. {
  43. char *name;
  44. void (*f)(char *tg, char *cmd);
  45. };
  46. static void appendCmd(char *tg, char *cmd);
  47. static void authenticateCmd(char *tg, char *cmd);
  48. static void capabilityCmd(char *tg, char *cmd);
  49. static void closeCmd(char *tg, char *cmd);
  50. static void copyCmd(char *tg, char *cmd);
  51. static void createCmd(char *tg, char *cmd);
  52. static void deleteCmd(char *tg, char *cmd);
  53. static void expungeCmd(char *tg, char *cmd);
  54. static void fetchCmd(char *tg, char *cmd);
  55. static void idleCmd(char *tg, char *cmd);
  56. static void listCmd(char *tg, char *cmd);
  57. static void loginCmd(char *tg, char *cmd);
  58. static void logoutCmd(char *tg, char *cmd);
  59. static void namespaceCmd(char *tg, char *cmd);
  60. static void noopCmd(char *tg, char *cmd);
  61. static void renameCmd(char *tg, char *cmd);
  62. static void searchCmd(char *tg, char *cmd);
  63. static void selectCmd(char *tg, char *cmd);
  64. static void statusCmd(char *tg, char *cmd);
  65. static void storeCmd(char *tg, char *cmd);
  66. static void subscribeCmd(char *tg, char *cmd);
  67. static void uidCmd(char *tg, char *cmd);
  68. static void unsubscribeCmd(char *tg, char *cmd);
  69. static void copyUCmd(char *tg, char *cmd, int uids);
  70. static void fetchUCmd(char *tg, char *cmd, int uids);
  71. static void searchUCmd(char *tg, char *cmd, int uids);
  72. static void storeUCmd(char *tg, char *cmd, int uids);
  73. static void imap4(int);
  74. static void status(int expungeable, int uids);
  75. static void cleaner(void);
  76. static void check(void);
  77. static int catcher(void*, char*);
  78. static Search *searchKey(int first);
  79. static Search *searchKeys(int first, Search *tail);
  80. static char *astring(void);
  81. static char *atomString(char *disallowed, char *initial);
  82. static char *atom(void);
  83. static void badsyn(void);
  84. static void clearcmd(void);
  85. static char *command(void);
  86. static void crnl(void);
  87. static Fetch *fetchAtt(char *s, Fetch *f);
  88. static Fetch *fetchWhat(void);
  89. static int flagList(void);
  90. static int flags(void);
  91. static int getc(void);
  92. static char *listmbox(void);
  93. static char *literal(void);
  94. static uint32_t litlen(void);
  95. static MsgSet *msgSet(int);
  96. static void mustBe(int c);
  97. static uint32_t number(int nonzero);
  98. static int peekc(void);
  99. static char *quoted(void);
  100. static void sectText(Fetch *f, int mimeOk);
  101. static uint32_t seqNo(void);
  102. static Store *storeWhat(void);
  103. static char *tag(void);
  104. static uint32_t uidNo(void);
  105. static void ungetc(void);
  106. static ParseCmd SNonAuthed[] =
  107. {
  108. {"capability", capabilityCmd},
  109. {"logout", logoutCmd},
  110. {"x-exit", logoutCmd},
  111. {"noop", noopCmd},
  112. {"login", loginCmd},
  113. {"authenticate", authenticateCmd},
  114. nil
  115. };
  116. static ParseCmd SAuthed[] =
  117. {
  118. {"capability", capabilityCmd},
  119. {"logout", logoutCmd},
  120. {"x-exit", logoutCmd},
  121. {"noop", noopCmd},
  122. {"append", appendCmd},
  123. {"create", createCmd},
  124. {"delete", deleteCmd},
  125. {"examine", selectCmd},
  126. {"select", selectCmd},
  127. {"idle", idleCmd},
  128. {"list", listCmd},
  129. {"lsub", listCmd},
  130. {"namespace", namespaceCmd},
  131. {"rename", renameCmd},
  132. {"status", statusCmd},
  133. {"subscribe", subscribeCmd},
  134. {"unsubscribe", unsubscribeCmd},
  135. nil
  136. };
  137. static ParseCmd SSelected[] =
  138. {
  139. {"capability", capabilityCmd},
  140. {"logout", logoutCmd},
  141. {"x-exit", logoutCmd},
  142. {"noop", noopCmd},
  143. {"append", appendCmd},
  144. {"create", createCmd},
  145. {"delete", deleteCmd},
  146. {"examine", selectCmd},
  147. {"select", selectCmd},
  148. {"idle", idleCmd},
  149. {"list", listCmd},
  150. {"lsub", listCmd},
  151. {"namespace", namespaceCmd},
  152. {"rename", renameCmd},
  153. {"status", statusCmd},
  154. {"subscribe", subscribeCmd},
  155. {"unsubscribe", unsubscribeCmd},
  156. {"check", noopCmd},
  157. {"close", closeCmd},
  158. {"copy", copyCmd},
  159. {"expunge", expungeCmd},
  160. {"fetch", fetchCmd},
  161. {"search", searchCmd},
  162. {"store", storeCmd},
  163. {"uid", uidCmd},
  164. nil
  165. };
  166. static char *atomStop = "(){%*\"\\";
  167. static Chalstate *chal;
  168. static int chaled;
  169. static ParseCmd *imapState;
  170. static jmp_buf parseJmp;
  171. static char *parseMsg;
  172. static int allowPass;
  173. static int allowCR;
  174. static int exiting;
  175. static QLock imaplock;
  176. static int idlepid = -1;
  177. Biobuf bout;
  178. Biobuf bin;
  179. char username[UserNameLen];
  180. char mboxDir[MboxNameLen];
  181. char *servername;
  182. char *site;
  183. char *remote;
  184. Box *selected;
  185. Bin *parseBin;
  186. int debug;
  187. void
  188. main(int argc, char *argv[])
  189. {
  190. char *s, *t;
  191. int preauth, n;
  192. Binit(&bin, 0, OREAD);
  193. Binit(&bout, 1, OWRITE);
  194. preauth = 0;
  195. allowPass = 0;
  196. allowCR = 0;
  197. ARGBEGIN{
  198. case 'a':
  199. preauth = 1;
  200. break;
  201. case 'd':
  202. site = ARGF();
  203. break;
  204. case 'c':
  205. allowCR = 1;
  206. break;
  207. case 'p':
  208. allowPass = 1;
  209. break;
  210. case 'r':
  211. remote = ARGF();
  212. break;
  213. case 's':
  214. servername = ARGF();
  215. break;
  216. case 'v':
  217. debug = 1;
  218. debuglog("imap4d debugging enabled\n");
  219. break;
  220. default:
  221. fprint(2, "usage: ip/imap4d [-acpv] [-d site] [-r remotehost] [-s servername]\n");
  222. bye("usage");
  223. break;
  224. }ARGEND
  225. if(allowPass && allowCR){
  226. fprint(2, "%s: -c and -p are mutually exclusive\n", argv0);
  227. bye("usage");
  228. }
  229. if(preauth)
  230. setupuser(nil);
  231. if(servername == nil){
  232. servername = csquery("sys", sysname(), "dom");
  233. if(servername == nil)
  234. servername = sysname();
  235. if(servername == nil){
  236. fprint(2, "ip/imap4d can't find server name: %r\n");
  237. bye("can't find system name");
  238. }
  239. }
  240. if(site == nil){
  241. t = getenv("site");
  242. if(t == nil)
  243. site = servername;
  244. else{
  245. n = strlen(t);
  246. s = strchr(servername, '.');
  247. if(s == nil)
  248. s = servername;
  249. else
  250. s++;
  251. n += strlen(s) + 2;
  252. site = emalloc(n);
  253. snprint(site, n, "%s.%s", t, s);
  254. }
  255. }
  256. rfork(RFNOTEG|RFREND);
  257. atnotify(catcher, 1);
  258. qlock(&imaplock);
  259. atexit(cleaner);
  260. imap4(preauth);
  261. }
  262. static void
  263. imap4(int preauth)
  264. {
  265. char *volatile tg;
  266. char *volatile cmd;
  267. ParseCmd *st;
  268. if(preauth){
  269. Bprint(&bout, "* preauth %s IMAP4rev1 server ready user %s authenticated\r\n", servername, username);
  270. imapState = SAuthed;
  271. }else{
  272. Bprint(&bout, "* OK %s IMAP4rev1 server ready\r\n", servername);
  273. imapState = SNonAuthed;
  274. }
  275. if(Bflush(&bout) < 0)
  276. writeErr();
  277. chaled = 0;
  278. tg = nil;
  279. cmd = nil;
  280. if(setjmp(parseJmp)){
  281. if(tg == nil)
  282. Bprint(&bout, "* bad empty command line: %s\r\n", parseMsg);
  283. else if(cmd == nil)
  284. Bprint(&bout, "%s BAD no command: %s\r\n", tg, parseMsg);
  285. else
  286. Bprint(&bout, "%s BAD %s %s\r\n", tg, cmd, parseMsg);
  287. clearcmd();
  288. if(Bflush(&bout) < 0)
  289. writeErr();
  290. binfree(&parseBin);
  291. }
  292. for(;;){
  293. if(mbLocked())
  294. bye("internal error: mailbox lock held");
  295. tg = nil;
  296. cmd = nil;
  297. tg = tag();
  298. mustBe(' ');
  299. cmd = atom();
  300. /*
  301. * note: outlook express is broken: it requires echoing the
  302. * command as part of matching response
  303. */
  304. for(st = imapState; st->name != nil; st++){
  305. if(cistrcmp(cmd, st->name) == 0){
  306. (*st->f)(tg, cmd);
  307. break;
  308. }
  309. }
  310. if(st->name == nil){
  311. clearcmd();
  312. Bprint(&bout, "%s BAD %s illegal command\r\n", tg, cmd);
  313. }
  314. if(Bflush(&bout) < 0)
  315. writeErr();
  316. binfree(&parseBin);
  317. }
  318. }
  319. void
  320. bye(char *fmt, ...)
  321. {
  322. va_list arg;
  323. va_start(arg, fmt);
  324. Bprint(&bout, "* bye ");
  325. Bvprint(&bout, fmt, arg);
  326. Bprint(&bout, "\r\n");
  327. Bflush(&bout);
  328. exits("rob2");
  329. exits(0);
  330. }
  331. void
  332. parseErr(char *msg)
  333. {
  334. parseMsg = msg;
  335. longjmp(parseJmp, 1);
  336. }
  337. /*
  338. * an error occured while writing to the client
  339. */
  340. void
  341. writeErr(void)
  342. {
  343. cleaner();
  344. _exits("connection closed");
  345. }
  346. static int
  347. catcher(void *v, char *msg)
  348. {
  349. USED(v);
  350. if(strstr(msg, "closed pipe") != nil)
  351. return 1;
  352. return 0;
  353. }
  354. /*
  355. * wipes out the idleCmd backgroung process if it is around.
  356. * this can only be called if the current proc has qlocked imaplock.
  357. * it must be the last piece of imap4d code executed.
  358. */
  359. static void
  360. cleaner(void)
  361. {
  362. int i;
  363. if(idlepid < 0)
  364. return;
  365. exiting = 1;
  366. close(0);
  367. close(1);
  368. close(2);
  369. /*
  370. * the other proc is either stuck in a read, a sleep,
  371. * or is trying to lock imap4lock.
  372. * get him out of it so he can exit cleanly
  373. */
  374. qunlock(&imaplock);
  375. for(i = 0; i < 4; i++)
  376. postnote(PNGROUP, getpid(), "die");
  377. }
  378. /*
  379. * send any pending status updates to the client
  380. * careful: shouldn't exit, because called by idle polling proc
  381. *
  382. * can't always send pending info
  383. * in particular, can't send expunge info
  384. * in response to a fetch, store, or search command.
  385. *
  386. * rfc2060 5.2: server must send mailbox size updates
  387. * rfc2060 5.2: server may send flag updates
  388. * rfc2060 5.5: servers prohibited from sending expunge while fetch, store, search in progress
  389. * rfc2060 7: in selected state, server checks mailbox for new messages as part of every command
  390. * sends untagged EXISTS and RECENT respsonses reflecting new size of the mailbox
  391. * should also send appropriate untagged FETCH and EXPUNGE messages if another agent
  392. * changes the state of any message flags or expunges any messages
  393. * rfc2060 7.4.1 expunge server response must not be sent when no command is in progress,
  394. * nor while responding to a fetch, stort, or search command (uid versions are ok)
  395. * command only "in progress" after entirely parsed.
  396. *
  397. * strategy for third party deletion of messages or of a mailbox
  398. *
  399. * deletion of a selected mailbox => act like all message are expunged
  400. * not strictly allowed by rfc2180, but close to method 3.2.
  401. *
  402. * renaming same as deletion
  403. *
  404. * copy
  405. * reject iff a deleted message is in the request
  406. *
  407. * search, store, fetch operations on expunged messages
  408. * ignore the expunged messages
  409. * return tagged no if referenced
  410. */
  411. static void
  412. status(int expungeable, int uids)
  413. {
  414. int tell;
  415. if(!selected)
  416. return;
  417. tell = 0;
  418. if(expungeable)
  419. tell = expungeMsgs(selected, 1);
  420. if(selected->sendFlags)
  421. sendFlags(selected, uids);
  422. if(tell || selected->toldMax != selected->max){
  423. Bprint(&bout, "* %lud EXISTS\r\n", selected->max);
  424. selected->toldMax = selected->max;
  425. }
  426. if(tell || selected->toldRecent != selected->recent){
  427. Bprint(&bout, "* %lud RECENT\r\n", selected->recent);
  428. selected->toldRecent = selected->recent;
  429. }
  430. if(tell)
  431. closeImp(selected, checkBox(selected, 1));
  432. }
  433. /*
  434. * careful: can't exit, because called by idle polling proc
  435. */
  436. static void
  437. check(void)
  438. {
  439. if(!selected)
  440. return;
  441. checkBox(selected, 0);
  442. status(1, 0);
  443. }
  444. static void
  445. appendCmd(char *tg, char *cmd)
  446. {
  447. char *mbox, head[128];
  448. uint32_t t, n, now;
  449. int flags, ok;
  450. mustBe(' ');
  451. mbox = astring();
  452. mustBe(' ');
  453. flags = 0;
  454. if(peekc() == '('){
  455. flags = flagList();
  456. mustBe(' ');
  457. }
  458. now = time(nil);
  459. if(peekc() == '"'){
  460. t = imap4DateTime(quoted());
  461. if(t == ~0)
  462. parseErr("illegal date format");
  463. mustBe(' ');
  464. if(t > now)
  465. t = now;
  466. }else
  467. t = now;
  468. n = litlen();
  469. mbox = mboxName(mbox);
  470. if(mbox == nil || !okMbox(mbox)){
  471. check();
  472. Bprint(&bout, "%s NO %s bad mailbox\r\n", tg, cmd);
  473. return;
  474. }
  475. if(!cdExists(mboxDir, mbox)){
  476. check();
  477. Bprint(&bout, "%s NO [TRYCREATE] %s mailbox does not exist\r\n", tg, cmd);
  478. return;
  479. }
  480. snprint(head, sizeof(head), "From %s %s", username, ctime(t));
  481. ok = appendSave(mbox, flags, head, &bin, n);
  482. crnl();
  483. check();
  484. if(ok)
  485. Bprint(&bout, "%s OK %s completed\r\n", tg, cmd);
  486. else
  487. Bprint(&bout, "%s NO %s message save failed\r\n", tg, cmd);
  488. }
  489. static void
  490. authenticateCmd(char *tg, char *cmd)
  491. {
  492. char *s, *t;
  493. mustBe(' ');
  494. s = atom();
  495. crnl();
  496. auth_freechal(chal);
  497. chal = nil;
  498. if(cistrcmp(s, "cram-md5") == 0){
  499. t = cramauth();
  500. if(t == nil){
  501. Bprint(&bout, "%s OK %s\r\n", tg, cmd);
  502. imapState = SAuthed;
  503. }else
  504. Bprint(&bout, "%s NO %s failed %s\r\n", tg, cmd, t);
  505. }else
  506. Bprint(&bout, "%s NO %s unsupported authentication protocol\r\n", tg, cmd);
  507. }
  508. static void
  509. capabilityCmd(char *tg, char *cmd)
  510. {
  511. crnl();
  512. check();
  513. // nslocum's capabilities
  514. // Bprint(&bout, "* CAPABILITY IMAP4 IMAP4REV1 NAMESPACE IDLE SCAN SORT MAILBOX-REFERRALS LOGIN-REFERRALS AUTH=LOGIN THREAD=ORDEREDSUBJECT\r\n");
  515. Bprint(&bout, "* CAPABILITY IMAP4REV1 IDLE NAMESPACE AUTH=CRAM-MD5\r\n");
  516. Bprint(&bout, "%s OK %s\r\n", tg, cmd);
  517. }
  518. static void
  519. closeCmd(char *tg, char *cmd)
  520. {
  521. crnl();
  522. imapState = SAuthed;
  523. closeBox(selected, 1);
  524. selected = nil;
  525. Bprint(&bout, "%s OK %s mailbox closed, now in authenticated state\r\n", tg, cmd);
  526. }
  527. /*
  528. * note: message id's are before any pending expunges
  529. */
  530. static void
  531. copyCmd(char *tg, char *cmd)
  532. {
  533. copyUCmd(tg, cmd, 0);
  534. }
  535. static void
  536. copyUCmd(char *tg, char *cmd, int uids)
  537. {
  538. MsgSet *ms;
  539. char *uid, *mbox;
  540. uint32_t max;
  541. int ok;
  542. mustBe(' ');
  543. ms = msgSet(uids);
  544. mustBe(' ');
  545. mbox = astring();
  546. crnl();
  547. uid = "";
  548. if(uids)
  549. uid = "uid ";
  550. mbox = mboxName(mbox);
  551. if(mbox == nil || !okMbox(mbox)){
  552. status(1, uids);
  553. Bprint(&bout, "%s NO %s%s bad mailbox\r\n", tg, uid, cmd);
  554. return;
  555. }
  556. if(cistrcmp(mbox, "inbox") == 0)
  557. mbox = "mbox";
  558. if(!cdExists(mboxDir, mbox)){
  559. check();
  560. Bprint(&bout, "%s NO [TRYCREATE] %s mailbox does not exist\r\n", tg, cmd);
  561. return;
  562. }
  563. max = selected->max;
  564. checkBox(selected, 0);
  565. ok = forMsgs(selected, ms, max, uids, copyCheck, nil);
  566. if(ok)
  567. ok = forMsgs(selected, ms, max, uids, copySave, mbox);
  568. status(1, uids);
  569. if(ok)
  570. Bprint(&bout, "%s OK %s%s completed\r\n", tg, uid, cmd);
  571. else
  572. Bprint(&bout, "%s NO %s%s failed\r\n", tg, uid, cmd);
  573. }
  574. static void
  575. createCmd(char *tg, char *cmd)
  576. {
  577. char *mbox, *m;
  578. int fd, slash;
  579. mustBe(' ');
  580. mbox = astring();
  581. crnl();
  582. check();
  583. m = strchr(mbox, '\0');
  584. slash = m != mbox && m[-1] == '/';
  585. mbox = mboxName(mbox);
  586. if(mbox == nil || !okMbox(mbox)){
  587. Bprint(&bout, "%s NO %s bad mailbox\r\n", tg, cmd);
  588. return;
  589. }
  590. if(cistrcmp(mbox, "inbox") == 0){
  591. Bprint(&bout, "%s NO %s cannot remotely create INBOX\r\n", tg, cmd);
  592. return;
  593. }
  594. if(access(mbox, AEXIST) >= 0){
  595. Bprint(&bout, "%s NO %s mailbox already exists\r\n", tg, cmd);
  596. return;
  597. }
  598. fd = createBox(mbox, slash);
  599. close(fd);
  600. if(fd < 0)
  601. Bprint(&bout, "%s NO %s cannot create mailbox %s\r\n", tg, cmd, mbox);
  602. else
  603. Bprint(&bout, "%s OK %s %s completed\r\n", tg, mbox, cmd);
  604. }
  605. static void
  606. deleteCmd(char *tg, char *cmd)
  607. {
  608. char *mbox, *imp;
  609. mustBe(' ');
  610. mbox = astring();
  611. crnl();
  612. check();
  613. mbox = mboxName(mbox);
  614. if(mbox == nil || !okMbox(mbox)){
  615. Bprint(&bout, "%s NO %s bad mailbox\r\n", tg, cmd);
  616. return;
  617. }
  618. imp = impName(mbox);
  619. if(cistrcmp(mbox, "inbox") == 0
  620. || imp != nil && cdRemove(mboxDir, imp) < 0 && cdExists(mboxDir, imp)
  621. || cdRemove(mboxDir, mbox) < 0)
  622. Bprint(&bout, "%s NO %s cannot delete mailbox %s\r\n", tg, cmd, mbox);
  623. else
  624. Bprint(&bout, "%s OK %s %s completed\r\n", tg, mbox, cmd);
  625. }
  626. static void
  627. expungeCmd(char *tg, char *cmd)
  628. {
  629. int ok;
  630. crnl();
  631. ok = deleteMsgs(selected);
  632. check();
  633. if(ok)
  634. Bprint(&bout, "%s OK %s messages erased\r\n", tg, cmd);
  635. else
  636. Bprint(&bout, "%s NO %s some messages not expunged\r\n", tg, cmd);
  637. }
  638. static void
  639. fetchCmd(char *tg, char *cmd)
  640. {
  641. fetchUCmd(tg, cmd, 0);
  642. }
  643. static void
  644. fetchUCmd(char *tg, char *cmd, int uids)
  645. {
  646. Fetch *f;
  647. MsgSet *ms;
  648. MbLock *ml;
  649. char *uid;
  650. uint32_t max;
  651. int ok;
  652. mustBe(' ');
  653. ms = msgSet(uids);
  654. mustBe(' ');
  655. f = fetchWhat();
  656. crnl();
  657. uid = "";
  658. if(uids)
  659. uid = "uid ";
  660. max = selected->max;
  661. ml = checkBox(selected, 1);
  662. if(ml != nil)
  663. forMsgs(selected, ms, max, uids, fetchSeen, f);
  664. closeImp(selected, ml);
  665. ok = ml != nil && forMsgs(selected, ms, max, uids, fetchMsg, f);
  666. status(uids, uids);
  667. if(ok)
  668. Bprint(&bout, "%s OK %s%s completed\r\n", tg, uid, cmd);
  669. else
  670. Bprint(&bout, "%s NO %s%s failed\r\n", tg, uid, cmd);
  671. }
  672. static void
  673. idleCmd(char *tg, char *cmd)
  674. {
  675. int c, pid;
  676. crnl();
  677. Bprint(&bout, "+ idling, waiting for done\r\n");
  678. if(Bflush(&bout) < 0)
  679. writeErr();
  680. if(idlepid < 0){
  681. pid = rfork(RFPROC|RFMEM|RFNOWAIT);
  682. if(pid == 0){
  683. for(;;){
  684. qlock(&imaplock);
  685. if(exiting)
  686. break;
  687. /*
  688. * parent may have changed curDir, but it doesn't change our .
  689. */
  690. resetCurDir();
  691. check();
  692. if(Bflush(&bout) < 0)
  693. writeErr();
  694. qunlock(&imaplock);
  695. sleep(15*1000);
  696. enableForwarding();
  697. }
  698. _exits("rob3");
  699. _exits(0);
  700. }
  701. idlepid = pid;
  702. }
  703. qunlock(&imaplock);
  704. /*
  705. * clear out the next line, which is supposed to contain (case-insensitive)
  706. * done\n
  707. * this is special code since it has to dance with the idle polling proc
  708. * and handle exiting correctly.
  709. */
  710. for(;;){
  711. c = getc();
  712. if(c < 0){
  713. qlock(&imaplock);
  714. if(!exiting)
  715. cleaner();
  716. _exits("rob4");
  717. _exits(0);
  718. }
  719. if(c == '\n')
  720. break;
  721. }
  722. qlock(&imaplock);
  723. if(exiting)
  724. {_exits("rob5");
  725. _exits(0);
  726. }
  727. /*
  728. * child may have changed curDir, but it doesn't change our .
  729. */
  730. resetCurDir();
  731. check();
  732. Bprint(&bout, "%s OK %s terminated\r\n", tg, cmd);
  733. }
  734. static void
  735. listCmd(char *tg, char *cmd)
  736. {
  737. char *s, *t, *ss, *ref, *mbox;
  738. int n;
  739. mustBe(' ');
  740. s = astring();
  741. mustBe(' ');
  742. t = listmbox();
  743. crnl();
  744. check();
  745. ref = mutf7str(s);
  746. mbox = mutf7str(t);
  747. if(ref == nil || mbox == nil){
  748. Bprint(&bout, "%s BAD %s mailbox name not in modified utf-7\r\n", tg, cmd);
  749. return;
  750. }
  751. /*
  752. * special request for hierarchy delimiter and root name
  753. * root name appears to be name up to and including any delimiter,
  754. * or the empty string, if there is no delimiter.
  755. *
  756. * this must change if the # namespace convention is supported.
  757. */
  758. if(*mbox == '\0'){
  759. s = strchr(ref, '/');
  760. if(s == nil)
  761. ref = "";
  762. else
  763. s[1] = '\0';
  764. Bprint(&bout, "* %s (\\Noselect) \"/\" \"%s\"\r\n", cmd, ref);
  765. Bprint(&bout, "%s OK %s\r\n", tg, cmd);
  766. return;
  767. }
  768. /*
  769. * massage the listing name:
  770. * clean up the components individually,
  771. * then rip off componenets from the ref to
  772. * take care of leading ..'s in the mbox.
  773. *
  774. * the cleanup can wipe out * followed by a ..
  775. * tough luck if such a stupid pattern is given.
  776. */
  777. cleanname(mbox);
  778. if(strcmp(mbox, ".") == 0)
  779. *mbox = '\0';
  780. if(mbox[0] == '/')
  781. *ref = '\0';
  782. else if(*ref != '\0'){
  783. cleanname(ref);
  784. if(strcmp(ref, ".") == 0)
  785. *ref = '\0';
  786. }else
  787. *ref = '\0';
  788. while(*ref && isdotdot(mbox)){
  789. s = strrchr(ref, '/');
  790. if(s == nil)
  791. s = ref;
  792. if(isdotdot(s))
  793. break;
  794. *s = '\0';
  795. mbox += 2;
  796. if(*mbox == '/')
  797. mbox++;
  798. }
  799. if(*ref == '\0'){
  800. s = mbox;
  801. ss = s;
  802. }else{
  803. n = strlen(ref) + strlen(mbox) + 2;
  804. t = binalloc(&parseBin, n, 0);
  805. if(t == nil)
  806. parseErr("out of memory");
  807. snprint(t, n, "%s/%s", ref, mbox);
  808. s = t;
  809. ss = s + strlen(ref);
  810. }
  811. /*
  812. * only allow activity in /mail/box
  813. */
  814. if(s[0] == '/' || isdotdot(s)){
  815. Bprint(&bout, "%s NO illegal mailbox pattern\r\n", tg);
  816. return;
  817. }
  818. if(cistrcmp(cmd, "lsub") == 0)
  819. lsubBoxes(cmd, s, ss);
  820. else
  821. listBoxes(cmd, s, ss);
  822. Bprint(&bout, "%s OK %s completed\r\n", tg, cmd);
  823. }
  824. static char*
  825. passCR(char*u, char*p)
  826. {
  827. static char Ebadch[] = "can't get challenge";
  828. static char nchall[64];
  829. static char response[64];
  830. static Chalstate *ch = nil;
  831. AuthInfo *ai;
  832. again:
  833. if (ch == nil){
  834. if(!(ch = auth_challenge("proto=p9cr role=server user=%q", u)))
  835. return Ebadch;
  836. snprint(nchall, 64, " encrypt challenge: %s", ch->chal);
  837. return nchall;
  838. } else {
  839. strncpy(response, p, 64);
  840. ch->resp = response;
  841. ch->nresp = strlen(response);
  842. ai = auth_response(ch);
  843. auth_freechal(ch);
  844. ch = nil;
  845. if (ai == nil)
  846. goto again;
  847. setupuser(ai);
  848. return nil;
  849. }
  850. }
  851. static void
  852. loginCmd(char *tg, char *cmd)
  853. {
  854. char *s, *t;
  855. AuthInfo *ai;
  856. char*r;
  857. mustBe(' ');
  858. s = astring(); /* uid */
  859. mustBe(' ');
  860. t = astring(); /* password */
  861. crnl();
  862. if(allowCR){
  863. if ((r = passCR(s, t)) == nil){
  864. Bprint(&bout, "%s OK %s succeeded\r\n", tg, cmd);
  865. imapState = SAuthed;
  866. } else {
  867. Bprint(&bout, "* NO [ALERT] %s\r\n", r);
  868. Bprint(&bout, "%s NO %s succeeded\r\n", tg, cmd);
  869. }
  870. return;
  871. }
  872. else if(allowPass){
  873. if(ai = passLogin(s, t)){
  874. setupuser(ai);
  875. Bprint(&bout, "%s OK %s succeeded\r\n", tg, cmd);
  876. imapState = SAuthed;
  877. }else
  878. Bprint(&bout, "%s NO %s failed check\r\n", tg, cmd);
  879. return;
  880. }
  881. Bprint(&bout, "%s NO %s plaintext passwords disallowed\r\n", tg, cmd);
  882. }
  883. /*
  884. * logout or x-exit, which doesn't expunge the mailbox
  885. */
  886. static void
  887. logoutCmd(char *tg, char *cmd)
  888. {
  889. crnl();
  890. if(cmd[0] != 'x' && selected){
  891. closeBox(selected, 1);
  892. selected = nil;
  893. }
  894. Bprint(&bout, "* bye\r\n");
  895. Bprint(&bout, "%s OK %s completed\r\n", tg, cmd);
  896. exits("rob6");
  897. exits(0);
  898. }
  899. static void
  900. namespaceCmd(char *tg, char *cmd)
  901. {
  902. crnl();
  903. check();
  904. /*
  905. * personal, other users, shared namespaces
  906. * send back nil or descriptions of (prefix heirarchy-delim) for each case
  907. */
  908. Bprint(&bout, "* NAMESPACE ((\"\" \"/\")) nil nil\r\n");
  909. Bprint(&bout, "%s OK %s completed\r\n", tg, cmd);
  910. }
  911. static void
  912. noopCmd(char *tg, char *cmd)
  913. {
  914. crnl();
  915. check();
  916. Bprint(&bout, "%s OK %s completed\r\n", tg, cmd);
  917. enableForwarding();
  918. }
  919. /*
  920. * this is only a partial implementation
  921. * should copy files to other directories,
  922. * and copy & truncate inbox
  923. */
  924. static void
  925. renameCmd(char *tg, char *cmd)
  926. {
  927. char *from, *to;
  928. int ok;
  929. mustBe(' ');
  930. from = astring();
  931. mustBe(' ');
  932. to = astring();
  933. crnl();
  934. check();
  935. to = mboxName(to);
  936. if(to == nil || !okMbox(to) || cistrcmp(to, "inbox") == 0){
  937. Bprint(&bout, "%s NO %s bad mailbox destination name\r\n", tg, cmd);
  938. return;
  939. }
  940. if(access(to, AEXIST) >= 0){
  941. Bprint(&bout, "%s NO %s mailbox already exists\r\n", tg, cmd);
  942. return;
  943. }
  944. from = mboxName(from);
  945. if(from == nil || !okMbox(from)){
  946. Bprint(&bout, "%s NO %s bad mailbox destination name\r\n", tg, cmd);
  947. return;
  948. }
  949. if(cistrcmp(from, "inbox") == 0)
  950. ok = copyBox(from, to, 0);
  951. else
  952. ok = moveBox(from, to);
  953. if(ok)
  954. Bprint(&bout, "%s OK %s completed\r\n", tg, cmd);
  955. else
  956. Bprint(&bout, "%s NO %s failed\r\n", tg, cmd);
  957. }
  958. static void
  959. searchCmd(char *tg, char *cmd)
  960. {
  961. searchUCmd(tg, cmd, 0);
  962. }
  963. static void
  964. searchUCmd(char *tg, char *cmd, int uids)
  965. {
  966. Search rock;
  967. Msg *m;
  968. char *uid;
  969. uint32_t id;
  970. mustBe(' ');
  971. rock.next = nil;
  972. searchKeys(1, &rock);
  973. crnl();
  974. uid = "";
  975. if(uids)
  976. uid = "uid ";
  977. if(rock.next != nil && rock.next->key == SKCharset){
  978. if(cistrcmp(rock.next->s, "utf-8") != 0
  979. && cistrcmp(rock.next->s, "us-ascii") != 0){
  980. Bprint(&bout, "%s NO [BADCHARSET] (\"US-ASCII\" \"UTF-8\") %s%s failed\r\n",
  981. tg, uid, cmd);
  982. checkBox(selected, 0);
  983. status(uids, uids);
  984. return;
  985. }
  986. rock.next = rock.next->next;
  987. }
  988. Bprint(&bout, "* search");
  989. for(m = selected->msgs; m != nil; m = m->next)
  990. m->matched = searchMsg(m, rock.next);
  991. for(m = selected->msgs; m != nil; m = m->next){
  992. if(m->matched){
  993. if(uids)
  994. id = m->uid;
  995. else
  996. id = m->seq;
  997. Bprint(&bout, " %lud", id);
  998. }
  999. }
  1000. Bprint(&bout, "\r\n");
  1001. checkBox(selected, 0);
  1002. status(uids, uids);
  1003. Bprint(&bout, "%s OK %s%s completed\r\n", tg, uid, cmd);
  1004. }
  1005. static void
  1006. selectCmd(char *tg, char *cmd)
  1007. {
  1008. Msg *m;
  1009. char *s, *mbox;
  1010. mustBe(' ');
  1011. mbox = astring();
  1012. crnl();
  1013. if(selected){
  1014. imapState = SAuthed;
  1015. closeBox(selected, 1);
  1016. selected = nil;
  1017. }
  1018. mbox = mboxName(mbox);
  1019. if(mbox == nil || !okMbox(mbox)){
  1020. Bprint(&bout, "%s NO %s bad mailbox\r\n", tg, cmd);
  1021. return;
  1022. }
  1023. selected = openBox(mbox, "imap", cistrcmp(cmd, "select") == 0);
  1024. if(selected == nil){
  1025. Bprint(&bout, "%s NO %s can't open mailbox %s: %r\r\n", tg, cmd, mbox);
  1026. return;
  1027. }
  1028. imapState = SSelected;
  1029. Bprint(&bout, "* FLAGS (\\Seen \\Answered \\Flagged \\Deleted \\Draft)\r\n");
  1030. Bprint(&bout, "* %lud EXISTS\r\n", selected->max);
  1031. selected->toldMax = selected->max;
  1032. Bprint(&bout, "* %lud RECENT\r\n", selected->recent);
  1033. selected->toldRecent = selected->recent;
  1034. for(m = selected->msgs; m != nil; m = m->next){
  1035. if(!m->expunged && (m->flags & MSeen) != MSeen){
  1036. Bprint(&bout, "* OK [UNSEEN %ld]\r\n", m->seq);
  1037. break;
  1038. }
  1039. }
  1040. Bprint(&bout, "* OK [PERMANENTFLAGS (\\Seen \\Answered \\Flagged \\Draft \\Deleted)]\r\n");
  1041. Bprint(&bout, "* OK [UIDNEXT %ld]\r\n", selected->uidnext);
  1042. Bprint(&bout, "* OK [UIDVALIDITY %ld]\r\n", selected->uidvalidity);
  1043. s = "READ-ONLY";
  1044. if(selected->writable)
  1045. s = "READ-WRITE";
  1046. Bprint(&bout, "%s OK [%s] %s %s completed\r\n", tg, s, cmd, mbox);
  1047. }
  1048. static NamedInt statusItems[] =
  1049. {
  1050. {"MESSAGES", SMessages},
  1051. {"RECENT", SRecent},
  1052. {"UIDNEXT", SUidNext},
  1053. {"UIDVALIDITY", SUidValidity},
  1054. {"UNSEEN", SUnseen},
  1055. {nil, 0}
  1056. };
  1057. static void
  1058. statusCmd(char *tg, char *cmd)
  1059. {
  1060. Box *box;
  1061. Msg *m;
  1062. char *s, *mbox;
  1063. uint32_t v;
  1064. int si, i;
  1065. mustBe(' ');
  1066. mbox = astring();
  1067. mustBe(' ');
  1068. mustBe('(');
  1069. si = 0;
  1070. for(;;){
  1071. s = atom();
  1072. i = mapInt(statusItems, s);
  1073. if(i == 0)
  1074. parseErr("illegal status item");
  1075. si |= i;
  1076. if(peekc() == ')')
  1077. break;
  1078. mustBe(' ');
  1079. }
  1080. mustBe(')');
  1081. crnl();
  1082. mbox = mboxName(mbox);
  1083. if(mbox == nil || !okMbox(mbox)){
  1084. check();
  1085. Bprint(&bout, "%s NO %s bad mailbox\r\n", tg, cmd);
  1086. return;
  1087. }
  1088. box = openBox(mbox, "status", 1);
  1089. if(box == nil){
  1090. check();
  1091. Bprint(&bout, "%s NO [TRYCREATE] %s can't open mailbox %s: %r\r\n", tg, cmd, mbox);
  1092. return;
  1093. }
  1094. Bprint(&bout, "* STATUS %s (", mbox);
  1095. s = "";
  1096. for(i = 0; statusItems[i].name != nil; i++){
  1097. if(si & statusItems[i].v){
  1098. v = 0;
  1099. switch(statusItems[i].v){
  1100. case SMessages:
  1101. v = box->max;
  1102. break;
  1103. case SRecent:
  1104. v = box->recent;
  1105. break;
  1106. case SUidNext:
  1107. v = box->uidnext;
  1108. break;
  1109. case SUidValidity:
  1110. v = box->uidvalidity;
  1111. break;
  1112. case SUnseen:
  1113. v = 0;
  1114. for(m = box->msgs; m != nil; m = m->next)
  1115. if((m->flags & MSeen) != MSeen)
  1116. v++;
  1117. break;
  1118. default:
  1119. Bprint(&bout, ")");
  1120. bye("internal error: status item not implemented");
  1121. break;
  1122. }
  1123. Bprint(&bout, "%s%s %lud", s, statusItems[i].name, v);
  1124. s = " ";
  1125. }
  1126. }
  1127. Bprint(&bout, ")\r\n");
  1128. closeBox(box, 1);
  1129. check();
  1130. Bprint(&bout, "%s OK %s completed\r\n", tg, cmd);
  1131. }
  1132. static void
  1133. storeCmd(char *tg, char *cmd)
  1134. {
  1135. storeUCmd(tg, cmd, 0);
  1136. }
  1137. static void
  1138. storeUCmd(char *tg, char *cmd, int uids)
  1139. {
  1140. Store *st;
  1141. MsgSet *ms;
  1142. MbLock *ml;
  1143. char *uid;
  1144. uint32_t max;
  1145. int ok;
  1146. mustBe(' ');
  1147. ms = msgSet(uids);
  1148. mustBe(' ');
  1149. st = storeWhat();
  1150. crnl();
  1151. uid = "";
  1152. if(uids)
  1153. uid = "uid ";
  1154. max = selected->max;
  1155. ml = checkBox(selected, 1);
  1156. ok = ml != nil && forMsgs(selected, ms, max, uids, storeMsg, st);
  1157. closeImp(selected, ml);
  1158. status(uids, uids);
  1159. if(ok)
  1160. Bprint(&bout, "%s OK %s%s completed\r\n", tg, uid, cmd);
  1161. else
  1162. Bprint(&bout, "%s NO %s%s failed\r\n", tg, uid, cmd);
  1163. }
  1164. /*
  1165. * minimal implementation of subscribe
  1166. * all folders are automatically subscribed,
  1167. * and can't be unsubscribed
  1168. */
  1169. static void
  1170. subscribeCmd(char *tg, char *cmd)
  1171. {
  1172. Box *box;
  1173. char *mbox;
  1174. int ok;
  1175. mustBe(' ');
  1176. mbox = astring();
  1177. crnl();
  1178. check();
  1179. mbox = mboxName(mbox);
  1180. ok = 0;
  1181. if(mbox != nil && okMbox(mbox)){
  1182. box = openBox(mbox, "subscribe", 0);
  1183. if(box != nil){
  1184. ok = subscribe(mbox, 's');
  1185. closeBox(box, 1);
  1186. }
  1187. }
  1188. if(!ok)
  1189. Bprint(&bout, "%s NO %s bad mailbox\r\n", tg, cmd);
  1190. else
  1191. Bprint(&bout, "%s OK %s completed\r\n", tg, cmd);
  1192. }
  1193. static void
  1194. uidCmd(char *tg, char *cmd)
  1195. {
  1196. char *sub;
  1197. mustBe(' ');
  1198. sub = atom();
  1199. if(cistrcmp(sub, "copy") == 0)
  1200. copyUCmd(tg, sub, 1);
  1201. else if(cistrcmp(sub, "fetch") == 0)
  1202. fetchUCmd(tg, sub, 1);
  1203. else if(cistrcmp(sub, "search") == 0)
  1204. searchUCmd(tg, sub, 1);
  1205. else if(cistrcmp(sub, "store") == 0)
  1206. storeUCmd(tg, sub, 1);
  1207. else{
  1208. clearcmd();
  1209. Bprint(&bout, "%s BAD %s illegal uid command %s\r\n", tg, cmd, sub);
  1210. }
  1211. }
  1212. static void
  1213. unsubscribeCmd(char *tg, char *cmd)
  1214. {
  1215. char *mbox;
  1216. mustBe(' ');
  1217. mbox = astring();
  1218. crnl();
  1219. check();
  1220. mbox = mboxName(mbox);
  1221. if(mbox == nil || !okMbox(mbox) || !subscribe(mbox, 'u'))
  1222. Bprint(&bout, "%s NO %s can't unsubscribe\r\n", tg, cmd);
  1223. else
  1224. Bprint(&bout, "%s OK %s completed\r\n", tg, cmd);
  1225. }
  1226. static void
  1227. badsyn(void)
  1228. {
  1229. parseErr("bad syntax");
  1230. }
  1231. static void
  1232. clearcmd(void)
  1233. {
  1234. int c;
  1235. for(;;){
  1236. c = getc();
  1237. if(c < 0)
  1238. bye("end of input");
  1239. if(c == '\n')
  1240. return;
  1241. }
  1242. }
  1243. static void
  1244. crnl(void)
  1245. {
  1246. int c;
  1247. c = getc();
  1248. if(c == '\n')
  1249. return;
  1250. if(c != '\r' || getc() != '\n')
  1251. badsyn();
  1252. }
  1253. static void
  1254. mustBe(int c)
  1255. {
  1256. if(getc() != c){
  1257. ungetc();
  1258. badsyn();
  1259. }
  1260. }
  1261. /*
  1262. * flaglist : '(' ')' | '(' flags ')'
  1263. */
  1264. static int
  1265. flagList(void)
  1266. {
  1267. int f;
  1268. mustBe('(');
  1269. f = 0;
  1270. if(peekc() != ')')
  1271. f = flags();
  1272. mustBe(')');
  1273. return f;
  1274. }
  1275. /*
  1276. * flags : flag | flags ' ' flag
  1277. * flag : '\' atom | atom
  1278. */
  1279. static int
  1280. flags(void)
  1281. {
  1282. int ff, flags;
  1283. char *s;
  1284. int c;
  1285. flags = 0;
  1286. for(;;){
  1287. c = peekc();
  1288. if(c == '\\'){
  1289. mustBe('\\');
  1290. s = atomString(atomStop, "\\");
  1291. }else if(strchr(atomStop, c) != nil)
  1292. s = atom();
  1293. else
  1294. break;
  1295. ff = mapFlag(s);
  1296. if(ff == 0)
  1297. parseErr("flag not supported");
  1298. flags |= ff;
  1299. if(peekc() != ' ')
  1300. break;
  1301. mustBe(' ');
  1302. }
  1303. if(flags == 0)
  1304. parseErr("no flags given");
  1305. return flags;
  1306. }
  1307. /*
  1308. * storeWhat : osign 'FLAGS' ' ' storeflags
  1309. * | osign 'FLAGS.SILENT' ' ' storeflags
  1310. * osign :
  1311. * | '+' | '-'
  1312. * storeflags : flagList | flags
  1313. */
  1314. static Store*
  1315. storeWhat(void)
  1316. {
  1317. int f;
  1318. char *s;
  1319. int c, w;
  1320. c = peekc();
  1321. if(c == '+' || c == '-')
  1322. mustBe(c);
  1323. else
  1324. c = 0;
  1325. s = atom();
  1326. w = 0;
  1327. if(cistrcmp(s, "flags") == 0)
  1328. w = STFlags;
  1329. else if(cistrcmp(s, "flags.silent") == 0)
  1330. w = STFlagsSilent;
  1331. else
  1332. parseErr("illegal store attribute");
  1333. mustBe(' ');
  1334. if(peekc() == '(')
  1335. f = flagList();
  1336. else
  1337. f = flags();
  1338. return mkStore(c, w, f);
  1339. }
  1340. /*
  1341. * fetchWhat : "ALL" | "FULL" | "FAST" | fetchAtt | '(' fetchAtts ')'
  1342. * fetchAtts : fetchAtt | fetchAtts ' ' fetchAtt
  1343. */
  1344. static char *fetchAtom = "(){}%*\"\\[]";
  1345. static Fetch*
  1346. fetchWhat(void)
  1347. {
  1348. Fetch *f;
  1349. char *s;
  1350. if(peekc() == '('){
  1351. getc();
  1352. f = nil;
  1353. for(;;){
  1354. s = atomString(fetchAtom, "");
  1355. f = fetchAtt(s, f);
  1356. if(peekc() == ')')
  1357. break;
  1358. mustBe(' ');
  1359. }
  1360. getc();
  1361. return revFetch(f);
  1362. }
  1363. s = atomString(fetchAtom, "");
  1364. if(cistrcmp(s, "all") == 0)
  1365. f = mkFetch(FFlags, mkFetch(FInternalDate, mkFetch(FRfc822Size, mkFetch(FEnvelope, nil))));
  1366. else if(cistrcmp(s, "fast") == 0)
  1367. f = mkFetch(FFlags, mkFetch(FInternalDate, mkFetch(FRfc822Size, nil)));
  1368. else if(cistrcmp(s, "full") == 0)
  1369. f = mkFetch(FFlags, mkFetch(FInternalDate, mkFetch(FRfc822Size, mkFetch(FEnvelope, mkFetch(FBody, nil)))));
  1370. else
  1371. f = fetchAtt(s, nil);
  1372. return f;
  1373. }
  1374. /*
  1375. * fetchAtt : "ENVELOPE" | "FLAGS" | "INTERNALDATE"
  1376. * | "RFC822" | "RFC822.HEADER" | "RFC822.SIZE" | "RFC822.TEXT"
  1377. * | "BODYSTRUCTURE"
  1378. * | "UID"
  1379. * | "BODY"
  1380. * | "BODY" bodysubs
  1381. * | "BODY.PEEK" bodysubs
  1382. * bodysubs : sect
  1383. * | sect '<' number '.' nz-number '>'
  1384. * sect : '[' sectSpec ']'
  1385. * sectSpec : sectMsgText
  1386. * | sectPart
  1387. * | sectPart '.' sectText
  1388. * sectPart : nz-number
  1389. * | sectPart '.' nz-number
  1390. */
  1391. static Fetch*
  1392. fetchAtt(char *s, Fetch *f)
  1393. {
  1394. NList *sect;
  1395. int c;
  1396. if(cistrcmp(s, "envelope") == 0)
  1397. return mkFetch(FEnvelope, f);
  1398. if(cistrcmp(s, "flags") == 0)
  1399. return mkFetch(FFlags, f);
  1400. if(cistrcmp(s, "internaldate") == 0)
  1401. return mkFetch(FInternalDate, f);
  1402. if(cistrcmp(s, "RFC822") == 0)
  1403. return mkFetch(FRfc822, f);
  1404. if(cistrcmp(s, "RFC822.header") == 0)
  1405. return mkFetch(FRfc822Head, f);
  1406. if(cistrcmp(s, "RFC822.size") == 0)
  1407. return mkFetch(FRfc822Size, f);
  1408. if(cistrcmp(s, "RFC822.text") == 0)
  1409. return mkFetch(FRfc822Text, f);
  1410. if(cistrcmp(s, "bodystructure") == 0)
  1411. return mkFetch(FBodyStruct, f);
  1412. if(cistrcmp(s, "uid") == 0)
  1413. return mkFetch(FUid, f);
  1414. if(cistrcmp(s, "body") == 0){
  1415. if(peekc() != '[')
  1416. return mkFetch(FBody, f);
  1417. f = mkFetch(FBodySect, f);
  1418. }else if(cistrcmp(s, "body.peek") == 0)
  1419. f = mkFetch(FBodyPeek, f);
  1420. else
  1421. parseErr("illegal fetch attribute");
  1422. mustBe('[');
  1423. c = peekc();
  1424. if(c >= '1' && c <= '9'){
  1425. sect = mkNList(number(1), nil);
  1426. while(peekc() == '.'){
  1427. getc();
  1428. c = peekc();
  1429. if(c >= '1' && c <= '9'){
  1430. sect = mkNList(number(1), sect);
  1431. }else{
  1432. break;
  1433. }
  1434. }
  1435. f->sect = revNList(sect);
  1436. }
  1437. if(peekc() != ']')
  1438. sectText(f, f->sect != nil);
  1439. mustBe(']');
  1440. if(peekc() != '<')
  1441. return f;
  1442. f->partial = 1;
  1443. mustBe('<');
  1444. f->start = number(0);
  1445. mustBe('.');
  1446. f->size = number(1);
  1447. mustBe('>');
  1448. return f;
  1449. }
  1450. /*
  1451. * sectText : sectMsgText | "MIME"
  1452. * sectMsgText : "HEADER"
  1453. * | "TEXT"
  1454. * | "HEADER.FIELDS" ' ' hdrList
  1455. * | "HEADER.FIELDS.NOT" ' ' hdrList
  1456. * hdrList : '(' hdrs ')'
  1457. * hdrs: : astring
  1458. * | hdrs ' ' astring
  1459. */
  1460. static void
  1461. sectText(Fetch *f, int mimeOk)
  1462. {
  1463. SList *h;
  1464. char *s;
  1465. s = atomString(fetchAtom, "");
  1466. if(cistrcmp(s, "header") == 0){
  1467. f->part = FPHead;
  1468. return;
  1469. }
  1470. if(cistrcmp(s, "text") == 0){
  1471. f->part = FPText;
  1472. return;
  1473. }
  1474. if(mimeOk && cistrcmp(s, "mime") == 0){
  1475. f->part = FPMime;
  1476. return;
  1477. }
  1478. if(cistrcmp(s, "header.fields") == 0)
  1479. f->part = FPHeadFields;
  1480. else if(cistrcmp(s, "header.fields.not") == 0)
  1481. f->part = FPHeadFieldsNot;
  1482. else
  1483. parseErr("illegal fetch section text");
  1484. mustBe(' ');
  1485. mustBe('(');
  1486. h = nil;
  1487. for(;;){
  1488. h = mkSList(astring(), h);
  1489. if(peekc() == ')')
  1490. break;
  1491. mustBe(' ');
  1492. }
  1493. mustBe(')');
  1494. f->hdrs = revSList(h);
  1495. }
  1496. /*
  1497. * searchWhat : "CHARSET" ' ' astring searchkeys | searchkeys
  1498. * searchkeys : searchkey | searchkeys ' ' searchkey
  1499. * searchkey : "ALL" | "ANSWERED" | "DELETED" | "FLAGGED" | "NEW" | "OLD" | "RECENT"
  1500. * | "SEEN" | "UNANSWERED" | "UNDELETED" | "UNFLAGGED" | "DRAFT" | "UNDRAFT"
  1501. * | astrkey ' ' astring
  1502. * | datekey ' ' date
  1503. * | "KEYWORD" ' ' flag | "UNKEYWORD" flag
  1504. * | "LARGER" ' ' number | "SMALLER" ' ' number
  1505. * | "HEADER" astring ' ' astring
  1506. * | set | "UID" ' ' set
  1507. * | "NOT" ' ' searchkey
  1508. * | "OR" ' ' searchkey ' ' searchkey
  1509. * | '(' searchkeys ')'
  1510. * astrkey : "BCC" | "BODY" | "CC" | "FROM" | "SUBJECT" | "TEXT" | "TO"
  1511. * datekey : "BEFORE" | "ON" | "SINCE" | "SENTBEFORE" | "SENTON" | "SENTSINCE"
  1512. */
  1513. static NamedInt searchMap[] =
  1514. {
  1515. {"ALL", SKAll},
  1516. {"ANSWERED", SKAnswered},
  1517. {"DELETED", SKDeleted},
  1518. {"FLAGGED", SKFlagged},
  1519. {"NEW", SKNew},
  1520. {"OLD", SKOld},
  1521. {"RECENT", SKRecent},
  1522. {"SEEN", SKSeen},
  1523. {"UNANSWERED", SKUnanswered},
  1524. {"UNDELETED", SKUndeleted},
  1525. {"UNFLAGGED", SKUnflagged},
  1526. {"DRAFT", SKDraft},
  1527. {"UNDRAFT", SKUndraft},
  1528. {"UNSEEN", SKUnseen},
  1529. {nil, 0}
  1530. };
  1531. static NamedInt searchMapStr[] =
  1532. {
  1533. {"CHARSET", SKCharset},
  1534. {"BCC", SKBcc},
  1535. {"BODY", SKBody},
  1536. {"CC", SKCc},
  1537. {"FROM", SKFrom},
  1538. {"SUBJECT", SKSubject},
  1539. {"TEXT", SKText},
  1540. {"TO", SKTo},
  1541. {nil, 0}
  1542. };
  1543. static NamedInt searchMapDate[] =
  1544. {
  1545. {"BEFORE", SKBefore},
  1546. {"ON", SKOn},
  1547. {"SINCE", SKSince},
  1548. {"SENTBEFORE", SKSentBefore},
  1549. {"SENTON", SKSentOn},
  1550. {"SENTSINCE", SKSentSince},
  1551. {nil, 0}
  1552. };
  1553. static NamedInt searchMapFlag[] =
  1554. {
  1555. {"KEYWORD", SKKeyword},
  1556. {"UNKEYWORD", SKUnkeyword},
  1557. {nil, 0}
  1558. };
  1559. static NamedInt searchMapNum[] =
  1560. {
  1561. {"SMALLER", SKSmaller},
  1562. {"LARGER", SKLarger},
  1563. {nil, 0}
  1564. };
  1565. static Search*
  1566. searchKeys(int first, Search *tail)
  1567. {
  1568. Search *s;
  1569. for(;;){
  1570. if(peekc() == '('){
  1571. getc();
  1572. tail = searchKeys(0, tail);
  1573. mustBe(')');
  1574. }else{
  1575. s = searchKey(first);
  1576. tail->next = s;
  1577. tail = s;
  1578. }
  1579. first = 0;
  1580. if(peekc() != ' ')
  1581. break;
  1582. getc();
  1583. }
  1584. return tail;
  1585. }
  1586. static Search*
  1587. searchKey(int first)
  1588. {
  1589. Search *sr, rock;
  1590. Tm tm;
  1591. char *a;
  1592. int i, c;
  1593. sr = binalloc(&parseBin, sizeof(Search), 1);
  1594. if(sr == nil)
  1595. parseErr("out of memory");
  1596. c = peekc();
  1597. if(c >= '0' && c <= '9'){
  1598. sr->key = SKSet;
  1599. sr->set = msgSet(0);
  1600. return sr;
  1601. }
  1602. a = atom();
  1603. if(i = mapInt(searchMap, a))
  1604. sr->key = i;
  1605. else if(i = mapInt(searchMapStr, a)){
  1606. if(!first && i == SKCharset)
  1607. parseErr("illegal search key");
  1608. sr->key = i;
  1609. mustBe(' ');
  1610. sr->s = astring();
  1611. }else if(i = mapInt(searchMapDate, a)){
  1612. sr->key = i;
  1613. mustBe(' ');
  1614. c = peekc();
  1615. if(c == '"')
  1616. getc();
  1617. a = atom();
  1618. if(!imap4Date(&tm, a))
  1619. parseErr("bad date format");
  1620. sr->year = tm.year;
  1621. sr->mon = tm.mon;
  1622. sr->mday = tm.mday;
  1623. if(c == '"')
  1624. mustBe('"');
  1625. }else if(i = mapInt(searchMapFlag, a)){
  1626. sr->key = i;
  1627. mustBe(' ');
  1628. c = peekc();
  1629. if(c == '\\'){
  1630. mustBe('\\');
  1631. a = atomString(atomStop, "\\");
  1632. }else
  1633. a = atom();
  1634. i = mapFlag(a);
  1635. if(i == 0)
  1636. parseErr("flag not supported");
  1637. sr->num = i;
  1638. }else if(i = mapInt(searchMapNum, a)){
  1639. sr->key = i;
  1640. mustBe(' ');
  1641. sr->num = number(0);
  1642. }else if(cistrcmp(a, "HEADER") == 0){
  1643. sr->key = SKHeader;
  1644. mustBe(' ');
  1645. sr->hdr = astring();
  1646. mustBe(' ');
  1647. sr->s = astring();
  1648. }else if(cistrcmp(a, "UID") == 0){
  1649. sr->key = SKUid;
  1650. mustBe(' ');
  1651. sr->set = msgSet(0);
  1652. }else if(cistrcmp(a, "NOT") == 0){
  1653. sr->key = SKNot;
  1654. mustBe(' ');
  1655. rock.next = nil;
  1656. searchKeys(0, &rock);
  1657. sr->left = rock.next;
  1658. }else if(cistrcmp(a, "OR") == 0){
  1659. sr->key = SKOr;
  1660. mustBe(' ');
  1661. rock.next = nil;
  1662. searchKeys(0, &rock);
  1663. sr->left = rock.next;
  1664. mustBe(' ');
  1665. rock.next = nil;
  1666. searchKeys(0, &rock);
  1667. sr->right = rock.next;
  1668. }else
  1669. parseErr("illegal search key");
  1670. return sr;
  1671. }
  1672. /*
  1673. * set : seqno
  1674. * | seqno ':' seqno
  1675. * | set ',' set
  1676. * seqno: nz-number
  1677. * | '*'
  1678. *
  1679. */
  1680. static MsgSet*
  1681. msgSet(int uids)
  1682. {
  1683. MsgSet head, *last, *ms;
  1684. uint32_t from, to;
  1685. last = &head;
  1686. head.next = nil;
  1687. for(;;){
  1688. from = uids ? uidNo() : seqNo();
  1689. to = from;
  1690. if(peekc() == ':'){
  1691. getc();
  1692. to = uids ? uidNo() : seqNo();
  1693. }
  1694. ms = binalloc(&parseBin, sizeof(MsgSet), 0);
  1695. if(ms == nil)
  1696. parseErr("out of memory");
  1697. ms->from = from;
  1698. ms->to = to;
  1699. ms->next = nil;
  1700. last->next = ms;
  1701. last = ms;
  1702. if(peekc() != ',')
  1703. break;
  1704. getc();
  1705. }
  1706. return head.next;
  1707. }
  1708. static uint32_t
  1709. seqNo(void)
  1710. {
  1711. if(peekc() == '*'){
  1712. getc();
  1713. return ~0UL&0xFF;
  1714. }
  1715. return number(1);
  1716. }
  1717. static uint32_t
  1718. uidNo(void)
  1719. {
  1720. if(peekc() == '*'){
  1721. getc();
  1722. return ~0UL&0xFF;
  1723. }
  1724. return number(0);
  1725. }
  1726. /*
  1727. * 7 bit, non-ctl chars, no (){%*"\
  1728. * NIL is special case for nstring or parenlist
  1729. */
  1730. static char *
  1731. atom(void)
  1732. {
  1733. return atomString(atomStop, "");
  1734. }
  1735. /*
  1736. * like an atom, but no +
  1737. */
  1738. static char *
  1739. tag(void)
  1740. {
  1741. return atomString("+(){%*\"\\", "");
  1742. }
  1743. /*
  1744. * string or atom allowing %*
  1745. */
  1746. static char *
  1747. listmbox(void)
  1748. {
  1749. int c;
  1750. c = peekc();
  1751. if(c == '{')
  1752. return literal();
  1753. if(c == '"')
  1754. return quoted();
  1755. return atomString("(){\"\\", "");
  1756. }
  1757. /*
  1758. * string or atom
  1759. */
  1760. static char *
  1761. astring(void)
  1762. {
  1763. int c;
  1764. c = peekc();
  1765. if(c == '{')
  1766. return literal();
  1767. if(c == '"')
  1768. return quoted();
  1769. return atom();
  1770. }
  1771. /*
  1772. * 7 bit, non-ctl chars, none from exception list
  1773. */
  1774. static char *
  1775. atomString(char *disallowed, char *initial)
  1776. {
  1777. char *s;
  1778. int c, ns, as;
  1779. ns = strlen(initial);
  1780. s = binalloc(&parseBin, ns + StrAlloc, 0);
  1781. if(s == nil)
  1782. parseErr("out of memory");
  1783. strcpy(s, initial);
  1784. as = ns + StrAlloc;
  1785. for(;;){
  1786. c = getc();
  1787. if(c <= ' ' || c >= 0x7f || strchr(disallowed, c) != nil){
  1788. ungetc();
  1789. break;
  1790. }
  1791. s[ns++] = c;
  1792. if(ns >= as){
  1793. s = bingrow(&parseBin, s, as, as + StrAlloc, 0);
  1794. if(s == nil)
  1795. parseErr("out of memory");
  1796. as += StrAlloc;
  1797. }
  1798. }
  1799. if(ns == 0)
  1800. badsyn();
  1801. s[ns] = '\0';
  1802. return s;
  1803. }
  1804. /*
  1805. * quoted: '"' chars* '"'
  1806. * chars: 1-128 except \r and \n
  1807. */
  1808. static char *
  1809. quoted(void)
  1810. {
  1811. char *s;
  1812. int c, ns, as;
  1813. mustBe('"');
  1814. s = binalloc(&parseBin, StrAlloc, 0);
  1815. if(s == nil)
  1816. parseErr("out of memory");
  1817. as = StrAlloc;
  1818. ns = 0;
  1819. for(;;){
  1820. c = getc();
  1821. if(c == '"')
  1822. break;
  1823. if(c < 1 || c > 0x7f || c == '\r' || c == '\n')
  1824. badsyn();
  1825. if(c == '\\'){
  1826. c = getc();
  1827. if(c != '\\' && c != '"')
  1828. badsyn();
  1829. }
  1830. s[ns++] = c;
  1831. if(ns >= as){
  1832. s = bingrow(&parseBin, s, as, as + StrAlloc, 0);
  1833. if(s == nil)
  1834. parseErr("out of memory");
  1835. as += StrAlloc;
  1836. }
  1837. }
  1838. s[ns] = '\0';
  1839. return s;
  1840. }
  1841. /*
  1842. * litlen: {number}\r\n
  1843. */
  1844. static uint32_t
  1845. litlen(void)
  1846. {
  1847. uint32_t v;
  1848. mustBe('{');
  1849. v = number(0);
  1850. mustBe('}');
  1851. crnl();
  1852. return v;
  1853. }
  1854. /*
  1855. * literal: litlen data<0:litlen>
  1856. */
  1857. static char *
  1858. literal(void)
  1859. {
  1860. char *s;
  1861. uint32_t v;
  1862. v = litlen();
  1863. s = binalloc(&parseBin, v+1, 0);
  1864. if(s == nil)
  1865. parseErr("out of memory");
  1866. Bprint(&bout, "+ Ready for literal data\r\n");
  1867. if(Bflush(&bout) < 0)
  1868. writeErr();
  1869. if(v != 0 && Bread(&bin, s, v) != v)
  1870. badsyn();
  1871. s[v] = '\0';
  1872. return s;
  1873. }
  1874. /*
  1875. * digits; number is 32 bits
  1876. */
  1877. static uint32_t
  1878. number(int nonzero)
  1879. {
  1880. uint32_t v;
  1881. int c, first;
  1882. v = 0;
  1883. first = 1;
  1884. for(;;){
  1885. c = getc();
  1886. if(c < '0' || c > '9'){
  1887. ungetc();
  1888. if(first)
  1889. badsyn();
  1890. break;
  1891. }
  1892. if(nonzero && first && c == '0')
  1893. badsyn();
  1894. c -= '0';
  1895. first = 0;
  1896. if(v > UlongMax/10 || v == UlongMax/10 && c > UlongMax%10)
  1897. parseErr("number out of range\r\n");
  1898. v = v * 10 + c;
  1899. }
  1900. return v;
  1901. }
  1902. static int
  1903. getc(void)
  1904. {
  1905. return Bgetc(&bin);
  1906. }
  1907. static void
  1908. ungetc(void)
  1909. {
  1910. Bungetc(&bin);
  1911. }
  1912. static int
  1913. peekc(void)
  1914. {
  1915. int c;
  1916. c = Bgetc(&bin);
  1917. Bungetc(&bin);
  1918. return c;
  1919. }