mirror of
https://git.notmuchmail.org/git/notmuch
synced 2024-11-24 03:48:10 +01:00
reply: Add a JSON reply format.
This new JSON format for replies includes headers generated for a reply message as well as the headers of the original message. Using this data, a client can intelligently create a reply. For example, the emacs client will be able to create replies with quoted HTML parts by parsing the HTML parts.
This commit is contained in:
parent
06a34f1407
commit
1904b01b96
4 changed files with 79 additions and 12 deletions
|
@ -62,7 +62,7 @@
|
|||
#define STRINGIFY(s) STRINGIFY_(s)
|
||||
#define STRINGIFY_(s) #s
|
||||
|
||||
struct mime_node;
|
||||
typedef struct mime_node mime_node_t;
|
||||
struct notmuch_show_params;
|
||||
|
||||
typedef struct notmuch_show_format {
|
||||
|
@ -191,6 +191,12 @@ show_message_body (notmuch_message_t *message,
|
|||
notmuch_status_t
|
||||
show_one_part (const char *filename, int part);
|
||||
|
||||
void
|
||||
format_part_json (const void *ctx, mime_node_t *node, notmuch_bool_t first);
|
||||
|
||||
void
|
||||
format_headers_json (const void *ctx, GMimeMessage *message, notmuch_bool_t reply);
|
||||
|
||||
char *
|
||||
json_quote_chararray (const void *ctx, const char *str, const size_t len);
|
||||
|
||||
|
@ -288,7 +294,7 @@ debugger_is_active (void);
|
|||
* parts. Message-type parts have one child, multipart-type parts
|
||||
* have multiple children, and leaf parts have zero children.
|
||||
*/
|
||||
typedef struct mime_node {
|
||||
struct mime_node {
|
||||
/* The MIME object of this part. This will be a GMimeMessage,
|
||||
* GMimePart, GMimeMultipart, or a subclass of one of these.
|
||||
*
|
||||
|
@ -351,7 +357,7 @@ typedef struct mime_node {
|
|||
* number to assign it (or -1 if unknown). */
|
||||
int next_child;
|
||||
int next_part_num;
|
||||
} mime_node_t;
|
||||
};
|
||||
|
||||
/* Construct a new MIME node pointing to the root message part of
|
||||
* message. If cryptoctx is non-NULL, it will be used to verify
|
||||
|
|
|
@ -604,6 +604,51 @@ notmuch_reply_format_default(void *ctx,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
notmuch_reply_format_json(void *ctx,
|
||||
notmuch_config_t *config,
|
||||
notmuch_query_t *query,
|
||||
notmuch_show_params_t *params,
|
||||
notmuch_bool_t reply_all)
|
||||
{
|
||||
GMimeMessage *reply;
|
||||
notmuch_messages_t *messages;
|
||||
notmuch_message_t *message;
|
||||
mime_node_t *node;
|
||||
|
||||
if (notmuch_query_count_messages (query) != 1) {
|
||||
fprintf (stderr, "Error: search term did not match precisely one message.\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
messages = notmuch_query_search_messages (query);
|
||||
message = notmuch_messages_get (messages);
|
||||
if (mime_node_open (ctx, message, params->cryptoctx, params->decrypt,
|
||||
&node) != NOTMUCH_STATUS_SUCCESS)
|
||||
return 1;
|
||||
|
||||
reply = create_reply_message (ctx, config, message, reply_all);
|
||||
if (!reply)
|
||||
return 1;
|
||||
|
||||
/* The headers of the reply message we've created */
|
||||
printf ("{\"reply-headers\": ");
|
||||
format_headers_json (ctx, reply, TRUE);
|
||||
g_object_unref (G_OBJECT (reply));
|
||||
reply = NULL;
|
||||
|
||||
/* Start the original */
|
||||
printf (", \"original\": ");
|
||||
|
||||
format_part_json (ctx, node, TRUE);
|
||||
|
||||
/* End */
|
||||
printf ("}\n");
|
||||
notmuch_message_destroy (message);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* This format is currently tuned for a git send-email --notmuch hook */
|
||||
static int
|
||||
notmuch_reply_format_headers_only(void *ctx,
|
||||
|
@ -666,6 +711,7 @@ notmuch_reply_format_headers_only(void *ctx,
|
|||
|
||||
enum {
|
||||
FORMAT_DEFAULT,
|
||||
FORMAT_JSON,
|
||||
FORMAT_HEADERS_ONLY,
|
||||
};
|
||||
|
||||
|
@ -685,6 +731,7 @@ notmuch_reply_command (void *ctx, int argc, char *argv[])
|
|||
notmuch_opt_desc_t options[] = {
|
||||
{ NOTMUCH_OPT_KEYWORD, &format, "format", 'f',
|
||||
(notmuch_keyword_t []){ { "default", FORMAT_DEFAULT },
|
||||
{ "json", FORMAT_JSON },
|
||||
{ "headers-only", FORMAT_HEADERS_ONLY },
|
||||
{ 0, 0 } } },
|
||||
{ NOTMUCH_OPT_KEYWORD, &reply_all, "reply-to", 'r',
|
||||
|
@ -703,6 +750,8 @@ notmuch_reply_command (void *ctx, int argc, char *argv[])
|
|||
|
||||
if (format == FORMAT_HEADERS_ONLY)
|
||||
reply_format_func = notmuch_reply_format_headers_only;
|
||||
else if (format == FORMAT_JSON)
|
||||
reply_format_func = notmuch_reply_format_json;
|
||||
else
|
||||
reply_format_func = notmuch_reply_format_default;
|
||||
|
||||
|
|
|
@ -200,8 +200,8 @@ _is_from_line (const char *line)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
format_headers_json (const void *ctx, GMimeMessage *message)
|
||||
void
|
||||
format_headers_json (const void *ctx, GMimeMessage *message, notmuch_bool_t reply)
|
||||
{
|
||||
void *local = talloc_new (ctx);
|
||||
InternetAddressList *recipients;
|
||||
|
@ -225,9 +225,22 @@ format_headers_json (const void *ctx, GMimeMessage *message)
|
|||
printf (", %s: %s",
|
||||
json_quote_str (local, "Cc"),
|
||||
json_quote_str (local, recipients_string));
|
||||
printf (", %s: %s}",
|
||||
|
||||
if (reply) {
|
||||
printf (", %s: %s",
|
||||
json_quote_str (local, "In-reply-to"),
|
||||
json_quote_str (local, g_mime_object_get_header (GMIME_OBJECT (message), "In-reply-to")));
|
||||
|
||||
printf (", %s: %s",
|
||||
json_quote_str (local, "References"),
|
||||
json_quote_str (local, g_mime_object_get_header (GMIME_OBJECT (message), "References")));
|
||||
} else {
|
||||
printf (", %s: %s",
|
||||
json_quote_str (local, "Date"),
|
||||
json_quote_str (local, g_mime_message_get_date_as_string (message)));
|
||||
}
|
||||
|
||||
printf ("}");
|
||||
|
||||
talloc_free (local);
|
||||
}
|
||||
|
@ -538,7 +551,7 @@ format_part_text (const void *ctx, mime_node_t *node,
|
|||
return NOTMUCH_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static void
|
||||
void
|
||||
format_part_json (const void *ctx, mime_node_t *node, notmuch_bool_t first)
|
||||
{
|
||||
/* Any changes to the JSON format should be reflected in the file
|
||||
|
@ -549,7 +562,7 @@ format_part_json (const void *ctx, mime_node_t *node, notmuch_bool_t first)
|
|||
format_message_json (ctx, node->envelope_file);
|
||||
|
||||
printf ("\"headers\": ");
|
||||
format_headers_json (ctx, GMIME_MESSAGE (node->part));
|
||||
format_headers_json (ctx, GMIME_MESSAGE (node->part), FALSE);
|
||||
|
||||
printf (", \"body\": [");
|
||||
format_part_json (ctx, mime_node_child (node, 0), first);
|
||||
|
@ -623,7 +636,7 @@ format_part_json (const void *ctx, mime_node_t *node, notmuch_bool_t first)
|
|||
} else if (GMIME_IS_MESSAGE (node->part)) {
|
||||
printf (", \"content\": [{");
|
||||
printf ("\"headers\": ");
|
||||
format_headers_json (local, GMIME_MESSAGE (node->part));
|
||||
format_headers_json (local, GMIME_MESSAGE (node->part), FALSE);
|
||||
|
||||
printf (", \"body\": [");
|
||||
terminator = "]}]";
|
||||
|
|
|
@ -613,7 +613,6 @@ EOF
|
|||
test_expect_equal_file OUTPUT EXPECTED
|
||||
|
||||
test_begin_subtest "'notmuch reply' to a multipart message with json format"
|
||||
test_subtest_known_broken
|
||||
notmuch reply --format=json 'id:87liy5ap00.fsf@yoom.home.cworth.org' | notmuch_json_show_sanitize >OUTPUT
|
||||
cat <<EOF >EXPECTED
|
||||
{"reply-headers": {"Subject": "Re: Multipart message",
|
||||
|
|
Loading…
Reference in a new issue