|
@@ -1,2110 +0,0 @@
|
|
|
-/*
|
|
|
- * This file is part of the UCB release of Plan 9. It is subject to the license
|
|
|
- * terms in the LICENSE file found in the top-level directory of this
|
|
|
- * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
|
|
|
- * part of the UCB release of Plan 9, including this file, may be copied,
|
|
|
- * modified, propagated, or distributed except according to the terms contained
|
|
|
- * in the LICENSE file.
|
|
|
- */
|
|
|
-
|
|
|
-#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 uint32_t litlen(void);
|
|
|
-static MsgSet *msgSet(int);
|
|
|
-static void mustBe(int c);
|
|
|
-static uint32_t number(int nonzero);
|
|
|
-static int peekc(void);
|
|
|
-static char *quoted(void);
|
|
|
-static void sectText(Fetch *f, int mimeOk);
|
|
|
-static uint32_t seqNo(void);
|
|
|
-static Store *storeWhat(void);
|
|
|
-static char *tag(void);
|
|
|
-static uint32_t 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];
|
|
|
- uint32_t 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;
|
|
|
- uint32_t 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;
|
|
|
- uint32_t 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;
|
|
|
- uint32_t id;
|
|
|
-
|
|
|
- mustBe(' ');
|
|
|
- rock.next = nil;
|
|
|
- searchKeys(1, &rock);
|
|
|
- crnl();
|
|
|
- uid = "";
|
|
|
- if(uids)
|
|
|
- uid = "uid ";
|
|
|
- if(rock.next != nil && rock.next->key == SKCharset){
|
|
|
- if(cistrcmp(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;
|
|
|
- uint32_t 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;
|
|
|
- uint32_t 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;
|
|
|
- uint32_t 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 uint32_t
|
|
|
-seqNo(void)
|
|
|
-{
|
|
|
- if(peekc() == '*'){
|
|
|
- getc();
|
|
|
- return ~0UL&0xFF;
|
|
|
- }
|
|
|
- return number(1);
|
|
|
-}
|
|
|
-
|
|
|
-static uint32_t
|
|
|
-uidNo(void)
|
|
|
-{
|
|
|
- if(peekc() == '*'){
|
|
|
- getc();
|
|
|
- return ~0UL&0xFF;
|
|
|
- }
|
|
|
- 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 uint32_t
|
|
|
-litlen(void)
|
|
|
-{
|
|
|
- uint32_t v;
|
|
|
-
|
|
|
- mustBe('{');
|
|
|
- v = number(0);
|
|
|
- mustBe('}');
|
|
|
- crnl();
|
|
|
- return v;
|
|
|
-}
|
|
|
-
|
|
|
-/*
|
|
|
- * literal: litlen data<0:litlen>
|
|
|
- */
|
|
|
-static char *
|
|
|
-literal(void)
|
|
|
-{
|
|
|
- char *s;
|
|
|
- uint32_t 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 uint32_t
|
|
|
-number(int nonzero)
|
|
|
-{
|
|
|
- uint32_t 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;
|
|
|
-}
|
|
|
-
|