#include "notmuch-private.h"
+#include <gmime/gmime.h>
+
#include <glib.h> /* GHashTable */
typedef struct {
GHashTable *headers;
int broken_headers;
int good_headers;
+ size_t header_size; /* Length of full message header in bytes. */
/* Parsing state */
char *line;
return hash;
}
+static int
+_notmuch_message_file_destructor (notmuch_message_file_t *message)
+{
+ if (message->line)
+ free (message->line);
+
+ if (message->value.size)
+ free (message->value.str);
+
+ if (message->headers)
+ g_hash_table_destroy (message->headers);
+
+ if (message->file)
+ fclose (message->file);
+
+ return 0;
+}
+
+/* Create a new notmuch_message_file_t for 'filename' with 'ctx' as
+ * the talloc owner. */
notmuch_message_file_t *
-notmuch_message_file_open (const char *filename)
+_notmuch_message_file_open_ctx (void *ctx, const char *filename)
{
notmuch_message_file_t *message;
- message = xcalloc (1, sizeof (notmuch_message_file_t));
+ message = talloc_zero (ctx, notmuch_message_file_t);
+ if (unlikely (message == NULL))
+ return NULL;
+
+ talloc_set_destructor (message, _notmuch_message_file_destructor);
message->file = fopen (filename, "r");
if (message->file == NULL)
return NULL;
}
+notmuch_message_file_t *
+notmuch_message_file_open (const char *filename)
+{
+ return _notmuch_message_file_open_ctx (NULL, filename);
+}
+
void
notmuch_message_file_close (notmuch_message_file_t *message)
{
- if (message == NULL)
- return;
-
- if (message->line)
- free (message->line);
-
- if (message->value.size)
- free (message->value.str);
-
- if (message->headers)
- g_hash_table_destroy (message->headers);
-
- if (message->file)
- fclose (message->file);
-
- free (message);
+ talloc_free (message);
}
void
{
char *header;
- if (message->parsing_started ) {
- fprintf (stderr, "Error: notmuch_message_file_restrict_headers called after parsing has started\n");
- exit (1);
- }
+ if (message->parsing_started)
+ INTERNAL_ERROR ("notmuch_message_file_restrict_headers called after parsing has started");
while (1) {
header = va_arg (va_headers, char*);
notmuch_message_file_restrict_headersv (message, va_headers);
}
-void
+static void
copy_header_unfolding (header_value_closure_t *value,
const char *chunk)
{
chunk++;
if (value->len + 1 + strlen (chunk) + 1 > value->size) {
- int new_size = value->size;
+ unsigned int new_size = value->size;
if (value->size == 0)
new_size = strlen (chunk) + 1;
else
}
}
+/* As a special-case, a value of NULL for header_desired will force
+ * the entire header to be parsed if it is not parsed already. This is
+ * used by the _notmuch_message_file_get_headers_end function. */
const char *
notmuch_message_file_get_header (notmuch_message_file_t *message,
const char *header_desired)
{
int contains;
- char *header, *value;
+ char *header, *decoded_value;
const char *s, *colon;
int match;
+ static int initialized = 0;
+
+ if (! initialized) {
+ g_mime_init (0);
+ initialized = 1;
+ }
message->parsing_started = 1;
- contains = g_hash_table_lookup_extended (message->headers,
- header_desired, NULL,
- (gpointer *) &value);
- if (contains && value)
- return value;
+ if (header_desired == NULL)
+ contains = 0;
+ else
+ contains = g_hash_table_lookup_extended (message->headers,
+ header_desired, NULL,
+ (gpointer *) &decoded_value);
+
+ if (contains && decoded_value)
+ return decoded_value;
if (message->parsing_finished)
return NULL;
#define NEXT_HEADER_LINE(closure) \
- do { \
+ while (1) { \
ssize_t bytes_read = getline (&message->line, \
&message->line_size, \
message->file); \
{ \
copy_header_unfolding ((closure), message->line); \
} \
- } while (*message->line == ' ' || *message->line == '\t');
+ if (*message->line == ' ' || *message->line == '\t') \
+ message->header_size += strlen (message->line); \
+ else \
+ break; \
+ }
if (message->line == NULL)
NEXT_HEADER_LINE (NULL);
continue;
}
+ message->header_size += strlen (message->line);
+
message->good_headers++;
header = xstrndup (message->line, colon - message->line);
NEXT_HEADER_LINE (&message->value);
- match = (strcasecmp (header, header_desired) == 0);
+ if (header_desired == 0)
+ match = 0;
+ else
+ match = (strcasecmp (header, header_desired) == 0);
- value = xstrdup (message->value.str);
+ decoded_value = g_mime_utils_header_decode_text (message->value.str);
- g_hash_table_insert (message->headers, header, value);
+ g_hash_table_insert (message->headers, header, decoded_value);
if (match)
- return value;
+ return decoded_value;
}
if (message->line)
message->value.len = 0;
}
+ /* We've parsed all headers and never found the one we're looking
+ * for. It's probably just not there, but let's check that we
+ * didn't make a mistake preventing us from seeing it. */
+ if (message->restrict_headers && header_desired &&
+ ! g_hash_table_lookup_extended (message->headers,
+ header_desired, NULL, NULL))
+ {
+ INTERNAL_ERROR ("Attempt to get header \"%s\" which was not\n"
+ "included in call to notmuch_message_file_restrict_headers\n",
+ header_desired);
+ }
+
return NULL;
}