plan9.c 7.3 KB

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