X-Git-Url: https://git.notmuchmail.org/git?p=notmuch;a=blobdiff_plain;f=notmuch-reply.c;h=47993d223090ee423b7c42126122dcd688215c8b;hp=3b2b58d194dfb8f1be470dfd6d04fbf2393af45c;hb=3fed6736a7ef8b8b1f05d0fabb136bdd3b5917ee;hpb=013d11c9f7c7f7876faf5035a5acb60924daf3c6 diff --git a/notmuch-reply.c b/notmuch-reply.c index 3b2b58d1..47993d22 100644 --- a/notmuch-reply.c +++ b/notmuch-reply.c @@ -21,28 +21,18 @@ */ #include "notmuch-client.h" -#include "gmime-filter-headers.h" #include "sprinter.h" static void show_reply_headers (GMimeMessage *message) { - GMimeStream *stream_stdout = NULL, *stream_filter = NULL; + GMimeStream *stream_stdout = NULL; stream_stdout = g_mime_stream_file_new (stdout); if (stream_stdout) { g_mime_stream_file_set_owner (GMIME_STREAM_FILE (stream_stdout), FALSE); - stream_filter = g_mime_stream_filter_new(stream_stdout); - if (stream_filter) { - // g_mime_object_write_to_stream will produce - // RFC2047-encoded headers, but we want to present the - // user with decoded headers and let whatever - // ultimately sends the mail do the RFC2047 encoding. - g_mime_stream_filter_add(GMIME_STREAM_FILTER(stream_filter), - g_mime_filter_headers_new()); - g_mime_object_write_to_stream(GMIME_OBJECT(message), stream_filter); - g_object_unref(stream_filter); - } + /* Output RFC 2822 formatted (and RFC 2047 encoded) headers. */ + g_mime_object_write_to_stream (GMIME_OBJECT(message), stream_stdout); g_object_unref(stream_stdout); } } @@ -379,78 +369,44 @@ add_recipients_from_message (GMimeMessage *reply, return from_addr; } +/* + * Look for the user's address in " for " in the + * received headers. + * + * Return the address that was found, if any, and NULL otherwise. + */ 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; - char *mta,*ptr,*token; - char *domain=NULL; - char *tld=NULL; - const char *delim=". \t"; - size_t i; + const char *ptr; - const char *to_headers[] = { - "Envelope-to", - "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 ) 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) + ptr = strstr (received, " for "); + if (! ptr) return NULL; - /* First we look for a " for " in the received - * header - */ - ptr = strstr (received, " for "); + return user_address_in_string (ptr, config); +} - /* Note: ptr potentially contains a list of email addresses. */ - addr = user_address_in_string (ptr, config); - if (addr) - return addr; - - /* 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) { +/* + * Parse all the " by MTA ..." parts in received headers to guess the + * email address that this was originally delivered to. + * + * 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; + + while ((by = strstr (by, " by ")) != NULL) { by += 4; if (*by == '\0') break; @@ -460,11 +416,12 @@ guess_from_received_header (notmuch_config_t *config, notmuch_message_t *message free (mta); 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; - while ((ptr = strsep (&token, delim)) != NULL) { + while ((ptr = strsep (&token, ". \t")) != NULL) { if (*ptr == '\0') continue; domain = tld; @@ -472,13 +429,14 @@ 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. - * 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. + /* + * 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) = '.'; + *(tld - 1) = '.'; addr = string_in_user_address (domain, config); if (addr) { @@ -492,6 +450,63 @@ guess_from_received_header (notmuch_config_t *config, notmuch_message_t *message 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 * create_reply_message(void *ctx, notmuch_config_t *config, @@ -518,9 +533,30 @@ create_reply_message(void *ctx, from_addr = add_recipients_from_message (reply, config, 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) + from_addr = get_from_in_to_headers (config, message); + + /* + * Check for a (for ) 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_received_header (config, message); + from_addr = guess_from_in_received_headers (config, message); + /* Default to user's primary address. */ if (from_addr == NULL) from_addr = notmuch_config_get_user_primary_email (config); @@ -537,9 +573,12 @@ create_reply_message(void *ctx, "In-Reply-To", in_reply_to); orig_references = notmuch_message_get_header (message, "references"); + if (!orig_references) + /* Treat errors like missing References headers. */ + orig_references = ""; references = talloc_asprintf (ctx, "%s%s%s", - orig_references ? orig_references : "", - orig_references ? " " : "", + *orig_references ? orig_references : "", + *orig_references ? " " : "", in_reply_to); g_mime_object_set_header (GMIME_OBJECT (reply), "References", references); @@ -628,7 +667,7 @@ notmuch_reply_format_sprinter(void *ctx, /* Start the original */ sp->map_key (sp, "original"); - format_part_sprinter (ctx, sp, node, TRUE, TRUE); + format_part_sprinter (ctx, sp, node, TRUE, TRUE, FALSE); /* End */ sp->end (sp); @@ -711,7 +750,7 @@ notmuch_reply_command (notmuch_config_t *config, int argc, char *argv[]) notmuch_database_t *notmuch; notmuch_query_t *query; char *query_string; - int opt_index, ret = 0; + int opt_index; int (*reply_format_func) (void *ctx, notmuch_config_t *config, notmuch_query_t *query, @@ -746,10 +785,8 @@ notmuch_reply_command (notmuch_config_t *config, int argc, char *argv[]) }; opt_index = parse_arguments (argc, argv, options, 1); - if (opt_index < 0) { - /* diagnostics already printed */ - return 1; - } + if (opt_index < 0) + return EXIT_FAILURE; if (format == FORMAT_HEADERS_ONLY) { reply_format_func = notmuch_reply_format_headers_only; @@ -768,30 +805,30 @@ notmuch_reply_command (notmuch_config_t *config, int argc, char *argv[]) query_string = query_string_from_args (config, argc-opt_index, argv+opt_index); if (query_string == NULL) { fprintf (stderr, "Out of memory\n"); - return 1; + return EXIT_FAILURE; } if (*query_string == '\0') { fprintf (stderr, "Error: notmuch reply requires at least one search term.\n"); - return 1; + return EXIT_FAILURE; } if (notmuch_database_open (notmuch_config_get_database_path (config), NOTMUCH_DATABASE_MODE_READ_ONLY, ¬much)) - return 1; + return EXIT_FAILURE; query = notmuch_query_create (notmuch, query_string); if (query == NULL) { fprintf (stderr, "Out of memory\n"); - return 1; + return EXIT_FAILURE; } if (reply_format_func (config, config, query, ¶ms, reply_all, sp) != 0) - return 1; + return EXIT_FAILURE; notmuch_crypto_cleanup (¶ms.crypto); notmuch_query_destroy (query); notmuch_database_destroy (notmuch); - return ret; + return EXIT_SUCCESS; }