[PATCH 2/2] Rearchitect From: header guessing code for replies
authorDirk Hohndel <hohndel@infradead.org>
Mon, 26 Apr 2010 19:58:35 +0000 (12:58 +1700)
committerW. Trevor King <wking@tremily.us>
Fri, 7 Nov 2014 17:36:56 +0000 (09:36 -0800)
a4/28a07579595f0cac1fc146e803523031cf05e6 [new file with mode: 0644]

diff --git a/a4/28a07579595f0cac1fc146e803523031cf05e6 b/a4/28a07579595f0cac1fc146e803523031cf05e6
new file mode 100644 (file)
index 0000000..b551096
--- /dev/null
@@ -0,0 +1,235 @@
+Return-Path: <hohndel@gr8dns.org>\r
+X-Original-To: notmuch@notmuchmail.org\r
+Delivered-To: notmuch@notmuchmail.org\r
+Received: from localhost (localhost [127.0.0.1])\r
+       by olra.theworths.org (Postfix) with ESMTP id DED3A417339\r
+       for <notmuch@notmuchmail.org>; Mon, 26 Apr 2010 12:58:53 -0700 (PDT)\r
+X-Virus-Scanned: Debian amavisd-new at olra.theworths.org\r
+X-Spam-Flag: NO\r
+X-Spam-Score: -1.9\r
+X-Spam-Level: \r
+X-Spam-Status: No, score=-1.9 tagged_above=-999 required=5\r
+       tests=[BAYES_00=-1.9] autolearn=ham\r
+Received: from olra.theworths.org ([127.0.0.1])\r
+       by localhost (olra.theworths.org [127.0.0.1]) (amavisd-new, port 10024)\r
+       with ESMTP id TcmZOx-CFKqj for <notmuch@notmuchmail.org>;\r
+       Mon, 26 Apr 2010 12:58:51 -0700 (PDT)\r
+Received: from mail.hohndel.org (mail.hohndel.org [65.23.157.147])\r
+       by olra.theworths.org (Postfix) with ESMTP id 9149B4196F4\r
+       for <notmuch@notmuchmail.org>; Mon, 26 Apr 2010 12:58:49 -0700 (PDT)\r
+Received: by mail.hohndel.org (Postfix, from userid 112)\r
+       id 2B2D3340F4; Mon, 26 Apr 2010 15:58:49 -0400 (EDT)\r
+Received: from x200.gr8dns.org (unknown [65.23.157.147])\r
+       by mail.hohndel.org (Postfix) with ESMTP id 6EB25340F8;\r
+       Mon, 26 Apr 2010 15:58:42 -0400 (EDT)\r
+Received: by x200.gr8dns.org (Postfix, from userid 500)\r
+       id 4EAC7CC903; Mon, 26 Apr 2010 12:58:42 -0700 (PDT)\r
+From: Dirk Hohndel <hohndel@infradead.org>\r
+To: <notmuch@notmuchmail.org>\r
+Subject: [PATCH 2/2] Rearchitect From: header guessing code for replies\r
+Date: Mon, 26 Apr 2010 12:58:35 -0700\r
+Message-Id: <1272311915-30575-3-git-send-email-hohndel@infradead.org>\r
+X-Mailer: git-send-email 1.6.6.1\r
+In-Reply-To: <1272311915-30575-2-git-send-email-hohndel@infradead.org>\r
+References: <m3sk6lcu9c.fsf@x200.gr8dns.org>\r
+       <1272311915-30575-1-git-send-email-hohndel@infradead.org>\r
+       <1272311915-30575-2-git-send-email-hohndel@infradead.org>\r
+X-BeenThere: notmuch@notmuchmail.org\r
+X-Mailman-Version: 2.1.13\r
+Precedence: list\r
+List-Id: "Use and development of the notmuch mail system."\r
+       <notmuch.notmuchmail.org>\r
+List-Unsubscribe: <http://notmuchmail.org/mailman/options/notmuch>,\r
+       <mailto:notmuch-request@notmuchmail.org?subject=unsubscribe>\r
+List-Archive: <http://notmuchmail.org/pipermail/notmuch>\r
+List-Post: <mailto:notmuch@notmuchmail.org>\r
+List-Help: <mailto:notmuch-request@notmuchmail.org?subject=help>\r
+List-Subscribe: <http://notmuchmail.org/mailman/listinfo/notmuch>,\r
+       <mailto:notmuch-request@notmuchmail.org?subject=subscribe>\r
+X-List-Received-Date: Mon, 26 Apr 2010 19:58:54 -0000\r
+\r
+We want to be able to correctly guess the best From: header to use when\r
+replying to emails. This is what we are looking at now:\r
+ 1 is one of the users' mail addresses in the To: or Cc: header\r
+ 2 check for an Envelope-to: header\r
+ 3 check for an X-Original-To: header\r
+ 4 check for a (for <email@add.res>) clause in Received: headers\r
+ 5 check for the domain part of known email addresses in the\r
+      'by' part of Received headers\r
+ 6 fall back to the primary email address\r
+\r
+This patch changes the algorithm for steps 2-5 of this process. Prior to\r
+this patch we had a first attempt to implement only step 5 - but this\r
+broke in many email setups where mail delivery to the local machine added\r
+additional Received: lines.\r
+Steps 2-4 are new, step 5 now analyzes the concatenated Received: header\r
+(this was in the previous patch) to do this analysis.\r
+\r
+Signed-off-by: Dirk Hohndel <hohndel@infradead.org>\r
+---\r
+ notmuch-reply.c |  125 +++++++++++++++++++++++++++++++++++++++++-------------\r
+ 1 files changed, 95 insertions(+), 30 deletions(-)\r
+\r
+diff --git a/notmuch-reply.c b/notmuch-reply.c\r
+index 7c43146..333e945 100644\r
+--- a/notmuch-reply.c\r
++++ b/notmuch-reply.c\r
+@@ -311,36 +311,100 @@ add_recipients_from_message (GMimeMessage *reply,\r
+ static const char *\r
+ guess_from_received_header (notmuch_config_t *config, notmuch_message_t *message)\r
+ {\r
+-    const char *received,*primary;\r
+-    char **other;\r
+-    char *by,*mta,*ptr,*token;\r
++    const char *received,*primary,*by;\r
++    char **other,*tohdr;\r
++    char *mta,*ptr,*token;\r
+     char *domain=NULL;\r
+     char *tld=NULL;\r
+     const char *delim=". \t";\r
+     size_t i,other_len;\r
\r
++    const char *to_headers[] = {"Envelope-to", "X-Original-To"};\r
++\r
++    primary = notmuch_config_get_user_primary_email (config);\r
++    other = notmuch_config_get_user_other_email (config, &other_len);\r
++\r
++    /* sadly, there is no standard way to find out to which email\r
++     * address a mail was delivered - what is in the headers depends\r
++     * on the MTAs used along the way. So we are trying a number of\r
++     * heuristics which hopefully will answer this question.\r
++\r
++     * We only got here if none of the users email addresses are in\r
++     * the To: or Cc: header. From here we try the following in order:\r
++     * 1) check for an Envelope-to: header\r
++     * 2) check for an X-Original-To: header\r
++     * 3) check for a (for <email@add.res>) clause in Received: headers\r
++     * 4) check for the domain part of known email addresses in the\r
++     *    'by' part of Received headers\r
++     * If none of these work, we give up and return NULL\r
++     */\r
++    for (i = 0; i < sizeof(to_headers)/sizeof(*to_headers); i++) {\r
++      tohdr = xstrdup(notmuch_message_get_header (message, to_headers[i]));\r
++      if (tohdr && *tohdr) {\r
++          /* tohdr is potentialy a list of email addresses, so here we\r
++           * check if one of the email addresses is a substring of tohdr\r
++           */\r
++          if (strcasestr(tohdr, primary)) {\r
++              free(tohdr);\r
++              return primary;\r
++          }\r
++          for (i = 0; i < other_len; i++)\r
++              if (strcasestr (tohdr, other[i])) {\r
++                  free(tohdr);\r
++                  return other[i];\r
++              }\r
++          free(tohdr);\r
++      }\r
++    }\r
++\r
++    /* We get the concatenated Received: headers and search from the\r
++     * front (last Received: header added) and try to extract from\r
++     * them indications to which email address this message was\r
++     * delivered.\r
++     * The Received: header is special in our get_header function\r
++     * and is always concated.\r
++     */\r
+     received = notmuch_message_get_header (message, "received");\r
+     if (received == NULL)\r
+       return NULL;\r
\r
+-    by = strstr (received, " by ");\r
+-    if (by && *(by+4)) {\r
+-      /* sadly, the format of Received: headers is a bit inconsistent,\r
+-       * depending on the MTA used. So we try to extract just the MTA\r
+-       * here by removing leading whitespace and assuming that the MTA\r
+-       * name ends at the next whitespace\r
+-       * we test for *(by+4) to be non-'\0' to make sure there's something\r
+-       * there at all - and then assume that the first whitespace delimited\r
+-       * token that follows is the last receiving server\r
++    /* First we look for a " for <email@add.res>" in the received\r
++     * header\r
++     */\r
++    ptr = strstr (received, " for ");\r
++    if (ptr) {\r
++      /* the text following is potentialy a list of email addresses,\r
++       * so again we check if one of the email addresses is a\r
++       * substring of ptr\r
+        */\r
+-      mta = strdup (by+4);\r
+-      if (mta == NULL)\r
+-          return NULL;\r
++      if (strcasestr(ptr, primary)) {\r
++          return primary;\r
++      }\r
++      for (i = 0; i < other_len; i++)\r
++          if (strcasestr (ptr, other[i])) {\r
++              return other[i];\r
++          }\r
++    }\r
++    /* Finally, we parse all the " by MTA ..." headers to guess the\r
++     * email address that this was originally delivered to.\r
++     * We extract just the MTA here by removing leading whitespace and\r
++     * assuming that the MTA name ends at the next whitespace.\r
++     * We test for *(by+4) to be non-'\0' to make sure there's\r
++     * something there at all - and then assume that the first\r
++     * whitespace delimited token that follows is the receiving\r
++     * system in this step of the receive chain\r
++     */\r
++    by = received;\r
++    while((by = strstr (by, " by ")) != NULL) {\r
++      by += 4;\r
++      if (*by == '\0')\r
++          break;\r
++      mta = xstrdup (by);\r
+       token = strtok(mta," \t");\r
+       if (token == NULL)\r
+-          return NULL;\r
++          break;\r
+       /* Now extract the last two components of the MTA host name\r
+-       * as domain and tld\r
++       * as domain and tld.\r
+        */\r
+       while ((ptr = strsep (&token, delim)) != NULL) {\r
+           if (*ptr == '\0')\r
+@@ -350,23 +414,24 @@ guess_from_received_header (notmuch_config_t *config, notmuch_message_t *message\r
+       }\r
\r
+       if (domain) {\r
+-          /* recombine domain and tld and look for it among the configured\r
+-           * email addresses\r
++          /* Recombine domain and tld and look for it among the configured\r
++           * email addresses.\r
++           * This time we have a known domain name and nothing else - so\r
++           * the test is the other way around: we check if this is a\r
++           * substring of one of the email addresses.\r
+            */\r
+           *(tld-1) = '.';\r
+-          primary = notmuch_config_get_user_primary_email (config);\r
+-          if (strcasestr (primary, domain)) {\r
+-              free (mta);\r
+-              return primary;\r
++\r
++          if (strcasestr(primary, domain)) {\r
++              free(mta);\r
++          return primary;\r
++      }\r
++      for (i = 0; i < other_len; i++)\r
++          if (strcasestr (other[i],domain)) {\r
++              free(mta);\r
++              return other[i];\r
+           }\r
+-          other = notmuch_config_get_user_other_email (config, &other_len);\r
+-          for (i = 0; i < other_len; i++)\r
+-              if (strcasestr (other[i], domain)) {\r
+-                  free (mta);\r
+-                  return other[i];\r
+-              }\r
+       }\r
+-\r
+       free (mta);\r
+     }\r
\r
+-- \r
+1.6.6.1\r
+\r