isync

mailbox synchronization program
git clone https://git.code.sf.net/p/isync/isync
Log | Files | Refs | README | LICENSE

commit c121ec912f743193a87b4151c0f4f7c6c40fe6f6
parent 2c648da5cfcc1886749eb2a22187c936a72d7d55
Author: Michael Elkins <me@mutt.org>
Date:   Wed, 16 Jan 2002 19:47:28 +0000

updated year in copyright notice

the uid for each message in the maildir is now stored in a dbm database
rather than the filename.  this change was necessary because isync became
confused if you copied a message to another folder, in which case the uid
was invalid.

as a result of the above change, isync now acquires a mutex on the mailbox
to protect the dbm database from concurrent access.

main() was reworked to continue gracefully when an error is encountered, and
to always call maildir_close() so that the lock can be disabled, and the
database closed.

Diffstat:
MChangeLog | 3+++
MNEWS | 8++++++++
MTODO | 2++
Mconfig.c | 2+-
Mconfigure.in | 3++-
Mcram.c | 2+-
Mimap.c | 2+-
Misync.1 | 12++++--------
Misync.h | 13++++++++-----
Mlist.c | 2+-
Mmaildir.c | 208+++++++++++++++++++++++++++++++++++++------------------------------------------
Mmain.c | 155+++++++++++++++++++++++++++++++++++++++++++------------------------------------
Msync.c | 92++++++++++++++++++++++++++++++++++++++++++++++++-------------------------------
13 files changed, 269 insertions(+), 235 deletions(-)

