maildir.c 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262
  1. /* $Id$
  2. *
  3. * isync - IMAP4 to maildir mailbox synchronizer
  4. * Copyright (C) 2000 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 "isync.h"
  28. /* 2,<flags> */
  29. static void
  30. parse_info (message_t * m, char *s)
  31. {
  32. if (*s == '2' && *(s + 1) == ',')
  33. {
  34. s += 2;
  35. while (*s)
  36. {
  37. if (*s == 'F')
  38. m->flags |= D_FLAGGED;
  39. else if (*s == 'R')
  40. m->flags |= D_ANSWERED;
  41. else if (*s == 'T')
  42. m->flags |= D_DELETED;
  43. else if (*s == 'S')
  44. m->flags |= D_SEEN;
  45. s++;
  46. }
  47. }
  48. }
  49. /* open a maildir mailbox. if `fast' is nonzero, we just check to make
  50. * sure its a valid mailbox and don't actually parse it. any IMAP messages
  51. * with the \Recent flag set are guaranteed not to be in the mailbox yet,
  52. * so we can save a lot of time when the user just wants to fetch new messages
  53. * without syncing the flags.
  54. */
  55. mailbox_t *
  56. maildir_open (const char *path, int fast)
  57. {
  58. char buf[_POSIX_PATH_MAX];
  59. DIR *d;
  60. struct dirent *e;
  61. message_t **cur;
  62. message_t *p;
  63. mailbox_t *m;
  64. char *s;
  65. int count = 0;
  66. FILE *fp;
  67. /* check to make sure this looks like a valid maildir box */
  68. snprintf (buf, sizeof (buf), "%s/new", path);
  69. if (access (buf, F_OK))
  70. {
  71. perror ("access");
  72. return 0;
  73. }
  74. snprintf (buf, sizeof (buf), "%s/cur", path);
  75. if (access (buf, F_OK))
  76. {
  77. perror ("access");
  78. return 0;
  79. }
  80. m = calloc (1, sizeof (mailbox_t));
  81. m->path = strdup (path);
  82. m->uidvalidity = -1;
  83. /* check for the uidvalidity value */
  84. snprintf (buf, sizeof (buf), "%s/isyncuidvalidity", path);
  85. if ((fp = fopen (buf, "r")))
  86. {
  87. buf[sizeof (buf) - 1] = 0;
  88. if (fgets (buf, sizeof (buf) - 1, fp))
  89. m->uidvalidity = atol (buf);
  90. fclose (fp);
  91. }
  92. if (fast)
  93. return m;
  94. cur = &m->msgs;
  95. for (; count < 2; count++)
  96. {
  97. /* read the msgs from the new subdir */
  98. snprintf (buf, sizeof (buf), "%s/%s", path,
  99. (count == 0) ? "new" : "cur");
  100. d = opendir (buf);
  101. if (!d)
  102. {
  103. perror ("opendir");
  104. return 0;
  105. }
  106. while ((e = readdir (d)))
  107. {
  108. if (*e->d_name == '.')
  109. continue; /* skip dot-files */
  110. *cur = calloc (1, sizeof (message_t));
  111. p = *cur;
  112. p->file = strdup (e->d_name);
  113. p->uid = -1;
  114. p->flags = (count == 1) ? D_SEEN : 0;
  115. p->new = (count == 0);
  116. /* filename format is something like:
  117. * <unique-prefix>.UID<n>:2,<flags>
  118. * This is completely non-standard, but in order for mail
  119. * clients to understand the flags, we have to use the
  120. * standard :info as described by the qmail spec
  121. */
  122. s = strstr (p->file, "UID");
  123. if (!s)
  124. puts ("warning, no uid for message");
  125. else
  126. {
  127. p->uid = strtol (s + 3, &s, 10);
  128. if (*s && *s != ':')
  129. {
  130. puts ("warning, unable to parse uid");
  131. p->uid = -1; /* reset */
  132. }
  133. }
  134. s = strchr (p->file, ':');
  135. if (s)
  136. parse_info (p, s + 1);
  137. if (p->flags & D_DELETED)
  138. m->deleted++;
  139. cur = &p->next;
  140. }
  141. closedir (d);
  142. }
  143. return m;
  144. }
  145. /* permanently remove messages from a maildir mailbox. if `dead' is nonzero,
  146. * we only remove the messags marked dead.
  147. */
  148. int
  149. maildir_expunge (mailbox_t * mbox, int dead)
  150. {
  151. message_t **cur = &mbox->msgs;
  152. message_t *tmp;
  153. char path[_POSIX_PATH_MAX];
  154. while (*cur)
  155. {
  156. if ((dead == 0 && (*cur)->flags & D_DELETED) ||
  157. (dead && (*cur)->dead))
  158. {
  159. tmp = *cur;
  160. *cur = (*cur)->next;
  161. snprintf (path, sizeof (path), "%s/%s/%s",
  162. mbox->path, tmp->new ? "new" : "cur", tmp->file);
  163. if (unlink (path))
  164. perror ("unlink");
  165. free (tmp->file);
  166. free (tmp);
  167. }
  168. else
  169. cur = &(*cur)->next;
  170. }
  171. return 0;
  172. }
  173. int
  174. maildir_sync (mailbox_t * mbox)
  175. {
  176. message_t *cur = mbox->msgs;
  177. char path[_POSIX_PATH_MAX];
  178. char oldpath[_POSIX_PATH_MAX];
  179. char *p;
  180. if (mbox->changed)
  181. {
  182. for (; cur; cur = cur->next)
  183. {
  184. if (cur->changed)
  185. {
  186. /* generate old path */
  187. snprintf (oldpath, sizeof (oldpath), "%s/%s/%s",
  188. mbox->path, cur->new ? "new" : "cur", cur->file);
  189. /* truncate old flags (if present) */
  190. p = strchr (cur->file, ':');
  191. if (p)
  192. *p = 0;
  193. /* generate new path */
  194. snprintf (path, sizeof (path), "%s/%s/%s:2,%s%s%s%s",
  195. mbox->path, (cur->flags & D_SEEN) ? "cur" : "new",
  196. cur->file, (cur->flags & D_FLAGGED) ? "F" : "",
  197. (cur->flags & D_ANSWERED) ? "R" : "",
  198. (cur->flags & D_SEEN) ? "S" : "",
  199. (cur->flags & D_DELETED) ? "T" : "");
  200. if (rename (oldpath, path))
  201. perror ("rename");
  202. }
  203. }
  204. }
  205. return 0;
  206. }
  207. int
  208. maildir_set_uidvalidity (mailbox_t * mbox, unsigned int uidvalidity)
  209. {
  210. char path[_POSIX_PATH_MAX];
  211. char buf[16];
  212. int fd;
  213. int ret;
  214. snprintf (path, sizeof (path), "%s/isyncuidvalidity", mbox->path);
  215. fd = open (path, O_WRONLY | O_CREAT | O_EXCL, 0600);
  216. if (fd == -1)
  217. {
  218. perror ("open");
  219. return -1;
  220. }
  221. snprintf (buf, sizeof (buf), "%u\n", uidvalidity);
  222. ret = write (fd, buf, strlen (buf));
  223. if (ret == -1)
  224. perror("write");
  225. else if ((size_t) ret != strlen (buf))
  226. ret = -1;
  227. else
  228. ret = 0;
  229. if (close (fd))
  230. {
  231. perror("close");
  232. ret = -1;
  233. }
  234. if (ret)
  235. if (unlink (path))
  236. perror ("unlink");
  237. return (ret);
  238. }