Files
evolution/camel/camel-folder.c
Not Zed 1fbfdbd43e Remove assertion that content is there, when it no longer can be.
2000-11-30  Not Zed  <NotZed@HelixCode.com>

	* providers/local/camel-mbox-folder.c (mbox_get_message): Remove
	assertion that content is there, when it no longer can be.

	* camel-folder-summary.h: Removed pos/bodypos/endpos from
	camelmeessagecontentinfo.
	(CamelMessageFlags): Added an attachments flag.

	* providers/local/camel-local-summary.h: Added load virtual
	function.

	* tests/lib/folders.c (test_message_info): Accessors.
	(test_folder_message): "

	* camel-folder-thread.c (get_root_subject): Fix accessors.
	(dump_tree_rec): "

	* camel-folder-search.c (camel_folder_search_execute_expression):
	Accessors for messageinfo.
	(search_match_all): "
	(search_header_contains): "
	(search_header_contains): "
	(search_body_contains): "
	(camel_folder_search_execute_expression): Use mepool_strdup.

	* providers/local/camel-mbox-summary.c (summary_update): Accessors
	for messageinfo.
	(mbox_summary_sync_full): "

	* providers/local/camel-mh-summary.c (remove_summary): Accessors
	for messageinfo.
	(mh_summary_check): "
	(mh_summary_sync_message): "
	(mh_summary_sync): "

	* providers/local/camel-mh-folder.c (mh_append_message): Use
	accessor for uid.

	* providers/local/camel-local-summary.c
	(local_summary_decode_x_evolution): Use accessor to uid.
	(local_summary_encode_x_evolution): Likewise.
	(message_info_new): And here.
	(camel_local_summary_load): Call virtual load function.
	(local_summary_load): Default load function, load summary.
	(camel_local_summary_load): Check file exists before trying to
	load.
	(camel_local_summary_construct): Turn off building content info!
	(CAMEL_LOCAL_SUMMARY_VERSION): Bump, since we dont build content
	info anymore.
	(camel_local_summary_load): After a successful load/check, do a
	save too so we dont have to go through it again randomly.

	* providers/nntp/camel-nntp-utils.c (get_XOVER_headers): Use
	accessors for messageinfo.

	* providers/nntp/camel-nntp-folder.c (nntp_folder_get_uids): Use
	accessors for uid.

	* providers/imap/camel-imap-folder.c (imap_refresh_info): Use
	accessor for uid.
	(imap_sync): Likewise.
	(imap_get_uids): Likewise.
	(imap_update_summary): And here.

	* providers/vee/camel-vee-folder.c (vfolder_remove_match): Use
	accessor for uid.
	(vfolder_add_match): Handle estrv stuff.
	(vfolder_change_match): Accessor for uid.
	(get_real_message): "
	(vee_get_uids): "
	(vee_folder_build): " + estrv.
	(vee_folder_build_folder): "

	* providers/local/camel-maildir-folder.c (maildir_append_message):
	Use acccessors for uid's.
	(maildir_get_message): Here too.

	* providers/local/camel-maildir-summary.c
	(camel_maildir_summary_init): Setup the string count for us.
	(message_info_new): Access the string array directly.
	(message_info_free): No need to free string if using array.
	(camel_maildir_summary_info_to_name): Use accessor to get to uid.
	(remove_summary): And here.
	(maildir_summary_check): Likewise.
	(maildir_summary_sync): And here.
	(maildir_summary_load): Load up a cache of uid->filename mappings
	before loading the actual summary file.  This saves us having to
	waste the diskspace storing the filenames in the summary itself,
	and also helps us sync the summary better on load.
	(message_info_load): If we have the load_map setup, and the uid
	exists, then set the filename cache from it, and update the flags
	from the name, incase our summary mismatches it.

	* camel-folder-summary.c (camel_folder_summary_init): Setup string
	count for compressed info record.  An optional compile mode which
	stores all strings for a given messageinfo into a packed array,
	which should save 36-50 bytes/record.
	(camel_folder_summary_info_new): Init the string array.
	(message_info_new): Set the string array items, as required.
	(message_info_load): And here too.
	(message_info_save): Use accessors to get to strings.
	(message_info_free): Free strings as one.
	(camel_message_info_dup_to): Handle packed array case.
	(camel_folder_summary_add): Use accessors.  And pack the strv
	before storing it.
	(summary_assign_uid): New function to assign a unique uid to a
	message, if it doesn't have one.
	(camel_folder_summary_add): Call assign_uid instead of doing it
	ourselves.
	(camel_folder_summary_info_new_from_parser): "
	(camel_folder_summary_info_new_from_message): "
	(camel_folder_summary_encode_string): constify.
	(camel_folder_summary_encode_token): "
	(summary_build_content_info_message): Fix accessors to messageinfo.
	(CAMEL_FOLDER_SUMMARY_VERSION): Bumped, for removal of
	contentinfo->pos data.
	(camel_folder_summary_info_new_from_parser): Calculate the size
	based on the parser position, not the removed contentinfo stuff.
	(camel_folder_summary_info_new_from_message): Remove size stuff.
	(camel_folder_summary_offset_content): Removed, no longer means anything.
	(content_info_new):
	(content_info_load):
	(content_info_save):
	(summary_build_content_info): Remove stuff for contentinfo->pos*.
	(summary_build_content_info): Take a msginfo argument, set
	attachments flag if we find any attachments.
	(summary_build_content_info_message): set attachments flag if we
	find any attachments.
	(camel_folder_summary_info_new_from_parser): Always scan the
	content info, even if we dont save it.
	(camel_folder_summary_info_new_from_message): And here too.
	(summary_build_content_info): Only create the contentinfo stuff if
	we have it turned on, otherwise just parse and discard.
	(summary_build_content_info_message): Likewise.

