imap4d.c 39 KB

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