Commit 2f6f7b4f 2f6f7b4fc2230352e8ff742b9999672dec35236b by Sergey Poznyakoff

Improve the algorithm of obtaining sender name from messages.

* include/mailutils/header.h: Remove unnecessary externs.
(mu_url_sget_param, mu_url_aget_param): New protos.
* libmailutils/base/url.c (mu_url_sget_param, mu_url_aget_param): New
functions.

* libmailutils/mailbox/hdrfirst.c: New file.
* libmailutils/mailbox/Makefile.am (libmailbox_la_SOURCES): Add hdrfirst.c.

* libmailutils/mailbox/message.c (message_envelope_sender): Try to restore
the envelope sender from X-Envelope-Sender, X-Envelope-From, X-Original-Sender,
and From headers, in that order.
* libmailutils/mailer/mailer.c (_set_from): Try to obtain sender from
the message envelope.  If that fails, fall back to the From header.
* libproto/mailer/mbox.c (parse_received, guess_message_recipient): New
static functions.
(remote_mbox_append_message): Get recipient address from the first
of X-Envelope-To, Delivered-To, X-Original-To headers, in that order.
If that fails, try to deduce it from the Received header.  If that
fails too, fall back to the To header.
If recipient-headers URL parameter is defined, its value (a comma-separated
list of names) overrides this list.
The strip-domain parameter, if present, instructs the function to strip
domain part from the recipient address before resolving it.
The domain parameter, if present, supplies the domain name which overrides
the domain obtained from the URL.
1 parent 1417084b
......@@ -105,6 +105,9 @@ extern int mu_header_get_value_n (mu_header_t, const char *, int, char *,
extern int mu_header_aget_value_n (mu_header_t, const char *, int, char **);
#define mu_header_aget_value(header, name, pptr) \
mu_header_aget_value_n (header, name, 1, pptr)
int mu_header_sget_firstof (mu_header_t hdr, char **names,
const char **pval, int *pidx);
/* Get field values as an mu_address_t. */
extern int mu_header_get_address_n (mu_header_t, const char *,
......
......@@ -25,61 +25,64 @@
extern "C" {
#endif
extern int mu_url_create (mu_url_t *, const char *name);
extern int mu_url_dup (mu_url_t old_url, mu_url_t *new_url);
extern int mu_url_uplevel (mu_url_t url, mu_url_t *upurl);
int mu_url_create (mu_url_t *, const char *name);
int mu_url_dup (mu_url_t old_url, mu_url_t *new_url);
int mu_url_uplevel (mu_url_t url, mu_url_t *upurl);
extern void mu_url_destroy (mu_url_t *);
extern int mu_url_parse (mu_url_t);
void mu_url_destroy (mu_url_t *);
int mu_url_parse (mu_url_t);
extern int mu_url_sget_scheme (const mu_url_t, const char **);
extern int mu_url_aget_scheme (const mu_url_t, char **);
extern int mu_url_get_scheme (const mu_url_t, char *, size_t, size_t *);
extern int mu_url_sget_user (const mu_url_t, const char **);
extern int mu_url_aget_user (const mu_url_t, char **);
extern int mu_url_get_user (const mu_url_t, char *, size_t, size_t *);
int mu_url_sget_scheme (const mu_url_t, const char **);
int mu_url_aget_scheme (const mu_url_t, char **);
int mu_url_get_scheme (const mu_url_t, char *, size_t, size_t *);
extern int mu_url_get_secret (const mu_url_t url, mu_secret_t *psecret);
extern int mu_url_sget_auth (const mu_url_t, const char **);
extern int mu_url_aget_auth (const mu_url_t, char **);
extern int mu_url_get_auth (const mu_url_t, char *, size_t, size_t *);
int mu_url_sget_user (const mu_url_t, const char **);
int mu_url_aget_user (const mu_url_t, char **);
int mu_url_get_user (const mu_url_t, char *, size_t, size_t *);
extern int mu_url_sget_host (const mu_url_t, const char **);
extern int mu_url_aget_host (const mu_url_t, char **);
extern int mu_url_get_host (const mu_url_t, char *, size_t, size_t *);
extern int mu_url_sget_path (const mu_url_t, const char **);
extern int mu_url_aget_path (const mu_url_t, char **);
extern int mu_url_get_path (const mu_url_t, char *, size_t, size_t *);
int mu_url_get_secret (const mu_url_t url, mu_secret_t *psecret);
extern int mu_url_sget_query (const mu_url_t url, size_t *qc, char ***qv);
extern int mu_url_aget_query (const mu_url_t url, size_t *qc, char ***qv);
extern int mu_url_get_port (const mu_url_t, long *);
int mu_url_sget_auth (const mu_url_t, const char **);
int mu_url_aget_auth (const mu_url_t, char **);
int mu_url_get_auth (const mu_url_t, char *, size_t, size_t *);
int mu_url_sget_host (const mu_url_t, const char **);
int mu_url_aget_host (const mu_url_t, char **);
int mu_url_get_host (const mu_url_t, char *, size_t, size_t *);
int mu_url_sget_path (const mu_url_t, const char **);
int mu_url_aget_path (const mu_url_t, char **);
int mu_url_get_path (const mu_url_t, char *, size_t, size_t *);
int mu_url_sget_query (const mu_url_t url, size_t *qc, char ***qv);
int mu_url_aget_query (const mu_url_t url, size_t *qc, char ***qv);
int mu_url_get_port (const mu_url_t, long *);
int mu_url_sget_fvpairs (const mu_url_t url, size_t *fvc, char ***fvp);
int mu_url_aget_fvpairs (const mu_url_t url, size_t *pfvc, char ***pfvp);
extern int mu_url_expand_path (mu_url_t url);
extern const char *mu_url_to_string (const mu_url_t);
extern int mu_url_set_scheme (mu_url_t url, const char *scheme);
int mu_url_sget_param (const mu_url_t url, const char *param, const char **val);
int mu_url_aget_param (const mu_url_t url, const char *param, char **val);
extern int mu_url_is_scheme (mu_url_t, const char *scheme);
int mu_url_expand_path (mu_url_t url);
const char *mu_url_to_string (const mu_url_t);
int mu_url_set_scheme (mu_url_t url, const char *scheme);
int mu_url_is_scheme (mu_url_t, const char *scheme);
extern int mu_url_is_same_scheme (mu_url_t, mu_url_t);
extern int mu_url_is_same_user (mu_url_t, mu_url_t);
extern int mu_url_is_same_path (mu_url_t, mu_url_t);
extern int mu_url_is_same_host (mu_url_t, mu_url_t);
extern int mu_url_is_same_port (mu_url_t, mu_url_t);
int mu_url_is_same_scheme (mu_url_t, mu_url_t);
int mu_url_is_same_user (mu_url_t, mu_url_t);
int mu_url_is_same_path (mu_url_t, mu_url_t);
int mu_url_is_same_host (mu_url_t, mu_url_t);
int mu_url_is_same_port (mu_url_t, mu_url_t);
extern char *mu_url_decode_len (const char *s, size_t len);
extern char *mu_url_decode (const char *s);
char *mu_url_decode_len (const char *s, size_t len);
char *mu_url_decode (const char *s);
extern int mu_url_is_ticket (mu_url_t ticket, mu_url_t url);
extern int mu_url_init (mu_url_t url, int port, const char *scheme);
int mu_url_is_ticket (mu_url_t ticket, mu_url_t url);
int mu_url_init (mu_url_t url, int port, const char *scheme);
#ifdef __cplusplus
}
......
......@@ -764,6 +764,48 @@ mu_url_sget_fvpairs (const mu_url_t url, size_t *fvc, char ***fvp)
}
int
mu_url_sget_param (const mu_url_t url, const char *param, const char **val)
{
size_t fvc;
char **fvp;
int status = mu_url_sget_fvpairs (url, &fvc, &fvp);
if (status)
return status;
if (fvc)
{
size_t i;
for (i = 0; i < fvc; i++)
{
const char *p;
char *q;
for (p = param, q = fvp[i]; *p && *q && *p == *q; p++, q++)
;
if (*p == 0)
{
if (*q == 0)
{
if (val)
*val = q;
return 0;
}
else if (*q == '=')
{
if (val)
*val = q + 1;
return 0;
}
}
}
}
return MU_ERR_NOENT;
}
int
mu_url_aget_fvpairs (const mu_url_t url, size_t *pfvc, char ***pfvp)
{
size_t fvc, i;
......@@ -792,6 +834,21 @@ mu_url_aget_fvpairs (const mu_url_t url, size_t *pfvc, char ***pfvp)
}
int
mu_url_aget_param (const mu_url_t url, const char *param, char **val)
{
const char *s;
int status = mu_url_sget_param (url, param, &s);
if (status == 0)
{
*val = strdup (s);
if (!*val)
status = ENOMEM;
}
return status;
}
int
mu_url_get_port (const mu_url_t url, long *pport)
{
if (url == NULL)
......
......@@ -25,6 +25,7 @@ libmailbox_la_SOURCES = \
body.c\
envelope.c\
folder.c\
hdrfirst.c\
hdritr.c\
header.c\
message.c\
......
/* GNU Mailutils -- a suite of utilities for electronic mail
Copyright (C) 2010 Free Software Foundation, Inc.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 3 of the License, or (at your option) any later version.
This library 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General
Public License along with this library. If not, see
<http://www.gnu.org/licenses/>. */
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include <stdlib.h>
#include <mailutils/types.h>
#include <mailutils/header.h>
#include <mailutils/errno.h>
int
mu_header_sget_firstof (mu_header_t hdr, char **names,
const char **pval, int *pidx)
{
int status;
const char *s = NULL;
int i;
for (i = 0; names[i]; i++)
{
status = mu_header_sget_value (hdr, names[i], &s);
if (status == 0 && *s != 0)
{
if (pval)
*pval = s;
if (pidx)
*pidx = i;
return 0;
}
}
return MU_ERR_NOENT;
}
......@@ -429,52 +429,59 @@ message_envelope_sender (mu_envelope_t envelope, char *buf, size_t len,
size_t *pnwrite)
{
mu_message_t msg = mu_envelope_get_owner (envelope);
mu_header_t header = NULL;
size_t n = 0;
mu_header_t header;
int status;
const char *sender;
struct mu_auth_data *auth = NULL;
static char *hdrnames[] = {
"X-Envelope-Sender",
"X-Envelope-From",
"X-Original-Sender",
"From",
NULL
};
mu_address_t address = NULL;
if (msg == NULL)
return EINVAL;
/* Can it be extracted from the From: */
mu_message_get_header (msg, &header);
status = mu_header_get_value (header, MU_HEADER_FROM, NULL, 0, &n);
if (status == 0 && n != 0)
/* First, try the header */
status = mu_message_get_header (msg, &header);
if (status)
return status;
status = mu_header_sget_firstof (header, hdrnames, &sender, NULL);
if (status)
{
char *sender;
mu_address_t address = NULL;
sender = calloc (1, n + 1);
if (sender == NULL)
return ENOMEM;
mu_header_get_value (header, MU_HEADER_FROM, sender, n + 1, NULL);
if (mu_address_create (&address, sender) == 0)
mu_address_get_email (address, 1, buf, n + 1, pnwrite);
free (sender);
mu_address_destroy (&address);
return 0;
auth = mu_get_auth_by_uid (getuid ());
if (!auth)
return MU_ERR_NOENT;
sender = auth->name;
}
else if (status == EAGAIN)
return status;
/* oops! We are still here */
{
struct mu_auth_data *auth = mu_get_auth_by_uid (getuid ());
const char *sender = auth ? auth->name : "unknown";
n = strlen (sender);
if (buf && len > 0)
{
len--; /* One for the null. */
n = (n < len) ? n : len;
memcpy (buf, auth->name, n);
buf[n] = '\0';
}
if (auth)
mu_auth_data_free (auth);
}
status = mu_address_create (&address, sender);
if (status == 0)
{
status = mu_address_sget_email (address, 1, &sender);
if (status == 0)
{
size_t n = strlen (sender);
if (buf && len > 0)
{
len--; /* One for the null. */
n = (n < len) ? n : len;
memcpy (buf, sender, n);
buf[n] = '\0';
}
if (pnwrite)
*pnwrite = n;
}
mu_address_destroy (&address);
}
if (auth)
mu_auth_data_free (auth);
if (pnwrite)
*pnwrite = n;
return 0;
return status;
}
......
......@@ -41,6 +41,7 @@
#include <mailutils/stream.h>
#include <mailutils/url.h>
#include <mailutils/header.h>
#include <mailutils/envelope.h>
#include <mailutils/body.h>
#include <mailutils/mailbox.h>
#include <mailutils/message.h>
......@@ -336,20 +337,27 @@ _set_from (mu_address_t *pfrom, mu_message_t msg, mu_address_t from,
mu_mailer_t mailer)
{
int status = 0;
char *mail_from;
mu_header_t header = NULL;
*pfrom = NULL;
/* Get MAIL_FROM from FROM, the message, or the environment. */
if (!from)
{
const char *type;
if ((status = mu_message_get_header (msg, &header)) != 0)
mu_envelope_t env;
const char *mail_from;
status = mu_message_get_envelope (msg, &env);
if (status)
return status;
status = mu_header_aget_value (header, MU_HEADER_FROM, &mail_from);
status = mu_envelope_sget_sender (env, &mail_from);
if (status)
{
mu_header_t header;
status = mu_message_get_header (msg, &header);
if (status)
return status;
status = mu_header_sget_value (header, MU_HEADER_FROM, &mail_from);
}
switch (status)
{
......@@ -361,9 +369,6 @@ _set_from (mu_address_t *pfrom, mu_message_t msg, mu_address_t from,
MU_DEBUG1 (mailer->debug, MU_DEBUG_TRACE,
"mu_mailer_send_message(): using From: %s\n",
mail_from);
status = mu_address_create (pfrom, mail_from);
free (mail_from);
break;
case MU_ERR_NOENT:
......@@ -379,18 +384,20 @@ _set_from (mu_address_t *pfrom, mu_message_t msg, mu_address_t from,
"mu_mailer_send_message(): using user's address: %s\n",
mail_from);
else
MU_DEBUG (mailer->debug, MU_DEBUG_ERROR,
"mu_mailer_send_message(): no user's address, failing\n");
if (!mail_from)
return errno;
status = mu_address_create (pfrom, mail_from);
{
MU_DEBUG (mailer->debug, MU_DEBUG_ERROR,
"mu_mailer_send_message(): "
"no user's address, failing\n");
return errno;
}
/* FIXME: should we add the From: header? */
break;
}
status = mu_address_create (pfrom, mail_from);
}
else
*pfrom = NULL;
return status;
}
......
......@@ -29,6 +29,10 @@
#include <mailutils/mailer.h>
#include <mailutils/url.h>
#include <mailutils/util.h>
#include <mailutils/argcv.h>
#include <mailutils/message.h>
#include <mailutils/envelope.h>
#include <mailutils/header.h>
#include <mailutils/sys/mailbox.h>
#include <mailutils/sys/mailer.h>
......@@ -116,6 +120,113 @@ mkaddr (mu_mailbox_t mbox, mu_property_t property,
return 0;
}
static int
parse_received (mu_header_t hdr, char **sptr)
{
const char *recv;
int wc, i;
char **ws;
enum { rcv_init, rcv_from, rcv_by, rcv_for } state;
int status;
char *s;
size_t len;
status = mu_header_sget_value (hdr, MU_HEADER_RECEIVED, &recv);
if (status)
return status;
status = mu_argcv_get (recv, NULL, NULL, &wc, &ws);
if (status)
return status;
state = rcv_init;
for (i = 0; i < wc && state != rcv_for; i++)
{
switch (state)
{
case rcv_init:
if (strcmp (ws[i], "from") == 0)
state = rcv_from;
break;
case rcv_from:
if (strcmp (ws[i], "by") == 0)
state = rcv_by;
break;
case rcv_by:
if (strcmp (ws[i], "for") == 0)
state = rcv_for;
break;
default:
break;
}
}
if (state != rcv_for || ws[i] == NULL)
return MU_ERR_NOENT;
s = ws[i];
len = strlen (s);
if (s[len - 1] == ';')
len--;
if (s[0] == '<' && s[len - 1] == '>')
{
s++;
len--;
}
*sptr = malloc (len);
if (!*sptr)
status = ENOMEM;
else
{
memcpy (*sptr, s, len);
(*sptr)[len - 1] = 0;
}
mu_argcv_free (wc, ws);
return status;
}
static int
guess_message_recipient (mu_message_t msg, char **hdrname, char **pptr)
{
mu_header_t hdr;
int status;
char *s = NULL;
status = mu_message_get_header (msg, &hdr);
if (status)
return status;
/* First, try an easy way. */
if (hdrname)
{
int i;
for (i = 0; hdrname[i]; i++)
{
status = mu_header_aget_value (hdr, hdrname[i], &s);
if (status == 0 && *s != 0)
break;
}
}
else
status = MU_ERR_NOENT;
if (status == MU_ERR_NOENT)
{
status = parse_received (hdr, &s);
if (status)
status = mu_header_aget_value (hdr, MU_HEADER_TO, &s);
}
if (status)
return status;
*pptr = s;
return 0;
}
static int
remote_mbox_append_message (mu_mailbox_t mbox, mu_message_t msg)
......@@ -137,9 +248,47 @@ remote_mbox_append_message (mu_mailbox_t mbox, mu_message_t msg)
mkaddr (mbox, property, "TO", &to);
if (!to)
{
const char *rcpt;
char *rcpt;
status = mu_url_sget_user (mbox->url, &rcpt);
status = mu_url_aget_user (mbox->url, &rcpt);
if (status == MU_ERR_NOENT)
{
static char *hdrnames[] = {
"X-Envelope-To",
"Delivered-To",
"X-Original-To",
NULL
};
const char *hstr;
int hc;
char **hv;
if (mu_url_sget_param (mbox->url, "recipient-headers", &hstr) == 0)
{
if (*hstr == 0)
{
hc = 0;
hv = NULL;
}
else
{
status = mu_argcv_get_np (hstr, strlen (hstr), ",", NULL, 0,
&hc, &hv, NULL);
if (status)
return status;
}
}
else
{
hc = 0;
hv = hdrnames;
}
status = guess_message_recipient (msg, hv, &rcpt);
if (hc)
mu_argcv_free (hc, hv);
}
if (status != MU_ERR_NOENT)
{
const char *host;
......@@ -148,16 +297,27 @@ remote_mbox_append_message (mu_mailbox_t mbox, mu_message_t msg)
if (status)
{
MU_DEBUG1 (mbox->debug, MU_DEBUG_ERROR,
"failed to get recipient from the url: %s\n",
"failed to get recipient: %s\n",
mu_strerror (status));
return status;
}
mu_url_sget_host (mbox->url, &host);
/* Get additional parameters */
status = mu_url_sget_param (mbox->url, "strip-domain", NULL);
if (status == 0)
{
char *q = strchr (rcpt, '@');
if (q)
*q = 0;
}
status = mu_url_sget_param (mbox->url, "domain", &host);
if (!(status == 0 && *host))
mu_url_sget_host (mbox->url, &host);
hint.domain = (char*) host;
status = mu_address_create_hint (&to, rcpt, &hint,
MU_ADDR_HINT_DOMAIN);
free (rcpt);
if (status)
{
MU_DEBUG3 (mbox->debug, MU_DEBUG_ERROR,
......