Эх сурвалжийг харах

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.
Michael Elkins 23 жил өмнө
parent
commit
c121ec912f
13 өөрчлөгдсөн 269 нэмэгдсэн , 235 устгасан
  1. 3 0
      ChangeLog
  2. 8 0
      NEWS
  3. 2 0
      TODO
  4. 1 1
      config.c
  5. 2 1
      configure.in
  6. 1 1
      cram.c
  7. 1 1
      imap.c
  8. 4 8
      isync.1
  9. 8 5
      isync.h
  10. 1 1
      list.c
  11. 97 111
      maildir.c
  12. 85 70
      main.c
  13. 56 36
      sync.c

+ 3 - 0
ChangeLog

@@ -1,5 +1,8 @@
 2001-11-20  me  <me@sigpipe.org>
 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:
 	* Makefile.am, NEWS, isync.1, isync.h, maildir.c, main.c:
 	added --create/-C command line option to force creation of the local
 	added --create/-C command line option to force creation of the local
 	maildir-style mailbox if nonexistent
 	maildir-style mailbox if nonexistent

+ 8 - 0
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]
 [0.7]
 
 
 Added `MaxMessages' configuration option to allow tracking of only the most
 Added `MaxMessages' configuration option to allow tracking of only the most

+ 2 - 0
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
 isync gets confused when new mail is delivered while in the middle of an
 IMAP session.  need to handled those asynchronous notifications properly.
 IMAP session.  need to handled those asynchronous notifications properly.
+
+add a way to automatically create and sync IMAP subfolders.

+ 1 - 1
config.c

@@ -1,7 +1,7 @@
 /* $Id$
 /* $Id$
  *
  *
  * isync - IMAP4 to maildir mailbox synchronizer
  * 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
  *  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
  *  it under the terms of the GNU General Public License as published by

+ 2 - 1
configure.in

@@ -1,5 +1,5 @@
 AC_INIT(isync.h)
 AC_INIT(isync.h)
-AM_INIT_AUTOMAKE(isync,0.7)
+AM_INIT_AUTOMAKE(isync,0.8)
 AM_PROG_CC_STDC
 AM_PROG_CC_STDC
 AC_ARG_WITH(ssl-dir, [  --with-ssl-dir=DIR	location where openssl is insalled],
 AC_ARG_WITH(ssl-dir, [  --with-ssl-dir=DIR	location where openssl is insalled],
 	[if test -d $withval/lib; then
 	[if test -d $withval/lib; then
@@ -19,6 +19,7 @@ AC_CHECK_LIB(socket,socket)
 AC_CHECK_LIB(nsl,inet_ntoa)
 AC_CHECK_LIB(nsl,inet_ntoa)
 AC_CHECK_LIB(crypto,ERR_error_string)
 AC_CHECK_LIB(crypto,ERR_error_string)
 AC_CHECK_LIB(ssl,SSL_library_init)
 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
 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
 if test `echo $CC | sed 's/^gcc.*/gcc/'` = gcc; then
 	CFLAGS="$CFLAGS -pipe -W -Wall -Wshadow -Wmissing-prototypes"
 	CFLAGS="$CFLAGS -pipe -W -Wall -Wshadow -Wmissing-prototypes"

+ 1 - 1
cram.c

@@ -1,7 +1,7 @@
 /* $Id$
 /* $Id$
  *
  *
  * isync - IMAP4 to maildir mailbox synchronizer
  * 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
  *  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
  *  it under the terms of the GNU General Public License as published by

+ 1 - 1
imap.c

@@ -1,7 +1,7 @@
 /* $Id$
 /* $Id$
  *
  *
  * isync - IMAP4 to maildir mailbox synchronizer
  * 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
  *  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
  *  it under the terms of the GNU General Public License as published by

+ 4 - 8
isync.1

@@ -1,6 +1,6 @@
 .ig
 .ig
 \" isync - IMAP4 to maildir mailbox synchronizer
 \" 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
 \"  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
 \"  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
 \"  along with this program; if not, write to the Free Software
 \"  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 \"  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
 .SH NAME
 isync - synchronize IMAP4 and maildir mailboxes
 isync - synchronize IMAP4 and maildir mailboxes
@@ -304,13 +304,9 @@ will then use the global value by default.
 Default configuration file
 Default configuration file
 ..
 ..
 .SH BUGS
 .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
 .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
 .P
 When synchronizing multiple mailboxes on the same IMAP server, it is not
 When synchronizing multiple mailboxes on the same IMAP server, it is not
 possible to select different SSL options for each mailbox.  Only the options
 possible to select different SSL options for each mailbox.  Only the options

+ 8 - 5
isync.h

@@ -1,7 +1,7 @@
 /* $Id$
 /* $Id$
  *
  *
  * isync - IMAP4 to maildir mailbox synchronizer
  * 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
  *  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
  *  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
  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  */
  */
 
 
