ソースを参照

broke config code into config.c

added support for uploading local messages with no UID to the IMAP server

added Expunge configuration option

added CopyDeletedTo configuration option
Michael Elkins 24 年 前
コミット
bcecbe5eeb
8 ファイル変更579 行追加222 行削除
  1. 1 1
      Makefile.am
  2. 242 0
      config.c
  3. 180 1
      imap.c
  4. 13 0
      isync.1
  5. 11 1
      isync.h
  6. 8 0
      isyncrc.sample
  7. 20 206
      main.c
  8. 104 13
      sync.c

+ 1 - 1
Makefile.am

@@ -1,5 +1,5 @@
 bin_PROGRAMS=isync
-isync_SOURCES=main.c imap.c sync.c maildir.c isync.h list.c cram.c
+isync_SOURCES=main.c imap.c sync.c maildir.c isync.h list.c cram.c config.c
 man_MANS=isync.1
 EXTRA_DIST=sample.isyncrc $(man_MANS)
 INCLUDES=$(RPM_OPT_FLAGS)

+ 242 - 0
config.c

@@ -0,0 +1,242 @@
+/* $Id$
+ *
+ * isync - IMAP4 to maildir mailbox synchronizer
+ * Copyright (C) 2000 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
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <unistd.h>
+#include <limits.h>
+#include <errno.h>
+#include <pwd.h>
+#include <sys/types.h>
+#include <string.h>
+#include "isync.h"
+
+static config_t *box = 0;
+
+/* set defaults from the global configuration section */
+void
+config_defaults (config_t * conf)
+{
+    conf->user = global.user;
+    conf->pass = global.pass;
+    conf->port = global.port;
+    conf->box = global.box;
+    conf->host = global.host;
+    conf->max_size = global.max_size;
+    conf->copy_deleted_to = global.copy_deleted_to;
+    conf->use_namespace = global.use_namespace;
+    conf->expunge = global.expunge;
+#if HAVE_LIBSSL
+    conf->require_ssl = global.require_ssl;
+    conf->use_imaps = global.use_imaps;
+    conf->cert_file = global.cert_file;
+    conf->use_sslv2 = global.use_sslv2;
+    conf->use_sslv3 = global.use_sslv3;
+    conf->use_tlsv1 = global.use_tlsv1;
+#endif
+}
+
+void
+load_config (const char *where)
+{
+    char path[_POSIX_PATH_MAX];
+    char buf[1024];
+    struct passwd *pw;
+    config_t **cur = &box;
+    int line = 0;
+    FILE *fp;
+    char *p, *cmd, *val;
+
+    if (!where)
+    {
+	pw = getpwuid (getuid ());
+	snprintf (path, sizeof (path), "%s/.isyncrc", pw->pw_dir);
+	where = path;
+    }
+    printf ("Reading %s\n", where);
+
+    fp = fopen (where, "r");
+    if (!fp)
+    {
+	if (errno != ENOENT)
+	{
+	    perror ("fopen");
+	    return;
+	}
+    }
+    buf[sizeof buf - 1] = 0;
+    while ((fgets (buf, sizeof (buf) - 1, fp)))
+    {
+	p = buf;
+	cmd = next_arg (&p);
+	val = next_arg (&p);
+	line++;
+	if (!cmd || *cmd == '#')
+	    continue;
+	if (!strncasecmp ("mailbox", cmd, 7))
+	{
+	    if (*cur)
+		cur = &(*cur)->next;
+	    *cur = calloc (1, sizeof (config_t));
+	    config_defaults (*cur);
+	    (*cur)->path = strdup (val);
+	}
+	else if (!strncasecmp ("host", cmd, 4))
+	{
+#if HAVE_LIBSSL
+	    if (!strncasecmp ("imaps:", val, 6))
+	    {
+		val += 6;
+		if (*cur)
+		{
+		    (*cur)->use_imaps = 1;
+		    (*cur)->port = 993;
+		}
+		else
+		{
+		    global.use_imaps = 1;
+		    global.port = 993;
+		}
+	    }
+#endif
+	    if (*cur)
+		(*cur)->host = strdup (val);
+	    else
+		global.host = strdup (val);
+	}
+	else if (!strncasecmp ("user", cmd, 4))
+	{
+	    if (*cur)
+		(*cur)->user = strdup (val);
+	    else
+		global.user = strdup (val);
+	}
+	else if (!strncasecmp ("pass", cmd, 4))
+	{
+	    if (*cur)
+		(*cur)->pass = strdup (val);
+	    else
+		global.pass = strdup (val);
+	}
+	else if (!strncasecmp ("port", cmd, 4))
+	{
+	    if (*cur)
+		(*cur)->port = atoi (val);
+	    else
+		global.port = atoi (val);
+	}
+	else if (!strncasecmp ("box", cmd, 3))
+	{
+	    if (*cur)
+		(*cur)->box = strdup (val);
+	    else
+		global.box = strdup (val);
+	}
+	else if (!strncasecmp ("alias", cmd, 5))
+	{
+	    if (*cur)
+		(*cur)->alias = strdup (val);
+	}
+	else if (!strncasecmp ("maxsize", cmd, 7))
+	{
+	    if (*cur)
+		(*cur)->max_size = atol (val);
+	    else
+		global.max_size = atol (val);
+	}
+	else if (!strncasecmp ("UseNamespace", cmd, 12))
+	{
+	    if (*cur)
+		(*cur)->use_namespace = (strcasecmp (val, "yes") == 0);
+	    else
+		global.use_namespace = (strcasecmp (val, "yes") == 0);
+	}
+	else if (!strncasecmp ("CopyDeletedTo", cmd, 13))
+	{
+	    if (*cur)
+		(*cur)->copy_deleted_to = strdup (val);
+	    else
+		global.copy_deleted_to = strdup (val);
+	}
+	else if (!strncasecmp ("Expunge", cmd, 7))
+	{
+	    if (*cur)
+		(*cur)->expunge = (strcasecmp (val, "yes") == 0);
+	    else
+		global.expunge = (strcasecmp (val, "yes") == 0);
+	}
+#if HAVE_LIBSSL
+	else if (!strncasecmp ("CertificateFile", cmd, 15))
+	{
+	    if (*cur)
+		(*cur)->cert_file = strdup (val);
+	    else
+		global.cert_file = strdup (val);
+	}
+	else if (!strncasecmp ("RequireSSL", cmd, 10))
+	{
+	    if (*cur)
+		(*cur)->require_ssl = (strcasecmp (val, "yes") == 0);
+	    else
+		global.require_ssl = (strcasecmp (val, "yes") == 0);
+	}
+	else if (!strncasecmp ("UseSSLv2", cmd, 8))
+	{
+	    if (*cur)
+		(*cur)->use_sslv2 = (strcasecmp (val, "yes") == 0);
+	    else
+		global.use_sslv2 = (strcasecmp (val, "yes") == 0);
+	}
+	else if (!strncasecmp ("UseSSLv3", cmd, 8))
+	{
+	    if (*cur)
+		(*cur)->use_sslv3 = (strcasecmp (val, "yes") == 0);
+	    else
+		global.use_sslv3 = (strcasecmp (val, "yes") == 0);
+	}
+	else if (!strncasecmp ("UseTLSv1", cmd, 8))
+	{
+	    if (*cur)
+		(*cur)->use_tlsv1 = (strcasecmp (val, "yes") == 0);
+	    else
+		global.use_tlsv1 = (strcasecmp (val, "yes") == 0);
+	}
+	else if (!strncasecmp ("RequireCRAM", cmd, 11))
+	{
+	    if (*cur)
+		(*cur)->require_cram = (strcasecmp (val, "yes") == 0);
+	    else
+		global.require_cram = (strcasecmp (val, "yes") == 0);
+	}
+#endif
+	else if (buf[0])
+	    printf ("%s:%d:unknown command:%s", path, line, cmd);
+    }
+    fclose (fp);
+}
+
+config_t *
+find_box (const char *s)
+{
+    config_t *p = box;
+
+    for (; p; p = p->next)
+	if (!strcmp (s, p->path) || (p->alias && !strcmp (s, p->alias)))
+	    return p;
+    return 0;
+}

