cli: refactor reply from guessing

The guess_from_received_header() function had grown quite big. Chop it
up into smaller functions.

No functional changes.
This commit is contained in:
Jani Nikula 2014-02-03 21:51:42 +02:00 committed by David Bremner
parent 6d0a17c46e
commit 998a8a95c3

View file

@ -369,78 +369,44 @@ add_recipients_from_message (GMimeMessage *reply,
return from_addr; return from_addr;
} }
/*
* Look for the user's address in " for <email@add.res>" in the
* received headers.
*
* Return the address that was found, if any, and NULL otherwise.
*/
static const char * static const char *
guess_from_received_header (notmuch_config_t *config, notmuch_message_t *message) guess_from_in_received_for (notmuch_config_t *config, const char *received)
{ {
const char *addr, *received, *by; const char *ptr;
char *mta,*ptr,*token;
char *domain=NULL;
char *tld=NULL;
const char *delim=". \t";
size_t i;
const char *to_headers[] = { ptr = strstr (received, " for ");
"Envelope-to", if (! ptr)
"X-Original-To",
"Delivered-To",
};
/* sadly, there is no standard way to find out to which email
* address a mail was delivered - what is in the headers depends
* on the MTAs used along the way. So we are trying a number of
* heuristics which hopefully will answer this question.
* We only got here if none of the users email addresses are in
* the To: or Cc: header. From here we try the following in order:
* 1) check for an Envelope-to: header
* 2) check for an X-Original-To: header
* 3) check for a Delivered-To: header
* 4) check for a (for <email@add.res>) clause in Received: headers
* 5) check for the domain part of known email addresses in the
* 'by' part of Received headers
* If none of these work, we give up and return NULL
*/
for (i = 0; i < ARRAY_SIZE (to_headers); i++) {
const char *tohdr = notmuch_message_get_header (message, to_headers[i]);
/* Note: tohdr potentially contains a list of email addresses. */
addr = user_address_in_string (tohdr, config);
if (addr)
return addr;
}
/* We get the concatenated Received: headers and search from the
* front (last Received: header added) and try to extract from
* them indications to which email address this message was
* delivered.
* The Received: header is special in our get_header function
* and is always concatenated.
*/
received = notmuch_message_get_header (message, "received");
if (received == NULL)
return NULL; return NULL;
/* First we look for a " for <email@add.res>" in the received return user_address_in_string (ptr, config);
* header }
*/
ptr = strstr (received, " for ");
/* Note: ptr potentially contains a list of email addresses. */ /*
addr = user_address_in_string (ptr, config); * Parse all the " by MTA ..." parts in received headers to guess the
if (addr) * email address that this was originally delivered to.
return addr; *
* Extract just the MTA here by removing leading whitespace and
* assuming that the MTA name ends at the next whitespace. Test for
* *(by+4) to be non-'\0' to make sure there's something there at all
* - and then assume that the first whitespace delimited token that
* follows is the receiving system in this step of the receive chain.
*
* Return the address that was found, if any, and NULL otherwise.
*/
static const char *
guess_from_in_received_by (notmuch_config_t *config, const char *received)
{
const char *addr;
const char *by = received;
char *domain, *tld, *mta, *ptr, *token;
/* Finally, we parse all the " by MTA ..." headers to guess the while ((by = strstr (by, " by ")) != NULL) {
* email address that this was originally delivered to.
* We extract just the MTA here by removing leading whitespace and
* assuming that the MTA name ends at the next whitespace.
* We test for *(by+4) to be non-'\0' to make sure there's
* something there at all - and then assume that the first
* whitespace delimited token that follows is the receiving
* system in this step of the receive chain
*/
by = received;
while((by = strstr (by, " by ")) != NULL) {
by += 4; by += 4;
if (*by == '\0') if (*by == '\0')
break; break;
@ -450,11 +416,12 @@ guess_from_received_header (notmuch_config_t *config, notmuch_message_t *message
free (mta); free (mta);
break; break;
} }
/* Now extract the last two components of the MTA host name /*
* as domain and tld. * Now extract the last two components of the MTA host name as
* domain and tld.
*/ */
domain = tld = NULL; domain = tld = NULL;
while ((ptr = strsep (&token, delim)) != NULL) { while ((ptr = strsep (&token, ". \t")) != NULL) {
if (*ptr == '\0') if (*ptr == '\0')
continue; continue;
domain = tld; domain = tld;
@ -462,13 +429,14 @@ guess_from_received_header (notmuch_config_t *config, notmuch_message_t *message
} }
if (domain) { if (domain) {
/* Recombine domain and tld and look for it among the configured /*
* email addresses. * Recombine domain and tld and look for it among the
* This time we have a known domain name and nothing else - so * configured email addresses. This time we have a known
* the test is the other way around: we check if this is a * domain name and nothing else - so the test is the other
* substring of one of the email addresses. * way around: we check if this is a substring of one of
* the email addresses.
*/ */
*(tld-1) = '.'; *(tld - 1) = '.';
addr = string_in_user_address (domain, config); addr = string_in_user_address (domain, config);
if (addr) { if (addr) {
@ -482,6 +450,63 @@ guess_from_received_header (notmuch_config_t *config, notmuch_message_t *message
return NULL; return NULL;
} }
/*
* Get the concatenated Received: headers and search from the front
* (last Received: header added) and try to extract from them
* indications to which email address this message was delivered.
*
* The Received: header is special in our get_header function and is
* always concatenated.
*
* Return the address that was found, if any, and NULL otherwise.
*/
static const char *
guess_from_in_received_headers (notmuch_config_t *config,
notmuch_message_t *message)
{
const char *received, *addr;
received = notmuch_message_get_header (message, "received");
if (! received)
return NULL;
addr = guess_from_in_received_for (config, received);
if (! addr)
addr = guess_from_in_received_by (config, received);
return addr;
}
/*
* Try to find user's email address in one of the extra To-like
* headers: Envelope-To, X-Original-To, and Delivered-To (searched in
* that order).
*
* Return the address that was found, if any, and NULL otherwise.
*/
static const char *
get_from_in_to_headers (notmuch_config_t *config, notmuch_message_t *message)
{
size_t i;
const char *tohdr, *addr;
const char *to_headers[] = {
"Envelope-to",
"X-Original-To",
"Delivered-To",
};
for (i = 0; i < ARRAY_SIZE (to_headers); i++) {
tohdr = notmuch_message_get_header (message, to_headers[i]);
/* Note: tohdr potentially contains a list of email addresses. */
addr = user_address_in_string (tohdr, config);
if (addr)
return addr;
}
return NULL;
}
static GMimeMessage * static GMimeMessage *
create_reply_message(void *ctx, create_reply_message(void *ctx,
notmuch_config_t *config, notmuch_config_t *config,
@ -508,9 +533,30 @@ create_reply_message(void *ctx,
from_addr = add_recipients_from_message (reply, config, from_addr = add_recipients_from_message (reply, config,
message, reply_all); message, reply_all);
/*
* Sadly, there is no standard way to find out to which email
* address a mail was delivered - what is in the headers depends
* on the MTAs used along the way.
*
* If none of the user's email addresses are in the To: or Cc:
* headers, we try a number of heuristics which hopefully will
* answer this question.
*
* First, check for Envelope-To:, X-Original-To:, and
* Delivered-To: headers.
*/
if (from_addr == NULL) if (from_addr == NULL)
from_addr = guess_from_received_header (config, message); from_addr = get_from_in_to_headers (config, message);
/*
* Check for a (for <email@add.res>) clause in Received: headers,
* and the domain part of known email addresses in the 'by' part
* of Received: headers
*/
if (from_addr == NULL)
from_addr = guess_from_in_received_headers (config, message);
/* Default to user's primary address. */
if (from_addr == NULL) if (from_addr == NULL)
from_addr = notmuch_config_get_user_primary_email (config); from_addr = notmuch_config_get_user_primary_email (config);