+format_message_sprinter (sprinter_t *sp, notmuch_message_t *message)
+{
+ /* Any changes to the JSON or S-Expression format should be
+ * reflected in the file devel/schemata. */
+
+ void *local = talloc_new (NULL);
+ notmuch_tags_t *tags;
+ time_t date;
+ const char *relative_date;
+
+ sp->map_key (sp, "id");
+ sp->string (sp, notmuch_message_get_message_id (message));
+
+ sp->map_key (sp, "match");
+ sp->boolean (sp, _get_message_flag (message, NOTMUCH_MESSAGE_FLAG_MATCH));
+
+ sp->map_key (sp, "excluded");
+ sp->boolean (sp, _get_message_flag (message, NOTMUCH_MESSAGE_FLAG_EXCLUDED));
+
+ sp->map_key (sp, "filename");
+ if (notmuch_format_version >= 3) {
+ notmuch_filenames_t *filenames;
+
+ sp->begin_list (sp);
+ for (filenames = notmuch_message_get_filenames (message);
+ notmuch_filenames_valid (filenames);
+ notmuch_filenames_move_to_next (filenames)) {
+ sp->string (sp, notmuch_filenames_get (filenames));
+ }
+ notmuch_filenames_destroy (filenames);
+ sp->end (sp);
+ } else {
+ sp->string (sp, notmuch_message_get_filename (message));
+ }
+
+ sp->map_key (sp, "timestamp");
+ date = notmuch_message_get_date (message);
+ sp->integer (sp, date);
+
+ sp->map_key (sp, "date_relative");
+ relative_date = notmuch_time_relative_date (local, date);
+ sp->string (sp, relative_date);
+
+ sp->map_key (sp, "tags");
+ sp->begin_list (sp);
+ for (tags = notmuch_message_get_tags (message);
+ notmuch_tags_valid (tags);
+ notmuch_tags_move_to_next (tags))
+ sp->string (sp, notmuch_tags_get (tags));
+ sp->end (sp);
+
+ talloc_free (local);
+}
+
+/* Extract just the email address from the contents of a From:
+ * header. */
+static const char *
+_extract_email_address (const void *ctx, const char *from)
+{
+ InternetAddressList *addresses;
+ InternetAddress *address;
+ InternetAddressMailbox *mailbox;
+ const char *email = "MAILER-DAEMON";
+
+ addresses = internet_address_list_parse (NULL, from);
+
+ /* Bail if there is no address here. */
+ if (addresses == NULL || internet_address_list_length (addresses) < 1)
+ goto DONE;
+
+ /* Otherwise, just use the first address. */
+ address = internet_address_list_get_address (addresses, 0);
+
+ /* The From header should never contain an address group rather
+ * than a mailbox. So bail if it does. */
+ if (! INTERNET_ADDRESS_IS_MAILBOX (address))
+ goto DONE;
+
+ mailbox = INTERNET_ADDRESS_MAILBOX (address);
+ email = internet_address_mailbox_get_addr (mailbox);
+ email = talloc_strdup (ctx, email);
+
+ DONE:
+ if (addresses)
+ g_object_unref (addresses);
+
+ return email;
+}
+
+/* Return 1 if 'line' is an mbox From_ line---that is, a line
+ * beginning with zero or more '>' characters followed by the
+ * characters 'F', 'r', 'o', 'm', and space.
+ *
+ * Any characters at all may appear after that in the line.
+ */
+static int
+_is_from_line (const char *line)
+{
+ const char *s = line;
+
+ if (line == NULL)
+ return 0;
+
+ while (*s == '>')
+ s++;
+
+ if (STRNCMP_LITERAL (s, "From ") == 0)
+ return 1;
+ else
+ return 0;
+}
+
+/* Output extra headers if configured with the `show.extra_headers'
+ * configuration option
+ */
+static void
+format_extra_headers_sprinter (sprinter_t *sp, GMimeMessage *message)
+{
+ GMimeHeaderList *header_list = g_mime_object_get_header_list (GMIME_OBJECT (message));
+
+ for (notmuch_config_values_t *extra_headers = notmuch_config_get_values (
+ sp->notmuch, NOTMUCH_CONFIG_EXTRA_HEADERS);
+ notmuch_config_values_valid (extra_headers);
+ notmuch_config_values_move_to_next (extra_headers)) {
+ GMimeHeader *header;
+ const char *header_name = notmuch_config_values_get (extra_headers);
+
+ header = g_mime_header_list_get_header (header_list, header_name);
+ if (header == NULL)
+ continue;
+
+ sp->map_key (sp, g_mime_header_get_name (header));
+ sp->string (sp, g_mime_header_get_value (header));
+ }
+}
+
+void
+format_headers_sprinter (sprinter_t *sp, GMimeMessage *message,
+ bool reply, const _notmuch_message_crypto_t *msg_crypto)
+{
+ /* Any changes to the JSON or S-Expression format should be
+ * reflected in the file devel/schemata. */
+
+ char *recipients_string;
+ const char *reply_to_string;
+ void *local = talloc_new (sp);
+
+ sp->begin_map (sp);
+
+ sp->map_key (sp, "Subject");
+ if (msg_crypto && msg_crypto->payload_subject) {
+ sp->string (sp, msg_crypto->payload_subject);
+ } else
+ sp->string (sp, g_mime_message_get_subject (message));
+
+ sp->map_key (sp, "From");
+ sp->string (sp, g_mime_message_get_from_string (message));
+
+ recipients_string = g_mime_message_get_address_string (message, GMIME_ADDRESS_TYPE_TO);
+ if (recipients_string) {
+ sp->map_key (sp, "To");
+ sp->string (sp, recipients_string);
+ g_free (recipients_string);
+ }
+
+ recipients_string = g_mime_message_get_address_string (message, GMIME_ADDRESS_TYPE_CC);
+ if (recipients_string) {
+ sp->map_key (sp, "Cc");
+ sp->string (sp, recipients_string);
+ g_free (recipients_string);
+ }
+
+ recipients_string = g_mime_message_get_address_string (message, GMIME_ADDRESS_TYPE_BCC);
+ if (recipients_string) {
+ sp->map_key (sp, "Bcc");
+ sp->string (sp, recipients_string);
+ g_free (recipients_string);
+ }
+
+ reply_to_string = g_mime_message_get_reply_to_string (local, message);
+ if (reply_to_string) {
+ sp->map_key (sp, "Reply-To");
+ sp->string (sp, reply_to_string);
+ }
+
+ if (reply) {
+ sp->map_key (sp, "In-reply-to");
+ sp->string (sp, g_mime_object_get_header (GMIME_OBJECT (message), "In-reply-to"));
+
+ sp->map_key (sp, "References");
+ sp->string (sp, g_mime_object_get_header (GMIME_OBJECT (message), "References"));
+ } else {
+ sp->map_key (sp, "Date");
+ sp->string (sp, g_mime_message_get_date_string (sp, message));
+ }
+
+ /* Output extra headers the user has configured, if any */
+ if (! reply)
+ format_extra_headers_sprinter (sp, message);
+ sp->end (sp);
+ talloc_free (local);
+}
+
+/* Write a MIME text part out to the given stream.
+ *
+ * If (flags & NOTMUCH_SHOW_TEXT_PART_REPLY), this prepends "> " to
+ * each output line.
+ *
+ * Both line-ending conversion (CRLF->LF) and charset conversion ( ->
+ * UTF-8) will be performed, so it is inappropriate to call this
+ * function with a non-text part. Doing so will trigger an internal
+ * error.
+ */
+void
+show_text_part_content (GMimeObject *part, GMimeStream *stream_out,
+ notmuch_show_text_part_flags flags)