+ 180 - 1
imap.c

@@ -701,7 +701,7 @@ imap_open (config_t * box, unsigned int minuid, imap_t * imap)
 
 	fputs ("Selecting mailbox... ", stdout);
 	fflush (stdout);
-	if ((ret = imap_exec (imap, "SELECT %s%s", ns_prefix, box->box)))
+	if ((ret = imap_exec (imap, "SELECT \"%s%s\"", ns_prefix, box->box)))
 	    break;
 	printf ("%d messages, %d recent\n", imap->count, imap->recent);
 
@@ -903,3 +903,182 @@ imap_expunge (imap_t * imap)
 {
     return imap_exec (imap, "EXPUNGE");
 }
+
+int
+imap_copy_message (imap_t * imap, unsigned int uid, const char *mailbox)
+{
+    char *ns_prefix = "";
+
+    /* XXX for now assume personal namespace */
+    if (imap->box->use_namespace && is_list (imap->ns_personal) &&
+	is_list (imap->ns_personal->child) &&
+	is_atom (imap->ns_personal->child->child))
+    {
+	ns_prefix = imap->ns_personal->child->child->val;
+    }
+
+    return imap_exec (imap, "UID COPY %u \"%s%s\"", uid, ns_prefix, mailbox);
+}
+
+int
+imap_append_message (imap_t * imap, int fd, message_t * msg)
+{
+    char buf[1024];
+    size_t len;
+    size_t sofar = 0;
+    int lines = 0;
+    char flagstr[128];
+    char *s;
+    size_t i;
+    size_t start, end;
+    char *arg;
+
+    /* ugh, we need to count the number of newlines */
+    while (sofar < msg->size)
+    {
+	len = msg->size - sofar;
+	if (len > sizeof (buf))
+	    len = sizeof (buf);
+	len = read (fd, buf, len);
+	if (len == (size_t) - 1)
+	{
+	    perror ("read");
+	    return -1;
+	}
+	for (i = 0; i < len; i++)
+	    if (buf[i] == '\n')
+		lines++;
+	sofar += len;
+    }
+
+    flagstr[0] = 0;
+    if (msg->flags)
+    {
+	strcpy (flagstr, "(");
+	if (msg->flags & D_DELETED)
+	    snprintf (flagstr + strlen (flagstr),
+		      sizeof (flagstr) - strlen (flagstr), "%s\\Deleted",
+		      flagstr[1] ? " " : "");
+	if (msg->flags & D_ANSWERED)
+	    snprintf (flagstr + strlen (flagstr),
+		      sizeof (flagstr) - strlen (flagstr), "%s\\Answered",
+		      flagstr[1] ? " " : "");
+	if (msg->flags & D_SEEN)
+	    snprintf (flagstr + strlen (flagstr),
+		      sizeof (flagstr) - strlen (flagstr), "%s\\Seen",
+		      flagstr[1] ? " " : "");
+	if (msg->flags & D_FLAGGED)
+	    snprintf (flagstr + strlen (flagstr),
+		      sizeof (flagstr) - strlen (flagstr), "%s\\Flagged",
+		      flagstr[1] ? " " : "");
+	if (msg->flags & D_DRAFT)
+	    snprintf (flagstr + strlen (flagstr),
+		      sizeof (flagstr) - strlen (flagstr), "%s\\Draft",
+		      flagstr[1] ? " " : "");
+	snprintf (flagstr + strlen (flagstr),
+		  sizeof (flagstr) - strlen (flagstr), ") ");
+    }
+
+    snprintf (buf, sizeof (buf), "%d APPEND %s %s{%d}\r\n", ++Tag,
+	      imap->box->box, flagstr, msg->size + lines);
+    socket_write (imap->sock, buf, strlen (buf));
+    if (Verbose)
+	fputs (buf, stdout);
+
+    if (buffer_gets (imap->buf, &s))
+	return -1;
+    if (Verbose)
+	puts (s);
+
+    if (*s != '+')
+	return -1;
+
+    /* rewind */
+    lseek (fd, 0, 0);
+
+    sofar = 0;
+    while (sofar < msg->size)
+    {
+	len = msg->size - sofar;
+	if (len > sizeof (buf))
+	    len = sizeof (buf);
+	len = read (fd, buf, len);
+	if (len == (size_t) - 1)
+	    return -1;
+	start = 0;
+	while (start < len)
+	{
+	    end = start;
+	    while (end < len && buf[end] != '\n')
+		end++;
+	    if (start != end)
+		socket_write (imap->sock, buf + start, end - start);
+/*	    if (Verbose)
+	    {
+		buf[end] = 0;
+		puts (buf + start);
+	    } */
+	    socket_write (imap->sock, "\r\n", 2);
+	    start = end + 1;
+	}
+	sofar += len;
+    }
+    socket_write (imap->sock, "\r\n", 2);
+
+    for (;;)
+    {
+	if (buffer_gets (imap->buf, &s))
+	    return -1;
+
+	if (Verbose)
+	    puts (s);
+
+	arg = next_arg (&s);
+	if (*arg == '*')
+	{
+	    /* XXX just ignore it for now */
+	}
+	else if (atoi (arg) != Tag)
+	{
+	    puts ("wrong tag");
+	    return -1;
+	}
+	else
+	{
+	    int uid;
+
+	    arg = next_arg (&s);
+	    if (strcmp (arg, "OK"))
+		return -1;
+	    arg = next_arg (&s);
+	    if (*arg != '[')
+		break;
+	    arg++;
+	    if (strcasecmp ("APPENDUID", arg))
+	    {
+		puts ("Error, expected APPENDUID");
+		break;
+	    }
+	    arg = next_arg (&s);
+	    if (!arg)
+		break;
+	    if (atoi (arg) != imap->uidvalidity)
+	    {
+		puts ("Error, UIDVALIDITY doesn't match APPENDUID");
+		return -1;
+	    }
+	    arg = next_arg (&s);
+	    if (!arg)
+		break;
+	    uid = strtol (arg, &s, 10);
+	    if (*s != ']')
+	    {
+		/* parse error */
+		break;
+	    }
+	    return uid;
+	}
+    }
+
+    return 0;
+}

