Hi all,
Currently the duplicate suppression mechanism of cyrus-imapd is only based on
the Message-Id and the To field of the email header. Some email clients reuse
the Message-Id if you resend the same email later on, so the resent email is
going to be suppressed by cyrus.
The solution for this is to not only store the message-id/mailbox pair in the
duplicate delivery database, but also the Date field of the given email (so the
message-id/mailbox/date triple is going to be stored). You can find a patch
attached, which applies to cyrus-imapd-2.4.10 and solves the problem. This is
my first open source contribution, please review my code and tell how I should
proceed.
I would like to also add, that the altered code paths are all tested (all the
modified functions are called at least once during the tests), and a patched
cyrus-imapd is currently running on a productive mail server.
Best regards,
Kristóf Katus
diff --git a/datastore/dev/rpmbuild/BUILD/cyrus-imapd-2.4.10/imap/duplicate.c b/GIT/TEMP/cyrus-imapd-2.4.10/imap/duplicate.c
index 3a72c43..4ac5342 100644
--- a/datastore/dev/rpmbuild/BUILD/cyrus-imapd-2.4.10/imap/duplicate.c
+++ b/GIT/TEMP/cyrus-imapd-2.4.10/imap/duplicate.c
@@ -121,9 +121,12 @@ int duplicate_init(const char *fname, int myflags __attribute__((unused)))
return r;
}
-time_t duplicate_check(char *id, int idlen, const char *to, int tolen)
+time_t duplicate_check(duplicate_key_t *dkey)
{
char buf[1024];
+ int idlen = strlen(dkey->id);
+ int tolen = strlen(dkey->to);
+ int datelen = strlen(dkey->date);
int r;
const char *data = NULL;
int len = 0;
@@ -131,16 +134,18 @@ time_t duplicate_check(char *id, int idlen, const char *to, int tolen)
if (!duplicate_dbopen) return 0;
- if (idlen + tolen > (int) sizeof(buf) - 30) return 0;
- memcpy(buf, id, idlen);
+ if (idlen + tolen + datelen > (int) sizeof(buf) - 30) return 0;
+ memcpy(buf, dkey->id, idlen);
buf[idlen] = '\0';
- memcpy(buf + idlen + 1, to, tolen);
+ memcpy(buf + idlen + 1, dkey->to, tolen);
buf[idlen + tolen + 1] = '\0';
+ assert(dkey->date != NULL);
+ memcpy(buf + idlen + tolen + 2, dkey->date, datelen);
+ buf[idlen + tolen + datelen + 2] = '\0';
do {
r = DB->fetch(dupdb, buf,
- idlen + tolen + 2, /* +2 b/c 1 for the center null;
- +1 for the terminating null */
+ idlen + tolen + datelen + 3, /* We have three concatenated values now, all parts ending with '\0' */
&data, &len, NULL);
} while (r == CYRUSDB_AGAIN);
@@ -152,54 +157,59 @@ time_t duplicate_check(char *id, int idlen, const char *to, int tolen)
memcpy(&mark, data, sizeof(time_t));
} else if (r != CYRUSDB_OK) {
if (r != CYRUSDB_NOTFOUND) {
- syslog(LOG_ERR, "duplicate_check: error looking up %s/%s: %s",
- id, to,
- cyrusdb_strerror(r));
+ syslog(LOG_ERR, "duplicate_check: error looking up %s/%s/%s: %s",
+ dkey->id, dkey->to, dkey->date,
+ cyrusdb_strerror(r));
}
mark = 0;
}
- syslog(LOG_DEBUG, "duplicate_check: %-40s %-20s %ld",
- buf, buf+idlen+1, mark);
+ syslog(LOG_DEBUG, "duplicate_check: %-40s %-20s %-40s %ld",
+ buf, buf+idlen+1, buf+idlen+tolen+2, mark);
return mark;
}
-void duplicate_log(char *msgid, const char *name, char *action)
+void duplicate_log(duplicate_key_t *dkey, char *action)
{
- syslog(LOG_INFO, "dupelim: eliminated duplicate message to %s id %s (%s)",
- name, msgid, action);
+ assert(dkey->date != NULL);
+ syslog(LOG_INFO, "dupelim: eliminated duplicate message to %s id %s date %s (%s)",
+ dkey->to, dkey->id, dkey->date, action);
if (config_auditlog)
- syslog(LOG_NOTICE, "auditlog: duplicate sessionid=<%s> action=<%s> message-id=%s user=<%s>",
- session_id(), action, msgid, name);
+ syslog(LOG_NOTICE, "auditlog: duplicate sessionid=<%s> action=<%s> message-id=%s user=<%s> date=<%s>",
+ session_id(), action, dkey->id, dkey->to, dkey->date);
}
-void duplicate_mark(char *id, int idlen, const char *to, int tolen, time_t mark,
- unsigned long uid)
+void duplicate_mark(duplicate_key_t *dkey, time_t mark, unsigned long uid)
{
char buf[1024], data[100];
+ int idlen = strlen(dkey->id);
+ int tolen = strlen(dkey->to);
+ int datelen = strlen(dkey->date);
int r;
if (!duplicate_dbopen) return;
- if (idlen + tolen > (int) sizeof(buf) - 30) return;
- memcpy(buf, id, idlen);
+ if (idlen + tolen + datelen > (int) sizeof(buf) - 30) return;
+ memcpy(buf, dkey->id, idlen);
buf[idlen] = '\0';
- memcpy(buf + idlen + 1, to, tolen);
+ memcpy(buf + idlen + 1, dkey->to, tolen);
buf[idlen + tolen + 1] = '\0';
+ assert(dkey->date != NULL);
+ memcpy(buf + idlen + tolen + 2, dkey->date, datelen);
+ buf[idlen + tolen + datelen + 2] = '\0';
memcpy(data, &mark, sizeof(mark));
memcpy(data + sizeof(mark), &uid, sizeof(uid));
do {
r = DB->store(dupdb, buf,
- idlen + tolen + 2, /* +2 b/c 1 for the center null;
- +1 for the terminating null */
+ idlen + tolen + datelen + 3, /* We have three concatenated values now, all parts ending with '\0' */
data, sizeof(mark)+sizeof(uid), NULL);
} while (r == CYRUSDB_AGAIN);
- syslog(LOG_DEBUG, "duplicate_mark: %-40s %-20s %ld %lu",
- buf, buf+idlen+1, mark, uid);
+ syslog(LOG_DEBUG, "duplicate_mark: %-40s %-20s %-40s %ld %lu",
+ buf, buf+idlen+1, buf+idlen+tolen+2, mark, uid);
return;
}
diff --git a/datastore/dev/rpmbuild/BUILD/cyrus-imapd-2.4.10/imap/duplicate.h b/GIT/TEMP/cyrus-imapd-2.4.10/imap/duplicate.h
index d1d8737..8a36a89 100644
--- a/datastore/dev/rpmbuild/BUILD/cyrus-imapd-2.4.10/imap/duplicate.h
+++ b/GIT/TEMP/cyrus-imapd-2.4.10/imap/duplicate.h
@@ -49,12 +49,17 @@
/* name of the duplicate delivery database */
#define FNAME_DELIVERDB "/deliver.db"
+typedef struct duplicate_key {
+ char *id;
+ char *to;
+ char *date;
+} duplicate_key_t;
+
int duplicate_init(const char *fname, int myflags);
-time_t duplicate_check(char *id, int idlen, const char *to, int tolen);
-void duplicate_log(char *msgid, const char *name, char *action);
-void duplicate_mark(char *id, int idlen, const char *to, int tolen, time_t mark,
- unsigned long uid);
+time_t duplicate_check(duplicate_key_t *dkey);
+void duplicate_log(duplicate_key_t *dkey, char *action);
+void duplicate_mark(duplicate_key_t *dkey, time_t mark, unsigned long uid);
int duplicate_find(char *msgid, int (*proc)(), void *rock);
int duplicate_prune(int seconds, struct hash_table *expire_table);
diff --git a/datastore/dev/rpmbuild/BUILD/cyrus-imapd-2.4.10/imap/lmtp_sieve.c b/GIT/TEMP/cyrus-imapd-2.4.10/imap/lmtp_sieve.c
index 379befd..4fb191c 100644
--- a/datastore/dev/rpmbuild/BUILD/cyrus-imapd-2.4.10/imap/lmtp_sieve.c
+++ b/GIT/TEMP/cyrus-imapd-2.4.10/imap/lmtp_sieve.c
@@ -225,6 +225,7 @@ static int send_rejection(const char *origid,
time_t t;
char datestr[80];
pid_t sm_pid, p;
+ duplicate_key_t dkey = {NULL, NULL, NULL};
smbuf[0] = "sendmail";
smbuf[1] = "-i"; /* ignore dots */
@@ -244,10 +245,13 @@ static int send_rejection(const char *origid,
global_outgoing_count++, config_servername);
namebuf = make_sieve_db(mailreceip);
- duplicate_mark(buf, strlen(buf), namebuf, strlen(namebuf), t, 0);
- fprintf(sm, "Message-ID: %s\r\n", buf);
+ dkey.id = buf;
+ dkey.to = namebuf;
rfc822date_gen(datestr, sizeof(datestr), t);
+ dkey.date = datestr;
+ duplicate_mark(&dkey, t, 0);
+ fprintf(sm, "Message-ID: %s\r\n", buf);
fprintf(sm, "Date: %s\r\n", datestr);
fprintf(sm, "X-Sieve: %s\r\n", SIEVE_VERSION);
@@ -368,6 +372,7 @@ static int sieve_redirect(void *ac,
script_data_t *sd = (script_data_t *) sc;
message_data_t *m = ((deliver_data_t *) mc)->m;
char buf[8192], *sievedb = NULL;
+ duplicate_key_t dkey = {NULL, NULL, NULL};
int res;
/* if we have a msgid, we can track our redirects */
@@ -375,17 +380,19 @@ static int sieve_redirect(void *ac,
snprintf(buf, sizeof(buf), "%s-%s", m->id, rc->addr);
sievedb = make_sieve_db(sd->username);
+ dkey.id = buf;
+ dkey.to = sievedb;
+ dkey.date = ((deliver_data_t *) mc)->m->date;
/* ok, let's see if we've redirected this message before */
- if (duplicate_check(buf, strlen(buf), sievedb, strlen(sievedb))) {
- duplicate_log(m->id, sd->username, "redirect");
+ if (duplicate_check(&dkey)) {
+ duplicate_log(&dkey, "redirect");
return SIEVE_OK;
}
}
if ((res = send_forward(rc->addr, m->return_path, m->data)) == 0) {
/* mark this message as redirected */
- if (sievedb) duplicate_mark(buf, strlen(buf),
- sievedb, strlen(sievedb), time(NULL), 0);
+ if (sievedb) duplicate_mark(&dkey, time(NULL), 0);
snmp_increment(SIEVE_REDIRECT, 1);
syslog(LOG_INFO, "sieve redirected: %s to: %s",
@@ -494,7 +501,7 @@ static int sieve_fileinto(void *ac,
fc->imapflags->flag, fc->imapflags->nflags,
(char *) sd->username, sd->authstate, md->id,
sd->username, mdata->notifyheader,
- namebuf, quotaoverride, 0);
+ namebuf, md->date, quotaoverride, 0);
}
if (!ret) {
@@ -564,14 +571,19 @@ static int autorespond(void *ac,
script_data_t *sd = (script_data_t *) sc;
time_t t, now;
int ret;
+ duplicate_key_t dkey = {NULL, NULL, NULL};
snmp_increment(SIEVE_VACATION_TOTAL, 1);
now = time(NULL);
/* ok, let's see if we've responded before */
- t = duplicate_check((char *) arc->hash, SIEVE_HASHLEN,
- sd->username, strlen(sd->username));
+ dkey.id = xmalloc(SIEVE_HASHLEN + 1);
+ memcpy(dkey.id, (char *) arc->hash, SIEVE_HASHLEN);
+ dkey.id[SIEVE_HASHLEN] = '\0';
+ dkey.to = sd->username;
+ dkey.date = ((deliver_data_t *) mc)->m->date;
+ t = duplicate_check(&dkey);
if (t) {
if (now >= t) {
/* yay, we can respond again! */
@@ -585,11 +597,10 @@ static int autorespond(void *ac,
}
if (ret == SIEVE_OK) {
- duplicate_mark((char *) arc->hash, SIEVE_HASHLEN,
- sd->username, strlen(sd->username),
- now + arc->days * (24 * 60 * 60), 0);
+ duplicate_mark(&dkey, now + arc->days * (24 * 60 * 60), 0);
}
+ if (dkey.id != NULL) free(dkey.id);
return ret;
}
@@ -607,6 +618,7 @@ static int send_response(void *ac,
sieve_send_response_context_t *src = (sieve_send_response_context_t *) ac;
message_data_t *md = ((deliver_data_t *) mc)->m;
script_data_t *sdata = (script_data_t *) sc;
+ duplicate_key_t dkey = {NULL, NULL, NULL};
smbuf[0] = "sendmail";
smbuf[1] = "-i"; /* ignore dots */
@@ -667,8 +679,10 @@ static int send_response(void *ac,
if (sm_stat == 0) { /* sendmail exit value */
sievedb = make_sieve_db(sdata->username);
- duplicate_mark(outmsgid, strlen(outmsgid),
- sievedb, strlen(sievedb), t, 0);
+ dkey.id = outmsgid;
+ dkey.to = sievedb;
+ dkey.date = ((deliver_data_t *) mc)->m->date;
+ duplicate_mark(&dkey, t, 0);
snmp_increment(SIEVE_VACATION_REPLIED, 1);
@@ -879,6 +893,7 @@ int run_sieve(const char *user, const char *domain, const char *mailbox,
char userbuf[MAX_MAILBOX_BUFFER] = "";
char authuserbuf[MAX_MAILBOX_BUFFER];
int r = 0;
+ duplicate_key_t dkey = {NULL, NULL, NULL};
if (!user) {
/* shared mailbox, check for annotation */
@@ -935,8 +950,10 @@ int run_sieve(const char *user, const char *domain, const char *mailbox,
domain ? domain : "");
sdb = make_sieve_db(namebuf);
- duplicate_mark(msgdata->m->id, strlen(msgdata->m->id),
- sdb, strlen(sdb), time(NULL), 0);
+ dkey.id = msgdata->m->id;
+ dkey.to = sdb;
+ dkey.date = msgdata->m->date;
+ duplicate_mark(&dkey, time(NULL), 0);
}
/* free everything */
diff --git a/datastore/dev/rpmbuild/BUILD/cyrus-imapd-2.4.10/imap/lmtpd.c b/GIT/TEMP/cyrus-imapd-2.4.10/imap/lmtpd.c
index ed34c7c..ca35331 100644
--- a/datastore/dev/rpmbuild/BUILD/cyrus-imapd-2.4.10/imap/lmtpd.c
+++ b/GIT/TEMP/cyrus-imapd-2.4.10/imap/lmtpd.c
@@ -496,6 +496,7 @@ int deliver_mailbox(FILE *f,
const char *user,
char *notifyheader,
const char *mailboxname,
+ char *date,
int quotaoverride,
int acloverride)
{
@@ -503,6 +504,7 @@ int deliver_mailbox(FILE *f,
struct appendstate as;
unsigned long uid;
const char *notifier;
+ duplicate_key_t dkey = {NULL, NULL, NULL};
r = append_setup(&as, mailboxname,
authuser, authstate, acloverride ? 0 : ACL_POST,
@@ -511,9 +513,12 @@ int deliver_mailbox(FILE *f,
(long) size : 0);
/* check for duplicate message */
+ dkey.id = id;
+ dkey.to = mailboxname;
+ dkey.date = date;
if (!r && id && dupelim && !(as.mailbox->i.options & OPT_IMAP_DUPDELIVER) &&
- duplicate_check(id, strlen(id), mailboxname, strlen(mailboxname))) {
- duplicate_log(id, mailboxname, "delivery");
+ duplicate_check(&dkey)) {
+ duplicate_log(&dkey, "delivery");
append_abort(&as);
return 0;
}
@@ -539,8 +544,7 @@ int deliver_mailbox(FILE *f,
syslog(LOG_INFO, "Delivered: %s to mailbox: %s",
id, mailboxname);
if (dupelim && id) {
- duplicate_mark(id, strlen(id), mailboxname,
- strlen(mailboxname), time(NULL), uid);
+ duplicate_mark(&dkey, time(NULL), uid);
}
mailbox_close(&mailbox);
}
@@ -690,7 +694,7 @@ int deliver_local(deliver_data_t *mydata, char **flag, int nflags,
md->size, flag, nflags,
mydata->authuser, mydata->authstate, md->id,
NULL, mydata->notifyheader,
- namebuf, quotaoverride, 0);
+ namebuf, md->date, quotaoverride, 0);
}
/* case 2: ordinary user */
@@ -710,7 +714,7 @@ int deliver_local(deliver_data_t *mydata, char **flag, int nflags,
md->size, flag, nflags,
mydata->authuser, mydata->authstate, md->id,
username, mydata->notifyheader,
- namebuf, quotaoverride, 0);
+ namebuf, md->date, quotaoverride, 0);
}
if (ret2 == IMAP_MAILBOX_NONEXISTENT && mailboxname &&
config_getswitch(IMAPOPT_LMTP_FUZZY_MAILBOX_MATCH) &&
@@ -720,7 +724,7 @@ int deliver_local(deliver_data_t *mydata, char **flag, int nflags,
md->size, flag, nflags,
mydata->authuser, mydata->authstate, md->id,
username, mydata->notifyheader,
- namebuf, quotaoverride, 0);
+ namebuf, md->date, quotaoverride, 0);
}
if (ret2) {
/* normal delivery to INBOX */
@@ -732,7 +736,7 @@ int deliver_local(deliver_data_t *mydata, char **flag, int nflags,
md->size, flag, nflags,
(char *) username, authstate, md->id,
username, mydata->notifyheader,
- namebuf, quotaoverride, 1);
+ namebuf, md->date, quotaoverride, 1);
if (authstate) auth_freestate(authstate);
}
diff --git a/datastore/dev/rpmbuild/BUILD/cyrus-imapd-2.4.10/imap/lmtpd.h b/GIT/TEMP/cyrus-imapd-2.4.10/imap/lmtpd.h
index d865777..7561a5a 100644
--- a/datastore/dev/rpmbuild/BUILD/cyrus-imapd-2.4.10/imap/lmtpd.h
+++ b/GIT/TEMP/cyrus-imapd-2.4.10/imap/lmtpd.h
@@ -89,6 +89,7 @@ extern int deliver_mailbox(FILE *f,
const char *user,
char *notifyheader,
const char *mailboxname,
+ char *date,
int quotaoverride,
int acloverride);
diff --git a/datastore/dev/rpmbuild/BUILD/cyrus-imapd-2.4.10/imap/lmtpengine.c b/GIT/TEMP/cyrus-imapd-2.4.10/imap/lmtpengine.c
index 3005ed6..f0357be 100644
--- a/datastore/dev/rpmbuild/BUILD/cyrus-imapd-2.4.10/imap/lmtpengine.c
+++ b/GIT/TEMP/cyrus-imapd-2.4.10/imap/lmtpengine.c
@@ -282,6 +282,7 @@ int msg_new(message_data_t **m)
ret->return_path = NULL;
ret->rcpt = NULL;
ret->rcpt_num = 0;
+ ret->date = NULL;
ret->authuser = NULL;
ret->authstate = NULL;
@@ -319,6 +320,9 @@ void msg_free(message_data_t *m)
}
free(m->rcpt);
}
+ if (m->date) {
+ free(m->date);
+ }
if (m->authuser) {
free(m->authuser);
@@ -741,12 +745,16 @@ static int savemsg(struct clientdata *cd,
}
/* get date */
- if (!spool_getheader(m->hdrcache, "date")) {
+ if (!(body = spool_getheader(m->hdrcache, "date"))) {
/* no date, create one */
addbody = xstrdup(datestr);
+ m->date = xstrdup(datestr);
fprintf(f, "Date: %s\r\n", addbody);
spool_cache_header(xstrdup("Date"), addbody, m->hdrcache);
}
+ else {
+ m->date = xstrdup(body[0]);
+ }
if (!m->return_path &&
(body = msg_getheader(m, "return-path"))) {
diff --git a/datastore/dev/rpmbuild/BUILD/cyrus-imapd-2.4.10/imap/lmtpengine.h b/GIT/TEMP/cyrus-imapd-2.4.10/imap/lmtpengine.h
index c3585da..16b6bc3 100644
--- a/datastore/dev/rpmbuild/BUILD/cyrus-imapd-2.4.10/imap/lmtpengine.h
+++ b/GIT/TEMP/cyrus-imapd-2.4.10/imap/lmtpengine.h
@@ -65,6 +65,7 @@ struct message_data {
char *return_path; /* where to return message */
address_data_t **rcpt; /* to recipients of this message */
int rcpt_num; /* number of recipients */
+ char *date; /* date field of header */
/* auth state */
char *authuser;
diff --git a/datastore/dev/rpmbuild/BUILD/cyrus-imapd-2.4.10/imap/nntpd.c b/GIT/TEMP/cyrus-imapd-2.4.10/imap/nntpd.c
index 9f6431d..f749449 100644
--- a/datastore/dev/rpmbuild/BUILD/cyrus-imapd-2.4.10/imap/nntpd.c
+++ b/GIT/TEMP/cyrus-imapd-2.4.10/imap/nntpd.c
@@ -2874,6 +2874,7 @@ struct message_data {
char **rcpt; /* mailboxes to post message */
int rcpt_num; /* number of groups */
+ char *date; /* date field of header */
hdrcache_t hdrcache;
};
@@ -2891,6 +2892,7 @@ int msg_new(message_data_t **m)
ret->size = 0;
ret->rcpt = NULL;
ret->rcpt_num = 0;
+ ret->date = NULL;
ret->hdrcache = spool_new_hdrcache();
@@ -2924,6 +2926,9 @@ void msg_free(message_data_t *m)
}
free(m->rcpt);
}
+ if (m->date) {
+ free(m->date);
+ }
spool_free_hdrcache(m->hdrcache);
@@ -3047,9 +3052,13 @@ static int savemsg(message_data_t *m, FILE *f)
char datestr[80];
rfc822date_gen(datestr, sizeof(datestr), now);
+ m->date = xstrdup(datestr);
fprintf(f, "Date: %s\r\n", datestr);
spool_cache_header(xstrdup("Date"), xstrdup(datestr), m->hdrcache);
}
+ else {
+ m->date = xstrdup(body[0]);
+ }
/* get control */
if ((body = spool_getheader(m->hdrcache, "control")) != NULL) {
@@ -3279,6 +3288,7 @@ static int deliver(message_data_t *msg)
unsigned long uid;
struct body *body = NULL;
struct dest *dlist = NULL;
+ duplicate_key_t dkey = {msg->id, NULL, msg->date};
/* check ACLs of all mailboxes */
for (n = 0; n < msg->rcpt_num; n++) {
@@ -3286,6 +3296,7 @@ static int deliver(message_data_t *msg)
/* look it up */
r = mlookup(rcpt, &server, &acl, NULL);
+ dkey.to = rcpt;
if (r) return IMAP_MAILBOX_NONEXISTENT;
if (!(acl && (myrights = cyrus_acl_myrights(nntp_authstate, acl)) &&
@@ -3301,9 +3312,9 @@ static int deliver(message_data_t *msg)
struct appendstate as;
if (msg->id &&
- duplicate_check(msg->id, strlen(msg->id), rcpt, strlen(rcpt))) {
+ duplicate_check(&dkey)) {
/* duplicate message */
- duplicate_log(msg->id, rcpt, "nntp delivery");
+ duplicate_log(&dkey, "nntp delivery");
continue;
}
@@ -3319,14 +3330,12 @@ static int deliver(message_data_t *msg)
r = append_fromstream(&as, &body, msg->data, msg->size, 0,
(const char **) NULL, 0);
}
- if (r || (msg->id &&
- duplicate_check(msg->id, strlen(msg->id),
- rcpt, strlen(rcpt)))) {
+ if (r || ( msg->id && duplicate_check(&dkey) ) ) {
append_abort(&as);
if (!r) {
/* duplicate message */
- duplicate_log(msg->id, rcpt, "nntp delivery");
+ duplicate_log(&dkey, "nntp delivery");
continue;
}
}
@@ -3336,8 +3345,7 @@ static int deliver(message_data_t *msg)
}
if (!r && msg->id)
- duplicate_mark(msg->id, strlen(msg->id), rcpt, strlen(rcpt),
- time(NULL), uid);
+ duplicate_mark(&dkey, time(NULL), uid);
if (r) return r;
@@ -3509,7 +3517,8 @@ static int cancel(message_data_t *msg)
/* store msgid of cancelled message for IHAVE/CHECK/TAKETHIS
* (in case we haven't received the message yet)
*/
- duplicate_mark(msgid, strlen(msgid), "", 0, 0, time(NULL));
+ duplicate_key_t dkey = {msgid, "", ""};
+ duplicate_mark(&dkey, 0, time(NULL));
return r;
}