Check the uid string is all digits before trying to write a 'standard'
2000-11-16 Not Zed <NotZed@HelixCode.com> * providers/local/camel-local-summary.c (local_summary_encode_x_evolution): Check the uid string is all digits before trying to write a 'standard' x-ev header. * providers/local/camel-maildir-summary.c (camel_maildir_summary_info_to_name): Convert an info into a maildir name:info filename. (camel_maildir_summary_name_to_info): Convert a name:info filename into an info, and tell us if it didn't match it. (message_info_new): When creating a new filename, gets its info from the flags field. Likewise if creating from an existing file, extract the flags. (maildir_summary_sync): Remove a small memleak. Also, if our flags and that requires a filename change, perform that here. (message_info_new): Get the received date from the filename. Also, dont overwirte the uid if we have one. (maildir_summary_check): Sort the summary in received order before completion. (maildir_summary_next_uid_string): Test the name for collusions before we give it out. Retry, and if that fails, well, I guess we collide :( * providers/local/camel-mbox-folder.c (mbox_lock): Implement mbox locking. (mbox_unlock): And unlocking. (mbox_append_message): Lock the folder for write before doing anything. (mbox_get_message): Lock the folder for read before doing anything. * providers/local/camel-local-folder.c (camel_local_folder_lock): Implement something here. We handle the recursive ability but pass the locking to the folder itself. (camel_local_folder_unlock): Likewise for unlocking. (local_lock): Default - do nothing, return success. (local_unlock): Same. (local_sync): Changed slightly for locking api changes, and also, only lock around the sync process itself. * camel-lock.c: New file - utility functions for locking using different strategies and/or for locking folders safely. * Makefile.am (libcamel_la_SOURCES): Adde camel-lock.[ch] svn path=/trunk/; revision=6592
This commit is contained in:
@ -1,5 +1,44 @@
|
||||
2000-11-16 Not Zed <NotZed@HelixCode.com>
|
||||
|
||||
* providers/local/camel-local-summary.c
|
||||
(local_summary_encode_x_evolution): Check the uid string is all
|
||||
digits before trying to write a 'standard' x-ev header.
|
||||
|
||||
* providers/local/camel-maildir-summary.c
|
||||
(camel_maildir_summary_info_to_name): Convert an info into a
|
||||
maildir name:info filename.
|
||||
(camel_maildir_summary_name_to_info): Convert a name:info filename
|
||||
into an info, and tell us if it didn't match it.
|
||||
(message_info_new): When creating a new filename, gets its info
|
||||
from the flags field. Likewise if creating from an existing file,
|
||||
extract the flags.
|
||||
(maildir_summary_sync): Remove a small memleak. Also, if our
|
||||
flags and that requires a filename change, perform that here.
|
||||
(message_info_new): Get the received date from the filename.
|
||||
Also, dont overwirte the uid if we have one.
|
||||
(maildir_summary_check): Sort the summary in received order before
|
||||
completion.
|
||||
(maildir_summary_next_uid_string): Test the name for collusions
|
||||
before we give it out. Retry, and if that fails, well, I guess we
|
||||
collide :(
|
||||
|
||||
* providers/local/camel-mbox-folder.c (mbox_lock): Implement mbox
|
||||
locking.
|
||||
(mbox_unlock): And unlocking.
|
||||
(mbox_append_message): Lock the folder for write before doing
|
||||
anything.
|
||||
(mbox_get_message): Lock the folder for read before doing
|
||||
anything.
|
||||
|
||||
* providers/local/camel-local-folder.c (camel_local_folder_lock):
|
||||
Implement something here. We handle the recursive ability but
|
||||
pass the locking to the folder itself.
|
||||
(camel_local_folder_unlock): Likewise for unlocking.
|
||||
(local_lock): Default - do nothing, return success.
|
||||
(local_unlock): Same.
|
||||
(local_sync): Changed slightly for locking api changes, and also,
|
||||
only lock around the sync process itself.
|
||||
|
||||
* camel-lock.c: New file - utility functions for locking using
|
||||
different strategies and/or for locking folders safely.
|
||||
|
||||
|
||||
@ -1,7 +1,5 @@
|
||||
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8; fill-column: 160 -*- */
|
||||
/* camel-local-folder.c : Abstract class for an email folder */
|
||||
|
||||
/*
|
||||
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8; fill-column: 160 -*-
|
||||
*
|
||||
* Authors: Michael Zucchi <notzed@helixcode.com>
|
||||
*
|
||||
* Copyright (C) 1999, 2000 Helix Code Inc.
|
||||
@ -53,6 +51,8 @@ static CamelFolderClass *parent_class = NULL;
|
||||
#define CF_CLASS(so) CAMEL_FOLDER_CLASS (CAMEL_OBJECT_GET_CLASS(so))
|
||||
#define CLOCALS_CLASS(so) CAMEL_STORE_CLASS (CAMEL_OBJECT_GET_CLASS(so))
|
||||
|
||||
static int local_lock(CamelLocalFolder *lf, CamelLockType type, CamelException *ex);
|
||||
static void local_unlock(CamelLocalFolder *lf);
|
||||
|
||||
static void local_sync(CamelFolder *folder, gboolean expunge, CamelException *ex);
|
||||
static gint local_get_message_count(CamelFolder *folder);
|
||||
@ -111,6 +111,9 @@ camel_local_folder_class_init(CamelLocalFolderClass * camel_local_folder_class)
|
||||
camel_folder_class->set_message_user_flag = local_set_message_user_flag;
|
||||
camel_folder_class->get_message_user_tag = local_get_message_user_tag;
|
||||
camel_folder_class->set_message_user_tag = local_set_message_user_tag;
|
||||
|
||||
camel_local_folder_class->lock = local_lock;
|
||||
camel_local_folder_class->unlock = local_unlock;
|
||||
}
|
||||
|
||||
static void
|
||||
@ -221,15 +224,45 @@ camel_local_folder_construct(CamelLocalFolder *lf, CamelStore *parent_store, con
|
||||
return lf;
|
||||
}
|
||||
|
||||
/* Have to work out how/when to lock */
|
||||
int camel_local_folder_lock(CamelLocalFolder *lf, CamelException *ex)
|
||||
/* lock the folder, may be called repeatedly (with matching unlock calls),
|
||||
with type the same or less than the first call */
|
||||
int camel_local_folder_lock(CamelLocalFolder *lf, CamelLockType type, CamelException *ex)
|
||||
{
|
||||
if (lf->locked > 0) {
|
||||
/* lets be anal here - its important the code knows what its doing */
|
||||
g_assert(lf->locktype == type || lf->locktype == CAMEL_LOCK_WRITE);
|
||||
} else {
|
||||
if (CLOCALF_CLASS(lf)->lock(lf, type, ex) == -1)
|
||||
return -1;
|
||||
lf->locktype = type;
|
||||
}
|
||||
|
||||
lf->locked++;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* unlock folder */
|
||||
int camel_local_folder_unlock(CamelLocalFolder *lf)
|
||||
{
|
||||
g_assert(lf->locked>0);
|
||||
lf->locked--;
|
||||
if (lf->locked == 0)
|
||||
CLOCALF_CLASS(lf)->unlock(lf);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
local_lock(CamelLocalFolder *lf, CamelLockType type, CamelException *ex)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int camel_local_folder_unlock(CamelLocalFolder *lf, CamelException *ex)
|
||||
static void
|
||||
local_unlock(CamelLocalFolder *lf)
|
||||
{
|
||||
return 0;
|
||||
/* nothing */
|
||||
}
|
||||
|
||||
static void
|
||||
@ -239,18 +272,18 @@ local_sync(CamelFolder *folder, gboolean expunge, CamelException *ex)
|
||||
|
||||
d(printf("local sync, expunge=%s\n", expunge?"true":"false"));
|
||||
|
||||
if (camel_local_folder_lock(lf, ex) == -1)
|
||||
if (camel_local_folder_lock(lf, CAMEL_LOCK_WRITE, ex) == -1)
|
||||
return;
|
||||
|
||||
/* if sync fails, we'll pass it up on exit through ex */
|
||||
camel_local_summary_sync(lf->summary, expunge, lf->changes, ex);
|
||||
camel_local_folder_unlock(lf);
|
||||
|
||||
if (camel_folder_change_info_changed(lf->changes)) {
|
||||
camel_object_trigger_event(CAMEL_OBJECT(folder), "folder_changed", lf->changes);
|
||||
camel_folder_change_info_clear(lf->changes);
|
||||
}
|
||||
|
||||
if (camel_local_folder_unlock(lf, ex) == -1)
|
||||
return;
|
||||
|
||||
/* force save of metadata */
|
||||
if (lf->index)
|
||||
ibex_save(lf->index);
|
||||
@ -267,186 +300,6 @@ local_expunge(CamelFolder *folder, CamelException *ex)
|
||||
camel_folder_sync(folder, TRUE, ex);
|
||||
}
|
||||
|
||||
#if 0
|
||||
static void
|
||||
local_append_message(CamelFolder *folder, CamelMimeMessage * message, const CamelMessageInfo * info, CamelException *ex)
|
||||
{
|
||||
CamelLocalFolder *local_folder = CAMEL_LOCAL_FOLDER(folder);
|
||||
CamelStream *output_stream = NULL, *filter_stream = NULL;
|
||||
CamelMimeFilter *filter_from = NULL;
|
||||
CamelMessageInfo *newinfo;
|
||||
struct stat st;
|
||||
off_t seek = -1;
|
||||
char *xev;
|
||||
guint32 uid;
|
||||
char *fromline = NULL;
|
||||
|
||||
if (stat(local_folder->folder_path, &st) != 0)
|
||||
goto fail;
|
||||
|
||||
output_stream = camel_stream_fs_new_with_name(local_folder->folder_file_path, O_WRONLY|O_APPEND, 0600);
|
||||
if (output_stream == NULL)
|
||||
goto fail;
|
||||
|
||||
seek = st.st_size;
|
||||
|
||||
/* assign a new x-evolution header/uid */
|
||||
camel_medium_remove_header(CAMEL_MEDIUM(message), "X-Evolution");
|
||||
uid = camel_folder_summary_next_uid(CAMEL_FOLDER_SUMMARY(local_folder->summary));
|
||||
/* important that the header matches exactly 00000000-0000 */
|
||||
xev = g_strdup_printf("%08x-%04x", uid, info ? info->flags & 0xFFFF : 0);
|
||||
camel_medium_add_header(CAMEL_MEDIUM(message), "X-Evolution", xev);
|
||||
g_free(xev);
|
||||
|
||||
/* we must write this to the non-filtered stream ... */
|
||||
fromline = camel_local_summary_build_from(CAMEL_MIME_PART(message)->headers);
|
||||
if (camel_stream_printf(output_stream, seek==0?"%s":"\n%s", fromline) == -1)
|
||||
goto fail;
|
||||
|
||||
/* and write the content to the filtering stream, that translated '\nFrom' into '\n>From' */
|
||||
filter_stream = (CamelStream *) camel_stream_filter_new_with_stream(output_stream);
|
||||
filter_from = (CamelMimeFilter *) camel_mime_filter_from_new();
|
||||
camel_stream_filter_add((CamelStreamFilter *) filter_stream, filter_from);
|
||||
if (camel_data_wrapper_write_to_stream(CAMEL_DATA_WRAPPER(message), filter_stream) == -1)
|
||||
goto fail;
|
||||
|
||||
if (camel_stream_close(filter_stream) == -1)
|
||||
goto fail;
|
||||
|
||||
/* filter stream ref's the output stream itself, so we need to unref it too */
|
||||
camel_object_unref(CAMEL_OBJECT(filter_from));
|
||||
camel_object_unref(CAMEL_OBJECT(filter_stream));
|
||||
camel_object_unref(CAMEL_OBJECT(output_stream));
|
||||
g_free(fromline);
|
||||
|
||||
/* force a summary update - will only update from the new position, if it can */
|
||||
if (camel_local_summary_update(local_folder->summary, seek==0?seek:seek+1, local_folder->changes) == 0) {
|
||||
char uidstr[16];
|
||||
|
||||
sprintf(uidstr, "%u", uid);
|
||||
newinfo = camel_folder_summary_uid(CAMEL_FOLDER_SUMMARY(local_folder->summary), uidstr);
|
||||
|
||||
if (info && newinfo) {
|
||||
CamelFlag *flag = info->user_flags;
|
||||
CamelTag *tag = info->user_tags;
|
||||
|
||||
while (flag) {
|
||||
camel_flag_set(&(newinfo->user_flags), flag->name, TRUE);
|
||||
flag = flag->next;
|
||||
}
|
||||
|
||||
while (tag) {
|
||||
camel_tag_set(&(newinfo->user_tags), tag->name, tag->value);
|
||||
tag = tag->next;
|
||||
}
|
||||
}
|
||||
camel_object_trigger_event(CAMEL_OBJECT(folder), "folder_changed", local_folder->changes);
|
||||
camel_folder_change_info_clear(local_folder->changes);
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
fail:
|
||||
if (camel_exception_is_set(ex)) {
|
||||
camel_exception_setv(ex, camel_exception_get_id(ex),
|
||||
_("Cannot append message to local file: %s"), camel_exception_get_description(ex));
|
||||
} else {
|
||||
camel_exception_setv(ex, CAMEL_EXCEPTION_SYSTEM,
|
||||
_("Cannot append message to local file: %s"), g_strerror(errno));
|
||||
}
|
||||
if (filter_stream) {
|
||||
/*camel_stream_close (filter_stream); */
|
||||
camel_object_unref(CAMEL_OBJECT(filter_stream));
|
||||
}
|
||||
if (output_stream)
|
||||
camel_object_unref(CAMEL_OBJECT(output_stream));
|
||||
|
||||
if (filter_from)
|
||||
camel_object_unref(CAMEL_OBJECT(filter_from));
|
||||
|
||||
g_free(fromline);
|
||||
|
||||
/* make sure the file isn't munged by us */
|
||||
if (seek != -1) {
|
||||
int fd = open(local_folder->folder_file_path, O_WRONLY, 0600);
|
||||
|
||||
if (fd != -1) {
|
||||
ftruncate(fd, st.st_size);
|
||||
close(fd);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static CamelMimeMessage *
|
||||
local_get_message(CamelFolder *folder, const gchar * uid, CamelException *ex)
|
||||
{
|
||||
CamelLocalFolder *local_folder = CAMEL_LOCAL_FOLDER(folder);
|
||||
CamelStream *message_stream = NULL;
|
||||
CamelMimeMessage *message = NULL;
|
||||
CamelLocalMessageInfo *info;
|
||||
CamelMimeParser *parser = NULL;
|
||||
char *buffer;
|
||||
int len;
|
||||
|
||||
/* get the message summary info */
|
||||
info = (CamelLocalMessageInfo *) camel_folder_summary_uid(CAMEL_FOLDER_SUMMARY(local_folder->summary), uid);
|
||||
|
||||
if (info == NULL) {
|
||||
errno = ENOENT;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* if this has no content, its an error in the library */
|
||||
g_assert(info->info.content);
|
||||
g_assert(info->frompos != -1);
|
||||
|
||||
/* where we read from */
|
||||
message_stream = camel_stream_fs_new_with_name(local_folder->folder_file_path, O_RDONLY, 0);
|
||||
if (message_stream == NULL)
|
||||
goto fail;
|
||||
|
||||
/* we use a parser to verify the message is correct, and in the correct position */
|
||||
parser = camel_mime_parser_new();
|
||||
camel_mime_parser_init_with_stream(parser, message_stream);
|
||||
camel_object_unref(CAMEL_OBJECT(message_stream));
|
||||
camel_mime_parser_scan_from(parser, TRUE);
|
||||
|
||||
camel_mime_parser_seek(parser, info->frompos, SEEK_SET);
|
||||
if (camel_mime_parser_step(parser, &buffer, &len) != HSCAN_FROM) {
|
||||
g_warning("File appears truncated");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (camel_mime_parser_tell_start_from(parser) != info->frompos) {
|
||||
/* TODO: This should probably perform a re-sync/etc, and try again? */
|
||||
g_warning("Summary doesn't match the folder contents! eek!\n"
|
||||
" expecting offset %ld got %ld", (long int)info->frompos,
|
||||
(long int)camel_mime_parser_tell_start_from(parser));
|
||||
errno = EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
message = camel_mime_message_new();
|
||||
if (camel_mime_part_construct_from_parser(CAMEL_MIME_PART(message), parser) == -1) {
|
||||
g_warning("Construction failed");
|
||||
goto fail;
|
||||
}
|
||||
camel_object_unref(CAMEL_OBJECT(parser));
|
||||
|
||||
return message;
|
||||
|
||||
fail:
|
||||
camel_exception_setv(ex, CAMEL_EXCEPTION_FOLDER_INVALID_UID, _("Cannot get message: %s"), g_strerror(errno));
|
||||
|
||||
if (parser)
|
||||
camel_object_unref(CAMEL_OBJECT(parser));
|
||||
if (message)
|
||||
camel_object_unref(CAMEL_OBJECT(message));
|
||||
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
The following functions all work off the summary, so the default operations provided
|
||||
in camel-local-folder will suffice for all subclasses. They may want to
|
||||
|
||||
@ -32,6 +32,7 @@ extern "C" {
|
||||
#include <camel/camel-folder-search.h>
|
||||
#include <libibex/ibex.h>
|
||||
#include "camel-local-summary.h"
|
||||
#include "camel-lock.h"
|
||||
|
||||
/* #include "camel-store.h" */
|
||||
|
||||
@ -46,6 +47,7 @@ typedef struct {
|
||||
guint32 flags; /* open mode flags */
|
||||
|
||||
int locked; /* lock counter */
|
||||
CamelLockType locktype; /* what type of lock we have */
|
||||
|
||||
char *base_path; /* base path of the local folder */
|
||||
char *folder_path; /* the path to the folder itself */
|
||||
@ -67,10 +69,10 @@ typedef struct {
|
||||
CamelLocalSummary *(*create_summary)(const char *path, const char *folder, ibex *index);
|
||||
|
||||
/* Lock the folder for my operations */
|
||||
int (*lock)(CamelLocalFolder *);
|
||||
int (*lock)(CamelLocalFolder *, CamelLockType type, CamelException *ex);
|
||||
|
||||
/* Unlock the folder for my operations */
|
||||
int (*unlock)(CamelLocalFolder *);
|
||||
void (*unlock)(CamelLocalFolder *);
|
||||
} CamelLocalFolderClass;
|
||||
|
||||
|
||||
@ -84,8 +86,8 @@ CamelType camel_local_folder_get_type(void);
|
||||
|
||||
/* Lock the folder for internal use. May be called repeatedly */
|
||||
/* UNIMPLEMENTED */
|
||||
int camel_local_folder_lock(CamelLocalFolder *lf, CamelException *ex);
|
||||
int camel_local_folder_unlock(CamelLocalFolder *lf, CamelException *ex);
|
||||
int camel_local_folder_lock(CamelLocalFolder *lf, CamelLockType type, CamelException *ex);
|
||||
int camel_local_folder_unlock(CamelLocalFolder *lf);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
||||
@ -290,12 +290,15 @@ local_summary_encode_x_evolution(CamelLocalSummary *cls, const CamelMessageInfo
|
||||
GString *val = g_string_new("");
|
||||
CamelFlag *flag = mi->user_flags;
|
||||
CamelTag *tag = mi->user_tags;
|
||||
char *ret;
|
||||
char *ret, *p;
|
||||
guint32 uid;
|
||||
|
||||
/* FIXME: work out what to do with uid's that aren't stored here? */
|
||||
/* FIXME: perhaps make that a mbox folder only issue?? */
|
||||
if (sscanf(mi->uid, "%u", &uid) == 1) {
|
||||
p = mi->uid;
|
||||
while (*p && isdigit(*p))
|
||||
p++;
|
||||
if (*p == 0 && sscanf(mi->uid, "%u", &uid) == 1) {
|
||||
g_string_sprintf(out, "%08x-%04x", uid, mi->flags & 0xffff);
|
||||
} else {
|
||||
g_string_sprintf(out, "%s-%04x", mi->uid, mi->flags & 0xffff);
|
||||
|
||||
@ -96,7 +96,7 @@ CamelType camel_maildir_folder_get_type(void)
|
||||
(CamelObjectInitFunc) maildir_init,
|
||||
(CamelObjectFinalizeFunc) maildir_finalize);
|
||||
}
|
||||
|
||||
|
||||
return camel_maildir_folder_type;
|
||||
}
|
||||
|
||||
@ -128,8 +128,6 @@ static void maildir_append_message(CamelFolder * folder, CamelMimeMessage * mess
|
||||
CamelMaildirMessageInfo *mdi;
|
||||
char *name, *dest;
|
||||
|
||||
/* FIXME: probably needs additional locking */
|
||||
|
||||
d(printf("Appending message\n"));
|
||||
|
||||
/* add it to the summary/assign the uid, etc */
|
||||
@ -202,6 +200,7 @@ static CamelMimeMessage *maildir_get_message(CamelFolder * folder, const gchar *
|
||||
|
||||
mdi = (CamelMaildirMessageInfo *)info;
|
||||
|
||||
/* what do we do if the message flags (and :info data) changes? filename mismatch - need to recheck I guess */
|
||||
name = g_strdup_printf("%s/cur/%s", lf->folder_path, mdi->filename);
|
||||
if ((message_stream = camel_stream_fs_new_with_name(name, O_RDONLY, 0)) == NULL) {
|
||||
camel_exception_setv(ex, CAMEL_EXCEPTION_FOLDER_INVALID_UID, _("Cannot get message: %s\n %s"),
|
||||
|
||||
@ -133,6 +133,71 @@ CamelMaildirSummary *camel_maildir_summary_new (const char *filename, const char
|
||||
return o;
|
||||
}
|
||||
|
||||
/* the 'standard' maildir flags. should be defined in sorted order. */
|
||||
static struct {
|
||||
char flag;
|
||||
guint32 flagbit;
|
||||
} flagbits[] = {
|
||||
{ 'F', CAMEL_MESSAGE_FLAGGED },
|
||||
{ 'R', CAMEL_MESSAGE_ANSWERED },
|
||||
{ 'S', CAMEL_MESSAGE_SEEN },
|
||||
{ 'T', CAMEL_MESSAGE_DELETED },
|
||||
};
|
||||
|
||||
/* convert the uid + flags into a unique:info maildir format */
|
||||
char *camel_maildir_summary_info_to_name(const CamelMessageInfo *info)
|
||||
{
|
||||
char *p, *buf;
|
||||
int i;
|
||||
|
||||
buf = alloca(strlen(info->uid) + strlen(":2,") + (sizeof(flagbits)/sizeof(flagbits[0])) + 1);
|
||||
p = buf + sprintf(buf, "%s:2,", info->uid);
|
||||
for (i=0;i<sizeof(flagbits)/sizeof(flagbits[0]);i++) {
|
||||
if (info->flags & flagbits[i].flagbit)
|
||||
*p++ = flagbits[i].flag;
|
||||
}
|
||||
*p = 0;
|
||||
|
||||
return g_strdup(buf);
|
||||
}
|
||||
|
||||
/* returns 0 if the info matches (or there was none), otherwise we changed it */
|
||||
int camel_maildir_summary_name_to_info(CamelMessageInfo *info, const char *name)
|
||||
{
|
||||
char *p, c;
|
||||
guint32 set = 0; /* what we set */
|
||||
/*guint32 all = 0;*/ /* all flags */
|
||||
int i;
|
||||
|
||||
p = strstr(name, ":2,");
|
||||
if (p) {
|
||||
p+=3;
|
||||
while ((c = *p++)) {
|
||||
/* we could assume that the flags are in order, but its just as easy not to require */
|
||||
for (i=0;i<sizeof(flagbits)/sizeof(flagbits[0]);i++) {
|
||||
if (flagbits[i].flag == c && (info->flags & flagbits[i].flagbit) == 0) {
|
||||
set |= flagbits[i].flagbit;
|
||||
}
|
||||
/*all |= flagbits[i].flagbit;*/
|
||||
}
|
||||
}
|
||||
|
||||
/* changed? */
|
||||
/*if ((info->flags & all) != set) {*/
|
||||
if ((info->flags & set) != set) {
|
||||
/* ok, they did change, only add the new flags ('merge flags'?) */
|
||||
/*info->flags &= all; if we wanted to set only the new flags, which we probably dont */
|
||||
info->flags |= set;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* FIXME: We need to also provide an encode/decode X-Evolution function, as the default
|
||||
is no good for us, and can screw up the uid info */
|
||||
|
||||
static CamelMessageInfo *message_info_new(CamelFolderSummary * s, struct _header_raw *h)
|
||||
{
|
||||
CamelMessageInfo *mi;
|
||||
@ -144,13 +209,41 @@ static CamelMessageInfo *message_info_new(CamelFolderSummary * s, struct _header
|
||||
if (mi) {
|
||||
mdi = (CamelMaildirMessageInfo *)mi;
|
||||
|
||||
mi->uid = camel_folder_summary_next_uid_string(s);
|
||||
if (mi->uid == NULL) {
|
||||
mi->uid = camel_folder_summary_next_uid_string(s);
|
||||
}
|
||||
|
||||
/* with maildir we know the real received date, from the filename */
|
||||
mi->date_received = strtoul(mi->uid, NULL, 10);
|
||||
|
||||
/* should store some status info in the filename, but we wont (yet) (fixme) */
|
||||
if (mds->priv->current_file) {
|
||||
#if 0
|
||||
char *p1, *p2, *p3;
|
||||
unsigned long uid;
|
||||
#endif
|
||||
/* if setting from a file, grab the flags from it */
|
||||
mdi->filename = g_strdup(mds->priv->current_file);
|
||||
camel_maildir_summary_name_to_info(mi, mdi->filename);
|
||||
|
||||
#if 0
|
||||
/* Actually, I dont think all this effort is worth it at all ... */
|
||||
|
||||
/* also, see if we can extract the next-id from tne name, and safe-if-fy ourselves against collisions */
|
||||
/* we check for something.something_number.something */
|
||||
p1 = strchr(mdi->filename, '.');
|
||||
if (p1) {
|
||||
p2 = strchr(p1+1, '.');
|
||||
p3 = strchr(p1+1, '_');
|
||||
if (p2 && p3 && p3<p2) {
|
||||
uid = strtoul(p3+1, &p1, 10);
|
||||
if (p1 == p2 && uid>0)
|
||||
camel_folder_summary_set_uid(s, uid);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
} else {
|
||||
mdi->filename = g_strdup_printf("%s:2,", mi->uid);
|
||||
/* if creating a file, set its name from the flags we have */
|
||||
mdi->filename = camel_maildir_summary_info_to_name(mi);
|
||||
}
|
||||
}
|
||||
|
||||
@ -160,9 +253,10 @@ static CamelMessageInfo *message_info_new(CamelFolderSummary * s, struct _header
|
||||
static char *maildir_summary_next_uid_string(CamelFolderSummary *s)
|
||||
{
|
||||
CamelMaildirSummary *mds = (CamelMaildirSummary *)s;
|
||||
/*CamelLocalSummary *cls = (CamelLocalSummary *)s;*/
|
||||
|
||||
/* current_file is more a current_filename, so map the filename to a uid */
|
||||
d(printf("next uid string called?\n"));
|
||||
|
||||
/* if we have a current file, then use that to get the uid */
|
||||
if (mds->priv->current_file) {
|
||||
char *cln;
|
||||
|
||||
@ -172,8 +266,33 @@ static char *maildir_summary_next_uid_string(CamelFolderSummary *s)
|
||||
else
|
||||
return g_strdup(mds->priv->current_file);
|
||||
} else {
|
||||
/* we use time.pid_count.hostname */
|
||||
/* the first would probably work, but just to be safe, check for collisions */
|
||||
#if 0
|
||||
return g_strdup_printf("%ld.%d_%u.%s", time(0), getpid(), camel_folder_summary_next_uid(s), mds->priv->hostname);
|
||||
#else
|
||||
CamelLocalSummary *cls = (CamelLocalSummary *)s;
|
||||
char *name = NULL, *uid = NULL;
|
||||
struct stat st;
|
||||
int retry = 0;
|
||||
guint32 nextuid = camel_folder_summary_next_uid(s);
|
||||
|
||||
/* we use time.pid_count.hostname */
|
||||
do {
|
||||
if (retry > 0) {
|
||||
g_free(name);
|
||||
g_free(uid);
|
||||
sleep(2);
|
||||
}
|
||||
uid = g_strdup_printf("%ld.%d_%u.%s", time(0), getpid(), nextuid, mds->priv->hostname);
|
||||
name = g_strdup_printf("%s/tmp/%s", cls->folder_path, uid);
|
||||
retry++;
|
||||
} while (stat(name, &st) == 0 && retry<3);
|
||||
|
||||
/* I dont know what we're supposed to do if it fails to find a unique name?? */
|
||||
|
||||
g_free(name);
|
||||
return uid;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@ -219,6 +338,21 @@ remove_summary(char *key, CamelMessageInfo *info, CamelLocalSummary *cls)
|
||||
camel_folder_summary_remove((CamelFolderSummary *)cls, info);
|
||||
}
|
||||
|
||||
static int
|
||||
sort_receive_cmp(const void *ap, const void *bp)
|
||||
{
|
||||
const CamelMessageInfo
|
||||
*a = *((CamelMessageInfo **)ap),
|
||||
*b = *((CamelMessageInfo **)bp);
|
||||
|
||||
if (a->date_received < b->date_received)
|
||||
return -1;
|
||||
else if (a->date_received > b->date_received)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
maildir_summary_check(CamelLocalSummary *cls, CamelFolderChangeInfo *changes, CamelException *ex)
|
||||
{
|
||||
@ -343,6 +477,9 @@ maildir_summary_check(CamelLocalSummary *cls, CamelFolderChangeInfo *changes, Ca
|
||||
g_free(new);
|
||||
g_free(cur);
|
||||
|
||||
/* sort the summary based on receive time, since the directory order is not useful */
|
||||
qsort(s->messages->pdata, s->messages->len, sizeof(CamelMessageInfo *), sort_receive_cmp);
|
||||
|
||||
/* FIXME: move this up a class? */
|
||||
|
||||
/* force a save of the index, just to make sure */
|
||||
@ -355,8 +492,7 @@ maildir_summary_check(CamelLocalSummary *cls, CamelFolderChangeInfo *changes, Ca
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* sync the summary with the ondisk files.
|
||||
It doesnt store the state in the file, the summary only, == MUCH faster */
|
||||
/* sync the summary with the ondisk files. */
|
||||
static int
|
||||
maildir_summary_sync(CamelLocalSummary *cls, gboolean expunge, CamelFolderChangeInfo *changes, CamelException *ex)
|
||||
{
|
||||
@ -364,20 +500,19 @@ maildir_summary_sync(CamelLocalSummary *cls, gboolean expunge, CamelFolderChange
|
||||
CamelMessageInfo *info;
|
||||
CamelMaildirMessageInfo *mdi;
|
||||
char *name;
|
||||
struct stat st;
|
||||
|
||||
d(printf("summary_sync(expunge=%s)\n", expunge?"true":"false"));
|
||||
|
||||
if (cls->index) {
|
||||
ibex_save(cls->index);
|
||||
}
|
||||
if (!expunge)
|
||||
return 0;
|
||||
|
||||
count = camel_folder_summary_count((CamelFolderSummary *)cls);
|
||||
for (i=count-1;i>=0;i--) {
|
||||
info = camel_folder_summary_index((CamelFolderSummary *)cls, i);
|
||||
if (info && info->flags & CAMEL_MESSAGE_DELETED) {
|
||||
mdi = (CamelMaildirMessageInfo *)info;
|
||||
mdi = (CamelMaildirMessageInfo *)info;
|
||||
if (info && (info->flags & CAMEL_MESSAGE_DELETED) && expunge) {
|
||||
name = g_strdup_printf("%s/cur/%s", cls->folder_path, mdi->filename);
|
||||
d(printf("deleting %s\n", name));
|
||||
if (unlink(name) == 0 || errno==ENOENT) {
|
||||
@ -389,6 +524,34 @@ maildir_summary_sync(CamelLocalSummary *cls, gboolean expunge, CamelFolderChange
|
||||
camel_folder_change_info_remove_uid(changes, info->uid);
|
||||
camel_folder_summary_remove((CamelFolderSummary *)cls, info);
|
||||
}
|
||||
g_free(name);
|
||||
} else if (info && (info->flags & CAMEL_MESSAGE_FOLDER_FLAGGED)) {
|
||||
char *newname = camel_maildir_summary_info_to_name(info);
|
||||
char *dest;
|
||||
|
||||
/* do we care about additional metainfo stored inside the message? */
|
||||
/* probably should all go in the filename? */
|
||||
|
||||
/* have our flags/ i.e. name changed? */
|
||||
if (strcmp(newname, mdi->filename)) {
|
||||
name = g_strdup_printf("%s/cur/%s", cls->folder_path, mdi->filename);
|
||||
dest = g_strdup_printf("%s/cur/%s", cls->folder_path, newname);
|
||||
rename(name, dest);
|
||||
if (stat(dest, &st) == -1) {
|
||||
/* we'll assume it didn't work, but dont change anything else */
|
||||
g_free(newname);
|
||||
} else {
|
||||
g_free(mdi->filename);
|
||||
mdi->filename = newname;
|
||||
}
|
||||
g_free(name);
|
||||
g_free(dest);
|
||||
} else {
|
||||
g_free(newname);
|
||||
}
|
||||
|
||||
/* strip FOLDER_MESSAGE_FLAGED, etc */
|
||||
info->flags &= 0xffff;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
|
||||
@ -59,5 +59,9 @@ struct _CamelMaildirSummaryClass {
|
||||
CamelType camel_maildir_summary_get_type (void);
|
||||
CamelMaildirSummary *camel_maildir_summary_new (const char *filename, const char *maildirdir, ibex *index);
|
||||
|
||||
/* convert some info->flags to/from the messageinfo */
|
||||
char *camel_maildir_summary_info_to_name(const CamelMessageInfo *info);
|
||||
int camel_maildir_summary_name_to_info(CamelMessageInfo *info, const char *name);
|
||||
|
||||
#endif /* ! _CAMEL_MAILDIR_SUMMARY_H */
|
||||
|
||||
|
||||
@ -51,6 +51,8 @@ static CamelLocalFolderClass *parent_class = NULL;
|
||||
#define CF_CLASS(so) CAMEL_FOLDER_CLASS (CAMEL_OBJECT_GET_CLASS(so))
|
||||
#define CMBOXS_CLASS(so) CAMEL_STORE_CLASS (CAMEL_OBJECT_GET_CLASS(so))
|
||||
|
||||
static int mbox_lock(CamelLocalFolder *lf, CamelLockType type, CamelException *ex);
|
||||
static void mbox_unlock(CamelLocalFolder *lf);
|
||||
|
||||
static void mbox_append_message(CamelFolder *folder, CamelMimeMessage * message, const CamelMessageInfo * info, CamelException *ex);
|
||||
static CamelMimeMessage *mbox_get_message(CamelFolder *folder, const gchar * uid, CamelException *ex);
|
||||
@ -73,19 +75,25 @@ camel_mbox_folder_class_init(CamelMboxFolderClass * camel_mbox_folder_class)
|
||||
camel_folder_class->get_message = mbox_get_message;
|
||||
|
||||
lclass->create_summary = mbox_create_summary;
|
||||
lclass->lock = mbox_lock;
|
||||
lclass->unlock = mbox_unlock;
|
||||
}
|
||||
|
||||
static void
|
||||
mbox_init(gpointer object, gpointer klass)
|
||||
{
|
||||
/*CamelFolder *folder = object;
|
||||
CamelMboxFolder *mbox_folder = object;*/
|
||||
/*CamelFolder *folder = object;*/
|
||||
CamelMboxFolder *mbox_folder = object;
|
||||
|
||||
mbox_folder->lockfd = -1;
|
||||
}
|
||||
|
||||
static void
|
||||
mbox_finalise(CamelObject * object)
|
||||
{
|
||||
/*CamelMboxFolder *mbox_folder = CAMEL_MBOX_FOLDER(object);*/
|
||||
CamelMboxFolder *mbox_folder = (CamelMboxFolder *)object;
|
||||
|
||||
g_assert(mbox_folder->lockfd == -1);
|
||||
}
|
||||
|
||||
CamelType camel_mbox_folder_get_type(void)
|
||||
@ -124,6 +132,32 @@ static CamelLocalSummary *mbox_create_summary(const char *path, const char *fold
|
||||
return (CamelLocalSummary *)camel_mbox_summary_new(path, folder, index);
|
||||
}
|
||||
|
||||
static int mbox_lock(CamelLocalFolder *lf, CamelLockType type, CamelException *ex)
|
||||
{
|
||||
CamelMboxFolder *mf = (CamelMboxFolder *)lf;
|
||||
|
||||
/* make sure we have matching unlocks for locks, camel-local-folder class should enforce this */
|
||||
g_assert(mf->lockfd == -1);
|
||||
|
||||
mf->lockfd = open(lf->folder_path, O_RDWR, 0);
|
||||
if (mf->lockfd == -1) {
|
||||
camel_exception_setv(ex, 1, "Cannot create folder lock on %s: %s", lf->folder_path, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
return camel_lock_folder(lf->folder_path, mf->lockfd, type, ex);
|
||||
}
|
||||
|
||||
static void mbox_unlock(CamelLocalFolder *lf)
|
||||
{
|
||||
CamelMboxFolder *mf = (CamelMboxFolder *)lf;
|
||||
|
||||
g_assert(mf->lockfd != -1);
|
||||
camel_unlock_folder(lf->folder_path, mf->lockfd);
|
||||
close(mf->lockfd);
|
||||
mf->lockfd = -1;
|
||||
}
|
||||
|
||||
static void
|
||||
mbox_append_message(CamelFolder *folder, CamelMimeMessage * message, const CamelMessageInfo * info, CamelException *ex)
|
||||
{
|
||||
@ -136,27 +170,28 @@ mbox_append_message(CamelFolder *folder, CamelMimeMessage * message, const Camel
|
||||
int fd;
|
||||
struct stat st;
|
||||
|
||||
/* FIXME: Locking */
|
||||
/* If we can't lock, dont do anything */
|
||||
if (camel_local_folder_lock(lf, CAMEL_LOCK_WRITE, ex) == -1)
|
||||
return;
|
||||
|
||||
d(printf("Appending message\n"));
|
||||
|
||||
/* first, check the summary is correct (updates folder_size too) */
|
||||
camel_local_summary_check(lf->summary, lf->changes, ex);
|
||||
if (camel_exception_is_set(ex))
|
||||
return;
|
||||
goto fail;
|
||||
|
||||
/* add it to the summary/assign the uid, etc */
|
||||
mi = camel_local_summary_add(lf->summary, message, info, lf->changes, ex);
|
||||
if (camel_exception_is_set(ex)) {
|
||||
return;
|
||||
}
|
||||
if (camel_exception_is_set(ex))
|
||||
goto fail;
|
||||
|
||||
d(printf("Appending message: uid is %s\n", mi->uid));
|
||||
|
||||
output_stream = camel_stream_fs_new_with_name(lf->folder_path, O_WRONLY|O_APPEND, 0600);
|
||||
if (output_stream == NULL) {
|
||||
camel_exception_setv(ex, 1, _("Cannot open mailbox: %s: %s\n"), lf->folder_path, strerror(errno));
|
||||
return;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* we must write this to the non-filtered stream ... prepend a \n if not at the start of the file */
|
||||
@ -174,12 +209,8 @@ mbox_append_message(CamelFolder *folder, CamelMimeMessage * message, const Camel
|
||||
if (camel_stream_close(filter_stream) == -1)
|
||||
goto fail_write;
|
||||
|
||||
/* now we 'fudge' the summary to tell it its uptodate, because its idea of uptodate has just changed */
|
||||
/* the stat really shouldn't fail, we just wrote to it */
|
||||
if (stat(lf->folder_path, &st) == 0) {
|
||||
mbs->folder_size = st.st_size;
|
||||
((CamelFolderSummary *)mbs)->time = st.st_mtime;
|
||||
}
|
||||
/* unlock as soon as we can */
|
||||
camel_local_folder_unlock(lf);
|
||||
|
||||
/* filter stream ref's the output stream itself, so we need to unref it too */
|
||||
camel_object_unref((CamelObject *)filter_from);
|
||||
@ -187,6 +218,13 @@ mbox_append_message(CamelFolder *folder, CamelMimeMessage * message, const Camel
|
||||
camel_object_unref((CamelObject *)output_stream);
|
||||
g_free(fromline);
|
||||
|
||||
/* now we 'fudge' the summary to tell it its uptodate, because its idea of uptodate has just changed */
|
||||
/* the stat really shouldn't fail, we just wrote to it */
|
||||
if (stat(lf->folder_path, &st) == 0) {
|
||||
mbs->folder_size = st.st_size;
|
||||
((CamelFolderSummary *)mbs)->time = st.st_mtime;
|
||||
}
|
||||
|
||||
if (camel_folder_change_info_changed(lf->changes)) {
|
||||
camel_object_trigger_event((CamelObject *)folder, "folder_changed", lf->changes);
|
||||
camel_folder_change_info_clear(lf->changes);
|
||||
@ -223,6 +261,10 @@ fail_write:
|
||||
((CamelFolderSummary *)mbs)->time = st.st_mtime;
|
||||
}
|
||||
|
||||
fail:
|
||||
/* make sure we unlock the folder - before we start triggering events into appland */
|
||||
camel_local_folder_unlock(lf);
|
||||
|
||||
/* cascade the changes through, anyway, if there are any outstanding */
|
||||
if (camel_folder_change_info_changed(lf->changes)) {
|
||||
camel_object_trigger_event((CamelObject *)folder, "folder_changed", lf->changes);
|
||||
@ -242,7 +284,9 @@ mbox_get_message(CamelFolder *folder, const gchar * uid, CamelException *ex)
|
||||
|
||||
d(printf("Getting message %s\n", uid));
|
||||
|
||||
/* FIXME: mbox locking */
|
||||
/* lock the folder first, burn if we can't */
|
||||
if (camel_local_folder_lock(lf, CAMEL_LOCK_READ, ex) == -1)
|
||||
return NULL;
|
||||
|
||||
retry:
|
||||
/* get the message summary info */
|
||||
@ -251,6 +295,7 @@ retry:
|
||||
if (info == NULL) {
|
||||
camel_exception_setv(ex, CAMEL_EXCEPTION_FOLDER_INVALID_UID,
|
||||
_("Cannot get message: %s\n %s"), uid, _("No such message"));
|
||||
camel_local_folder_unlock(lf);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@ -260,13 +305,15 @@ retry:
|
||||
|
||||
/* we use an fd instead of a normal stream here - the reason is subtle, camel_mime_part will cache
|
||||
the whole message in memory if the stream is non-seekable (which it is when built from a parser
|
||||
with no stream). This means we dont have to lock the mbox for the life of the message. */
|
||||
with no stream). This means we dont have to lock the mbox for the life of the message, but only
|
||||
while it is being created. */
|
||||
|
||||
fd = open(lf->folder_path, O_RDONLY);
|
||||
if (fd == -1) {
|
||||
camel_exception_setv(ex, CAMEL_EXCEPTION_FOLDER_INVALID_UID,
|
||||
_("Cannot get message: %s from folder %s\n %s"), uid, lf->folder_path,
|
||||
strerror(errno));
|
||||
camel_local_folder_unlock(lf);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@ -296,6 +343,7 @@ retry:
|
||||
_("Cannot get message: %s from folder %s\n %s"), uid, lf->folder_path,
|
||||
_("The folder appears to be irrecoverably corrupted."));
|
||||
|
||||
camel_local_folder_unlock(lf);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@ -307,9 +355,13 @@ retry:
|
||||
_("Message construction failed: Corrupt mailbox?"));
|
||||
camel_object_unref((CamelObject *)parser);
|
||||
camel_object_unref((CamelObject *)message);
|
||||
camel_local_folder_unlock(lf);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* and unlock now we're finished with it */
|
||||
camel_local_folder_unlock(lf);
|
||||
|
||||
camel_object_unref((CamelObject *)parser);
|
||||
|
||||
/* use the opportunity to notify of changes (particularly if we had a rebuild) */
|
||||
|
||||
@ -39,6 +39,7 @@ extern "C" {
|
||||
typedef struct {
|
||||
CamelLocalFolder parent_object;
|
||||
|
||||
int lockfd; /* for when we have a lock on the folder */
|
||||
} CamelMboxFolder;
|
||||
|
||||
typedef struct {
|
||||
|
||||
@ -127,7 +127,7 @@ static void mh_append_message(CamelFolder * folder, CamelMimeMessage * message,
|
||||
CamelMessageInfo *mi;
|
||||
char *name;
|
||||
|
||||
/* FIXME: probably needs additional locking */
|
||||
/* FIXME: probably needs additional locking (although mh doesn't appear do do it) */
|
||||
|
||||
d(printf("Appending message\n"));
|
||||
|
||||
|
||||
Reference in New Issue
Block a user