mbox.c 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863
  1. #include <u.h>
  2. #include <libc.h>
  3. #include <bio.h>
  4. #include <auth.h>
  5. #include "imap4d.h"
  6. static NamedInt flagChars[NFlags] =
  7. {
  8. {"s", MSeen},
  9. {"a", MAnswered},
  10. {"f", MFlagged},
  11. {"D", MDeleted},
  12. {"d", MDraft},
  13. {"r", MRecent},
  14. };
  15. static int fsCtl = -1;
  16. static void boxFlags(Box *box);
  17. static int createImp(Box *box, Qid *qid);
  18. static void fsInit(void);
  19. static void mboxGone(Box *box);
  20. static MbLock *openImp(Box *box, int new);
  21. static int parseImp(Biobuf *b, Box *box);
  22. static int readBox(Box *box);
  23. static ulong uidRenumber(Msg *m, ulong uid, int force);
  24. static int impFlags(Box *box, Msg *m, char *flags);
  25. /*
  26. * strategy:
  27. * every mailbox file has an associated .imp file
  28. * which maps upas/fs message digests to uids & message flags.
  29. *
  30. * the .imp files are locked by /mail/fs/usename/L.mbox.
  31. * whenever the flags can be modified, the lock file
  32. * should be opened, thereby locking the uid & flag state.
  33. * for example, whenever new uids are assigned to messages,
  34. * and whenever flags are changed internally, the lock file
  35. * should be open and locked. this means the file must be
  36. * opened during store command, and when changing the \seen
  37. * flag for the fetch command.
  38. *
  39. * if no .imp file exists, a null one must be created before
  40. * assigning uids.
  41. *
  42. * the .imp file has the following format
  43. * imp : "imap internal mailbox description\n"
  44. * uidvalidity " " uidnext "\n"
  45. * messageLines
  46. *
  47. * messageLines :
  48. * | messageLines digest " " uid " " flags "\n"
  49. *
  50. * uid, uidnext, and uidvalidity are 32 bit decimal numbers
  51. * printed right justified in a field NUid characters long.
  52. * the 0 uid implies that no uid has been assigned to the message,
  53. * but the flags are valid. note that message lines are in mailbox
  54. * order, except possibly for 0 uid messages.
  55. *
  56. * digest is an ascii hex string NDigest characters long.
  57. *
  58. * flags has a character for each of NFlag flag fields.
  59. * if the flag is clear, it is represented by a "-".
  60. * set flags are represented as a unique single ascii character.
  61. * the currently assigned flags are, in order:
  62. * MSeen s
  63. * MAnswered a
  64. * MFlagged f
  65. * MDeleted D
  66. * MDraft d
  67. */
  68. Box*
  69. openBox(char *name, char *fsname, int writable)
  70. {
  71. Box *box;
  72. MbLock *ml;
  73. int n, new;
  74. if(cistrcmp(name, "inbox") == 0)
  75. if(access("msgs", AEXIST) == 0)
  76. name = "msgs";
  77. else
  78. name = "mbox";
  79. fsInit();
  80. debuglog("imap4d open %s %s\n", name, fsname);
  81. if(fprint(fsCtl, "open /mail/box/%s/%s %s", username, name, fsname) < 0){
  82. //ZZZ
  83. char err[ERRMAX];
  84. rerrstr(err, sizeof err);
  85. if(strstr(err, "file does not exist") == nil)
  86. fprint(2,
  87. "imap4d at %lud: upas/fs open %s/%s as %s failed: '%s' %s",
  88. time(nil), username, name, fsname, err,
  89. ctime(time(nil))); /* NB: ctime result ends with \n */
  90. fprint(fsCtl, "close %s", fsname);
  91. return nil;
  92. }
  93. /*
  94. * read box to find all messages
  95. * each one has a directory, and is in numerical order
  96. */
  97. box = MKZ(Box);
  98. box->writable = writable;
  99. n = strlen(name) + 1;
  100. box->name = emalloc(n);
  101. strcpy(box->name, name);
  102. n += STRLEN(".imp");
  103. box->imp = emalloc(n);
  104. snprint(box->imp, n, "%s.imp", name);
  105. n = strlen(fsname) + 1;
  106. box->fs = emalloc(n);
  107. strcpy(box->fs, fsname);
  108. n = STRLEN("/mail/fs/") + strlen(fsname) + 1;
  109. box->fsDir = emalloc(n);
  110. snprint(box->fsDir, n, "/mail/fs/%s", fsname);
  111. box->uidnext = 1;
  112. new = readBox(box);
  113. if(new >= 0){
  114. ml = openImp(box, new);
  115. if(ml != nil){
  116. closeImp(box, ml);
  117. return box;
  118. }
  119. }
  120. closeBox(box, 0);
  121. return nil;
  122. }
  123. /*
  124. * check mailbox
  125. * returns fd of open .imp file if imped.
  126. * otherwise, return value is insignificant
  127. *
  128. * careful: called by idle polling proc
  129. */
  130. MbLock*
  131. checkBox(Box *box, int imped)
  132. {
  133. MbLock *ml;
  134. Dir *d;
  135. int new;
  136. if(box == nil)
  137. return nil;
  138. /*
  139. * if stat fails, mailbox must be gone
  140. */
  141. d = cdDirstat(box->fsDir, ".");
  142. if(d == nil){
  143. mboxGone(box);
  144. return nil;
  145. }
  146. new = 0;
  147. if(box->qid.path != d->qid.path || box->qid.vers != d->qid.vers
  148. || box->mtime != d->mtime){
  149. new = readBox(box);
  150. if(new < 0){
  151. free(d);
  152. return nil;
  153. }
  154. }
  155. free(d);
  156. ml = openImp(box, new);
  157. if(ml == nil)
  158. box->writable = 0;
  159. else if(!imped){
  160. closeImp(box, ml);
  161. ml = nil;
  162. }
  163. return ml;
  164. }
  165. /*
  166. * mailbox is unreachable, so mark all messages expunged
  167. * clean up .imp files as well.
  168. */
  169. static void
  170. mboxGone(Box *box)
  171. {
  172. Msg *m;
  173. if(cdExists(mboxDir, box->name) < 0)
  174. cdRemove(mboxDir, box->imp);
  175. for(m = box->msgs; m != nil; m = m->next)
  176. m->expunged = 1;
  177. box->writable = 0;
  178. }
  179. /*
  180. * read messages in the mailbox
  181. * mark message that no longer exist as expunged
  182. * returns -1 for failure, 0 if no new messages, 1 if new messages.
  183. */
  184. static int
  185. readBox(Box *box)
  186. {
  187. Msg *msgs, *m, *last;
  188. Dir *d;
  189. char *s;
  190. long max, id;
  191. int i, nd, fd, new;
  192. fd = cdOpen(box->fsDir, ".", OREAD);
  193. if(fd < 0){
  194. syslog(0, "mail",
  195. "imap4d at %lud: upas/fs stat of %s/%s aka %s failed: %r",
  196. time(nil), username, box->name, box->fsDir);
  197. mboxGone(box);
  198. return -1;
  199. }
  200. /*
  201. * read box to find all messages
  202. * each one has a directory, and is in numerical order
  203. */
  204. d = dirfstat(fd);
  205. if(d == nil){
  206. close(fd);
  207. return -1;
  208. }
  209. box->mtime = d->mtime;
  210. box->qid = d->qid;
  211. last = nil;
  212. msgs = box->msgs;
  213. max = 0;
  214. new = 0;
  215. free(d);
  216. while((nd = dirread(fd, &d)) > 0){
  217. for(i = 0; i < nd; i++){
  218. s = d[i].name;
  219. id = strtol(s, &s, 10);
  220. if(id <= max || *s != '\0'
  221. || (d[i].mode & DMDIR) != DMDIR)
  222. continue;
  223. max = id;
  224. while(msgs != nil){
  225. last = msgs;
  226. msgs = msgs->next;
  227. if(last->id == id)
  228. goto continueDir;
  229. last->expunged = 1;
  230. }
  231. new = 1;
  232. m = MKZ(Msg);
  233. m->id = id;
  234. m->fsDir = box->fsDir;
  235. m->fs = emalloc(2 * (MsgNameLen + 1));
  236. m->efs = seprint(m->fs, m->fs + (MsgNameLen + 1), "%lud/", id);
  237. m->size = ~0UL;
  238. m->lines = ~0UL;
  239. m->prev = last;
  240. m->flags = MRecent;
  241. if(!msgInfo(m))
  242. freeMsg(m);
  243. else{
  244. if(last == nil)
  245. box->msgs = m;
  246. else
  247. last->next = m;
  248. last = m;
  249. }
  250. continueDir:;
  251. }
  252. free(d);
  253. }
  254. close(fd);
  255. for(; msgs != nil; msgs = msgs->next)
  256. msgs->expunged = 1;
  257. /*
  258. * make up the imap message sequence numbers
  259. */
  260. id = 1;
  261. for(m = box->msgs; m != nil; m = m->next){
  262. if(m->seq && m->seq != id)
  263. bye("internal error assigning message numbers");
  264. m->seq = id++;
  265. }
  266. box->max = id - 1;
  267. return new;
  268. }
  269. /*
  270. * read in the .imp file, or make one if it doesn't exist.
  271. * make sure all flags and uids are consistent.
  272. * return the mailbox lock.
  273. */
  274. #define IMPMAGIC "imap internal mailbox description\n"
  275. static MbLock*
  276. openImp(Box *box, int new)
  277. {
  278. Qid qid;
  279. Biobuf b;
  280. MbLock *ml;
  281. int fd;
  282. //ZZZZ
  283. int once;
  284. ml = mbLock();
  285. if(ml == nil)
  286. return nil;
  287. fd = cdOpen(mboxDir, box->imp, OREAD);
  288. once = 0;
  289. ZZZhack:
  290. if(fd < 0 || fqid(fd, &qid) < 0){
  291. if(fd < 0){
  292. char buf[ERRMAX];
  293. errstr(buf, sizeof buf);
  294. if(cistrstr(buf, "does not exist") == nil)
  295. fprint(2, "imap4d at %lud: imp open failed: %s\n", time(nil), buf);
  296. if(!once && cistrstr(buf, "locked") != nil){
  297. once = 1;
  298. fprint(2, "imap4d at %lud: imp %s/%s %s locked when it shouldn't be; spinning\n", time(nil), username, box->name, box->imp);
  299. fd = openLocked(mboxDir, box->imp, OREAD);
  300. goto ZZZhack;
  301. }
  302. }
  303. if(fd >= 0)
  304. close(fd);
  305. fd = createImp(box, &qid);
  306. if(fd < 0){
  307. mbUnlock(ml);
  308. return nil;
  309. }
  310. box->dirtyImp = 1;
  311. if(box->uidvalidity == 0)
  312. box->uidvalidity = box->mtime;
  313. box->impQid = qid;
  314. new = 1;
  315. }else if(qid.path != box->impQid.path || qid.vers != box->impQid.vers){
  316. Binit(&b, fd, OREAD);
  317. if(!parseImp(&b, box)){
  318. box->dirtyImp = 1;
  319. if(box->uidvalidity == 0)
  320. box->uidvalidity = box->mtime;
  321. }
  322. Bterm(&b);
  323. box->impQid = qid;
  324. new = 1;
  325. }
  326. if(new)
  327. boxFlags(box);
  328. close(fd);
  329. return ml;
  330. }
  331. /*
  332. * close the .imp file, after writing out any changes
  333. */
  334. void
  335. closeImp(Box *box, MbLock *ml)
  336. {
  337. Msg *m;
  338. Qid qid;
  339. Biobuf b;
  340. char buf[NFlags+1];
  341. int fd;
  342. if(ml == nil)
  343. return;
  344. if(!box->dirtyImp){
  345. mbUnlock(ml);
  346. return;
  347. }
  348. fd = cdCreate(mboxDir, box->imp, OWRITE, 0664);
  349. if(fd < 0){
  350. mbUnlock(ml);
  351. return;
  352. }
  353. Binit(&b, fd, OWRITE);
  354. box->dirtyImp = 0;
  355. Bprint(&b, "%s", IMPMAGIC);
  356. Bprint(&b, "%.*lud %.*lud\n", NUid, box->uidvalidity, NUid, box->uidnext);
  357. for(m = box->msgs; m != nil; m = m->next){
  358. if(m->expunged)
  359. continue;
  360. wrImpFlags(buf, m->flags, strcmp(box->fs, "imap") == 0);
  361. Bprint(&b, "%.*s %.*lud %s\n", NDigest, m->info[IDigest], NUid, m->uid, buf);
  362. }
  363. Bterm(&b);
  364. if(fqid(fd, &qid) >= 0)
  365. box->impQid = qid;
  366. close(fd);
  367. mbUnlock(ml);
  368. }
  369. void
  370. wrImpFlags(char *buf, int flags, int killRecent)
  371. {
  372. int i;
  373. for(i = 0; i < NFlags; i++){
  374. if((flags & flagChars[i].v)
  375. && (flagChars[i].v != MRecent || !killRecent))
  376. buf[i] = flagChars[i].name[0];
  377. else
  378. buf[i] = '-';
  379. }
  380. buf[i] = '\0';
  381. }
  382. int
  383. emptyImp(char *mbox)
  384. {
  385. Dir *d;
  386. long mode;
  387. int fd;
  388. fd = cdCreate(mboxDir, impName(mbox), OWRITE, 0664);
  389. if(fd < 0)
  390. return -1;
  391. d = cdDirstat(mboxDir, mbox);
  392. if(d == nil){
  393. close(fd);
  394. return -1;
  395. }
  396. fprint(fd, "%s%.*lud %.*lud\n", IMPMAGIC, NUid, d->mtime, NUid, 1UL);
  397. mode = d->mode & 0777;
  398. nulldir(d);
  399. d->mode = mode;
  400. dirfwstat(fd, d);
  401. free(d);
  402. return fd;
  403. }
  404. /*
  405. * try to match permissions with mbox
  406. */
  407. static int
  408. createImp(Box *box, Qid *qid)
  409. {
  410. Dir *d;
  411. long mode;
  412. int fd;
  413. fd = cdCreate(mboxDir, box->imp, OREAD, 0664);
  414. if(fd < 0)
  415. return -1;
  416. d = cdDirstat(mboxDir, box->name);
  417. if(d != nil){
  418. mode = d->mode & 0777;
  419. nulldir(d);
  420. d->mode = mode;
  421. dirfwstat(fd, d);
  422. free(d);
  423. }
  424. if(fqid(fd, qid) < 0){
  425. close(fd);
  426. return -1;
  427. }
  428. return fd;
  429. }
  430. /*
  431. * read or re-read a .imp file.
  432. * this is tricky:
  433. * messages can be deleted by another agent
  434. * we might still have a Msg for an expunged message,
  435. * because we haven't told the client yet.
  436. * we can have a Msg without a .imp entry.
  437. * flag information is added at the end of the .imp by copy & append
  438. * there can be duplicate messages (same digests).
  439. *
  440. * look up existing messages based on uid.
  441. * look up new messages based on in order digest matching.
  442. *
  443. * note: in the face of duplicate messages, one of which is deleted,
  444. * two active servers may decide different ones are valid, and so return
  445. * different uids for the messages. this situation will stablize when the servers exit.
  446. */
  447. static int
  448. parseImp(Biobuf *b, Box *box)
  449. {
  450. Msg *m, *mm;
  451. char *s, *t, *toks[3];
  452. ulong uid, u;
  453. int match, n;
  454. m = box->msgs;
  455. s = Brdline(b, '\n');
  456. if(s == nil || Blinelen(b) != STRLEN(IMPMAGIC)
  457. || strncmp(s, IMPMAGIC, STRLEN(IMPMAGIC)) != 0)
  458. return 0;
  459. s = Brdline(b, '\n');
  460. if(s == nil || Blinelen(b) != 2*NUid + 2)
  461. return 0;
  462. s[2*NUid + 1] = '\0';
  463. u = strtoul(s, &t, 10);
  464. if(u != box->uidvalidity && box->uidvalidity != 0)
  465. return 0;
  466. box->uidvalidity = u;
  467. if(*t != ' ' || t != s + NUid)
  468. return 0;
  469. t++;
  470. u = strtoul(t, &t, 10);
  471. if(box->uidnext > u)
  472. return 0;
  473. box->uidnext = u;
  474. if(t != s + 2*NUid+1 || box->uidnext == 0)
  475. return 0;
  476. uid = ~0;
  477. while(m != nil){
  478. s = Brdline(b, '\n');
  479. if(s == nil)
  480. break;
  481. n = Blinelen(b) - 1;
  482. if(n != NDigest + NUid + NFlags + 2
  483. || s[NDigest] != ' ' || s[NDigest + NUid + 1] != ' ')
  484. return 0;
  485. toks[0] = s;
  486. s[NDigest] = '\0';
  487. toks[1] = s + NDigest + 1;
  488. s[NDigest + NUid + 1] = '\0';
  489. toks[2] = s + NDigest + NUid + 2;
  490. s[n] = '\0';
  491. t = toks[1];
  492. u = strtoul(t, &t, 10);
  493. if(*t != '\0' || uid != ~0 && (uid >= u && u || u && !uid))
  494. return 0;
  495. uid = u;
  496. /*
  497. * zero uid => added by append or copy, only flags valid
  498. * can only match messages without uids, but this message
  499. * may not be the next one, and may have been deleted.
  500. */
  501. if(!uid){
  502. for(; m != nil && m->uid; m = m->next)
  503. ;
  504. for(mm = m; mm != nil; mm = mm->next){
  505. if(mm->info[IDigest] != nil &&
  506. strcmp(mm->info[IDigest], toks[0]) == 0){
  507. if(!mm->uid)
  508. mm->flags = 0;
  509. if(!impFlags(box, mm, toks[2]))
  510. return 0;
  511. m = mm->next;
  512. break;
  513. }
  514. }
  515. continue;
  516. }
  517. /*
  518. * ignore expunged messages,
  519. * and messages already assigned uids which don't match this uid.
  520. * such messages must have been deleted by another imap server,
  521. * which updated the mailbox and .imp file since we read the mailbox,
  522. * or because upas/fs got confused by consecutive duplicate messages,
  523. * the first of which was deleted by another imap server.
  524. */
  525. for(; m != nil && (m->expunged || m->uid && m->uid < uid); m = m->next)
  526. ;
  527. if(m == nil)
  528. break;
  529. /*
  530. * only check for digest match on the next message,
  531. * since it comes before all other messages, and therefore
  532. * must be in the .imp file if they should be.
  533. */
  534. match = m->info[IDigest] != nil &&
  535. strcmp(m->info[IDigest], toks[0]) == 0;
  536. if(uid && (m->uid == uid || !m->uid && match)){
  537. if(!match)
  538. bye("inconsistent uid");
  539. /*
  540. * wipe out recent flag if some other server saw this new message.
  541. * it will be read from the .imp file if is really should be set,
  542. * ie the message was only seen by a status command.
  543. */
  544. if(!m->uid)
  545. m->flags = 0;
  546. if(!impFlags(box, m, toks[2]))
  547. return 0;
  548. m->uid = uid;
  549. m = m->next;
  550. }
  551. }
  552. return 1;
  553. }
  554. /*
  555. * parse .imp flags
  556. */
  557. static int
  558. impFlags(Box *box, Msg *m, char *flags)
  559. {
  560. int i, f;
  561. f = 0;
  562. for(i = 0; i < NFlags; i++){
  563. if(flags[i] == '-')
  564. continue;
  565. if(flags[i] != flagChars[i].name[0])
  566. return 0;
  567. f |= flagChars[i].v;
  568. }
  569. /*
  570. * recent flags are set until the first time message's box is selected or examined.
  571. * it may be stored in the file as a side effect of a status or subscribe command;
  572. * if so, clear it out.
  573. */
  574. if((f & MRecent) && strcmp(box->fs, "imap") == 0)
  575. box->dirtyImp = 1;
  576. f |= m->flags & MRecent;
  577. /*
  578. * all old messages with changed flags should be reported to the client
  579. */
  580. if(m->uid && m->flags != f){
  581. box->sendFlags = 1;
  582. m->sendFlags = 1;
  583. }
  584. m->flags = f;
  585. return 1;
  586. }
  587. /*
  588. * assign uids to any new messages
  589. * which aren't already in the .imp file.
  590. * sum up totals for flag values.
  591. */
  592. static void
  593. boxFlags(Box *box)
  594. {
  595. Msg *m;
  596. box->recent = 0;
  597. for(m = box->msgs; m != nil; m = m->next){
  598. if(m->uid == 0){
  599. box->dirtyImp = 1;
  600. box->uidnext = uidRenumber(m, box->uidnext, 0);
  601. }
  602. if(m->flags & MRecent)
  603. box->recent++;
  604. }
  605. }
  606. static ulong
  607. uidRenumber(Msg *m, ulong uid, int force)
  608. {
  609. for(; m != nil; m = m->next){
  610. if(!force && m->uid != 0)
  611. bye("uid renumbering with a valid uid");
  612. m->uid = uid++;
  613. }
  614. return uid;
  615. }
  616. void
  617. closeBox(Box *box, int opened)
  618. {
  619. Msg *m, *next;
  620. /*
  621. * make sure to leave the mailbox directory so upas/fs can close the mailbox
  622. */
  623. myChdir(mboxDir);
  624. if(box->writable){
  625. deleteMsgs(box);
  626. if(expungeMsgs(box, 0))
  627. closeImp(box, checkBox(box, 1));
  628. }
  629. if(fprint(fsCtl, "close %s", box->fs) < 0 && opened)
  630. bye("can't talk to mail server");
  631. for(m = box->msgs; m != nil; m = next){
  632. next = m->next;
  633. freeMsg(m);
  634. }
  635. free(box->name);
  636. free(box->fs);
  637. free(box->fsDir);
  638. free(box->imp);
  639. free(box);
  640. }
  641. int
  642. deleteMsgs(Box *box)
  643. {
  644. Msg *m;
  645. char buf[BufSize], *p, *start;
  646. int ok;
  647. if(!box->writable)
  648. return 0;
  649. /*
  650. * first pass: delete messages; gang the writes together for speed.
  651. */
  652. ok = 1;
  653. start = seprint(buf, buf + sizeof(buf), "delete %s", box->fs);
  654. p = start;
  655. for(m = box->msgs; m != nil; m = m->next){
  656. if((m->flags & MDeleted) && !m->expunged){
  657. m->expunged = 1;
  658. p = seprint(p, buf + sizeof(buf), " %lud", m->id);
  659. if(p + 32 >= buf + sizeof(buf)){
  660. if(write(fsCtl, buf, p - buf) < 0)
  661. bye("can't talk to mail server");
  662. p = start;
  663. }
  664. }
  665. }
  666. if(p != start && write(fsCtl, buf, p - buf) < 0)
  667. bye("can't talk to mail server");
  668. return ok;
  669. }
  670. /*
  671. * second pass: remove the message structure,
  672. * and renumber message sequence numbers.
  673. * update messages counts in mailbox.
  674. * returns true if anything changed.
  675. */
  676. int
  677. expungeMsgs(Box *box, int send)
  678. {
  679. Msg *m, *next, *last;
  680. ulong n;
  681. n = 0;
  682. last = nil;
  683. for(m = box->msgs; m != nil; m = next){
  684. m->seq -= n;
  685. next = m->next;
  686. if(m->expunged){
  687. if(send)
  688. Bprint(&bout, "* %lud expunge\r\n", m->seq);
  689. if(m->flags & MRecent)
  690. box->recent--;
  691. n++;
  692. if(last == nil)
  693. box->msgs = next;
  694. else
  695. last->next = next;
  696. freeMsg(m);
  697. }else
  698. last = m;
  699. }
  700. if(n){
  701. box->max -= n;
  702. box->dirtyImp = 1;
  703. }
  704. return n;
  705. }
  706. static void
  707. fsInit(void)
  708. {
  709. if(fsCtl >= 0)
  710. return;
  711. fsCtl = open("/mail/fs/ctl", ORDWR);
  712. if(fsCtl < 0)
  713. bye("can't open mail file system");
  714. if(fprint(fsCtl, "close mbox") < 0)
  715. bye("can't initialize mail file system");
  716. }
  717. static char *stoplist[] =
  718. {
  719. "mbox",
  720. "pipeto",
  721. "forward",
  722. "names",
  723. "pipefrom",
  724. "headers",
  725. "imap.ok",
  726. 0
  727. };
  728. enum {
  729. Maxokbytes = 4096,
  730. Maxfolders = Maxokbytes / 4,
  731. };
  732. static char *folders[Maxfolders];
  733. static char *folderbuff;
  734. static void
  735. readokfolders(void)
  736. {
  737. int fd, nr;
  738. fd = open("imap.ok", OREAD);
  739. if(fd < 0)
  740. return;
  741. folderbuff = malloc(Maxokbytes);
  742. if(folderbuff == nil) {
  743. close(fd);
  744. return;
  745. }
  746. nr = read(fd, folderbuff, Maxokbytes-1); /* once is ok */
  747. close(fd);
  748. if(nr < 0){
  749. free(folderbuff);
  750. folderbuff = nil;
  751. return;
  752. }
  753. folderbuff[nr] = 0;
  754. tokenize(folderbuff, folders, nelem(folders));
  755. }
  756. /*
  757. * reject bad mailboxes based on mailbox name
  758. */
  759. int
  760. okMbox(char *path)
  761. {
  762. char *name;
  763. int i;
  764. if(folderbuff == nil && access("imap.ok", AREAD) == 0)
  765. readokfolders();
  766. name = strrchr(path, '/');
  767. if(name == nil)
  768. name = path;
  769. else
  770. name++;
  771. if(folderbuff != nil){
  772. for(i = 0; i < nelem(folders) && folders[i] != nil; i++)
  773. if(cistrcmp(folders[i], name) == 0)
  774. return 1;
  775. return 0;
  776. }
  777. if(strlen(name) + STRLEN(".imp") >= MboxNameLen)
  778. return 0;
  779. for(i = 0; stoplist[i]; i++)
  780. if(strcmp(name, stoplist[i]) == 0)
  781. return 0;
  782. if(isprefix("L.", name) || isprefix("imap-tmp.", name)
  783. || issuffix(".imp", name)
  784. || strcmp("imap.subscribed", name) == 0
  785. || isdotdot(name) || name[0] == '/')
  786. return 0;
  787. return 1;
  788. }