mirror of
https://git.notmuchmail.org/git/notmuch
synced 2024-11-21 18:38:08 +01:00
Rearchitect From: header guessing code for replies
We want to be able to correctly guess the best From: header to use when replying to emails. This is what we are looking at now: 1 is one of the users' mail addresses in the To: or Cc: header 2 check for an Envelope-to: header 3 check for an X-Original-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 6 fall back to the primary email address This patch changes the algorithm for steps 2-5 of this process. Prior to this patch we had a first attempt to implement only step 5 - but this broke in many email setups where mail delivery to the local machine added additional Received: lines. Steps 2-4 are new, step 5 now analyzes the concatenated Received: header (this was in the previous patch) to do this analysis. Signed-off-by: Dirk Hohndel <hohndel@infradead.org>
This commit is contained in:
parent
5b8b0377cb
commit
932e1c165c
1 changed files with 96 additions and 31 deletions
127
notmuch-reply.c
127
notmuch-reply.c
|
@ -311,36 +311,100 @@ add_recipients_from_message (GMimeMessage *reply,
|
||||||
static const char *
|
static const char *
|
||||||
guess_from_received_header (notmuch_config_t *config, notmuch_message_t *message)
|
guess_from_received_header (notmuch_config_t *config, notmuch_message_t *message)
|
||||||
{
|
{
|
||||||
const char *received,*primary;
|
const char *received,*primary,*by;
|
||||||
char **other;
|
char **other,*tohdr;
|
||||||
char *by,*mta,*ptr,*token;
|
char *mta,*ptr,*token;
|
||||||
char *domain=NULL;
|
char *domain=NULL;
|
||||||
char *tld=NULL;
|
char *tld=NULL;
|
||||||
const char *delim=". \t";
|
const char *delim=". \t";
|
||||||
size_t i,other_len;
|
size_t i,other_len;
|
||||||
|
|
||||||
|
const char *to_headers[] = {"Envelope-to", "X-Original-To"};
|
||||||
|
|
||||||
|
primary = notmuch_config_get_user_primary_email (config);
|
||||||
|
other = notmuch_config_get_user_other_email (config, &other_len);
|
||||||
|
|
||||||
|
/* 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 (for <email@add.res>) clause in Received: headers
|
||||||
|
* 4) 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 < sizeof(to_headers)/sizeof(*to_headers); i++) {
|
||||||
|
tohdr = xstrdup(notmuch_message_get_header (message, to_headers[i]));
|
||||||
|
if (tohdr && *tohdr) {
|
||||||
|
/* tohdr is potentialy a list of email addresses, so here we
|
||||||
|
* check if one of the email addresses is a substring of tohdr
|
||||||
|
*/
|
||||||
|
if (strcasestr(tohdr, primary)) {
|
||||||
|
free(tohdr);
|
||||||
|
return primary;
|
||||||
|
}
|
||||||
|
for (i = 0; i < other_len; i++)
|
||||||
|
if (strcasestr (tohdr, other[i])) {
|
||||||
|
free(tohdr);
|
||||||
|
return other[i];
|
||||||
|
}
|
||||||
|
free(tohdr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 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 concated.
|
||||||
|
*/
|
||||||
received = notmuch_message_get_header (message, "received");
|
received = notmuch_message_get_header (message, "received");
|
||||||
if (received == NULL)
|
if (received == NULL)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
by = strstr (received, " by ");
|
/* First we look for a " for <email@add.res>" in the received
|
||||||
if (by && *(by+4)) {
|
* header
|
||||||
/* sadly, the format of Received: headers is a bit inconsistent,
|
*/
|
||||||
* depending on the MTA used. So we try to extract just the MTA
|
ptr = strstr (received, " for ");
|
||||||
* here by removing leading whitespace and assuming that the MTA
|
if (ptr) {
|
||||||
* name ends at the next whitespace
|
/* the text following is potentialy a list of email addresses,
|
||||||
* we test for *(by+4) to be non-'\0' to make sure there's something
|
* so again we check if one of the email addresses is a
|
||||||
* there at all - and then assume that the first whitespace delimited
|
* substring of ptr
|
||||||
* token that follows is the last receiving server
|
|
||||||
*/
|
*/
|
||||||
mta = strdup (by+4);
|
if (strcasestr(ptr, primary)) {
|
||||||
if (mta == NULL)
|
return primary;
|
||||||
return NULL;
|
}
|
||||||
|
for (i = 0; i < other_len; i++)
|
||||||
|
if (strcasestr (ptr, other[i])) {
|
||||||
|
return other[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* Finally, we parse all the " by MTA ..." headers to guess the
|
||||||
|
* 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;
|
||||||
|
if (*by == '\0')
|
||||||
|
break;
|
||||||
|
mta = xstrdup (by);
|
||||||
token = strtok(mta," \t");
|
token = strtok(mta," \t");
|
||||||
if (token == NULL)
|
if (token == NULL)
|
||||||
return NULL;
|
break;
|
||||||
/* Now extract the last two components of the MTA host name
|
/* Now extract the last two components of the MTA host name
|
||||||
* as domain and tld
|
* as domain and tld.
|
||||||
*/
|
*/
|
||||||
while ((ptr = strsep (&token, delim)) != NULL) {
|
while ((ptr = strsep (&token, delim)) != NULL) {
|
||||||
if (*ptr == '\0')
|
if (*ptr == '\0')
|
||||||
|
@ -350,23 +414,24 @@ 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
|
/* Recombine domain and tld and look for it among the configured
|
||||||
* email addresses
|
* email addresses.
|
||||||
|
* This time we have a known domain name and nothing else - so
|
||||||
|
* the test is the other way around: we check if this is a
|
||||||
|
* substring of one of the email addresses.
|
||||||
*/
|
*/
|
||||||
*(tld-1) = '.';
|
*(tld-1) = '.';
|
||||||
primary = notmuch_config_get_user_primary_email (config);
|
|
||||||
if (strcasestr (primary, domain)) {
|
|
||||||
free (mta);
|
|
||||||
return primary;
|
|
||||||
}
|
|
||||||
other = notmuch_config_get_user_other_email (config, &other_len);
|
|
||||||
for (i = 0; i < other_len; i++)
|
|
||||||
if (strcasestr (other[i], domain)) {
|
|
||||||
free (mta);
|
|
||||||
return other[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
if (strcasestr(primary, domain)) {
|
||||||
|
free(mta);
|
||||||
|
return primary;
|
||||||
|
}
|
||||||
|
for (i = 0; i < other_len; i++)
|
||||||
|
if (strcasestr (other[i],domain)) {
|
||||||
|
free(mta);
|
||||||
|
return other[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
free (mta);
|
free (mta);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue