+ const char *content_charset = g_mime_object_get_content_type_parameter (meta, "charset");
+ const char *cte = g_mime_object_get_header (meta, "content-transfer-encoding");
+ GMimeDataWrapper *wrapper = g_mime_part_get_content_object (part);
+ GMimeStream *stream = g_mime_data_wrapper_get_stream (wrapper);
+ ssize_t content_length = g_mime_stream_length (stream);
+
+ if (content_charset != NULL) {
+ sp->map_key (sp, "content-charset");
+ sp->string (sp, content_charset);
+ }
+ if (cte != NULL) {
+ sp->map_key (sp, "content-transfer-encoding");
+ sp->string (sp, cte);
+ }
+ if (content_length >= 0) {
+ sp->map_key (sp, "content-length");
+ sp->integer (sp, content_length);
+ }
+}
+
+void
+format_part_sprinter (const void *ctx, sprinter_t *sp, mime_node_t *node,
+ notmuch_bool_t first, notmuch_bool_t output_body,
+ notmuch_bool_t include_html)
+{
+ /* Any changes to the JSON or S-Expression format should be
+ * reflected in the file devel/schemata. */
+
+ if (node->envelope_file) {
+ sp->begin_map (sp);
+ format_message_sprinter (sp, node->envelope_file);
+
+ sp->map_key (sp, "headers");
+ format_headers_sprinter (sp, GMIME_MESSAGE (node->part), FALSE);
+
+ if (output_body) {
+ sp->map_key (sp, "body");
+ sp->begin_list (sp);
+ format_part_sprinter (ctx, sp, mime_node_child (node, 0), first, TRUE, include_html);
+ sp->end (sp);
+ }
+ sp->end (sp);
+ return;
+ }
+
+ /* The disposition and content-type metadata are associated with
+ * the envelope for message parts */
+ GMimeObject *meta = node->envelope_part ?
+ GMIME_OBJECT (node->envelope_part) : node->part;
+ GMimeContentType *content_type = g_mime_object_get_content_type (meta);
+ const char *cid = g_mime_object_get_content_id (meta);
+ const char *filename = GMIME_IS_PART (node->part) ?
+ g_mime_part_get_filename (GMIME_PART (node->part)) : NULL;
+ int nclose = 0;
+ int i;
+
+ sp->begin_map (sp);
+
+ sp->map_key (sp, "id");
+ sp->integer (sp, node->part_num);
+
+ if (node->decrypt_attempted) {
+ sp->map_key (sp, "encstatus");
+ sp->begin_list (sp);
+ sp->begin_map (sp);
+ sp->map_key (sp, "status");
+ sp->string (sp, node->decrypt_success ? "good" : "bad");
+ sp->end (sp);
+ sp->end (sp);
+ }
+
+ if (node->verify_attempted) {
+ sp->map_key (sp, "sigstatus");
+ format_part_sigstatus_sprinter (sp, node);
+ }
+
+ sp->map_key (sp, "content-type");
+ sp->string (sp, g_mime_content_type_to_string (content_type));
+
+ if (cid) {
+ sp->map_key (sp, "content-id");
+ sp->string (sp, cid);
+ }
+
+ if (filename) {
+ sp->map_key (sp, "filename");
+ sp->string (sp, filename);
+ }
+
+ if (GMIME_IS_PART (node->part)) {
+ /* For non-HTML text parts, we include the content in the
+ * JSON. Since JSON must be Unicode, we handle charset
+ * decoding here and do not report a charset to the caller.
+ * For text/html parts, we do not include the content unless
+ * the --include-html option has been passed. If a html part
+ * is not included, it can be requested directly. This makes
+ * charset decoding the responsibility on the caller so we
+ * report the charset for text/html parts.
+ */
+ if (g_mime_content_type_is_type (content_type, "text", "*") &&
+ (include_html ||
+ ! g_mime_content_type_is_type (content_type, "text", "html")))
+ {
+ GMimeStream *stream_memory = g_mime_stream_mem_new ();
+ GByteArray *part_content;
+ show_text_part_content (node->part, stream_memory, 0);
+ part_content = g_mime_stream_mem_get_byte_array (GMIME_STREAM_MEM (stream_memory));
+ sp->map_key (sp, "content");
+ sp->string_len (sp, (char *) part_content->data, part_content->len);
+ g_object_unref (stream_memory);
+ } else {
+ format_omitted_part_meta_sprinter (sp, meta, GMIME_PART (node->part));
+ }
+ } else if (GMIME_IS_MULTIPART (node->part)) {
+ sp->map_key (sp, "content");
+ sp->begin_list (sp);
+ nclose = 1;
+ } else if (GMIME_IS_MESSAGE (node->part)) {
+ sp->map_key (sp, "content");
+ sp->begin_list (sp);
+ sp->begin_map (sp);
+
+ sp->map_key (sp, "headers");
+ format_headers_sprinter (sp, GMIME_MESSAGE (node->part), FALSE);
+
+ sp->map_key (sp, "body");
+ sp->begin_list (sp);
+ nclose = 3;
+ }
+
+ for (i = 0; i < node->nchildren; i++)
+ format_part_sprinter (ctx, sp, mime_node_child (node, i), i == 0, TRUE, include_html);
+
+ /* Close content structures */
+ for (i = 0; i < nclose; i++)
+ sp->end (sp);
+ /* Close part map */
+ sp->end (sp);
+}
+
+static notmuch_status_t
+format_part_sprinter_entry (const void *ctx, sprinter_t *sp,
+ mime_node_t *node, unused (int indent),
+ const notmuch_show_params_t *params)
+{
+ format_part_sprinter (ctx, sp, node, TRUE, params->output_body, params->include_html);
+
+ return NOTMUCH_STATUS_SUCCESS;
+}
+
+/* Print a message in "mboxrd" format as documented, for example,
+ * here:
+ *
+ * http://qmail.org/qmail-manual-html/man5/mbox.html
+ */
+static notmuch_status_t
+format_part_mbox (const void *ctx, unused (sprinter_t *sp), mime_node_t *node,
+ unused (int indent),
+ unused (const notmuch_show_params_t *params))
+{
+ notmuch_message_t *message = node->envelope_file;
+
+ const char *filename;
+ FILE *file;
+ const char *from;