plan9.c 7.3 KB

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