plan9.c 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412
  1. #include "common.h"
  2. #include <ctype.h>
  3. #include <plumb.h>
  4. #include <libsec.h>
  5. #include "dat.h"
  6. enum {
  7. Buffersize = 64*1024,
  8. };
  9. typedef struct Inbuf Inbuf;
  10. struct Inbuf
  11. {
  12. int fd;
  13. uchar *lim;
  14. uchar *rptr;
  15. uchar *wptr;
  16. uchar data[Buffersize+7];
  17. };
  18. static void
  19. addtomessage(Message *m, uchar *p, int n, int done)
  20. {
  21. int i, len;
  22. // add to message (+1 in malloc is for a trailing NUL)
  23. if(m->lim - m->end < n){
  24. if(m->start != nil){
  25. i = m->end-m->start;
  26. if(done)
  27. len = i + n;
  28. else
  29. len = (4*(i+n))/3;
  30. m->start = erealloc(m->start, len + 1);
  31. m->end = m->start + i;
  32. } else {
  33. if(done)
  34. len = n;
  35. else
  36. len = 2*n;
  37. m->start = emalloc(len + 1);
  38. m->end = m->start;
  39. }
  40. m->lim = m->start + len;
  41. *m->lim = '\0';
  42. }
  43. memmove(m->end, p, n);
  44. m->end += n;
  45. *m->end = '\0';
  46. }
  47. //
  48. // read in a single message
  49. //
  50. static int
  51. readmessage(Message *m, Inbuf *inb)
  52. {
  53. int i, n, done;
  54. uchar *p, *np;
  55. char sdigest[SHA1dlen*2+1];
  56. char tmp[64];
  57. for(done = 0; !done;){
  58. n = inb->wptr - inb->rptr;
  59. if(n < 6){
  60. if(n)
  61. memmove(inb->data, inb->rptr, n);
  62. inb->rptr = inb->data;
  63. inb->wptr = inb->rptr + n;
  64. i = read(inb->fd, inb->wptr, Buffersize);
  65. if(i < 0){
  66. if(fd2path(inb->fd, tmp, sizeof tmp) < 0)
  67. strcpy(tmp, "unknown mailbox");
  68. fprint(2, "error reading '%s': %r\n", tmp);
  69. return -1;
  70. }
  71. if(i == 0){
  72. if(n != 0)
  73. addtomessage(m, inb->rptr, n, 1);
  74. if(m->end == m->start)
  75. return -1;
  76. break;
  77. }
  78. inb->wptr += i;
  79. }
  80. // look for end of message
  81. for(p = inb->rptr; p < inb->wptr; p = np+1){
  82. // first part of search for '\nFrom '
  83. np = memchr(p, '\n', inb->wptr - p);
  84. if(np == nil){
  85. p = inb->wptr;
  86. break;
  87. }
  88. /*
  89. * if we've found a \n but there's
  90. * not enough room for '\nFrom ', don't do
  91. * the comparison till we've read in more.
  92. */
  93. if(inb->wptr - np < 6){
  94. p = np;
  95. break;
  96. }
  97. if(strncmp((char*)np, "\nFrom ", 6) == 0){
  98. done = 1;
  99. p = np+1;
  100. break;
  101. }
  102. }
  103. // add to message (+ 1 in malloc is for a trailing null)
  104. n = p - inb->rptr;
  105. addtomessage(m, inb->rptr, n, done);
  106. inb->rptr += n;
  107. }
  108. // if it doesn't start with a 'From ', this ain't a mailbox
  109. if(strncmp(m->start, "From ", 5) != 0)
  110. return -1;
  111. // dump trailing newline, make sure there's a trailing null
  112. // (helps in body searches)
  113. if(*(m->end-1) == '\n')
  114. m->end--;
  115. *m->end = 0;
  116. m->bend = m->rbend = m->end;
  117. // digest message
  118. sha1((uchar*)m->start, m->end - m->start, m->digest, nil);
  119. for(i = 0; i < SHA1dlen; i++)
  120. sprint(sdigest+2*i, "%2.2ux", m->digest[i]);
  121. m->sdigest = s_copy(sdigest);
  122. return 0;
  123. }
  124. // throw out deleted messages. return number of freshly deleted messages
  125. int
  126. purgedeleted(Mailbox *mb)
  127. {
  128. Message *m, *next;
  129. int newdels;
  130. // forget about what's no longer in the mailbox
  131. newdels = 0;
  132. for(m = mb->root->part; m != nil; m = next){
  133. next = m->next;
  134. if(m->deleted && m->refs == 0){
  135. if(m->inmbox)
  136. newdels++;
  137. delmessage(mb, m);
  138. }
  139. }
  140. return newdels;
  141. }
  142. //
  143. // read in the mailbox and parse into messages.
  144. //
  145. static char*
  146. _readmbox(Mailbox *mb, int doplumb, Mlock *lk)
  147. {
  148. int fd, n;
  149. String *tmp;
  150. Dir *d;
  151. static char err[Errlen];
  152. Message *m, **l;
  153. Inbuf *inb;
  154. char *x;
  155. l = &mb->root->part;
  156. /*
  157. * open the mailbox. If it doesn't exist, try the temporary one.
  158. */
  159. n = 0;
  160. retry:
  161. fd = open(mb->path, OREAD);
  162. if(fd < 0){
  163. rerrstr(err, sizeof(err));
  164. if(strstr(err, "exclusive lock") != 0 && n++ < 20){
  165. sleep(500); /* wait for lock to go away */
  166. goto retry;
  167. }
  168. if(strstr(err, "exist") != 0){
  169. tmp = s_copy(mb->path);
  170. s_append(tmp, ".tmp");
  171. if(sysrename(s_to_c(tmp), mb->path) == 0){
  172. s_free(tmp);
  173. goto retry;
  174. }
  175. s_free(tmp);
  176. }
  177. return err;
  178. }
  179. /*
  180. * a new qid.path means reread the mailbox, while
  181. * a new qid.vers means read any new messages
  182. */
  183. d = dirfstat(fd);
  184. if(d == nil){
  185. close(fd);
  186. errstr(err, sizeof(err));
  187. return err;
  188. }
  189. if(mb->d != nil){
  190. if(d->qid.path == mb->d->qid.path && d->qid.vers == mb->d->qid.vers){
  191. close(fd);
  192. free(d);
  193. return nil;
  194. }
  195. if(d->qid.path == mb->d->qid.path){
  196. while(*l != nil)
  197. l = &(*l)->next;
  198. seek(fd, mb->d->length, 0);
  199. }
  200. free(mb->d);
  201. }
  202. mb->d = d;
  203. mb->vers++;
  204. henter(PATH(0, Qtop), mb->name,
  205. (Qid){PATH(mb->id, Qmbox), mb->vers, QTDIR}, nil, mb);
  206. inb = emalloc(sizeof(Inbuf));
  207. inb->rptr = inb->wptr = inb->data;
  208. inb->fd = fd;
  209. // read new messages
  210. snprint(err, sizeof err, "reading '%s'", mb->path);
  211. logmsg(err, nil);
  212. for(;;){
  213. if(lk != nil)
  214. syslockrefresh(lk);
  215. m = newmessage(mb->root);
  216. m->mallocd = 1;
  217. m->inmbox = 1;
  218. if(readmessage(m, inb) < 0){
  219. delmessage(mb, m);
  220. mb->root->subname--;
  221. break;
  222. }
  223. // merge mailbox versions
  224. while(*l != nil){
  225. if(memcmp((*l)->digest, m->digest, SHA1dlen) == 0){
  226. // matches mail we already read, discard
  227. logmsg("duplicate", *l);
  228. delmessage(mb, m);
  229. mb->root->subname--;
  230. m = nil;
  231. l = &(*l)->next;
  232. break;
  233. } else {
  234. // old mail no longer in box, mark deleted
  235. logmsg("disappeared", *l);
  236. if(doplumb)
  237. mailplumb(mb, *l, 1);
  238. (*l)->inmbox = 0;
  239. (*l)->deleted = 1;
  240. l = &(*l)->next;
  241. }
  242. }
  243. if(m == nil)
  244. continue;
  245. x = strchr(m->start, '\n');
  246. if(x == nil)
  247. m->header = m->end;
  248. else
  249. m->header = x + 1;
  250. m->mheader = m->mhend = m->header;
  251. parseunix(m);
  252. parse(m, 0, mb, 0);
  253. logmsg("new", m);
  254. /* chain in */
  255. *l = m;
  256. l = &m->next;
  257. if(doplumb)
  258. mailplumb(mb, m, 0);
  259. }
  260. logmsg("mbox read", nil);
  261. // whatever is left has been removed from the mbox, mark deleted
  262. while(*l != nil){
  263. if(doplumb)
  264. mailplumb(mb, *l, 1);
  265. (*l)->inmbox = 0;
  266. (*l)->deleted = 1;
  267. l = &(*l)->next;
  268. }
  269. close(fd);
  270. free(inb);
  271. return nil;
  272. }
  273. static void
  274. _writembox(Mailbox *mb, Mlock *lk)
  275. {
  276. Dir *d;
  277. Message *m;
  278. String *tmp;
  279. int mode, errs;
  280. Biobuf *b;
  281. tmp = s_copy(mb->path);
  282. s_append(tmp, ".tmp");
  283. /*
  284. * preserve old files permissions, if possible
  285. */
  286. d = dirstat(mb->path);
  287. if(d != nil){
  288. mode = d->mode&0777;
  289. free(d);
  290. } else
  291. mode = MBOXMODE;
  292. sysremove(s_to_c(tmp));
  293. b = sysopen(s_to_c(tmp), "alc", mode);
  294. if(b == 0){
  295. fprint(2, "can't write temporary mailbox %s: %r\n", s_to_c(tmp));
  296. return;
  297. }
  298. logmsg("writing new mbox", nil);
  299. errs = 0;
  300. for(m = mb->root->part; m != nil; m = m->next){
  301. if(lk != nil)
  302. syslockrefresh(lk);
  303. if(m->deleted)
  304. continue;
  305. logmsg("writing", m);
  306. if(Bwrite(b, m->start, m->end - m->start) < 0)
  307. errs = 1;
  308. if(Bwrite(b, "\n", 1) < 0)
  309. errs = 1;
  310. }
  311. logmsg("wrote new mbox", nil);
  312. if(sysclose(b) < 0)
  313. errs = 1;
  314. if(errs){
  315. fprint(2, "error writing temporary mail file\n");
  316. s_free(tmp);
  317. return;
  318. }
  319. sysremove(mb->path);
  320. if(sysrename(s_to_c(tmp), mb->path) < 0)
  321. fprint(2, "%s: can't rename %s to %s: %r\n", argv0,
  322. s_to_c(tmp), mb->path);
  323. s_free(tmp);
  324. if(mb->d != nil)
  325. free(mb->d);
  326. mb->d = dirstat(mb->path);
  327. }
  328. char*
  329. plan9syncmbox(Mailbox *mb, int doplumb)
  330. {
  331. Mlock *lk;
  332. char *rv;
  333. lk = nil;
  334. if(mb->dolock){
  335. lk = syslock(mb->path);
  336. if(lk == nil)
  337. return "can't lock mailbox";
  338. }
  339. rv = _readmbox(mb, doplumb, lk); /* interpolate */
  340. if(purgedeleted(mb) > 0)
  341. _writembox(mb, lk);
  342. if(lk != nil)
  343. sysunlock(lk);
  344. return rv;
  345. }
  346. //
  347. // look to see if we can open this mail box
  348. //
  349. char*
  350. plan9mbox(Mailbox *mb, char *path)
  351. {
  352. static char err[Errlen];
  353. String *tmp;
  354. if(access(path, AEXIST) < 0){
  355. errstr(err, sizeof(err));
  356. tmp = s_copy(path);
  357. s_append(tmp, ".tmp");
  358. if(access(s_to_c(tmp), AEXIST) < 0){
  359. s_free(tmp);
  360. return err;
  361. }
  362. s_free(tmp);
  363. }
  364. mb->sync = plan9syncmbox;
  365. return nil;
  366. }