+#define DB_DBM_HSEARCH 1
+
 #include <sys/types.h>
 #include <sys/types.h>
 #include <stdarg.h>
 #include <stdarg.h>
 #if HAVE_LIBSSL
 #if HAVE_LIBSSL
 #include <openssl/ssl.h>
 #include <openssl/ssl.h>
 #endif
 #endif
+#include <db.h>
 #include "debug.h"
 #include "debug.h"
 
 
 typedef struct
 typedef struct
@@ -79,13 +82,13 @@ struct config
 /* struct representing local mailbox file */
 /* struct representing local mailbox file */
 struct mailbox
 struct mailbox
 {
 {
+    DBM *db;
     char *path;
     char *path;
     message_t *msgs;
     message_t *msgs;
+    int lockfd;
     unsigned int deleted;	/* # of deleted messages */
     unsigned int deleted;	/* # of deleted messages */
     unsigned int uidvalidity;
     unsigned int uidvalidity;
     unsigned int maxuid;	/* largest uid we know about */
     unsigned int maxuid;	/* largest uid we know about */
-    unsigned int changed:1;
-    unsigned int maxuidchanged:1;
 };
 };
 
 
 /* message dispositions */
 /* message dispositions */
@@ -106,7 +109,6 @@ struct message
     message_t *next;
     message_t *next;
     unsigned int processed:1;	/* message has already been evaluated */
     unsigned int processed:1;	/* message has already been evaluated */
     unsigned int new:1;		/* message is in the new/ subdir */
     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 dead:1;	/* message doesn't exist on the server */
     unsigned int wanted:1;	/* when using MaxMessages, keep this message */
     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);
 mailbox_t *maildir_open (const char *, int flags);
 int maildir_expunge (mailbox_t *, int);
 int maildir_expunge (mailbox_t *, int);
 int maildir_set_uidvalidity (mailbox_t *, unsigned int uidvalidity);
 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);
 message_t * find_msg (message_t * list, unsigned int uid);
 void free_message (message_t *);
 void free_message (message_t *);

+ 1 - 1
list.c

@@ -1,7 +1,7 @@
 /* $Id$
 /* $Id$
  *
  *
  * isync - IMAP4 to maildir mailbox synchronizer
  * 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
  *  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
  *  it under the terms of the GNU General Public License as published by

+ 97 - 111
maildir.c

@@ -1,7 +1,7 @@
 /* $Id$
 /* $Id$
  *
  *
  * isync - IMAP4 to maildir mailbox synchronizer
  * 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
  *  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
  *  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];
     char full[_POSIX_PATH_MAX];
     int fd;
     int fd;
-    int ret;
+    int ret = 0;
     int len;
     int len;
     char buf[64];
     char buf[64];
     unsigned int uid = 0;
     unsigned int uid = 0;
@@ -96,29 +96,59 @@ read_uid (const char *path, const char *file)
     {
     {
 	if (errno != ENOENT)
 	if (errno != ENOENT)
 	{
 	{
-	    perror ("open");
+	    perror (full);
 	    return -1;
 	    return -1;
 	}
 	}
 	return 0;		/* doesn't exist */
 	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);
     close (fd);
     return ret ? (unsigned int) ret : uid;
     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.
 /* open a maildir mailbox.
  * if OPEN_FAST is set, we just check to make
  * if OPEN_FAST is set, we just check to make
  * sure its a valid mailbox and don't actually parse it.  any IMAP messages
  * 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;
     struct stat sb;
     const char *subdirs[] = { "cur", "new", "tmp" };
     const char *subdirs[] = { "cur", "new", "tmp" };
     int i;
     int i;
+    datum key;
 
 
     m = calloc (1, sizeof (mailbox_t));
     m = calloc (1, sizeof (mailbox_t));
+    m->lockfd = -1;
     /* filename expansion happens here, not in the config parser */
     /* filename expansion happens here, not in the config parser */
     m->path = expand_strdup (path);
     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",
 		fprintf (stderr, "ERROR: mkdir %s: %s (errno %d)\n",
 			 m->path, strerror (errno), errno);
 			 m->path, strerror (errno), errno);
-		free (m->path);
-		free (m);
-		return NULL;
+		goto err;
 	    }
 	    }
 
 
 	    for (i = 0; i < 3; i++)
 	    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",
 		    fprintf (stderr, "ERROR: mkdir %s: %s (errno %d)\n",
 			     buf, strerror (errno), errno);
 			     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,
 	    fprintf (stderr, "ERROR: stat %s: %s (errno %d)\n", m->path,
 		     strerror (errno), errno);
 		     strerror (errno), errno);
-	    free (m->path);
-	    free (m);
-	    return NULL;
+	    goto err;
 	}
 	}
     }
     }
     else
     else
@@ -195,33 +221,37 @@ maildir_open (const char *path, int flags)
 		fprintf (stderr,
 		fprintf (stderr,
 			 "ERROR: %s does not appear to be a valid maildir style mailbox\n",
 			 "ERROR: %s does not appear to be a valid maildir style mailbox\n",
 			 m->path);
 			 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 */
     /* check for the uidvalidity value */
     m->uidvalidity = read_uid (m->path, "isyncuidvalidity");
     m->uidvalidity = read_uid (m->path, "isyncuidvalidity");
     if (m->uidvalidity == (unsigned int) -1)
     if (m->uidvalidity == (unsigned int) -1)
-    {
-	free (m->path);
-	free (m);
-	return NULL;
-    }
+	goto err;
 
 
     /* load the current maxuid */
     /* load the current maxuid */
     if ((m->maxuid = read_uid (m->path, "isyncmaxuid")) == (unsigned int) -1)
     if ((m->maxuid = read_uid (m->path, "isyncmaxuid")) == (unsigned int) -1)
-    {
-	free (m->path);
-	free (m);
-	return NULL;
-    }
+	goto err;
 
 
     if (flags & OPEN_FAST)
     if (flags & OPEN_FAST)
 	return m;
 	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;
     cur = &m->msgs;
     for (; count < 2; count++)
     for (; count < 2; count++)
     {
     {
@@ -231,10 +261,8 @@ maildir_open (const char *path, int flags)
 	d = opendir (buf);
 	d = opendir (buf);
 	if (!d)
 	if (!d)
 	{
 	{
-	    free (m->path);
-	    free (m);
 	    perror ("opendir");
 	    perror ("opendir");
-	    return 0;
+	    goto err;
 	}
 	}
 	while ((e = readdir (d)))
 	while ((e = readdir (d)))
 	{
 	{
@@ -247,40 +275,25 @@ maildir_open (const char *path, int flags)
 	    p->flags = 0;
 	    p->flags = 0;
 	    p->new = (count == 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)
 		if (p->uid > m->maxuid)
-		{
 		    m->maxuid = p->uid;
 		    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)
 	    if (s)
 		parse_info (p, s + 1);
 		parse_info (p, s + 1);
 	    if (p->flags & D_DELETED)
 	    if (p->flags & D_DELETED)
@@ -290,6 +303,15 @@ maildir_open (const char *path, int flags)
 	closedir (d);
 	closedir (d);
     }
     }
     return m;
     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,
 /* permanently remove messages from a maildir mailbox.  if `dead' is nonzero,
@@ -322,8 +344,8 @@ maildir_expunge (mailbox_t * mbox, int dead)
     return 0;
     return 0;
 }
 }
 
 
-static int
-update_maxuid (mailbox_t * mbox)
+int
+maildir_update_maxuid (mailbox_t * mbox)
 {
 {
     int fd;
     int fd;
     char buf[64];
     char buf[64];
@@ -355,7 +377,7 @@ update_maxuid (mailbox_t * mbox)
     uid = atol (buf);
     uid = atol (buf);
     if (uid > mbox->maxuid)
     if (uid > mbox->maxuid)
     {
     {
-	puts ("Error, maxuid is now higher (fatal)");
+	fputs ("ERROR: maxuid is now higher (fatal)\n", stderr);
 	ret = -1;
 	ret = -1;
     }
     }
 
 
@@ -429,48 +451,14 @@ maildir_clean_tmp (const char *mbox)
     }
     }
 }
 }
 
 
-int
+void
 maildir_close (mailbox_t * mbox)
 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
     /* per the maildir(5) specification, delivery agents are supposed to
      * set a 24-hour timer on items placed in the `tmp' directory.
      * 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);
     free_message (mbox->msgs);
     memset (mbox, 0xff, sizeof (mailbox_t));
     memset (mbox, 0xff, sizeof (mailbox_t));
     free (mbox);
     free (mbox);
-
-    return ret;
 }
 }
 
 
 int
 int

+ 85 - 70
main.c

@@ -1,7 +1,7 @@
 /* $Id$
 /* $Id$
  *
  *
  * isync - IMAP4 to maildir mailbox synchronizer
  * 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
  *  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
  *  it under the terms of the GNU General Public License as published by
@@ -67,7 +67,7 @@ static void
 usage (void)
 usage (void)
 {
 {
     printf ("%s %s IMAP4 to maildir synchronizer\n", PACKAGE, VERSION);
     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);
     printf ("usage: %s [ flags ] mailbox [mailbox ...]\n", PACKAGE);
     puts ("  -a, --all	Synchronize all defined mailboxes");
     puts ("  -a, --all	Synchronize all defined mailboxes");
     puts ("  -c, --config CONFIG	read an alternate config file (default: ~/.isyncrc)");
     puts ("  -c, --config CONFIG	read an alternate config file (default: ~/.isyncrc)");
@@ -134,7 +134,7 @@ main (int argc, char **argv)
 {
 {
     int i;
     int i;
     config_t *box = 0;
     config_t *box = 0;
-    mailbox_t *mail;
+    mailbox_t *mail = 0;
     imap_t *imap = 0;
     imap_t *imap = 0;
     int expunge = 0;		/* by default, don't delete anything */
     int expunge = 0;		/* by default, don't delete anything */
     int fast = 0;
     int fast = 0;
@@ -250,6 +250,9 @@ main (int argc, char **argv)
 		if (!global.host)
 		if (!global.host)
 		{
 		{
 		    fprintf (stderr, "%s: no such mailbox\n", argv[optind]);
 		    fprintf (stderr, "%s: no such mailbox\n", argv[optind]);
+		    /* continue is ok here because we are not handling the
+		     * `all' case.
+		     */
 		    continue;
 		    continue;
 		}
 		}
 		global.path = argv[optind];
 		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)
 		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)
 	if (all)
 	    box = box->next;
 	    box = box->next;
     }
     }

+ 56 - 36
sync.c

@@ -1,7 +1,7 @@
 /* $Id$
 /* $Id$
  *
  *
  * isync - IMAP4 to maildir mailbox synchronizer
  * 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
  *  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
  *  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;
     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
 int
 sync_mailbox (mailbox_t * mbox, imap_t * imap, int flags,
 sync_mailbox (mailbox_t * mbox, imap_t * imap, int flags,
 	      unsigned int max_size, unsigned int max_msgs)
 	      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)
     if (mbox->maxuid == 0 || imap->maxuid > mbox->maxuid)
     {
     {
 	mbox->maxuid = imap->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
     /* 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)
 	    if (cur->uid == (unsigned int) -1)
 	    {
 	    {
 		struct stat sb;
 		struct stat sb;
-		int uid;
 
 
 		if ((flags & SYNC_QUIET) == 0)
 		if ((flags & SYNC_QUIET) == 0)
 		{
 		{
@@ -132,35 +150,12 @@ sync_mailbox (mailbox_t * mbox, imap_t * imap, int flags,
 		}
 		}
 
 
 		cur->size = sb.st_size;
 		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);
 		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)
 	    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,
 	    if (imap_copy_message (imap, cur->uid,
 				   imap->box->copy_deleted_to))
 				   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);
 			imap->box->copy_deleted_to);
 		return -1;
 		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))
 	    if ((cur->flags & D_DELETED) == 0 && (tmp->flags & D_DELETED))
 		mbox->deleted++;
 		mbox->deleted++;
 	    cur->flags |= (tmp->flags & ~(D_RECENT | D_DRAFT));
 	    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 (;;)
 	    for (;;)
 	    {
 	    {
 		/* create new file */
 		/* 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 (),
 			  mbox->path, time (0), MaildirCount++, getpid (),
-			  Hostname, cur->uid, suffix);
+			  Hostname, suffix);
 
 
 		if ((fd = open (path, O_WRONLY | O_CREAT | O_EXCL, 0600)) > 0)
 		if ((fd = open (path, O_WRONLY | O_CREAT | O_EXCL, 0600)) > 0)
 		    break;
 		    break;
 		if (errno != EEXIST)
 		if (errno != EEXIST)
 		{
 		{
-		    perror ("open");
+		    perror (path);
 		    break;
 		    break;
 		}
 		}
 
 
@@ -336,6 +351,11 @@ sync_mailbox (mailbox_t * mbox, imap_t * imap, int flags,
 		 */
 		 */
 		if (link (path, newpath))
 		if (link (path, newpath))
 		    perror ("link");
 		    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 */
 	    /* always remove the temp file */