123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100 |
- #include <u.h>
- #include <libc.h>
- #include <auth.h>
- #include <bio.h>
- #include "imap4d.h"
- /*
- * these should be in libraries
- */
- char *csquery(char *attr, char *val, char *rattr);
- /*
- * /lib/rfc/rfc2060 imap4rev1
- * /lib/rfc/rfc2683 is implementation advice
- * /lib/rfc/rfc2342 is namespace capability
- * /lib/rfc/rfc2222 is security protocols
- * /lib/rfc/rfc1731 is security protocols
- * /lib/rfc/rfc2221 is LOGIN-REFERRALS
- * /lib/rfc/rfc2193 is MAILBOX-REFERRALS
- * /lib/rfc/rfc2177 is IDLE capability
- * /lib/rfc/rfc2195 is CRAM-MD5 authentication
- * /lib/rfc/rfc2088 is LITERAL+ capability
- * /lib/rfc/rfc1760 is S/Key authentication
- *
- * outlook uses "Secure Password Authentication" aka ntlm authentication
- *
- * capabilities from nslocum
- * CAPABILITY IMAP4 IMAP4REV1 NAMESPACE IDLE SCAN SORT MAILBOX-REFERRALS LOGIN-REFERRALS AUTH=LOGIN THREAD=ORDEREDSUBJECT
- */
- typedef struct ParseCmd ParseCmd;
- enum
- {
- UlongMax = 4294967295,
- };
- struct ParseCmd
- {
- char *name;
- void (*f)(char *tg, char *cmd);
- };
- static void appendCmd(char *tg, char *cmd);
- static void authenticateCmd(char *tg, char *cmd);
- static void capabilityCmd(char *tg, char *cmd);
- static void closeCmd(char *tg, char *cmd);
- static void copyCmd(char *tg, char *cmd);
- static void createCmd(char *tg, char *cmd);
- static void deleteCmd(char *tg, char *cmd);
- static void expungeCmd(char *tg, char *cmd);
- static void fetchCmd(char *tg, char *cmd);
- static void idleCmd(char *tg, char *cmd);
- static void listCmd(char *tg, char *cmd);
- static void loginCmd(char *tg, char *cmd);
- static void logoutCmd(char *tg, char *cmd);
- static void namespaceCmd(char *tg, char *cmd);
- static void noopCmd(char *tg, char *cmd);
- static void renameCmd(char *tg, char *cmd);
- static void searchCmd(char *tg, char *cmd);
- static void selectCmd(char *tg, char *cmd);
- static void statusCmd(char *tg, char *cmd);
- static void storeCmd(char *tg, char *cmd);
- static void subscribeCmd(char *tg, char *cmd);
- static void uidCmd(char *tg, char *cmd);
- static void unsubscribeCmd(char *tg, char *cmd);
- static void copyUCmd(char *tg, char *cmd, int uids);
- static void fetchUCmd(char *tg, char *cmd, int uids);
- static void searchUCmd(char *tg, char *cmd, int uids);
- static void storeUCmd(char *tg, char *cmd, int uids);
- static void imap4(int);
- static void status(int expungeable, int uids);
- static void cleaner(void);
- static void check(void);
- static int catcher(void*, char*);
- static Search *searchKey(int first);
- static Search *searchKeys(int first, Search *tail);
- static char *astring(void);
- static char *atomString(char *disallowed, char *initial);
- static char *atom(void);
- static void badsyn(void);
- static void clearcmd(void);
- static char *command(void);
- static void crnl(void);
- static Fetch *fetchAtt(char *s, Fetch *f);
- static Fetch *fetchWhat(void);
- static int flagList(void);
- static int flags(void);
- static int getc(void);
- static char *listmbox(void);
- static char *literal(void);
- static ulong litlen(void);
- static MsgSet *msgSet(int);
- static void mustBe(int c);
- static ulong number(int nonzero);
- static int peekc(void);
- static char *quoted(void);
- static void sectText(Fetch *f, int mimeOk);
- static ulong seqNo(void);
- static Store *storeWhat(void);
- static char *tag(void);
- static ulong uidNo(void);
- static void ungetc(void);
- static ParseCmd SNonAuthed[] =
- {
- {"capability", capabilityCmd},
- {"logout", logoutCmd},
- {"x-exit", logoutCmd},
- {"noop", noopCmd},
- {"login", loginCmd},
- {"authenticate", authenticateCmd},
- nil
- };
- static ParseCmd SAuthed[] =
- {
- {"capability", capabilityCmd},
- {"logout", logoutCmd},
- {"x-exit", logoutCmd},
- {"noop", noopCmd},
- {"append", appendCmd},
- {"create", createCmd},
- {"delete", deleteCmd},
- {"examine", selectCmd},
- {"select", selectCmd},
- {"idle", idleCmd},
- {"list", listCmd},
- {"lsub", listCmd},
- {"namespace", namespaceCmd},
- {"rename", renameCmd},
- {"status", statusCmd},
- {"subscribe", subscribeCmd},
- {"unsubscribe", unsubscribeCmd},
- nil
- };
- static ParseCmd SSelected[] =
- {
- {"capability", capabilityCmd},
- {"logout", logoutCmd},
- {"x-exit", logoutCmd},
- {"noop", noopCmd},
- {"append", appendCmd},
- {"create", createCmd},
- {"delete", deleteCmd},
- {"examine", selectCmd},
- {"select", selectCmd},
- {"idle", idleCmd},
- {"list", listCmd},
- {"lsub", listCmd},
- {"namespace", namespaceCmd},
- {"rename", renameCmd},
- {"status", statusCmd},
- {"subscribe", subscribeCmd},
- {"unsubscribe", unsubscribeCmd},
- {"check", noopCmd},
- {"close", closeCmd},
- {"copy", copyCmd},
- {"expunge", expungeCmd},
- {"fetch", fetchCmd},
- {"search", searchCmd},
- {"store", storeCmd},
- {"uid", uidCmd},
- nil
- };
- static char *atomStop = "(){%*\"\\";
- static Chalstate *chal;
- static int chaled;
- static ParseCmd *imapState;
- static jmp_buf parseJmp;
- static char *parseMsg;
- static int allowPass;
- static int allowCR;
- static int exiting;
- static QLock imaplock;
- static int idlepid = -1;
- Biobuf bout;
- Biobuf bin;
- char username[UserNameLen];
- char mboxDir[MboxNameLen];
- char *servername;
- char *site;
- char *remote;
- Box *selected;
- Bin *parseBin;
- int debug;
- void
- main(int argc, char *argv[])
- {
- char *s, *t;
- int preauth, n;
- Binit(&bin, 0, OREAD);
- Binit(&bout, 1, OWRITE);
- preauth = 0;
- allowPass = 0;
- allowCR = 0;
- ARGBEGIN{
- case 'a':
- preauth = 1;
- break;
- case 'd':
- site = ARGF();
- break;
- case 'c':
- allowCR = 1;
- break;
- case 'p':
- allowPass = 1;
- break;
- case 'r':
- remote = ARGF();
- break;
- case 's':
- servername = ARGF();
- break;
- case 'v':
- debug = 1;
- debuglog("imap4d debugging enabled\n");
- break;
- default:
- fprint(2, "usage: ip/imap4d [-acpv] [-d site] [-r remotehost] [-s servername]\n");
- bye("usage");
- break;
- }ARGEND
- if(allowPass && allowCR){
- fprint(2, "%s: -c and -p are mutually exclusive\n", argv0);
- bye("usage");
- }
- if(preauth)
- setupuser(nil);
- if(servername == nil){
- servername = csquery("sys", sysname(), "dom");
- if(servername == nil)
- servername = sysname();
- if(servername == nil){
- fprint(2, "ip/imap4d can't find server name: %r\n");
- bye("can't find system name");
- }
- }
- if(site == nil){
- t = getenv("site");
- if(t == nil)
- site = servername;
- else{
- n = strlen(t);
- s = strchr(servername, '.');
- if(s == nil)
- s = servername;
- else
- s++;
- n += strlen(s) + 2;
- site = emalloc(n);
- snprint(site, n, "%s.%s", t, s);
- }
- }
- rfork(RFNOTEG|RFREND);
- atnotify(catcher, 1);
- qlock(&imaplock);
- atexit(cleaner);
- imap4(preauth);
- }
- static void
- imap4(int preauth)
- {
- char *volatile tg;
- char *volatile cmd;
- ParseCmd *st;
- if(preauth){
- Bprint(&bout, "* preauth %s IMAP4rev1 server ready user %s authenticated\r\n", servername, username);
- imapState = SAuthed;
- }else{
- Bprint(&bout, "* OK %s IMAP4rev1 server ready\r\n", servername);
- imapState = SNonAuthed;
- }
- if(Bflush(&bout) < 0)
- writeErr();
- chaled = 0;
- tg = nil;
- cmd = nil;
- if(setjmp(parseJmp)){
- if(tg == nil)
- Bprint(&bout, "* bad empty command line: %s\r\n", parseMsg);
- else if(cmd == nil)
- Bprint(&bout, "%s BAD no command: %s\r\n", tg, parseMsg);
- else
- Bprint(&bout, "%s BAD %s %s\r\n", tg, cmd, parseMsg);
- clearcmd();
- if(Bflush(&bout) < 0)
- writeErr();
- binfree(&parseBin);
- }
- for(;;){
- if(mbLocked())
- bye("internal error: mailbox lock held");
- tg = nil;
- cmd = nil;
- tg = tag();
- mustBe(' ');
- cmd = atom();
- /*
- * note: outlook express is broken: it requires echoing the
- * command as part of matching response
- */
- for(st = imapState; st->name != nil; st++){
- if(cistrcmp(cmd, st->name) == 0){
- (*st->f)(tg, cmd);
- break;
- }
- }
- if(st->name == nil){
- clearcmd();
- Bprint(&bout, "%s BAD %s illegal command\r\n", tg, cmd);
- }
- if(Bflush(&bout) < 0)
- writeErr();
- binfree(&parseBin);
- }
- }
- void
- bye(char *fmt, ...)
- {
- va_list arg;
- va_start(arg, fmt);
- Bprint(&bout, "* bye ");
- Bvprint(&bout, fmt, arg);
- Bprint(&bout, "\r\n");
- Bflush(&bout);
- exits("rob2");
- exits(0);
- }
- void
- parseErr(char *msg)
- {
- parseMsg = msg;
- longjmp(parseJmp, 1);
- }
- /*
- * an error occured while writing to the client
- */
- void
- writeErr(void)
- {
- cleaner();
- _exits("connection closed");
- }
- static int
- catcher(void *v, char *msg)
- {
- USED(v);
- if(strstr(msg, "closed pipe") != nil)
- return 1;
- return 0;
- }
- /*
- * wipes out the idleCmd backgroung process if it is around.
- * this can only be called if the current proc has qlocked imaplock.
- * it must be the last piece of imap4d code executed.
- */
- static void
- cleaner(void)
- {
- int i;
- if(idlepid < 0)
- return;
- exiting = 1;
- close(0);
- close(1);
- close(2);
- /*
- * the other proc is either stuck in a read, a sleep,
- * or is trying to lock imap4lock.
- * get him out of it so he can exit cleanly
- */
- qunlock(&imaplock);
- for(i = 0; i < 4; i++)
- postnote(PNGROUP, getpid(), "die");
- }
- /*
- * send any pending status updates to the client
- * careful: shouldn't exit, because called by idle polling proc
- *
- * can't always send pending info
- * in particular, can't send expunge info
- * in response to a fetch, store, or search command.
- *
- * rfc2060 5.2: server must send mailbox size updates
- * rfc2060 5.2: server may send flag updates
- * rfc2060 5.5: servers prohibited from sending expunge while fetch, store, search in progress
- * rfc2060 7: in selected state, server checks mailbox for new messages as part of every command
- * sends untagged EXISTS and RECENT respsonses reflecting new size of the mailbox
- * should also send appropriate untagged FETCH and EXPUNGE messages if another agent
- * changes the state of any message flags or expunges any messages
- * rfc2060 7.4.1 expunge server response must not be sent when no command is in progress,
- * nor while responding to a fetch, stort, or search command (uid versions are ok)
- * command only "in progress" after entirely parsed.
- *
- * strategy for third party deletion of messages or of a mailbox
- *
- * deletion of a selected mailbox => act like all message are expunged
- * not strictly allowed by rfc2180, but close to method 3.2.
- *
- * renaming same as deletion
- *
- * copy
- * reject iff a deleted message is in the request
- *
- * search, store, fetch operations on expunged messages
- * ignore the expunged messages
- * return tagged no if referenced
- */
- static void
- status(int expungeable, int uids)
- {
- int tell;
- if(!selected)
- return;
- tell = 0;
- if(expungeable)
- tell = expungeMsgs(selected, 1);
- if(selected->sendFlags)
- sendFlags(selected, uids);
- if(tell || selected->toldMax != selected->max){
- Bprint(&bout, "* %lud EXISTS\r\n", selected->max);
- selected->toldMax = selected->max;
- }
- if(tell || selected->toldRecent != selected->recent){
- Bprint(&bout, "* %lud RECENT\r\n", selected->recent);
- selected->toldRecent = selected->recent;
- }
- if(tell)
- closeImp(selected, checkBox(selected, 1));
- }
- /*
- * careful: can't exit, because called by idle polling proc
- */
- static void
- check(void)
- {
- if(!selected)
- return;
- checkBox(selected, 0);
- status(1, 0);
- }
- static void
- appendCmd(char *tg, char *cmd)
- {
- char *mbox, head[128];
- ulong t, n, now;
- int flags, ok;
- mustBe(' ');
- mbox = astring();
- mustBe(' ');
- flags = 0;
- if(peekc() == '('){
- flags = flagList();
- mustBe(' ');
- }
- now = time(nil);
- if(peekc() == '"'){
- t = imap4DateTime(quoted());
- if(t == ~0)
- parseErr("illegal date format");
- mustBe(' ');
- if(t > now)
- t = now;
- }else
- t = now;
- n = litlen();
- mbox = mboxName(mbox);
- if(mbox == nil || !okMbox(mbox)){
- check();
- Bprint(&bout, "%s NO %s bad mailbox\r\n", tg, cmd);
- return;
- }
- if(!cdExists(mboxDir, mbox)){
- check();
- Bprint(&bout, "%s NO [TRYCREATE] %s mailbox does not exist\r\n", tg, cmd);
- return;
- }
- snprint(head, sizeof(head), "From %s %s", username, ctime(t));
- ok = appendSave(mbox, flags, head, &bin, n);
- crnl();
- check();
- if(ok)
- Bprint(&bout, "%s OK %s completed\r\n", tg, cmd);
- else
- Bprint(&bout, "%s NO %s message save failed\r\n", tg, cmd);
- }
- static void
- authenticateCmd(char *tg, char *cmd)
- {
- char *s, *t;
- mustBe(' ');
- s = atom();
- crnl();
- auth_freechal(chal);
- chal = nil;
- if(cistrcmp(s, "cram-md5") == 0){
- t = cramauth();
- if(t == nil){
- Bprint(&bout, "%s OK %s\r\n", tg, cmd);
- imapState = SAuthed;
- }else
- Bprint(&bout, "%s NO %s failed %s\r\n", tg, cmd, t);
- }else
- Bprint(&bout, "%s NO %s unsupported authentication protocol\r\n", tg, cmd);
- }
- static void
- capabilityCmd(char *tg, char *cmd)
- {
- crnl();
- check();
- // nslocum's capabilities
- // Bprint(&bout, "* CAPABILITY IMAP4 IMAP4REV1 NAMESPACE IDLE SCAN SORT MAILBOX-REFERRALS LOGIN-REFERRALS AUTH=LOGIN THREAD=ORDEREDSUBJECT\r\n");
- Bprint(&bout, "* CAPABILITY IMAP4REV1 IDLE NAMESPACE AUTH=CRAM-MD5\r\n");
- Bprint(&bout, "%s OK %s\r\n", tg, cmd);
- }
- static void
- closeCmd(char *tg, char *cmd)
- {
- crnl();
- imapState = SAuthed;
- closeBox(selected, 1);
- selected = nil;
- Bprint(&bout, "%s OK %s mailbox closed, now in authenticated state\r\n", tg, cmd);
- }
- /*
- * note: message id's are before any pending expunges
- */
- static void
- copyCmd(char *tg, char *cmd)
- {
- copyUCmd(tg, cmd, 0);
- }
- static void
- copyUCmd(char *tg, char *cmd, int uids)
- {
- MsgSet *ms;
- char *uid, *mbox;
- ulong max;
- int ok;
- mustBe(' ');
- ms = msgSet(uids);
- mustBe(' ');
- mbox = astring();
- crnl();
- uid = "";
- if(uids)
- uid = "uid ";
- mbox = mboxName(mbox);
- if(mbox == nil || !okMbox(mbox)){
- status(1, uids);
- Bprint(&bout, "%s NO %s%s bad mailbox\r\n", tg, uid, cmd);
- return;
- }
- if(cistrcmp(mbox, "inbox") == 0)
- mbox = "mbox";
- if(!cdExists(mboxDir, mbox)){
- check();
- Bprint(&bout, "%s NO [TRYCREATE] %s mailbox does not exist\r\n", tg, cmd);
- return;
- }
- max = selected->max;
- checkBox(selected, 0);
- ok = forMsgs(selected, ms, max, uids, copyCheck, nil);
- if(ok)
- ok = forMsgs(selected, ms, max, uids, copySave, mbox);
- status(1, uids);
- if(ok)
- Bprint(&bout, "%s OK %s%s completed\r\n", tg, uid, cmd);
- else
- Bprint(&bout, "%s NO %s%s failed\r\n", tg, uid, cmd);
- }
- static void
- createCmd(char *tg, char *cmd)
- {
- char *mbox, *m;
- int fd, slash;
- mustBe(' ');
- mbox = astring();
- crnl();
- check();
- m = strchr(mbox, '\0');
- slash = m != mbox && m[-1] == '/';
- mbox = mboxName(mbox);
- if(mbox == nil || !okMbox(mbox)){
- Bprint(&bout, "%s NO %s bad mailbox\r\n", tg, cmd);
- return;
- }
- if(cistrcmp(mbox, "inbox") == 0){
- Bprint(&bout, "%s NO %s cannot remotely create INBOX\r\n", tg, cmd);
- return;
- }
- if(access(mbox, AEXIST) >= 0){
- Bprint(&bout, "%s NO %s mailbox already exists\r\n", tg, cmd);
- return;
- }
- fd = createBox(mbox, slash);
- close(fd);
- if(fd < 0)
- Bprint(&bout, "%s NO %s cannot create mailbox %s\r\n", tg, cmd, mbox);
- else
- Bprint(&bout, "%s OK %s %s completed\r\n", tg, mbox, cmd);
- }
- static void
- deleteCmd(char *tg, char *cmd)
- {
- char *mbox, *imp;
- mustBe(' ');
- mbox = astring();
- crnl();
- check();
- mbox = mboxName(mbox);
- if(mbox == nil || !okMbox(mbox)){
- Bprint(&bout, "%s NO %s bad mailbox\r\n", tg, cmd);
- return;
- }
- imp = impName(mbox);
- if(cistrcmp(mbox, "inbox") == 0
- || imp != nil && cdRemove(mboxDir, imp) < 0 && cdExists(mboxDir, imp)
- || cdRemove(mboxDir, mbox) < 0)
- Bprint(&bout, "%s NO %s cannot delete mailbox %s\r\n", tg, cmd, mbox);
- else
- Bprint(&bout, "%s OK %s %s completed\r\n", tg, mbox, cmd);
- }
- static void
- expungeCmd(char *tg, char *cmd)
- {
- int ok;
- crnl();
- ok = deleteMsgs(selected);
- check();
- if(ok)
- Bprint(&bout, "%s OK %s messages erased\r\n", tg, cmd);
- else
- Bprint(&bout, "%s NO %s some messages not expunged\r\n", tg, cmd);
- }
- static void
- fetchCmd(char *tg, char *cmd)
- {
- fetchUCmd(tg, cmd, 0);
- }
- static void
- fetchUCmd(char *tg, char *cmd, int uids)
- {
- Fetch *f;
- MsgSet *ms;
- MbLock *ml;
- char *uid;
- ulong max;
- int ok;
- mustBe(' ');
- ms = msgSet(uids);
- mustBe(' ');
- f = fetchWhat();
- crnl();
- uid = "";
- if(uids)
- uid = "uid ";
- max = selected->max;
- ml = checkBox(selected, 1);
- if(ml != nil)
- forMsgs(selected, ms, max, uids, fetchSeen, f);
- closeImp(selected, ml);
- ok = ml != nil && forMsgs(selected, ms, max, uids, fetchMsg, f);
- status(uids, uids);
- if(ok)
- Bprint(&bout, "%s OK %s%s completed\r\n", tg, uid, cmd);
- else
- Bprint(&bout, "%s NO %s%s failed\r\n", tg, uid, cmd);
- }
- static void
- idleCmd(char *tg, char *cmd)
- {
- int c, pid;
- crnl();
- Bprint(&bout, "+ idling, waiting for done\r\n");
- if(Bflush(&bout) < 0)
- writeErr();
- if(idlepid < 0){
- pid = rfork(RFPROC|RFMEM|RFNOWAIT);
- if(pid == 0){
- for(;;){
- qlock(&imaplock);
- if(exiting)
- break;
- /*
- * parent may have changed curDir, but it doesn't change our .
- */
- resetCurDir();
- check();
- if(Bflush(&bout) < 0)
- writeErr();
- qunlock(&imaplock);
- sleep(15*1000);
- enableForwarding();
- }
- _exits("rob3");
- _exits(0);
- }
- idlepid = pid;
- }
- qunlock(&imaplock);
- /*
- * clear out the next line, which is supposed to contain (case-insensitive)
- * done\n
- * this is special code since it has to dance with the idle polling proc
- * and handle exiting correctly.
- */
- for(;;){
- c = getc();
- if(c < 0){
- qlock(&imaplock);
- if(!exiting)
- cleaner();
- _exits("rob4");
- _exits(0);
- }
- if(c == '\n')
- break;
- }
- qlock(&imaplock);
- if(exiting)
- {_exits("rob5");
- _exits(0);
- }
- /*
- * child may have changed curDir, but it doesn't change our .
- */
- resetCurDir();
- check();
- Bprint(&bout, "%s OK %s terminated\r\n", tg, cmd);
- }
- static void
- listCmd(char *tg, char *cmd)
- {
- char *s, *t, *ss, *ref, *mbox;
- int n;
- mustBe(' ');
- s = astring();
- mustBe(' ');
- t = listmbox();
- crnl();
- check();
- ref = mutf7str(s);
- mbox = mutf7str(t);
- if(ref == nil || mbox == nil){
- Bprint(&bout, "%s BAD %s mailbox name not in modified utf-7\r\n", tg, cmd);
- return;
- }
- /*
- * special request for hierarchy delimiter and root name
- * root name appears to be name up to and including any delimiter,
- * or the empty string, if there is no delimiter.
- *
- * this must change if the # namespace convention is supported.
- */
- if(*mbox == '\0'){
- s = strchr(ref, '/');
- if(s == nil)
- ref = "";
- else
- s[1] = '\0';
- Bprint(&bout, "* %s (\\Noselect) \"/\" \"%s\"\r\n", cmd, ref);
- Bprint(&bout, "%s OK %s\r\n", tg, cmd);
- return;
- }
- /*
- * massage the listing name:
- * clean up the components individually,
- * then rip off componenets from the ref to
- * take care of leading ..'s in the mbox.
- *
- * the cleanup can wipe out * followed by a ..
- * tough luck if such a stupid pattern is given.
- */
- cleanname(mbox);
- if(strcmp(mbox, ".") == 0)
- *mbox = '\0';
- if(mbox[0] == '/')
- *ref = '\0';
- else if(*ref != '\0'){
- cleanname(ref);
- if(strcmp(ref, ".") == 0)
- *ref = '\0';
- }else
- *ref = '\0';
- while(*ref && isdotdot(mbox)){
- s = strrchr(ref, '/');
- if(s == nil)
- s = ref;
- if(isdotdot(s))
- break;
- *s = '\0';
- mbox += 2;
- if(*mbox == '/')
- mbox++;
- }
- if(*ref == '\0'){
- s = mbox;
- ss = s;
- }else{
- n = strlen(ref) + strlen(mbox) + 2;
- t = binalloc(&parseBin, n, 0);
- if(t == nil)
- parseErr("out of memory");
- snprint(t, n, "%s/%s", ref, mbox);
- s = t;
- ss = s + strlen(ref);
- }
- /*
- * only allow activity in /mail/box
- */
- if(s[0] == '/' || isdotdot(s)){
- Bprint(&bout, "%s NO illegal mailbox pattern\r\n", tg);
- return;
- }
- if(cistrcmp(cmd, "lsub") == 0)
- lsubBoxes(cmd, s, ss);
- else
- listBoxes(cmd, s, ss);
- Bprint(&bout, "%s OK %s completed\r\n", tg, cmd);
- }
- static char*
- passCR(char*u, char*p)
- {
- static char Ebadch[] = "can't get challenge";
- static char nchall[64];
- static char response[64];
- static Chalstate *ch = nil;
- AuthInfo *ai;
- again:
- if (ch == nil){
- if(!(ch = auth_challenge("proto=p9cr role=server user=%q", u)))
- return Ebadch;
- snprint(nchall, 64, " encrypt challenge: %s", ch->chal);
- return nchall;
- } else {
- strncpy(response, p, 64);
- ch->resp = response;
- ch->nresp = strlen(response);
- ai = auth_response(ch);
- auth_freechal(ch);
- ch = nil;
- if (ai == nil)
- goto again;
- setupuser(ai);
- return nil;
- }
-
- }
- static void
- loginCmd(char *tg, char *cmd)
- {
- char *s, *t;
- AuthInfo *ai;
- char*r;
- mustBe(' ');
- s = astring(); /* uid */
- mustBe(' ');
- t = astring(); /* password */
- crnl();
- if(allowCR){
- if ((r = passCR(s, t)) == nil){
- Bprint(&bout, "%s OK %s succeeded\r\n", tg, cmd);
- imapState = SAuthed;
- } else {
- Bprint(&bout, "* NO [ALERT] %s\r\n", r);
- Bprint(&bout, "%s NO %s succeeded\r\n", tg, cmd);
- }
- return;
- }
- else if(allowPass){
- if(ai = passLogin(s, t)){
- setupuser(ai);
- Bprint(&bout, "%s OK %s succeeded\r\n", tg, cmd);
- imapState = SAuthed;
- }else
- Bprint(&bout, "%s NO %s failed check\r\n", tg, cmd);
- return;
- }
- Bprint(&bout, "%s NO %s plaintext passwords disallowed\r\n", tg, cmd);
- }
- /*
- * logout or x-exit, which doesn't expunge the mailbox
- */
- static void
- logoutCmd(char *tg, char *cmd)
- {
- crnl();
- if(cmd[0] != 'x' && selected){
- closeBox(selected, 1);
- selected = nil;
- }
- Bprint(&bout, "* bye\r\n");
- Bprint(&bout, "%s OK %s completed\r\n", tg, cmd);
- exits("rob6");
- exits(0);
- }
- static void
- namespaceCmd(char *tg, char *cmd)
- {
- crnl();
- check();
- /*
- * personal, other users, shared namespaces
- * send back nil or descriptions of (prefix heirarchy-delim) for each case
- */
- Bprint(&bout, "* NAMESPACE ((\"\" \"/\")) nil nil\r\n");
- Bprint(&bout, "%s OK %s completed\r\n", tg, cmd);
- }
- static void
- noopCmd(char *tg, char *cmd)
- {
- crnl();
- check();
- Bprint(&bout, "%s OK %s completed\r\n", tg, cmd);
- enableForwarding();
- }
- /*
- * this is only a partial implementation
- * should copy files to other directories,
- * and copy & truncate inbox
- */
- static void
- renameCmd(char *tg, char *cmd)
- {
- char *from, *to;
- int ok;
- mustBe(' ');
- from = astring();
- mustBe(' ');
- to = astring();
- crnl();
- check();
- to = mboxName(to);
- if(to == nil || !okMbox(to) || cistrcmp(to, "inbox") == 0){
- Bprint(&bout, "%s NO %s bad mailbox destination name\r\n", tg, cmd);
- return;
- }
- if(access(to, AEXIST) >= 0){
- Bprint(&bout, "%s NO %s mailbox already exists\r\n", tg, cmd);
- return;
- }
- from = mboxName(from);
- if(from == nil || !okMbox(from)){
- Bprint(&bout, "%s NO %s bad mailbox destination name\r\n", tg, cmd);
- return;
- }
- if(cistrcmp(from, "inbox") == 0)
- ok = copyBox(from, to, 0);
- else
- ok = moveBox(from, to);
- if(ok)
- Bprint(&bout, "%s OK %s completed\r\n", tg, cmd);
- else
- Bprint(&bout, "%s NO %s failed\r\n", tg, cmd);
- }
- static void
- searchCmd(char *tg, char *cmd)
- {
- searchUCmd(tg, cmd, 0);
- }
- static void
- searchUCmd(char *tg, char *cmd, int uids)
- {
- Search rock;
- Msg *m;
- char *uid;
- ulong id;
- mustBe(' ');
- rock.next = nil;
- searchKeys(1, &rock);
- crnl();
- uid = "";
- if(uids)
- uid = "uid ";
- if(rock.next != nil && rock.next->key == SKCharset){
- if(cistrstr(rock.next->s, "utf-8") != 0
- && cistrcmp(rock.next->s, "us-ascii") != 0){
- Bprint(&bout, "%s NO [BADCHARSET] (\"US-ASCII\" \"UTF-8\") %s%s failed\r\n", tg, uid, cmd);
- checkBox(selected, 0);
- status(uids, uids);
- return;
- }
- rock.next = rock.next->next;
- }
- Bprint(&bout, "* search");
- for(m = selected->msgs; m != nil; m = m->next)
- m->matched = searchMsg(m, rock.next);
- for(m = selected->msgs; m != nil; m = m->next){
- if(m->matched){
- if(uids)
- id = m->uid;
- else
- id = m->seq;
- Bprint(&bout, " %lud", id);
- }
- }
- Bprint(&bout, "\r\n");
- checkBox(selected, 0);
- status(uids, uids);
- Bprint(&bout, "%s OK %s%s completed\r\n", tg, uid, cmd);
- }
- static void
- selectCmd(char *tg, char *cmd)
- {
- Msg *m;
- char *s, *mbox;
- mustBe(' ');
- mbox = astring();
- crnl();
- if(selected){
- imapState = SAuthed;
- closeBox(selected, 1);
- selected = nil;
- }
- mbox = mboxName(mbox);
- if(mbox == nil || !okMbox(mbox)){
- Bprint(&bout, "%s NO %s bad mailbox\r\n", tg, cmd);
- return;
- }
- selected = openBox(mbox, "imap", cistrcmp(cmd, "select") == 0);
- if(selected == nil){
- Bprint(&bout, "%s NO %s can't open mailbox %s: %r\r\n", tg, cmd, mbox);
- return;
- }
- imapState = SSelected;
- Bprint(&bout, "* FLAGS (\\Seen \\Answered \\Flagged \\Deleted \\Draft)\r\n");
- Bprint(&bout, "* %lud EXISTS\r\n", selected->max);
- selected->toldMax = selected->max;
- Bprint(&bout, "* %lud RECENT\r\n", selected->recent);
- selected->toldRecent = selected->recent;
- for(m = selected->msgs; m != nil; m = m->next){
- if(!m->expunged && (m->flags & MSeen) != MSeen){
- Bprint(&bout, "* OK [UNSEEN %ld]\r\n", m->seq);
- break;
- }
- }
- Bprint(&bout, "* OK [PERMANENTFLAGS (\\Seen \\Answered \\Flagged \\Draft \\Deleted)]\r\n");
- Bprint(&bout, "* OK [UIDNEXT %ld]\r\n", selected->uidnext);
- Bprint(&bout, "* OK [UIDVALIDITY %ld]\r\n", selected->uidvalidity);
- s = "READ-ONLY";
- if(selected->writable)
- s = "READ-WRITE";
- Bprint(&bout, "%s OK [%s] %s %s completed\r\n", tg, s, cmd, mbox);
- }
- static NamedInt statusItems[] =
- {
- {"MESSAGES", SMessages},
- {"RECENT", SRecent},
- {"UIDNEXT", SUidNext},
- {"UIDVALIDITY", SUidValidity},
- {"UNSEEN", SUnseen},
- {nil, 0}
- };
- static void
- statusCmd(char *tg, char *cmd)
- {
- Box *box;
- Msg *m;
- char *s, *mbox;
- ulong v;
- int si, i;
- mustBe(' ');
- mbox = astring();
- mustBe(' ');
- mustBe('(');
- si = 0;
- for(;;){
- s = atom();
- i = mapInt(statusItems, s);
- if(i == 0)
- parseErr("illegal status item");
- si |= i;
- if(peekc() == ')')
- break;
- mustBe(' ');
- }
- mustBe(')');
- crnl();
- mbox = mboxName(mbox);
- if(mbox == nil || !okMbox(mbox)){
- check();
- Bprint(&bout, "%s NO %s bad mailbox\r\n", tg, cmd);
- return;
- }
- box = openBox(mbox, "status", 1);
- if(box == nil){
- check();
- Bprint(&bout, "%s NO [TRYCREATE] %s can't open mailbox %s: %r\r\n", tg, cmd, mbox);
- return;
- }
- Bprint(&bout, "* STATUS %s (", mbox);
- s = "";
- for(i = 0; statusItems[i].name != nil; i++){
- if(si & statusItems[i].v){
- v = 0;
- switch(statusItems[i].v){
- case SMessages:
- v = box->max;
- break;
- case SRecent:
- v = box->recent;
- break;
- case SUidNext:
- v = box->uidnext;
- break;
- case SUidValidity:
- v = box->uidvalidity;
- break;
- case SUnseen:
- v = 0;
- for(m = box->msgs; m != nil; m = m->next)
- if((m->flags & MSeen) != MSeen)
- v++;
- break;
- default:
- Bprint(&bout, ")");
- bye("internal error: status item not implemented");
- break;
- }
- Bprint(&bout, "%s%s %lud", s, statusItems[i].name, v);
- s = " ";
- }
- }
- Bprint(&bout, ")\r\n");
- closeBox(box, 1);
- check();
- Bprint(&bout, "%s OK %s completed\r\n", tg, cmd);
- }
- static void
- storeCmd(char *tg, char *cmd)
- {
- storeUCmd(tg, cmd, 0);
- }
- static void
- storeUCmd(char *tg, char *cmd, int uids)
- {
- Store *st;
- MsgSet *ms;
- MbLock *ml;
- char *uid;
- ulong max;
- int ok;
- mustBe(' ');
- ms = msgSet(uids);
- mustBe(' ');
- st = storeWhat();
- crnl();
- uid = "";
- if(uids)
- uid = "uid ";
- max = selected->max;
- ml = checkBox(selected, 1);
- ok = ml != nil && forMsgs(selected, ms, max, uids, storeMsg, st);
- closeImp(selected, ml);
- status(uids, uids);
- if(ok)
- Bprint(&bout, "%s OK %s%s completed\r\n", tg, uid, cmd);
- else
- Bprint(&bout, "%s NO %s%s failed\r\n", tg, uid, cmd);
- }
- /*
- * minimal implementation of subscribe
- * all folders are automatically subscribed,
- * and can't be unsubscribed
- */
- static void
- subscribeCmd(char *tg, char *cmd)
- {
- Box *box;
- char *mbox;
- int ok;
- mustBe(' ');
- mbox = astring();
- crnl();
- check();
- mbox = mboxName(mbox);
- ok = 0;
- if(mbox != nil && okMbox(mbox)){
- box = openBox(mbox, "subscribe", 0);
- if(box != nil){
- ok = subscribe(mbox, 's');
- closeBox(box, 1);
- }
- }
- if(!ok)
- Bprint(&bout, "%s NO %s bad mailbox\r\n", tg, cmd);
- else
- Bprint(&bout, "%s OK %s completed\r\n", tg, cmd);
- }
- static void
- uidCmd(char *tg, char *cmd)
- {
- char *sub;
- mustBe(' ');
- sub = atom();
- if(cistrcmp(sub, "copy") == 0)
- copyUCmd(tg, sub, 1);
- else if(cistrcmp(sub, "fetch") == 0)
- fetchUCmd(tg, sub, 1);
- else if(cistrcmp(sub, "search") == 0)
- searchUCmd(tg, sub, 1);
- else if(cistrcmp(sub, "store") == 0)
- storeUCmd(tg, sub, 1);
- else{
- clearcmd();
- Bprint(&bout, "%s BAD %s illegal uid command %s\r\n", tg, cmd, sub);
- }
- }
- static void
- unsubscribeCmd(char *tg, char *cmd)
- {
- char *mbox;
- mustBe(' ');
- mbox = astring();
- crnl();
- check();
- mbox = mboxName(mbox);
- if(mbox == nil || !okMbox(mbox) || !subscribe(mbox, 'u'))
- Bprint(&bout, "%s NO %s can't unsubscribe\r\n", tg, cmd);
- else
- Bprint(&bout, "%s OK %s completed\r\n", tg, cmd);
- }
- static void
- badsyn(void)
- {
- parseErr("bad syntax");
- }
- static void
- clearcmd(void)
- {
- int c;
- for(;;){
- c = getc();
- if(c < 0)
- bye("end of input");
- if(c == '\n')
- return;
- }
- }
- static void
- crnl(void)
- {
- int c;
- c = getc();
- if(c == '\n')
- return;
- if(c != '\r' || getc() != '\n')
- badsyn();
- }
- static void
- mustBe(int c)
- {
- if(getc() != c){
- ungetc();
- badsyn();
- }
- }
- /*
- * flaglist : '(' ')' | '(' flags ')'
- */
- static int
- flagList(void)
- {
- int f;
- mustBe('(');
- f = 0;
- if(peekc() != ')')
- f = flags();
- mustBe(')');
- return f;
- }
- /*
- * flags : flag | flags ' ' flag
- * flag : '\' atom | atom
- */
- static int
- flags(void)
- {
- int ff, flags;
- char *s;
- int c;
- flags = 0;
- for(;;){
- c = peekc();
- if(c == '\\'){
- mustBe('\\');
- s = atomString(atomStop, "\\");
- }else if(strchr(atomStop, c) != nil)
- s = atom();
- else
- break;
- ff = mapFlag(s);
- if(ff == 0)
- parseErr("flag not supported");
- flags |= ff;
- if(peekc() != ' ')
- break;
- mustBe(' ');
- }
- if(flags == 0)
- parseErr("no flags given");
- return flags;
- }
- /*
- * storeWhat : osign 'FLAGS' ' ' storeflags
- * | osign 'FLAGS.SILENT' ' ' storeflags
- * osign :
- * | '+' | '-'
- * storeflags : flagList | flags
- */
- static Store*
- storeWhat(void)
- {
- int f;
- char *s;
- int c, w;
- c = peekc();
- if(c == '+' || c == '-')
- mustBe(c);
- else
- c = 0;
- s = atom();
- w = 0;
- if(cistrcmp(s, "flags") == 0)
- w = STFlags;
- else if(cistrcmp(s, "flags.silent") == 0)
- w = STFlagsSilent;
- else
- parseErr("illegal store attribute");
- mustBe(' ');
- if(peekc() == '(')
- f = flagList();
- else
- f = flags();
- return mkStore(c, w, f);
- }
- /*
- * fetchWhat : "ALL" | "FULL" | "FAST" | fetchAtt | '(' fetchAtts ')'
- * fetchAtts : fetchAtt | fetchAtts ' ' fetchAtt
- */
- static char *fetchAtom = "(){}%*\"\\[]";
- static Fetch*
- fetchWhat(void)
- {
- Fetch *f;
- char *s;
- if(peekc() == '('){
- getc();
- f = nil;
- for(;;){
- s = atomString(fetchAtom, "");
- f = fetchAtt(s, f);
- if(peekc() == ')')
- break;
- mustBe(' ');
- }
- getc();
- return revFetch(f);
- }
- s = atomString(fetchAtom, "");
- if(cistrcmp(s, "all") == 0)
- f = mkFetch(FFlags, mkFetch(FInternalDate, mkFetch(FRfc822Size, mkFetch(FEnvelope, nil))));
- else if(cistrcmp(s, "fast") == 0)
- f = mkFetch(FFlags, mkFetch(FInternalDate, mkFetch(FRfc822Size, nil)));
- else if(cistrcmp(s, "full") == 0)
- f = mkFetch(FFlags, mkFetch(FInternalDate, mkFetch(FRfc822Size, mkFetch(FEnvelope, mkFetch(FBody, nil)))));
- else
- f = fetchAtt(s, nil);
- return f;
- }
- /*
- * fetchAtt : "ENVELOPE" | "FLAGS" | "INTERNALDATE"
- * | "RFC822" | "RFC822.HEADER" | "RFC822.SIZE" | "RFC822.TEXT"
- * | "BODYSTRUCTURE"
- * | "UID"
- * | "BODY"
- * | "BODY" bodysubs
- * | "BODY.PEEK" bodysubs
- * bodysubs : sect
- * | sect '<' number '.' nz-number '>'
- * sect : '[' sectSpec ']'
- * sectSpec : sectMsgText
- * | sectPart
- * | sectPart '.' sectText
- * sectPart : nz-number
- * | sectPart '.' nz-number
- */
- static Fetch*
- fetchAtt(char *s, Fetch *f)
- {
- NList *sect;
- int c;
- if(cistrcmp(s, "envelope") == 0)
- return mkFetch(FEnvelope, f);
- if(cistrcmp(s, "flags") == 0)
- return mkFetch(FFlags, f);
- if(cistrcmp(s, "internaldate") == 0)
- return mkFetch(FInternalDate, f);
- if(cistrcmp(s, "RFC822") == 0)
- return mkFetch(FRfc822, f);
- if(cistrcmp(s, "RFC822.header") == 0)
- return mkFetch(FRfc822Head, f);
- if(cistrcmp(s, "RFC822.size") == 0)
- return mkFetch(FRfc822Size, f);
- if(cistrcmp(s, "RFC822.text") == 0)
- return mkFetch(FRfc822Text, f);
- if(cistrcmp(s, "bodystructure") == 0)
- return mkFetch(FBodyStruct, f);
- if(cistrcmp(s, "uid") == 0)
- return mkFetch(FUid, f);
- if(cistrcmp(s, "body") == 0){
- if(peekc() != '[')
- return mkFetch(FBody, f);
- f = mkFetch(FBodySect, f);
- }else if(cistrcmp(s, "body.peek") == 0)
- f = mkFetch(FBodyPeek, f);
- else
- parseErr("illegal fetch attribute");
- mustBe('[');
- c = peekc();
- if(c >= '1' && c <= '9'){
- sect = mkNList(number(1), nil);
- while(peekc() == '.'){
- getc();
- c = peekc();
- if(c >= '1' && c <= '9'){
- sect = mkNList(number(1), sect);
- }else{
- break;
- }
- }
- f->sect = revNList(sect);
- }
- if(peekc() != ']')
- sectText(f, f->sect != nil);
- mustBe(']');
- if(peekc() != '<')
- return f;
- f->partial = 1;
- mustBe('<');
- f->start = number(0);
- mustBe('.');
- f->size = number(1);
- mustBe('>');
- return f;
- }
- /*
- * sectText : sectMsgText | "MIME"
- * sectMsgText : "HEADER"
- * | "TEXT"
- * | "HEADER.FIELDS" ' ' hdrList
- * | "HEADER.FIELDS.NOT" ' ' hdrList
- * hdrList : '(' hdrs ')'
- * hdrs: : astring
- * | hdrs ' ' astring
- */
- static void
- sectText(Fetch *f, int mimeOk)
- {
- SList *h;
- char *s;
- s = atomString(fetchAtom, "");
- if(cistrcmp(s, "header") == 0){
- f->part = FPHead;
- return;
- }
- if(cistrcmp(s, "text") == 0){
- f->part = FPText;
- return;
- }
- if(mimeOk && cistrcmp(s, "mime") == 0){
- f->part = FPMime;
- return;
- }
- if(cistrcmp(s, "header.fields") == 0)
- f->part = FPHeadFields;
- else if(cistrcmp(s, "header.fields.not") == 0)
- f->part = FPHeadFieldsNot;
- else
- parseErr("illegal fetch section text");
- mustBe(' ');
- mustBe('(');
- h = nil;
- for(;;){
- h = mkSList(astring(), h);
- if(peekc() == ')')
- break;
- mustBe(' ');
- }
- mustBe(')');
- f->hdrs = revSList(h);
- }
- /*
- * searchWhat : "CHARSET" ' ' astring searchkeys | searchkeys
- * searchkeys : searchkey | searchkeys ' ' searchkey
- * searchkey : "ALL" | "ANSWERED" | "DELETED" | "FLAGGED" | "NEW" | "OLD" | "RECENT"
- * | "SEEN" | "UNANSWERED" | "UNDELETED" | "UNFLAGGED" | "DRAFT" | "UNDRAFT"
- * | astrkey ' ' astring
- * | datekey ' ' date
- * | "KEYWORD" ' ' flag | "UNKEYWORD" flag
- * | "LARGER" ' ' number | "SMALLER" ' ' number
- * | "HEADER" astring ' ' astring
- * | set | "UID" ' ' set
- * | "NOT" ' ' searchkey
- * | "OR" ' ' searchkey ' ' searchkey
- * | '(' searchkeys ')'
- * astrkey : "BCC" | "BODY" | "CC" | "FROM" | "SUBJECT" | "TEXT" | "TO"
- * datekey : "BEFORE" | "ON" | "SINCE" | "SENTBEFORE" | "SENTON" | "SENTSINCE"
- */
- static NamedInt searchMap[] =
- {
- {"ALL", SKAll},
- {"ANSWERED", SKAnswered},
- {"DELETED", SKDeleted},
- {"FLAGGED", SKFlagged},
- {"NEW", SKNew},
- {"OLD", SKOld},
- {"RECENT", SKRecent},
- {"SEEN", SKSeen},
- {"UNANSWERED", SKUnanswered},
- {"UNDELETED", SKUndeleted},
- {"UNFLAGGED", SKUnflagged},
- {"DRAFT", SKDraft},
- {"UNDRAFT", SKUndraft},
- {"UNSEEN", SKUnseen},
- {nil, 0}
- };
- static NamedInt searchMapStr[] =
- {
- {"CHARSET", SKCharset},
- {"BCC", SKBcc},
- {"BODY", SKBody},
- {"CC", SKCc},
- {"FROM", SKFrom},
- {"SUBJECT", SKSubject},
- {"TEXT", SKText},
- {"TO", SKTo},
- {nil, 0}
- };
- static NamedInt searchMapDate[] =
- {
- {"BEFORE", SKBefore},
- {"ON", SKOn},
- {"SINCE", SKSince},
- {"SENTBEFORE", SKSentBefore},
- {"SENTON", SKSentOn},
- {"SENTSINCE", SKSentSince},
- {nil, 0}
- };
- static NamedInt searchMapFlag[] =
- {
- {"KEYWORD", SKKeyword},
- {"UNKEYWORD", SKUnkeyword},
- {nil, 0}
- };
- static NamedInt searchMapNum[] =
- {
- {"SMALLER", SKSmaller},
- {"LARGER", SKLarger},
- {nil, 0}
- };
- static Search*
- searchKeys(int first, Search *tail)
- {
- Search *s;
- for(;;){
- if(peekc() == '('){
- getc();
- tail = searchKeys(0, tail);
- mustBe(')');
- }else{
- s = searchKey(first);
- tail->next = s;
- tail = s;
- }
- first = 0;
- if(peekc() != ' ')
- break;
- getc();
- }
- return tail;
- }
- static Search*
- searchKey(int first)
- {
- Search *sr, rock;
- Tm tm;
- char *a;
- int i, c;
- sr = binalloc(&parseBin, sizeof(Search), 1);
- if(sr == nil)
- parseErr("out of memory");
- c = peekc();
- if(c >= '0' && c <= '9'){
- sr->key = SKSet;
- sr->set = msgSet(0);
- return sr;
- }
- a = atom();
- if(i = mapInt(searchMap, a))
- sr->key = i;
- else if(i = mapInt(searchMapStr, a)){
- if(!first && i == SKCharset)
- parseErr("illegal search key");
- sr->key = i;
- mustBe(' ');
- sr->s = astring();
- }else if(i = mapInt(searchMapDate, a)){
- sr->key = i;
- mustBe(' ');
- c = peekc();
- if(c == '"')
- getc();
- a = atom();
- if(!imap4Date(&tm, a))
- parseErr("bad date format");
- sr->year = tm.year;
- sr->mon = tm.mon;
- sr->mday = tm.mday;
- if(c == '"')
- mustBe('"');
- }else if(i = mapInt(searchMapFlag, a)){
- sr->key = i;
- mustBe(' ');
- c = peekc();
- if(c == '\\'){
- mustBe('\\');
- a = atomString(atomStop, "\\");
- }else
- a = atom();
- i = mapFlag(a);
- if(i == 0)
- parseErr("flag not supported");
- sr->num = i;
- }else if(i = mapInt(searchMapNum, a)){
- sr->key = i;
- mustBe(' ');
- sr->num = number(0);
- }else if(cistrcmp(a, "HEADER") == 0){
- sr->key = SKHeader;
- mustBe(' ');
- sr->hdr = astring();
- mustBe(' ');
- sr->s = astring();
- }else if(cistrcmp(a, "UID") == 0){
- sr->key = SKUid;
- mustBe(' ');
- sr->set = msgSet(0);
- }else if(cistrcmp(a, "NOT") == 0){
- sr->key = SKNot;
- mustBe(' ');
- rock.next = nil;
- searchKeys(0, &rock);
- sr->left = rock.next;
- }else if(cistrcmp(a, "OR") == 0){
- sr->key = SKOr;
- mustBe(' ');
- rock.next = nil;
- searchKeys(0, &rock);
- sr->left = rock.next;
- mustBe(' ');
- rock.next = nil;
- searchKeys(0, &rock);
- sr->right = rock.next;
- }else
- parseErr("illegal search key");
- return sr;
- }
- /*
- * set : seqno
- * | seqno ':' seqno
- * | set ',' set
- * seqno: nz-number
- * | '*'
- *
- */
- static MsgSet*
- msgSet(int uids)
- {
- MsgSet head, *last, *ms;
- ulong from, to;
- last = &head;
- head.next = nil;
- for(;;){
- from = uids ? uidNo() : seqNo();
- to = from;
- if(peekc() == ':'){
- getc();
- to = uids ? uidNo() : seqNo();
- }
- ms = binalloc(&parseBin, sizeof(MsgSet), 0);
- if(ms == nil)
- parseErr("out of memory");
- ms->from = from;
- ms->to = to;
- ms->next = nil;
- last->next = ms;
- last = ms;
- if(peekc() != ',')
- break;
- getc();
- }
- return head.next;
- }
- static ulong
- seqNo(void)
- {
- if(peekc() == '*'){
- getc();
- return ~0UL;
- }
- return number(1);
- }
- static ulong
- uidNo(void)
- {
- if(peekc() == '*'){
- getc();
- return ~0UL;
- }
- return number(0);
- }
- /*
- * 7 bit, non-ctl chars, no (){%*"\
- * NIL is special case for nstring or parenlist
- */
- static char *
- atom(void)
- {
- return atomString(atomStop, "");
- }
- /*
- * like an atom, but no +
- */
- static char *
- tag(void)
- {
- return atomString("+(){%*\"\\", "");
- }
- /*
- * string or atom allowing %*
- */
- static char *
- listmbox(void)
- {
- int c;
- c = peekc();
- if(c == '{')
- return literal();
- if(c == '"')
- return quoted();
- return atomString("(){\"\\", "");
- }
- /*
- * string or atom
- */
- static char *
- astring(void)
- {
- int c;
- c = peekc();
- if(c == '{')
- return literal();
- if(c == '"')
- return quoted();
- return atom();
- }
- /*
- * 7 bit, non-ctl chars, none from exception list
- */
- static char *
- atomString(char *disallowed, char *initial)
- {
- char *s;
- int c, ns, as;
- ns = strlen(initial);
- s = binalloc(&parseBin, ns + StrAlloc, 0);
- if(s == nil)
- parseErr("out of memory");
- strcpy(s, initial);
- as = ns + StrAlloc;
- for(;;){
- c = getc();
- if(c <= ' ' || c >= 0x7f || strchr(disallowed, c) != nil){
- ungetc();
- break;
- }
- s[ns++] = c;
- if(ns >= as){
- s = bingrow(&parseBin, s, as, as + StrAlloc, 0);
- if(s == nil)
- parseErr("out of memory");
- as += StrAlloc;
- }
- }
- if(ns == 0)
- badsyn();
- s[ns] = '\0';
- return s;
- }
- /*
- * quoted: '"' chars* '"'
- * chars: 1-128 except \r and \n
- */
- static char *
- quoted(void)
- {
- char *s;
- int c, ns, as;
- mustBe('"');
- s = binalloc(&parseBin, StrAlloc, 0);
- if(s == nil)
- parseErr("out of memory");
- as = StrAlloc;
- ns = 0;
- for(;;){
- c = getc();
- if(c == '"')
- break;
- if(c < 1 || c > 0x7f || c == '\r' || c == '\n')
- badsyn();
- if(c == '\\'){
- c = getc();
- if(c != '\\' && c != '"')
- badsyn();
- }
- s[ns++] = c;
- if(ns >= as){
- s = bingrow(&parseBin, s, as, as + StrAlloc, 0);
- if(s == nil)
- parseErr("out of memory");
- as += StrAlloc;
- }
- }
- s[ns] = '\0';
- return s;
- }
- /*
- * litlen: {number}\r\n
- */
- static ulong
- litlen(void)
- {
- ulong v;
- mustBe('{');
- v = number(0);
- mustBe('}');
- crnl();
- return v;
- }
- /*
- * literal: litlen data<0:litlen>
- */
- static char *
- literal(void)
- {
- char *s;
- ulong v;
- v = litlen();
- s = binalloc(&parseBin, v+1, 0);
- if(s == nil)
- parseErr("out of memory");
- Bprint(&bout, "+ Ready for literal data\r\n");
- if(Bflush(&bout) < 0)
- writeErr();
- if(v != 0 && Bread(&bin, s, v) != v)
- badsyn();
- s[v] = '\0';
- return s;
- }
- /*
- * digits; number is 32 bits
- */
- static ulong
- number(int nonzero)
- {
- ulong v;
- int c, first;
- v = 0;
- first = 1;
- for(;;){
- c = getc();
- if(c < '0' || c > '9'){
- ungetc();
- if(first)
- badsyn();
- break;
- }
- if(nonzero && first && c == '0')
- badsyn();
- c -= '0';
- first = 0;
- if(v > UlongMax/10 || v == UlongMax/10 && c > UlongMax%10)
- parseErr("number out of range\r\n");
- v = v * 10 + c;
- }
- return v;
- }
- static int
- getc(void)
- {
- return Bgetc(&bin);
- }
- static void
- ungetc(void)
- {
- Bungetc(&bin);
- }
- static int
- peekc(void)
- {
- int c;
- c = Bgetc(&bin);
- Bungetc(&bin);
- return c;
- }
|