aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--Makefile.local5
-rw-r--r--gmime-filter-reply.c208
-rw-r--r--gmime-filter-reply.h66
-rw-r--r--notmuch-client.h21
-rw-r--r--notmuch-reply.c141
-rw-r--r--notmuch-show.c101
-rw-r--r--notmuch.119
-rw-r--r--notmuch.c17
-rw-r--r--show-message.c117
9 files changed, 592 insertions, 103 deletions
diff --git a/Makefile.local b/Makefile.local
index ef437757..7c90d833 100644
--- a/Makefile.local
+++ b/Makefile.local
@@ -4,6 +4,7 @@ notmuch_client_srcs = \
notmuch.c \
notmuch-dump.c \
notmuch-new.c \
+ notmuch-reply.c \
notmuch-restore.c \
notmuch-search.c \
notmuch-setup.c \
@@ -11,7 +12,9 @@ notmuch_client_srcs = \
notmuch-tag.c \
notmuch-time.c \
add-files.c \
- query-string.c
+ gmime-filter-reply.c \
+ query-string.c \
+ show-message.c
notmuch_client_modules = $(notmuch_client_srcs:.c=.o)
notmuch: $(notmuch_client_modules) lib/notmuch.a
diff --git a/gmime-filter-reply.c b/gmime-filter-reply.c
new file mode 100644
index 00000000..3e298e1b
--- /dev/null
+++ b/gmime-filter-reply.c
@@ -0,0 +1,208 @@
+/*
+ * Copyright © 2009 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include "gmime-filter-reply.h"
+
+/**
+ * SECTION: gmime-filter-reply
+ * @title: GMimeFilterReply
+ * @short_description: Add/remove reply markers
+ *
+ * A #GMimeFilter for adding or removing reply markers
+ **/
+
+
+static void g_mime_filter_reply_class_init (GMimeFilterReplyClass *klass);
+static void g_mime_filter_reply_init (GMimeFilterReply *filter, GMimeFilterReplyClass *klass);
+static void g_mime_filter_reply_finalize (GObject *object);
+
+static GMimeFilter *filter_copy (GMimeFilter *filter);
+static void filter_filter (GMimeFilter *filter, char *in, size_t len, size_t prespace,
+ char **out, size_t *outlen, size_t *outprespace);
+static void filter_complete (GMimeFilter *filter, char *in, size_t len, size_t prespace,
+ char **out, size_t *outlen, size_t *outprespace);
+static void filter_reset (GMimeFilter *filter);
+
+
+static GMimeFilterClass *parent_class = NULL;
+
+GType
+g_mime_filter_reply_get_type (void)
+{
+ static GType type = 0;
+
+ if (!type) {
+ static const GTypeInfo info = {
+ sizeof (GMimeFilterReplyClass),
+ NULL, /* base_class_init */
+ NULL, /* base_class_finalize */
+ (GClassInitFunc) g_mime_filter_reply_class_init,
+ NULL, /* class_finalize */
+ NULL, /* class_data */
+ sizeof (GMimeFilterReply),
+ 0, /* n_preallocs */
+ (GInstanceInitFunc) g_mime_filter_reply_init,
+ NULL /* value_table */
+ };
+
+ type = g_type_register_static (GMIME_TYPE_FILTER, "GMimeFilterReply", &info, (GTypeFlags) 0);
+ }
+
+ return type;
+}
+
+
+static void
+g_mime_filter_reply_class_init (GMimeFilterReplyClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GMimeFilterClass *filter_class = GMIME_FILTER_CLASS (klass);
+
+ parent_class = (GMimeFilterClass *) g_type_class_ref (GMIME_TYPE_FILTER);
+
+ object_class->finalize = g_mime_filter_reply_finalize;
+
+ filter_class->copy = filter_copy;
+ filter_class->filter = filter_filter;
+ filter_class->complete = filter_complete;
+ filter_class->reset = filter_reset;
+}
+
+static void
+g_mime_filter_reply_init (GMimeFilterReply *filter, GMimeFilterReplyClass *klass)
+{
+ (void) klass;
+ filter->saw_nl = TRUE;
+ filter->saw_angle = FALSE;
+}
+
+static void
+g_mime_filter_reply_finalize (GObject *object)
+{
+ G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+
+static GMimeFilter *
+filter_copy (GMimeFilter *filter)
+{
+ GMimeFilterReply *reply = (GMimeFilterReply *) filter;
+
+ return g_mime_filter_reply_new (reply->encode);
+}
+
+static void
+filter_filter (GMimeFilter *filter, char *inbuf, size_t inlen, size_t prespace,
+ char **outbuf, size_t *outlen, size_t *outprespace)
+{
+ GMimeFilterReply *reply = (GMimeFilterReply *) filter;
+ register const char *inptr = inbuf;
+ const char *inend = inbuf + inlen;
+ char *outptr;
+
+ (void) prespace;
+ if (reply->encode) {
+ g_mime_filter_set_size (filter, 3 * inlen, FALSE);
+
+ outptr = filter->outbuf;
+ while (inptr < inend) {
+ if (reply->saw_nl) {
+ *outptr++ = '>';
+ *outptr++ = ' ';
+ reply->saw_nl = FALSE;
+ }
+ if (*inptr == '\n')
+ reply->saw_nl = TRUE;
+ else
+ reply->saw_nl = FALSE;
+
+ *outptr++ = *inptr++;
+ }
+ } else {
+ g_mime_filter_set_size (filter, inlen + 1, FALSE);
+
+ outptr = filter->outbuf;
+ while (inptr < inend) {
+ if (reply->saw_nl) {
+ if (*inptr == '>')
+ reply->saw_angle = TRUE;
+ else
+ *outptr++ = *inptr;
+ reply->saw_nl = FALSE;
+ } else if (reply->saw_angle) {
+ if (*inptr == ' ')
+ ;
+ else
+ *outptr++ = *inptr;
+ reply->saw_angle = FALSE;
+ } else {
+ if (*inptr == '\n')
+ reply->saw_nl = TRUE;
+ *outptr++ = *inptr;
+ }
+
+ inptr++;
+ }
+ }
+
+ *outlen = outptr - filter->outbuf;
+ *outprespace = filter->outpre;
+ *outbuf = filter->outbuf;
+}
+
+static void
+filter_complete (GMimeFilter *filter, char *inbuf, size_t inlen, size_t prespace,
+ char **outbuf, size_t *outlen, size_t *outprespace)
+{
+ if (inbuf && inlen)
+ filter_filter (filter, inbuf, inlen, prespace, outbuf, outlen, outprespace);
+}
+
+static void
+filter_reset (GMimeFilter *filter)
+{
+ GMimeFilterReply *reply = (GMimeFilterReply *) filter;
+
+ reply->saw_nl = TRUE;
+ reply->saw_angle = FALSE;
+}
+
+
+/**
+ * g_mime_filter_reply_new:
+ * @encode: %TRUE if the filter should encode or %FALSE otherwise
+ * @dots: encode/decode dots (as for SMTP)
+ *
+ * Creates a new #GMimeFilterReply filter.
+ *
+ * If @encode is %TRUE, then all lines will be prefixed by "> ",
+ * otherwise any lines starting with "> " will have that removed
+ *
+ * Returns: a new #GMimeFilterReply filter.
+ **/
+GMimeFilter *
+g_mime_filter_reply_new (gboolean encode)
+{
+ GMimeFilterReply *new_reply;
+
+ new_reply = (GMimeFilterReply *) g_object_newv (GMIME_TYPE_FILTER_REPLY, 0, NULL);
+ new_reply->encode = encode;
+
+ return (GMimeFilter *) new_reply;
+}
+
diff --git a/gmime-filter-reply.h b/gmime-filter-reply.h
new file mode 100644
index 00000000..41cbc134
--- /dev/null
+++ b/gmime-filter-reply.h
@@ -0,0 +1,66 @@
+/*
+ * Copyright © 2009 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#ifndef _GMIME_FILTER_REPLY_H_
+#define _GMIME_FILTER_REPLY_H_
+
+#include <gmime/gmime-filter.h>
+
+G_BEGIN_DECLS
+
+#define GMIME_TYPE_FILTER_REPLY (g_mime_filter_reply_get_type ())
+#define GMIME_FILTER_REPLY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GMIME_TYPE_FILTER_REPLY, GMimeFilterReply))
+#define GMIME_FILTER_REPLY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GMIME_TYPE_FILTER_REPLY, GMimeFilterReplyClass))
+#define GMIME_IS_FILTER_REPLY(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GMIME_TYPE_FILTER_REPLY))
+#define GMIME_IS_FILTER_REPLY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GMIME_TYPE_FILTER_REPLY))
+#define GMIME_FILTER_REPLY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GMIME_TYPE_FILTER_REPLY, GMimeFilterReplyClass))
+
+typedef struct _GMimeFilterReply GMimeFilterReply;
+typedef struct _GMimeFilterReplyClass GMimeFilterReplyClass;
+
+/**
+ * GMimeFilterReply:
+ * @parent_object: parent #GMimeFilter
+ * @encode: encoding vs decoding reply markers
+ * @saw_nl: previous char was a \n
+ * @saw_angle: previous char was a >
+ *
+ * A filter to insert/remove reply markers (lines begining with >)
+ **/
+struct _GMimeFilterReply {
+ GMimeFilter parent_object;
+
+ gboolean encode;
+ gboolean saw_nl;
+ gboolean saw_angle;
+};
+
+struct _GMimeFilterReplyClass {
+ GMimeFilterClass parent_class;
+
+};
+
+
+GType g_mime_filter_reply_get_type (void);
+
+GMimeFilter *g_mime_filter_reply_new (gboolean encode);
+
+G_END_DECLS
+
+
+#endif /* _GMIME_FILTER_REPLY_H_ */
diff --git a/notmuch-client.h b/notmuch-client.h
index 50f31fa1..f39900a9 100644
--- a/notmuch-client.h
+++ b/notmuch-client.h
@@ -103,6 +103,9 @@ int
notmuch_new_command (void *ctx, int argc, char *argv[]);
int
+notmuch_reply_command (void *ctx, int argc, char *argv[]);
+
+int
notmuch_restore_command (void *ctx, int argc, char *argv[]);
int
@@ -117,13 +120,6 @@ notmuch_show_command (void *ctx, int argc, char *argv[]);
int
notmuch_tag_command (void *ctx, int argc, char *argv[]);
-notmuch_status_t
-add_files (notmuch_database_t *notmuch, const char *path,
- add_files_state_t *state);
-
-char *
-query_string_from_args (void *ctx, int argc, char *argv[]);
-
const char *
notmuch_time_relative_date (void *ctx, time_t then);
@@ -133,4 +129,15 @@ notmuch_time_print_formatted_seconds (double seconds);
double
notmuch_time_elapsed (struct timeval start, struct timeval end);
+notmuch_status_t
+add_files (notmuch_database_t *notmuch, const char *path,
+ add_files_state_t *state);
+
+char *
+query_string_from_args (void *ctx, int argc, char *argv[]);
+
+notmuch_status_t
+show_message_body (const char *filename,
+ void (*show_part) (GMimeObject *part, int *part_count));
+
#endif
diff --git a/notmuch-reply.c b/notmuch-reply.c
new file mode 100644
index 00000000..0c3ea13f
--- /dev/null
+++ b/notmuch-reply.c
@@ -0,0 +1,141 @@
+/* notmuch - Not much of an email program, (just index and search)
+ *
+ * Copyright © 2009 Carl Worth
+ * Copyright © 2009 Keith Packard
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see http://www.gnu.org/licenses/ .
+ *
+ * Authors: Carl Worth <cworth@cworth.org>
+ * Keith Packard <keithp@keithp.com>
+ */
+
+#include "notmuch-client.h"
+#include "gmime-filter-reply.h"
+
+static void
+reply_part(GMimeObject *part, int *part_count)
+{
+ GMimeContentDisposition *disposition;
+ GMimeContentType *content_type;
+ GMimeDataWrapper *wrapper;
+
+ (void) part_count;
+ disposition = g_mime_object_get_content_disposition (part);
+ if (disposition &&
+ strcmp (disposition->disposition, GMIME_DISPOSITION_ATTACHMENT) == 0)
+ {
+ const char *filename = g_mime_part_get_filename (GMIME_PART (part));
+ content_type = g_mime_object_get_content_type (GMIME_OBJECT (part));
+
+ printf ("Attachment: %s (%s)\n", filename,
+ g_mime_content_type_to_string (content_type));
+ return;
+ }
+
+ content_type = g_mime_object_get_content_type (GMIME_OBJECT (part));
+
+ if (g_mime_content_type_is_type (content_type, "text", "*") &&
+ !g_mime_content_type_is_type (content_type, "text", "html"))
+ {
+ GMimeStream *stream_stdout = NULL, *stream_filter = 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);
+ }
+ g_mime_stream_filter_add(GMIME_STREAM_FILTER(stream_filter),
+ g_mime_filter_reply_new(TRUE));
+ wrapper = g_mime_part_get_content_object (GMIME_PART (part));
+ if (wrapper && stream_filter)
+ g_mime_data_wrapper_write_to_stream (wrapper, stream_filter);
+ if (stream_filter)
+ g_object_unref(stream_filter);
+ if (stream_stdout)
+ g_object_unref(stream_stdout);
+ }
+ else
+ {
+ printf ("Non-text part: %s\n",
+ g_mime_content_type_to_string (content_type));
+ }
+}
+
+int
+notmuch_reply_command (void *ctx, int argc, char *argv[])
+{
+ void *local = talloc_new (ctx);
+ char *query_string;
+ notmuch_database_t *notmuch = NULL;
+ notmuch_query_t *query = NULL;
+ notmuch_messages_t *messages;
+ notmuch_message_t *message;
+ int ret = 0;
+
+ const char *headers[] = {
+ "Subject", "From", "To", "Cc", "Bcc", "Date",
+ "In-Reply-To", "References"
+ };
+ const char *name, *value;
+ unsigned int i;
+
+ notmuch = notmuch_database_open (NULL);
+ if (notmuch == NULL) {
+ ret = 1;
+ goto DONE;
+ }
+
+ query_string = query_string_from_args (local, argc, argv);
+ if (query_string == NULL) {
+ fprintf (stderr, "Out of memory\n");
+ ret = 1;
+ goto DONE;
+ }
+
+ query = notmuch_query_create (notmuch, query_string);
+ if (query == NULL) {
+ fprintf (stderr, "Out of memory\n");
+ ret = 1;
+ goto DONE;
+ }
+
+ for (messages = notmuch_query_search_messages (query);
+ notmuch_messages_has_more (messages);
+ notmuch_messages_advance (messages))
+ {
+ message = notmuch_messages_get (messages);
+
+ for (i = 0; i < ARRAY_SIZE (headers); i++) {
+ name = headers[i];
+ value = notmuch_message_get_header (message, name);
+ if (value)
+ printf ("%s: %s\n", name, value);
+ }
+
+ show_message_body (notmuch_message_get_filename (message), reply_part);
+
+ notmuch_message_destroy (message);
+ }
+
+ DONE:
+ if (local)
+ talloc_free (local);
+
+ if (query)
+ notmuch_query_destroy (query);
+
+ if (notmuch)
+ notmuch_database_close (notmuch);
+
+ return ret;
+}
diff --git a/notmuch-show.c b/notmuch-show.c
index 7647e9e4..b5db3df9 100644
--- a/notmuch-show.c
+++ b/notmuch-show.c
@@ -67,49 +67,11 @@ _get_one_line_summary (void *ctx, notmuch_message_t *message)
}
static void
-show_message_part (GMimeObject *part, int *part_count)
+show_part(GMimeObject *part, int *part_count)
{
- GMimeStream *stream;
- GMimeDataWrapper *wrapper;
GMimeContentDisposition *disposition;
GMimeContentType *content_type;
-
- *part_count = *part_count + 1;
-
- if (GMIME_IS_MULTIPART (part)) {
- GMimeMultipart *multipart = GMIME_MULTIPART (part);
- int i;
-
- for (i = 0; i < g_mime_multipart_get_count (multipart); i++) {
- if (GMIME_IS_MULTIPART_SIGNED (multipart)) {
- /* Don't index the signature. */
- if (i == 1)
- continue;
- if (i > 1)
- fprintf (stderr, "Warning: Unexpected extra parts of mutlipart/signed. Continuing.\n");
- }
- show_message_part (g_mime_multipart_get_part (multipart, i),
- part_count);
- }
- return;
- }
-
- if (GMIME_IS_MESSAGE_PART (part)) {
- GMimeMessage *mime_message;
-
- mime_message = g_mime_message_part_get_message (GMIME_MESSAGE_PART (part));
-
- show_message_part (g_mime_message_get_mime_part (mime_message),
- part_count);
-
- return;
- }
-
- if (! (GMIME_IS_PART (part))) {
- fprintf (stderr, "Warning: Not displaying unknown mime part: %s.\n",
- g_type_name (G_OBJECT_TYPE (part)));
- return;
- }
+ GMimeDataWrapper *wrapper;
disposition = g_mime_object_get_content_disposition (part);
if (disposition &&
@@ -137,14 +99,14 @@ show_message_part (GMimeObject *part, int *part_count)
if (g_mime_content_type_is_type (content_type, "text", "*") &&
!g_mime_content_type_is_type (content_type, "text", "html"))
{
- stream = g_mime_stream_file_new (stdout);
+ GMimeStream *stream = g_mime_stream_file_new (stdout);
g_mime_stream_file_set_owner (GMIME_STREAM_FILE (stream), FALSE);
wrapper = g_mime_part_get_content_object (GMIME_PART (part));
- if (wrapper)
+ if (wrapper && stream)
g_mime_data_wrapper_write_to_stream (wrapper, stream);
-
- g_object_unref (stream);
+ if (stream)
+ g_object_unref(stream);
}
else
{
@@ -155,55 +117,6 @@ show_message_part (GMimeObject *part, int *part_count)
printf ("\fpart}\n");
}
-static notmuch_status_t
-show_message_body (const char *filename)
-{
- GMimeStream *stream = NULL;
- GMimeParser *parser = NULL;
- GMimeMessage *mime_message = NULL;
- notmuch_status_t ret = NOTMUCH_STATUS_SUCCESS;
- static int initialized = 0;
- FILE *file = NULL;
- int part_count = 0;
-
- if (! initialized) {
- g_mime_init (0);
- initialized = 1;
- }
-
- file = fopen (filename, "r");
- if (! file) {
- fprintf (stderr, "Error opening %s: %s\n", filename, strerror (errno));
- ret = NOTMUCH_STATUS_FILE_ERROR;
- goto DONE;
- }
-
- stream = g_mime_stream_file_new (file);
- g_mime_stream_file_set_owner (GMIME_STREAM_FILE (stream), FALSE);
-
- parser = g_mime_parser_new_with_stream (stream);
-
- mime_message = g_mime_parser_construct_message (parser);
-
- show_message_part (g_mime_message_get_mime_part (mime_message),
- &part_count);
-
- DONE:
- if (mime_message)
- g_object_unref (mime_message);
-
- if (parser)
- g_object_unref (parser);
-
- if (stream)
- g_object_unref (stream);
-
- if (file)
- fclose (file);
-
- return ret;
-}
-
int
notmuch_show_command (void *ctx, unused (int argc), unused (char *argv[]))
{
@@ -267,7 +180,7 @@ notmuch_show_command (void *ctx, unused (int argc), unused (char *argv[]))
printf ("\fheader}\n");
printf ("\fbody{\n");
- show_message_body (notmuch_message_get_filename (message));
+ show_message_body (notmuch_message_get_filename (message), show_part);
printf ("\fbody}\n");
diff --git a/notmuch.1 b/notmuch.1
index 75820710..6c3d10fd 100644
--- a/notmuch.1
+++ b/notmuch.1
@@ -167,6 +167,25 @@ operators, but will have to be protected from interpretation by the
shell, (such as by putting quotation marks around any parenthesized
expression).
.TP
+.BR reply " <search-term>..."
+
+Constructs a reply template for a set of messages.
+
+See the documentation of
+.B search
+for deatils of the supported syntax of search terms.
+
+To make replying to email easier,
+.B notmuch reply
+takes an existing set of messages and constructs a suitable mail
+template, taking From: and To: messages and using those for the new
+To: address; copying Cc: addresses, building a suitable new subject
+including Re: at the front, adding the old message IDs to the
+References list and setting the In-Reply-To: field correctly.
+
+The resulting message template is output to stdout.
+
+.TP
.BR show " <search-term>..."
Shows all messages matching the search terms.
diff --git a/notmuch.c b/notmuch.c
index 339144dd..224e4a6b 100644
--- a/notmuch.c
+++ b/notmuch.c
@@ -1,6 +1,7 @@
/* notmuch - Not much of an email program, (just index and search)
*
* Copyright © 2009 Carl Worth
+ * Copyright © 2009 Keith Packard
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -15,7 +16,8 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see http://www.gnu.org/licenses/ .
*
- * Author: Carl Worth <cworth@cworth.org>
+ * Authors: Carl Worth <cworth@cworth.org>
+ * Keith Packard <keithp@keithp.com>
*/
#include "notmuch-client.h"
@@ -82,6 +84,19 @@ command_t commands[] = {
"\t\tthe Boolean operators, but will have to be protected from\n"
"\t\tinterpretation by the shell, (such as by putting quotation\n"
"\t\tmarks around any parenthesized expression)." },
+ { "reply", notmuch_reply_command,
+ "<search-terms> [...]\n\n"
+ "\t\tFormats a reply from a set of existing messages.",
+ "\t\tConstructs a new message as a reply to a set of existing\n"
+ "\t\tmessages. The From: address is used as a To: address\n"
+ "\t\talong with all old To: addresses. All of the Cc: addresses\n"
+ "\t\tare copied as new Cc: addresses. An In-Reply-To: header\n"
+ "\t\twill be constructed from the name and date of the original\n"
+ "\t\tmessage, and the original Message-ID will be added to the\n"
+ "\t\tlist of References in the new message. The text of each\n"
+ "\t\tmessage (as described in the \"show\" command) will be\n"
+ "\t\tpresented, each line prefixed with \"> \" The resulting\n"
+ "\t\tmessage will be dumped to stdout." },
{ "show", notmuch_show_command,
"<search-terms> [...]\n\n"
"\t\tShows all messages matching the search terms.",
diff --git a/show-message.c b/show-message.c
new file mode 100644
index 00000000..2d3189e3
--- /dev/null
+++ b/show-message.c
@@ -0,0 +1,117 @@
+/* notmuch - Not much of an email program, (just index and search)
+ *
+ * Copyright © 2009 Carl Worth
+ * Copyright © 2009 Keith Packard
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see http://www.gnu.org/licenses/ .
+ *
+ * Authors: Carl Worth <cworth@cworth.org>
+ * Keith Packard <keithp@keithp.com>
+ */
+
+#include "notmuch-client.h"
+
+static void
+show_message_part (GMimeObject *part, int *part_count,
+ void (*show_part) (GMimeObject *part, int *part_count))
+{
+ *part_count = *part_count + 1;
+
+ if (GMIME_IS_MULTIPART (part)) {
+ GMimeMultipart *multipart = GMIME_MULTIPART (part);
+ int i;
+
+ for (i = 0; i < g_mime_multipart_get_count (multipart); i++) {
+ if (GMIME_IS_MULTIPART_SIGNED (multipart)) {
+ /* Don't index the signature. */
+ if (i == 1)
+ continue;
+ if (i > 1)
+ fprintf (stderr, "Warning: Unexpected extra parts of mutlipart/signed. Continuing.\n");
+ }
+ show_message_part (g_mime_multipart_get_part (multipart, i),
+ part_count, show_part);
+ }
+ return;
+ }
+
+ if (GMIME_IS_MESSAGE_PART (part)) {
+ GMimeMessage *mime_message;
+
+ mime_message = g_mime_message_part_get_message (GMIME_MESSAGE_PART (part));
+
+ show_message_part (g_mime_message_get_mime_part (mime_message),
+ part_count, show_part);
+
+ return;
+ }
+
+ if (! (GMIME_IS_PART (part))) {
+ fprintf (stderr, "Warning: Not displaying unknown mime part: %s.\n",
+ g_type_name (G_OBJECT_TYPE (part)));
+ return;
+ }
+
+ (*show_part) (part, part_count);
+}
+
+notmuch_status_t
+show_message_body (const char *filename,
+ void (*show_part) (GMimeObject *part, int *part_count))
+{
+ GMimeStream *stream = NULL;
+ GMimeParser *parser = NULL;
+ GMimeMessage *mime_message = NULL;
+ notmuch_status_t ret = NOTMUCH_STATUS_SUCCESS;
+ static int initialized = 0;
+ FILE *file = NULL;
+ int part_count = 0;
+
+ if (! initialized) {
+ g_mime_init (0);
+ initialized = 1;
+ }
+
+ file = fopen (filename, "r");
+ if (! file) {
+ fprintf (stderr, "Error opening %s: %s\n", filename, strerror (errno));
+ ret = NOTMUCH_STATUS_FILE_ERROR;
+ goto DONE;
+ }
+
+ stream = g_mime_stream_file_new (file);
+ g_mime_stream_file_set_owner (GMIME_STREAM_FILE (stream), FALSE);
+
+ parser = g_mime_parser_new_with_stream (stream);
+
+ mime_message = g_mime_parser_construct_message (parser);
+
+ show_message_part (g_mime_message_get_mime_part (mime_message),
+ &part_count, show_part);
+
+ DONE:
+ if (mime_message)
+ g_object_unref (mime_message);
+
+ if (parser)
+ g_object_unref (parser);
+
+ if (stream)
+ g_object_unref (stream);
+
+ if (file)
+ fclose (file);
+
+ return ret;
+}