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:
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 ));
}