maildir.c 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437
  1. /* $Id$
  2. *
  3. * isync - IMAP4 to maildir mailbox synchronizer
  4. * Copyright (C) 2000-2 Michael R. Elkins <me@mutt.org>
  5. *
  6. * This program is free software; you can redistribute it and/or modify
  7. * it under the terms of the GNU General Public License as published by
  8. * the Free Software Foundation; either version 2 of the License, or
  9. * (at your option) any later version.
  10. *
  11. * This program is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU General Public License
  17. * along with this program; if not, write to the Free Software
  18. * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  19. */
  20. #include <limits.h>
  21. #include <stdlib.h>
  22. #include <string.h>
  23. #include <dirent.h>
  24. #include <fcntl.h>
  25. #include <stdio.h>
  26. #include <unistd.h>
  27. #include <sys/stat.h>
  28. #include <errno.h>
  29. #include <time.h>
  30. #include "isync.h"
  31. #include "dotlock.h"
  32. /* 2,<flags> */
  33. static void
  34. parse_info (message_t * m, char *s)
  35. {
  36. if (*s == '2' && *(s + 1) == ',')
  37. {
  38. s += 2;
  39. while (*s)
  40. {
  41. if (*s == 'F')
  42. m->flags |= D_FLAGGED;
  43. else if (*s == 'R')
  44. m->flags |= D_ANSWERED;
  45. else if (*s == 'T')
  46. m->flags |= D_DELETED;
  47. else if (*s == 'S')
  48. m->flags |= D_SEEN;
  49. s++;
  50. }
  51. }
  52. }
  53. /*
  54. * There are three possible results of this function:
  55. * >1 uid was already seen
  56. * 0 uid was not yet seen
  57. * -1 unable to read uid because of some other error
  58. */
  59. static int
  60. read_uid (const char *path, const char *file, unsigned int *uid /* out */)
  61. {
  62. char full[_POSIX_PATH_MAX];
  63. int fd;
  64. int ret = -1;
  65. int len;
  66. char buf[64], *ptr;
  67. snprintf (full, sizeof (full), "%s/%s", path, file);
  68. fd = open (full, O_RDONLY);
  69. if (fd == -1)
  70. {
  71. if (errno != ENOENT)
  72. {
  73. perror (full);
  74. return -1;
  75. }
  76. return 0; /* doesn't exist */
  77. }
  78. len = read (fd, buf, sizeof (buf) - 1);
  79. if (len == -1)
  80. perror ("read");
  81. else
  82. {
  83. buf[len] = 0;
  84. errno = 0;
  85. *uid = strtoul (buf, &ptr, 10);
  86. if (errno)
  87. perror ("strtoul");
  88. else if (ptr && *ptr == '\n')
  89. ret = 1;
  90. /* else invalid value */
  91. }
  92. close (fd);
  93. return ret;
  94. }
  95. /*
  96. * open a maildir mailbox.
  97. * if OPEN_FAST is set, we just check to make
  98. * sure its a valid mailbox and don't actually parse it. any IMAP messages
  99. * with the \Recent flag set are guaranteed not to be in the mailbox yet,
  100. * so we can save a lot of time when the user just wants to fetch new messages
  101. * without syncing the flags.
  102. * if OPEN_CREATE is set, we create the mailbox if it doesn't already exist.
  103. */
  104. mailbox_t *
  105. maildir_open (const char *path, int flags)
  106. {
  107. char buf[_POSIX_PATH_MAX];
  108. DIR *d;
  109. struct dirent *e;
  110. message_t **cur;
  111. message_t *p;
  112. mailbox_t *m;
  113. char *s;
  114. int count = 0;
  115. struct stat sb;
  116. const char *subdirs[] = { "cur", "new", "tmp" };
  117. int i;
  118. datum key;
  119. m = calloc (1, sizeof (mailbox_t));
  120. m->lockfd = -1;
  121. /* filename expansion happens here, not in the config parser */
  122. m->path = expand_strdup (path);
  123. if (stat (m->path, &sb))
  124. {
  125. if (errno == ENOENT && (flags & OPEN_CREATE))
  126. {
  127. if (mkdir (m->path, S_IRUSR | S_IWUSR | S_IXUSR))
  128. {
  129. fprintf (stderr, "ERROR: mkdir %s: %s (errno %d)\n",
  130. m->path, strerror (errno), errno);
  131. goto err;
  132. }
  133. for (i = 0; i < 3; i++)
  134. {
  135. snprintf (buf, sizeof (buf), "%s/%s", m->path, subdirs[i]);
  136. if (mkdir (buf, S_IRUSR | S_IWUSR | S_IXUSR))
  137. {
  138. fprintf (stderr, "ERROR: mkdir %s: %s (errno %d)\n",
  139. buf, strerror (errno), errno);
  140. goto err;
  141. }
  142. }
  143. }
  144. else
  145. {
  146. fprintf (stderr, "ERROR: stat %s: %s (errno %d)\n", m->path,
  147. strerror (errno), errno);
  148. goto err;
  149. }
  150. }
  151. else
  152. {
  153. /* check to make sure this looks like a valid maildir box */
  154. for (i = 0; i < 3; i++)
  155. {
  156. snprintf (buf, sizeof (buf), "%s/%s", m->path, subdirs[i]);
  157. if (stat (buf, &sb))
  158. {
  159. fprintf (stderr, "ERROR: stat %s: %s (errno %d)\n", buf,
  160. strerror (errno), errno);
  161. fprintf (stderr,
  162. "ERROR: %s does not appear to be a valid maildir style mailbox\n",
  163. m->path);
  164. goto err;
  165. }
  166. }
  167. }
  168. /*
  169. * we need a mutex on the maildir because of the state files that isync
  170. * uses.
  171. */
  172. snprintf (buf, sizeof (buf), "%s/isynclock", m->path);
  173. if (dotlock_lock (buf, &m->lockfd))
  174. goto err;
  175. /* check for the uidvalidity value */
  176. i = read_uid (m->path, "isyncuidvalidity", &m->uidvalidity);
  177. if (i == -1)
  178. goto err;
  179. else if (i > 0)
  180. m->uidseen = 1;
  181. /* load the current maxuid */
  182. if (read_uid (m->path, "isyncmaxuid", &m->maxuid) == -1)
  183. goto err;
  184. if (flags & OPEN_FAST)
  185. return m;
  186. snprintf (buf, sizeof (buf), "%s/isyncuidmap", m->path);
  187. m->db = dbm_open (buf, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
  188. if (m->db == NULL)
  189. {
  190. fputs ("ERROR: unable to open UID db\n", stderr);
  191. goto err;
  192. }
  193. cur = &m->msgs;
  194. for (; count < 2; count++)
  195. {
  196. /* read the msgs from the new subdir */
  197. snprintf (buf, sizeof (buf), "%s/%s", m->path,
  198. (count == 0) ? "new" : "cur");
  199. d = opendir (buf);
  200. if (!d)
  201. {
  202. perror ("opendir");
  203. goto err;
  204. }
  205. while ((e = readdir (d)))
  206. {
  207. if (*e->d_name == '.')
  208. continue; /* skip dot-files */
  209. *cur = calloc (1, sizeof (message_t));
  210. p = *cur;
  211. p->file = strdup (e->d_name);
  212. p->uid = -1;
  213. p->flags = 0;
  214. p->new = (count == 0);
  215. /* determine the UID for this message. The basename (sans
  216. * flags) is used as the key in the db
  217. */
  218. key.dptr = p->file;
  219. s = strchr (key.dptr, ':');
  220. key.dsize = s ? (size_t) (s - key.dptr) : strlen (key.dptr);
  221. key = dbm_fetch (m->db, key);
  222. if (key.dptr)
  223. {
  224. p->uid = *(int *) key.dptr;
  225. if (p->uid > m->maxuid)
  226. m->maxuid = p->uid;
  227. }
  228. else
  229. puts ("Warning, no UID for message");
  230. if (s)
  231. parse_info (p, s + 1);
  232. if (p->flags & D_DELETED)
  233. m->deleted++;
  234. cur = &p->next;
  235. }
  236. closedir (d);
  237. }
  238. return m;
  239. err:
  240. if (m->db)
  241. dbm_close (m->db);
  242. dotlock_unlock (&m->lockfd);
  243. free (m->path);
  244. free (m);
  245. return NULL;
  246. }
  247. /* permanently remove messages from a maildir mailbox. if `dead' is nonzero,
  248. * we only remove the messags marked dead.
  249. */
  250. int
  251. maildir_expunge (mailbox_t * mbox, int dead)
  252. {
  253. message_t **cur = &mbox->msgs;
  254. message_t *tmp;
  255. char *s;
  256. datum key;
  257. char path[_POSIX_PATH_MAX];
  258. while (*cur)
  259. {
  260. if ((dead == 0 && (*cur)->flags & D_DELETED) ||
  261. (dead && (*cur)->dead))
  262. {
  263. tmp = *cur;
  264. snprintf (path, sizeof (path), "%s/%s/%s",
  265. mbox->path, tmp->new ? "new" : "cur", tmp->file);
  266. if (unlink (path))
  267. perror (path);
  268. /* remove the message from the UID map */
  269. key.dptr = tmp->file;
  270. s = strchr (key.dptr, ':');
  271. key.dsize = s ? (size_t) (s - key.dptr) : strlen (key.dptr);
  272. dbm_delete (mbox->db, key);
  273. *cur = (*cur)->next;
  274. free (tmp->file);
  275. free (tmp);
  276. }
  277. else
  278. cur = &(*cur)->next;
  279. }
  280. return 0;
  281. }
  282. int
  283. maildir_update_maxuid (mailbox_t * mbox)
  284. {
  285. int fd;
  286. char buf[64];
  287. size_t len;
  288. char path[_POSIX_PATH_MAX];
  289. int ret = 0;
  290. snprintf (path, sizeof (path), "%s/isyncmaxuid", mbox->path);
  291. fd = open (path, O_WRONLY | O_CREAT, 0600);
  292. if (fd == -1)
  293. {
  294. perror ("open");
  295. return -1;
  296. }
  297. /* write out the file */
  298. snprintf (buf, sizeof (buf), "%u\n", mbox->maxuid);
  299. len = write (fd, buf, strlen (buf));
  300. if (len == (size_t) - 1)
  301. {
  302. perror ("write");
  303. ret = -1;
  304. }
  305. if (close (fd))
  306. ret = -1;
  307. return ret;
  308. }
  309. #define _24_HOURS (3600 * 24)
  310. static void
  311. maildir_clean_tmp (const char *mbox)
  312. {
  313. char path[_POSIX_PATH_MAX];
  314. DIR *dirp;
  315. struct dirent *entry;
  316. struct stat info;
  317. time_t now;
  318. snprintf (path, sizeof (path), "%s/tmp", mbox);
  319. dirp = opendir (path);
  320. if (dirp == NULL)
  321. {
  322. fprintf (stderr, "maildir_clean_tmp: opendir: %s: %s (errno %d)\n",
  323. path, strerror (errno), errno);
  324. return;
  325. }
  326. /* assuming this scan will take less than a second, we only need to
  327. * check the time once before the following loop.
  328. */
  329. time (&now);
  330. while ((entry = readdir (dirp)))
  331. {
  332. snprintf (path, sizeof (path), "%s/tmp/%s", mbox, entry->d_name);
  333. if (stat (path, &info))
  334. fprintf (stderr, "maildir_clean_tmp: stat: %s: %s (errno %d)\n",
  335. path, strerror (errno), errno);
  336. else if (S_ISREG (info.st_mode) && now - info.st_ctime >= _24_HOURS)
  337. {
  338. /* this should happen infrequently enough that it won't be
  339. * bothersome to the user to display when it occurs.
  340. */
  341. printf ("Warning: removing stale file %s\n", path);
  342. if (unlink (path))
  343. fprintf (stderr,
  344. "maildir_clean_tmp: unlink: %s: %s (errno %d)\n",
  345. path, strerror (errno), errno);
  346. }
  347. }
  348. closedir(dirp);
  349. }
  350. void
  351. maildir_close (mailbox_t * mbox)
  352. {
  353. if (mbox->db)
  354. dbm_close (mbox->db);
  355. /* release the mutex on the mailbox */
  356. dotlock_unlock (&mbox->lockfd);
  357. /* per the maildir(5) specification, delivery agents are supposed to
  358. * set a 24-hour timer on items placed in the `tmp' directory.
  359. */
  360. maildir_clean_tmp (mbox->path);
  361. free (mbox->path);
  362. free_message (mbox->msgs);
  363. memset (mbox, 0xff, sizeof (mailbox_t));
  364. free (mbox);
  365. }
  366. int
  367. maildir_set_uidvalidity (mailbox_t * mbox, unsigned int uidvalidity)
  368. {
  369. char path[_POSIX_PATH_MAX];
  370. char buf[16];
  371. int fd;
  372. int ret;
  373. snprintf (path, sizeof (path), "%s/isyncuidvalidity", mbox->path);
  374. fd = open (path, O_WRONLY | O_CREAT | O_EXCL, 0600);
  375. if (fd == -1)
  376. {
  377. perror ("open");
  378. return -1;
  379. }
  380. snprintf (buf, sizeof (buf), "%u\n", uidvalidity);
  381. ret = write (fd, buf, strlen (buf));
  382. if (ret == -1)
  383. perror ("write");
  384. else if ((size_t) ret != strlen (buf))
  385. ret = -1;
  386. else
  387. ret = 0;
  388. if (close (fd))
  389. {
  390. perror ("close");
  391. ret = -1;
  392. }
  393. if (ret)
  394. if (unlink (path))
  395. perror ("unlink");
  396. return (ret);
  397. }