plan9.c 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407
  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;
  149. String *tmp;
  150. Dir *d;
  151. static char err[128];
  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. retry:
  160. fd = open(mb->path, OREAD);
  161. if(fd < 0){
  162. errstr(err, sizeof(err));
  163. if(strstr(err, "exist") != 0){
  164. tmp = s_copy(mb->path);
  165. s_append(tmp, ".tmp");
  166. if(sysrename(s_to_c(tmp), mb->path) == 0){
  167. s_free(tmp);
  168. goto retry;
  169. }
  170. s_free(tmp);
  171. }
  172. return err;
  173. }
  174. /*
  175. * a new qid.path means reread the mailbox, while
  176. * a new qid.vers means read any new messages
  177. */
  178. d = dirfstat(fd);
  179. if(d == nil){
  180. close(fd);
  181. errstr(err, sizeof(err));
  182. return err;
  183. }
  184. if(mb->d != nil){
  185. if(d->qid.path == mb->d->qid.path && d->qid.vers == mb->d->qid.vers){
  186. close(fd);
  187. free(d);
  188. return nil;
  189. }
  190. if(d->qid.path == mb->d->qid.path){
  191. while(*l != nil)
  192. l = &(*l)->next;
  193. seek(fd, mb->d->length, 0);
  194. }
  195. free(mb->d);
  196. }
  197. mb->d = d;
  198. mb->vers++;
  199. henter(PATH(0, Qtop), mb->name,
  200. (Qid){PATH(mb->id, Qmbox), mb->vers, QTDIR}, nil, mb);
  201. inb = emalloc(sizeof(Inbuf));
  202. inb->rptr = inb->wptr = inb->data;
  203. inb->fd = fd;
  204. // read new messages
  205. snprint(err, sizeof err, "reading '%s'", mb->path);
  206. logmsg(err, nil);
  207. for(;;){
  208. if(lk != nil)
  209. syslockrefresh(lk);
  210. m = newmessage(mb->root);
  211. m->mallocd = 1;
  212. m->inmbox = 1;
  213. if(readmessage(m, inb) < 0){
  214. delmessage(mb, m);
  215. mb->root->subname--;
  216. break;
  217. }
  218. // merge mailbox versions
  219. while(*l != nil){
  220. if(memcmp((*l)->digest, m->digest, SHA1dlen) == 0){
  221. // matches mail we already read, discard
  222. logmsg("duplicate", *l);
  223. delmessage(mb, m);
  224. mb->root->subname--;
  225. m = nil;
  226. l = &(*l)->next;
  227. break;
  228. } else {
  229. // old mail no longer in box, mark deleted
  230. logmsg("disappeared", *l);
  231. if(doplumb)
  232. mailplumb(mb, *l, 1);
  233. (*l)->inmbox = 0;
  234. (*l)->deleted = 1;
  235. l = &(*l)->next;
  236. }
  237. }
  238. if(m == nil)
  239. continue;
  240. x = strchr(m->start, '\n');
  241. if(x == nil)
  242. m->header = m->end;
  243. else
  244. m->header = x + 1;
  245. m->mheader = m->mhend = m->header;
  246. parseunix(m);
  247. parse(m, 0, mb, 0);
  248. logmsg("new", m);
  249. /* chain in */
  250. *l = m;
  251. l = &m->next;
  252. if(doplumb)
  253. mailplumb(mb, m, 0);
  254. }
  255. logmsg("mbox read", nil);
  256. // whatever is left has been removed from the mbox, mark deleted
  257. while(*l != nil){
  258. if(doplumb)
  259. mailplumb(mb, *l, 1);
  260. (*l)->inmbox = 0;
  261. (*l)->deleted = 1;
  262. l = &(*l)->next;
  263. }
  264. close(fd);
  265. free(inb);
  266. return nil;
  267. }
  268. static void
  269. _writembox(Mailbox *mb, Mlock *lk)
  270. {
  271. Dir *d;
  272. Message *m;
  273. String *tmp;
  274. int mode, errs;
  275. Biobuf *b;
  276. tmp = s_copy(mb->path);
  277. s_append(tmp, ".tmp");
  278. /*
  279. * preserve old files permissions, if possible
  280. */
  281. d = dirstat(mb->path);
  282. if(d != nil){
  283. mode = d->mode&0777;
  284. free(d);
  285. } else
  286. mode = MBOXMODE;
  287. sysremove(s_to_c(tmp));
  288. b = sysopen(s_to_c(tmp), "alc", mode);
  289. if(b == 0){
  290. fprint(2, "can't write temporary mailbox %s: %r\n", s_to_c(tmp));
  291. return;
  292. }
  293. logmsg("writing new mbox", nil);
  294. errs = 0;
  295. for(m = mb->root->part; m != nil; m = m->next){
  296. if(lk != nil)
  297. syslockrefresh(lk);
  298. if(m->deleted)
  299. continue;
  300. logmsg("writing", m);
  301. if(Bwrite(b, m->start, m->end - m->start) < 0)
  302. errs = 1;
  303. if(Bwrite(b, "\n", 1) < 0)
  304. errs = 1;
  305. }
  306. logmsg("wrote new mbox", nil);
  307. if(sysclose(b) < 0)
  308. errs = 1;
  309. if(errs){
  310. fprint(2, "error writing temporary mail file\n");
  311. s_free(tmp);
  312. return;
  313. }
  314. sysremove(mb->path);
  315. if(sysrename(s_to_c(tmp), mb->path) < 0)
  316. fprint(2, "%s: can't rename %s to %s: %r\n", argv0,
  317. s_to_c(tmp), mb->path);
  318. s_free(tmp);
  319. if(mb->d != nil)
  320. free(mb->d);
  321. mb->d = dirstat(mb->path);
  322. }
  323. char*
  324. plan9syncmbox(Mailbox *mb, int doplumb)
  325. {
  326. Mlock *lk;
  327. char *rv;
  328. lk = nil;
  329. if(mb->dolock){
  330. lk = syslock(mb->path);
  331. if(lk == nil)
  332. return "can't lock mailbox";
  333. }
  334. rv = _readmbox(mb, doplumb, lk); /* interpolate */
  335. if(purgedeleted(mb) > 0)
  336. _writembox(mb, lk);
  337. if(lk != nil)
  338. sysunlock(lk);
  339. return rv;
  340. }
  341. //
  342. // look to see if we can open this mail box
  343. //
  344. char*
  345. plan9mbox(Mailbox *mb, char *path)
  346. {
  347. static char err[64];
  348. String *tmp;
  349. if(access(path, AEXIST) < 0){
  350. errstr(err, sizeof(err));
  351. tmp = s_copy(path);
  352. s_append(tmp, ".tmp");
  353. if(access(s_to_c(tmp), AEXIST) < 0){
  354. s_free(tmp);
  355. return err;
  356. }
  357. s_free(tmp);
  358. }
  359. mb->sync = plan9syncmbox;
  360. return nil;
  361. }