diff --git a/ChangeLog b/ChangeLog @@ -1,5 +1,8 @@ 2001-11-20 me <me@sigpipe.org> + * ChangeLog, Makefile.am, isync.spec: + post 0.7-release commit + * Makefile.am, NEWS, isync.1, isync.h, maildir.c, main.c: added --create/-C command line option to force creation of the local maildir-style mailbox if nonexistent diff --git a/NEWS b/NEWS @@ -1,3 +1,11 @@ +[0.8] + +IMPORTANT: In order to fix the problem where messages copied from one mailbox +to another were not uploaded to the new mailbox, the way Isync stores the UID +for each message needed to be changed. As a result, you MUST delete all the +messages in the local maildir box before using this version. Otherwise it +will upload every message to the server thinking its a new mail. + [0.7] Added `MaxMessages' configuration option to allow tracking of only the most diff --git a/TODO b/TODO @@ -4,3 +4,5 @@ add support for syncing with other: and shared: via NAMESPACE isync gets confused when new mail is delivered while in the middle of an IMAP session. need to handled those asynchronous notifications properly. + +add a way to automatically create and sync IMAP subfolders. diff --git a/config.c b/config.c @@ -1,7 +1,7 @@ /* $Id$ * * isync - IMAP4 to maildir mailbox synchronizer - * Copyright (C) 2000-1 Michael R. Elkins <me@mutt.org> + * Copyright (C) 2000-2 Michael R. Elkins <me@mutt.org> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/configure.in b/configure.in @@ -1,5 +1,5 @@ AC_INIT(isync.h) -AM_INIT_AUTOMAKE(isync,0.7) +AM_INIT_AUTOMAKE(isync,0.8) AM_PROG_CC_STDC AC_ARG_WITH(ssl-dir, [ --with-ssl-dir=DIR location where openssl is insalled], [if test -d $withval/lib; then @@ -19,6 +19,7 @@ AC_CHECK_LIB(socket,socket) AC_CHECK_LIB(nsl,inet_ntoa) AC_CHECK_LIB(crypto,ERR_error_string) AC_CHECK_LIB(ssl,SSL_library_init) +AC_CHECK_LIB(db,db_create) dnl test for gcc. use the prefix so we know that gcc-3.0 is also gcc if test `echo $CC | sed 's/^gcc.*/gcc/'` = gcc; then CFLAGS="$CFLAGS -pipe -W -Wall -Wshadow -Wmissing-prototypes" diff --git a/cram.c b/cram.c @@ -1,7 +1,7 @@ /* $Id$ * * isync - IMAP4 to maildir mailbox synchronizer - * Copyright (C) 2000-1 Michael R. Elkins <me@mutt.org> + * Copyright (C) 2000-2 Michael R. Elkins <me@mutt.org> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/imap.c b/imap.c @@ -1,7 +1,7 @@ /* $Id$ * * isync - IMAP4 to maildir mailbox synchronizer - * Copyright (C) 2000-1 Michael R. Elkins <me@mutt.org> + * Copyright (C) 2000-2 Michael R. Elkins <me@mutt.org> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/isync.1 b/isync.1 @@ -1,6 +1,6 @@ .ig \" isync - IMAP4 to maildir mailbox synchronizer -\" Copyright (C) 2000-1 Michael R. Elkins <me@mutt.org> +\" Copyright (C) 2000-2 Michael R. Elkins <me@mutt.org> \" \" This program is free software; you can redistribute it and/or modify \" it under the terms of the GNU General Public License as published by @@ -16,7 +16,7 @@ \" along with this program; if not, write to the Free Software \" Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA .. -.TH isync 1 "2001 Nov 20" +.TH isync 1 "2002 Jan 16" .. .SH NAME isync - synchronize IMAP4 and maildir mailboxes @@ -304,13 +304,9 @@ will then use the global value by default. Default configuration file .. .SH BUGS -maildir(5) states that readers should not attempt to parse the filename of a -a message other than the :info field. However, since .B isync -relies on using the message UIDs that info must be inserted into the -filename in a way which will be interoperable with existing readers. So -the UID is placed in the filename of the messages in the local maildir -mailbox rather than the :info field. +does not use NFS-safe locking. It will correctly prevent concurrent +synchronization of a mailbox on the same host, but not across NFS. .P When synchronizing multiple mailboxes on the same IMAP server, it is not possible to select different SSL options for each mailbox. Only the options diff --git a/isync.h b/isync.h @@ -1,7 +1,7 @@ /* $Id$ * * isync - IMAP4 to maildir mailbox synchronizer - * Copyright (C) 2000 Michael R. Elkins <me@mutt.org> + * Copyright (C) 2000-2 Michael R. Elkins <me@mutt.org> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -18,11 +18,14 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#define DB_DBM_HSEARCH 1 + #include <sys/types.h> #include <stdarg.h> #if HAVE_LIBSSL #include <openssl/ssl.h> #endif +#include <db.h> #include "debug.h" typedef struct @@ -79,13 +82,13 @@ struct config /* struct representing local mailbox file */ struct mailbox { + DBM *db; char *path; message_t *msgs; + int lockfd; unsigned int deleted; /* # of deleted messages */ unsigned int uidvalidity; unsigned int maxuid; /* largest uid we know about */ - unsigned int changed:1; - unsigned int maxuidchanged:1; }; /* message dispositions */ @@ -106,7 +109,6 @@ struct message message_t *next; unsigned int processed:1; /* message has already been evaluated */ unsigned int new:1; /* message is in the new/ subdir */ - unsigned int changed:1; /* flags changed */ unsigned int dead:1; /* message doesn't exist on the server */ unsigned int wanted:1; /* when using MaxMessages, keep this message */ }; @@ -191,7 +193,8 @@ int imap_append_message (imap_t *, int, message_t *); mailbox_t *maildir_open (const char *, int flags); int maildir_expunge (mailbox_t *, int); int maildir_set_uidvalidity (mailbox_t *, unsigned int uidvalidity); -int maildir_close (mailbox_t *); +void maildir_close (mailbox_t *); +int maildir_update_maxuid (mailbox_t * mbox); message_t * find_msg (message_t * list, unsigned int uid); void free_message (message_t *); diff --git a/list.c b/list.c @@ -1,7 +1,7 @@ /* $Id$ * * isync - IMAP4 to maildir mailbox synchronizer - * Copyright (C) 2000-1 Michael R. Elkins <me@mutt.org> + * Copyright (C) 2000-2 Michael R. Elkins <me@mutt.org> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/maildir.c b/maildir.c @@ -1,7 +1,7 @@ /* $Id$ * * isync - IMAP4 to maildir mailbox synchronizer - * Copyright (C) 2000-1 Michael R. Elkins <me@mutt.org> + * Copyright (C) 2000-2 Michael R. Elkins <me@mutt.org> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -85,7 +85,7 @@ read_uid (const char *path, const char *file) { char full[_POSIX_PATH_MAX]; int fd; - int ret; + int ret = 0; int len; char buf[64]; unsigned int uid = 0; @@ -96,29 +96,59 @@ read_uid (const char *path, const char *file) { if (errno != ENOENT) { - perror ("open"); + perror (full); return -1; } return 0; /* doesn't exist */ } - ret = do_lock (fd, F_RDLCK); - if (!ret) + len = read (fd, buf, sizeof (buf) - 1); + if (len == -1) { - len = read (fd, buf, sizeof (buf) - 1); - if (len == -1) - ret = -1; - else - { - buf[len] = 0; - uid = atol (buf); - } + perror ("read"); + ret = -1; + } + else + { + buf[len] = 0; + uid = atol (buf); } - ret |= do_lock (fd, F_UNLCK); close (fd); return ret ? (unsigned int) ret : uid; } +/* NOTE: this is NOT NFS safe */ +static int +maildir_lock (mailbox_t * m) +{ + char path[_POSIX_PATH_MAX]; + + snprintf (path, sizeof (path), "%s/isynclock", m->path); + m->lockfd = open (path, O_WRONLY | O_CREAT | O_EXCL, S_IWUSR | S_IRUSR); + if (m->lockfd == -1) + { + perror (path); + return -1; + } + if (do_lock (m->lockfd, F_WRLCK)) + { + close (m->lockfd); + return -1; + } + return 0; +} + +static void +maildir_unlock (mailbox_t * m) +{ + char path[_POSIX_PATH_MAX]; + + snprintf (path, sizeof (path), "%s/isynclock", m->path); + unlink (path); + do_lock (m->lockfd, F_UNLCK); + close (m->lockfd); +} + /* open a maildir mailbox. * if OPEN_FAST is set, we just check to make * sure its a valid mailbox and don't actually parse it. any IMAP messages @@ -141,8 +171,10 @@ maildir_open (const char *path, int flags) struct stat sb; const char *subdirs[] = { "cur", "new", "tmp" }; int i; + datum key; m = calloc (1, sizeof (mailbox_t)); + m->lockfd = -1; /* filename expansion happens here, not in the config parser */ m->path = expand_strdup (path); @@ -154,9 +186,7 @@ maildir_open (const char *path, int flags) { fprintf (stderr, "ERROR: mkdir %s: %s (errno %d)\n", m->path, strerror (errno), errno); - free (m->path); - free (m); - return NULL; + goto err; } for (i = 0; i < 3; i++) @@ -166,9 +196,7 @@ maildir_open (const char *path, int flags) { fprintf (stderr, "ERROR: mkdir %s: %s (errno %d)\n", buf, strerror (errno), errno); - free (m->path); - free (m); - return NULL; + goto err; } } @@ -177,9 +205,7 @@ maildir_open (const char *path, int flags) { fprintf (stderr, "ERROR: stat %s: %s (errno %d)\n", m->path, strerror (errno), errno); - free (m->path); - free (m); - return NULL; + goto err; } } else @@ -195,33 +221,37 @@ maildir_open (const char *path, int flags) fprintf (stderr, "ERROR: %s does not appear to be a valid maildir style mailbox\n", m->path); - free (m->path); - free (m); - return 0; + goto err; } } } + /* we need a mutex on the maildir because of the state files that isync + * uses. + */ + if (maildir_lock (m)) + goto err; + /* check for the uidvalidity value */ m->uidvalidity = read_uid (m->path, "isyncuidvalidity"); if (m->uidvalidity == (unsigned int) -1) - { - free (m->path); - free (m); - return NULL; - } + goto err; /* load the current maxuid */ if ((m->maxuid = read_uid (m->path, "isyncmaxuid")) == (unsigned int) -1) - { - free (m->path); - free (m); - return NULL; - } + goto err; if (flags & OPEN_FAST) return m; + snprintf (buf, sizeof (buf), "%s/isyncuidmap", m->path); + m->db = dbm_open (buf, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR); + if (m->db == NULL) + { + fputs ("ERROR: unable to open UID db\n", stderr); + goto err; + } + cur = &m->msgs; for (; count < 2; count++) { @@ -231,10 +261,8 @@ maildir_open (const char *path, int flags) d = opendir (buf); if (!d) { - free (m->path); - free (m); perror ("opendir"); - return 0; + goto err; } while ((e = readdir (d))) { @@ -247,40 +275,25 @@ maildir_open (const char *path, int flags) p->flags = 0; p->new = (count == 0); - /* filename format is something like: - * <unique-prefix>,U=<n>:2,<flags> - * This is completely non-standard, but in order for mail - * clients to understand the flags, we have to use the - * standard :info as described by the qmail spec + /* determine the UID for this message. The basename (sans + * flags) is used as the key in the db */ - s = strstr (p->file, ",U="); - if (!s) - s = strstr (p->file, "UID"); - if (!s) - puts ("Warning, no UID for message"); - else + strfcpy (buf, p->file, sizeof (buf)); + s = strchr (p->file, ':'); + if (s) + *s = 0; + key.dptr = buf; + key.dsize = strlen (buf); + key = dbm_fetch (m->db, key); + if (key.dptr) { - p->uid = strtol (s + 3, &s, 10); + p->uid = *(int *) key.dptr; if (p->uid > m->maxuid) - { m->maxuid = p->uid; - m->maxuidchanged = 1; - } - /* Courier-IMAP names it files - * unique,S=<size>:info - * so we need to put the UID before the size, hence here - * we check for a comma as a valid terminator as well, - * since the format will be - * unique,U=<uid>,S=<size>:info - */ - if (*s && *s != ':' && *s != ',') - { - puts ("Warning, unable to parse UID"); - p->uid = -1; /* reset */ - } } + else + puts ("Warning, no UID for message"); - s = strchr (p->file, ':'); if (s) parse_info (p, s + 1); if (p->flags & D_DELETED) @@ -290,6 +303,15 @@ maildir_open (const char *path, int flags) closedir (d); } return m; + + err: + if (m->db) + dbm_close (m->db); + if (m->lockfd != -1) + maildir_unlock (m); + free (m->path); + free (m); + return NULL; } /* permanently remove messages from a maildir mailbox. if `dead' is nonzero, @@ -322,8 +344,8 @@ maildir_expunge (mailbox_t * mbox, int dead) return 0; } -static int -update_maxuid (mailbox_t * mbox) +int +maildir_update_maxuid (mailbox_t * mbox) { int fd; char buf[64]; @@ -355,7 +377,7 @@ update_maxuid (mailbox_t * mbox) uid = atol (buf); if (uid > mbox->maxuid) { - puts ("Error, maxuid is now higher (fatal)"); + fputs ("ERROR: maxuid is now higher (fatal)\n", stderr); ret = -1; } @@ -429,48 +451,14 @@ maildir_clean_tmp (const char *mbox) } } -int +void maildir_close (mailbox_t * mbox) { - message_t *cur = mbox->msgs; - char path[_POSIX_PATH_MAX]; - char oldpath[_POSIX_PATH_MAX]; - char *p; - int ret = 0; - - if (mbox->changed) - { - for (; cur; cur = cur->next) - { - if (cur->changed) - { - /* generate old path */ - snprintf (oldpath, sizeof (oldpath), "%s/%s/%s", - mbox->path, cur->new ? "new" : "cur", cur->file); - - /* truncate old flags (if present) */ - p = strchr (cur->file, ':'); - if (p) - *p = 0; - - /* generate new path - always put this in the cur/ directory - * because its no longer new - */ - snprintf (path, sizeof (path), "%s/cur/%s:2,%s%s%s%s", - mbox->path, - cur->file, (cur->flags & D_FLAGGED) ? "F" : "", - (cur->flags & D_ANSWERED) ? "R" : "", - (cur->flags & D_SEEN) ? "S" : "", - (cur->flags & D_DELETED) ? "T" : ""); - - if (rename (oldpath, path)) - perror ("rename"); - } - } - } + if (mbox->db) + dbm_close (mbox->db); - if (mbox->maxuidchanged) - ret = update_maxuid (mbox); + /* release the mutex on the mailbox */ + maildir_unlock (mbox); /* per the maildir(5) specification, delivery agents are supposed to * set a 24-hour timer on items placed in the `tmp' directory. @@ -481,8 +469,6 @@ maildir_close (mailbox_t * mbox) free_message (mbox->msgs); memset (mbox, 0xff, sizeof (mailbox_t)); free (mbox); - - return ret; } int diff --git a/main.c b/main.c @@ -1,7 +1,7 @@ /* $Id$ * * isync - IMAP4 to maildir mailbox synchronizer - * Copyright (C) 2000-1 Michael R. Elkins <me@mutt.org> + * Copyright (C) 2000-2 Michael R. Elkins <me@mutt.org> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -67,7 +67,7 @@ static void usage (void) { printf ("%s %s IMAP4 to maildir synchronizer\n", PACKAGE, VERSION); - puts ("Copyright (C) 2000-1 Michael R. Elkins <me@mutt.org>"); + puts ("Copyright (C) 2000-2 Michael R. Elkins <me@mutt.org>"); printf ("usage: %s [ flags ] mailbox [mailbox ...]\n", PACKAGE); puts (" -a, --all Synchronize all defined mailboxes"); puts (" -c, --config CONFIG read an alternate config file (default: ~/.isyncrc)"); @@ -134,7 +134,7 @@ main (int argc, char **argv) { int i; config_t *box = 0; - mailbox_t *mail; + mailbox_t *mail = 0; imap_t *imap = 0; int expunge = 0; /* by default, don't delete anything */ int fast = 0; @@ -250,6 +250,9 @@ main (int argc, char **argv) if (!global.host) { fprintf (stderr, "%s: no such mailbox\n", argv[optind]); + /* continue is ok here because we are not handling the + * `all' case. + */ continue; } global.path = argv[optind]; @@ -257,87 +260,99 @@ main (int argc, char **argv) } } - if (!box->pass) - { - /* if we don't have a global password set, prompt the user for - * it now. - */ - if (!global.pass) + do { + if (!box->pass) { - global.pass = getpass ("Password:"); + /* if we don't have a global password set, prompt the user for + * it now. + */ if (!global.pass) { - puts ("Aborting, no password"); - exit (1); + global.pass = getpass ("Password:"); + if (!global.pass) + { + fprintf (stderr, "Skipping %s, no password", box->path); + break; + } } + box->pass = strdup (global.pass); } - box->pass = strdup (global.pass); - } - if (!quiet) - printf ("Reading %s\n", box->path); - i = 0; - if (fast) - i |= OPEN_FAST; - if (create) - i |= OPEN_CREATE; - mail = maildir_open (box->path, i); - if (!mail) - { - fprintf (stderr, "ERROR: unable to load mailbox %s\n", box->path); - goto cleanup; - } + if (!quiet) + printf ("Reading %s\n", box->path); + i = 0; + if (fast) + i |= OPEN_FAST; + if (create) + i |= OPEN_CREATE; + mail = maildir_open (box->path, i); + if (!mail) + { + fprintf (stderr, "%s: unable to open mailbox\n", box->path); + break; + } - imap = imap_open (box, fast ? mail->maxuid + 1 : 1, imap); - if (!imap) - { - fprintf (stderr, "%s: skipping mailbox due to IMAP error\n", - box->path); - goto cleanup; - } + imap = imap_open (box, fast ? mail->maxuid + 1 : 1, imap); + if (!imap) + { + fprintf (stderr, "%s: skipping mailbox due to IMAP error\n", + box->path); + break; + } - if (!quiet) - puts ("Synchronizing"); - i = 0; - if (quiet) - i |= SYNC_QUIET; - i |= (delete || box->delete) ? SYNC_DELETE : 0; - i |= (expunge || box->expunge) ? SYNC_EXPUNGE : 0; - if (sync_mailbox (mail, imap, i, box->max_size, box->max_messages)) - exit (1); + if (!quiet) + puts ("Synchronizing"); + i = 0; + if (quiet) + i |= SYNC_QUIET; + i |= (delete || box->delete) ? SYNC_DELETE : 0; + i |= (expunge || box->expunge) ? SYNC_EXPUNGE : 0; + if (sync_mailbox (mail, imap, i, box->max_size, box->max_messages)) + { + imap_close (imap); /* Just to be safe. Don't really know + * what the problem was. + */ + break; + } - if (!fast) - { - if ((expunge || box->expunge) && (imap->deleted || mail->deleted)) + if (!fast) { - /* remove messages marked for deletion */ - if (!quiet) - printf ("Expunging %d messages from server\n", - imap->deleted); - if (imap_expunge (imap)) - exit (1); - if (!quiet) - printf ("Expunging %d messages from local mailbox\n", - mail->deleted); - if (maildir_expunge (mail, 0)) - exit (1); + if ((expunge || box->expunge) && + (imap->deleted || mail->deleted)) + { + /* remove messages marked for deletion */ + if (!quiet) + printf ("Expunging %d messages from server\n", + imap->deleted); + if (imap_expunge (imap)) + { + imap_close (imap); + imap = NULL; + break; + } + if (!quiet) + printf ("Expunging %d messages from local mailbox\n", + mail->deleted); + if (maildir_expunge (mail, 0)) + break; + } + /* remove messages deleted from server. this can safely be an + * `else' clause since dead messages are marked as deleted by + * sync_mailbox. + */ + else if (delete) + maildir_expunge (mail, 1); } - /* remove messages deleted from server. this can safely be an - * `else' clause since dead messages are marked as deleted by - * sync_mailbox. - */ - else if (delete) - maildir_expunge (mail, 1); - } - /* write changed flags back to the mailbox */ - if (!quiet) - printf ("Committing changes to %s\n", mail->path); + } while (0); - if (maildir_close (mail)) - exit (1); + /* we never sync the same mailbox twice, so close it now */ + if (mail) + maildir_close (mail); - cleanup: + /* the imap connection is not closed so we can keep the connection + * open, and there is no IMAP command for un-SELECT-ing a mailbox. + */ if (all) box = box->next; } diff --git a/sync.c b/sync.c @@ -1,7 +1,7 @@ /* $Id$ * * isync - IMAP4 to maildir mailbox synchronizer - * Copyright (C) 2000-1 Michael R. Elkins <me@mutt.org> + * Copyright (C) 2000-2 Michael R. Elkins <me@mutt.org> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -40,6 +40,24 @@ find_msg (message_t * list, unsigned int uid) return 0; } +static int set_uid (DBM *db, const char *f, unsigned int uid) +{ + char path[_POSIX_PATH_MAX]; + char *s; + datum key, val; + + strfcpy (path, f, sizeof (path)); + s = strchr (path, ':'); + if (s) + *s = 0; + key.dptr = path; + key.dsize = strlen (path); + val.dptr = (void*) &uid; + val.dsize = sizeof (uid); + dbm_store (db, key, val, DBM_REPLACE); + return 0; +} + int sync_mailbox (mailbox_t * mbox, imap_t * imap, int flags, unsigned int max_size, unsigned int max_msgs) @@ -76,7 +94,8 @@ sync_mailbox (mailbox_t * mbox, imap_t * imap, int flags, if (mbox->maxuid == 0 || imap->maxuid > mbox->maxuid) { mbox->maxuid = imap->maxuid; - mbox->maxuidchanged = 1; + if (maildir_update_maxuid (mbox)) + return -1; } /* if we are --fast mode, the mailbox wont have been loaded, so @@ -93,7 +112,6 @@ sync_mailbox (mailbox_t * mbox, imap_t * imap, int flags, if (cur->uid == (unsigned int) -1) { struct stat sb; - int uid; if ((flags & SYNC_QUIET) == 0) { @@ -132,35 +150,12 @@ sync_mailbox (mailbox_t * mbox, imap_t * imap, int flags, } cur->size = sb.st_size; - - uid = imap_append_message (imap, fd, cur); + cur->uid = imap_append_message (imap, fd, cur); + /* if the server gave us back a uid, update the db */ + if (cur->uid != (unsigned int) -1) + set_uid (mbox->db, cur->file, cur->uid); close (fd); - - /* if the server gave us back a uid, rename the file so - * we remember for next time - */ - if (uid != -1) - { - strfcpy (newpath, path, sizeof (newpath)); - /* kill :info field */ - p = strchr (newpath, ':'); - if (p) - *p = 0; - - /* XXX not quite right, should really always put the - * msg in "cur/", but i'm too tired right now. - */ - snprintf (newpath + strlen (newpath), - sizeof (newpath) - strlen (newpath), - ",U=%d:2,%s%s%s%s", uid, - (cur->flags & D_FLAGGED) ? "F" : "", - (cur->flags & D_ANSWERED) ? "R" : "", - (cur->flags & D_SEEN) ? "S" : "", - (cur->flags & D_DELETED) ? "T" : ""); - if (rename (path, newpath)) - perror ("rename"); - } } else if (flags & SYNC_DELETE) { @@ -187,7 +182,7 @@ sync_mailbox (mailbox_t * mbox, imap_t * imap, int flags, if (imap_copy_message (imap, cur->uid, imap->box->copy_deleted_to)) { - printf ("Error, unable to copy deleted message to \"%s\"\n", + fprintf (stderr, "ERROR: unable to copy deleted message to \"%s\"\n", imap->box->copy_deleted_to); return -1; } @@ -208,8 +203,28 @@ sync_mailbox (mailbox_t * mbox, imap_t * imap, int flags, if ((cur->flags & D_DELETED) == 0 && (tmp->flags & D_DELETED)) mbox->deleted++; cur->flags |= (tmp->flags & ~(D_RECENT | D_DRAFT)); - cur->changed = 1; - mbox->changed = 1; + + /* generate old path */ + snprintf (path, sizeof (path), "%s/%s/%s", + mbox->path, cur->new ? "new" : "cur", cur->file); + + /* truncate old flags (if present) */ + p = strchr (cur->file, ':'); + if (p) + *p = 0; + + /* generate new path - always put this in the cur/ directory + * because its no longer new + */ + snprintf (newpath, sizeof (newpath), "%s/cur/%s:2,%s%s%s%s", + mbox->path, + cur->file, (cur->flags & D_FLAGGED) ? "F" : "", + (cur->flags & D_ANSWERED) ? "R" : "", + (cur->flags & D_SEEN) ? "S" : "", + (cur->flags & D_DELETED) ? "T" : ""); + + if (rename (path, newpath)) + perror ("rename"); } } @@ -289,15 +304,15 @@ sync_mailbox (mailbox_t * mbox, imap_t * imap, int flags, for (;;) { /* create new file */ - snprintf (path, sizeof (path), "%s/tmp/%ld_%d.%d.%s,U=%d%s", + snprintf (path, sizeof (path), "%s/tmp/%ld_%d.%d.%s%s", mbox->path, time (0), MaildirCount++, getpid (), - Hostname, cur->uid, suffix); + Hostname, suffix); if ((fd = open (path, O_WRONLY | O_CREAT | O_EXCL, 0600)) > 0) break; if (errno != EEXIST) { - perror ("open"); + perror (path); break; } @@ -336,6 +351,11 @@ sync_mailbox (mailbox_t * mbox, imap_t * imap, int flags, */ if (link (path, newpath)) perror ("link"); + else + { + /* update the db with the UID mapping for this file */ + set_uid (mbox->db, newpath, cur->uid); + } } /* always remove the temp file */