sync.c 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364
  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 <stdio.h>
  21. #include <limits.h>
  22. #include <stdlib.h>
  23. #include <unistd.h>
  24. #include <time.h>
  25. #include <fcntl.h>
  26. #include <string.h>
  27. #include <errno.h>
  28. #include <sys/stat.h>
  29. #include "isync.h"
  30. static unsigned int MaildirCount = 0;
  31. message_t *
  32. find_msg (message_t * list, unsigned int uid)
  33. {
  34. for (; list; list = list->next)
  35. if (list->uid == uid)
  36. return list;
  37. return 0;
  38. }
  39. static int set_uid (DBM *db, const char *f, unsigned int uid)
  40. {
  41. char *s;
  42. datum key, val;
  43. key.dptr = (void *) f;
  44. s = strchr (f, ':');
  45. key.dsize = s ? (size_t) (s - key.dptr) : strlen (f);
  46. val.dptr = (void*) &uid;
  47. val.dsize = sizeof (uid);
  48. dbm_store (db, key, val, DBM_REPLACE);
  49. return 0;
  50. }
  51. int
  52. sync_mailbox (mailbox_t * mbox, imap_t * imap, int flags,
  53. unsigned int max_size, unsigned int max_msgs)
  54. {
  55. message_t *cur;
  56. message_t *tmp;
  57. char path[_POSIX_PATH_MAX];
  58. char newpath[_POSIX_PATH_MAX];
  59. char suffix[_POSIX_PATH_MAX];
  60. char *p;
  61. int fd;
  62. int ret;
  63. int fetched = 0;
  64. int upload = 0;
  65. unsigned int msg_count;
  66. if (mbox->uidvalidity > 0)
  67. {
  68. if (mbox->uidvalidity != imap->uidvalidity)
  69. {
  70. /* if the UIDVALIDITY value has changed, it means all our
  71. * local UIDs are invalid, so we can't sync.
  72. */
  73. fputs ("ERROR: UIDVALIDITY changed on server (fatal)\n", stderr);
  74. return -1;
  75. }
  76. }
  77. else if (maildir_set_uidvalidity (mbox, imap->uidvalidity))
  78. {
  79. fputs ("ERROR: unable to store UIDVALIDITY\n", stderr);
  80. return -1;
  81. }
  82. if (mbox->maxuid == 0 || imap->maxuid > mbox->maxuid)
  83. {
  84. mbox->maxuid = imap->maxuid;
  85. if (maildir_update_maxuid (mbox))
  86. return -1;
  87. }
  88. /* if we are --fast mode, the mailbox wont have been loaded, so
  89. * this next step is skipped.
  90. */
  91. for (cur = mbox->msgs; cur; cur = cur->next)
  92. {
  93. tmp = find_msg (imap->msgs, cur->uid);
  94. if (!tmp)
  95. {
  96. /* if this message wasn't fetched from the server, attempt to
  97. * upload it
  98. */
  99. if (cur->uid == (unsigned int) -1)
  100. {
  101. struct stat sb;
  102. if ((flags & SYNC_QUIET) == 0)
  103. {
  104. if (!upload)
  105. fputs ("Uploading messages", stdout);
  106. fputc ('.', stdout);
  107. fflush (stdout);
  108. upload++;
  109. }
  110. /* upload the message if its not too big */
  111. snprintf (path, sizeof (path), "%s/%s/%s", mbox->path,
  112. cur->new ? "new" : "cur", cur->file);
  113. if (stat (path, &sb))
  114. {
  115. perror (path);
  116. continue; /* not fatal */
  117. }
  118. if (imap->box->max_size > 0
  119. && sb.st_size > imap->box->max_size)
  120. {
  121. if ((flags & SYNC_QUIET) == 0)
  122. printf
  123. ("Warning, local message is too large (%lu), skipping...\n",
  124. (unsigned long) sb.st_size);
  125. continue;
  126. }
  127. fd = open (path, O_RDONLY);
  128. if (fd == -1)
  129. {
  130. printf ("Error, unable to open %s: %s (errno %d)\n",
  131. path, strerror (errno), errno);
  132. continue;
  133. }
  134. cur->size = sb.st_size;
  135. cur->uid = imap_append_message (imap, fd, cur);
  136. /* if the server gave us back a uid, update the db */
  137. if (cur->uid != (unsigned int) -1)
  138. set_uid (mbox->db, cur->file, cur->uid);
  139. close (fd);
  140. }
  141. else if (flags & SYNC_DELETE)
  142. {
  143. cur->flags |= D_DELETED;
  144. cur->dead = 1;
  145. mbox->deleted++;
  146. }
  147. /* if the user doesn't want local msgs deleted when they don't
  148. * exist on the server, warn that such messages exist.
  149. */
  150. else if ((flags & SYNC_QUIET) == 0)
  151. printf ("Warning, uid %u doesn't exist on server\n",
  152. cur->uid);
  153. continue;
  154. }
  155. tmp->processed = 1;
  156. /* if the message is deleted, and CopyDeletedTo is set, and we
  157. * are expunging, make a copy of the message now.
  158. */
  159. if (((cur->flags | tmp->flags) & D_DELETED) != 0 &&
  160. (flags & SYNC_EXPUNGE) && imap->box->copy_deleted_to)
  161. {
  162. if (imap_copy_message (imap, cur->uid,
  163. imap->box->copy_deleted_to))
  164. {
  165. fprintf (stderr, "ERROR: unable to copy deleted message to \"%s\"\n",
  166. imap->box->copy_deleted_to);
  167. return -1;
  168. }
  169. }
  170. /* check if local flags are different from server flags.
  171. * ignore \Recent and \Draft
  172. */
  173. if (cur->flags != (tmp->flags & ~(D_RECENT | D_DRAFT)))
  174. {
  175. /* set local flags that don't exist on the server */
  176. if (!(tmp->flags & D_DELETED) && (cur->flags & D_DELETED))
  177. imap->deleted++;
  178. imap_set_flags (imap, cur->uid, cur->flags & ~tmp->flags);
  179. /* update local flags */
  180. if ((cur->flags & D_DELETED) == 0 && (tmp->flags & D_DELETED))
  181. mbox->deleted++;
  182. cur->flags |= (tmp->flags & ~(D_RECENT | D_DRAFT));
  183. /* generate old path */
  184. snprintf (path, sizeof (path), "%s/%s/%s",
  185. mbox->path, cur->new ? "new" : "cur", cur->file);
  186. /* truncate old flags (if present) */
  187. p = strchr (cur->file, ':');
  188. if (p)
  189. *p = 0;
  190. /* generate new path - always put this in the cur/ directory
  191. * because its no longer new
  192. */
  193. snprintf (newpath, sizeof (newpath), "%s/cur/%s:2,%s%s%s%s",
  194. mbox->path,
  195. cur->file, (cur->flags & D_FLAGGED) ? "F" : "",
  196. (cur->flags & D_ANSWERED) ? "R" : "",
  197. (cur->flags & D_SEEN) ? "S" : "",
  198. (cur->flags & D_DELETED) ? "T" : "");
  199. if (rename (path, newpath))
  200. perror ("rename");
  201. }
  202. }
  203. if (upload)
  204. fprintf (stdout, " %d messages.\n", upload);
  205. if ((flags & SYNC_QUIET) == 0)
  206. {
  207. fputs ("Fetching new messages", stdout);
  208. fflush (stdout);
  209. }
  210. if (max_msgs == 0)
  211. max_msgs = UINT_MAX;
  212. else
  213. {
  214. /* expire messages in excess of the max-count for this mailbox.
  215. * flagged mails are considered sacrosant and not deleted.
  216. * we have already done the upload to the server, so messing with
  217. * the flags variable do not have remote side effects.
  218. */
  219. for (cur = imap->msgs, msg_count = 0;
  220. cur && msg_count < max_msgs; cur = cur->next, msg_count++)
  221. {
  222. tmp = find_msg (mbox->msgs, cur->uid);
  223. if (tmp)
  224. tmp->wanted = 1;
  225. }
  226. for (cur = mbox->msgs; cur; cur = cur->next)
  227. {
  228. if (!cur->wanted && !(cur->flags & D_FLAGGED))
  229. {
  230. cur->flags |= D_DELETED;
  231. cur->dead = 1;
  232. mbox->deleted++;
  233. }
  234. }
  235. }
  236. for (cur = imap->msgs, msg_count = 0;
  237. cur && msg_count < max_msgs; cur = cur->next, msg_count++)
  238. {
  239. if (!cur->processed)
  240. {
  241. /* new message on server */
  242. if ((flags & SYNC_EXPUNGE) && (cur->flags & D_DELETED))
  243. {
  244. /* this message has been marked for deletion and
  245. * we are currently expunging a mailbox. don't
  246. * bother downloading this message
  247. */
  248. continue;
  249. }
  250. if (max_size && cur->size > max_size)
  251. {
  252. if ((flags & SYNC_QUIET) == 0)
  253. printf
  254. ("Warning, message skipped because it is too big (%u)\n",
  255. cur->size);
  256. continue;
  257. }
  258. /* construct the flags part of the file name. */
  259. *suffix = 0;
  260. if (cur->flags & ~D_RECENT)
  261. {
  262. snprintf (suffix, sizeof (suffix), ":2,%s%s%s%s",
  263. (cur->flags & D_FLAGGED) ? "F" : "",
  264. (cur->flags & D_ANSWERED) ? "R" : "",
  265. (cur->flags & D_SEEN) ? "S" : "",
  266. (cur->flags & D_DELETED) ? "T" : "");
  267. }
  268. for (;;)
  269. {
  270. /* create new file */
  271. snprintf (path, sizeof (path), "%s/tmp/%ld_%d.%d.%s%s",
  272. mbox->path, time (0), MaildirCount++, getpid (),
  273. Hostname, suffix);
  274. if ((fd = open (path, O_WRONLY | O_CREAT | O_EXCL, 0600)) > 0)
  275. break;
  276. if (errno != EEXIST)
  277. {
  278. perror (path);
  279. break;
  280. }
  281. sleep (2);
  282. }
  283. if (fd < 0)
  284. continue;
  285. if ((flags & SYNC_QUIET) == 0)
  286. {
  287. /* give some visual feedback that something is happening */
  288. fputs (".", stdout);
  289. fflush (stdout);
  290. }
  291. fetched++;
  292. ret = imap_fetch_message (imap, cur->uid, fd);
  293. if (fsync (fd))
  294. {
  295. perror ("fsync");
  296. close (fd);
  297. }
  298. else if (close (fd))
  299. perror ("close");
  300. else if (!ret)
  301. {
  302. p = strrchr (path, '/');
  303. snprintf (newpath, sizeof (newpath), "%s/%s%s", mbox->path,
  304. (cur->flags & ~D_RECENT) ? "cur" : "new", p);
  305. /* its ok if this fails, the next time we sync the message
  306. * will get pulled down
  307. */
  308. if (link (path, newpath))
  309. perror ("link");
  310. else
  311. {
  312. /* update the db with the UID mapping for this file */
  313. set_uid (mbox->db, p + 1, cur->uid);
  314. }
  315. }
  316. /* always remove the temp file */
  317. unlink (path);
  318. }
  319. }
  320. if ((flags & SYNC_QUIET) == 0)
  321. printf (" %d messages\n", fetched);
  322. return 0;
  323. }