+ 13 - 0
isync.1

@@ -155,6 +155,19 @@ Defines an alias for the mailbox which can be used as a shortcut on the
 command line.
 ..
 .TP
+\fBCopyDeletedTo\fR \fIstring\fR
+Specifies the remote IMAP mailbox to copy deleted messages prior to
+expunging (Default: none).
+..
+.TP
+\fBExpunge\fR \fIyes|no\fR
+Specifies whether deleted messages are expunged by default (Default: no).
+\fBNOTE:\fR  The
+.I -e
+command line option overrides this setting when set to
+\fIno\fR.
+..
+.TP
 \fBMaxSize\fR \fIbytes\fR
 Sets a threshold for the maximum message size (in bytes) for which
 .B isync

+ 11 - 1
isync.h

@@ -54,7 +54,8 @@ struct config
     char *pass;
     char *box;
     char *alias;
-    unsigned int max_size;
+    char *copy_deleted_to;
+    off_t max_size;
     config_t *next;
 #if HAVE_LIBSSL
     char *cert_file;
@@ -66,6 +67,7 @@ struct config
     unsigned int require_cram:1;
 #endif
     unsigned int use_namespace:1;
+    unsigned int expunge:1;
 };
 
 /* struct representing local mailbox file */
@@ -159,11 +161,17 @@ char *next_arg (char **);
 
 int sync_mailbox (mailbox_t *, imap_t *, int, unsigned int);
 
