📄 mbox.c
字号:
#include <u.h>#include <libc.h>#include <bio.h>#include <auth.h>#include "imap4d.h"static NamedInt flagChars[NFlags] ={ {"s", MSeen}, {"a", MAnswered}, {"f", MFlagged}, {"D", MDeleted}, {"d", MDraft}, {"r", MRecent},};static int fsCtl = -1;static void boxFlags(Box *box);static int createImp(Box *box, Qid *qid);static void fsInit(void);static void mboxGone(Box *box);static MbLock *openImp(Box *box, int new);static int parseImp(Biobuf *b, Box *box);static int readBox(Box *box);static ulong uidRenumber(Msg *m, ulong uid, int force);static int impFlags(Box *box, Msg *m, char *flags);/* * strategy: * every mailbox file has an associated .imp file * which maps upas/fs message digests to uids & message flags. * * the .imp files are locked by /mail/fs/usename/L.mbox. * whenever the flags can be modified, the lock file * should be opened, thereby locking the uid & flag state. * for example, whenever new uids are assigned to messages, * and whenever flags are changed internally, the lock file * should be open and locked. this means the file must be * opened during store command, and when changing the \seen * flag for the fetch command. * * if no .imp file exists, a null one must be created before * assigning uids. * * the .imp file has the following format * imp : "imap internal mailbox description\n" * uidvalidity " " uidnext "\n" * messageLines * * messageLines : * | messageLines digest " " uid " " flags "\n" * * uid, uidnext, and uidvalidity are 32 bit decimal numbers * printed right justified in a field NUid characters long. * the 0 uid implies that no uid has been assigned to the message, * but the flags are valid. note that message lines are in mailbox * order, except possibly for 0 uid messages. * * digest is an ascii hex string NDigest characters long. * * flags has a character for each of NFlag flag fields. * if the flag is clear, it is represented by a "-". * set flags are represented as a unique single ascii character. * the currently assigned flags are, in order: * MSeen s * MAnswered a * MFlagged f * MDeleted D * MDraft d */Box*openBox(char *name, char *fsname, int writable){ Box *box; MbLock *ml; int n, new; if(cistrcmp(name, "inbox") == 0) name = "mbox"; fsInit(); if(fprint(fsCtl, "open /mail/box/%s/%s %s", username, name, fsname) < 0){//ZZZchar err[ERRMAX];errstr(err, sizeof err);if(strstr(err, "file does not exist") == nil) fprint(2, "imap4d at %lud: upas/fs open %s/%s as %s failed: '%s' %s\n", time(nil), username, name, fsname, err, ctime(time(nil))); fprint(fsCtl, "close %s", fsname); return nil; } /* * read box to find all messages * each one has a directory, and is in numerical order */ box = MKZ(Box); box->writable = writable; n = strlen(name) + 1; box->name = emalloc(n); strcpy(box->name, name); n += STRLEN(".imp"); box->imp = emalloc(n); snprint(box->imp, n, "%s.imp", name); n = strlen(fsname) + 1; box->fs = emalloc(n); strcpy(box->fs, fsname); n = STRLEN("/mail/fs/") + strlen(fsname) + 1; box->fsDir = emalloc(n); snprint(box->fsDir, n, "/mail/fs/%s", fsname); box->uidnext = 1; new = readBox(box); if(new >= 0){ ml = openImp(box, new); if(ml != nil){ closeImp(box, ml); return box; } } closeBox(box, 0); return nil;}/* * check mailbox * returns fd of open .imp file if imped. * otherwise, return value is insignificant * * careful: called by idle polling proc */MbLock*checkBox(Box *box, int imped){ MbLock *ml; Dir *d; int new; if(box == nil) return nil; /* * if stat fails, mailbox must be gone */ d = cdDirstat(box->fsDir, "."); if(d == nil){ mboxGone(box); return nil; } new = 0; if(box->qid.path != d->qid.path || box->qid.vers != d->qid.vers || box->mtime != d->mtime){ new = readBox(box); if(new < 0){ free(d); return nil; } } free(d); ml = openImp(box, new); if(ml == nil) box->writable = 0; else if(!imped){ closeImp(box, ml); ml = nil; } return ml;}/* * mailbox is unreachable, so mark all messages expunged * clean up .imp files as well. */static voidmboxGone(Box *box){ Msg *m; if(cdExists(mboxDir, box->name) < 0) cdRemove(mboxDir, box->imp); for(m = box->msgs; m != nil; m = m->next) m->expunged = 1; box->writable = 0;}/* * read messages in the mailbox * mark message that no longer exist as expunged * returns -1 for failure, 0 if no new messages, 1 if new messages. */static intreadBox(Box *box){ Msg *msgs, *m, *last; Dir *d; char *s; long max, id; int i, nd, fd, new; fd = cdOpen(box->fsDir, ".", OREAD); if(fd < 0){ syslog(0, "mail", "imap4d at %lud: upas/fs stat of %s/%s aka %s failed: %r\n", time(nil), username, box->name, box->fsDir); mboxGone(box); return -1; } /* * read box to find all messages * each one has a directory, and is in numerical order */ d = dirfstat(fd); if(d == nil){ close(fd); return -1; } box->mtime = d->mtime; box->qid = d->qid; last = nil; msgs = box->msgs; max = 0; new = 0; free(d); while((nd = dirread(fd, &d)) > 0){ for(i = 0; i < nd; i++){ s = d[i].name; id = strtol(s, &s, 10); if(id <= max || *s != '\0' || (d[i].mode & DMDIR) != DMDIR) continue; max = id; while(msgs != nil){ last = msgs; msgs = msgs->next; if(last->id == id) goto continueDir; last->expunged = 1; } new = 1; m = MKZ(Msg); m->id = id; m->fsDir = box->fsDir; m->fs = emalloc(2 * (MsgNameLen + 1)); m->efs = seprint(m->fs, m->fs + (MsgNameLen + 1), "%lud/", id); m->size = ~0UL; m->lines = ~0UL; m->prev = last; m->flags = MRecent; if(!msgInfo(m)) freeMsg(m); else{ if(last == nil) box->msgs = m; else last->next = m; last = m; } continueDir:; } free(d); } close(fd); for(; msgs != nil; msgs = msgs->next) msgs->expunged = 1; /* * make up the imap message sequence numbers */ id = 1; for(m = box->msgs; m != nil; m = m->next){ if(m->seq && m->seq != id) bye("internal error assigning message numbers"); m->seq = id++; } box->max = id - 1; return new;}/* * read in the .imp file, or make one if it doesn't exist. * make sure all flags and uids are consistent. * return the mailbox lock. */#define IMPMAGIC "imap internal mailbox description\n"static MbLock*openImp(Box *box, int new){ Qid qid; Biobuf b; MbLock *ml; int fd;//ZZZZ int once; ml = mbLock(); if(ml == nil) return nil; fd = cdOpen(mboxDir, box->imp, OREAD); once = 0;ZZZhack: if(fd < 0 || fqid(fd, &qid) < 0){ if(fd < 0){ char buf[ERRMAX]; errstr(buf, sizeof buf); if(cistrstr(buf, "does not exist") == nil) fprint(2, "imap4d at %lud: imp open failed: %s\n", time(nil), buf); if(!once && cistrstr(buf, "locked") != nil){ once = 1; fprint(2, "imap4d at %lud: imp %s/%s %s locked when it shouldn't be; spinning\n", time(nil), username, box->name, box->imp); fd = openLocked(mboxDir, box->imp, OREAD); goto ZZZhack; } } if(fd >= 0) close(fd); fd = createImp(box, &qid); if(fd < 0){ mbUnlock(ml); return nil; } box->dirtyImp = 1; if(box->uidvalidity == 0) box->uidvalidity = box->mtime; box->impQid = qid; new = 1; }else if(qid.path != box->impQid.path || qid.vers != box->impQid.vers){ Binit(&b, fd, OREAD); if(!parseImp(&b, box)){ box->dirtyImp = 1; if(box->uidvalidity == 0) box->uidvalidity = box->mtime; } Bterm(&b); box->impQid = qid; new = 1; } if(new) boxFlags(box); close(fd); return ml;}/* * close the .imp file, after writing out any changes */voidcloseImp(Box *box, MbLock *ml){ Msg *m; Qid qid; Biobuf b; char buf[NFlags+1]; int fd; if(ml == nil) return; if(!box->dirtyImp){ mbUnlock(ml); return; } fd = cdCreate(mboxDir, box->imp, OWRITE, 0664); if(fd < 0){ mbUnlock(ml); return; } Binit(&b, fd, OWRITE); box->dirtyImp = 0; Bprint(&b, "%s", IMPMAGIC); Bprint(&b, "%.*lud %.*lud\n", NUid, box->uidvalidity, NUid, box->uidnext); for(m = box->msgs; m != nil; m = m->next){ if(m->expunged) continue; wrImpFlags(buf, m->flags, strcmp(box->fs, "imap") == 0); Bprint(&b, "%.*s %.*lud %s\n", NDigest, m->info[IDigest], NUid, m->uid, buf); } Bterm(&b); if(fqid(fd, &qid) >= 0) box->impQid = qid; close(fd); mbUnlock(ml);}voidwrImpFlags(char *buf, int flags, int killRecent){ int i; for(i = 0; i < NFlags; i++){ if((flags & flagChars[i].v) && (flagChars[i].v != MRecent || !killRecent)) buf[i] = flagChars[i].name[0]; else buf[i] = '-'; } buf[i] = '\0';
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -