notmuch reply: Add (incomplete) reply command

Reviewed-by: Carl Worth <cworth@cworth.org>

Keith wrote all the code here against notmuch before notmuch.c was
split up into multiple files. So I've pushed the code around in
various ways to match the new code structure, but have generally tried
to avoid making any changes to the behavior of the code.

I did fix one bug---a missing call to g_mime_stream_file_set_owner in
show_part which would cause "notmuch show" to go off into the weeds
when trying to show multiple messages, (since the first stream would
fclose stdout).
This commit is contained in:
Keith Packard 2009-11-06 10:00:38 -08:00 committed by Carl Worth
parent 50144f95ca
commit 357aba3ec8
9 changed files with 592 additions and 103 deletions

View file

@ -4,6 +4,7 @@ notmuch_client_srcs = \
notmuch.c \ notmuch.c \
notmuch-dump.c \ notmuch-dump.c \
notmuch-new.c \ notmuch-new.c \
notmuch-reply.c \
notmuch-restore.c \ notmuch-restore.c \
notmuch-search.c \ notmuch-search.c \
notmuch-setup.c \ notmuch-setup.c \
@ -11,7 +12,9 @@ notmuch_client_srcs = \
notmuch-tag.c \ notmuch-tag.c \
notmuch-time.c \ notmuch-time.c \
add-files.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_client_modules = $(notmuch_client_srcs:.c=.o)
notmuch: $(notmuch_client_modules) lib/notmuch.a notmuch: $(notmuch_client_modules) lib/notmuch.a

208
gmime-filter-reply.c Normal file
View file

@ -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;
}

66
gmime-filter-reply.h Normal file
View file

@ -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_ */

View file

@ -102,6 +102,9 @@ notmuch_dump_command (void *ctx, int argc, char *argv[]);
int int
notmuch_new_command (void *ctx, int argc, char *argv[]); notmuch_new_command (void *ctx, int argc, char *argv[]);
int
notmuch_reply_command (void *ctx, int argc, char *argv[]);
int int
notmuch_restore_command (void *ctx, int argc, char *argv[]); notmuch_restore_command (void *ctx, int argc, char *argv[]);
@ -117,13 +120,6 @@ notmuch_show_command (void *ctx, int argc, char *argv[]);
int int
notmuch_tag_command (void *ctx, int argc, char *argv[]); 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 * const char *
notmuch_time_relative_date (void *ctx, time_t then); notmuch_time_relative_date (void *ctx, time_t then);
@ -133,4 +129,15 @@ notmuch_time_print_formatted_seconds (double seconds);
double double
notmuch_time_elapsed (struct timeval start, struct timeval end); 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 #endif

141
notmuch-reply.c Normal file
View file

@ -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;
}

View file

@ -67,49 +67,11 @@ _get_one_line_summary (void *ctx, notmuch_message_t *message)
} }
static void static void
show_message_part (GMimeObject *part, int *part_count) show_part(GMimeObject *part, int *part_count)
{ {
GMimeStream *stream;
GMimeDataWrapper *wrapper;
GMimeContentDisposition *disposition; GMimeContentDisposition *disposition;
GMimeContentType *content_type; GMimeContentType *content_type;
GMimeDataWrapper *wrapper;
*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;
}
disposition = g_mime_object_get_content_disposition (part); disposition = g_mime_object_get_content_disposition (part);
if (disposition && if (disposition &&
@ -137,13 +99,13 @@ show_message_part (GMimeObject *part, int *part_count)
if (g_mime_content_type_is_type (content_type, "text", "*") && if (g_mime_content_type_is_type (content_type, "text", "*") &&
!g_mime_content_type_is_type (content_type, "text", "html")) !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); g_mime_stream_file_set_owner (GMIME_STREAM_FILE (stream), FALSE);
wrapper = g_mime_part_get_content_object (GMIME_PART (part)); 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_mime_data_wrapper_write_to_stream (wrapper, stream);
if (stream)
g_object_unref(stream); g_object_unref(stream);
} }
else else
@ -155,55 +117,6 @@ show_message_part (GMimeObject *part, int *part_count)
printf ("\fpart}\n"); 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 int
notmuch_show_command (void *ctx, unused (int argc), unused (char *argv[])) 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 ("\fheader}\n");
printf ("\fbody{\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"); printf ("\fbody}\n");

View file

@ -166,6 +166,25 @@ Parentheses can also be used to control the combination of the Boolean
operators, but will have to be protected from interpretation by the operators, but will have to be protected from interpretation by the
shell, (such as by putting quotation marks around any parenthesized shell, (such as by putting quotation marks around any parenthesized
expression). 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 .TP
.BR show " <search-term>..." .BR show " <search-term>..."

View file

@ -1,6 +1,7 @@
/* notmuch - Not much of an email program, (just index and search) /* notmuch - Not much of an email program, (just index and search)
* *
* Copyright © 2009 Carl Worth * Copyright © 2009 Carl Worth
* Copyright © 2009 Keith Packard
* *
* This program is free software: you can redistribute it and/or modify * 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 * 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 * You should have received a copy of the GNU General Public License
* along with this program. If not, see http://www.gnu.org/licenses/ . * 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" #include "notmuch-client.h"
@ -82,6 +84,19 @@ command_t commands[] = {
"\t\tthe Boolean operators, but will have to be protected from\n" "\t\tthe Boolean operators, but will have to be protected from\n"
"\t\tinterpretation by the shell, (such as by putting quotation\n" "\t\tinterpretation by the shell, (such as by putting quotation\n"
"\t\tmarks around any parenthesized expression)." }, "\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, { "show", notmuch_show_command,
"<search-terms> [...]\n\n" "<search-terms> [...]\n\n"
"\t\tShows all messages matching the search terms.", "\t\tShows all messages matching the search terms.",

117
show-message.c Normal file
View file

@ -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;
}