+void config_defaults (config_t *);
+void load_config (const char *);
+config_t *find_box (const char *);
+
 void imap_close (imap_t *);
+int imap_copy_message (imap_t * imap, unsigned int uid, const char *mailbox);
 int imap_fetch_message (imap_t *, unsigned int, int);
 int imap_set_flags (imap_t *, unsigned int, unsigned int);
 int imap_expunge (imap_t *);
 imap_t *imap_open (config_t *, unsigned int, imap_t *);
+int imap_append_message (imap_t *, int, message_t *);
 
 mailbox_t *maildir_open (const char *, int fast);
 int maildir_expunge (mailbox_t *, int);
@@ -180,3 +188,5 @@ int is_atom (list_t *list);
 int is_list (list_t *list);
 int is_nil (list_t *list);
 void free_list (list_t *list);
+
+#define strfcpy(a,b,c) {strncpy(a,b,c);(a)[c-1]=0;}

+ 8 - 0
isyncrc.sample

@@ -2,6 +2,12 @@
 #   Values here are used as defaults for any following Mailbox section that
 #   doesn't specify it.
 
+# by default, expunge deleted messages (same as -e on command line)
+Expunge yes
+
+# copy deleted messages to the IMAP "Trash" folder
+CopyDeletedTo "Trash"
+
 # my default username, if different from the local username
 User me
 #Port	143
@@ -18,6 +24,8 @@ Host	work.host.com
 Pass	xxxxxxxx
 # define a shortcut so I can just use "isync work" from the command line
 Alias	work
+# don't auto expunge messages in this box (overridden by -e on command line)
+Expunge no
 
 ###
 ### personal mailbox

+ 20 - 206
main.c

@@ -50,7 +50,6 @@ struct option Opts[] = {
 
 config_t global;
 unsigned int Tag = 0;
-static config_t *box = 0;
 char Hostname[256];
 int Verbose = 0;
 
@@ -85,203 +84,6 @@ usage (void)
     exit (0);
 }
 
-/* set defaults from the global configuration section */
-static void
-config_defaults (config_t * conf)
-{
-    conf->user = global.user;
-    conf->pass = global.pass;
-    conf->port = global.port;
-    conf->box = global.box;
-    conf->host = global.host;
-    conf->max_size = global.max_size;
-    conf->use_namespace = global.use_namespace;
-#if HAVE_LIBSSL
-    conf->require_ssl = global.require_ssl;
-    conf->use_imaps = global.use_imaps;
-    conf->cert_file = global.cert_file;
-    conf->use_sslv2 = global.use_sslv2;
-    conf->use_sslv3 = global.use_sslv3;
-    conf->use_tlsv1 = global.use_tlsv1;
-#endif
-}
-
-static void
-load_config (char *where)
-{
-    char path[_POSIX_PATH_MAX];
-    char buf[1024];
-    struct passwd *pw;
-    config_t **cur = &box;
-    int line = 0;
-    FILE *fp;
-    char *p, *cmd, *val;
-
-    if (!where)
-    {
-	pw = getpwuid (getuid ());
-	snprintf (path, sizeof (path), "%s/.isyncrc", pw->pw_dir);
-	where = path;
-    }
-    printf ("Reading %s\n", where);
-
-    fp = fopen (where, "r");
-    if (!fp)
-    {
-	if (errno != ENOENT)
-	{
-	    perror ("fopen");
-	    return;
-	}
-    }
-    buf[sizeof buf - 1] = 0;
-    while ((fgets (buf, sizeof (buf) - 1, fp)))
-    {
-	p = buf;
-	cmd = next_arg (&p);
-	val = next_arg (&p);
-	line++;
-	if (!cmd || *cmd == '#')
-	    continue;
-	if (!strncasecmp ("mailbox", cmd, 7))
-	{
-	    if (*cur)
-		cur = &(*cur)->next;
-	    *cur = calloc (1, sizeof (config_t));
-	    config_defaults (*cur);
-	    (*cur)->path = strdup (val);
-	}
-	else if (!strncasecmp ("host", cmd, 4))
-	{
-#if HAVE_LIBSSL
-	    if (!strncasecmp ("imaps:", val, 6))
-	    {
-		val += 6;
-		if (*cur)
-		{
-		    (*cur)->use_imaps = 1;
-		    (*cur)->port = 993;
-		}
-		else
-		{
-		    global.use_imaps = 1;
-		    global.port = 993;
-		}
-	    }
-#endif
-	    if (*cur)
-		(*cur)->host = strdup (val);
-	    else
-		global.host = strdup (val);
-	}
-	else if (!strncasecmp ("user", cmd, 4))
-	{
-	    if (*cur)
-		(*cur)->user = strdup (val);
-	    else
-		global.user = strdup (val);
-	}
-	else if (!strncasecmp ("pass", cmd, 4))
-	{
-	    if (*cur)
-		(*cur)->pass = strdup (val);
-	    else
-		global.pass = strdup (val);
-	}
-	else if (!strncasecmp ("port", cmd, 4))
-	{
-	    if (*cur)
-		(*cur)->port = atoi (val);
-	    else
-		global.port = atoi (val);
-	}
-	else if (!strncasecmp ("box", cmd, 3))
-	{
-	    if (*cur)
-		(*cur)->box = strdup (val);
-	    else
-		global.box = strdup (val);
-	}
-	else if (!strncasecmp ("alias", cmd, 5))
-	{
-	    if (*cur)
-		(*cur)->alias = strdup (val);
-	}
-	else if (!strncasecmp ("maxsize", cmd, 7))
-	{
-	    if (*cur)
-		(*cur)->max_size = atol (val);
-	    else
-		global.max_size = atol (val);
-	}
-	else if (!strncasecmp ("UseNamespace", cmd, 12))
-	{
-	    if (*cur)
-		(*cur)->use_namespace = (strcasecmp (val, "yes") == 0);
-	    else
-		global.use_namespace = (strcasecmp (val, "yes") == 0);
-	}
-#if HAVE_LIBSSL
-	else if (!strncasecmp ("CertificateFile", cmd, 15))
-	{
-	    if (*cur)
-		(*cur)->cert_file = strdup (val);
-	    else
-		global.cert_file = strdup (val);
-	}
-	else if (!strncasecmp ("RequireSSL", cmd, 10))
-	{
-	    if (*cur)
-		(*cur)->require_ssl = (strcasecmp (val, "yes") == 0);
-	    else
-		global.require_ssl = (strcasecmp (val, "yes") == 0);
-	}
-	else if (!strncasecmp ("UseSSLv2", cmd, 8))
-	{
-	    if (*cur)
-		(*cur)->use_sslv2 = (strcasecmp (val, "yes") == 0);
-	    else
-		global.use_sslv2 = (strcasecmp (val, "yes") == 0);
-	}
-	else if (!strncasecmp ("UseSSLv3", cmd, 8))
-	{
-	    if (*cur)
-		(*cur)->use_sslv3 = (strcasecmp (val, "yes") == 0);
-	    else
-		global.use_sslv3 = (strcasecmp (val, "yes") == 0);
-	}
-	else if (!strncasecmp ("UseTLSv1", cmd, 8))
-	{
-	    if (*cur)
-		(*cur)->use_tlsv1 = (strcasecmp (val, "yes") == 0);
-	    else
-		global.use_tlsv1 = (strcasecmp (val, "yes") == 0);
-	}
-	else if (!strncasecmp ("RequireCRAM", cmd, 11))
-	{
-	    if (*cur)
-		(*cur)->require_cram = (strcasecmp (val, "yes") == 0);
-	    else
-		global.require_cram = (strcasecmp (val, "yes") == 0);
-	}
-#endif
-	else if (buf[0])
-	    printf ("%s:%d:unknown command:%s", path, line, cmd);
-    }
-    fclose (fp);
-}
-
-static config_t *
-find_box (const char *s)
-{
-    config_t *p = box;
-
-    for (; p; p = p->next)
-	if (!strcmp (s, p->path) || (p->alias && !strcmp (s, p->alias)))
-	    return p;
-    return 0;
-}
-
 char *
 next_arg (char **s)
 {
@@ -298,13 +100,25 @@ next_arg (char **s)
 	*s = 0;
 	return 0;
     }
-    ret = *s;
-    while (**s && !isspace ((unsigned char) **s))
-	(*s)++;
-    if (**s)
-	*(*s)++ = 0;
-    if (!**s)
-	*s = 0;
+    if (**s == '"')
+    {
+	++*s;
+	ret = *s;
+	*s = strchr (*s, '"');
+    }
+    else
+    {
+	ret = *s;
+	while (**s && !isspace ((unsigned char) **s))
+	    (*s)++;
+    }
+    if (*s)
+    {
+	if (**s)
+	    *(*s)++ = 0;
+	if (!**s)
+	    *s = 0;
+    }
     return ret;
 }
 
@@ -441,7 +255,7 @@ main (int argc, char **argv)
 
 	puts ("Synchronizing");
 	i = delete ? SYNC_DELETE : 0;
-	i |= expunge ? SYNC_EXPUNGE : 0;
+	i |= (expunge || box->expunge) ? SYNC_EXPUNGE : 0;
 	if (sync_mailbox (mail, imap, i, box->max_size))
 	    exit (1);
 

+ 104 - 13
sync.c

@@ -26,6 +26,7 @@
 #include <fcntl.h>
 #include <string.h>
 #include <errno.h>
+#include <sys/stat.h>
 #include "isync.h"
 
 static unsigned int MaildirCount = 0;
