]> git.notmuchmail.org Git - notmuch/blobdiff - notmuch-reply.c
notmuch show: Properly nest MIME parts within mulipart parts
[notmuch] / notmuch-reply.c
index 230cacc34fc539f1c1714ee83f0c86738a99ddbf..71edb662e02edb35fcdc3441ec2e92e33d3d2674 100644 (file)
@@ -72,7 +72,7 @@ show_reply_headers (GMimeMessage *message)
 }
 
 static void
-reply_part (GMimeObject *part, int *part_count)
+reply_part (GMimeObject *part, int *part_count, unused (int first))
 {
     GMimeContentDisposition *disposition;
     GMimeContentType *content_type;
@@ -119,7 +119,7 @@ static int
 address_is_users (const char *address, notmuch_config_t *config)
 {
     const char *primary;
-    char **other;
+    const char **other;
     size_t i, other_len;
 
     primary = notmuch_config_get_user_primary_email (config);
@@ -201,6 +201,9 @@ add_recipients_for_string (GMimeMessage *message,
 {
     InternetAddressList *list;
 
+    if (recipients == NULL)
+       return NULL;
+
     list = internet_address_list_parse_string (recipients);
     if (list == NULL)
        return NULL;
@@ -214,16 +217,16 @@ add_recipients_for_string (GMimeMessage *message,
 static int
 reply_to_header_is_redundant (notmuch_message_t *message)
 {
-    const char *header, *addr;
+    const char *reply_to, *to, *cc, *addr;
     InternetAddressList *list;
     InternetAddress *address;
     InternetAddressMailbox *mailbox;
 
-    header = notmuch_message_get_header (message, "reply-to");
-    if (*header == '\0')
+    reply_to = notmuch_message_get_header (message, "reply-to");
+    if (reply_to == NULL || *reply_to == '\0')
        return 0;
 
-    list = internet_address_list_parse_string (header);
+    list = internet_address_list_parse_string (reply_to);
 
     if (internet_address_list_length (list) != 1)
        return 0;
@@ -235,8 +238,11 @@ reply_to_header_is_redundant (notmuch_message_t *message)
     mailbox = INTERNET_ADDRESS_MAILBOX (address);
     addr = internet_address_mailbox_get_addr (mailbox);
 
-    if (strstr (notmuch_message_get_header (message, "to"), addr) != 0 ||
-       strstr (notmuch_message_get_header (message, "cc"), addr) != 0)
+    to = notmuch_message_get_header (message, "to");
+    cc = notmuch_message_get_header (message, "cc");
+
+    if ((to && strstr (to, addr) != 0) ||
+       (cc && strstr (cc, addr) != 0))
     {
        return 1;
     }
@@ -305,33 +311,101 @@ add_recipients_from_message (GMimeMessage *reply,
 static const char *
 guess_from_received_header (notmuch_config_t *config, notmuch_message_t *message)
 {
-    const char *received,*primary;
-    char **other;
-    char *by,*mta,*ptr,*token;
+    const char *received,*primary,*by;
+    const char **other;
+    char *tohdr;
+    char *mta,*ptr,*token;
     char *domain=NULL;
     char *tld=NULL;
     const char *delim=". \t";
-    size_t i,other_len;
+    size_t i,j,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 (j = 0; j < other_len; j++)
+               if (strcasestr (tohdr, other[j])) {
+                   free(tohdr);
+                   return other[j];
+               }
+           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");
-    by = strstr (received, " by ");
-    if (by && *(by+4)) {
-       /* sadly, the format of Received: headers is a bit inconsistent,
-        * depending on the MTA used. So we try to 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 last receiving server
+    if (received == NULL)
+       return NULL;
+
+    /* First we look for a " for <email@add.res>" in the received
+     * header
+     */
+    ptr = strstr (received, " for ");
+    if (ptr) {
+       /* the text following is potentialy a list of email addresses,
+        * so again we check if one of the email addresses is a
+        * substring of ptr
         */
-       mta = strdup (by+4);
-       if (mta == NULL)
-           return NULL;
+       if (strcasestr(ptr, primary)) {
+           return primary;
+       }
+       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");
        if (token == NULL)
-           return NULL;
+           break;
        /* 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) {
            if (*ptr == '\0')
@@ -341,23 +415,24 @@ guess_from_received_header (notmuch_config_t *config, notmuch_message_t *message
        }
 
        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 configured
+            * 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) = '.';
-           primary = notmuch_config_get_user_primary_email (config);
-           if (strcasestr (primary, domain)) {
-               free (mta);
-               return primary;
+
+           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];
            }
-           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];
-               }
        }
-
        free (mta);
     }
 
@@ -387,9 +462,11 @@ notmuch_reply_format_default(void *ctx, notmuch_config_t *config, notmuch_query_
        }
 
        subject = notmuch_message_get_header (message, "subject");
-       if (strncasecmp (subject, "Re:", 3))
-           subject = talloc_asprintf (ctx, "Re: %s", subject);
-       g_mime_message_set_subject (reply, subject);
+       if (subject) {
+           if (strncasecmp (subject, "Re:", 3))
+               subject = talloc_asprintf (ctx, "Re: %s", subject);
+           g_mime_message_set_subject (reply, subject);
+       }
 
        from_addr = add_recipients_from_message (reply, config, message);
 
@@ -405,9 +482,6 @@ notmuch_reply_format_default(void *ctx, notmuch_config_t *config, notmuch_query_
        g_mime_object_set_header (GMIME_OBJECT (reply),
                                  "From", from_addr);
 
-       g_mime_object_set_header (GMIME_OBJECT (reply), "Bcc",
-                          notmuch_config_get_user_primary_email (config));
-
        in_reply_to = talloc_asprintf (ctx, "<%s>",
                             notmuch_message_get_message_id (message));
 
@@ -431,7 +505,8 @@ notmuch_reply_format_default(void *ctx, notmuch_config_t *config, notmuch_query_
                notmuch_message_get_header (message, "date"),
                notmuch_message_get_header (message, "from"));
 
-       show_message_body (notmuch_message_get_filename (message), reply_part);
+       show_message_body (notmuch_message_get_filename (message),
+                          reply_part, NULL);
 
        notmuch_message_destroy (message);
     }
@@ -482,9 +557,6 @@ notmuch_reply_format_headers_only(void *ctx, notmuch_config_t *config, notmuch_q
 
        (void)add_recipients_from_message (reply, config, message);
 
-       g_mime_object_set_header (GMIME_OBJECT (reply), "Bcc",
-                          notmuch_config_get_user_primary_email (config));
-
        reply_headers = g_mime_object_to_string (GMIME_OBJECT (reply));
        printf ("%s", reply_headers);
        free (reply_headers);