📄 imap4d.c
字号:
fetchCmd(char *tg, char *cmd){ fetchUCmd(tg, cmd, 0);}static voidfetchUCmd(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 voididleCmd(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 voidlistCmd(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 voidloginCmd(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 voidlogoutCmd(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 voidnamespaceCmd(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 voidnoopCmd(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 voidrenameCmd(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 voidsearchCmd(char *tg, char *cmd){ searchUCmd(tg, cmd, 0);}static voidsearchUCmd(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 voidselectCmd(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 voidstatusCmd(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 = ""; 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 voidstoreCmd(char *tg, char *cmd){ storeUCmd(tg, cmd, 0);}static voidstoreUCmd(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 voidsubscribeCmd(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 voiduidCmd(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 voidunsubscribeCmd(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 voidbadsyn(void){ parseErr("bad syntax");}static voidclearcmd(void){ int c; for(;;){ c = getc(); if(c < 0) bye("end of input"); if(c == '\n') return; }}static voidcrnl(void){ int c; c = getc(); if(c == '\n') return; if(c != '\r' || getc() != '\n') badsyn();}static voidmustBe(int c){ if(getc() != c){ ungetc(); badsyn(); }}/* * flaglist : '(' ')' | '(' flags ')' */static intflagList(void){ int f; mustBe('('); f = 0; if(peekc() != ')') f = flags(); mustBe(')');
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -