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.
This commit is contained in:
Michael Elkins 2002-01-16 19:47:28 +00:00
parent 2c648da5cf
commit c121ec912f
13 changed files with 276 additions and 242 deletions

View File

@ -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
NEWS
View File

@ -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
TODO
View File

@ -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.

View File

@ -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

View File

@ -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"

2
cram.c
View File

@ -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
imap.c
View File

@ -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

12
isync.1
View File

@ -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 does not use NFS-safe locking. It will correctly prevent concurrent
filename in a way which will be interoperable with existing readers. So synchronization of a mailbox on the same host, but not across NFS.
the UID is placed in the filename of the messages in the local maildir
mailbox rather than the :info field.
.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

13
isync.h
View File

@ -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 *);

2
list.c
View File

@ -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

216
maildir.c
View File

@ -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); len = read (fd, buf, sizeof (buf) - 1);
if (!ret) if (len == -1)
{ {
len = read (fd, buf, sizeof (buf) - 1); perror ("read");
if (len == -1) ret = -1;
ret = -1; }
else else
{ {
buf[len] = 0; buf[len] = 0;
uid = atol (buf); 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); goto err;
free (m);
return NULL;
} }
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); goto err;
free (m);
return NULL;
} }
} }
@ -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); goto err;
free (m);
return NULL;
} }
} }
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); goto err;
free (m);
return 0;
} }
} }
} }
/* 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)
{ goto err;
free (m->path);
free (m);
return NULL;
}
/* 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)
{ goto err;
free (m->path);
free (m);
return NULL;
}
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: /* determine the UID for this message. The basename (sans
* <unique-prefix>,U=<n>:2,<flags> * flags) is used as the key in the db
* 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
*/ */
s = strstr (p->file, ",U="); strfcpy (buf, p->file, sizeof (buf));
if (!s)
s = strstr (p->file, "UID");
if (!s)
puts ("Warning, no UID for message");
else
{
p->uid = strtol (s + 3, &s, 10);
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 */
}
}
s = strchr (p->file, ':'); 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 = *(int *) key.dptr;
if (p->uid > m->maxuid)
m->maxuid = p->uid;
}
else
puts ("Warning, no UID for message");
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 int
update_maxuid (mailbox_t * mbox) 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; if (mbox->db)
char path[_POSIX_PATH_MAX]; dbm_close (mbox->db);
char oldpath[_POSIX_PATH_MAX];
char *p;
int ret = 0;
if (mbox->changed) /* release the mutex on the mailbox */
{ maildir_unlock (mbox);
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->maxuidchanged)
ret = update_maxuid (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

161
main.c
View File

@ -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) do {
{ if (!box->pass)
/* if we don't have a global password set, prompt the user for
* it now.
*/
if (!global.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"); global.pass = getpass ("Password:");
exit (1); if (!global.pass)
{
fprintf (stderr, "Skipping %s, no password", box->path);
break;
}
} }
box->pass = strdup (global.pass);
} }
box->pass = strdup (global.pass);
}
if (!quiet) if (!quiet)
printf ("Reading %s\n", box->path); printf ("Reading %s\n", box->path);
i = 0; i = 0;
if (fast) if (fast)
i |= OPEN_FAST; i |= OPEN_FAST;
if (create) if (create)
i |= OPEN_CREATE; i |= OPEN_CREATE;
mail = maildir_open (box->path, i); mail = maildir_open (box->path, i);
if (!mail) if (!mail)
{
fprintf (stderr, "ERROR: unable to load mailbox %s\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);
goto cleanup;
}
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 (!fast)
{
if ((expunge || box->expunge) && (imap->deleted || mail->deleted))
{ {
/* remove messages marked for deletion */ fprintf (stderr, "%s: unable to open mailbox\n", box->path);
if (!quiet) break;
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);
} }
/* 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 */ imap = imap_open (box, fast ? mail->maxuid + 1 : 1, imap);
if (!quiet) if (!imap)
printf ("Committing changes to %s\n", mail->path); {
fprintf (stderr, "%s: skipping mailbox due to IMAP error\n",
box->path);
break;
}
if (maildir_close (mail)) if (!quiet)
exit (1); 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;
}
cleanup: if (!fast)
{
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);
}
} while (0);
/* we never sync the same mailbox twice, so close it now */
if (mail)
maildir_close (mail);
/* 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;
} }

92
sync.c
View File

@ -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;
cur->uid = imap_append_message (imap, fd, 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 */