diff options
-rw-r--r-- | camel/ChangeLog | 34 | ||||
-rw-r--r-- | camel/Makefile.am | 2 | ||||
-rw-r--r-- | camel/camel-folder-search.c | 8 | ||||
-rw-r--r-- | camel/camel-folder-summary.c | 1041 | ||||
-rw-r--r-- | camel/camel-folder-summary.h | 181 | ||||
-rw-r--r-- | camel/camel-folder.c | 1 | ||||
-rw-r--r-- | camel/camel-folder.h | 13 | ||||
-rw-r--r-- | camel/camel-mime-filter-index.c | 12 | ||||
-rw-r--r-- | camel/camel-mime-filter-index.h | 1 | ||||
-rw-r--r-- | camel/camel-mime-parser.c | 288 | ||||
-rw-r--r-- | camel/camel-mime-parser.h | 1 | ||||
-rw-r--r-- | camel/camel-mime-part-utils.c | 8 | ||||
-rw-r--r-- | camel/camel-mime-part.c | 8 | ||||
-rw-r--r-- | camel/camel-mime-utils.c | 36 | ||||
-rw-r--r-- | camel/providers/mbox/camel-mbox-summary.c | 10 |
15 files changed, 1597 insertions, 47 deletions
diff --git a/camel/ChangeLog b/camel/ChangeLog index 39846f46ca..5a655a3aa7 100644 --- a/camel/ChangeLog +++ b/camel/ChangeLog @@ -1,3 +1,25 @@ +2000-05-04 NotZed <NotZed@HelixCode.com> + + * providers/mbox/camel-mbox-summary.c: Yes, and anotherone. + + * camel-mime-utils.c: And another one. + + * camel-mime-part.c: And another one. + + * camel-mime-part-utils.c: And another one. + + * camel-folder-search.c: And another one. + + * camel-mime-parser.c: Reverted a change wihtout a ChangeLog entry. + +2000-05-04 NotZed <NotZed@HelixCode.com> + + * camel-folder-summary.[hc]: Yes, CamelFolderSummary is back ... + ... re-usable class to summarise and index any stream or message + and to manage/load/save the created summaries. + + * camel-folder.c: Include string.h to kill a warning. + 2000-05-03 Jason Leach <leach@wam.umd.edu> * Makefile.am (INCLUDES): add $(UNICODE_CFLAGS) to the INCLUDES, @@ -6,6 +28,18 @@ 2000-05-03 NotZed <NotZed@HelixCode.com> + * camel-folder.h: Added pos/bodypos/endpos to the basic message + content info object. Size to be removed? Moved the + messageconentinfo and messageinfo back to camel-folder-summary.h. + + * camel-mime-filter-index.c (camel_mime_filter_index_set_ibex): + New function to (re)set the index to use on a filter. + + * camel-mime-parser.c (camel_mime_parser_scan_from): Whole bunch + of inline docs. + (camel_mime_parser_drop_step): New function to drop a state from + the parser. Needs more testing. + * camel-mime-utils.c (rfc2047_decode_word): If the iconv handle is -1, then dont try and convert (crashes unicode_iconv?). (rfc2047_decode_word): Use alloca for variables instead of diff --git a/camel/Makefile.am b/camel/Makefile.am index 81914c5950..513cd25648 100644 --- a/camel/Makefile.am +++ b/camel/Makefile.am @@ -38,6 +38,7 @@ libcamel_la_SOURCES = \ camel-exception.c \ camel-folder.c \ camel-folder-search.c \ + camel-folder-summary.c \ camel-medium.c \ camel-marshal-utils.c \ camel-mime-message.c \ @@ -81,6 +82,7 @@ libcamelinclude_HEADERS = \ camel-exception.h \ camel-folder.h \ camel-folder-search.h \ + camel-folder-summary.h \ camel-marshal-utils.h \ camel-medium.h \ camel-mime-message.h \ diff --git a/camel/camel-folder-search.c b/camel/camel-folder-search.c index 9d6f80d256..8a58d9a6ef 100644 --- a/camel/camel-folder-search.c +++ b/camel/camel-folder-search.c @@ -358,15 +358,15 @@ search_header_contains(struct _ESExp *f, int argc, struct _ESExpResult **argv, C /* only a subset of headers are supported .. */ headername = argv[0]->value.string; - if (!g_strcasecmp(headername, "subject")) { + if (!strcasecmp(headername, "subject")) { header = search->current->subject; - } else if (!g_strcasecmp(headername, "date")) { + } else if (!strcasecmp(headername, "date")) { /* FIXME: not a very useful form of the date */ sprintf(strbuf, "%d", (int)search->current->date_sent); header = strbuf; - } else if (!g_strcasecmp(headername, "from")) { + } else if (!strcasecmp(headername, "from")) { header = search->current->from; - } else if (!g_strcasecmp(headername, "to")) { + } else if (!strcasecmp(headername, "to")) { header = search->current->from; } else { g_warning("Performing query on unknown header: %s", headername); diff --git a/camel/camel-folder-summary.c b/camel/camel-folder-summary.c new file mode 100644 index 0000000000..c54100375f --- /dev/null +++ b/camel/camel-folder-summary.c @@ -0,0 +1,1041 @@ +/* + * Copyright (C) 2000 Helix Code Inc. + * + * Authors: Michael Zucchi <notzed@helixcode.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 2 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 <unistd.h> +#include <netinet/in.h> +#include <ctype.h> +#include <string.h> + +#include "camel-folder-summary.h" + +#include <camel/camel-mime-filter.h> +#include <camel/camel-mime-filter-index.h> +#include <camel/camel-mime-filter-charset.h> +#include <camel/camel-mime-filter-save.h> +#include <camel/camel-mime-filter-basic.h> +#include "hash-table-utils.h" + +#define d(x) + +#define CAMEL_FOLDER_SUMMARY_VERSION (3) + +struct _CamelFolderSummaryPrivate { + GHashTable *filter_charset; /* CamelMimeFilterCharset's indexed by source charset */ + + CamelMimeFilterIndex *filter_index; + CamelMimeFilterBasic *filter_64; + CamelMimeFilterBasic *filter_qp; + CamelMimeFilterSave *filter_save; + + ibex *index; +}; + +#define _PRIVATE(o) (((CamelFolderSummary *)(o))->priv) + +/* trivial lists, just because ... */ +struct _node { + struct _node *next; +}; + +static struct _node *my_list_append(struct _node **list, struct _node *n); +static int my_list_size(struct _node **list); + +static int summary_header_load(CamelFolderSummary *, FILE *); +static int summary_header_save(CamelFolderSummary *, FILE *); + +static CamelMessageInfo * message_info_new(CamelFolderSummary *, struct _header_raw *); +static CamelMessageInfo * message_info_new_from_parser(CamelFolderSummary *, CamelMimeParser *); +static CamelMessageInfo * message_info_load(CamelFolderSummary *, FILE *); +static int message_info_save(CamelFolderSummary *, FILE *, CamelMessageInfo *); +static void message_info_free(CamelFolderSummary *, CamelMessageInfo *); + +static CamelMessageContentInfo * content_info_new(CamelFolderSummary *, struct _header_raw *); +static CamelMessageContentInfo * content_info_new_from_parser(CamelFolderSummary *, CamelMimeParser *); +static CamelMessageContentInfo * content_info_load(CamelFolderSummary *, FILE *); +static int content_info_save(CamelFolderSummary *, FILE *, CamelMessageContentInfo *); +static void content_info_free(CamelFolderSummary *, CamelMessageContentInfo *); + +static CamelMessageContentInfo * summary_build_content_info(CamelFolderSummary *s, CamelMimeParser *mp); + +static void camel_folder_summary_class_init (CamelFolderSummaryClass *klass); +static void camel_folder_summary_init (CamelFolderSummary *obj); +static void camel_folder_summary_finalise (GtkObject *obj); + +static GtkObjectClass *camel_folder_summary_parent; + +enum SIGNALS { + LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL] = { 0 }; + +guint +camel_folder_summary_get_type (void) +{ + static guint type = 0; + + if (!type) { + GtkTypeInfo type_info = { + "CamelFolderSummary", + sizeof (CamelFolderSummary), + sizeof (CamelFolderSummaryClass), + (GtkClassInitFunc) camel_folder_summary_class_init, + (GtkObjectInitFunc) camel_folder_summary_init, + (GtkArgSetFunc) NULL, + (GtkArgGetFunc) NULL + }; + + type = gtk_type_unique (gtk_object_get_type (), &type_info); + } + + return type; +} + +static void +camel_folder_summary_class_init (CamelFolderSummaryClass *klass) +{ + GtkObjectClass *object_class = (GtkObjectClass *) klass; + + camel_folder_summary_parent = gtk_type_class (gtk_object_get_type ()); + + object_class->finalize = camel_folder_summary_finalise; + + klass->summary_header_load = summary_header_load; + klass->summary_header_save = summary_header_save; + + klass->message_info_new = message_info_new; + klass->message_info_new_from_parser = message_info_new_from_parser; + klass->message_info_load = message_info_load; + klass->message_info_save = message_info_save; + klass->message_info_free = message_info_free; + + klass->content_info_new = content_info_new; + klass->content_info_new_from_parser = content_info_new_from_parser; + klass->content_info_load = content_info_load; + klass->content_info_save = content_info_save; + klass->content_info_free = content_info_free; + + gtk_object_class_add_signals (object_class, signals, LAST_SIGNAL); +} + +static void +camel_folder_summary_init (CamelFolderSummary *s) +{ + struct _CamelFolderSummaryPrivate *p; + + p = _PRIVATE(s) = g_malloc0(sizeof(*p)); + + p->filter_charset = g_hash_table_new(g_strcase_hash, g_strcase_equal); + + s->message_info_size = sizeof(CamelMessageInfo); + s->content_info_size = sizeof(CamelMessageContentInfo); + + s->version = CAMEL_FOLDER_SUMMARY_VERSION; + s->flags = 0; + s->time = 0; + s->nextuid = 1; + + s->messages = g_ptr_array_new(); + s->messages_uid = g_hash_table_new(g_str_hash, g_str_equal); +} + +static void +camel_folder_summary_finalise (GtkObject *obj) +{ + struct _CamelFolderSummaryPrivate *p; + CamelFolderSummary *s = (CamelFolderSummary *)obj; + + p = _PRIVATE(obj); + + /* FIXME: free contents */ + g_ptr_array_free(s->messages, TRUE); + + g_hash_table_destroy(s->messages_uid); + + /* FIXME: free contents */ + g_hash_table_destroy(p->filter_charset); + g_free(p); + + ((GtkObjectClass *)(camel_folder_summary_parent))->finalize((GtkObject *)obj); +} + +/** + * camel_folder_summary_new: + * + * Create a new CamelFolderSummary object. + * + * Return value: A new CamelFolderSummary widget. + **/ +CamelFolderSummary * +camel_folder_summary_new (void) +{ + CamelFolderSummary *new = CAMEL_FOLDER_SUMMARY ( gtk_type_new (camel_folder_summary_get_type ())); + return new; +} + + +void camel_folder_summary_set_filename(CamelFolderSummary *s, const char *name) +{ + g_free(s->summary_path); + s->summary_path = g_strdup(name); +} + +void camel_folder_summary_set_index(CamelFolderSummary *s, ibex *index) +{ + struct _CamelFolderSummaryPrivate *p = _PRIVATE(s); + + if (p->index) + ibex_write(p->index); + p->index = index; +} + +void camel_folder_summary_set_build_content(CamelFolderSummary *s, gboolean state) +{ + s->build_content = state; +} + +int +camel_folder_summary_count(CamelFolderSummary *s) +{ + return s->messages->len; +} + +CamelMessageInfo * +camel_folder_summary_index(CamelFolderSummary *s, int i) +{ + if (i<s->messages->len) + return g_ptr_array_index(s->messages, i); + return NULL; +} + +CamelMessageInfo * +camel_folder_summary_uid(CamelFolderSummary *s, const char *uid) +{ + return g_hash_table_lookup(s->messages_uid, uid); +} + +void +camel_folder_summary_set_uid(CamelFolderSummary *s, guint32 base) +{ + if (s->nextuid <= base) + s->nextuid = base+1; +} + +guint32 camel_folder_summary_next_uid(CamelFolderSummary *s) +{ + guint32 uid = s->nextuid++; + + /* FIXME: sync this to disk */ +/* summary_header_save(s);*/ + return uid; +} + +/* loads the content descriptions, recursively */ +static CamelMessageContentInfo * +perform_content_info_load(CamelFolderSummary *s, FILE *in) +{ + int i; + guint32 count; + CamelMessageContentInfo *ci, *part; + + ci = ((CamelFolderSummaryClass *)((GtkObject *)s)->klass)->content_info_load(s, in); + camel_folder_summary_decode_uint32(in, &count); + for (i=0;i<count;i++) { + part = perform_content_info_load(s, in); + if (part) { + my_list_append((struct _node **)&ci->childs, (struct _node *)ci); + ci->parent = ci; + } else { + g_warning("Summary file format messed up?"); + } + } + return ci; +} + +int +camel_folder_summary_load(CamelFolderSummary *s) +{ + FILE *in; + int i; + CamelMessageInfo *mi; + + g_assert(s->summary_path); + + in = fopen(s->summary_path, "r"); + if ( in == NULL ) { + return -1; + } + + if ( ((CamelFolderSummaryClass *)((GtkObject *)s)->klass)->summary_header_load(s, in) == -1) { + fclose(in); + return -1; + } + + /* now read in each message ... */ + /* FIXME: check returns */ + for (i=0;i<s->saved_count;i++) { + mi = ((CamelFolderSummaryClass *)((GtkObject *)s)->klass)->message_info_load(s, in); + + if (s->build_content) { + mi->content = content_info_load(s, in); + } + } + + /* FIXME: check error return */ + return 0; +} + +/* saves the content descriptions, recursively */ +static int +perform_content_info_save(CamelFolderSummary *s, FILE *out, CamelMessageContentInfo *ci) +{ + CamelMessageContentInfo *part; + + ((CamelFolderSummaryClass *)((GtkObject *)s)->klass)->content_info_save(s, out, ci); + camel_folder_summary_encode_uint32(out, my_list_size((struct _node **)&ci->childs)); + part = ci->childs; + while (part) { + perform_content_info_save(s, out, part); + part = part->next; + } + return 0; +} + +int +camel_folder_summary_save(CamelFolderSummary *s) +{ + FILE *out; + int fd; + int i; + guint32 count; + CamelMessageInfo *mi; + + g_assert(s->summary_path); + + fd = open(s->summary_path, O_RDWR|O_CREAT, 0600); + if (fd == -1) + return -1; + out = fdopen(fd, "w"); + if ( out == NULL ) { + close(fd); + return -1; + } + + if ( ((CamelFolderSummaryClass *)((GtkObject *)s)->klass)->summary_header_save(s, out) == -1) { + fclose(out); + return -1; + } + + /* now write out each message ... */ + /* FIXME: check returns */ + count = camel_folder_summary_count(s); + for (i=0;i<count;i++) { + mi = camel_folder_summary_index(s, i); + ((CamelFolderSummaryClass *)((GtkObject *)s)->klass)->message_info_save(s, out, mi); + + if (s->build_content) { + perform_content_info_save(s, out, mi->content); + } + } + return fclose(out); +} + +void camel_folder_summary_add(CamelFolderSummary *s, CamelMessageInfo *info) +{ + if (info == NULL) + return; +retry: + if (info->uid == NULL) { + info->uid = g_strdup_printf("%u", s->nextuid++); + } + if (g_hash_table_lookup(s->messages_uid, info->uid)) { + g_warning("Trying to insert message with clashing uid. new uid re-assigned"); + g_free(info->uid); + info->uid = NULL; + goto retry; + } + + g_ptr_array_add(s->messages, info); + g_hash_table_insert(s->messages_uid, info->uid, info); + s->flags |= CAMEL_SUMMARY_DIRTY; +} + +void camel_folder_summary_add_from_header(CamelFolderSummary *s, struct _header_raw *h) +{ + CamelMessageInfo *info; + + info = ((CamelFolderSummaryClass *)((GtkObject *)s)->klass)->message_info_new(s, h); + camel_folder_summary_add(s, info); +} + +void camel_folder_summary_add_from_parser(CamelFolderSummary *s, CamelMimeParser *mp) +{ + CamelMessageInfo *info; + char *buffer; + int len; + struct _CamelFolderSummaryPrivate *p = _PRIVATE(s); + + /* should this check the parser is in the right state, or assume it is?? */ + + if (camel_mime_parser_step(mp, &buffer, &len) != HSCAN_EOF) { + info = ((CamelFolderSummaryClass *)((GtkObject *)s)->klass)->message_info_new_from_parser(s, mp); + + camel_mime_parser_unstep(mp); + + /* FIXME: better uid assignment method? */ + if (info->uid == NULL) { + info->uid = g_strdup_printf("%u", s->nextuid++); + } + + if (p->index) { + if (p->filter_index == NULL) + p->filter_index = camel_mime_filter_index_new_ibex(p->index); + camel_mime_filter_index_set_name(p->filter_index, info->uid); + ibex_unindex(p->index, info->uid); + } + + /* build the content info, if we're supposed to */ + if (s->build_content) { + info->content = summary_build_content_info(s, mp); + if (info->content->pos != -1) + info->size = info->content->endpos - info->content->pos; + } else { + camel_mime_parser_drop_step(mp); + } + + camel_folder_summary_add(s, info); + } +} + +int +camel_folder_summary_encode_uint32(FILE *out, guint32 value) +{ + int i; + + for (i=28;i>0;i-=7) { + if (value >= (1<<i)) { + unsigned int c = (value>>i) & 0x7f; + if (fputc(c, out) == -1) + return -1; + } + } + return fputc(value | 0x80, out); +} + +int +camel_folder_summary_decode_uint32(FILE *in, guint32 *dest) +{ + gint32 value=0, v; + + /* until we get the last byte, keep decoding 7 bits at a time */ + while ( ((v = fgetc(in)) & 0x80) == 0 && v!=EOF) { + value |= v; + value <<= 7; + } + if (v == EOF) { + *dest = value>>7; + return 01; + } + *dest = value | (v&0x7f); + return 0; +} + +int +camel_folder_summary_encode_fixed_int32(FILE *out, gint32 value) +{ + guint32 save; + + save = htonl(value); + return fwrite(&save, sizeof(save), 1, out); +} + +int +camel_folder_summary_decode_fixed_int32(FILE *in, gint32 *dest) +{ + guint32 save; + + if (fread(&save, sizeof(save), 1, in) != -1) { + *dest = ntohl(save); + return 0; + } else { + return -1; + } +} + +/* should be sorted, for binary search */ +/* This is a tokenisation mechanism for strings written to the + summary - to save space. + This list can have at most 31 words. */ +static char * tokens[] = { + "7bit", + "8bit", + "alternative", + "application", + "base64", + "boundary", + "charset", + "filename", + "html", + "image", + "iso-8859-1", + "iso-8859-8", + "message", + "mixed", + "multipart", + "name", + "octet-stream", + "parallel", + "plain", + "quoted-printable", + "rfc822", + "text", + "us-ascii", /* 23 words */ +}; + +#define tokens_len (sizeof(tokens)/sizeof(tokens[0])) + +/* baiscally ... + 0 = null + 1-tokens_len == tokens[id-1] + >=32 string, length = n-32 +*/ + +int +camel_folder_summary_encode_token(FILE *out, char *str) +{ + if (str == NULL) { + return camel_folder_summary_encode_uint32(out, 0); + } else { + int len = strlen(str); + int i, token=-1; + + if (len <= 16) { + char lower[32]; + + for (i=0;i<len;i++) + lower[i] = tolower(str[i]); + lower[i] = 0; + for (i=0;i<tokens_len;i++) { + if (!strcmp(tokens[i], lower)) { + token = i; + break; + } + } + } + if (token != -1) { + return camel_folder_summary_encode_uint32(out, token+1); + } else { + if (camel_folder_summary_encode_uint32(out, len+32) == -1) + return -1; + return fwrite(str, len, 1, out); + } + } + return 0; +} + +int +camel_folder_summary_decode_token(FILE *in, char **str) +{ + char *ret; + int len; + + if (camel_folder_summary_decode_uint32(in, &len) == -1) { + *str = NULL; + return -1; + } + + if (len<32) { + if (len <= 0) { + ret = NULL; + } else if (len<= tokens_len) { + ret = g_strdup(tokens[len-1]); + } else { + g_warning("Invalid token encountered: %d", len); + *str = NULL; + return -1; + } + } else if (len > 10240) { + g_warning("Got broken string header length: %d bytes", len); + *str = NULL; + return -1; + } else { + len -= 32; + ret = g_malloc(len+1); + if (fread(ret, len, 1, in) == -1) { + g_free(ret); + *str = NULL; + return -1; + } + ret[len]=0; + } + + *str = ret; + return 0; +} + +int +camel_folder_summary_encode_string(FILE *out, char *str) +{ + register int len; + + if (str == NULL) + return camel_folder_summary_encode_uint32(out, 0); + + len = strlen(str); + if (camel_folder_summary_encode_uint32(out, len+1) == -1) + return -1; + return fwrite(str, len, 1, out); +} + + +int +camel_folder_summary_decode_string(FILE *in, char **str) +{ + int len; + register char *ret; + + if (camel_folder_summary_decode_uint32(in, &len) == -1) { + *str = NULL; + return -1; + } + + len--; + if (len < 0) { + *str = NULL; + return -1; + } + + ret = g_malloc(len+1); + if (fread(ret, len, 1, in) == -1) { + g_free(ret); + *str = NULL; + return -1; + } + + ret[len] = 0; + *str = ret; + return 0; +} + +void +camel_folder_summary_offset_content(CamelMessageContentInfo *content, off_t offset) +{ + content->pos += offset; + content->bodypos += offset; + content->endpos += offset; + content = content->childs; + while (content) { + camel_folder_summary_offset_content(content, offset); + content = content->next; + } +} + +static struct _node * +my_list_append(struct _node **list, struct _node *n) +{ + struct _node *ln = (struct _node *)list; + while (ln->next) + ln = ln->next; + n->next = 0; + ln->next = n; + return n; +} + +static int +my_list_size(struct _node **list) +{ + int len = 0; + struct _node *ln = (struct _node *)list; + while (ln->next) { + ln = ln->next; + len++; + } + return len; +} + +static int +summary_header_load(CamelFolderSummary *s, FILE *in) +{ + guint32 version, flags, nextuid, count; + time_t time; + + fseek(in, 0, SEEK_SET); + + if (camel_folder_summary_decode_fixed_int32(in, &version) == -1 + || camel_folder_summary_decode_fixed_int32(in, &flags) == -1 + || camel_folder_summary_decode_fixed_int32(in, &nextuid) == -1 + || camel_folder_summary_decode_fixed_int32(in, &time) == -1 /* TODO: yes i know this warns, to be fixed later */ + || camel_folder_summary_decode_fixed_int32(in, &count) == -1) { + return -1; + } + + s->nextuid = nextuid; + s->flags = flags; + s->time = time; + s->saved_count = count; + if (s->version != version) { + g_warning("Summary header version mismatch"); + return -1; + } + return 0; +} + +static int +summary_header_save(CamelFolderSummary *s, FILE *out) +{ + fseek(out, 0, SEEK_SET); + + camel_folder_summary_encode_fixed_int32(out, s->version); + camel_folder_summary_encode_fixed_int32(out, s->flags); + camel_folder_summary_encode_fixed_int32(out, s->nextuid); + camel_folder_summary_encode_fixed_int32(out, s->time); + return camel_folder_summary_encode_fixed_int32(out, camel_folder_summary_count(s)); +} + +/* are these even useful for anything??? */ +static CamelMessageInfo * message_info_new_from_parser(CamelFolderSummary *s, CamelMimeParser *mp) +{ + CamelMessageInfo *mi = NULL; + int state; + + state = camel_mime_parser_state(mp); + switch (state) { + case HSCAN_HEADER: + case HSCAN_MESSAGE: + case HSCAN_MULTIPART: + mi = ((CamelFolderSummaryClass *)((GtkObject *)s)->klass)->message_info_new(s, camel_mime_parser_headers_raw(mp)); + break; + default: + g_error("Invalid parser state"); + } + + return mi; +} + +static CamelMessageContentInfo * content_info_new_from_parser(CamelFolderSummary *s, CamelMimeParser *mp) +{ + CamelMessageContentInfo *ci = NULL; + + switch (camel_mime_parser_state(mp)) { + case HSCAN_HEADER: + case HSCAN_MESSAGE: + case HSCAN_MULTIPART: + ci = ((CamelFolderSummaryClass *)((GtkObject *)s)->klass)->content_info_new(s, camel_mime_parser_headers_raw(mp)); + if (ci) { + ci->type = camel_mime_parser_content_type(mp); + header_content_type_ref(ci->type); + } + break; + default: + g_error("Invalid parser state"); + } + + return ci; +} + +static char * +summary_format_address(struct _header_raw *h, const char *name) +{ + struct _header_address *addr; + const char *text; + char *ret; + + text = header_raw_find(&h, name, NULL); + addr = header_address_decode(text); + if (addr) { + ret = header_address_list_format(addr); + header_address_list_clear(&addr); + } else { + ret = g_strdup(text); + } + return ret; +} + +static CamelMessageInfo * +message_info_new(CamelFolderSummary *s, struct _header_raw *h) +{ + CamelMessageInfo *mi; + + mi = g_malloc0(s->message_info_size); + + mi->subject = header_decode_string(header_raw_find(&h, "subject", NULL)); + mi->from = summary_format_address(h, "from"); + mi->to = summary_format_address(h, "to"); + mi->date_sent = header_decode_date(header_raw_find(&h, "date", NULL), NULL); + mi->date_received = 0; + + return mi; +} + + +static CamelMessageInfo * +message_info_load(CamelFolderSummary *s, FILE *in) +{ + guint32 tmp; + CamelMessageInfo *mi; + + mi = g_malloc0(s->message_info_size); + + camel_folder_summary_decode_uint32(in, &tmp); + mi->uid = g_strdup_printf("%u", tmp); + camel_folder_summary_decode_uint32(in, &mi->flags); + camel_folder_summary_decode_uint32(in, &mi->date_sent); /* warnings, leave them here */ + camel_folder_summary_decode_uint32(in, &mi->date_received); +/* ms->xev_offset = camel_folder_summary_decode_uint32(in);*/ + camel_folder_summary_decode_string(in, &mi->subject); + camel_folder_summary_decode_string(in, &mi->from); + camel_folder_summary_decode_string(in, &mi->to); + mi->content = NULL; + + return mi; +} + +static int +message_info_save(CamelFolderSummary *s, FILE *out, CamelMessageInfo *mi) +{ + camel_folder_summary_encode_uint32(out, strtoul(mi->uid, NULL, 10)); + camel_folder_summary_encode_uint32(out, mi->flags); + camel_folder_summary_encode_uint32(out, mi->date_sent); + camel_folder_summary_encode_uint32(out, mi->date_received); +/* camel_folder_summary_encode_uint32(out, ms->xev_offset);*/ + camel_folder_summary_encode_string(out, mi->subject); + camel_folder_summary_encode_string(out, mi->from); + return camel_folder_summary_encode_string(out, mi->to); +} + +static void +message_info_free(CamelFolderSummary *s, CamelMessageInfo *mi) +{ + g_free(mi->uid); + g_free(mi->subject); + g_free(mi->from); + g_free(mi->to); + g_free(mi); +} + +static CamelMessageContentInfo * +content_info_new(CamelFolderSummary *s, struct _header_raw *h) +{ + CamelMessageContentInfo *ci; + + ci = g_malloc0(s->content_info_size); + + ci->id = header_msgid_decode(header_raw_find(&h, "content-id", NULL)); + ci->description = header_decode_string(header_raw_find(&h, "content-description", NULL)); + ci->encoding = header_content_encoding_decode(header_raw_find(&h, "content-transfer-encoding", NULL)); + + ci->pos = -1; + ci->bodypos = -1; + ci->endpos = -1; + return ci; +} + +static CamelMessageContentInfo * +content_info_load(CamelFolderSummary *s, FILE *in) +{ + CamelMessageContentInfo *ci; + char *type, *subtype; + guint32 count, i; + struct _header_content_type *ct; + + ci = g_malloc0(s->content_info_size); + +/* bs->pos = decode_int(in); + bs->bodypos = bs->pos + decode_int(in); + bs->endpos = bs->pos + decode_int(in);*/ + + camel_folder_summary_decode_token(in, &type); + camel_folder_summary_decode_token(in, &subtype); + ct = header_content_type_new(type, subtype); + g_free(type); /* can this be removed? */ + g_free(subtype); + camel_folder_summary_decode_uint32(in, &count); + for (i=0;i<count;i++) { + char *name, *value; + camel_folder_summary_decode_token(in, &name); + camel_folder_summary_decode_token(in, &value); + header_content_type_set_param(ct, name, value); + /* TODO: do this so we dont have to double alloc/free */ + g_free(name); + g_free(value); + } + ci->type = ct; + + camel_folder_summary_decode_token(in, &ci->id); + camel_folder_summary_decode_token(in, &ci->description); + camel_folder_summary_decode_token(in, &ci->encoding); + + ci->childs = NULL; + return ci; +} + +static int +content_info_save(CamelFolderSummary *s, FILE *out, CamelMessageContentInfo *ci) +{ + struct _header_content_type *ct; + struct _header_param *hp; + +/* camel_folder_summary_encode_uint32(out, bs->pos); + camel_folder_summary_encode_uint32(out, bs->bodypos - bs->pos); + camel_folder_summary_encode_uint32(out, bs->endpos - bs->pos);*/ + + ct = ci->type; + if (ct) { + camel_folder_summary_encode_token(out, ct->type); + camel_folder_summary_encode_token(out, ct->subtype); + camel_folder_summary_encode_uint32(out, my_list_size((struct _node **)&ct->params)); + hp = ct->params; + while (hp) { + camel_folder_summary_encode_token(out, hp->name); + camel_folder_summary_encode_token(out, hp->value); + hp = hp->next; + } + } else { + camel_folder_summary_encode_token(out, NULL); + camel_folder_summary_encode_token(out, NULL); + camel_folder_summary_encode_uint32(out, 0); + } + camel_folder_summary_encode_token(out, ci->id); + camel_folder_summary_encode_token(out, ci->description); + return camel_folder_summary_encode_token(out, ci->encoding); +} + +static void +content_info_free(CamelFolderSummary *s, CamelMessageContentInfo *ci) +{ + header_content_type_unref(ci->type); + g_free(ci->id); + g_free(ci->description); + g_free(ci->encoding); + g_free(ci); +} + +/* + OK + Now this is where all the "smarts" happen, where the content info is built, + and any indexing and what not is performed +*/ + +static CamelMessageContentInfo * +summary_build_content_info(CamelFolderSummary *s, CamelMimeParser *mp) +{ + int state, len; + char *buffer; + CamelMessageContentInfo *info = NULL; + struct _header_content_type *ct; + int start, body; + int enc_id = -1, chr_id = -1, idx_id = -1; + struct _CamelFolderSummaryPrivate *p = _PRIVATE(s); + CamelMimeFilterCharset *mfc; + CamelMessageContentInfo *part; + + /* start of this part */ + start = camel_mime_parser_tell(mp); + state = camel_mime_parser_step(mp, &buffer, &len); + body = camel_mime_parser_tell(mp); + + info = ((CamelFolderSummaryClass *)((GtkObject *)s)->klass)->content_info_new_from_parser(s, mp); + + info->pos = start; + info->bodypos = body; + + switch(state) { + case HSCAN_HEADER: + /* check content type for indexing, then read body */ + ct = camel_mime_parser_content_type(mp); + if (p->index && header_content_type_is(ct, "text", "*")) { + char *encoding; + const char *charset; + + encoding = header_content_encoding_decode(camel_mime_parser_header(mp, "content-transfer-encoding", NULL)); + if (encoding) { + if (!strcasecmp(encoding, "base64")) { + if (p->filter_64 == NULL) + p->filter_64 = camel_mime_filter_basic_new_type(CAMEL_MIME_FILTER_BASIC_BASE64_DEC); + enc_id = camel_mime_parser_filter_add(mp, (CamelMimeFilter *)p->filter_64); + } else if (!strcasecmp(encoding, "quoted-printable")) { + if (p->filter_qp == NULL) + p->filter_qp = camel_mime_filter_basic_new_type(CAMEL_MIME_FILTER_BASIC_QP_DEC); + enc_id = camel_mime_parser_filter_add(mp, (CamelMimeFilter *)p->filter_qp); + } + g_free(encoding); + } + + charset = header_content_type_param(ct, "charset"); + if (charset!=NULL + && !(strcasecmp(charset, "us-ascii")==0 + || strcasecmp(charset, "utf-8")==0)) { + d(printf("Adding conversion filter from %s to utf-8\n", charset)); + mfc = g_hash_table_lookup(p->filter_charset, charset); + if (mfc == NULL) { + mfc = camel_mime_filter_charset_new_convert(charset, "utf-8"); + if (mfc) + g_hash_table_insert(p->filter_charset, g_strdup(charset), mfc); + } + if (mfc) { + chr_id = camel_mime_parser_filter_add(mp, (CamelMimeFilter *)mfc); + } else { + g_warning("Cannot convert '%s' to 'utf-8', message index may be corrupt", charset); + } + } + + /* and this filter actually does the indexing */ + idx_id = camel_mime_parser_filter_add(mp, (CamelMimeFilter *)p->filter_index); + } + /* and scan/index everything */ + while (camel_mime_parser_step(mp, &buffer, &len) != HSCAN_BODY_END) + ; + /* and remove the filters */ + camel_mime_parser_filter_remove(mp, enc_id); + camel_mime_parser_filter_remove(mp, chr_id); + camel_mime_parser_filter_remove(mp, idx_id); + break; + case HSCAN_MULTIPART: + while (camel_mime_parser_step(mp, &buffer, &len) != HSCAN_MULTIPART_END) { + camel_mime_parser_unstep(mp); + part = summary_build_content_info(s, mp); + if (part) { + part->parent = info; + my_list_append((struct _node **)&info->childs, (struct _node *)part); + } else { + g_error("Parsing failed: could not build part of a multipart"); + } + } + break; + case HSCAN_MESSAGE: + part = summary_build_content_info(s, mp); + if (part) { + part->parent = info; + my_list_append((struct _node **)&info->childs, (struct _node *)part); + } else { + g_error("Parsing failed: no content of a message?"); + } + if (!(state == HSCAN_MESSAGE_END)) { + g_error("Bad parser state: Expecing MESSAGE_END or MESSAGE_EOF, got: %d", state); + camel_mime_parser_unstep(mp); + } + break; + } + + info->endpos = camel_mime_parser_tell(mp); + + return info; +} diff --git a/camel/camel-folder-summary.h b/camel/camel-folder-summary.h new file mode 100644 index 0000000000..69045d6931 --- /dev/null +++ b/camel/camel-folder-summary.h @@ -0,0 +1,181 @@ +/* + * Copyright (C) 2000 Helix Code Inc. + * + * Authors: Michael Zucchi <notzed@helixcode.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 2 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 _CAMEL_FOLDER_SUMMARY_H +#define _CAMEL_FOLDER_SUMMARY_H + +#include <gtk/gtk.h> +#include <stdio.h> +#include <time.h> +#include <camel/camel-mime-parser.h> +#include <libibex/ibex.h> + +#define CAMEL_FOLDER_SUMMARY(obj) GTK_CHECK_CAST (obj, camel_folder_summary_get_type (), CamelFolderSummary) +#define CAMEL_FOLDER_SUMMARY_CLASS(klass) GTK_CHECK_CLASS_CAST (klass, camel_folder_summary_get_type (), CamelFolderSummaryClass) +#define IS_CAMEL_FOLDER_SUMMARY(obj) GTK_CHECK_TYPE (obj, camel_folder_summary_get_type ()) + +/*typedef struct _CamelFolderSummary CamelFolderSummary;*/ +typedef struct _CamelFolderSummaryClass CamelFolderSummaryClass; + +/* these structs from camel-folder-summary.h ... (remove comment after cleanup soon) */ +/* TODO: perhaps they should be full-block objects? */ +/* FIXME: rename this to something more suitable */ +typedef struct { + gchar *name; + gint nb_message; /* ick, these should be renamed to something better */ + gint nb_unread_message; + gint nb_deleted_message; +} CamelFolderInfo; + +/* A tree of message content info structures + describe the content structure of the message (if it has any) */ +typedef struct _CamelMessageContentInfo { + struct _CamelMessageContentInfo *next; + + struct _CamelMessageContentInfo *childs; + struct _CamelMessageContentInfo *parent; + + struct _header_content_type *type; + char *id; + char *description; + char *encoding; + + /* information about where this object lives in the stream. + if pos is -1 these are all invalid */ + off_t pos; + off_t bodypos; + off_t endpos; +} CamelMessageContentInfo; + +/* information about a given object */ +typedef struct { + /* public fields */ + gchar *subject; + gchar *to; + gchar *from; + + gchar *uid; + guint32 flags; + guint32 size; + + time_t date_sent; + time_t date_received; + + /* tree of content description - NULL if it is not available */ + CamelMessageContentInfo *content; +} CamelMessageInfo; + +enum _CamelFolderSummaryFlags { + CAMEL_SUMMARY_DIRTY = 1<<0, +}; + +struct _CamelFolderSummary { + GtkObject parent; + + struct _CamelFolderSummaryPrivate *priv; + + /* header info */ + guint32 version; /* version of file required, should be set by implementors */ + guint32 flags; /* flags */ + guint32 nextuid; /* next uid? */ + guint32 saved_count; /* how many were saved/loaded */ + time_t time; /* timestamp for this summary */ + + /* sizes of memory objects */ + guint32 message_info_size; + guint32 content_info_size; + + char *summary_path; + gboolean build_content; /* do we try and parse/index the content, or not? */ + + GPtrArray *messages; /* CamelMessageInfo's */ + GHashTable *messages_uid; /* CamelMessageInfo's by uid */ +}; + +struct _CamelFolderSummaryClass { + GtkObjectClass parent_class; + + /* load/save the global info */ + int (*summary_header_load)(CamelFolderSummary *, FILE *); + int (*summary_header_save)(CamelFolderSummary *, FILE *); + + /* create/save/load an individual message info */ + CamelMessageInfo * (*message_info_new)(CamelFolderSummary *, struct _header_raw *); + CamelMessageInfo * (*message_info_new_from_parser)(CamelFolderSummary *, CamelMimeParser *); + CamelMessageInfo * (*message_info_load)(CamelFolderSummary *, FILE *); + int (*message_info_save)(CamelFolderSummary *, FILE *, CamelMessageInfo *); + void (*message_info_free)(CamelFolderSummary *, CamelMessageInfo *); + + /* save/load individual content info's */ + CamelMessageContentInfo * (*content_info_new)(CamelFolderSummary *, struct _header_raw *); + CamelMessageContentInfo * (*content_info_new_from_parser)(CamelFolderSummary *, CamelMimeParser *); + CamelMessageContentInfo * (*content_info_load)(CamelFolderSummary *, FILE *); + int (*content_info_save)(CamelFolderSummary *, FILE *, CamelMessageContentInfo *); + void (*content_info_free)(CamelFolderSummary *, CamelMessageContentInfo *); +}; + +guint camel_folder_summary_get_type (void); +CamelFolderSummary *camel_folder_summary_new (void); + +void camel_folder_summary_set_filename(CamelFolderSummary *, const char *); +void camel_folder_summary_set_index(CamelFolderSummary *, ibex *); +void camel_folder_summary_set_uid(CamelFolderSummary *, guint32); +void camel_folder_summary_set_build_content(CamelFolderSummary *, gboolean state); + +guint32 camel_folder_summary_next_uid(CamelFolderSummary *s); + +/* load/save the summary in its entirety */ +int camel_folder_summary_load(CamelFolderSummary *); +int camel_folder_summary_save(CamelFolderSummary *); + +/* add a new raw summary item */ +void camel_folder_summary_add(CamelFolderSummary *, CamelMessageInfo *info); +void camel_folder_summary_add_from_header(CamelFolderSummary *, struct _header_raw *); +void camel_folder_summary_add_from_parser(CamelFolderSummary *, CamelMimeParser *); + +/* removes a summary item, fixes offsets? */ +void camel_mbox_summary_remove_uid(CamelFolderSummary *s, const char *uid); + +/* lookup functions */ +int camel_folder_summary_count(CamelFolderSummary *); +CamelMessageInfo *camel_folder_summary_index(CamelFolderSummary *, int); +CamelMessageInfo *camel_folder_summary_uid(CamelFolderSummary *, const char *uid); + +/* utility functions */ +void camel_folder_summary_set_flags_by_uid(CamelFolderSummary *s, const char *uid, guint32 flags); +/* shift content ... */ +void camel_folder_summary_offset_content(CamelMessageContentInfo *content, off_t offset); + +/* summary file loading/saving helper functions */ +int camel_folder_summary_encode_fixed_int32(FILE *, gint32); +int camel_folder_summary_decode_fixed_int32(FILE *, gint32 *); + +int camel_folder_summary_encode_uint32(FILE *, guint32); +int camel_folder_summary_decode_uint32(FILE *, guint32 *); + +int camel_folder_summary_encode_string(FILE *, char *); +int camel_folder_summary_decode_string(FILE *, char **); + +/* basically like strings, but certain keywords can be compressed and de-cased */ +int camel_folder_summary_encode_token(FILE *, char *); +int camel_folder_summary_decode_token(FILE *, char **); + +#endif /* ! _CAMEL_FOLDER_SUMMARY_H */ diff --git a/camel/camel-folder.c b/camel/camel-folder.c index 6455fbf77b..6188c35866 100644 --- a/camel/camel-folder.c +++ b/camel/camel-folder.c @@ -24,6 +24,7 @@ * USA */ #include <config.h> +#include <string.h> #include "camel-folder.h" #include "camel-exception.h" #include "camel-store.h" diff --git a/camel/camel-folder.h b/camel/camel-folder.h index 713b3ced5c..5cc379be3b 100644 --- a/camel/camel-folder.h +++ b/camel/camel-folder.h @@ -56,11 +56,8 @@ typedef enum { } CamelFolderOpenMode; -typedef void (*CamelFolderAsyncCallback) (); +#warning old summary stuff to be removed! -/* these structs from camel-folder-summary.h ... (remove comment after cleanup soon) */ -/* TODO: perhaps they should be full-block objects? */ -/* FIXME: rename this to something more suitable */ typedef struct { gchar *name; gint nb_message; /* ick, these should be renamed to something better */ @@ -82,10 +79,10 @@ typedef struct _CamelMessageContentInfo { char *encoding; guint32 size; + } CamelMessageContentInfo; -/* TODO: rename this?? */ -/* TODO: Make this an object, maybe? */ +/* information about a given object */ typedef struct { /* public fields */ gchar *subject; @@ -98,11 +95,13 @@ typedef struct { time_t date_sent; time_t date_received; - /* tree of content description */ + /* tree of content description - NULL if it is not available */ CamelMessageContentInfo *content; } CamelMessageInfo; +typedef void (*CamelFolderAsyncCallback) (); + struct _CamelFolder { GtkObject parent_object; diff --git a/camel/camel-mime-filter-index.c b/camel/camel-mime-filter-index.c index 24c223d4e5..53a9f72920 100644 --- a/camel/camel-mime-filter-index.c +++ b/camel/camel-mime-filter-index.c @@ -163,4 +163,16 @@ void camel_mime_filter_index_set_name (CamelMimeFilterIndex *mf, char *name) mf->name = g_strdup(name); } +void camel_mime_filter_index_set_ibex (CamelMimeFilterIndex *mf, ibex *index) +{ + if (mf->index) { + char *out; + size_t outlen, outspace; + + camel_mime_filter_complete((CamelMimeFilter *)mf, "", 0, 0, &out, &outlen, &outspace); + } + mf->index = index; +} + + diff --git a/camel/camel-mime-filter-index.h b/camel/camel-mime-filter-index.h index b4ea1966f0..f480fc55c7 100644 --- a/camel/camel-mime-filter-index.h +++ b/camel/camel-mime-filter-index.h @@ -53,5 +53,6 @@ CamelMimeFilterIndex *camel_mime_filter_index_new_ibex (ibex *); /* Set the match name for any indexed words */ void camel_mime_filter_index_set_name (CamelMimeFilterIndex *, char *); +void camel_mime_filter_index_set_ibex (CamelMimeFilterIndex *mf, ibex *index); #endif /* ! _CAMEL_MIME_FILTER_INDEX_H */ diff --git a/camel/camel-mime-parser.c b/camel/camel-mime-parser.c index 5417fffa3a..cf81eed25b 100644 --- a/camel/camel-mime-parser.c +++ b/camel/camel-mime-parser.c @@ -118,6 +118,7 @@ struct _header_scan_filter { }; static void folder_scan_step(struct _header_scan_state *s, char **databuffer, int *datalength); +static void folder_scan_drop_step(struct _header_scan_state *s); static int folder_scan_init_with_fd(struct _header_scan_state *s, int fd); static int folder_scan_init_with_stream(struct _header_scan_state *s, CamelStream *stream); static struct _header_scan_state *folder_scan_init(void); @@ -226,6 +227,22 @@ camel_mime_parser_new (void) } +/** + * camel_mime_parser_filter_add: + * @m: + * @mf: + * + * Add a filter that will be applied to any body content before it is passed + * to the caller. Filters may be pipelined to perform multi-pass operations + * on the content, and are applied in the order they were added. + * + * Note that filters are only applied to the body content of messages, and once + * a filter has been set, all content returned by a filter_step() with a state + * of HSCAN_BODY will have passed through the filter. + * + * Return value: An id that may be passed to filter_remove() to remove + * the filter, or -1 if the operation failed. + **/ int camel_mime_parser_filter_add(CamelMimeParser *m, CamelMimeFilter *mf) { @@ -248,6 +265,14 @@ camel_mime_parser_filter_add(CamelMimeParser *m, CamelMimeFilter *mf) return new->id; } +/** + * camel_mime_parser_filter_remove: + * @m: + * @id: + * + * Remove a processing filter from the pipeline. There is no + * restriction on the order the filters can be removed. + **/ void camel_mime_parser_filter_remove(CamelMimeParser *m, int id) { @@ -268,6 +293,18 @@ camel_mime_parser_filter_remove(CamelMimeParser *m, int id) } } +/** + * camel_mime_parser_header: + * @m: + * @name: Name of header. + * @offset: Pointer that can receive the offset of the header in + * the stream from the start of parsing. + * + * Lookup a header by name. + * + * Return value: The header value, or NULL if the header is not + * defined. + **/ const char * camel_mime_parser_header(CamelMimeParser *m, const char *name, int *offset) { @@ -280,6 +317,17 @@ camel_mime_parser_header(CamelMimeParser *m, const char *name, int *offset) return NULL; } +/** + * camel_mime_parser_headers_raw: + * @m: + * + * Get the list of the raw headers which are defined for the + * current state of the parser. These headers are valid + * until the next call to parser_step(), or parser_drop_step(). + * + * Return value: The raw headers, or NULL if there are no headers + * defined for the current part or state. + **/ struct _header_raw * camel_mime_parser_headers_raw(CamelMimeParser *m) { @@ -290,6 +338,21 @@ camel_mime_parser_headers_raw(CamelMimeParser *m) return NULL; } +/** + * camel_mime_parser_init_with_fd: + * @m: + * @fd: A valid file descriptor. + * + * Initialise the scanner with an fd. The scanner's offsets + * will be relative to the current file position of the file + * descriptor. As a result, seekable descritors should + * be seeked using the parser seek functions. + * + * An initial buffer will be read from the file descriptor + * immediately, although no parsing will occur. + * + * Return value: Returns -1 on error. + **/ int camel_mime_parser_init_with_fd(CamelMimeParser *m, int fd) { @@ -298,6 +361,21 @@ camel_mime_parser_init_with_fd(CamelMimeParser *m, int fd) return folder_scan_init_with_fd(s, fd); } +/** + * camel_mime_parser_init_with_stream: + * @m: + * @stream: + * + * Initialise the scanner with a source stream. The scanner's + * offsets will be relative to the current file position of + * the stream. As a result, seekable streams should only + * be seeked using the parser seek function. + * + * An initial buffer will be read from the stream + * immediately, although no parsing will occur. + * + * Return value: -1 on error. + **/ int camel_mime_parser_init_with_stream(CamelMimeParser *m, CamelStream *stream) { @@ -306,6 +384,17 @@ camel_mime_parser_init_with_stream(CamelMimeParser *m, CamelStream *stream) return folder_scan_init_with_stream(s, stream); } +/** + * camel_mime_parser_scan_from: + * @m: + * @scan_from: #TRUE if the scanner should scan From lines. + * + * Tell the scanner if it should scan "^From " lines or not. + * + * If the scanner is scanning from lines, two additional + * states HSCAN_FROM and HSCAN_FROM_END will be returned + * to the caller during parsing. + **/ void camel_mime_parser_scan_from(CamelMimeParser *m, int scan_from) { @@ -313,6 +402,16 @@ camel_mime_parser_scan_from(CamelMimeParser *m, int scan_from) s->scan_from = scan_from; } +/** + * camel_mime_parser_content_type: + * @m: + * + * Get the content type defined in the current part. + * + * Return value: A content_type structure, or NULL if there + * is no content-type defined for this part of state of the + * parser. + **/ struct _header_content_type * camel_mime_parser_content_type(CamelMimeParser *m) { @@ -325,6 +424,17 @@ camel_mime_parser_content_type(CamelMimeParser *m) return NULL; } +/** + * camel_mime_parser_unstep: + * @m: + * + * Cause the last step operation to repeat itself. If this is + * called repeated times, then the same step will be repeated + * that many times. + * + * Note that it is not possible to scan back using this function, + * only to have a way of peeking the next state. + **/ void camel_mime_parser_unstep(CamelMimeParser *m) { struct _header_scan_state *s = _PRIVATE(m); @@ -332,6 +442,49 @@ void camel_mime_parser_unstep(CamelMimeParser *m) s->unstep++; } +/** + * camel_mime_parser_drop_step: + * @m: + * + * Drop the last step call. This should only be used + * in conjunction with seeking of the stream as the + * stream may be in an undefined state relative to the + * state of the parser. + * + * Use this call with care. + **/ +void camel_mime_parser_drop_step(CamelMimeParser *m) +{ + struct _header_scan_state *s = _PRIVATE(m); + + s->unstep = 0; + folder_scan_drop_step(s); +} + +/** + * camel_mime_parser_step: + * @m: + * @databuffer: Pointer to accept a pointer to the data + * associated with this step (if any). + * @datalength: Pointer to accept a pointer to the data + * length associated with this step (if any). + * + * Parse the next part of the MIME message. If _unstep() + * has been called, then continue to return the same state + * for that many calls. + * + * If the step is HSCAN_BODY then the databuffer and datalength + * pointers will be setup to point to the internal data buffer + * of the scanner and may be processed as required. Any + * filters will have already been applied to this data. + * + * Refer to the state diagram elsewhere for a full listing of + * the states an application is gauranteed to get from the + * scanner. + * + * Return value: The current new state of the parser + * is returned. + **/ enum _header_state camel_mime_parser_step(CamelMimeParser *m, char **databuffer, int *datalength) { @@ -349,6 +502,26 @@ camel_mime_parser_step(CamelMimeParser *m, char **databuffer, int *datalength) return s->state; } +/** + * camel_mime_parser_tell: + * @m: + * + * Return the current scanning offset. The meaning of this + * value will depend on the current state of the parser. + * + * An incomplete listing of the states: + * + * HSCAN_INITIAL, The start of the current message. + * HSCAN_HEADER, HSCAN_MESSAGE, HSCAN_MULTIPART, the character + * position immediately after the end of the header. + * HSCAN_BODY, Position within the message of the start + * of the current data block. + * HSCAN_*_END, The position of the character starting + * the next section of the scan (the last position + 1 of + * the respective current state). + * + * Return value: See above. + **/ off_t camel_mime_parser_tell(CamelMimeParser *m) { struct _header_scan_state *s = _PRIVATE(m); @@ -356,6 +529,17 @@ off_t camel_mime_parser_tell(CamelMimeParser *m) return folder_tell(s); } +/** + * camel_mime_parser_tell_start_headers: + * @m: + * + * Find out the position within the file of where the + * headers started, this is cached by the parser + * at the time. + * + * Return value: The header start position, or -1 if + * no headers were scanned in the current state. + **/ off_t camel_mime_parser_tell_start_headers(CamelMimeParser *m) { struct _header_scan_state *s = _PRIVATE(m); @@ -363,6 +547,16 @@ off_t camel_mime_parser_tell_start_headers(CamelMimeParser *m) return s->start_of_headers; } +/** + * camel_mime_parser_tell_start_from: + * @m: + * + * If the parser is scanning From lines, then this returns + * the position of the start of the From line. + * + * Return value: The start of the from line, or -1 if there + * was no From line, or From lines are not being scanned. + **/ off_t camel_mime_parser_tell_start_from(CamelMimeParser *m) { struct _header_scan_state *s = _PRIVATE(m); @@ -370,24 +564,76 @@ off_t camel_mime_parser_tell_start_from(CamelMimeParser *m) return s->start_of_from; } +/** + * camel_mime_parser_seek: + * @m: + * @off: Number of bytes to offset the seek by. + * @whence: SEEK_SET, SEEK_CUR, SEEK_END + * + * Reset the source position to a known value. + * + * Note that if the source stream/descriptor was not + * positioned at 0 to begin with, and an absolute seek + * is specified (whence != SEEK_CUR), then the seek + * position may not match the desired seek position. + * + * Return value: The new seek offset, or -1 on + * an error (for example, trying to seek on a non-seekable + * stream or file descriptor). + **/ off_t camel_mime_parser_seek(CamelMimeParser *m, off_t off, int whence) { struct _header_scan_state *s = _PRIVATE(m); return folder_seek(s, off, whence); } +/** + * camel_mime_parser_state: + * @m: + * + * Get the current parser state. + * + * Return value: The current parser state. + **/ enum _header_state camel_mime_parser_state(CamelMimeParser *m) { struct _header_scan_state *s = _PRIVATE(m); return s->state; } +/** + * camel_mime_parser_stream: + * @m: + * + * Get the stream, if any, the parser has been initialised + * with. May be used to setup sub-streams, but should not + * be read from directly (without saving and restoring + * the seek position in between). + * + * Return value: The stream from _init_with_stream(), or NULL + * if the parser is reading from a file descriptor or is + * uninitialised. + **/ CamelStream *camel_mime_parser_stream(CamelMimeParser *m) { struct _header_scan_state *s = _PRIVATE(m); return s->stream; } +/** + * camel_mime_parser_fd: + * @m: + * + * Return the file descriptor, if any, the parser has been + * initialised with. + * + * Should not be read from unless the parser it to terminate, + * or the seek offset can be reset before the next parse + * step. + * + * Return value: The file descriptor or -1 if the parser + * is reading from a stream or has not been initialised. + **/ int camel_mime_parser_fd(CamelMimeParser *m) { struct _header_scan_state *s = _PRIVATE(m); @@ -1022,7 +1268,7 @@ tail_recurse: type = HSCAN_HEADER; if ( (content = header_raw_find(&h->headers, "Content-Type", NULL)) && (ct = header_content_type_decode(content))) { - if (!g_strcasecmp(ct->type, "multipart")) { + if (!strcasecmp(ct->type, "multipart")) { bound = header_content_type_param(ct, "boundary"); if (bound) { d(printf("multipart, boundary = %s\n", bound)); @@ -1037,9 +1283,9 @@ tail_recurse: /* header_raw_replace(&h->headers, "Content-Type", "text/plain", offset);*/ g_warning("Multipart with no boundary, treating as text/plain"); } - } else if (!g_strcasecmp(ct->type, "message")) { - if (!g_strcasecmp(ct->subtype, "rfc822") - /*|| !g_strcasecmp(ct->subtype, "partial")*/) { + } else if (!strcasecmp(ct->type, "message")) { + if (!strcasecmp(ct->subtype, "rfc822") + /*|| !strcasecmp(ct->subtype, "partial")*/) { type = HSCAN_MESSAGE; } } @@ -1138,6 +1384,38 @@ tail_recurse: return; } +/* drops the current state back one */ +static void +folder_scan_drop_step(struct _header_scan_state *s) +{ + switch (s->state) { + case HSCAN_INITIAL: + case HSCAN_EOF: + return; + + case HSCAN_FROM: + s->state = HSCAN_INITIAL; + folder_pull_part(s); + return; + + case HSCAN_MESSAGE: + case HSCAN_HEADER: + case HSCAN_MULTIPART: + + case HSCAN_FROM_END: + case HSCAN_BODY_END: + case HSCAN_MULTIPART_END: + case HSCAN_MESSAGE_END: + + s->state = s->parts->savestate; + folder_pull_part(s); + if (s->state & HSCAN_END) { + s->state &= ~HSCAN_END; + } + return; + } +} + #ifdef STANDALONE int main(int argc, char **argv) { @@ -1182,7 +1460,7 @@ int main(int argc, char **argv) case HSCAN_HEADER: if (s->parts->content_type && (charset = header_content_type_param(s->parts->content_type, "charset"))) { - if (g_strcasecmp(charset, "us-ascii")) { + if (strcasecmp(charset, "us-ascii")) { folder_push_filter_charset(s, "UTF-8", charset); } else { charset = NULL; diff --git a/camel/camel-mime-parser.h b/camel/camel-mime-parser.h index f8964ed86c..cbc7b6095e 100644 --- a/camel/camel-mime-parser.h +++ b/camel/camel-mime-parser.h @@ -89,6 +89,7 @@ void camel_mime_parser_scan_from(CamelMimeParser *, int); /* normal interface */ enum _header_state camel_mime_parser_step(CamelMimeParser *, char **, int *); void camel_mime_parser_unstep(CamelMimeParser *); +void camel_mime_parser_drop_step(CamelMimeParser *m); enum _header_state camel_mime_parser_state(CamelMimeParser *); /* get content type for the current part/header */ diff --git a/camel/camel-mime-part-utils.c b/camel/camel-mime-part-utils.c index ac2c81ad0a..fa914be960 100644 --- a/camel/camel-mime-part-utils.c +++ b/camel/camel-mime-part-utils.c @@ -71,11 +71,11 @@ simple_data_wrapper_construct_from_parser(CamelDataWrapper *dw, CamelMimeParser /* first, work out conversion, if any, required, we dont care about what we dont know about */ encoding = header_content_encoding_decode(camel_mime_parser_header(mp, "content-transfer-encoding", NULL)); if (encoding) { - if (!g_strcasecmp(encoding, "base64")) { + if (!strcasecmp(encoding, "base64")) { d(printf("Adding base64 decoder ...\n")); fdec = (CamelMimeFilter *)camel_mime_filter_basic_new_type(CAMEL_MIME_FILTER_BASIC_BASE64_DEC); decid = camel_mime_parser_filter_add(mp, fdec); - } else if (!g_strcasecmp(encoding, "quoted-printable")) { + } else if (!strcasecmp(encoding, "quoted-printable")) { d(printf("Adding quoted-printable decoder ...\n")); fdec = (CamelMimeFilter *)camel_mime_filter_basic_new_type(CAMEL_MIME_FILTER_BASIC_QP_DEC); decid = camel_mime_parser_filter_add(mp, fdec); @@ -88,8 +88,8 @@ simple_data_wrapper_construct_from_parser(CamelDataWrapper *dw, CamelMimeParser if (header_content_type_is(ct, "text", "*")) { const char *charset = header_content_type_param(ct, "charset"); if (charset!=NULL - && !(g_strcasecmp(charset, "us-ascii")==0 - || g_strcasecmp(charset, "utf-8")==0)) { + && !(strcasecmp(charset, "us-ascii")==0 + || strcasecmp(charset, "utf-8")==0)) { d(printf("Adding conversion filter from %s to utf-8\n", charset)); fch = (CamelMimeFilter *)camel_mime_filter_charset_new_convert(charset, "utf-8"); if (fch) { diff --git a/camel/camel-mime-part.c b/camel/camel-mime-part.c index 744f2a7247..bb3abe7062 100644 --- a/camel/camel-mime-part.c +++ b/camel/camel-mime-part.c @@ -606,13 +606,13 @@ camel_mime_part_encoding_from_string (const gchar *string) { if (string == NULL) return CAMEL_MIME_PART_ENCODING_DEFAULT; - else if (g_strcasecmp (string, "7bit") == 0) + else if (strcasecmp (string, "7bit") == 0) return CAMEL_MIME_PART_ENCODING_7BIT; - else if (g_strcasecmp (string, "8bit") == 0) + else if (strcasecmp (string, "8bit") == 0) return CAMEL_MIME_PART_ENCODING_8BIT; - else if (g_strcasecmp (string, "base64") == 0) + else if (strcasecmp (string, "base64") == 0) return CAMEL_MIME_PART_ENCODING_BASE64; - else if (g_strcasecmp (string, "quoted-printable") == 0) + else if (strcasecmp (string, "quoted-printable") == 0) return CAMEL_MIME_PART_ENCODING_QUOTEDPRINTABLE; else /* FIXME? Spit a warning? */ diff --git a/camel/camel-mime-utils.c b/camel/camel-mime-utils.c index f53531feb7..3f4c4d99d3 100644 --- a/camel/camel-mime-utils.c +++ b/camel/camel-mime-utils.c @@ -808,7 +808,7 @@ char *rfc2047_encode_word(const char *in, int len, char *type) out = buffer; /* if we can't convert from utf-8, just encode as utf-8 */ - if (!g_strcasecmp(type, "UTF-8") + if (!strcasecmp(type, "UTF-8") || (ic = unicode_iconv_open(type, "UTF-8")) == (unicode_iconv_t)-1) { memcpy(buffer, in, len); out = buffer+len; @@ -1065,7 +1065,7 @@ header_decode_param(const char **in, char **paramp, char **valuep) char * header_param(struct _header_param *p, const char *name) { - while (p && g_strcasecmp(p->name, name) != 0) + while (p && strcasecmp(p->name, name) != 0) p = p->next; if (p) return p->value; @@ -1079,7 +1079,7 @@ header_set_param(struct _header_param **l, const char *name, const char *value) while (p->next) { pn = p->next; - if (!g_strcasecmp(pn->name, name)) { + if (!strcasecmp(pn->name, name)) { g_free(pn->value); if (value) { pn->value = g_strdup(value); @@ -1135,16 +1135,16 @@ header_content_type_is(struct _header_content_type *ct, const char *type, const { /* no type == text/plain or text/"*" */ if (ct==NULL) { - return (!g_strcasecmp(type, "text") - && (!g_strcasecmp(subtype, "plain") - || !g_strcasecmp(subtype, "*"))); + return (!strcasecmp(type, "text") + && (!strcasecmp(subtype, "plain") + || !strcasecmp(subtype, "*"))); } return (ct->type != NULL - && (!g_strcasecmp(ct->type, type) + && (!strcasecmp(ct->type, type) && ((ct->subtype != NULL - && !g_strcasecmp(ct->subtype, subtype)) - || !g_strcasecmp("*", subtype)))); + && !strcasecmp(ct->subtype, subtype)) + || !strcasecmp("*", subtype)))); } void @@ -1660,7 +1660,7 @@ header_content_type_decode(const char *in) inptr++; subtype = decode_token(&inptr); } - if (subtype == NULL && (!g_strcasecmp(type, "text"))) { + if (subtype == NULL && (!strcasecmp(type, "text"))) { g_warning("text type with no subtype, resorting to text/plain: %s", in); subtype = g_strdup("plain"); } @@ -1713,7 +1713,7 @@ header_content_type_format(struct _header_content_type *ct) g_warning("Content-Type with no main type"); } else if (ct->subtype == NULL) { g_warning("Content-Type with no sub type: %s", ct->type); - if (!g_strcasecmp(ct->type, "multipart")) + if (!strcasecmp(ct->type, "multipart")) g_string_sprintfa(out, "%s/mixed", ct->type); else g_string_sprintfa(out, "%s", ct->type); @@ -1879,7 +1879,7 @@ header_decode_date(const char *in, int *saveoffset) monthname = decode_token(&inptr); if (monthname) { for (i=0;i<sizeof(tz_months)/sizeof(tz_months[0]);i++) { - if (!g_strcasecmp(tz_months[i], monthname)) { + if (!strcasecmp(tz_months[i], monthname)) { tm.tm_mon = i; break; } @@ -1916,7 +1916,7 @@ header_decode_date(const char *in, int *saveoffset) if (tz) { for (i=0;i<sizeof(tz_offsets)/sizeof(tz_offsets[0]);i++) { - if (!g_strcasecmp(tz_offsets[i].name, tz)) { + if (!strcasecmp(tz_offsets[i].name, tz)) { offset = tz_offsets[i].offset; break; } @@ -2031,13 +2031,13 @@ header_raw_append(struct _header_raw **list, const char *name, const char *value /* debug */ #if 0 - if (!g_strcasecmp(name, "To")) { + if (!strcasecmp(name, "To")) { printf("- Decoding To\n"); header_to_decode(value); - } else if (!g_strcasecmp(name, "Content-type")) { + } else if (!strcasecmp(name, "Content-type")) { printf("- Decoding content-type\n"); header_content_type_dump(header_content_type_decode(value)); - } else if (!g_strcasecmp(name, "MIME-Version")) { + } else if (!strcasecmp(name, "MIME-Version")) { printf("- Decoding mime version\n"); header_mime_decode(value); } @@ -2051,7 +2051,7 @@ header_raw_find_node(struct _header_raw **list, const char *name) l = *list; while (l) { - if (!g_strcasecmp(l->name, name)) + if (!strcasecmp(l->name, name)) break; l = l->next; } @@ -2103,7 +2103,7 @@ header_raw_remove(struct _header_raw **list, const char *name) p = (struct _header_raw *)list; l = *list; while (l) { - if (!g_strcasecmp(l->name, name)) { + if (!strcasecmp(l->name, name)) { p->next = l->next; header_raw_free(l); l = p->next; diff --git a/camel/providers/mbox/camel-mbox-summary.c b/camel/providers/mbox/camel-mbox-summary.c index 01deaef19a..35b7945e49 100644 --- a/camel/providers/mbox/camel-mbox-summary.c +++ b/camel/providers/mbox/camel-mbox-summary.c @@ -595,7 +595,7 @@ header_write(int fd, struct _header_raw *header, unsigned int uid, unsigned int iv[3].iov_len = 1; while (header) { - if (g_strcasecmp(header->name, "x-evolution")) { + if (strcasecmp(header->name, "x-evolution")) { int len; iv[0].iov_base = header->name; @@ -876,12 +876,12 @@ static int index_folder(CamelMboxSummary *s, int startoffset) messages/message parts */ encoding = header_content_encoding_decode(camel_mime_parser_header(mp, "content-transfer-encoding", NULL)); if (encoding) { - if (!g_strcasecmp(encoding, "base64")) { + if (!strcasecmp(encoding, "base64")) { d(printf("Adding decoding filter for base64\n")); if (mf64 == NULL) mf64 = camel_mime_filter_basic_new_type(CAMEL_MIME_FILTER_BASIC_BASE64_DEC); enc_id = camel_mime_parser_filter_add(mp, (CamelMimeFilter *)mf64); - } else if (!g_strcasecmp(encoding, "quoted-printable")) { + } else if (!strcasecmp(encoding, "quoted-printable")) { d(printf("Adding decoding filter for quoted-printable\n")); if (mfqp == NULL) mfqp = camel_mime_filter_basic_new_type(CAMEL_MIME_FILTER_BASIC_QP_DEC); @@ -892,8 +892,8 @@ static int index_folder(CamelMboxSummary *s, int startoffset) charset = header_content_type_param(ct, "charset"); if (charset!=NULL - && !(g_strcasecmp(charset, "us-ascii")==0 - || g_strcasecmp(charset, "utf-8")==0)) { + && !(strcasecmp(charset, "us-ascii")==0 + || strcasecmp(charset, "utf-8")==0)) { d(printf("Adding conversion filter from %s to utf-8\n", charset)); if (mfc == NULL) mfc = camel_mime_filter_charset_new_convert(charset, "utf-8"); |