@@ -40,7 +41,8 @@ find_msg (message_t * list, unsigned int uid)
 }
 
 int
-sync_mailbox (mailbox_t * mbox, imap_t * imap, int flags, unsigned int max_size)
+sync_mailbox (mailbox_t * mbox, imap_t * imap, int flags,
+	      unsigned int max_size)
 {
     message_t *cur;
     message_t *tmp;
@@ -83,17 +85,104 @@ sync_mailbox (mailbox_t * mbox, imap_t * imap, int flags, unsigned int max_size)
 	tmp = find_msg (imap->msgs, cur->uid);
 	if (!tmp)
 	{
-	    printf ("Warning, uid %d doesn't exist on server\n", cur->uid);
-	    if (flags & SYNC_DELETE)
+	    /* if this message wasn't fetched from the server, attempt to
+	     * upload it
+	     */
+	    if (cur->uid == (unsigned int) -1)
 	    {
-		cur->flags |= D_DELETED;
-		cur->dead = 1;
-		mbox->deleted++;
+		struct stat sb;
+		int fd;
+		int uid;
+
+		/* upload the message if its not too big */
+		snprintf (path, sizeof (path), "%s/%s/%s", mbox->path,
+			  cur->new ? "new" : "cur", cur->file);
+		if (stat (path, &sb))
+		{
+		    printf ("Error, unable to stat %s: %s (errno %d)\n",
+			    path, strerror (errno), errno);
+
+		    continue;	/* not fatal */
+		}
+		if (sb.st_size > imap->box->max_size)
+		{
+		    printf
+			("Warning, local message is too large (%ld), skipping...\n",
+			 sb.st_size);
+		    continue;
+		}
+		fd = open (path, O_RDONLY);
+		if (fd == -1)
+		{
+		    printf ("Error, unable to open %s: %s (errno %d)\n",
+			    path, strerror (errno), errno);
+		    continue;
+		}
+
+		cur->size = sb.st_size;
+
+		uid = imap_append_message (imap, fd, cur);
+
+		close (fd);
+
+		/* if the server gave us back a uid, rename the file so
+		 * we remember for next time
+		 */
+		if (uid != -1)
+		{
+		    char newpath[_POSIX_PATH_MAX];
+		    char *p;
+
+		    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
+	    {
+		printf ("Warning, uid %u doesn't exist on server\n",
+			cur->uid);
+		if (flags & SYNC_DELETE)
+		{
+		    cur->flags |= D_DELETED;
+		    cur->dead = 1;
+		    mbox->deleted++;
+		}
 	    }
 	    continue;
 	}
 	tmp->processed = 1;
 
+	/* if the message is deleted, and CopyDeletedTo is set, and we
+	 * are expunging, make a copy of the message now.
+	 */
+	if (((cur->flags | tmp->flags) & D_DELETED) != 0 &&
+	    (flags & SYNC_EXPUNGE) && imap->box->copy_deleted_to)
+	{
+	    if (imap_copy_message (imap, cur->uid,
+				   imap->box->copy_deleted_to))
+	    {
+		printf ("Error, unable to copy deleted message to \"%s\"\n",
+			imap->box->copy_deleted_to);
+		return -1;
+	    }
+	}
+
 	/* check if local flags are different from server flags.
 	 * ignore \Recent and \Draft
 	 */
@@ -102,10 +191,11 @@ sync_mailbox (mailbox_t * mbox, imap_t * imap, int flags, unsigned int max_size)
 	    /* set local flags that don't exist on the server */
 	    if (!(tmp->flags & D_DELETED) && (cur->flags & D_DELETED))
 		imap->deleted++;
+
 	    imap_set_flags (imap, cur->uid, cur->flags & ~tmp->flags);
 
 	    /* update local flags */
-	    if((cur->flags & D_DELETED) == 0 && (tmp->flags & D_DELETED))
+	    if ((cur->flags & D_DELETED) == 0 && (tmp->flags & D_DELETED))
 		mbox->deleted++;
 	    cur->flags |= (tmp->flags & ~(D_RECENT | D_DRAFT));
 	    cur->changed = 1;
@@ -132,8 +222,9 @@ sync_mailbox (mailbox_t * mbox, imap_t * imap, int flags, unsigned int max_size)
 
 	    if (max_size && cur->size > max_size)
 	    {
-		printf ("Warning, message skipped because it is too big (%u)\n",
-			cur->size);
+		printf
+		    ("Warning, message skipped because it is too big (%u)\n",
+		     cur->size);
 		continue;
 	    }
 
@@ -148,13 +239,13 @@ sync_mailbox (mailbox_t * mbox, imap_t * imap, int flags, unsigned int max_size)
 			  (cur->flags & D_SEEN) ? "S" : "",
 			  (cur->flags & D_DELETED) ? "T" : "");
 	    }
-	    
+
 	    for (;;)
 	    {
 		/* create new file */
 		snprintf (path, sizeof (path), "%s/tmp/%s.%ld_%d.%d,U=%d%s",
-			mbox->path, Hostname, time (0), MaildirCount++,
-			getpid (), cur->uid, suffix);
+			  mbox->path, Hostname, time (0), MaildirCount++,
+			  getpid (), cur->uid, suffix);
 
 		if ((fd = open (path, O_WRONLY | O_CREAT | O_EXCL, 0600)) > 0)
 		    break;
@@ -184,7 +275,7 @@ sync_mailbox (mailbox_t * mbox, imap_t * imap, int flags, unsigned int max_size)
 		p = strrchr (path, '/');
 
 		snprintf (newpath, sizeof (newpath), "%s/%s%s", mbox->path,
-			(cur->flags & D_SEEN) ? "cur" : "new", p);
+			  cur->flags ? "cur" : "new", p);
 
 		/* its ok if this fails, the next time we sync the message
 		 * will get pulled down