* providers/imap/camel-imap-summary.c: Simple subclass of CamelFolderSummary that also keeps a UIDVALIDITY value (and doesn't, for the moment, build content info). * providers/imap/camel-imap-folder.c: (various): Use a CamelImapSummary to store/fetch summary info. (camel_imap_folder_new): Take a path to a file to use for the summary. Set the folder's permanent_flags correctly according to the server response. Read in the summary (checking the UIDVALIDITY) and update it if it's out of date. (imap_refresh_info): Just fetch UIDs and flags. If the UIDs all match, update the flags as needed and be done with it. Otherwise, delete messages that have been expunged from the server and fetch full summary info for any new messages. (imap_sync): Save the summary to disk. (imap_update_summary): Renamed from imap_get_summary_internal. Can now be told to get summary for only a subset of messages. Use camel-mime-utils functions rather than rolling our own header parsing. (imap_get_message_info_internal): Merged into imap_update_summary. (imap_set_message_flags): Don't marked the message FOLDER_FLAGGED if we're not actually changing the value of any of the flags. (camel_imap_folder_changed): Deal with EXISTS rather than RECENT. * providers/imap/camel-imap-store.c (imap_connect): Call camel_session_get_storage_path and save the value. (get_folder): Create a local directory to store summary information and pass a summary file name to camel_imap_folder_new. Don't call camel_folder_refresh_info from here any more since camel_imap_folder_new does it again. * providers/imap/camel-imap-command.c (camel_imap_command): Add a special case to this to make it possible to get the repsonses from a SELECT and still have store->current_folder be updated correctly. (imap_read_response): parse EXISTS rather than RECENT * camel-session.c (camel_session_get_storage_path): Use e_mkdir_hier. * camel-folder-summary.c (camel_folder_summary_remove_index): New function. * camel-mime-utils.c (header_raw_append_parse): fix this. (camel-mime-parser.c doesn't use this code because of the MEMPOOL optimization, so nothing was ever actually calling it before.) svn path=/trunk/; revision=5891
1683 lines
41 KiB
C
1683 lines
41 KiB
C
/*
|
|
* 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 <errno.h>
|
|
#include <stdlib.h>
|
|
|
|
#include "camel-folder-summary.h"
|
|
|
|
#include <camel/camel-mime-message.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 <camel/camel-mime-message.h>
|
|
#include "hash-table-utils.h"
|
|
|
|
/* this should probably be conditional on it existing */
|
|
#define USE_BSEARCH
|
|
|
|
#define d(x)
|
|
#define io(x) /* io debug */
|
|
|
|
#if 0
|
|
extern int strdup_count, malloc_count, free_count;
|
|
#endif
|
|
|
|
#define CAMEL_FOLDER_SUMMARY_VERSION (8)
|
|
|
|
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_finalize (CamelObject *obj);
|
|
|
|
static CamelObjectClass *camel_folder_summary_parent;
|
|
|
|
static void
|
|
camel_folder_summary_class_init (CamelFolderSummaryClass *klass)
|
|
{
|
|
camel_folder_summary_parent = camel_type_get_global_classfuncs (camel_object_get_type ());
|
|
|
|
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;
|
|
}
|
|
|
|
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 free_o_name(void *key, void *value, void *data)
|
|
{
|
|
camel_object_unref((CamelObject *)value);
|
|
g_free(key);
|
|
}
|
|
|
|
static void
|
|
camel_folder_summary_finalize (CamelObject *obj)
|
|
{
|
|
struct _CamelFolderSummaryPrivate *p;
|
|
CamelFolderSummary *s = (CamelFolderSummary *)obj;
|
|
|
|
p = _PRIVATE(obj);
|
|
|
|
camel_folder_summary_clear(s);
|
|
g_ptr_array_free(s->messages, TRUE);
|
|
g_hash_table_destroy(s->messages_uid);
|
|
|
|
g_hash_table_foreach(p->filter_charset, free_o_name, 0);
|
|
g_hash_table_destroy(p->filter_charset);
|
|
|
|
g_free(s->summary_path);
|
|
|
|
if (p->filter_index)
|
|
camel_object_unref ((CamelObject *)p->filter_index);
|
|
if (p->filter_64)
|
|
camel_object_unref ((CamelObject *)p->filter_64);
|
|
if (p->filter_qp)
|
|
camel_object_unref ((CamelObject *)p->filter_qp);
|
|
if (p->filter_save)
|
|
camel_object_unref ((CamelObject *)p->filter_save);
|
|
|
|
g_free(p);
|
|
}
|
|
|
|
CamelType
|
|
camel_folder_summary_get_type (void)
|
|
{
|
|
static CamelType type = CAMEL_INVALID_TYPE;
|
|
|
|
if (type == CAMEL_INVALID_TYPE) {
|
|
type = camel_type_register (camel_object_get_type (), "CamelFolderSummary",
|
|
sizeof (CamelFolderSummary),
|
|
sizeof (CamelFolderSummaryClass),
|
|
(CamelObjectClassInitFunc) camel_folder_summary_class_init,
|
|
NULL,
|
|
(CamelObjectInitFunc) camel_folder_summary_init,
|
|
(CamelObjectFinalizeFunc) camel_folder_summary_finalize);
|
|
}
|
|
|
|
return type;
|
|
}
|
|
|
|
/**
|
|
* 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 ( camel_object_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);
|
|
|
|
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);
|
|
}
|
|
|
|
guint32 camel_folder_summary_next_uid(CamelFolderSummary *s)
|
|
{
|
|
guint32 uid = s->nextuid++;
|
|
|
|
/* FIXME: sync this to disk */
|
|
/* summary_header_save(s);*/
|
|
return uid;
|
|
}
|
|
|
|
void camel_folder_summary_set_uid(CamelFolderSummary *s, guint32 uid)
|
|
{
|
|
s->nextuid = MAX(s->nextuid, uid);
|
|
}
|
|
|
|
char *
|
|
camel_folder_summary_next_uid_string(CamelFolderSummary *s)
|
|
{
|
|
return g_strdup_printf("%u", camel_folder_summary_next_uid(s));
|
|
}
|
|
|
|
/* loads the content descriptions, recursively */
|
|
static CamelMessageContentInfo *
|
|
perform_content_info_load(CamelFolderSummary *s, FILE *in)
|
|
{
|
|
int i;
|
|
guint32 count;
|
|
CamelMessageContentInfo *ci, *part;
|
|
|
|
ci = ((CamelFolderSummaryClass *)(CAMEL_OBJECT_GET_CLASS(s)))->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 *)part);
|
|
part->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 *)(CAMEL_OBJECT_GET_CLASS(s)))->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 *)(CAMEL_OBJECT_GET_CLASS(s)))->message_info_load(s, in);
|
|
|
|
if (s->build_content) {
|
|
mi->content = perform_content_info_load(s, in);
|
|
}
|
|
|
|
camel_folder_summary_add(s, mi);
|
|
}
|
|
|
|
if (fclose(in) == -1)
|
|
return -1;
|
|
|
|
s->flags &= ~CAMEL_SUMMARY_DIRTY;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* saves the content descriptions, recursively */
|
|
static int
|
|
perform_content_info_save(CamelFolderSummary *s, FILE *out, CamelMessageContentInfo *ci)
|
|
{
|
|
CamelMessageContentInfo *part;
|
|
|
|
((CamelFolderSummaryClass *)(CAMEL_OBJECT_GET_CLASS(s)))->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);
|
|
|
|
if ((s->flags & CAMEL_SUMMARY_DIRTY) == 0)
|
|
return 0;
|
|
|
|
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;
|
|
}
|
|
|
|
io(printf("saving header\n"));
|
|
|
|
if ( ((CamelFolderSummaryClass *)(CAMEL_OBJECT_GET_CLASS(s)))->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 *)(CAMEL_OBJECT_GET_CLASS(s)))->message_info_save(s, out, mi);
|
|
|
|
if (s->build_content) {
|
|
perform_content_info_save(s, out, mi->content);
|
|
}
|
|
}
|
|
if (fclose(out) == -1)
|
|
return -1;
|
|
|
|
s->flags &= ~CAMEL_SUMMARY_DIRTY;
|
|
return 0;
|
|
}
|
|
|
|
void camel_folder_summary_add(CamelFolderSummary *s, CamelMessageInfo *info)
|
|
{
|
|
if (info == NULL)
|
|
return;
|
|
retry:
|
|
if (info->uid == NULL) {
|
|
info->uid = camel_folder_summary_next_uid_string(s);
|
|
}
|
|
if (g_hash_table_lookup(s->messages_uid, info->uid)) {
|
|
g_warning("Trying to insert message with clashing uid (%s). new uid re-assigned", info->uid);
|
|
g_free(info->uid);
|
|
info->uid = NULL;
|
|
info->flags |= CAMEL_MESSAGE_FOLDER_FLAGGED;
|
|
goto retry;
|
|
}
|
|
|
|
g_ptr_array_add(s->messages, info);
|
|
g_hash_table_insert(s->messages_uid, info->uid, info);
|
|
s->flags |= CAMEL_SUMMARY_DIRTY;
|
|
}
|
|
|
|
CamelMessageInfo *camel_folder_summary_add_from_header(CamelFolderSummary *s, struct _header_raw *h)
|
|
{
|
|
CamelMessageInfo *info = NULL;
|
|
|
|
info = ((CamelFolderSummaryClass *)(CAMEL_OBJECT_GET_CLASS(s))) -> message_info_new(s, h);
|
|
camel_folder_summary_add(s, info);
|
|
|
|
return info;
|
|
}
|
|
|
|
CamelMessageInfo *camel_folder_summary_add_from_parser(CamelFolderSummary *s, CamelMimeParser *mp)
|
|
{
|
|
CamelMessageInfo *info = NULL;
|
|
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 *)(CAMEL_OBJECT_GET_CLASS(s)))->message_info_new_from_parser(s, mp);
|
|
|
|
camel_mime_parser_unstep(mp);
|
|
|
|
camel_folder_summary_add(s, info);
|
|
|
|
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);
|
|
}
|
|
}
|
|
return info;
|
|
}
|
|
|
|
static void
|
|
perform_content_info_free(CamelFolderSummary *s, CamelMessageContentInfo *ci)
|
|
{
|
|
CamelMessageContentInfo *pw, *pn;
|
|
|
|
pw = ci->childs;
|
|
((CamelFolderSummaryClass *)(CAMEL_OBJECT_GET_CLASS(s)))->content_info_free(s, ci);
|
|
while (pw) {
|
|
pn = pw->next;
|
|
perform_content_info_free(s, pw);
|
|
pw = pn;
|
|
}
|
|
}
|
|
|
|
void
|
|
camel_folder_summary_touch(CamelFolderSummary *s)
|
|
{
|
|
s->flags |= CAMEL_SUMMARY_DIRTY;
|
|
}
|
|
|
|
void
|
|
camel_folder_summary_clear(CamelFolderSummary *s)
|
|
{
|
|
int i;
|
|
|
|
if (camel_folder_summary_count(s) == 0)
|
|
return;
|
|
|
|
for (i=0;i<camel_folder_summary_count(s);i++) {
|
|
CamelMessageInfo *mi = camel_folder_summary_index(s, i);
|
|
CamelMessageContentInfo *ci = mi->content;
|
|
|
|
((CamelFolderSummaryClass *)(CAMEL_OBJECT_GET_CLASS(s)))->message_info_free(s, mi);
|
|
if (s->build_content && ci) {
|
|
perform_content_info_free(s, ci);
|
|
}
|
|
}
|
|
|
|
g_ptr_array_set_size(s->messages, 0);
|
|
g_hash_table_destroy(s->messages_uid);
|
|
s->messages_uid = g_hash_table_new(g_str_hash, g_str_equal);
|
|
s->flags |= CAMEL_SUMMARY_DIRTY;
|
|
}
|
|
|
|
void camel_folder_summary_remove(CamelFolderSummary *s, CamelMessageInfo *info)
|
|
{
|
|
CamelMessageContentInfo *ci = info->content;
|
|
|
|
g_hash_table_remove(s->messages_uid, info->uid);
|
|
g_ptr_array_remove(s->messages, info);
|
|
((CamelFolderSummaryClass *)(CAMEL_OBJECT_GET_CLASS(s)))->message_info_free(s, info);
|
|
if (s->build_content && ci) {
|
|
perform_content_info_free(s, ci);
|
|
}
|
|
s->flags |= CAMEL_SUMMARY_DIRTY;
|
|
}
|
|
|
|
void camel_folder_summary_remove_uid(CamelFolderSummary *s, const char *uid)
|
|
{
|
|
CamelMessageInfo *oldinfo;
|
|
char *olduid;
|
|
|
|
if (g_hash_table_lookup_extended(s->messages_uid, uid, (void *)&olduid, (void *)&oldinfo)) {
|
|
camel_folder_summary_remove(s, oldinfo);
|
|
g_free(olduid);
|
|
}
|
|
}
|
|
|
|
void camel_folder_summary_remove_index(CamelFolderSummary *s, int index)
|
|
{
|
|
CamelMessageInfo *oldinfo;
|
|
|
|
oldinfo = camel_folder_summary_index (s, index);
|
|
if (oldinfo)
|
|
camel_folder_summary_remove(s, oldinfo);
|
|
}
|
|
|
|
int
|
|
camel_folder_summary_encode_uint32(FILE *out, guint32 value)
|
|
{
|
|
int i;
|
|
|
|
io(printf("Encoding int %u\n", value));
|
|
|
|
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)
|
|
{
|
|
guint32 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);
|
|
|
|
io(printf("Decoding int %u\n", *dest));
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
camel_folder_summary_encode_fixed_int32(FILE *out, gint32 value)
|
|
{
|
|
guint32 save;
|
|
|
|
save = htonl(value);
|
|
if (fwrite(&save, sizeof(save), 1, out) != 1)
|
|
return -1;
|
|
return 0;
|
|
}
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
int
|
|
camel_folder_summary_encode_time_t(FILE *out, time_t value)
|
|
{
|
|
int i;
|
|
|
|
for (i=sizeof(time_t)-1;i>=0;i--) {
|
|
if (fputc((value >> (i*8)) & 0xff, out) == -1)
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
camel_folder_summary_decode_time_t(FILE *in, time_t *dest)
|
|
{
|
|
time_t save = 0;
|
|
unsigned int v;
|
|
int i = sizeof(time_t) - 1;
|
|
|
|
while ( i>=0 && (v = fgetc(in)) != EOF) {
|
|
save |= v << (i*8);
|
|
i--;
|
|
}
|
|
*dest = save;
|
|
return 0;
|
|
}
|
|
|
|
/* 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",
|
|
"postscript",
|
|
"quoted-printable",
|
|
"related",
|
|
"rfc822",
|
|
"text",
|
|
"us-ascii", /* 25 words */
|
|
};
|
|
|
|
#define tokens_len (sizeof(tokens)/sizeof(tokens[0]))
|
|
|
|
/* baiscally ...
|
|
0 = null
|
|
1-tokens_len == tokens[id-1]
|
|
>=32 string, length = n-32
|
|
*/
|
|
|
|
#ifdef USE_BSEARCH
|
|
static int
|
|
token_search_cmp(char *key, char **index)
|
|
{
|
|
d(printf("comparing '%s' to '%s'\n", key, *index));
|
|
return strcmp(key, *index);
|
|
}
|
|
#endif
|
|
|
|
int
|
|
camel_folder_summary_encode_token(FILE *out, char *str)
|
|
{
|
|
io(printf("Encoding token: '%s'\n", 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];
|
|
char **match;
|
|
|
|
for (i=0;i<len;i++)
|
|
lower[i] = tolower(str[i]);
|
|
lower[i] = 0;
|
|
#ifdef USE_BSEARCH
|
|
match = bsearch(lower, tokens, tokens_len, sizeof(char *), (int (*)(const void *, const void *))token_search_cmp);
|
|
if (match)
|
|
token = match-tokens;
|
|
#else
|
|
for (i=0;i<tokens_len;i++) {
|
|
if (!strcmp(tokens[i], lower)) {
|
|
token = i;
|
|
break;
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
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;
|
|
if (fwrite(str, len, 1, out) != 1)
|
|
return -1;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
camel_folder_summary_decode_token(FILE *in, char **str)
|
|
{
|
|
char *ret;
|
|
guint32 len;
|
|
|
|
io(printf("Decode token ...\n"));
|
|
|
|
if (camel_folder_summary_decode_uint32(in, &len) == -1) {
|
|
g_warning("Could not decode token from file");
|
|
*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;
|
|
}
|
|
|
|
io(printf("Token = '%s'\n", ret));
|
|
|
|
*str = ret;
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
camel_folder_summary_encode_string(FILE *out, char *str)
|
|
{
|
|
register int len;
|
|
|
|
io(printf("Encoding string: '%s'\n", str));
|
|
|
|
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;
|
|
if (fwrite(str, len, 1, out) == 1)
|
|
return 0;
|
|
return -1;
|
|
}
|
|
|
|
|
|
int
|
|
camel_folder_summary_decode_string(FILE *in, char **str)
|
|
{
|
|
gint32 len;
|
|
register char *ret;
|
|
|
|
io(printf("Decode string ...\n", str));
|
|
|
|
if (camel_folder_summary_decode_uint32(in, &len) == -1) {
|
|
*str = NULL;
|
|
return -1;
|
|
}
|
|
|
|
len--;
|
|
if (len < 0) {
|
|
*str = NULL;
|
|
io(printf("String = '%s'\n", *str));
|
|
return -1;
|
|
}
|
|
|
|
ret = g_malloc(len+1);
|
|
if (fread(ret, len, 1, in) != 1) {
|
|
g_free(ret);
|
|
*str = NULL;
|
|
return -1;
|
|
}
|
|
|
|
io(printf("String = '%s'\n", ret));
|
|
|
|
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)
|
|
{
|
|
gint32 version, flags, nextuid, count, utime;
|
|
|
|
fseek(in, 0, SEEK_SET);
|
|
|
|
io(printf("Loading header\n"));
|
|
|
|
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, &utime) == -1
|
|
|| camel_folder_summary_decode_fixed_int32(in, &count) == -1) {
|
|
return -1;
|
|
}
|
|
|
|
s->nextuid = nextuid;
|
|
s->flags = flags;
|
|
s->time = (time_t) utime;
|
|
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);
|
|
|
|
io(printf("Savining header\n"));
|
|
|
|
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 *)(CAMEL_OBJECT_GET_CLASS(s)))->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 *)(CAMEL_OBJECT_GET_CLASS(s)))->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;
|
|
}
|
|
|
|
char *
|
|
camel_folder_summary_format_address(struct _header_raw *h, const char *name)
|
|
{
|
|
struct _header_address *addr;
|
|
const char *text;
|
|
char *ret, *tmp;
|
|
|
|
text = header_raw_find (&h, name, NULL);
|
|
addr = header_address_decode (text);
|
|
if (addr) {
|
|
/* FIXME: perhaps decoding would be best done in header_address_list_format */
|
|
tmp = header_address_list_format (addr);
|
|
ret = header_decode_string (tmp);
|
|
g_free (tmp);
|
|
header_address_list_clear (&addr);
|
|
} else {
|
|
ret = g_strdup (text);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
char *
|
|
camel_folder_summary_format_string(struct _header_raw *h, const char *name)
|
|
{
|
|
const char *text;
|
|
|
|
text = header_raw_find(&h, name, NULL);
|
|
if (text) {
|
|
while (isspace(*text))
|
|
text++;
|
|
return header_decode_string(text);
|
|
} else {
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
static CamelMessageInfo *
|
|
message_info_new(CamelFolderSummary *s, struct _header_raw *h)
|
|
{
|
|
CamelMessageInfo *mi;
|
|
const char *received;
|
|
|
|
mi = g_malloc0(s->message_info_size);
|
|
|
|
mi->subject = camel_folder_summary_format_string(h, "subject");
|
|
mi->from = camel_folder_summary_format_address(h, "from");
|
|
mi->to = camel_folder_summary_format_address(h, "to");
|
|
mi->cc = camel_folder_summary_format_address(h, "cc");
|
|
mi->user_flags = NULL;
|
|
mi->user_tags = NULL;
|
|
mi->date_sent = header_decode_date(header_raw_find(&h, "date", NULL), NULL);
|
|
received = header_raw_find(&h, "received", NULL);
|
|
if (received)
|
|
received = strrchr(received, ';');
|
|
if (received)
|
|
mi->date_received = header_decode_date(received + 1, NULL);
|
|
else
|
|
mi->date_received = 0;
|
|
mi->message_id = header_msgid_decode(header_raw_find(&h, "message-id", NULL));
|
|
/* if we have a references, use that, otherwise, see if we have an in-reply-to
|
|
header, with parsable content, otherwise *shrug* */
|
|
mi->references = header_references_decode(header_raw_find(&h, "references", NULL));
|
|
if (mi->references == NULL)
|
|
mi->references = header_references_decode(header_raw_find(&h, "in-reply-to", NULL));
|
|
return mi;
|
|
}
|
|
|
|
|
|
static CamelMessageInfo *
|
|
message_info_load(CamelFolderSummary *s, FILE *in)
|
|
{
|
|
CamelMessageInfo *mi;
|
|
guint count;
|
|
int i;
|
|
|
|
mi = g_malloc0(s->message_info_size);
|
|
|
|
io(printf("Loading message info\n"));
|
|
|
|
camel_folder_summary_decode_string(in, &mi->uid);
|
|
camel_folder_summary_decode_uint32(in, &mi->flags);
|
|
camel_folder_summary_decode_uint32(in, &mi->size);
|
|
camel_folder_summary_decode_time_t(in, &mi->date_sent);
|
|
camel_folder_summary_decode_time_t(in, &mi->date_received);
|
|
camel_folder_summary_decode_string(in, &mi->subject);
|
|
camel_folder_summary_decode_string(in, &mi->from);
|
|
camel_folder_summary_decode_string(in, &mi->to);
|
|
camel_folder_summary_decode_string(in, &mi->cc);
|
|
mi->content = NULL;
|
|
|
|
camel_folder_summary_decode_string(in, &mi->message_id);
|
|
|
|
camel_folder_summary_decode_uint32(in, &count);
|
|
for (i=0;i<count;i++) {
|
|
char *id;
|
|
camel_folder_summary_decode_string(in, &id);
|
|
header_references_list_append_asis(&mi->references, id);
|
|
}
|
|
|
|
camel_folder_summary_decode_uint32(in, &count);
|
|
for (i=0;i<count;i++) {
|
|
char *name;
|
|
camel_folder_summary_decode_string(in, &name);
|
|
camel_flag_set(&mi->user_flags, name, TRUE);
|
|
g_free(name);
|
|
}
|
|
|
|
camel_folder_summary_decode_uint32(in, &count);
|
|
for (i=0;i<count;i++) {
|
|
char *name, *value;
|
|
camel_folder_summary_decode_string(in, &name);
|
|
camel_folder_summary_decode_string(in, &value);
|
|
camel_tag_set(&mi->user_tags, name, value);
|
|
g_free(name);
|
|
g_free(value);
|
|
}
|
|
|
|
return mi;
|
|
}
|
|
|
|
static int
|
|
message_info_save(CamelFolderSummary *s, FILE *out, CamelMessageInfo *mi)
|
|
{
|
|
guint32 count;
|
|
CamelFlag *flag;
|
|
CamelTag *tag;
|
|
struct _header_references *refs;
|
|
|
|
io(printf("Saving message info\n"));
|
|
|
|
camel_folder_summary_encode_string(out, mi->uid);
|
|
camel_folder_summary_encode_uint32(out, mi->flags);
|
|
camel_folder_summary_encode_uint32(out, mi->size);
|
|
camel_folder_summary_encode_time_t(out, mi->date_sent);
|
|
camel_folder_summary_encode_time_t(out, mi->date_received);
|
|
camel_folder_summary_encode_string(out, mi->subject);
|
|
camel_folder_summary_encode_string(out, mi->from);
|
|
camel_folder_summary_encode_string(out, mi->to);
|
|
camel_folder_summary_encode_string(out, mi->cc);
|
|
|
|
camel_folder_summary_encode_string(out, mi->message_id);
|
|
|
|
count = header_references_list_size(&mi->references);
|
|
camel_folder_summary_encode_uint32(out, count);
|
|
refs = mi->references;
|
|
while (refs) {
|
|
camel_folder_summary_encode_string(out, refs->id);
|
|
refs = refs->next;
|
|
}
|
|
|
|
count = camel_flag_list_size(&mi->user_flags);
|
|
camel_folder_summary_encode_uint32(out, count);
|
|
flag = mi->user_flags;
|
|
while (flag) {
|
|
camel_folder_summary_encode_string(out, flag->name);
|
|
flag = flag->next;
|
|
}
|
|
|
|
count = camel_tag_list_size(&mi->user_tags);
|
|
camel_folder_summary_encode_uint32(out, count);
|
|
tag = mi->user_tags;
|
|
while (tag) {
|
|
camel_folder_summary_encode_string(out, tag->name);
|
|
camel_folder_summary_encode_string(out, tag->value);
|
|
tag = tag->next;
|
|
}
|
|
|
|
return ferror(out);
|
|
}
|
|
|
|
static void
|
|
message_info_free(CamelFolderSummary *s, CamelMessageInfo *mi)
|
|
{
|
|
camel_message_info_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, upos, ubodypos, uendpos;
|
|
struct _header_content_type *ct;
|
|
|
|
io(printf("Loading content info\n"));
|
|
|
|
ci = g_malloc0(s->content_info_size);
|
|
|
|
camel_folder_summary_decode_uint32(in, &upos);
|
|
camel_folder_summary_decode_uint32(in, &ubodypos);
|
|
camel_folder_summary_decode_uint32(in, &uendpos);
|
|
|
|
ci->pos = (off_t) upos;
|
|
ci->bodypos = (off_t) ubodypos;
|
|
ci->endpos = (off_t) uendpos;
|
|
|
|
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;
|
|
|
|
io(printf("Saving content info\n"));
|
|
|
|
camel_folder_summary_encode_uint32(out, ci->pos);
|
|
camel_folder_summary_encode_uint32(out, ci->bodypos);
|
|
camel_folder_summary_encode_uint32(out, ci->endpos);
|
|
|
|
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 body;
|
|
int enc_id = -1, chr_id = -1, idx_id = -1;
|
|
struct _CamelFolderSummaryPrivate *p = _PRIVATE(s);
|
|
CamelMimeFilterCharset *mfc;
|
|
CamelMessageContentInfo *part;
|
|
|
|
d(printf("building content info\n"));
|
|
|
|
/* start of this part */
|
|
state = camel_mime_parser_step(mp, &buffer, &len);
|
|
body = camel_mime_parser_tell(mp);
|
|
|
|
info = ((CamelFolderSummaryClass *)(CAMEL_OBJECT_GET_CLASS(s)))->content_info_new_from_parser(s, mp);
|
|
|
|
info->pos = camel_mime_parser_tell_start_headers(mp);
|
|
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;
|
|
|
|
d(printf("generating index:\n"));
|
|
|
|
encoding = header_content_encoding_decode(camel_mime_parser_header(mp, "content-transfer-encoding", NULL));
|
|
if (encoding) {
|
|
if (!strcasecmp(encoding, "base64")) {
|
|
d(printf(" decoding base64\n"));
|
|
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")) {
|
|
d(printf(" decoding quoted-printable\n"));
|
|
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);
|
|
} else {
|
|
d(printf(" ignoring encoding %s\n", encoding));
|
|
}
|
|
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:
|
|
d(printf("Summarising multipart\n"));
|
|
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:
|
|
d(printf("Summarising message\n"));
|
|
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?");
|
|
}
|
|
state = camel_mime_parser_step(mp, &buffer, &len);
|
|
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);
|
|
|
|
d(printf("finished building content info\n"));
|
|
|
|
return info;
|
|
}
|
|
|
|
gboolean
|
|
camel_flag_get(CamelFlag **list, const char *name)
|
|
{
|
|
CamelFlag *flag;
|
|
flag = *list;
|
|
while (flag) {
|
|
if (!strcmp(flag->name, name))
|
|
return TRUE;
|
|
flag = flag->next;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
void
|
|
camel_flag_set(CamelFlag **list, const char *name, gboolean value)
|
|
{
|
|
CamelFlag *flag, *tmp;
|
|
|
|
/* this 'trick' works because flag->next is the first element */
|
|
flag = (CamelFlag *)list;
|
|
while (flag->next) {
|
|
tmp = flag->next;
|
|
if (!strcmp(flag->next->name, name)) {
|
|
if (!value) {
|
|
flag->next = tmp->next;
|
|
g_free(tmp);
|
|
}
|
|
return;
|
|
}
|
|
flag = tmp;
|
|
}
|
|
|
|
if (value) {
|
|
tmp = g_malloc(sizeof(*tmp) + strlen(name));
|
|
strcpy(tmp->name, name);
|
|
tmp->next = 0;
|
|
flag->next = tmp;
|
|
}
|
|
}
|
|
|
|
int
|
|
camel_flag_list_size(CamelFlag **list)
|
|
{
|
|
int count=0;
|
|
CamelFlag *flag;
|
|
|
|
flag = *list;
|
|
while (flag) {
|
|
count++;
|
|
flag = flag->next;
|
|
}
|
|
return count;
|
|
}
|
|
|
|
void
|
|
camel_flag_list_free(CamelFlag **list)
|
|
{
|
|
CamelFlag *flag, *tmp;
|
|
flag = *list;
|
|
while (flag) {
|
|
tmp = flag->next;
|
|
g_free(flag);
|
|
flag = tmp;
|
|
}
|
|
*list = NULL;
|
|
}
|
|
|
|
const char *camel_tag_get(CamelTag **list, const char *name)
|
|
{
|
|
CamelTag *tag;
|
|
|
|
tag = *list;
|
|
while (tag) {
|
|
if (!strcmp(tag->name, name))
|
|
return (const char *)tag->value;
|
|
tag = tag->next;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
void camel_tag_set(CamelTag **list, const char *name, const char *value)
|
|
{
|
|
CamelTag *tag, *tmp;
|
|
|
|
/* this 'trick' works because tag->next is the first element */
|
|
tag = (CamelTag *)list;
|
|
while (tag->next) {
|
|
tmp = tag->next;
|
|
if (!strcmp(tmp->name, name)) {
|
|
if (value == NULL) { /* clear it? */
|
|
tag->next = tmp->next;
|
|
g_free(tmp->value);
|
|
g_free(tmp);
|
|
} else if (strcmp(tmp->value, value)) { /* has it changed? */
|
|
g_free(tmp->value);
|
|
tmp->value = g_strdup(value);
|
|
}
|
|
return;
|
|
}
|
|
tag = tmp;
|
|
}
|
|
|
|
if (value) {
|
|
tmp = g_malloc(sizeof(*tmp)+strlen(name));
|
|
strcpy(tmp->name, name);
|
|
tmp->value = g_strdup(value);
|
|
tmp->next = 0;
|
|
tag->next = tmp;
|
|
}
|
|
}
|
|
|
|
int camel_tag_list_size(CamelTag **list)
|
|
{
|
|
int count=0;
|
|
CamelTag *tag;
|
|
|
|
tag = *list;
|
|
while (tag) {
|
|
count++;
|
|
tag = tag->next;
|
|
}
|
|
return count;
|
|
}
|
|
|
|
void camel_tag_list_free(CamelTag **list)
|
|
{
|
|
CamelTag *tag, *tmp;
|
|
tag = *list;
|
|
while (tag) {
|
|
tmp = tag->next;
|
|
g_free(tag->value);
|
|
g_free(tag);
|
|
tag = tmp;
|
|
}
|
|
*list = NULL;
|
|
}
|
|
|
|
/**
|
|
* camel_message_info_dup_to:
|
|
* @from: source message info
|
|
* @to: destination message info
|
|
*
|
|
* Duplicates the contents of one CamelMessageInfo structure into another.
|
|
* (The destination is assumed to be empty: its contents are not freed.)
|
|
* The slightly odd interface is to allow this to be used to initialize
|
|
* "subclasses" of CamelMessageInfo.
|
|
**/
|
|
void
|
|
camel_message_info_dup_to(const CamelMessageInfo *from, CamelMessageInfo *to)
|
|
{
|
|
CamelFlag *flag;
|
|
CamelTag *tag;
|
|
|
|
/* Copy numbers */
|
|
to->flags = from->flags;
|
|
to->size = from->size;
|
|
to->date_sent = from->date_sent;
|
|
to->date_received = from->date_received;
|
|
|
|
/* Copy strings */
|
|
to->subject = g_strdup(from->subject);
|
|
to->from = g_strdup(from->from);
|
|
to->to = g_strdup(from->to);
|
|
to->cc = g_strdup(from->cc);
|
|
to->uid = g_strdup(from->uid);
|
|
to->message_id = g_strdup(from->message_id);
|
|
|
|
/* Copy structures */
|
|
to->references = header_references_dup(from->references);
|
|
flag = from->user_flags;
|
|
while (flag) {
|
|
camel_flag_set(&to->user_flags, flag->name, TRUE);
|
|
flag = flag->next;
|
|
}
|
|
|
|
tag = from->user_tags;
|
|
while (tag) {
|
|
camel_tag_set(&to->user_tags, tag->name, tag->value);
|
|
tag = tag->next;
|
|
}
|
|
|
|
/* FIXME some day */
|
|
to->content = NULL;
|
|
}
|
|
|
|
/**
|
|
* camel_message_info_free:
|
|
* @mi: the message info
|
|
*
|
|
* Frees a CamelMessageInfo and its contents.
|
|
**/
|
|
void
|
|
camel_message_info_free(CamelMessageInfo *mi)
|
|
{
|
|
g_free(mi->uid);
|
|
g_free(mi->subject);
|
|
g_free(mi->from);
|
|
g_free(mi->to);
|
|
g_free(mi->cc);
|
|
g_free(mi->message_id);
|
|
header_references_list_clear(&mi->references);
|
|
camel_flag_list_free(&mi->user_flags);
|
|
camel_tag_list_free(&mi->user_tags);
|
|
/* FIXME: content info? */
|
|
g_free(mi);
|
|
}
|
|
|
|
#if 0
|
|
static void
|
|
content_info_dump(CamelMessageContentInfo *ci, int depth)
|
|
{
|
|
char *p;
|
|
|
|
p = alloca(depth*4+1);
|
|
memset(p, ' ', depth*4);
|
|
p[depth*4] = 0;
|
|
|
|
if (ci == NULL) {
|
|
printf("%s<empty>\n", p);
|
|
return;
|
|
}
|
|
|
|
printf("%sconent-type: %s/%s\n", p, ci->type->type, ci->type->subtype);
|
|
printf("%sontent-transfer-encoding: %s\n", p, ci->encoding);
|
|
printf("%scontent-description: %s\n", p, ci->description);
|
|
printf("%sbytes: %d %d %d\n", p, (int)ci->pos, (int)ci->bodypos, (int)ci->endpos);
|
|
ci = ci->childs;
|
|
while (ci) {
|
|
content_info_dump(ci, depth+1);
|
|
ci = ci->next;
|
|
}
|
|
}
|
|
|
|
static void
|
|
message_info_dump(CamelMessageInfo *mi)
|
|
{
|
|
if (mi == NULL) {
|
|
printf("No message?\n");
|
|
return;
|
|
}
|
|
|
|
printf("Subject: %s\n", mi->subject);
|
|
printf("To: %s\n", mi->to);
|
|
printf("Cc: %s\n", mi->cc);
|
|
printf("From: %s\n", mi->from);
|
|
printf("UID: %s\n", mi->uid);
|
|
printf("Flags: %04x\n", mi->flags & 0xffff);
|
|
content_info_dump(mi->content, 0);
|
|
}
|
|
|
|
int main(int argc, char **argv)
|
|
{
|
|
CamelMimeParser *mp;
|
|
int fd;
|
|
CamelFolderSummary *s;
|
|
char *buffer;
|
|
int len;
|
|
int i;
|
|
ibex *index;
|
|
|
|
/*g_tk_init(&argc, &argv);*/
|
|
|
|
#if 0
|
|
{
|
|
int i;
|
|
char *s;
|
|
char buf[1024];
|
|
|
|
for (i=0;i<434712;i++) {
|
|
memcpy(buf, " ", 50);
|
|
buf[50] = 0;
|
|
#if 0
|
|
s = g_strdup(buf);
|
|
g_free(s);
|
|
#endif
|
|
}
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
if (argc < 2 ) {
|
|
printf("usage: %s mbox\n", argv[0]);
|
|
return 1;
|
|
}
|
|
|
|
fd = open(argv[1], O_RDONLY);
|
|
|
|
index = ibex_open("index.ibex", O_CREAT|O_RDWR, 0600);
|
|
|
|
mp = camel_mime_parser_new();
|
|
camel_mime_parser_scan_from(mp, TRUE);
|
|
/* camel_mime_parser_set_header_regex(mp, "^(content-[^:]*|subject|from|to|date):");*/
|
|
camel_mime_parser_init_with_fd(mp, fd);
|
|
|
|
s = camel_folder_summary_new();
|
|
camel_folder_summary_set_build_content(s, TRUE);
|
|
/* camel_folder_summary_set_index(s, index);*/
|
|
|
|
while (camel_mime_parser_step(mp, &buffer, &len) == HSCAN_FROM) {
|
|
/*printf("Parsing message ...\n");*/
|
|
camel_folder_summary_add_from_parser(s, mp);
|
|
if (camel_mime_parser_step(mp, &buffer, &len) != HSCAN_FROM_END) {
|
|
g_warning("Uknown state encountered, excpecting %d, got %d\n", HSCAN_FROM_END, camel_mime_parser_state(mp));
|
|
break;
|
|
}
|
|
}
|
|
|
|
printf("Printing summary\n");
|
|
for (i=0;i<camel_folder_summary_count(s);i++) {
|
|
message_info_dump(camel_folder_summary_index(s, i));
|
|
}
|
|
|
|
printf("Saivng summary\n");
|
|
camel_folder_summary_set_filename(s, "index.summary");
|
|
camel_folder_summary_save(s);
|
|
|
|
{
|
|
CamelFolderSummary *n;
|
|
|
|
printf("\nLoading summary\n");
|
|
n = camel_folder_summary_new();
|
|
camel_folder_summary_set_build_content(n, TRUE);
|
|
camel_folder_summary_set_filename(n, "index.summary");
|
|
camel_folder_summary_load(n);
|
|
|
|
printf("Printing summary\n");
|
|
for (i=0;i<camel_folder_summary_count(n);i++) {
|
|
message_info_dump(camel_folder_summary_index(n, i));
|
|
}
|
|
camel_object_unref(n);
|
|
}
|
|
|
|
|
|
camel_object_unref(mp);
|
|
camel_object_unref(s);
|
|
|
|
printf("summarised %d messages\n", camel_folder_summary_count(s));
|
|
#if 0
|
|
printf("g_strdup count = %d\n", strdup_count);
|
|
printf("g_malloc count = %d\n", malloc_count);
|
|
printf("g_free count = %d\n", free_count);
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
#endif
|