Rewritten to use a cache for body searches when online. Will need some
2002-01-14 Not Zed <NotZed@Ximian.com> * providers/imap/camel-imap-search.c (imap_body_contains): Rewritten to use a cache for body searches when online. Will need some heavy testing but so far seems to be beneficial. * providers/imap/camel-imap-folder.c (imap_search_by_expression, search_by_uids): dont initialise search object here. (camel_imap_folder_new): Setup search object here with pointer to cache dir. 2001-12-01 Not Zed <NotZed@Ximian.com> * camel-store-summary.[ch]: New class to store a store's folder list in. Not yet completed. svn path=/trunk/; revision=15314
This commit is contained in:
@ -1,3 +1,19 @@
|
||||
2002-01-14 Not Zed <NotZed@Ximian.com>
|
||||
|
||||
* providers/imap/camel-imap-search.c (imap_body_contains):
|
||||
Rewritten to use a cache for body searches when online. Will need
|
||||
some heavy testing but so far seems to be beneficial.
|
||||
|
||||
* providers/imap/camel-imap-folder.c (imap_search_by_expression,
|
||||
search_by_uids): dont initialise search object here.
|
||||
(camel_imap_folder_new): Setup search object here with pointer to
|
||||
cache dir.
|
||||
|
||||
2001-12-01 Not Zed <NotZed@Ximian.com>
|
||||
|
||||
* camel-store-summary.[ch]: New class to store a store's folder
|
||||
list in. Not yet completed.
|
||||
|
||||
2002-01-11 Jeffrey Stedfast <fejj@ximian.com>
|
||||
|
||||
* providers/imap/camel-imap-folder.c (imap_update_summary): Kludge
|
||||
|
@ -86,6 +86,7 @@ libcamel_la_SOURCES = \
|
||||
camel-service.c \
|
||||
camel-session.c \
|
||||
camel-store.c \
|
||||
camel-store-summary.c \
|
||||
camel-stream-buffer.c \
|
||||
camel-stream-filter.c \
|
||||
camel-stream-fs.c \
|
||||
@ -172,6 +173,7 @@ libcamelinclude_HEADERS = \
|
||||
camel-service.h \
|
||||
camel-session.h \
|
||||
camel-store.h \
|
||||
camel-store-summary.h \
|
||||
camel-stream-buffer.h \
|
||||
camel-stream-filter.h \
|
||||
camel-stream-fs.h \
|
||||
|
@ -164,6 +164,23 @@ struct _CamelFolderSummaryPrivate {
|
||||
#define CAMEL_SUMMARY_UNLOCK(f, l)
|
||||
#endif
|
||||
|
||||
struct _CamelStoreSummaryPrivate {
|
||||
#ifdef ENABLE_THREADS
|
||||
GMutex *summary_lock; /* for the summary hashtable/array */
|
||||
GMutex *io_lock; /* load/save lock, for access to saved_count, etc */
|
||||
GMutex *alloc_lock; /* for setting up and using allocators */
|
||||
GMutex *ref_lock; /* for reffing/unreffing messageinfo's ALWAYS obtain before summary_lock */
|
||||
#endif
|
||||
};
|
||||
|
||||
#ifdef ENABLE_THREADS
|
||||
#define CAMEL_STORE_SUMMARY_LOCK(f, l) (g_mutex_lock(((CamelStoreSummary *)f)->priv->l))
|
||||
#define CAMEL_STORE_SUMMARY_UNLOCK(f, l) (g_mutex_unlock(((CamelStoreSummary *)f)->priv->l))
|
||||
#else
|
||||
#define CAMEL_STORE_SUMMARY_LOCK(f, l)
|
||||
#define CAMEL_STORE_SUMMARY_UNLOCK(f, l)
|
||||
#endif
|
||||
|
||||
struct _CamelVeeFolderPrivate {
|
||||
GList *folders; /* lock using subfolder_lock before changing/accessing */
|
||||
GList *folders_changed; /* for list of folders that have changed between updates */
|
||||
|
899
camel/camel-store-summary.c
Normal file
899
camel/camel-store-summary.c
Normal file
@ -0,0 +1,899 @@
|
||||
/*
|
||||
* Copyright (C) 2001 Ximian Inc.
|
||||
*
|
||||
* Authors: Michael Zucchi <notzed@ximian.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of version 2 of the GNU General Public
|
||||
* License as published by the Free Software Foundation.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <unistd.h>
|
||||
#include <ctype.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "camel-store-summary.h"
|
||||
|
||||
#include "camel-file-utils.h"
|
||||
|
||||
#include "hash-table-utils.h"
|
||||
#include "e-util/md5-utils.h"
|
||||
#include "e-util/e-memory.h"
|
||||
|
||||
#include "camel-private.h"
|
||||
|
||||
#define d(x)
|
||||
#define io(x) /* io debug */
|
||||
|
||||
#define CAMEL_STORE_SUMMARY_VERSION (13)
|
||||
|
||||
#define _PRIVATE(o) (((CamelStoreSummary *)(o))->priv)
|
||||
|
||||
static int summary_header_load(CamelStoreSummary *, FILE *);
|
||||
static int summary_header_save(CamelStoreSummary *, FILE *);
|
||||
|
||||
static CamelFolderInfo * folder_info_new(CamelStoreSummary *, const char *);
|
||||
static CamelFolderInfo * folder_info_load(CamelStoreSummary *, FILE *);
|
||||
static int folder_info_save(CamelStoreSummary *, FILE *, CamelFolderInfo *);
|
||||
static void folder_info_free(CamelStoreSummary *, CamelFolderInfo *);
|
||||
|
||||
static const char *folder_info_string(CamelStoreSummary *, const CamelFolderInfo *, int);
|
||||
static void folder_info_set_string(CamelStoreSummary *, CamelFolderInfo *, int, const char *);
|
||||
|
||||
static void camel_store_summary_class_init (CamelStoreSummaryClass *klass);
|
||||
static void camel_store_summary_init (CamelStoreSummary *obj);
|
||||
static void camel_store_summary_finalise (CamelObject *obj);
|
||||
|
||||
static CamelObjectClass *camel_store_summary_parent;
|
||||
|
||||
static void
|
||||
camel_store_summary_class_init (CamelStoreSummaryClass *klass)
|
||||
{
|
||||
camel_store_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->folder_info_new = folder_info_new;
|
||||
klass->folder_info_load = folder_info_load;
|
||||
klass->folder_info_save = folder_info_save;
|
||||
klass->folder_info_free = folder_info_free;
|
||||
|
||||
klass->folder_info_string = folder_info_string;
|
||||
klass->folder_info_set_string = folder_info_set_string;
|
||||
}
|
||||
|
||||
static void
|
||||
camel_store_summary_init (CamelStoreSummary *s)
|
||||
{
|
||||
struct _CamelStoreSummaryPrivate *p;
|
||||
|
||||
p = _PRIVATE(s) = g_malloc0(sizeof(*p));
|
||||
|
||||
s->folder_info_size = sizeof(CamelFolderInfo);
|
||||
|
||||
s->folder_info_chunks = NULL;
|
||||
|
||||
s->version = CAMEL_STORE_SUMMARY_VERSION;
|
||||
s->flags = 0;
|
||||
s->count = 0;
|
||||
s->time = 0;
|
||||
|
||||
s->folders = g_ptr_array_new();
|
||||
s->folders_full = g_hash_table_new(g_str_hash, g_str_equal);
|
||||
|
||||
#ifdef ENABLE_THREADS
|
||||
p->summary_lock = g_mutex_new();
|
||||
p->io_lock = g_mutex_new();
|
||||
p->alloc_lock = g_mutex_new();
|
||||
p->ref_lock = g_mutex_new();
|
||||
#endif
|
||||
}
|
||||
|
||||
static void
|
||||
camel_store_summary_finalise (CamelObject *obj)
|
||||
{
|
||||
struct _CamelStoreSummaryPrivate *p;
|
||||
CamelStoreSummary *s = (CamelStoreSummary *)obj;
|
||||
|
||||
p = _PRIVATE(obj);
|
||||
|
||||
camel_store_summary_clear(s);
|
||||
g_ptr_array_free(s->folders, TRUE);
|
||||
g_hash_table_destroy(s->folders_full);
|
||||
|
||||
g_free(s->summary_path);
|
||||
|
||||
if (s->folder_info_chunks)
|
||||
e_memchunk_destroy(s->folder_info_chunks);
|
||||
|
||||
#ifdef ENABLE_THREADS
|
||||
g_mutex_free(p->summary_lock);
|
||||
g_mutex_free(p->io_lock);
|
||||
g_mutex_free(p->alloc_lock);
|
||||
g_mutex_free(p->ref_lock);
|
||||
#endif
|
||||
|
||||
g_free(p);
|
||||
}
|
||||
|
||||
CamelType
|
||||
camel_store_summary_get_type (void)
|
||||
{
|
||||
static CamelType type = CAMEL_INVALID_TYPE;
|
||||
|
||||
if (type == CAMEL_INVALID_TYPE) {
|
||||
type = camel_type_register (camel_object_get_type (), "CamelStoreSummary",
|
||||
sizeof (CamelStoreSummary),
|
||||
sizeof (CamelStoreSummaryClass),
|
||||
(CamelObjectClassInitFunc) camel_store_summary_class_init,
|
||||
NULL,
|
||||
(CamelObjectInitFunc) camel_store_summary_init,
|
||||
(CamelObjectFinalizeFunc) camel_store_summary_finalise);
|
||||
}
|
||||
|
||||
return type;
|
||||
}
|
||||
|
||||
/**
|
||||
* camel_store_summary_new:
|
||||
*
|
||||
* Create a new CamelStoreSummary object.
|
||||
*
|
||||
* Return value: A new CamelStoreSummary widget.
|
||||
**/
|
||||
CamelStoreSummary *
|
||||
camel_store_summary_new (void)
|
||||
{
|
||||
CamelStoreSummary *new = CAMEL_STORE_SUMMARY ( camel_object_new (camel_store_summary_get_type ())); return new;
|
||||
}
|
||||
|
||||
/**
|
||||
* camel_store_summary_set_filename:
|
||||
* @s:
|
||||
* @name:
|
||||
*
|
||||
* Set the filename where the summary will be loaded to/saved from.
|
||||
**/
|
||||
void camel_store_summary_set_filename(CamelStoreSummary *s, const char *name)
|
||||
{
|
||||
CAMEL_STORE_SUMMARY_LOCK(s, summary_lock);
|
||||
|
||||
g_free(s->summary_path);
|
||||
s->summary_path = g_strdup(name);
|
||||
|
||||
CAMEL_STORE_SUMMARY_UNLOCK(s, summary_lock);
|
||||
}
|
||||
|
||||
void camel_store_summary_set_uri_prefix(CamelStoreSummary *s, const char *prefix)
|
||||
{
|
||||
CAMEL_STORE_SUMMARY_LOCK(s, summary_lock);
|
||||
|
||||
g_free(s->uri_prefix);
|
||||
s->uri_prefix = g_strdup(prefix);
|
||||
|
||||
CAMEL_STORE_SUMMARY_UNLOCK(s, summary_lock);
|
||||
}
|
||||
|
||||
/**
|
||||
* camel_store_summary_count:
|
||||
* @s:
|
||||
*
|
||||
* Get the number of summary items stored in this summary.
|
||||
*
|
||||
* Return value: The number of items int he summary.
|
||||
**/
|
||||
int
|
||||
camel_store_summary_count(CamelStoreSummary *s)
|
||||
{
|
||||
return s->folders->len;
|
||||
}
|
||||
|
||||
/**
|
||||
* camel_store_summary_index:
|
||||
* @s:
|
||||
* @i:
|
||||
*
|
||||
* Retrieve a summary item by index number.
|
||||
*
|
||||
* A referenced to the summary item is returned, which may be
|
||||
* ref'd or free'd as appropriate.
|
||||
*
|
||||
* Return value: The summary item, or NULL if the index @i is out
|
||||
* of range.
|
||||
* It must be freed using camel_store_summary_info_free().
|
||||
**/
|
||||
CamelFolderInfo *
|
||||
camel_store_summary_index(CamelStoreSummary *s, int i)
|
||||
{
|
||||
CamelFolderInfo *info = NULL;
|
||||
|
||||
CAMEL_STORE_SUMMARY_LOCK(s, ref_lock);
|
||||
CAMEL_STORE_SUMMARY_LOCK(s, summary_lock);
|
||||
|
||||
if (i<s->folders->len)
|
||||
info = g_ptr_array_index(s->folders, i);
|
||||
|
||||
CAMEL_STORE_SUMMARY_UNLOCK(s, summary_lock);
|
||||
|
||||
if (info)
|
||||
info->refcount++;
|
||||
|
||||
CAMEL_STORE_SUMMARY_UNLOCK(s, ref_lock);
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
/**
|
||||
* camel_store_summary_index:
|
||||
* @s:
|
||||
* @i:
|
||||
*
|
||||
* Obtain a copy of the summary array. This is done atomically,
|
||||
* so cannot contain empty entries.
|
||||
*
|
||||
* It must be freed using camel_store_summary_array_free().
|
||||
**/
|
||||
GPtrArray *
|
||||
camel_store_summary_array(CamelStoreSummary *s)
|
||||
{
|
||||
CamelFolderInfo *info;
|
||||
GPtrArray *res = g_ptr_array_new();
|
||||
int i;
|
||||
|
||||
CAMEL_STORE_SUMMARY_LOCK(s, ref_lock);
|
||||
CAMEL_STORE_SUMMARY_LOCK(s, summary_lock);
|
||||
|
||||
g_ptr_array_set_size(res, s->folders->len);
|
||||
for (i=0;i<s->folders->len;i++) {
|
||||
info = res->pdata[i] = g_ptr_array_index(s->folders, i);
|
||||
info->refcount++;
|
||||
}
|
||||
|
||||
CAMEL_STORE_SUMMARY_UNLOCK(s, summary_lock);
|
||||
CAMEL_STORE_SUMMARY_UNLOCK(s, ref_lock);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* camel_store_summary_array_free:
|
||||
* @s:
|
||||
* @array:
|
||||
*
|
||||
* Free the folder summary array.
|
||||
**/
|
||||
void
|
||||
camel_store_summary_array_free(CamelStoreSummary *s, GPtrArray *array)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i=0;i<array->len;i++)
|
||||
camel_store_summary_info_free(s, array->pdata[i]);
|
||||
|
||||
g_ptr_array_free(array, TRUE);
|
||||
}
|
||||
|
||||
/**
|
||||
* camel_store_summary_full:
|
||||
* @s:
|
||||
* @full:
|
||||
*
|
||||
* Retrieve a summary item by full name.
|
||||
*
|
||||
* A referenced to the summary item is returned, which may be
|
||||
* ref'd or free'd as appropriate.
|
||||
*
|
||||
* Return value: The summary item, or NULL if the @full name
|
||||
* is not available.
|
||||
* It must be freed using camel_store_summary_info_free().
|
||||
**/
|
||||
CamelFolderInfo *
|
||||
camel_store_summary_full(CamelStoreSummary *s, const char *full)
|
||||
{
|
||||
CamelFolderInfo *info;
|
||||
|
||||
CAMEL_STORE_SUMMARY_LOCK(s, ref_lock);
|
||||
CAMEL_STORE_SUMMARY_LOCK(s, summary_lock);
|
||||
|
||||
info = g_hash_table_lookup(s->folders_full, full);
|
||||
|
||||
CAMEL_STORE_SUMMARY_UNLOCK(s, summary_lock);
|
||||
|
||||
if (info)
|
||||
info->refcount++;
|
||||
|
||||
CAMEL_STORE_SUMMARY_UNLOCK(s, ref_lock);
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
int
|
||||
camel_store_summary_load(CamelStoreSummary *s)
|
||||
{
|
||||
FILE *in;
|
||||
int i;
|
||||
CamelFolderInfo *mi;
|
||||
|
||||
g_assert(s->summary_path);
|
||||
|
||||
in = fopen(s->summary_path, "r");
|
||||
if (in == NULL)
|
||||
return -1;
|
||||
|
||||
CAMEL_STORE_SUMMARY_LOCK(s, io_lock);
|
||||
if ( ((CamelStoreSummaryClass *)(CAMEL_OBJECT_GET_CLASS(s)))->summary_header_load(s, in) == -1)
|
||||
goto error;
|
||||
|
||||
/* now read in each message ... */
|
||||
for (i=0;i<s->count;i++) {
|
||||
mi = ((CamelStoreSummaryClass *)(CAMEL_OBJECT_GET_CLASS(s)))->folder_info_load(s, in);
|
||||
|
||||
if (mi == NULL)
|
||||
goto error;
|
||||
|
||||
camel_store_summary_add(s, mi);
|
||||
}
|
||||
|
||||
CAMEL_STORE_SUMMARY_UNLOCK(s, io_lock);
|
||||
|
||||
if (fclose(in) == -1)
|
||||
return -1;
|
||||
|
||||
s->flags &= ~CAMEL_STORE_SUMMARY_DIRTY;
|
||||
|
||||
return 0;
|
||||
|
||||
error:
|
||||
g_warning("Cannot load summary file: %s", strerror(ferror(in)));
|
||||
CAMEL_STORE_SUMMARY_UNLOCK(s, io_lock);
|
||||
fclose(in);
|
||||
s->flags |= ~CAMEL_STORE_SUMMARY_DIRTY;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* camel_store_summary_save:
|
||||
* @s:
|
||||
*
|
||||
* Writes the summary to disk. The summary is only written if changes
|
||||
* have occured.
|
||||
*
|
||||
* Return value: Returns -1 on error.
|
||||
**/
|
||||
int
|
||||
camel_store_summary_save(CamelStoreSummary *s)
|
||||
{
|
||||
FILE *out;
|
||||
int fd;
|
||||
int i;
|
||||
guint32 count;
|
||||
CamelFolderInfo *mi;
|
||||
|
||||
g_assert(s->summary_path);
|
||||
|
||||
if ((s->flags & CAMEL_STORE_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"));
|
||||
|
||||
CAMEL_STORE_SUMMARY_LOCK(s, io_lock);
|
||||
|
||||
if ( ((CamelStoreSummaryClass *)(CAMEL_OBJECT_GET_CLASS(s)))->summary_header_save(s, out) == -1) {
|
||||
fclose(out);
|
||||
CAMEL_STORE_SUMMARY_UNLOCK(s, io_lock);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* now write out each message ... */
|
||||
|
||||
/* FIXME: Locking? */
|
||||
|
||||
count = s->folders->len;
|
||||
for (i=0;i<count;i++) {
|
||||
mi = s->folders->pdata[i];
|
||||
((CamelStoreSummaryClass *)(CAMEL_OBJECT_GET_CLASS(s)))->folder_info_save(s, out, mi);
|
||||
}
|
||||
|
||||
CAMEL_STORE_SUMMARY_UNLOCK(s, io_lock);
|
||||
|
||||
if (fclose(out) == -1)
|
||||
return -1;
|
||||
|
||||
s->flags &= ~CAMEL_STORE_SUMMARY_DIRTY;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* camel_store_summary_header_load:
|
||||
* @s: Summary object.
|
||||
*
|
||||
* Only load the header information from the summary,
|
||||
* keep the rest on disk. This should only be done on
|
||||
* a fresh summary object.
|
||||
*
|
||||
* Return value: -1 on error.
|
||||
**/
|
||||
int camel_store_summary_header_load(CamelStoreSummary *s)
|
||||
{
|
||||
FILE *in;
|
||||
int ret;
|
||||
|
||||
g_assert(s->summary_path);
|
||||
|
||||
in = fopen(s->summary_path, "r");
|
||||
if (in == NULL)
|
||||
return -1;
|
||||
|
||||
CAMEL_STORE_SUMMARY_LOCK(s, io_lock);
|
||||
ret = ((CamelStoreSummaryClass *)(CAMEL_OBJECT_GET_CLASS(s)))->summary_header_load(s, in);
|
||||
CAMEL_STORE_SUMMARY_UNLOCK(s, io_lock);
|
||||
|
||||
fclose(in);
|
||||
s->flags &= ~CAMEL_STORE_SUMMARY_DIRTY;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* camel_store_summary_add:
|
||||
* @s:
|
||||
* @info:
|
||||
*
|
||||
* Adds a new @info record to the summary. If @info->uid is NULL, then a new
|
||||
* uid is automatically re-assigned by calling :next_uid_string().
|
||||
*
|
||||
* The @info record should have been generated by calling one of the
|
||||
* info_new_*() functions, as it will be free'd based on the summary
|
||||
* class. And MUST NOT be allocated directly using malloc.
|
||||
**/
|
||||
void camel_store_summary_add(CamelStoreSummary *s, CamelFolderInfo *info)
|
||||
{
|
||||
if (info == NULL)
|
||||
return;
|
||||
|
||||
if (camel_folder_info_full(s, info) == NULL) {
|
||||
g_warning("Trying to add a folder info with missing required full name\n");
|
||||
return;
|
||||
}
|
||||
|
||||
CAMEL_STORE_SUMMARY_LOCK(s, summary_lock);
|
||||
|
||||
g_ptr_array_add(s->folders, info);
|
||||
g_hash_table_insert(s->folders_full, (char *)camel_folder_info_full(s, info), info);
|
||||
s->flags |= CAMEL_STORE_SUMMARY_DIRTY;
|
||||
|
||||
CAMEL_STORE_SUMMARY_UNLOCK(s, summary_lock);
|
||||
}
|
||||
|
||||
/**
|
||||
* camel_store_summary_add_from_full:
|
||||
* @s:
|
||||
* @h:
|
||||
*
|
||||
* Build a new info record based on the name, and add it to the summary.
|
||||
*
|
||||
* Return value: The newly added record.
|
||||
**/
|
||||
CamelFolderInfo *camel_store_summary_add_from_full(CamelStoreSummary *s, const char *full)
|
||||
{
|
||||
CamelFolderInfo *info;
|
||||
|
||||
CAMEL_STORE_SUMMARY_LOCK(s, summary_lock);
|
||||
|
||||
info = g_hash_table_lookup(s->folders_full, full);
|
||||
if (info != NULL) {
|
||||
g_warning("Trying to add folder '%s' to summary that already has it", full);
|
||||
info = NULL;
|
||||
} else {
|
||||
info = camel_store_summary_info_new_from_full(s, full);
|
||||
g_ptr_array_add(s->folders, info);
|
||||
g_hash_table_insert(s->folders_full, (char *)camel_folder_info_full(s, info), info);
|
||||
s->flags |= CAMEL_STORE_SUMMARY_DIRTY;
|
||||
}
|
||||
|
||||
CAMEL_STORE_SUMMARY_UNLOCK(s, summary_lock);
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
/**
|
||||
* camel_store_summary_info_new_from_full:
|
||||
* @s:
|
||||
* @h:
|
||||
*
|
||||
* Create a new info record from a name.
|
||||
*
|
||||
* Return value: Guess? This info record MUST be freed using
|
||||
* camel_store_summary_info_free(), camel_folder_info_free() will not work.
|
||||
**/
|
||||
CamelFolderInfo *camel_store_summary_info_new_from_full(CamelStoreSummary *s, const char *f)
|
||||
{
|
||||
return ((CamelStoreSummaryClass *)(CAMEL_OBJECT_GET_CLASS(s))) -> folder_info_new(s, f);
|
||||
}
|
||||
|
||||
/**
|
||||
* camel_store_summary_info_free:
|
||||
* @s:
|
||||
* @mi:
|
||||
*
|
||||
* Unref and potentially free the message info @mi, and all associated memory.
|
||||
**/
|
||||
void camel_store_summary_info_free(CamelStoreSummary *s, CamelFolderInfo *mi)
|
||||
{
|
||||
g_assert(mi);
|
||||
g_assert(s);
|
||||
|
||||
CAMEL_STORE_SUMMARY_LOCK(s, ref_lock);
|
||||
|
||||
g_assert(mi->refcount >= 1);
|
||||
|
||||
mi->refcount--;
|
||||
if (mi->refcount > 0) {
|
||||
CAMEL_STORE_SUMMARY_UNLOCK(s, ref_lock);
|
||||
return;
|
||||
}
|
||||
|
||||
CAMEL_STORE_SUMMARY_UNLOCK(s, ref_lock);
|
||||
|
||||
((CamelStoreSummaryClass *)(CAMEL_OBJECT_GET_CLASS(s)))->folder_info_free(s, mi);
|
||||
}
|
||||
|
||||
/**
|
||||
* camel_store_summary_info_ref:
|
||||
* @s:
|
||||
* @mi:
|
||||
*
|
||||
* Add an extra reference to @mi.
|
||||
**/
|
||||
void camel_store_summary_info_ref(CamelStoreSummary *s, CamelFolderInfo *mi)
|
||||
{
|
||||
g_assert(mi);
|
||||
g_assert(s);
|
||||
|
||||
CAMEL_STORE_SUMMARY_LOCK(s, ref_lock);
|
||||
g_assert(mi->refcount >= 1);
|
||||
mi->refcount++;
|
||||
CAMEL_STORE_SUMMARY_UNLOCK(s, ref_lock);
|
||||
}
|
||||
|
||||
/**
|
||||
* camel_store_summary_touch:
|
||||
* @s:
|
||||
*
|
||||
* Mark the summary as changed, so that a save will save it.
|
||||
**/
|
||||
void
|
||||
camel_store_summary_touch(CamelStoreSummary *s)
|
||||
{
|
||||
CAMEL_STORE_SUMMARY_LOCK(s, summary_lock);
|
||||
s->flags |= CAMEL_STORE_SUMMARY_DIRTY;
|
||||
CAMEL_STORE_SUMMARY_UNLOCK(s, summary_lock);
|
||||
}
|
||||
|
||||
/**
|
||||
* camel_store_summary_clear:
|
||||
* @s:
|
||||
*
|
||||
* Empty the summary contents.
|
||||
**/
|
||||
void
|
||||
camel_store_summary_clear(CamelStoreSummary *s)
|
||||
{
|
||||
int i;
|
||||
|
||||
CAMEL_STORE_SUMMARY_LOCK(s, summary_lock);
|
||||
if (camel_store_summary_count(s) == 0) {
|
||||
CAMEL_STORE_SUMMARY_UNLOCK(s, summary_lock);
|
||||
return;
|
||||
}
|
||||
|
||||
for (i=0;i<s->folders->len;i++)
|
||||
camel_store_summary_info_free(s, s->folders->pdata[i]);
|
||||
|
||||
g_ptr_array_set_size(s->folders, 0);
|
||||
g_hash_table_destroy(s->folders_full);
|
||||
s->folders_full = g_hash_table_new(g_str_hash, g_str_equal);
|
||||
s->flags |= CAMEL_STORE_SUMMARY_DIRTY;
|
||||
CAMEL_STORE_SUMMARY_UNLOCK(s, summary_lock);
|
||||
}
|
||||
|
||||
/**
|
||||
* camel_store_summary_remove:
|
||||
* @s:
|
||||
* @info:
|
||||
*
|
||||
* Remove a specific @info record from the summary.
|
||||
**/
|
||||
void camel_store_summary_remove(CamelStoreSummary *s, CamelFolderInfo *info)
|
||||
{
|
||||
CAMEL_STORE_SUMMARY_LOCK(s, summary_lock);
|
||||
g_hash_table_remove(s->folders_full, camel_folder_info_full(s, info));
|
||||
g_ptr_array_remove(s->folders, info);
|
||||
s->flags |= CAMEL_STORE_SUMMARY_DIRTY;
|
||||
CAMEL_STORE_SUMMARY_UNLOCK(s, summary_lock);
|
||||
|
||||
camel_store_summary_info_free(s, info);
|
||||
}
|
||||
|
||||
/**
|
||||
* camel_store_summary_remove_uid:
|
||||
* @s:
|
||||
* @full:
|
||||
*
|
||||
* Remove a specific info record from the summary, by @full.
|
||||
**/
|
||||
void camel_store_summary_remove_full(CamelStoreSummary *s, const char *full)
|
||||
{
|
||||
CamelFolderInfo *oldinfo;
|
||||
char *oldfull;
|
||||
|
||||
CAMEL_STORE_SUMMARY_LOCK(s, ref_lock);
|
||||
CAMEL_STORE_SUMMARY_LOCK(s, summary_lock);
|
||||
if (g_hash_table_lookup_extended(s->folders_full, full, (void *)&oldfull, (void *)&oldinfo)) {
|
||||
/* make sure it doesn't vanish while we're removing it */
|
||||
oldinfo->refcount++;
|
||||
CAMEL_STORE_SUMMARY_UNLOCK(s, summary_lock);
|
||||
CAMEL_STORE_SUMMARY_UNLOCK(s, ref_lock);
|
||||
camel_store_summary_remove(s, oldinfo);
|
||||
camel_store_summary_info_free(s, oldinfo);
|
||||
} else {
|
||||
CAMEL_STORE_SUMMARY_UNLOCK(s, summary_lock);
|
||||
CAMEL_STORE_SUMMARY_UNLOCK(s, ref_lock);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* camel_store_summary_remove_index:
|
||||
* @s:
|
||||
* @index:
|
||||
*
|
||||
* Remove a specific info record from the summary, by index.
|
||||
**/
|
||||
void camel_store_summary_remove_index(CamelStoreSummary *s, int index)
|
||||
{
|
||||
CAMEL_STORE_SUMMARY_LOCK(s, summary_lock);
|
||||
if (index < s->folders->len) {
|
||||
CamelFolderInfo *info = s->folders->pdata[index];
|
||||
|
||||
g_hash_table_remove(s->folders_full, camel_folder_info_full(s, info));
|
||||
g_ptr_array_remove_index(s->folders, index);
|
||||
s->flags |= CAMEL_STORE_SUMMARY_DIRTY;
|
||||
|
||||
CAMEL_STORE_SUMMARY_UNLOCK(s, summary_lock);
|
||||
camel_store_summary_info_free(s, info);
|
||||
} else {
|
||||
CAMEL_STORE_SUMMARY_UNLOCK(s, summary_lock);
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
summary_header_load(CamelStoreSummary *s, FILE *in)
|
||||
{
|
||||
gint32 version, flags, count;
|
||||
time_t time;
|
||||
|
||||
fseek(in, 0, SEEK_SET);
|
||||
|
||||
io(printf("Loading header\n"));
|
||||
|
||||
if (camel_file_util_decode_fixed_int32(in, &version) == -1
|
||||
|| camel_file_util_decode_fixed_int32(in, &flags) == -1
|
||||
|| camel_file_util_decode_time_t(in, &time) == -1
|
||||
|| camel_file_util_decode_fixed_int32(in, &count) == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
s->flags = flags;
|
||||
s->time = time;
|
||||
s->count = count;
|
||||
if (s->version != version) {
|
||||
g_warning("Summary header version mismatch");
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
summary_header_save(CamelStoreSummary *s, FILE *out)
|
||||
{
|
||||
fseek(out, 0, SEEK_SET);
|
||||
|
||||
io(printf("Savining header\n"));
|
||||
|
||||
camel_file_util_encode_fixed_int32(out, s->version);
|
||||
camel_file_util_encode_fixed_int32(out, s->flags);
|
||||
camel_file_util_encode_time_t(out, s->time);
|
||||
return camel_file_util_encode_fixed_int32(out, camel_store_summary_count(s));
|
||||
}
|
||||
|
||||
/**
|
||||
* camel_store_summary_info_new:
|
||||
* @s:
|
||||
*
|
||||
* Allocate a new camel message info, suitable for adding
|
||||
* to this summary.
|
||||
*
|
||||
* Return value:
|
||||
**/
|
||||
CamelFolderInfo *
|
||||
camel_store_summary_info_new(CamelStoreSummary *s)
|
||||
{
|
||||
CamelFolderInfo *mi;
|
||||
|
||||
CAMEL_STORE_SUMMARY_LOCK(s, alloc_lock);
|
||||
if (s->folder_info_chunks == NULL)
|
||||
s->folder_info_chunks = e_memchunk_new(32, s->folder_info_size);
|
||||
mi = e_memchunk_alloc0(s->folder_info_chunks);
|
||||
CAMEL_STORE_SUMMARY_UNLOCK(s, alloc_lock);
|
||||
mi->refcount = 1;
|
||||
return mi;
|
||||
}
|
||||
|
||||
const char *camel_folder_info_string(CamelStoreSummary *s, const CamelFolderInfo *mi, int type)
|
||||
{
|
||||
return ((CamelStoreSummaryClass *)(CAMEL_OBJECT_GET_CLASS(s)))->folder_info_string(s, mi, type);
|
||||
}
|
||||
|
||||
void camel_folder_info_set_string(CamelStoreSummary *s, CamelFolderInfo *mi, int type, const char *value)
|
||||
{
|
||||
return ((CamelStoreSummaryClass *)(CAMEL_OBJECT_GET_CLASS(s)))->folder_info_set_string(s, mi, type, value);
|
||||
}
|
||||
|
||||
static CamelFolderInfo *
|
||||
folder_info_new(CamelStoreSummary *s, const char *f)
|
||||
{
|
||||
CamelFolderInfo *mi;
|
||||
|
||||
mi = camel_store_summary_info_new(s);
|
||||
|
||||
mi->full = g_strdup(f);
|
||||
mi->unread = CAMEL_STORE_SUMMARY_UNKNOWN;
|
||||
mi->total = CAMEL_STORE_SUMMARY_UNKNOWN;
|
||||
|
||||
return mi;
|
||||
}
|
||||
|
||||
static CamelFolderInfo *
|
||||
folder_info_load(CamelStoreSummary *s, FILE *in)
|
||||
{
|
||||
CamelFolderInfo *mi;
|
||||
|
||||
mi = camel_store_summary_info_new(s);
|
||||
|
||||
io(printf("Loading folder info\n"));
|
||||
|
||||
camel_file_util_decode_string(in, &mi->full);
|
||||
camel_file_util_decode_uint32(in, &mi->flags);
|
||||
camel_file_util_decode_uint32(in, &mi->unread);
|
||||
camel_file_util_decode_uint32(in, &mi->total);
|
||||
|
||||
if (!ferror(in))
|
||||
return mi;
|
||||
|
||||
camel_store_summary_info_free(s, mi);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int
|
||||
folder_info_save(CamelStoreSummary *s, FILE *out, CamelFolderInfo *mi)
|
||||
{
|
||||
io(printf("Saving folder info\n"));
|
||||
|
||||
camel_file_util_encode_string(out, camel_folder_info_full(s, mi));
|
||||
camel_file_util_encode_uint32(out, mi->flags);
|
||||
camel_file_util_encode_uint32(out, mi->unread);
|
||||
camel_file_util_encode_uint32(out, mi->total);
|
||||
|
||||
return ferror(out);
|
||||
}
|
||||
|
||||
static void
|
||||
folder_info_free(CamelStoreSummary *s, CamelFolderInfo *mi)
|
||||
{
|
||||
g_free(mi->full);
|
||||
g_free(mi->uri);
|
||||
e_memchunk_free(s->folder_info_chunks, mi);
|
||||
}
|
||||
|
||||
static const char *
|
||||
folder_info_string(CamelStoreSummary *s, const CamelFolderInfo *mi, int type)
|
||||
{
|
||||
const char *p;
|
||||
|
||||
/* FIXME: Locks? */
|
||||
|
||||
g_assert (mi != NULL);
|
||||
|
||||
switch (type) {
|
||||
case CAMEL_STORE_SUMMARY_FULL:
|
||||
return mi->full;
|
||||
case CAMEL_STORE_SUMMARY_NAME:
|
||||
p = strrchr(mi->full, '/');
|
||||
if (p)
|
||||
return p;
|
||||
else
|
||||
return mi->full;
|
||||
case CAMEL_STORE_SUMMARY_URI:
|
||||
if (mi->uri)
|
||||
return mi->uri;
|
||||
if (s->uri_prefix)
|
||||
return (((CamelFolderInfo *)mi)->uri = g_strdup_printf("%s%s", s->uri_prefix, mi->full));
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
static void
|
||||
folder_info_set_string (CamelStoreSummary *s, CamelFolderInfo *mi, int type, const char *str)
|
||||
{
|
||||
const char *p;
|
||||
char *v;
|
||||
int len;
|
||||
|
||||
g_assert (mi != NULL);
|
||||
|
||||
switch(type) {
|
||||
case CAMEL_STORE_SUMMARY_FULL:
|
||||
g_free(mi->full);
|
||||
g_free(mi->uri);
|
||||
mi->full = g_strdup(str);
|
||||
break;
|
||||
case CAMEL_STORE_SUMMARY_NAME:
|
||||
p = strrchr(mi->full, '/');
|
||||
if (p) {
|
||||
len = p-mi->full+1;
|
||||
v = g_malloc(len+strlen(str)+1);
|
||||
memcpy(v, mi->full, len);
|
||||
strcpy(v+len, str);
|
||||
} else {
|
||||
v = g_strdup(str);
|
||||
}
|
||||
g_free(mi->full);
|
||||
mi->full = v;
|
||||
break;
|
||||
case CAMEL_STORE_SUMMARY_URI:
|
||||
if (s->uri_prefix) {
|
||||
len = strlen(s->uri_prefix);
|
||||
if (len > strlen(str)
|
||||
|| strncmp(s->uri_prefix, str, len) != 0) {
|
||||
g_warning("Trying to set folderinfo uri '%s' for summary with prefix '%s'",
|
||||
str, s->uri_prefix);
|
||||
return;
|
||||
}
|
||||
g_free(mi->full);
|
||||
g_free(mi->uri);
|
||||
mi->full = g_strdup(str + len);
|
||||
mi->uri = g_strdup(str);
|
||||
} else {
|
||||
g_warning("Trying to set folderinfo uri '%s' for summary with no uri prefix", str);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
162
camel/camel-store-summary.h
Normal file
162
camel/camel-store-summary.h
Normal file
@ -0,0 +1,162 @@
|
||||
/*
|
||||
* Copyright (C) 2000 Ximian Inc.
|
||||
*
|
||||
* Authors: Michael Zucchi <notzed@ximian.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of version 2 of the GNU General Public
|
||||
* License as published by the Free Software Foundation.
|
||||
*
|
||||
* 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_STORE_SUMMARY_H
|
||||
#define _CAMEL_STORE_SUMMARY_H
|
||||
|
||||
#include <stdio.h>
|
||||
#include <time.h>
|
||||
#include <camel/camel-mime-parser.h>
|
||||
#include <camel/camel-object.h>
|
||||
#include <libibex/ibex.h>
|
||||
|
||||
#define CAMEL_STORE_SUMMARY(obj) CAMEL_CHECK_CAST (obj, camel_store_summary_get_type (), CamelStoreSummary)
|
||||
#define CAMEL_STORE_SUMMARY_CLASS(klass) CAMEL_CHECK_CLASS_CAST (klass, camel_store_summary_get_type (), CamelStoreSummaryClass)
|
||||
#define CAMEL_IS_FOLDER_SUMMARY(obj) CAMEL_CHECK_TYPE (obj, camel_store_summary_get_type ())
|
||||
|
||||
typedef struct _CamelStoreSummary CamelStoreSummary;
|
||||
typedef struct _CamelStoreSummaryClass CamelStoreSummaryClass;
|
||||
|
||||
typedef struct _CamelFolderInfo CamelFolderInfo;
|
||||
|
||||
enum _CamelFolderFlags {
|
||||
CAMEL_STORE_SUMMARY_FOLDER_NOSELECT,
|
||||
CAMEL_STORE_SUMMARY_FOLDER_READONLY,
|
||||
CAMEL_STORE_SUMMARY_FOLDER_SUBSCRIBED,
|
||||
CAMEL_STORE_SUMMARY_FOLDER_FLAGGED,
|
||||
};
|
||||
|
||||
#define CAMEL_STORE_SUMMARY_UNKNOWN (~0)
|
||||
|
||||
enum {
|
||||
CAMEL_STORE_SUMMARY_FULL = 0,
|
||||
CAMEL_STORE_SUMMARY_NAME,
|
||||
CAMEL_STORE_SUMMARY_URI,
|
||||
CAMEL_STORE_SUMMARY_LAST,
|
||||
};
|
||||
|
||||
struct _CamelFolderInfo {
|
||||
guint32 refcount;
|
||||
char *uri;
|
||||
char *full;
|
||||
guint32 flags;
|
||||
guint32 unread;
|
||||
guint32 total;
|
||||
};
|
||||
|
||||
enum _CamelStoreSummaryFlags {
|
||||
CAMEL_STORE_SUMMARY_DIRTY = 1<<0,
|
||||
};
|
||||
|
||||
struct _CamelStoreSummary {
|
||||
CamelObject parent;
|
||||
|
||||
struct _CamelStoreSummaryPrivate *priv;
|
||||
|
||||
/* header info */
|
||||
guint32 version; /* version of file required, should be set by implementors */
|
||||
guint32 flags; /* flags */
|
||||
guint32 count; /* how many were saved/loaded */
|
||||
time_t time; /* timestamp for this summary (for implementors to use) */
|
||||
|
||||
/* sizes of memory objects */
|
||||
guint32 folder_info_size;
|
||||
|
||||
/* memory allocators (setup automatically) */
|
||||
struct _EMemChunk *folder_info_chunks;
|
||||
|
||||
char *summary_path;
|
||||
char *uri_prefix;
|
||||
|
||||
GPtrArray *folders; /* CamelFolderInfo's */
|
||||
GHashTable *folders_full; /* CamelFolderInfo's by full name */
|
||||
};
|
||||
|
||||
struct _CamelStoreSummaryClass {
|
||||
CamelObjectClass parent_class;
|
||||
|
||||
/* load/save the global info */
|
||||
int (*summary_header_load)(CamelStoreSummary *, FILE *);
|
||||
int (*summary_header_save)(CamelStoreSummary *, FILE *);
|
||||
|
||||
/* create/save/load an individual message info */
|
||||
CamelFolderInfo * (*folder_info_new)(CamelStoreSummary *, const char *full);
|
||||
CamelFolderInfo * (*folder_info_load)(CamelStoreSummary *, FILE *);
|
||||
int (*folder_info_save)(CamelStoreSummary *, FILE *, CamelFolderInfo *);
|
||||
void (*folder_info_free)(CamelStoreSummary *, CamelFolderInfo *);
|
||||
|
||||
/* virtualise access methods */
|
||||
const char *(*folder_info_string)(CamelStoreSummary *, const CamelFolderInfo *, int);
|
||||
void (*folder_info_set_string)(CamelStoreSummary *, CamelFolderInfo *, int, const char *);
|
||||
};
|
||||
|
||||
guint camel_store_summary_get_type (void);
|
||||
CamelStoreSummary *camel_store_summary_new (void);
|
||||
|
||||
void camel_store_summary_set_filename(CamelStoreSummary *, const char *);
|
||||
void camel_store_summary_set_uri_prefix(CamelStoreSummary *, const char *);
|
||||
|
||||
/* load/save the summary in its entirety */
|
||||
int camel_store_summary_load(CamelStoreSummary *);
|
||||
int camel_store_summary_save(CamelStoreSummary *);
|
||||
|
||||
/* only load the header */
|
||||
int camel_store_summary_header_load(CamelStoreSummary *);
|
||||
|
||||
/* set the dirty bit on the summary */
|
||||
void camel_store_summary_touch(CamelStoreSummary *s);
|
||||
|
||||
/* add a new raw summary item */
|
||||
void camel_store_summary_add(CamelStoreSummary *, CamelFolderInfo *info);
|
||||
|
||||
/* build/add raw summary items */
|
||||
CamelFolderInfo *camel_store_summary_add_from_full(CamelStoreSummary *, const char *);
|
||||
|
||||
/* Just build raw summary items */
|
||||
CamelFolderInfo *camel_store_summary_info_new(CamelStoreSummary *s);
|
||||
CamelFolderInfo *camel_store_summary_info_new_from_full(CamelStoreSummary *s, const char *);
|
||||
|
||||
void camel_store_summary_info_ref(CamelStoreSummary *, CamelFolderInfo *);
|
||||
void camel_store_summary_info_free(CamelStoreSummary *, CamelFolderInfo *);
|
||||
|
||||
/* removes a summary item */
|
||||
void camel_store_summary_remove(CamelStoreSummary *s, CamelFolderInfo *info);
|
||||
void camel_store_summary_remove_full(CamelStoreSummary *s, const char *full);
|
||||
void camel_store_summary_remove_index(CamelStoreSummary *s, int);
|
||||
|
||||
/* remove all items */
|
||||
void camel_store_summary_clear(CamelStoreSummary *s);
|
||||
|
||||
/* lookup functions */
|
||||
int camel_store_summary_count(CamelStoreSummary *);
|
||||
CamelFolderInfo *camel_store_summary_index(CamelStoreSummary *, int);
|
||||
CamelFolderInfo *camel_store_summary_full(CamelStoreSummary *, const char *uid);
|
||||
GPtrArray *camel_store_summary_array(CamelStoreSummary *s);
|
||||
void camel_store_summary_array_free(CamelStoreSummary *s, GPtrArray *array);
|
||||
|
||||
const char *camel_folder_info_string(CamelStoreSummary *, const CamelFolderInfo *, int type);
|
||||
void camel_folder_info_set_string(CamelStoreSummary *, CamelFolderInfo *, int type, const char *value);
|
||||
|
||||
/* helper macro's */
|
||||
#define camel_folder_info_full(s, i) (camel_folder_info_string((CamelStoreSummary *)s, (const CamelFolderInfo *)i, CAMEL_STORE_SUMMARY_FULL))
|
||||
#define camel_folder_info_uri(s, i) (camel_folder_info_string((CamelStoreSummary *)s, (const CamelFolderInfo *)i, CAMEL_STORE_SUMMARY_URI))
|
||||
#define camel_folder_info_name(s, i) (camel_folder_info_string((CamelStoreSummary *)s, (const CamelFolderInfo *)i, CAMEL_STORE_SUMMARY_NAME))
|
||||
|
||||
#endif /* ! _CAMEL_STORE_SUMMARY_H */
|
@ -226,6 +226,8 @@ camel_imap_folder_new (CamelStore *parent, const char *folder_name,
|
||||
!g_strcasecmp (folder_name, "INBOX"))
|
||||
folder->folder_flags |= CAMEL_FOLDER_FILTER_RECENT;
|
||||
|
||||
imap_folder->search = camel_imap_search_new(folder_dir);
|
||||
|
||||
return folder;
|
||||
}
|
||||
|
||||
@ -1314,9 +1316,6 @@ imap_search_by_expression (CamelFolder *folder, const char *expression, CamelExc
|
||||
command channel too */
|
||||
CAMEL_IMAP_FOLDER_LOCK(folder, search_lock);
|
||||
|
||||
if (!imap_folder->search)
|
||||
imap_folder->search = camel_imap_search_new ();
|
||||
|
||||
camel_folder_search_set_folder (imap_folder->search, folder);
|
||||
summary = camel_folder_get_summary(folder);
|
||||
camel_folder_search_set_summary(imap_folder->search, summary);
|
||||
@ -1353,9 +1352,6 @@ imap_search_by_uids(CamelFolder *folder, const char *expression, GPtrArray *uids
|
||||
|
||||
CAMEL_IMAP_FOLDER_LOCK(folder, search_lock);
|
||||
|
||||
if (imap_folder->search == NULL)
|
||||
imap_folder->search = camel_imap_search_new();
|
||||
|
||||
camel_folder_search_set_folder(imap_folder->search, folder);
|
||||
camel_folder_search_set_summary(imap_folder->search, summary);
|
||||
|
||||
|
@ -4,8 +4,9 @@
|
||||
/*
|
||||
* Authors:
|
||||
* Dan Winship <danw@ximian.com>
|
||||
* Michael Zucchi <notzed@ximian.com>
|
||||
*
|
||||
* Copyright 2000, 2001 Ximian, Inc.
|
||||
* Copyright 2000, 2001, 2002 Ximian, Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of version 2 of the GNU General Public
|
||||
@ -35,10 +36,58 @@
|
||||
#include "camel-imap-search.h"
|
||||
#include "camel-imap-private.h"
|
||||
#include "camel-imap-utils.h"
|
||||
#include "camel-imap-summary.h"
|
||||
|
||||
static ESExpResult *
|
||||
imap_body_contains (struct _ESExp *f, int argc, struct _ESExpResult **argv,
|
||||
CamelFolderSearch *s);
|
||||
#include "e-util/md5-utils.h" /* md5 hash building */
|
||||
#include "camel-mime-utils.h" /* base64 encoding */
|
||||
|
||||
#include "camel-seekable-stream.h"
|
||||
|
||||
#define d(x) x
|
||||
|
||||
/*
|
||||
File is:
|
||||
BODY (as in body search)
|
||||
Last uid when search performed
|
||||
termcount: number of search terms
|
||||
matchcount: number of matches
|
||||
term0, term1 ...
|
||||
match0, match1, match2, ...
|
||||
*/
|
||||
|
||||
/* size of in-memory cache */
|
||||
#define MATCH_CACHE_SIZE (32)
|
||||
|
||||
/* Also takes care of 'endianness' file magic */
|
||||
#define MATCH_MARK (('B' << 24) | ('O' << 16) | ('D' << 8) | 'Y')
|
||||
|
||||
/* on-disk header, in native endianness format, matches follow */
|
||||
struct _match_header {
|
||||
guint32 mark;
|
||||
guint32 validity; /* uidvalidity for this folder */
|
||||
guint32 lastuid;
|
||||
guint32 termcount;
|
||||
guint32 matchcount;
|
||||
};
|
||||
|
||||
/* in-memory record */
|
||||
struct _match_record {
|
||||
struct _match_record *next;
|
||||
struct _match_record *prev;
|
||||
|
||||
char hash[17];
|
||||
|
||||
guint32 lastuid;
|
||||
guint32 validity;
|
||||
|
||||
unsigned int termcount;
|
||||
char **terms;
|
||||
GArray *matches;
|
||||
};
|
||||
|
||||
|
||||
static void free_match(CamelImapSearch *is, struct _match_record *mr);
|
||||
static ESExpResult *imap_body_contains (struct _ESExp *f, int argc, struct _ESExpResult **argv, CamelFolderSearch *s);
|
||||
|
||||
static CamelFolderSearchClass *imap_search_parent_class;
|
||||
|
||||
@ -49,12 +98,33 @@ camel_imap_search_class_init (CamelImapSearchClass *camel_imap_search_class)
|
||||
CamelFolderSearchClass *camel_folder_search_class =
|
||||
CAMEL_FOLDER_SEARCH_CLASS (camel_imap_search_class);
|
||||
|
||||
imap_search_parent_class = camel_type_get_global_classfuncs (camel_folder_search_get_type ());
|
||||
imap_search_parent_class = (CamelFolderSearchClass *)camel_type_get_global_classfuncs (camel_folder_search_get_type ());
|
||||
|
||||
/* virtual method overload */
|
||||
camel_folder_search_class->body_contains = imap_body_contains;
|
||||
}
|
||||
|
||||
static void
|
||||
camel_imap_search_init(CamelImapSearch *is)
|
||||
{
|
||||
e_dlist_init(&is->matches);
|
||||
is->matches_hash = g_hash_table_new(g_str_hash, g_str_equal);
|
||||
is->matches_count = 0;
|
||||
is->lastuid = 0;
|
||||
}
|
||||
|
||||
static void
|
||||
camel_imap_search_finalise(CamelImapSearch *is)
|
||||
{
|
||||
struct _match_record *mr;
|
||||
|
||||
while ( (mr = (struct _match_record *)e_dlist_remtail(&is->matches)) )
|
||||
free_match(is, mr);
|
||||
g_hash_table_destroy(is->matches_hash);
|
||||
if (is->cache)
|
||||
camel_object_unref((CamelObject *)is->cache);
|
||||
}
|
||||
|
||||
CamelType
|
||||
camel_imap_search_get_type (void)
|
||||
{
|
||||
@ -65,138 +135,334 @@ camel_imap_search_get_type (void)
|
||||
CAMEL_FOLDER_SEARCH_TYPE, "CamelImapSearch",
|
||||
sizeof (CamelImapSearch),
|
||||
sizeof (CamelImapSearchClass),
|
||||
(CamelObjectClassInitFunc) camel_imap_search_class_init,
|
||||
NULL, NULL, NULL);
|
||||
(CamelObjectClassInitFunc) camel_imap_search_class_init, NULL,
|
||||
(CamelObjectInitFunc) camel_imap_search_init,
|
||||
(CamelObjectFinalizeFunc) camel_imap_search_finalise);
|
||||
}
|
||||
|
||||
return camel_imap_search_type;
|
||||
}
|
||||
|
||||
static int
|
||||
cmp_uid(const void *ap, const void *bp)
|
||||
{
|
||||
unsigned int a, b;
|
||||
|
||||
a = strtoul(((char **)ap)[0], NULL, 10);
|
||||
b = strtoul(((char **)bp)[0], NULL, 10);
|
||||
if (a<b)
|
||||
return -1;
|
||||
else if (a>b)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ESExpResult *
|
||||
imap_body_contains (struct _ESExp *f, int argc, struct _ESExpResult **argv,
|
||||
CamelFolderSearch *s)
|
||||
{
|
||||
CamelImapStore *store = CAMEL_IMAP_STORE (s->folder->parent_store);
|
||||
char *value = argv[0]->value.string;
|
||||
CamelImapResponse *response = NULL;
|
||||
char *result, *p, *lasts = NULL, *real_uid;
|
||||
const char *uid = "";
|
||||
ESExpResult *r;
|
||||
CamelMessageInfo *info;
|
||||
GHashTable *uid_hash = NULL;
|
||||
char *set;
|
||||
GPtrArray *sorted;
|
||||
int i;
|
||||
|
||||
/* If offline, search using the parent class, which can handle this manually */
|
||||
if (!camel_disco_store_check_online (CAMEL_DISCO_STORE (store), NULL))
|
||||
return imap_search_parent_class->body_contains(f, argc, argv, s);
|
||||
|
||||
if (s->current) {
|
||||
uid = camel_message_info_uid (s->current);
|
||||
r = e_sexp_result_new (f, ESEXP_RES_BOOL);
|
||||
r->value.bool = FALSE;
|
||||
response = camel_imap_command (store, s->folder, NULL,
|
||||
"UID SEARCH UID %s BODY \"%s\"",
|
||||
uid, value);
|
||||
} else {
|
||||
r = e_sexp_result_new (f, ESEXP_RES_ARRAY_PTR);
|
||||
|
||||
if (argc == 1 && *value == '\0' && s->folder) {
|
||||
/* optimise the match "" case - match everything */
|
||||
r->value.ptrarray = g_ptr_array_new ();
|
||||
for (i = 0; i < s->summary->len; i++) {
|
||||
CamelMessageInfo *info = g_ptr_array_index (s->summary, i);
|
||||
g_ptr_array_add (r->value.ptrarray, (char *)camel_message_info_uid (info));
|
||||
}
|
||||
} else {
|
||||
/* If searching a (reasonably small) subset of
|
||||
the real folder size, then use a
|
||||
message-set to optimise it */
|
||||
/* TODO: This peeks a bunch of 'private'ish data */
|
||||
if (s->summary->len < camel_folder_get_message_count(s->folder)/2) {
|
||||
sorted = g_ptr_array_new();
|
||||
g_ptr_array_set_size(sorted, s->summary->len);
|
||||
for (i=0;i<s->summary->len;i++)
|
||||
sorted->pdata[i] = (void *)camel_message_info_uid((CamelMessageInfo *)s->summary->pdata[i]);
|
||||
qsort(sorted->pdata, sorted->len, sizeof(sorted->pdata[0]), cmp_uid);
|
||||
set = imap_uid_array_to_set(s->folder->summary, sorted);
|
||||
response = camel_imap_command (store, s->folder, NULL,
|
||||
"UID SEARCH UID %s BODY \"%s\"",
|
||||
set, value);
|
||||
g_free(set);
|
||||
g_ptr_array_free(sorted, TRUE);
|
||||
} else {
|
||||
response = camel_imap_command (store, s->folder, NULL,
|
||||
"UID SEARCH BODY \"%s\"",
|
||||
value);
|
||||
}
|
||||
|
||||
r->value.ptrarray = g_ptr_array_new ();
|
||||
}
|
||||
}
|
||||
|
||||
if (!response)
|
||||
return r;
|
||||
result = camel_imap_response_extract (store, response, "SEARCH", NULL);
|
||||
if (!result)
|
||||
return r;
|
||||
|
||||
p = result + sizeof ("* SEARCH");
|
||||
for (p = strtok_r (p, " ", &lasts); p; p = strtok_r (NULL, " ", &lasts)) {
|
||||
if (s->current) {
|
||||
if (!strcmp (uid, p)) {
|
||||
r->value.bool = TRUE;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
/* if we need to setup a hash of summary items, this way we get
|
||||
access to the summary memory which is locked for the duration of
|
||||
the search, and wont vanish on us */
|
||||
if (uid_hash == NULL) {
|
||||
uid_hash = g_hash_table_new (g_str_hash, g_str_equal);
|
||||
for (i = 0; i < s->summary->len; i++) {
|
||||
info = s->summary->pdata[i];
|
||||
g_hash_table_insert (uid_hash, (char *)camel_message_info_uid (info), info);
|
||||
}
|
||||
}
|
||||
if (g_hash_table_lookup_extended (uid_hash, p, (void *)&real_uid, (void *)&info))
|
||||
g_ptr_array_add (r->value.ptrarray, real_uid);
|
||||
}
|
||||
}
|
||||
|
||||
/* we could probably cache this globally, but its probably not worth it */
|
||||
if (uid_hash)
|
||||
g_hash_table_destroy (uid_hash);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
/**
|
||||
* camel_imap_search_new:
|
||||
*
|
||||
* Return value: A new CamelImapSearch widget.
|
||||
**/
|
||||
CamelFolderSearch *
|
||||
camel_imap_search_new (void)
|
||||
camel_imap_search_new (const char *cachedir)
|
||||
{
|
||||
CamelFolderSearch *new = CAMEL_FOLDER_SEARCH (camel_object_new (camel_imap_search_get_type ()));
|
||||
CamelImapSearch *is = (CamelImapSearch *)new;
|
||||
|
||||
camel_folder_search_construct (new);
|
||||
|
||||
is->cache = camel_data_cache_new(cachedir, 0, NULL);
|
||||
if (is->cache) {
|
||||
/* Expire entries after 14 days of inactivity */
|
||||
camel_data_cache_set_expire_access(is->cache, 60*60*24*14);
|
||||
}
|
||||
|
||||
return new;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
hash_match(char hash[17], int argc, struct _ESExpResult **argv)
|
||||
{
|
||||
MD5Context ctx;
|
||||
unsigned char digest[16];
|
||||
unsigned int state = 0, save = 0;
|
||||
int i;
|
||||
|
||||
md5_init(&ctx);
|
||||
for (i=0;i<argc;i++) {
|
||||
if (argv[i]->type == ESEXP_RES_STRING)
|
||||
md5_update(&ctx, argv[i]->value.string, strlen(argv[i]->value.string));
|
||||
}
|
||||
md5_final(&ctx, digest);
|
||||
|
||||
base64_encode_close(digest, 12, FALSE, hash, &state, &save);
|
||||
|
||||
for (i=0;i<16;i++) {
|
||||
if (hash[i] == '+')
|
||||
hash[i] = ',';
|
||||
if (hash[i] == '/')
|
||||
hash[i] = '_';
|
||||
}
|
||||
|
||||
hash[16] = 0;
|
||||
}
|
||||
|
||||
static int
|
||||
save_match(CamelImapSearch *is, struct _match_record *mr)
|
||||
{
|
||||
guint32 mark = MATCH_MARK;
|
||||
int ret = 0;
|
||||
struct _match_header header;
|
||||
CamelStream *stream;
|
||||
|
||||
/* since its a cache, doesn't matter if it doesn't save, at least we have the in-memory cache
|
||||
for this session */
|
||||
if (is->cache == NULL)
|
||||
return -1;
|
||||
|
||||
stream = camel_data_cache_add(is->cache, "search/body-contains", mr->hash, NULL);
|
||||
if (stream == NULL)
|
||||
return -1;
|
||||
|
||||
d(printf("Saving search cache entry to '%s': %s\n", mr->hash, mr->terms[0]));
|
||||
|
||||
/* we write the whole thing, then re-write the header magic, saves fancy sync code */
|
||||
memcpy(&header.mark, " ", 4);
|
||||
header.termcount = 0;
|
||||
header.matchcount = mr->matches->len;
|
||||
header.lastuid = mr->lastuid;
|
||||
header.validity = mr->validity;
|
||||
|
||||
if (camel_stream_write(stream, (char *)&header, sizeof(header)) != sizeof(header)
|
||||
|| camel_stream_write(stream, mr->matches->data, mr->matches->len*sizeof(guint32)) != mr->matches->len*sizeof(guint32)
|
||||
|| camel_seekable_stream_seek((CamelSeekableStream *)stream, 0, CAMEL_STREAM_SET) == -1
|
||||
|| camel_stream_write(stream, (char *)&mark, sizeof(mark)) != sizeof(mark)) {
|
||||
d(printf(" saving failed, removing cache entry\n"));
|
||||
camel_data_cache_remove(is->cache, "search/body-contains", mr->hash, NULL);
|
||||
ret = -1;
|
||||
}
|
||||
|
||||
camel_object_unref((CamelObject *)stream);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void
|
||||
free_match(CamelImapSearch *is, struct _match_record *mr)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i=0;i<mr->termcount;i++)
|
||||
g_free(mr->terms[i]);
|
||||
g_free(mr->terms);
|
||||
g_array_free(mr->matches, TRUE);
|
||||
g_free(mr);
|
||||
}
|
||||
|
||||
static struct _match_record *
|
||||
load_match(CamelImapSearch *is, char hash[17], int argc, struct _ESExpResult **argv)
|
||||
{
|
||||
struct _match_record *mr;
|
||||
CamelStream *stream = NULL;
|
||||
struct _match_header header;
|
||||
int i;
|
||||
|
||||
mr = g_malloc0(sizeof(*mr));
|
||||
mr->matches = g_array_new(0, 0, sizeof(guint32));
|
||||
g_assert(strlen(hash) == 16);
|
||||
strcpy(mr->hash, hash);
|
||||
mr->terms = g_malloc0(sizeof(mr->terms[0]) * argc);
|
||||
for (i=0;i<argc;i++) {
|
||||
if (argv[i]->type == ESEXP_RES_STRING) {
|
||||
mr->termcount++;
|
||||
mr->terms[i] = g_strdup(argv[i]->value.string);
|
||||
}
|
||||
}
|
||||
|
||||
d(printf("Loading search cache entry to '%s': %s\n", mr->hash, mr->terms[0]));
|
||||
|
||||
memset(&header, 0, sizeof(header));
|
||||
if (is->cache)
|
||||
stream = camel_data_cache_get(is->cache, "search/body-contains", mr->hash, NULL);
|
||||
if (stream != NULL) {
|
||||
/* 'cause i'm gonna be lazy, i'm going to have the termcount == 0 for now,
|
||||
and not load or save them since i can't think of a nice way to do it, the hash
|
||||
should be sufficient to key it */
|
||||
/* This check should also handle endianness changes, we just throw away
|
||||
the data (its only a cache) */
|
||||
if (camel_stream_read(stream, (char *)&header, sizeof(header)) == sizeof(header)
|
||||
&& header.validity == is->validity
|
||||
&& header.mark == MATCH_MARK
|
||||
&& header.termcount == 0) {
|
||||
d(printf(" found %d matches\n", header.matchcount));
|
||||
g_array_set_size(mr->matches, header.matchcount);
|
||||
camel_stream_read(stream, mr->matches->data, sizeof(guint32)*header.matchcount);
|
||||
} else {
|
||||
d(printf(" file format invalid/validity changed\n"));
|
||||
memset(&header, 0, sizeof(header));
|
||||
}
|
||||
camel_object_unref((CamelObject *)stream);
|
||||
} else {
|
||||
d(printf(" no cache entry found\n"));
|
||||
}
|
||||
|
||||
mr->validity = header.validity;
|
||||
if (mr->validity != is->validity)
|
||||
mr->lastuid = 0;
|
||||
else
|
||||
mr->lastuid = header.lastuid;
|
||||
|
||||
return mr;
|
||||
}
|
||||
|
||||
static int
|
||||
sync_match(CamelImapSearch *is, struct _match_record *mr)
|
||||
{
|
||||
char *p, *result, *lasts = NULL;
|
||||
CamelImapResponse *response;
|
||||
guint32 uid;
|
||||
CamelFolder *folder = ((CamelFolderSearch *)is)->folder;
|
||||
CamelImapStore *store = (CamelImapStore *)folder->parent_store;
|
||||
|
||||
if (mr->lastuid >= is->lastuid && mr->validity == is->validity)
|
||||
return 0;
|
||||
|
||||
d(printf("updating match record for uid's %d:%d\n", mr->lastuid+1, is->lastuid));
|
||||
|
||||
/* TODO: Handle multiple search terms */
|
||||
|
||||
response = camel_imap_command (store, folder, NULL,
|
||||
"UID SEARCH UID %d:%d BODY \"%s\"",
|
||||
mr->lastuid+1, is->lastuid, mr->terms[0]);
|
||||
if (!response)
|
||||
return -1;
|
||||
result = camel_imap_response_extract (store, response, "SEARCH", NULL);
|
||||
if (!result)
|
||||
return -1;
|
||||
|
||||
p = result + sizeof ("* SEARCH");
|
||||
for (p = strtok_r (p, " ", &lasts); p; p = strtok_r (NULL, " ", &lasts)) {
|
||||
uid = strtoul(p, NULL, 10);
|
||||
g_array_append_vals(mr->matches, &uid, 1);
|
||||
}
|
||||
g_free(result);
|
||||
|
||||
mr->validity = is->validity;
|
||||
mr->lastuid = is->lastuid;
|
||||
save_match(is, mr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct _match_record *
|
||||
get_match(CamelImapSearch *is, int argc, struct _ESExpResult **argv)
|
||||
{
|
||||
char hash[17];
|
||||
struct _match_record *mr;
|
||||
|
||||
hash_match(hash, argc, argv);
|
||||
|
||||
mr = g_hash_table_lookup(is->matches_hash, hash);
|
||||
if (mr == NULL) {
|
||||
while (is->matches_count >= MATCH_CACHE_SIZE) {
|
||||
mr = (struct _match_record *)e_dlist_remtail(&is->matches);
|
||||
if (mr) {
|
||||
printf("expiring match '%s' (%s)\n", mr->hash, mr->terms[0]);
|
||||
g_hash_table_remove(is->matches_hash, mr->hash);
|
||||
free_match(is, mr);
|
||||
is->matches_count--;
|
||||
} else {
|
||||
is->matches_count = 0;
|
||||
}
|
||||
}
|
||||
mr = load_match(is, hash, argc, argv);
|
||||
g_hash_table_insert(is->matches_hash, mr->hash, mr);
|
||||
is->matches_count++;
|
||||
} else {
|
||||
e_dlist_remove((EDListNode *)mr);
|
||||
}
|
||||
|
||||
e_dlist_addhead(&is->matches, (EDListNode *)mr);
|
||||
|
||||
/* what about offline mode? */
|
||||
/* We could cache those results too, or should we cache them elsewhere? */
|
||||
sync_match(is, mr);
|
||||
|
||||
return mr;
|
||||
}
|
||||
|
||||
static ESExpResult *
|
||||
imap_body_contains (struct _ESExp *f, int argc, struct _ESExpResult **argv, CamelFolderSearch *s)
|
||||
{
|
||||
CamelImapStore *store = CAMEL_IMAP_STORE (s->folder->parent_store);
|
||||
CamelImapSearch *is = (CamelImapSearch *)s;
|
||||
char *uid;
|
||||
ESExpResult *r;
|
||||
CamelMessageInfo *info;
|
||||
GHashTable *uid_hash = NULL;
|
||||
GPtrArray *array;
|
||||
int i, j;
|
||||
struct _match_record *mr;
|
||||
guint32 uidn, *uidp;
|
||||
|
||||
d(printf("Performing body search '%s'\n", argv[0]->value.string));
|
||||
|
||||
/* TODO: Cache offline searches too? */
|
||||
|
||||
/* If offline, search using the parent class, which can handle this manually */
|
||||
if (!camel_disco_store_check_online (CAMEL_DISCO_STORE (store), NULL))
|
||||
return imap_search_parent_class->body_contains(f, argc, argv, s);
|
||||
|
||||
/* optimise the match "" case - match everything */
|
||||
if (argc == 1 && argv[0]->value.string[0] == '\0') {
|
||||
if (s->current) {
|
||||
r = e_sexp_result_new(f, ESEXP_RES_BOOL);
|
||||
r->value.bool = TRUE;
|
||||
} else {
|
||||
r = e_sexp_result_new(f, ESEXP_RES_ARRAY_PTR);
|
||||
r->value.ptrarray = g_ptr_array_new ();
|
||||
for (i = 0; i < s->summary->len; i++) {
|
||||
info = g_ptr_array_index(s->summary, i);
|
||||
g_ptr_array_add(r->value.ptrarray, (char *)camel_message_info_uid(info));
|
||||
}
|
||||
}
|
||||
} else if (s->summary->len == 0) {
|
||||
/* nothing to match case, do nothing (should be handled higher up?) */
|
||||
if (s->current) {
|
||||
r = e_sexp_result_new(f, ESEXP_RES_BOOL);
|
||||
r->value.bool = FALSE;
|
||||
} else {
|
||||
r = e_sexp_result_new(f, ESEXP_RES_ARRAY_PTR);
|
||||
r->value.ptrarray = g_ptr_array_new ();
|
||||
}
|
||||
} else {
|
||||
int truth = FALSE;
|
||||
|
||||
/* setup lastuid/validity for synchronising */
|
||||
info = g_ptr_array_index(s->summary, s->summary->len-1);
|
||||
is->lastuid = strtoul(camel_message_info_uid(info), NULL, 10);
|
||||
is->validity = ((CamelImapSummary *)(s->folder->summary))->validity;
|
||||
|
||||
mr = get_match(is, argc, argv);
|
||||
|
||||
if (s->current) {
|
||||
uidn = strtoul(camel_message_info_uid(s->current), NULL, 10);
|
||||
uidp = (guint32 *)mr->matches->data;
|
||||
j = mr->matches->len;
|
||||
for (i=0;i<j && !truth;i++)
|
||||
truth = *uidp++ == uidn;
|
||||
r = e_sexp_result_new(f, ESEXP_RES_BOOL);
|
||||
r->value.bool = truth;
|
||||
} else {
|
||||
r = e_sexp_result_new(f, ESEXP_RES_ARRAY_PTR);
|
||||
array = r->value.ptrarray = g_ptr_array_new();
|
||||
|
||||
/* We use a hash to map the uid numbers to uid strings as required by the search api */
|
||||
/* We use the summary's strings so we dont need to alloc more */
|
||||
uid_hash = g_hash_table_new(NULL, NULL);
|
||||
for (i = 0; i < s->summary->len; i++) {
|
||||
info = s->summary->pdata[i];
|
||||
uid = (char *)camel_message_info_uid(info);
|
||||
uidn = strtoul(uid, NULL, 10);
|
||||
g_hash_table_insert(uid_hash, (void *)uidn, uid);
|
||||
}
|
||||
|
||||
uidp = (guint32 *)mr->matches->data;
|
||||
j = mr->matches->len;
|
||||
for (i=0;i<j && !truth;i++) {
|
||||
uid = g_hash_table_lookup(uid_hash, (void *)*uidp++);
|
||||
if (uid)
|
||||
g_ptr_array_add(array, uid);
|
||||
}
|
||||
|
||||
g_hash_table_destroy(uid_hash);
|
||||
}
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
@ -27,6 +27,8 @@
|
||||
#define _CAMEL_IMAP_SEARCH_H
|
||||
|
||||
#include <camel/camel-folder-search.h>
|
||||
#include <e-util/e-msgport.h>
|
||||
#include <camel/camel-data-cache.h>
|
||||
|
||||
#define CAMEL_IMAP_SEARCH_TYPE (camel_imap_search_get_type ())
|
||||
#define CAMEL_IMAP_SEARCH(obj) CAMEL_CHECK_CAST (obj, camel_imap_search_get_type (), CamelImapSearch)
|
||||
@ -38,6 +40,15 @@ typedef struct _CamelImapSearchClass CamelImapSearchClass;
|
||||
struct _CamelImapSearch {
|
||||
CamelFolderSearch parent;
|
||||
|
||||
guint32 lastuid; /* current 'last uid' for the folder */
|
||||
guint32 validity; /* validity of the current folder */
|
||||
|
||||
CamelDataCache *cache; /* disk-cache for searches */
|
||||
|
||||
/* cache of body search matches */
|
||||
unsigned int matches_count;
|
||||
EDList matches;
|
||||
GHashTable *matches_hash;
|
||||
};
|
||||
|
||||
struct _CamelImapSearchClass {
|
||||
@ -46,6 +57,6 @@ struct _CamelImapSearchClass {
|
||||
};
|
||||
|
||||
guint camel_imap_search_get_type (void);
|
||||
CamelFolderSearch *camel_imap_search_new (void);
|
||||
CamelFolderSearch *camel_imap_search_new (const char *cachedir);
|
||||
|
||||
#endif /* ! _CAMEL_IMAP_SEARCH_H */
|
||||
|
Reference in New Issue
Block a user