isync

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

commit eb1f10762f63f02a941d2af5a9388b7107b03163
parent 6577bf3e6113962f464745c8d955f0194f773a8b
Author: Oswald Buddenhagen <ossi@users.sf.net>
Date:   Sun, 28 Jul 2013 15:55:13 +0200

added sync support for the arrival date of messages

initial patch by Marc Hoersken <info@marc-hoersken.de>

Diffstat:
Mconfigure.ac | 17+++++++++++++++++
Msrc/config.c | 2++
Msrc/drv_imap.c | 38+++++++++++++++++++++++++++++++++-----
Msrc/drv_maildir.c | 15+++++++++++++++
Msrc/isync.h | 2++
Msrc/mbsync.1 | 10++++++++++
Msrc/sync.c | 1+
7 files changed, 80 insertions(+), 5 deletions(-)

diff --git a/configure.ac b/configure.ac @@ -11,6 +11,23 @@ fi CPPFLAGS="$CPPFLAGS -D_GNU_SOURCE" +AC_CACHE_CHECK([whether strftime supports %z], ob_cv_strftime_z, + [AC_TRY_RUN( +[#include <time.h> +#include <string.h> + +int main(void) +{ + time_t t = 0; + char buf[32]; + strftime(buf, sizeof(buf), "%z", gmtime(&t)); + return !!strcmp(buf, "+0000"); +} +], [ob_cv_strftime_z=yes], [ob_cv_strftime_z=no], [ob_cv_strftime_z="yes (assumed)"])]) +if test "x$ob_cv_strftime_z" = x"no"; then + AC_MSG_ERROR([libc lacks necessary feature]) +fi + AC_CHECK_HEADERS(sys/poll.h sys/select.h) AC_CHECK_FUNCS(vasprintf memrchr) diff --git a/src/config.c b/src/config.c @@ -362,6 +362,8 @@ load_config( const char *where, int pseudo ) max_size = parse_size( &cfile ); else if (!strcasecmp( "MaxMessages", cfile.cmd )) channel->max_messages = parse_int( &cfile ); + else if (!strcasecmp( "CopyArrivalDate", cfile.cmd )) + channel->use_internal_date = parse_bool( &cfile ); else if (!strcasecmp( "Pattern", cfile.cmd ) || !strcasecmp( "Patterns", cfile.cmd )) { diff --git a/src/drv_imap.c b/src/drv_imap.c @@ -31,6 +31,7 @@ #include <limits.h> #include <string.h> #include <ctype.h> +#include <time.h> #include <sys/wait.h> typedef struct imap_server_conf { @@ -710,6 +711,8 @@ parse_fetch_rsp( imap_store_t *ctx, list_t *list, char *s ATTR_UNUSED ) struct imap_cmd *cmdp; int uid = 0, mask = 0, status = 0, size = 0; unsigned i; + time_t date = 0; + struct tm datetime; if (!is_list( list )) { error( "IMAP error: bogus FETCH response\n" ); @@ -751,6 +754,15 @@ parse_fetch_rsp( imap_store_t *ctx, list_t *list, char *s ATTR_UNUSED ) status |= M_FLAGS; } else error( "IMAP error: unable to parse FLAGS\n" ); + } else if (!strcmp( "INTERNALDATE", tmp->val )) { + tmp = tmp->next; + if (is_atom( tmp )) { + if (strptime( tmp->val, "%d-%b-%Y %H:%M:%S %z", &datetime )) + date = mktime( &datetime ); + else + error( "IMAP error: unable to parse INTERNALDATE format\n" ); + } else + error( "IMAP error: unable to parse INTERNALDATE\n" ); } else if (!strcmp( "RFC822.SIZE", tmp->val )) { tmp = tmp->next; if (is_atom( tmp )) @@ -794,6 +806,7 @@ parse_fetch_rsp( imap_store_t *ctx, list_t *list, char *s ATTR_UNUSED ) msgdata = ((struct imap_cmd_fetch_msg *)cmdp)->msg_data; msgdata->data = body; msgdata->len = size; + msgdata->date = date; if (status & M_FLAGS) msgdata->flags = mask; } else if (uid) { /* ignore async flag updates for now */ @@ -1738,8 +1751,9 @@ imap_fetch_msg( store_t *ctx, message_t *msg, msg_data_t *data, cmd->gen.gen.param.uid = msg->uid; cmd->msg_data = data; imap_exec( (imap_store_t *)ctx, &cmd->gen.gen, imap_done_simple_msg, - "UID FETCH %d (%sBODY.PEEK[])", - msg->uid, (msg->status & M_FLAGS) ? "" : "FLAGS " ); + "UID FETCH %d (%s%sBODY.PEEK[])", msg->uid, + !(msg->status & M_FLAGS) ? "FLAGS " : "", + (data->date== -1) ? "INTERNALDATE " : "" ); } /******************* imap_set_flags *******************/ @@ -1888,7 +1902,7 @@ imap_store_msg( store_t *gctx, msg_data_t *data, int to_trash, imap_store_t *ctx = (imap_store_t *)gctx; struct imap_cmd_out_uid *cmd; int d; - char flagstr[128], buf[1024]; + char flagstr[128], datestr[64], buf[1024]; d = 0; if (data->flags) { @@ -1915,8 +1929,22 @@ imap_store_msg( store_t *gctx, msg_data_t *data, int to_trash, return; } } - imap_exec( ctx, &cmd->gen, imap_store_msg_p2, - "APPEND \"%s\" %s", buf, flagstr ); + if (data->date) { +#ifdef __GNUC__ +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wformat" + /* configure ensures that %z actually works. */ +#endif + strftime( datestr, sizeof(datestr), "%d-%b-%Y %H:%M:%S %z", localtime( &data->date ) ); +#ifdef __GNUC__ +# pragma GCC diagnostic pop +#endif + imap_exec( ctx, &cmd->gen, imap_store_msg_p2, + "APPEND \"%s\" %s\"%s\" ", buf, flagstr, datestr ); + } else { + imap_exec( ctx, &cmd->gen, imap_store_msg_p2, + "APPEND \"%s\" %s", buf, flagstr ); + } } static void diff --git a/src/drv_maildir.c b/src/drv_maildir.c @@ -36,6 +36,7 @@ #include <sys/file.h> #include <errno.h> #include <time.h> +#include <utime.h> #define USE_DB 1 #ifdef __linux__ @@ -1124,6 +1125,8 @@ maildir_fetch_msg( store_t *gctx, message_t *gmsg, msg_data_t *data, } fstat( fd, &st ); data->len = st.st_size; + if (data->date == -1) + data->date = st.st_mtime; data->data = nfmalloc( data->len ); if (read( fd, data->data, data->len ) != data->len) { sys_error( "Maildir error: cannot read %s", buf ); @@ -1225,6 +1228,18 @@ maildir_store_msg( store_t *gctx, msg_data_t *data, int to_trash, cb( DRV_BOX_BAD, 0, aux ); return; } + + if (data->date) { + /* Set atime and mtime according to INTERNALDATE or mtime of source message */ + struct utimbuf utimebuf; + utimebuf.actime = utimebuf.modtime = data->date; + if (utime( buf, &utimebuf ) < 0) { + sys_error( "Maildir error: cannot set times for %s", buf ); + cb( DRV_BOX_BAD, 0, aux ); + return; + } + } + /* Moving seen messages to cur/ is strictly speaking incorrect, but makes mutt happy. */ nfsnprintf( nbuf, sizeof(nbuf), "%s/%s/%s%s", box, subdirs[!(data->flags & F_SEEN)], base, fbuf ); if (rename( buf, nbuf )) { diff --git a/src/isync.h b/src/isync.h @@ -166,6 +166,7 @@ typedef struct channel_conf { string_list_t *patterns; int ops[2]; unsigned max_messages; /* for slave only */ + unsigned use_internal_date:1; } channel_conf_t; typedef struct group_conf { @@ -245,6 +246,7 @@ set_bad_callback( store_t *ctx, void (*cb)( void *aux ), void *aux ) typedef struct { char *data; int len; + time_t date; unsigned char flags; } msg_data_t; diff --git a/src/mbsync.1 b/src/mbsync.1 @@ -477,6 +477,16 @@ a global effect. The global settings are overridden by Channel-specific options, which in turn are overridden by command line switches. .. .TP +\fBCopyArrivalDate\fR {\fIyes\fR|\fIno\fR} +Selects whether their arrival time should be propagated together with +the messages. +Enabling this makes sense in order to keep the time stamp based message +sorting intact. +Note that IMAP does not guarantee that the time stamp (termed \fBinternal +date\fR) is actually the arrival time, but it is usually close enough. +(Default: \fIno\fR) +.. +.TP \fBSyncState\fR {\fB*\fR|\fIpath\fR} Set the location of this Channel's synchronization state files. \fB*\fR means that the state should be saved in a file named .mbsyncstate in the diff --git a/src/sync.c b/src/sync.c @@ -281,6 +281,7 @@ copy_msg( copy_vars_t *vars ) t ^= 1; vars->data.flags = vars->msg->flags; + vars->data.date = svars->chan->use_internal_date ? -1 : 0; DRIVER_CALL_RET(fetch_msg( svars->ctx[t], vars->msg, &vars->data, msg_fetched, vars )); }