svn path=/trunk/; revision=6731
2000-11-30 11:05:36 +00:00

1375 lines
36 KiB
C

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
/* camel-folder.c: Abstract class for an email folder */
/*
* Author:
* Bertrand Guiheneuf <bertrand@helixcode.com>
*
* Copyright 1999, 2000 Helix Code, Inc. (http://www.helixcode.com)
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
* USA
*/
#include <config.h>
#include <string.h>
#include "camel-folder.h"
#include "camel-exception.h"
#include "camel-store.h"
#include "camel-mime-message.h"
#include "string-utils.h"
#include "e-util/e-memory.h"
static CamelObjectClass *parent_class = NULL;
/* Returns the class for a CamelFolder */
#define CF_CLASS(so) CAMEL_FOLDER_CLASS (CAMEL_OBJECT_GET_CLASS(so))
static void camel_folder_finalize (CamelObject *object);
static void refresh_info (CamelFolder *folder, CamelException *ex);
static void folder_sync (CamelFolder *folder, gboolean expunge,
CamelException *ex);
static const gchar *get_name (CamelFolder *folder);
static const gchar *get_full_name (CamelFolder *folder);
static CamelStore *get_parent_store (CamelFolder *folder);
static guint32 get_permanent_flags (CamelFolder *folder);
static guint32 get_message_flags (CamelFolder *folder, const char *uid);
static void set_message_flags (CamelFolder *folder, const char *uid,
guint32 flags, guint32 set);
static gboolean get_message_user_flag (CamelFolder *folder, const char *uid, const char *name);
static void set_message_user_flag (CamelFolder *folder, const char *uid,
const char *name, gboolean value);
static const char *get_message_user_tag(CamelFolder *folder, const char *uid, const char *name);
static void set_message_user_tag(CamelFolder *folder, const char *uid, const char *name, const char *value);
static gint get_message_count (CamelFolder *folder);
static gint get_unread_message_count (CamelFolder *folder);
static void expunge (CamelFolder *folder,
CamelException *ex);
static void append_message (CamelFolder *folder, CamelMimeMessage *message,
const CamelMessageInfo *info, CamelException *ex);
static GPtrArray *get_uids (CamelFolder *folder);
static void free_uids (CamelFolder *folder,
GPtrArray *array);
static GPtrArray *get_summary (CamelFolder *folder);
static void free_summary (CamelFolder *folder,
GPtrArray *array);
static CamelMimeMessage *get_message (CamelFolder *folder,
const gchar *uid,
CamelException *ex);
static const CamelMessageInfo *get_message_info (CamelFolder *folder,
const char *uid);
static GPtrArray *search_by_expression (CamelFolder *folder,
const char *exp,
CamelException *ex);
static void search_free (CamelFolder * folder,
GPtrArray * result);
static void copy_message_to (CamelFolder *source,
const char *uid,
CamelFolder *dest,
CamelException *ex);
static void move_message_to (CamelFolder *source,
const char *uid,
CamelFolder *dest,
CamelException *ex);
static void freeze (CamelFolder *folder);
static void thaw (CamelFolder *folder);
static gboolean folder_changed (CamelObject *object,
gpointer event_data);
static gboolean message_changed (CamelObject *object,
/*const char *uid*/gpointer event_data);
static void
camel_folder_class_init (CamelFolderClass *camel_folder_class)
{
CamelObjectClass *camel_object_class =
CAMEL_OBJECT_CLASS (camel_folder_class);
parent_class = camel_type_get_global_classfuncs (camel_object_get_type ());
/* virtual method definition */
camel_folder_class->sync = folder_sync;
camel_folder_class->refresh_info = refresh_info;
camel_folder_class->get_name = get_name;
camel_folder_class->get_full_name = get_full_name;
camel_folder_class->get_parent_store = get_parent_store;
camel_folder_class->expunge = expunge;
camel_folder_class->get_message_count = get_message_count;
camel_folder_class->get_unread_message_count = get_unread_message_count;
camel_folder_class->append_message = append_message;
camel_folder_class->get_permanent_flags = get_permanent_flags;
camel_folder_class->get_message_flags = get_message_flags;
camel_folder_class->set_message_flags = set_message_flags;
camel_folder_class->get_message_user_flag = get_message_user_flag;
camel_folder_class->set_message_user_flag = set_message_user_flag;
camel_folder_class->get_message_user_tag = get_message_user_tag;
camel_folder_class->set_message_user_tag = set_message_user_tag;
camel_folder_class->get_message = get_message;
camel_folder_class->get_uids = get_uids;
camel_folder_class->free_uids = free_uids;
camel_folder_class->get_summary = get_summary;
camel_folder_class->free_summary = free_summary;
camel_folder_class->search_by_expression = search_by_expression;
camel_folder_class->search_free = search_free;
camel_folder_class->get_message_info = get_message_info;
camel_folder_class->copy_message_to = copy_message_to;
camel_folder_class->move_message_to = move_message_to;
camel_folder_class->freeze = freeze;
camel_folder_class->thaw = thaw;
/* virtual method overload */
camel_object_class_declare_event (camel_object_class,
"folder_changed", folder_changed);
camel_object_class_declare_event (camel_object_class,
"message_changed", message_changed);
}
static void
camel_folder_init (gpointer object, gpointer klass)
{
CamelFolder *folder = object;
folder->frozen = 0;
folder->changed_frozen = camel_folder_change_info_new();
}
static void
camel_folder_finalize (CamelObject *object)
{
CamelFolder *camel_folder = CAMEL_FOLDER (object);
g_free (camel_folder->name);
g_free (camel_folder->full_name);
if (camel_folder->parent_store)
camel_object_unref (CAMEL_OBJECT (camel_folder->parent_store));
camel_folder_change_info_free(camel_folder->changed_frozen);
}
CamelType
camel_folder_get_type (void)
{
static CamelType camel_folder_type = CAMEL_INVALID_TYPE;
if (camel_folder_type == CAMEL_INVALID_TYPE) {
camel_folder_type = camel_type_register (CAMEL_OBJECT_TYPE, "CamelFolder",
sizeof (CamelFolder),
sizeof (CamelFolderClass),
(CamelObjectClassInitFunc) camel_folder_class_init,
NULL,
(CamelObjectInitFunc) camel_folder_init,
(CamelObjectFinalizeFunc) camel_folder_finalize );
}
return camel_folder_type;
}
/**
* camel_folder_construct:
* @folder: folder object to construct
* @parent_store: parent store object of the folder
* @full_name: full name of the folder
* @name: short name of the folder
*
* Initalizes the folder by setting the parent store and name.
**/
void
camel_folder_construct (CamelFolder *folder, CamelStore *parent_store,
const char *full_name, const char *name)
{
g_return_if_fail (CAMEL_IS_FOLDER (folder));
g_return_if_fail (CAMEL_IS_STORE (parent_store));
g_return_if_fail (folder->parent_store == NULL);
g_return_if_fail (folder->name == NULL);
folder->parent_store = parent_store;
camel_object_ref (CAMEL_OBJECT (parent_store));
folder->name = g_strdup (name);
folder->full_name = g_strdup (full_name);
}
static void
folder_sync (CamelFolder *folder, gboolean expunge, CamelException *ex)
{
g_warning ("CamelFolder::sync not implemented for `%s'",
camel_type_to_name (CAMEL_OBJECT_GET_TYPE (folder)));
}
/**
* camel_folder_sync:
* @folder: The folder object
* @expunge: whether or not to expunge deleted messages
* @ex: exception object
*
* Sync changes made to a folder to its backing store, possibly expunging
* deleted messages as well.
**/
void
camel_folder_sync (CamelFolder *folder, gboolean expunge, CamelException *ex)
{
g_return_if_fail (CAMEL_IS_FOLDER (folder));
CF_CLASS (folder)->sync (folder, expunge, ex);
}
static void
refresh_info (CamelFolder *folder, CamelException *ex)
{
/* No op */
}
/**
* camel_folder_refresh_info:
* @folder: The folder object
* @ex: exception object
*
* Updates a folder's summary to be in sync with its backing store.
**/
void
camel_folder_refresh_info (CamelFolder *folder, CamelException *ex)
{
g_return_if_fail (CAMEL_IS_FOLDER (folder));
CF_CLASS (folder)->refresh_info (folder, ex);
}
static const char *
get_name (CamelFolder *folder)
{
return folder->name;
}
/**
* camel_folder_get_name:
* @folder: a folder
*
* Get the (short) name of the folder. The fully qualified name
* can be obtained with the get_full_name method.
*
* Return value: name of the folder
**/
const char *
camel_folder_get_name (CamelFolder * folder)
{
g_return_val_if_fail (CAMEL_IS_FOLDER (folder), NULL);
return CF_CLASS (folder)->get_name (folder);
}
static const char *
get_full_name (CamelFolder *folder)
{
return folder->full_name;
}
/**
* camel_folder_get_full_name:
* @folder: a folder
*
* Get the (full) name of the folder.
*
* Return value: full name of the folder
**/
const char *
camel_folder_get_full_name (CamelFolder *folder)
{
g_return_val_if_fail (CAMEL_IS_FOLDER (folder), NULL);
return CF_CLASS (folder)->get_full_name (folder);
}
static CamelStore *
get_parent_store (CamelFolder * folder)
{
return folder->parent_store;
}
/**
* camel_folder_get_parent_store:
* @folder: folder to get the parent of
*
* Return value: the parent store of the folder.
**/
CamelStore *
camel_folder_get_parent_store (CamelFolder *folder)
{
g_return_val_if_fail (CAMEL_IS_FOLDER (folder), NULL);
return CF_CLASS (folder)->get_parent_store (folder);
}
static void
expunge (CamelFolder *folder, CamelException *ex)
{
g_warning ("CamelFolder::expunge not implemented for `%s'",
camel_type_to_name (CAMEL_OBJECT_GET_TYPE (folder)));
}
/**
* camel_folder_expunge:
* @folder: the folder
* @ex: a CamelException
*
* Delete messages which have been marked as "DELETED"
**/
void
camel_folder_expunge (CamelFolder *folder, CamelException *ex)
{
g_return_if_fail (CAMEL_IS_FOLDER (folder));
CF_CLASS (folder)->expunge (folder, ex);
}
static int
get_message_count (CamelFolder *folder)
{
g_warning ("CamelFolder::get_message_count not implemented "
"for `%s'", camel_type_to_name (CAMEL_OBJECT_GET_TYPE (folder)));
return -1;
}
/**
* camel_folder_get_message_count:
* @folder: A CamelFolder object
*
* Return value: the number of messages in the folder, or -1 if unknown.
**/
int
camel_folder_get_message_count (CamelFolder *folder)
{
g_return_val_if_fail (CAMEL_IS_FOLDER (folder), -1);
return CF_CLASS (folder)->get_message_count (folder);
}
static int
get_unread_message_count (CamelFolder *folder)
{
g_warning ("CamelFolder::get_unread_message_count not implemented "
"for `%s'", camel_type_to_name (CAMEL_OBJECT_GET_TYPE (folder)));
return -1;
}
/**
* camel_folder_unread_get_message_count:
* @folder: A CamelFolder object
*
* Return value: the number of unread messages in the folder, or -1 if unknown.
**/
int
camel_folder_get_unread_message_count (CamelFolder *folder)
{
g_return_val_if_fail (CAMEL_IS_FOLDER (folder), -1);
return CF_CLASS (folder)->get_unread_message_count (folder);
}
static void
append_message (CamelFolder *folder, CamelMimeMessage *message,
const CamelMessageInfo *info, CamelException *ex)
{
g_warning ("CamelFolder::append_message not implemented for `%s'",
camel_type_to_name (CAMEL_OBJECT_GET_TYPE (folder)));
return;
}
/**
* camel_folder_append_message: add a message to a folder
* @folder: folder object to add the message to
* @message: message object
* @info: message info with additional flags/etc to set on
* new message, or %NULL
* @ex: exception object
*
* Add a message to a folder. Only the flag and tag data from @info
* is used. If @info is %NULL, no flags or tags will be set.
**/
void
camel_folder_append_message (CamelFolder *folder, CamelMimeMessage *message,
const CamelMessageInfo *info, CamelException *ex)
{
g_return_if_fail (CAMEL_IS_FOLDER (folder));
CF_CLASS (folder)->append_message (folder, message, info, ex);
}
static guint32
get_permanent_flags (CamelFolder *folder)
{
return folder->permanent_flags;
}
/**
* camel_folder_get_permanent_flags:
* @folder: a CamelFolder
*
* Return value: the set of CamelMessageFlags that can be permanently
* stored on a message between sessions. If it includes %CAMEL_FLAG_USER,
* then user-defined flags will be remembered.
**/
guint32
camel_folder_get_permanent_flags (CamelFolder *folder)
{
g_return_val_if_fail (CAMEL_IS_FOLDER (folder), 0);
return CF_CLASS (folder)->get_permanent_flags (folder);
}
static guint32
get_message_flags (CamelFolder *folder, const char *uid)
{
g_warning ("CamelFolder::get_message_flags not implemented for `%s'",
camel_type_to_name (CAMEL_OBJECT_GET_TYPE (folder)));
return 0;
}
/**
* camel_folder_get_message_flags:
* @folder: a CamelFolder
* @uid: the UID of a message in @folder
*
* Return value: the CamelMessageFlags that are set on the indicated
* message.
**/
guint32
camel_folder_get_message_flags (CamelFolder *folder, const char *uid)
{
g_return_val_if_fail (CAMEL_IS_FOLDER (folder), 0);
return CF_CLASS (folder)->get_message_flags (folder, uid);
}
static void
set_message_flags (CamelFolder *folder, const char *uid,
guint32 flags, guint32 set)
{
g_warning ("CamelFolder::set_message_flags not implemented for `%s'",
camel_type_to_name (CAMEL_OBJECT_GET_TYPE (folder)));
}
/**
* camel_folder_set_message_flags:
* @folder: a CamelFolder
* @uid: the UID of a message in @folder
* @flags: a set of CamelMessageFlag values to set
* @set: the mask of values in @flags to use.
*
* Sets those flags specified by @set to the values specified by @flags
* on the indicated message. (This may or may not persist after the
* folder or store is closed. See camel_folder_get_permanent_flags().)
**/
void
camel_folder_set_message_flags (CamelFolder *folder, const char *uid,
guint32 flags, guint32 set)
{
g_return_if_fail (CAMEL_IS_FOLDER (folder));
CF_CLASS (folder)->set_message_flags (folder, uid, flags, set);
}
static gboolean
get_message_user_flag (CamelFolder *folder, const char *uid,
const char *name)
{
g_warning ("CamelFolder::get_message_user_flag not implemented "
"for `%s'", camel_type_to_name (CAMEL_OBJECT_GET_TYPE (folder)));
return FALSE;
}
/**
* camel_folder_get_message_user_flag:
* @folder: a CamelFolder
* @uid: the UID of a message in @folder
* @name: the name of a user flag
*
* Return value: whether or not the given user flag is set on the message.
**/
gboolean
camel_folder_get_message_user_flag (CamelFolder *folder, const char *uid,
const char *name)
{
g_return_val_if_fail (CAMEL_IS_FOLDER (folder), 0);
return CF_CLASS (folder)->get_message_user_flag (folder, uid, name);
}
static void
set_message_user_flag (CamelFolder *folder, const char *uid,
const char *name, gboolean value)
{
g_warning ("CamelFolder::set_message_user_flag not implemented "
"for `%s'", camel_type_to_name (CAMEL_OBJECT_GET_TYPE (folder)));
}
/**
* camel_folder_set_message_user_flag:
* @folder: a CamelFolder
* @uid: the UID of a message in @folder
* @name: the name of the user flag to set
* @value: the value to set it to
*
* Sets the user flag specified by @name to the value specified by @value
* on the indicated message. (This may or may not persist after the
* folder or store is closed. See camel_folder_get_permanent_flags().)
**/
void
camel_folder_set_message_user_flag (CamelFolder *folder, const char *uid,
const char *name, gboolean value)
{
g_return_if_fail (CAMEL_IS_FOLDER (folder));
CF_CLASS (folder)->set_message_user_flag (folder, uid, name, value);
}
static const char *get_message_user_tag(CamelFolder *folder, const char *uid, const char *name)
{
g_warning ("CamelFolder::get_message_user_tag not implemented "
"for `%s'", camel_type_to_name (CAMEL_OBJECT_GET_TYPE (folder)));
return NULL;
}
/**
* camel_folder_get_message_user_tag:
* @folder: a CamelFolder
* @uid: the UID of a message in @folder
* @name: the name of a user tag
*
* Return value: Returns the value of the user tag.
**/
const char *
camel_folder_get_message_user_tag (CamelFolder *folder, const char *uid, const char *name)
{
g_return_val_if_fail (CAMEL_IS_FOLDER (folder), 0);
return CF_CLASS (folder)->get_message_user_tag (folder, uid, name);
}
static void
set_message_user_tag(CamelFolder *folder, const char *uid, const char *name, const char *value)
{
g_warning ("CamelFolder::set_message_user_tag not implemented "
"for `%s'", camel_type_to_name (CAMEL_OBJECT_GET_TYPE (folder)));
}
/**
* camel_folder_set_message_user_tag:
* @folder: a CamelFolder
* @uid: the UID of a message in @folder
* @name: the name of the user tag to set
* @value: the value to set it to
*
* Sets the user tag specified by @name to the value specified by @value
* on the indicated message. (This may or may not persist after the
* folder or store is closed. See camel_folder_get_permanent_flags().)
**/
void
camel_folder_set_message_user_tag (CamelFolder *folder, const char *uid, const char *name, const char *value)
{
g_return_if_fail (CAMEL_IS_FOLDER (folder));
CF_CLASS (folder)->set_message_user_tag (folder, uid, name, value);
}
static const CamelMessageInfo *
get_message_info (CamelFolder *folder, const char *uid)
{
g_warning ("CamelFolder::get_message_info not implemented for `%s'",
camel_type_to_name (CAMEL_OBJECT_GET_TYPE (folder)));
return NULL;
}
/**
* camel_folder_get_message_info:
* @folder: a CamelFolder
* @uid: the uid of a message
*
* Return value: the summary information for the indicated message
**/
const CamelMessageInfo *
camel_folder_get_message_info (CamelFolder *folder, const char *uid)
{
g_return_val_if_fail (CAMEL_IS_FOLDER (folder), NULL);
g_return_val_if_fail (uid != NULL, NULL);
return CF_CLASS (folder)->get_message_info (folder, uid);
}
/* TODO: is this function required anyway? */
gboolean
camel_folder_has_summary_capability (CamelFolder *folder)
{
g_return_val_if_fail (CAMEL_IS_FOLDER (folder), FALSE);
return folder->has_summary_capability;
}
/* UIDs stuff */
static CamelMimeMessage *
get_message (CamelFolder *folder, const gchar *uid, CamelException *ex)
{
g_warning ("CamelFolder::get_message not implemented for `%s'",
camel_type_to_name (CAMEL_OBJECT_GET_TYPE (folder)));
return NULL;
}
/**
* camel_folder_get_message:
* @folder: the folder object
* @uid: the UID
* @ex: a CamelException
*
* Get a message from its UID in the folder. Messages are cached
* within a folder, that is, asking twice for the same UID returns the
* same message object. (FIXME: is this true?)
*
* Return value: Message corresponding to the UID
**/
CamelMimeMessage *
camel_folder_get_message (CamelFolder *folder, const gchar *uid,
CamelException *ex)
{
g_return_val_if_fail (CAMEL_IS_FOLDER (folder), NULL);
return CF_CLASS (folder)->get_message (folder, uid, ex);
}
static GPtrArray *
get_uids (CamelFolder *folder)
{
g_warning ("CamelFolder::get_uids not implemented for `%s'",
camel_type_to_name (CAMEL_OBJECT_GET_TYPE (folder)));
return NULL;
}
/**
* camel_folder_get_uids:
* @folder: folder object
*
* Get the list of UIDs available in a folder. This routine is useful
* for finding what messages are available when the folder does not
* support summaries. The returned array shoudl not be modified, and
* must be freed by passing it to camel_folder_free_uids().
*
* Return value: GPtrArray of UIDs corresponding to the messages
* available in the folder.
**/
GPtrArray *
camel_folder_get_uids (CamelFolder *folder)
{
g_return_val_if_fail (CAMEL_IS_FOLDER (folder), NULL);
return CF_CLASS (folder)->get_uids (folder);
}
static void
free_uids (CamelFolder *folder, GPtrArray *array)
{
g_warning ("CamelFolder::free_uids not implemented for `%s'",
camel_type_to_name (CAMEL_OBJECT_GET_TYPE (folder)));
}
/**
* camel_folder_free_uids:
* @folder: folder object
* @array: the array of uids to free
*
* Frees the array of UIDs returned by camel_folder_get_uids().
**/
void
camel_folder_free_uids (CamelFolder *folder, GPtrArray *array)
{
g_return_if_fail (CAMEL_IS_FOLDER (folder));
CF_CLASS (folder)->free_uids (folder, array);
}
static GPtrArray *
get_summary (CamelFolder *folder)
{
g_warning ("CamelFolder::get_summary not implemented for `%s'",
camel_type_to_name (CAMEL_OBJECT_GET_TYPE (folder)));
return NULL;
}
/**
* camel_folder_get_summary:
* @folder: a folder object
*
* This returns the summary information for the folder. This array
* should not be modified, and must be freed with
* camel_folder_free_summary().
*
* Return value: an array of CamelMessageInfo
**/
GPtrArray *
camel_folder_get_summary (CamelFolder *folder)
{
g_return_val_if_fail (CAMEL_IS_FOLDER (folder), NULL);
return CF_CLASS (folder)->get_summary (folder);
}
static void
free_summary (CamelFolder *folder, GPtrArray *array)
{
g_warning ("CamelFolder::free_summary not implemented for `%s'",
camel_type_to_name (CAMEL_OBJECT_GET_TYPE (folder)));
}
/**
* camel_folder_free_summary:
* @folder: folder object
* @array: the summary array to free
*
* Frees the summary array returned by camel_folder_get_summary().
**/
void camel_folder_free_summary(CamelFolder * folder, GPtrArray * array)
{
g_return_if_fail(CAMEL_IS_FOLDER(folder));
CF_CLASS(folder)->free_summary(folder, array);
}
/**
* camel_folder_has_search_capability:
* @folder: Folder object
*
* Checks if a folder supports searching.
*
* Return value: %TRUE if the folder supports searching
**/
gboolean
camel_folder_has_search_capability (CamelFolder *folder)
{
g_return_val_if_fail (CAMEL_IS_FOLDER (folder), FALSE);
return folder->has_search_capability;
}
static GPtrArray *
search_by_expression (CamelFolder *folder, const char *expression,
CamelException *ex)
{
g_warning ("CamelFolder::search_by_expression not implemented for "
"`%s'", camel_type_to_name (CAMEL_OBJECT_GET_TYPE (folder)));
return NULL;
}
/**
* camel_folder_search_by_expression:
* @folder: Folder object
* @expression: a search expression
* @ex: a CamelException
*
* Searches the folder for messages matching the given search expression.
*
* Return value: a list of uids of matching messages. The caller must
* free the list and each of the elements when it is done.
**/
GPtrArray *
camel_folder_search_by_expression (CamelFolder *folder, const char *expression,
CamelException *ex)
{
g_return_val_if_fail (CAMEL_IS_FOLDER (folder), NULL);
g_return_val_if_fail (folder->has_search_capability, NULL);
return CF_CLASS (folder)->search_by_expression (folder, expression, ex);
}
static void
search_free (CamelFolder *folder, GPtrArray *result)
{
int i;
for (i = 0; i < result->len; i++)
g_free (g_ptr_array_index (result, i));
g_ptr_array_free (result, TRUE);
}
/**
* camel_folder_search_free:
* @folder:
* @result:
*
* Free the result of a search.
**/
void
camel_folder_search_free (CamelFolder *folder, GPtrArray *result)
{
g_return_if_fail (CAMEL_IS_FOLDER (folder));
g_return_if_fail (folder->has_search_capability);
return CF_CLASS (folder)->search_free (folder, result);
}
static void
copy_message_to (CamelFolder *source, const char *uid, CamelFolder *dest,
CamelException *ex)
{
CamelMimeMessage *msg;
const CamelMessageInfo *info;
/* Default implementation. */
msg = camel_folder_get_message (source, uid, ex);
if (!msg)
return;
info = camel_folder_get_message_info (source, uid);
camel_folder_append_message (dest, msg, info, ex);
camel_object_unref (CAMEL_OBJECT (msg));
}
/**
* camel_folder_copy_message_to:
* @source: source folder
* @uid: UID of message in @source
* @dest: destination folder
* @ex: a CamelException
*
* This copies a message from one folder to another. If the @source and
* @dest folders have the same parent_store, this may be more efficient
* than a camel_folder_append_message().
*
* FIXME: This call should be deprecated, as append_message() can determine
* this information for itself.
**/
void
camel_folder_copy_message_to (CamelFolder *source, const char *uid,
CamelFolder *dest, CamelException *ex)
{
g_return_if_fail (CAMEL_IS_FOLDER (source));
g_return_if_fail (CAMEL_IS_FOLDER (dest));
g_return_if_fail (uid != NULL);
g_warning("CamelFolder.copy_message_to() is a deprecated api");
if (source->parent_store == dest->parent_store) {
return CF_CLASS (source)->copy_message_to (source, uid,
dest, ex);
} else
return copy_message_to (source, uid, dest, ex);
}
static void
move_message_to (CamelFolder *source, const char *uid,
CamelFolder *dest, CamelException *ex)
{
CamelMimeMessage *msg;
const CamelMessageInfo *info;
/* Default implementation. */
msg = camel_folder_get_message (source, uid, ex);
if (!msg)
return;
info = camel_folder_get_message_info (source, uid);
camel_folder_append_message (dest, msg, info, ex);
camel_object_unref (CAMEL_OBJECT (msg));
if (camel_exception_is_set(ex))
return;
camel_folder_delete_message (source, uid);
}
/**
* camel_folder_move_message_to:
* @source: source folder
* @uid: UID of message in @source
* @dest: destination folder
* @ex: a CamelException
*
* This moves a message from one folder to another. If the @source and
* @dest folders have the same parent_store, this may be more efficient
* than a camel_folder_append_message() followed by
* camel_folder_delete_message().
*
* FIXME: This call should be depracated, since append_message() can
* determine this from the message itself.
**/
void
camel_folder_move_message_to (CamelFolder *source, const char *uid,
CamelFolder *dest, CamelException *ex)
{
g_return_if_fail (CAMEL_IS_FOLDER (source));
g_return_if_fail (CAMEL_IS_FOLDER (dest));
g_return_if_fail (uid != NULL);
g_warning("CamelFolder.move_message_to() is a deprecated api");
if (source->parent_store == dest->parent_store) {
return CF_CLASS (source)->move_message_to (source, uid,
dest, ex);
} else
return move_message_to (source, uid, dest, ex);
}
static void
freeze (CamelFolder *folder)
{
folder->frozen++;
}
/**
* camel_folder_freeze:
* @folder: a folder
*
* Freezes the folder so that a series of operation can be performed
* without "message_changed" and "folder_changed" signals being emitted.
* When the folder is later thawed with camel_folder_thaw(), the
* suppressed signals will be emitted.
**/
void
camel_folder_freeze (CamelFolder * folder)
{
g_return_if_fail (CAMEL_IS_FOLDER (folder));
CF_CLASS (folder)->freeze (folder);
}
static void
thaw (CamelFolder * folder)
{
int i;
CamelFolderChangeInfo *info;
folder->frozen--;
if (folder->frozen != 0)
return;
/* If we have more or less messages, do a folder changed, otherwise just
do a message changed for each one.
TODO: message_changed is now probably irrelevant and not required */
info = folder->changed_frozen;
if (info->uid_added->len > 0 || info->uid_removed->len > 0) {
camel_object_trigger_event(CAMEL_OBJECT(folder), "folder_changed", info);
} else if (info->uid_changed->len > 0) {
for (i=0;i<info->uid_changed->len;i++) {
camel_object_trigger_event(CAMEL_OBJECT(folder), "message_changed", info->uid_changed->pdata[i]);
}
}
camel_folder_change_info_clear(info);
}
/**
* camel_folder_thaw:
* @folder: a folder
*
* Thaws the folder and emits any pending folder_changed or
* message_changed signals.
**/
void
camel_folder_thaw (CamelFolder *folder)
{
g_return_if_fail (CAMEL_IS_FOLDER (folder));
g_return_if_fail (folder->frozen != 0);
CF_CLASS (folder)->thaw (folder);
}
/* Event hooks that block emission when frozen */
static gboolean
folder_changed (CamelObject *obj, gpointer event_data)
{
CamelFolder *folder = CAMEL_FOLDER (obj);
CamelFolderChangeInfo *changed = event_data;
if (folder->frozen) {
if (changed != NULL)
camel_folder_change_info_cat(folder->changed_frozen, changed);
else
g_warning("Class %s is passing NULL to folder_changed event",
camel_type_to_name (CAMEL_OBJECT_GET_TYPE (folder)));
return FALSE;
}
return TRUE;
}
static gboolean
message_changed (CamelObject *obj, /*const char *uid*/gpointer event_data)
{
CamelFolder *folder = CAMEL_FOLDER (obj);
if (folder->frozen) {
camel_folder_change_info_change_uid(folder->changed_frozen, (char *)event_data);
return FALSE;
}
return TRUE;
}
/**
* camel_folder_free_nop:
* @folder: a folder
* @array: an array of uids or CamelMessageInfo
*
* "Frees" the provided array by doing nothing. Used by CamelFolder
* subclasses as an implementation for free_uids, or free_summary when
* the returned array is "static" information and should not be freed.
**/
void
camel_folder_free_nop (CamelFolder *folder, GPtrArray *array)
{
;
}
/**
* camel_folder_free_shallow:
* @folder: a folder
* @array: an array of uids or CamelMessageInfo
*
* Frees the provided array but not its contents. Used by CamelFolder
* subclasses as an implementation for free_uids or free_summary when
* the returned array needs to be freed but its contents come from
* "static" information.
**/
void
camel_folder_free_shallow (CamelFolder *folder, GPtrArray *array)
{
g_ptr_array_free (array, TRUE);
}
/**
* camel_folder_free_deep:
* @folder: a folder
* @array: an array of uids
*
* Frees the provided array and its contents. Used by CamelFolder
* subclasses as an implementation for free_uids when the provided
* information was created explicitly by the corresponding get_ call.
**/
void
camel_folder_free_deep (CamelFolder *folder, GPtrArray *array)
{
int i;
for (i = 0; i < array->len; i++)
g_free (array->pdata[i]);
g_ptr_array_free (array, TRUE);
}
/**
* camel_folder_change_info_new:
* @void:
*
* Create a new folder change info structure.
*
* Return value:
**/
CamelFolderChangeInfo *
camel_folder_change_info_new(void)
{
CamelFolderChangeInfo *info;
info = g_malloc(sizeof(*info));
info->uid_added = g_ptr_array_new();
info->uid_removed = g_ptr_array_new();
info->uid_changed = g_ptr_array_new();
info->uid_source = NULL;
info->uid_pool = e_mempool_new(512, 256, E_MEMPOOL_ALIGN_BYTE);
return info;
}
static void
change_info_add_uid(CamelFolderChangeInfo *info, GPtrArray *uids, const char *uid, int copy)
{
int i;
/* TODO: Check that it is in the other arrays and remove it from them/etc? */
for (i=0;i<uids->len;i++) {
if (!strcmp(uids->pdata[i], uid))
return;
}
if (copy)
g_ptr_array_add(uids, e_mempool_strdup(info->uid_pool, uid));
else
g_ptr_array_add(uids, (char *)uid);
}
static void
change_info_cat(CamelFolderChangeInfo *info, GPtrArray *uids, GPtrArray *source)
{
int i;
for (i=0;i<source->len;i++) {
change_info_add_uid(info, uids, source->pdata[i], TRUE);
}
}
/**
* camel_folder_change_info_add_source:
* @info:
* @uid:
*
* Add a source uid for generating a changeset.
**/
void
camel_folder_change_info_add_source(CamelFolderChangeInfo *info, const char *uid)
{
if (info->uid_source == NULL)
info->uid_source = g_hash_table_new(g_str_hash, g_str_equal);
if (g_hash_table_lookup(info->uid_source, uid) == NULL)
g_hash_table_insert(info->uid_source, e_mempool_strdup(info->uid_pool, uid), (void *)1);
}
/**
* camel_folder_change_info_add_source_list:
* @info:
* @list:
*
* Add a list of source uid's for generating a changeset.
**/
void
camel_folder_change_info_add_source_list(CamelFolderChangeInfo *info, const GPtrArray *list)
{
int i;
if (info->uid_source == NULL)
info->uid_source = g_hash_table_new(g_str_hash, g_str_equal);
for (i=0;i<list->len;i++) {
char *uid = list->pdata[i];
if (g_hash_table_lookup(info->uid_source, uid) == NULL)
g_hash_table_insert(info->uid_source, e_mempool_strdup(info->uid_pool, uid), (void *)1);
}
}
/**
* camel_folder_change_info_add_update:
* @info:
* @uid:
*
* Add a uid from the updated list, used to generate a changeset diff.
**/
void
camel_folder_change_info_add_update(CamelFolderChangeInfo *info, const char *uid)
{
char *key;
int value;
if (info->uid_source == NULL) {
change_info_add_uid(info, info->uid_added, uid, TRUE);
return;
}
if (g_hash_table_lookup_extended(info->uid_source, uid, (void **)&key, (void **)&value)) {
g_hash_table_remove(info->uid_source, key);
} else {
change_info_add_uid(info, info->uid_added, uid, TRUE);
}
}
/**
* camel_folder_change_info_add_update_list:
* @info:
* @list:
*
* Add a list of uid's from the updated list.
**/
void
camel_folder_change_info_add_update_list(CamelFolderChangeInfo *info, const GPtrArray *list)
{
int i;
for (i=0;i<list->len;i++) {
camel_folder_change_info_add_update(info, list->pdata[i]);
}
}
static void
change_info_remove(char *key, void *value, CamelFolderChangeInfo *info)
{
/* we dont need to copy this, as they've already been copied into our pool */
change_info_add_uid(info, info->uid_removed, key, FALSE);
}
/**
* camel_folder_change_info_build_diff:
* @info:
*
* Compare the source uid set to the updated uid set and generate the differences
* into the added and removed lists.
**/
void
camel_folder_change_info_build_diff(CamelFolderChangeInfo *info)
{
if (info->uid_source) {
g_hash_table_foreach(info->uid_source, (GHFunc)change_info_remove, info);
g_hash_table_destroy(info->uid_source);
info->uid_source = NULL;
}
}
/**
* camel_folder_change_info_cat:
* @info:
* @source:
*
* Concatenate one change info onto antoher. Can be used to copy
* them too.
**/
void
camel_folder_change_info_cat(CamelFolderChangeInfo *info, CamelFolderChangeInfo *source)
{
change_info_cat(info, info->uid_added, source->uid_added);
change_info_cat(info, info->uid_removed, source->uid_removed);
change_info_cat(info, info->uid_changed, source->uid_changed);
}
/**
* camel_folder_change_info_add_uid:
* @info:
* @uid:
*
* Add a new uid to the changeinfo.
**/
void
camel_folder_change_info_add_uid(CamelFolderChangeInfo *info, const char *uid)
{
change_info_add_uid(info, info->uid_added, uid, TRUE);
}
/**
* camel_folder_change_info_remove_uid:
* @info:
* @uid:
*
* Add a uid to the removed uid list.
**/
void
camel_folder_change_info_remove_uid(CamelFolderChangeInfo *info, const char *uid)
{
change_info_add_uid(info, info->uid_removed, uid, TRUE);
}
/**
* camel_folder_change_info_change_uid:
* @info:
* @uid:
*
* Add a uid to the changed uid list.
**/
void
camel_folder_change_info_change_uid(CamelFolderChangeInfo *info, const char *uid)
{
change_info_add_uid(info, info->uid_changed, uid, TRUE);
}
/**
* camel_folder_change_info_changed:
* @info:
*
* Return true if the changeset contains any changes.
*
* Return Value:
**/
gboolean
camel_folder_change_info_changed(CamelFolderChangeInfo *info)
{
return (info->uid_added->len || info->uid_removed->len || info->uid_changed->len);
}
/**
* camel_folder_change_info_clear:
* @info:
*
* Empty out the change info; called after changes have been processed.
**/
void
camel_folder_change_info_clear(CamelFolderChangeInfo *info)
{
g_ptr_array_set_size(info->uid_added, 0);
g_ptr_array_set_size(info->uid_removed, 0);
g_ptr_array_set_size(info->uid_changed, 0);
if (info->uid_source) {
g_hash_table_destroy(info->uid_source);
info->uid_source = NULL;
}
e_mempool_flush(info->uid_pool, TRUE);
}
/**
* camel_folder_change_info_free:
* @info:
*
* Free memory associated with the folder change info lists.
**/
void
camel_folder_change_info_free(CamelFolderChangeInfo *info)
{
if (info->uid_source)
g_hash_table_destroy(info->uid_source);
e_mempool_destroy(info->uid_pool);
g_ptr_array_free(info->uid_added, TRUE);
g_ptr_array_free(info->uid_removed, TRUE);
g_ptr_array_free(info->uid_changed, TRUE);
g_free(info);
}