X-Git-Url: https://git.notmuchmail.org/git?p=notmuch;a=blobdiff_plain;f=notmuch-reply.c;h=ceb4f39bc587b7fe21a9ec04289f362cfbbba644;hp=daad453efb0907aba71d7510a1c2745886cf3bab;hb=HEAD;hpb=5e438d95c4671844cf4fddbd16c5744f41cda85c diff --git a/notmuch-reply.c b/notmuch-reply.c index daad453e..44297251 100644 --- a/notmuch-reply.c +++ b/notmuch-reply.c @@ -25,74 +25,66 @@ #include "sprinter.h" static void -show_reply_headers (GMimeMessage *message) +show_reply_headers (GMimeStream *stream, GMimeMessage *message) { - 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); - /* 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); + /* Output RFC 2822 formatted (and RFC 2047 encoded) headers. */ + if (g_mime_object_write_to_stream (GMIME_OBJECT (message), NULL, stream) < 0) { + INTERNAL_ERROR ("failed to write headers to stdout\n"); } } static void -format_part_reply (mime_node_t *node) +format_part_reply (GMimeStream *stream, mime_node_t *node) { int i; if (node->envelope_file) { - printf ("On %s, %s wrote:\n", - notmuch_message_get_header (node->envelope_file, "date"), - notmuch_message_get_header (node->envelope_file, "from")); + g_mime_stream_printf (stream, "On %s, %s wrote:\n", + notmuch_message_get_header (node->envelope_file, "date"), + notmuch_message_get_header (node->envelope_file, "from")); } else if (GMIME_IS_MESSAGE (node->part)) { GMimeMessage *message = GMIME_MESSAGE (node->part); - InternetAddressList *recipients; - const char *recipients_string; + char *recipients_string; - printf ("> From: %s\n", g_mime_message_get_sender (message)); - recipients = g_mime_message_get_recipients (message, GMIME_RECIPIENT_TYPE_TO); - recipients_string = internet_address_list_to_string (recipients, 0); + g_mime_stream_printf (stream, "> From: %s\n", g_mime_message_get_from_string (message)); + recipients_string = g_mime_message_get_address_string (message, GMIME_ADDRESS_TYPE_TO); if (recipients_string) - printf ("> To: %s\n", - recipients_string); - recipients = g_mime_message_get_recipients (message, GMIME_RECIPIENT_TYPE_CC); - recipients_string = internet_address_list_to_string (recipients, 0); + g_mime_stream_printf (stream, "> To: %s\n", + recipients_string); + g_free (recipients_string); + recipients_string = g_mime_message_get_address_string (message, GMIME_ADDRESS_TYPE_CC); if (recipients_string) - printf ("> Cc: %s\n", - recipients_string); - printf ("> Subject: %s\n", g_mime_message_get_subject (message)); - printf ("> Date: %s\n", g_mime_message_get_date_as_string (message)); - printf (">\n"); + g_mime_stream_printf (stream, "> Cc: %s\n", + recipients_string); + g_free (recipients_string); + g_mime_stream_printf (stream, "> Subject: %s\n", g_mime_message_get_subject (message)); + g_mime_stream_printf (stream, "> Date: %s\n", g_mime_message_get_date_string (node, message)); + g_mime_stream_printf (stream, ">\n"); } else if (GMIME_IS_PART (node->part)) { GMimeContentType *content_type = g_mime_object_get_content_type (node->part); GMimeContentDisposition *disposition = g_mime_object_get_content_disposition (node->part); if (g_mime_content_type_is_type (content_type, "application", "pgp-encrypted") || - g_mime_content_type_is_type (content_type, "application", "pgp-signature")) { - /* Ignore PGP/MIME cruft parts */ + g_mime_content_type_is_type (content_type, "application", "pgp-signature") || + g_mime_content_type_is_type (content_type, "application", "pkcs7-mime")) { + /* Ignore PGP/MIME and S/MIME cruft parts */ } else if (g_mime_content_type_is_type (content_type, "text", "*") && - !g_mime_content_type_is_type (content_type, "text", "html")) { - GMimeStream *stream_stdout = g_mime_stream_file_new (stdout); - g_mime_stream_file_set_owner (GMIME_STREAM_FILE (stream_stdout), FALSE); - show_text_part_content (node->part, stream_stdout, NOTMUCH_SHOW_TEXT_PART_REPLY); - g_object_unref(stream_stdout); + ! g_mime_content_type_is_type (content_type, "text", "html")) { + show_text_part_content (node->part, stream, NOTMUCH_SHOW_TEXT_PART_REPLY); } else if (disposition && strcasecmp (g_mime_content_disposition_get_disposition (disposition), GMIME_DISPOSITION_ATTACHMENT) == 0) { const char *filename = g_mime_part_get_filename (GMIME_PART (node->part)); - printf ("Attachment: %s (%s)\n", filename, - g_mime_content_type_to_string (content_type)); + g_mime_stream_printf (stream, "Attachment: %s (%s)\n", filename, + g_mime_content_type_get_mime_type (content_type)); } else { - printf ("Non-text part: %s\n", - g_mime_content_type_to_string (content_type)); + g_mime_stream_printf (stream, "Non-text part: %s\n", + g_mime_content_type_get_mime_type (content_type)); } } for (i = 0; i < node->nchildren; i++) - format_part_reply (mime_node_child (node, i)); + format_part_reply (stream, mime_node_child (node, i)); } typedef enum { @@ -102,7 +94,7 @@ typedef enum { } address_match_t; /* Match given string against given address according to mode. */ -static notmuch_bool_t +static bool match_address (const char *str, const char *address, address_match_t mode) { switch (mode) { @@ -114,31 +106,32 @@ match_address (const char *str, const char *address, address_match_t mode) return strcasecmp (address, str) == 0; } - return FALSE; + return false; } /* Match given string against user's configured "primary" and "other" * addresses according to mode. */ static const char * -address_match (const char *str, notmuch_config_t *config, address_match_t mode) +address_match (const char *str, notmuch_database_t *notmuch, address_match_t mode) { const char *primary; - const char **other; - size_t i, other_len; + notmuch_config_values_t *other = NULL; - if (!str || *str == '\0') + if (! str || *str == '\0') return NULL; - primary = notmuch_config_get_user_primary_email (config); + primary = notmuch_config_get (notmuch, NOTMUCH_CONFIG_PRIMARY_EMAIL); if (match_address (str, primary, mode)) return primary; - other = notmuch_config_get_user_other_email (config, &other_len); - for (i = 0; i < other_len; i++) { - if (match_address (str, other[i], mode)) - return other[i]; - } + for (other = notmuch_config_get_values (notmuch, NOTMUCH_CONFIG_OTHER_EMAIL); + notmuch_config_values_valid (other); + notmuch_config_values_move_to_next (other)) { + const char *addr = notmuch_config_values_get (other); + if (match_address (str, addr, mode)) + return addr; + } return NULL; } @@ -146,26 +139,26 @@ address_match (const char *str, notmuch_config_t *config, address_match_t mode) * user's "primary" or "other" addresses. If so, return the matching * address, NULL otherwise. */ static const char * -user_address_in_string (const char *str, notmuch_config_t *config) +user_address_in_string (const char *str, notmuch_database_t *notmuch) { - return address_match (str, config, USER_ADDRESS_IN_STRING); + return address_match (str, notmuch, USER_ADDRESS_IN_STRING); } /* Do any of the addresses configured as one of the user's "primary" * or "other" addresses contain the given string. If so, return the * matching address, NULL otherwise. */ static const char * -string_in_user_address (const char *str, notmuch_config_t *config) +string_in_user_address (const char *str, notmuch_database_t *notmuch) { - return address_match (str, config, STRING_IN_USER_ADDRESS); + return address_match (str, notmuch, STRING_IN_USER_ADDRESS); } /* Is the given address configured as one of the user's "primary" or * "other" addresses. */ -static notmuch_bool_t -address_is_users (const char *address, notmuch_config_t *config) +static bool +address_is_users (const char *address, notmuch_database_t *notmuch) { - return address_match (address, config, STRING_IS_USER_ADDRESS) != NULL; + return address_match (address, notmuch, STRING_IS_USER_ADDRESS) != NULL; } /* Scan addresses in 'list'. @@ -183,15 +176,18 @@ address_is_users (const char *address, notmuch_config_t *config) */ static unsigned int scan_address_list (InternetAddressList *list, - notmuch_config_t *config, + notmuch_database_t *notmuch, GMimeMessage *message, - GMimeRecipientType type, + GMimeAddressType type, const char **user_from) { InternetAddress *address; int i; unsigned int n = 0; + if (list == NULL) + return 0; + for (i = 0; i < internet_address_list_length (list); i++) { address = internet_address_list_get_address (list, i); if (INTERNET_ADDRESS_IS_GROUP (address)) { @@ -200,10 +196,7 @@ scan_address_list (InternetAddressList *list, group = INTERNET_ADDRESS_GROUP (address); group_list = internet_address_group_get_members (group); - if (group_list == NULL) - continue; - - n += scan_address_list (group_list, config, message, type, user_from); + n += scan_address_list (group_list, notmuch, message, type, user_from); } else { InternetAddressMailbox *mailbox; const char *name; @@ -214,11 +207,11 @@ scan_address_list (InternetAddressList *list, name = internet_address_get_name (address); addr = internet_address_mailbox_get_addr (mailbox); - if (address_is_users (addr, config)) { + if (address_is_users (addr, notmuch)) { if (user_from && *user_from == NULL) *user_from = addr; } else if (message) { - g_mime_message_add_recipient (message, type, name, addr); + g_mime_message_add_mailbox (message, type, name, addr); n++; } } @@ -227,68 +220,95 @@ scan_address_list (InternetAddressList *list, return n; } -/* Scan addresses in 'recipients'. - * - * See the documentation of scan_address_list() above. This function - * does exactly the same, but converts 'recipients' to an - * InternetAddressList first. - */ -static unsigned int -scan_address_string (const char *recipients, - notmuch_config_t *config, - GMimeMessage *message, - GMimeRecipientType type, - const char **user_from) -{ - InternetAddressList *list; - - if (recipients == NULL) - return 0; - - list = internet_address_list_parse_string (recipients); - if (list == NULL) - return 0; - - return scan_address_list (list, config, message, type, user_from); -} - /* Does the address in the Reply-To header of 'message' already appear * in either the 'To' or 'Cc' header of the message? */ -static int -reply_to_header_is_redundant (notmuch_message_t *message) +static bool +reply_to_header_is_redundant (GMimeMessage *message, + InternetAddressList *reply_to_list) { - const char *reply_to, *to, *cc, *addr; - InternetAddressList *list; + const char *addr, *reply_to; InternetAddress *address; InternetAddressMailbox *mailbox; + InternetAddressList *recipients; + bool ret = false; + int i; - reply_to = notmuch_message_get_header (message, "reply-to"); - if (reply_to == NULL || *reply_to == '\0') - return 0; - - list = internet_address_list_parse_string (reply_to); - - if (internet_address_list_length (list) != 1) + if (reply_to_list == NULL || + internet_address_list_length (reply_to_list) != 1) return 0; - address = internet_address_list_get_address (list, 0); + address = internet_address_list_get_address (reply_to_list, 0); if (INTERNET_ADDRESS_IS_GROUP (address)) return 0; mailbox = INTERNET_ADDRESS_MAILBOX (address); - addr = internet_address_mailbox_get_addr (mailbox); + reply_to = internet_address_mailbox_get_addr (mailbox); - to = notmuch_message_get_header (message, "to"); - cc = notmuch_message_get_header (message, "cc"); + recipients = g_mime_message_get_all_recipients (message); - if ((to && strstr (to, addr) != 0) || - (cc && strstr (cc, addr) != 0)) - { - return 1; + for (i = 0; i < internet_address_list_length (recipients); i++) { + address = internet_address_list_get_address (recipients, i); + if (INTERNET_ADDRESS_IS_GROUP (address)) + continue; + + mailbox = INTERNET_ADDRESS_MAILBOX (address); + addr = internet_address_mailbox_get_addr (mailbox); + if (strcmp (addr, reply_to) == 0) { + ret = true; + break; + } } - return 0; + g_object_unref (G_OBJECT (recipients)); + + return ret; +} + +static InternetAddressList * +get_sender (GMimeMessage *message) +{ + InternetAddressList *reply_to_list; + + reply_to_list = g_mime_message_get_reply_to_list (message); + if (reply_to_list && + internet_address_list_length (reply_to_list) > 0) { + /* + * Some mailing lists munge the Reply-To header despite it + * being A Bad Thing, see + * http://marc.merlins.org/netrants/reply-to-harmful.html + * + * The munging is easy to detect, because it results in a + * redundant reply-to header, (with an address that already + * exists in either To or Cc). So in this case, we ignore the + * Reply-To field and use the From header. This ensures the + * original sender will get the reply even if not subscribed + * to the list. Note that the address in the Reply-To header + * will always appear in the reply if reply_all is true. + */ + if (! reply_to_header_is_redundant (message, reply_to_list)) + return reply_to_list; + } + + return g_mime_message_get_from (message); +} + +static InternetAddressList * +get_to (GMimeMessage *message) +{ + return g_mime_message_get_addresses (message, GMIME_ADDRESS_TYPE_TO); +} + +static InternetAddressList * +get_cc (GMimeMessage *message) +{ + return g_mime_message_get_addresses (message, GMIME_ADDRESS_TYPE_CC); +} + +static InternetAddressList * +get_bcc (GMimeMessage *message) +{ + return g_mime_message_get_addresses (message, GMIME_ADDRESS_TYPE_BCC); } /* Augment the recipients of 'reply' from the "Reply-to:", "From:", @@ -305,53 +325,32 @@ reply_to_header_is_redundant (notmuch_message_t *message) */ static const char * add_recipients_from_message (GMimeMessage *reply, - notmuch_config_t *config, - notmuch_message_t *message, - notmuch_bool_t reply_all) + notmuch_database_t *notmuch, + GMimeMessage *message, + bool reply_all) { struct { - const char *header; - const char *fallback; - GMimeRecipientType recipient_type; + InternetAddressList * (*get_header)(GMimeMessage *message); + GMimeAddressType recipient_type; } reply_to_map[] = { - { "reply-to", "from", GMIME_RECIPIENT_TYPE_TO }, - { "to", NULL, GMIME_RECIPIENT_TYPE_TO }, - { "cc", NULL, GMIME_RECIPIENT_TYPE_CC }, - { "bcc", NULL, GMIME_RECIPIENT_TYPE_BCC } + { get_sender, GMIME_ADDRESS_TYPE_TO }, + { get_to, GMIME_ADDRESS_TYPE_TO }, + { get_cc, GMIME_ADDRESS_TYPE_CC }, + { get_bcc, GMIME_ADDRESS_TYPE_BCC }, }; const char *from_addr = NULL; unsigned int i; unsigned int n = 0; - /* Some mailing lists munge the Reply-To header despite it being A Bad - * Thing, see http://marc.merlins.org/netrants/reply-to-harmful.html - * - * The munging is easy to detect, because it results in a - * redundant reply-to header, (with an address that already exists - * in either To or Cc). So in this case, we ignore the Reply-To - * field and use the From header. This ensures the original sender - * will get the reply even if not subscribed to the list. Note - * that the address in the Reply-To header will always appear in - * the reply if reply_all is true. - */ - if (reply_to_header_is_redundant (message)) { - reply_to_map[0].header = "from"; - reply_to_map[0].fallback = NULL; - } - for (i = 0; i < ARRAY_SIZE (reply_to_map); i++) { - const char *recipients; + InternetAddressList *recipients; - recipients = notmuch_message_get_header (message, - reply_to_map[i].header); - if ((recipients == NULL || recipients[0] == '\0') && reply_to_map[i].fallback) - recipients = notmuch_message_get_header (message, - reply_to_map[i].fallback); + recipients = reply_to_map[i].get_header (message); - n += scan_address_string (recipients, config, reply, - reply_to_map[i].recipient_type, &from_addr); + n += scan_address_list (recipients, notmuch, reply, + reply_to_map[i].recipient_type, &from_addr); - if (!reply_all && n) { + if (! reply_all && n) { /* Stop adding new recipients in reply-to-sender mode if * we have added some recipient(s) above. * @@ -368,6 +367,14 @@ add_recipients_from_message (GMimeMessage *reply, } } + /* If no recipients were added but we found one of the user's + * addresses to use as a from address then the message is from the + * user to the user - add the discovered from address to the list + * of recipients so that the reply goes back to the user. + */ + if (n == 0 && from_addr) + g_mime_message_add_mailbox (reply, GMIME_ADDRESS_TYPE_TO, NULL, from_addr); + return from_addr; } @@ -378,7 +385,7 @@ add_recipients_from_message (GMimeMessage *reply, * Return the address that was found, if any, and NULL otherwise. */ static const char * -guess_from_in_received_for (notmuch_config_t *config, const char *received) +guess_from_in_received_for (notmuch_database_t *notmuch, const char *received) { const char *ptr; @@ -386,7 +393,7 @@ guess_from_in_received_for (notmuch_config_t *config, const char *received) if (! ptr) return NULL; - return user_address_in_string (ptr, config); + return user_address_in_string (ptr, notmuch); } /* @@ -402,7 +409,7 @@ guess_from_in_received_for (notmuch_config_t *config, const char *received) * 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) +guess_from_in_received_by (notmuch_database_t *notmuch, const char *received) { const char *addr; const char *by = received; @@ -413,7 +420,7 @@ guess_from_in_received_by (notmuch_config_t *config, const char *received) if (*by == '\0') break; mta = xstrdup (by); - token = strtok(mta," \t"); + token = strtok (mta, " \t"); if (token == NULL) { free (mta); break; @@ -440,7 +447,7 @@ guess_from_in_received_by (notmuch_config_t *config, const char *received) */ *(tld - 1) = '.'; - addr = string_in_user_address (domain, config); + addr = string_in_user_address (domain, notmuch); if (addr) { free (mta); return addr; @@ -457,18 +464,19 @@ guess_from_in_received_by (notmuch_config_t *config, const char *received) * (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. + * The Received: header is among special ones 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) +guess_from_in_received_headers (notmuch_message_t *message) { const char *received, *addr; char *sanitized; + notmuch_database_t *notmuch = notmuch_message_get_database (message); + received = notmuch_message_get_header (message, "received"); if (! received) return NULL; @@ -477,9 +485,9 @@ guess_from_in_received_headers (notmuch_config_t *config, if (! sanitized) return NULL; - addr = guess_from_in_received_for (config, sanitized); + addr = guess_from_in_received_for (notmuch, sanitized); if (! addr) - addr = guess_from_in_received_by (config, sanitized); + addr = guess_from_in_received_by (notmuch, sanitized); talloc_free (sanitized); @@ -491,10 +499,13 @@ guess_from_in_received_headers (notmuch_config_t *config, * headers: Envelope-To, X-Original-To, and Delivered-To (searched in * that order). * + * The Delivered-To: header is among special ones in our get_header + * function and is always concatenated. + * * 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) +get_from_in_to_headers (notmuch_message_t *message) { size_t i; const char *tohdr, *addr; @@ -504,11 +515,13 @@ get_from_in_to_headers (notmuch_config_t *config, notmuch_message_t *message) "Delivered-To", }; + notmuch_database_t *notmuch = notmuch_message_get_database (message); + 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); + addr = user_address_in_string (tohdr, notmuch); if (addr) return addr; } @@ -517,20 +530,21 @@ get_from_in_to_headers (notmuch_config_t *config, notmuch_message_t *message) } static GMimeMessage * -create_reply_message(void *ctx, - notmuch_config_t *config, - notmuch_message_t *message, - notmuch_bool_t reply_all, - notmuch_bool_t limited) +create_reply_message (void *ctx, + notmuch_message_t *message, + GMimeMessage *mime_message, + bool reply_all, + bool limited) { const char *subject, *from_addr = NULL; const char *in_reply_to, *orig_references, *references; - + notmuch_database_t *notmuch = notmuch_message_get_database (message); /* * Use the below header order for limited headers, "pretty" order * otherwise. */ GMimeMessage *reply = g_mime_message_new (limited ? 0 : 1); + if (reply == NULL) { fprintf (stderr, "Out of memory\n"); return NULL; @@ -539,7 +553,7 @@ create_reply_message(void *ctx, in_reply_to = talloc_asprintf (ctx, "<%s>", notmuch_message_get_message_id (message)); - g_mime_object_set_header (GMIME_OBJECT (reply), "In-Reply-To", in_reply_to); + g_mime_object_set_header (GMIME_OBJECT (reply), "In-Reply-To", in_reply_to, NULL); orig_references = notmuch_message_get_header (message, "references"); if (orig_references && *orig_references) @@ -548,10 +562,10 @@ create_reply_message(void *ctx, else references = talloc_strdup (ctx, in_reply_to); - g_mime_object_set_header (GMIME_OBJECT (reply), "References", references); + g_mime_object_set_header (GMIME_OBJECT (reply), "References", references, NULL); - from_addr = add_recipients_from_message (reply, config, - message, reply_all); + from_addr = add_recipients_from_message (reply, notmuch, + mime_message, reply_all); /* The above is all that is needed for limited headers. */ if (limited) @@ -570,7 +584,7 @@ create_reply_message(void *ctx, * Delivered-To: headers. */ if (from_addr == NULL) - from_addr = get_from_in_to_headers (config, message); + from_addr = get_from_in_to_headers (message); /* * Check for a (for ) clause in Received: headers, @@ -578,113 +592,27 @@ create_reply_message(void *ctx, * of Received: headers */ if (from_addr == NULL) - from_addr = guess_from_in_received_headers (config, message); + from_addr = guess_from_in_received_headers (message); /* Default to user's primary address. */ if (from_addr == NULL) - from_addr = notmuch_config_get_user_primary_email (config); + from_addr = notmuch_config_get (notmuch, NOTMUCH_CONFIG_PRIMARY_EMAIL); from_addr = talloc_asprintf (ctx, "%s <%s>", - notmuch_config_get_user_name (config), + notmuch_config_get (notmuch, NOTMUCH_CONFIG_USER_NAME), from_addr); - g_mime_object_set_header (GMIME_OBJECT (reply), "From", from_addr); + g_mime_object_set_header (GMIME_OBJECT (reply), "From", from_addr, NULL); - subject = notmuch_message_get_header (message, "subject"); + subject = g_mime_message_get_subject (mime_message); if (subject) { if (strncasecmp (subject, "Re:", 3)) subject = talloc_asprintf (ctx, "Re: %s", subject); - g_mime_message_set_subject (reply, subject); + g_mime_message_set_subject (reply, subject, NULL); } return reply; } -static int -notmuch_reply_format_default(void *ctx, - notmuch_config_t *config, - notmuch_message_t *message, - notmuch_show_params_t *params, - notmuch_bool_t reply_all, - unused (sprinter_t *sp)) -{ - GMimeMessage *reply; - mime_node_t *node; - - if (mime_node_open (ctx, message, ¶ms->crypto, &node)) - return 1; - - reply = create_reply_message (ctx, config, message, reply_all, FALSE); - if (!reply) - return 1; - - show_reply_headers (reply); - format_part_reply (node); - - g_object_unref (G_OBJECT (reply)); - talloc_free (node); - - return 0; -} - -static int -notmuch_reply_format_sprinter(void *ctx, - notmuch_config_t *config, - notmuch_message_t *message, - notmuch_show_params_t *params, - notmuch_bool_t reply_all, - sprinter_t *sp) -{ - GMimeMessage *reply; - mime_node_t *node; - - if (mime_node_open (ctx, message, ¶ms->crypto, &node)) - return 1; - - reply = create_reply_message (ctx, config, message, reply_all, FALSE); - if (!reply) - return 1; - - sp->begin_map (sp); - - /* The headers of the reply message we've created */ - sp->map_key (sp, "reply-headers"); - format_headers_sprinter (sp, reply, TRUE); - - /* Start the original */ - sp->map_key (sp, "original"); - format_part_sprinter (ctx, sp, node, TRUE, TRUE, FALSE); - - /* End */ - sp->end (sp); - - g_object_unref (G_OBJECT (reply)); - talloc_free (node); - - return 0; -} - -/* This format is currently tuned for a git send-email --notmuch hook */ -static int -notmuch_reply_format_headers_only(void *ctx, - notmuch_config_t *config, - notmuch_message_t *message, - unused (notmuch_show_params_t *params), - notmuch_bool_t reply_all, - unused (sprinter_t *sp)) -{ - GMimeMessage *reply; - - reply = create_reply_message (ctx, config, message, reply_all, TRUE); - if (!reply) - return 1; - - show_reply_headers (reply); - - g_object_unref (G_OBJECT (reply)); - - return 0; -} - enum { FORMAT_DEFAULT, FORMAT_JSON, @@ -692,114 +620,140 @@ enum { FORMAT_HEADERS_ONLY, }; -static int do_reply(notmuch_config_t *config, - notmuch_query_t *query, - notmuch_show_params_t *params, - int format, - notmuch_bool_t reply_all) +static int +do_reply (notmuch_database_t *notmuch, + notmuch_query_t *query, + notmuch_show_params_t *params, + int format, + bool reply_all) { + GMimeMessage *reply; + mime_node_t *node; notmuch_messages_t *messages; notmuch_message_t *message; notmuch_status_t status; struct sprinter *sp = NULL; - int ret = 0; - int (*reply_format_func) (void *ctx, - notmuch_config_t *config, - notmuch_message_t *message, - notmuch_show_params_t *params, - notmuch_bool_t reply_all, - struct sprinter *sp); if (format == FORMAT_JSON || format == FORMAT_SEXP) { unsigned count; - status = notmuch_query_count_messages_st (query, &count); + status = notmuch_query_count_messages (query, &count); if (print_status_query ("notmuch reply", query, status)) return 1; if (count != 1) { - fprintf (stderr, "Error: search term did not match precisely one message (matched %d messages).\n", count); + fprintf (stderr, + "Error: search term did not match precisely one message (matched %u messages).\n", + count); return 1; } - } - if (format == FORMAT_HEADERS_ONLY) { - reply_format_func = notmuch_reply_format_headers_only; - } else if (format == FORMAT_JSON) { - reply_format_func = notmuch_reply_format_sprinter; - sp = sprinter_json_create (config, stdout); - } else if (format == FORMAT_SEXP) { - reply_format_func = notmuch_reply_format_sprinter; - sp = sprinter_sexp_create (config, stdout); - } else { - reply_format_func = notmuch_reply_format_default; + if (format == FORMAT_JSON) + sp = sprinter_json_create (notmuch, stdout); + else + sp = sprinter_sexp_create (notmuch, stdout); } - status = notmuch_query_search_messages_st (query, &messages); + status = notmuch_query_search_messages (query, &messages); if (print_status_query ("notmuch reply", query, status)) return 1; for (; notmuch_messages_valid (messages); - notmuch_messages_move_to_next (messages)) - { + notmuch_messages_move_to_next (messages)) { message = notmuch_messages_get (messages); - ret = reply_format_func(config, config, message, params, reply_all, sp); + if (mime_node_open (notmuch, message, params->duplicate, ¶ms->crypto, &node)) + return 1; - notmuch_message_destroy (message); + reply = create_reply_message (notmuch, message, + GMIME_MESSAGE (node->part), reply_all, + format == FORMAT_HEADERS_ONLY); + if (! reply) + return 1; - if (ret) - break; + if (format == FORMAT_JSON || format == FORMAT_SEXP) { + sp->begin_map (sp); + + /* The headers of the reply message we've created */ + sp->map_key (sp, "reply-headers"); + /* FIXME: send msg_crypto here to avoid killing the + * subject line on reply to encrypted messages! */ + format_headers_sprinter (sp, reply, true, NULL); + + /* Start the original */ + sp->map_key (sp, "original"); + format_part_sprinter (notmuch, sp, node, params->duplicate, true, false); + + /* End */ + sp->end (sp); + } else { + GMimeStream *stream_stdout = stream_stdout = g_mime_stream_stdout_new (); + if (stream_stdout) { + show_reply_headers (stream_stdout, reply); + if (format == FORMAT_DEFAULT) + format_part_reply (stream_stdout, node); + } + g_mime_stream_flush (stream_stdout); + g_object_unref (stream_stdout); + } + + g_object_unref (G_OBJECT (reply)); + talloc_free (node); + + notmuch_message_destroy (message); } - return ret; + return 0; } int -notmuch_reply_command (notmuch_config_t *config, int argc, char *argv[]) +notmuch_reply_command (notmuch_database_t *notmuch, int argc, char *argv[]) { - notmuch_database_t *notmuch; notmuch_query_t *query; char *query_string; int opt_index; notmuch_show_params_t params = { .part = -1, - .crypto = { - .verify = FALSE, - .decrypt = FALSE, - .gpgpath = NULL - } + .duplicate = 0, + .crypto = { .decrypt = NOTMUCH_DECRYPT_AUTO }, }; int format = FORMAT_DEFAULT; - int reply_all = TRUE; + int reply_all = true; + notmuch_status_t status; notmuch_opt_desc_t options[] = { - { NOTMUCH_OPT_KEYWORD, &format, "format", 'f', - (notmuch_keyword_t []){ { "default", FORMAT_DEFAULT }, - { "json", FORMAT_JSON }, - { "sexp", FORMAT_SEXP }, - { "headers-only", FORMAT_HEADERS_ONLY }, - { 0, 0 } } }, - { NOTMUCH_OPT_INT, ¬much_format_version, "format-version", 0, 0 }, - { NOTMUCH_OPT_KEYWORD, &reply_all, "reply-to", 'r', - (notmuch_keyword_t []){ { "all", TRUE }, - { "sender", FALSE }, - { 0, 0 } } }, - { NOTMUCH_OPT_BOOLEAN, ¶ms.crypto.decrypt, "decrypt", 'd', 0 }, - { NOTMUCH_OPT_INHERIT, (void *) ¬much_shared_options, NULL, 0, 0 }, - { 0, 0, 0, 0, 0 } + { .opt_keyword = &format, .name = "format", .keywords = + (notmuch_keyword_t []){ { "default", FORMAT_DEFAULT }, + { "json", FORMAT_JSON }, + { "sexp", FORMAT_SEXP }, + { "headers-only", FORMAT_HEADERS_ONLY }, + { 0, 0 } } }, + { .opt_int = ¬much_format_version, .name = "format-version" }, + { .opt_keyword = &reply_all, .name = "reply-to", .keywords = + (notmuch_keyword_t []){ { "all", true }, + { "sender", false }, + { 0, 0 } } }, + { .opt_keyword = (int *) (¶ms.crypto.decrypt), .name = "decrypt", + .keyword_no_arg_value = "true", .keywords = + (notmuch_keyword_t []){ { "false", NOTMUCH_DECRYPT_FALSE }, + { "auto", NOTMUCH_DECRYPT_AUTO }, + { "true", NOTMUCH_DECRYPT_NOSTASH }, + { 0, 0 } } }, + { .opt_int = ¶ms.duplicate, .name = "duplicate" }, + { .opt_inherit = notmuch_shared_options }, + { } }; opt_index = parse_arguments (argc, argv, options, 1); if (opt_index < 0) return EXIT_FAILURE; - notmuch_process_shared_options (argv[0]); + notmuch_process_shared_options (notmuch, argv[0]); notmuch_exit_if_unsupported_format (); - query_string = query_string_from_args (config, argc-opt_index, argv+opt_index); + query_string = query_string_from_args (notmuch, argc - opt_index, argv + opt_index); if (query_string == NULL) { fprintf (stderr, "Out of memory\n"); return EXIT_FAILURE; @@ -810,24 +764,16 @@ notmuch_reply_command (notmuch_config_t *config, int argc, char *argv[]) return EXIT_FAILURE; } - params.crypto.gpgpath = notmuch_config_get_crypto_gpg_path (config); - - if (notmuch_database_open (notmuch_config_get_database_path (config), - NOTMUCH_DATABASE_MODE_READ_ONLY, ¬much)) - return EXIT_FAILURE; - - notmuch_exit_if_unmatched_db_uuid (notmuch); - - query = notmuch_query_create (notmuch, query_string); - if (query == NULL) { - fprintf (stderr, "Out of memory\n"); + status = notmuch_query_create_with_syntax (notmuch, query_string, + shared_option_query_syntax (), + &query); + if (print_status_database ("notmuch reply", notmuch, status)) return EXIT_FAILURE; - } - if (do_reply (config, query, ¶ms, format, reply_all) != 0) + if (do_reply (notmuch, query, ¶ms, format, reply_all) != 0) return EXIT_FAILURE; - notmuch_crypto_cleanup (¶ms.crypto); + _notmuch_crypto_cleanup (¶ms.crypto); notmuch_query_destroy (query); notmuch_database_destroy (notmuch);