/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
/*
* Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
*
* This library is free software: you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation.
*
* This library 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 Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library. If not, see .
*
* Authors: Michael Zucchi
* Jeffrey Stedfast
*/
#ifdef HAVE_CONFIG_H
#include
#endif
#include
#include
#include "camel-db.h"
#include "camel-debug.h"
#include "camel-folder-search.h"
#include "camel-mime-message.h"
#include "camel-session.h"
#include "camel-store.h"
#include "camel-vee-folder.h"
#include "camel-vee-store.h" /* for open flags */
#include "camel-vee-summary.h"
#include "camel-string-utils.h"
#include "camel-vtrash-folder.h"
#define d(x)
#define dd(x) (camel_debug ("vfolder")?(x):0)
typedef struct _FolderChangedData FolderChangedData;
#define CAMEL_VEE_FOLDER_GET_PRIVATE(obj) \
(G_TYPE_INSTANCE_GET_PRIVATE \
((obj), CAMEL_TYPE_VEE_FOLDER, CamelVeeFolderPrivate))
struct _CamelVeeFolderPrivate {
gboolean destroyed;
GList *subfolders; /* lock using subfolder_lock before changing/accessing */
GHashTable *ignore_changed; /* hash of subfolder pointers to ignore the next folder's 'changed' signal */
GHashTable *skipped_changes; /* CamelFolder -> CamelFolderChangeInfo accumulating ignored changes */
GHashTable *unmatched_add_changed; /* CamelVeeMessageInfoData -> 1, for unmatched folder, postponed additions from camel_vee_folder_add_vuid () */
GHashTable *unmatched_remove_changed; /* CamelVeeMessageInfoData -> 1, for unmatched folder, postponed removal from camel_vee_folder_remove_vuid () */
gboolean auto_update;
/* Processing queue for folder changes. */
GAsyncQueue *change_queue;
gboolean change_queue_busy;
GRecMutex subfolder_lock; /* for locking the subfolder list */
GRecMutex changed_lock; /* for locking the folders-changed list */
gchar *expression; /* query expression */
/* only set-up if our parent is a vee-store, used also as a flag to
* say that this folder is part of the unmatched folder */
CamelVeeStore *parent_vee_store;
CamelVeeDataCache *vee_data_cache;
};
/* The custom property ID is a CamelArg artifact.
* It still identifies the property in state files. */
enum {
PROP_0,
PROP_AUTO_UPDATE = 0x2401
};
G_DEFINE_TYPE (CamelVeeFolder, camel_vee_folder, CAMEL_TYPE_FOLDER)
struct _FolderChangedData {
CamelFolderChangeInfo *changes;
CamelFolder *subfolder;
};
static FolderChangedData *
vee_folder_changed_data_new (CamelFolder *subfolder,
CamelFolderChangeInfo *changes)
{
FolderChangedData *data;
data = g_slice_new0 (FolderChangedData);
data->changes = camel_folder_change_info_new ();
camel_folder_change_info_cat (data->changes, changes);
data->subfolder = g_object_ref (subfolder);
return data;
}
static void
vee_folder_changed_data_free (FolderChangedData *data)
{
camel_folder_change_info_free (data->changes);
g_object_unref (data->subfolder);
g_slice_free (FolderChangedData, data);
}
static CamelVeeDataCache *
vee_folder_get_data_cache (CamelVeeFolder *vfolder)
{
g_return_val_if_fail (CAMEL_IS_VEE_FOLDER (vfolder), NULL);
if (vfolder->priv->parent_vee_store)
return camel_vee_store_get_vee_data_cache (vfolder->priv->parent_vee_store);
return vfolder->priv->vee_data_cache;
}
static gboolean
vee_folder_is_unmatched (CamelVeeFolder *vfolder)
{
g_return_val_if_fail (vfolder != NULL, FALSE);
return vfolder->priv->parent_vee_store &&
vfolder == camel_vee_store_get_unmatched_folder (vfolder->priv->parent_vee_store);
}
static void
vee_folder_note_added_uid (CamelVeeFolder *vfolder,
CamelVeeSummary *vsummary,
CamelVeeMessageInfoData *added_mi_data,
CamelFolderChangeInfo *changes,
gboolean included_as_changed)
{
const gchar *vuid;
vuid = camel_vee_message_info_data_get_vee_message_uid (added_mi_data);
if (!camel_folder_summary_check_uid (&vsummary->summary, vuid)) {
/* add it only if it wasn't in yet */
CamelVeeMessageInfo *vmi;
vmi = camel_vee_summary_add (vsummary, added_mi_data);
if (vmi) {
if (changes)
camel_folder_change_info_add_uid (changes, vuid);
camel_message_info_unref (vmi);
if (vfolder->priv->parent_vee_store)
camel_vee_store_note_vuid_used (vfolder->priv->parent_vee_store, added_mi_data, vfolder);
}
} else {
camel_vee_summary_replace_flags (vsummary, vuid);
if (included_as_changed && changes)
camel_folder_change_info_change_uid (changes, vuid);
}
}
static void
vee_folder_note_unmatch_uid (CamelVeeFolder *vfolder,
CamelVeeSummary *vsummary,
CamelFolder *subfolder,
CamelVeeDataCache *data_cache,
CamelVeeMessageInfoData *unmatched_mi_data,
CamelFolderChangeInfo *changes)
{
const gchar *vuid;
vuid = camel_vee_message_info_data_get_vee_message_uid (unmatched_mi_data);
if (camel_folder_summary_check_uid (&vsummary->summary, vuid)) {
g_object_ref (unmatched_mi_data);
/* this one doesn't belong to us anymore */
if (changes)
camel_folder_change_info_remove_uid (changes, vuid);
camel_vee_summary_remove (vsummary, vuid, subfolder);
if (vfolder->priv->parent_vee_store)
camel_vee_store_note_vuid_unused (vfolder->priv->parent_vee_store, unmatched_mi_data, vfolder);
else
camel_vee_data_cache_remove_message_info_data (data_cache, unmatched_mi_data);
g_object_unref (unmatched_mi_data);
}
}
static void
vee_folder_remove_unmatched (CamelVeeFolder *vfolder,
CamelVeeSummary *vsummary,
CamelVeeDataCache *data_cache,
CamelFolderChangeInfo *changes,
CamelFolder *subfolder,
const gchar *orig_message_uid,
gboolean is_orig_message_uid) /* if not,
then it's 'vee_message_uid' */
{
CamelVeeMessageInfoData *mi_data;
if (is_orig_message_uid) {
/* camel_vee_data_cache_get_message_info_data() auto-adds items if not there,
* thus check whether the cache has it already, and if not, then skip the action.
* This can happen for virtual Junk/Trash folders.
*/
if (!camel_vee_data_cache_contains_message_info_data (data_cache, subfolder, orig_message_uid))
return;
mi_data = camel_vee_data_cache_get_message_info_data (data_cache, subfolder, orig_message_uid);
} else
mi_data = camel_vee_data_cache_get_message_info_data_by_vuid (data_cache, orig_message_uid);
if (!mi_data)
return;
vee_folder_note_unmatch_uid (vfolder, vsummary, subfolder, data_cache, mi_data, changes);
g_object_unref (mi_data);
}
struct RemoveUnmatchedData
{
CamelVeeFolder *vfolder;
CamelVeeSummary *vsummary;
CamelFolder *subfolder;
CamelVeeDataCache *data_cache;
CamelFolderChangeInfo *changes;
gboolean is_orig_message_uid;
};
static void
vee_folder_remove_unmatched_cb (gpointer key,
gpointer value,
gpointer user_data)
{
struct RemoveUnmatchedData *rud = user_data;
const gchar *uid = key;
g_return_if_fail (rud != NULL);
vee_folder_remove_unmatched (rud->vfolder, rud->vsummary, rud->data_cache, rud->changes, rud->subfolder, uid, rud->is_orig_message_uid);
}
static void
vee_folder_merge_matching (CamelVeeFolder *vfolder,
CamelFolder *subfolder,
GHashTable *all_uids,
GPtrArray *match,
CamelFolderChangeInfo *changes,
gboolean included_as_changed)
{
CamelVeeDataCache *data_cache;
CamelVeeMessageInfoData *mi_data;
CamelFolder *folder;
CamelVeeSummary *vsummary;
struct RemoveUnmatchedData rud;
gint ii;
g_return_if_fail (CAMEL_IS_VEE_FOLDER (vfolder));
g_return_if_fail (CAMEL_IS_FOLDER (subfolder));
g_return_if_fail (all_uids != NULL);
g_return_if_fail (match != NULL);
folder = CAMEL_FOLDER (vfolder);
g_return_if_fail (folder != NULL);
vsummary = CAMEL_VEE_SUMMARY (folder->summary);
g_return_if_fail (vsummary != NULL);
data_cache = vee_folder_get_data_cache (vfolder);
for (ii = 0; ii < match->len; ii++) {
const gchar *uid = match->pdata[ii];
mi_data = camel_vee_data_cache_get_message_info_data (data_cache, subfolder, uid);
if (!mi_data)
continue;
g_hash_table_remove (all_uids, uid);
vee_folder_note_added_uid (vfolder, vsummary, mi_data, changes, included_as_changed);
g_object_unref (mi_data);
}
rud.vfolder = vfolder;
rud.vsummary = vsummary;
rud.subfolder = subfolder;
rud.data_cache = data_cache;
rud.changes = changes;
rud.is_orig_message_uid = TRUE;
/* in 'all_uids' left only those which are not part of the folder anymore */
g_hash_table_foreach (all_uids, vee_folder_remove_unmatched_cb, &rud);
}
static void
vee_folder_rebuild_folder_with_changes (CamelVeeFolder *vfolder,
CamelFolder *subfolder,
CamelFolderChangeInfo *changes,
GCancellable *cancellable)
{
GPtrArray *match = NULL;
g_return_if_fail (CAMEL_IS_VEE_FOLDER (vfolder));
g_return_if_fail (CAMEL_IS_FOLDER (subfolder));
/* Unmatched folder cannot be rebuilt */
if (vee_folder_is_unmatched (vfolder))
return;
/* if we have no expression, or its been cleared, then act as if no matches */
if (vfolder->priv->expression == NULL) {
match = g_ptr_array_new ();
} else {
match = camel_folder_search_by_expression (subfolder, vfolder->priv->expression, cancellable, NULL);
if (!match)
return;
}
if (!g_cancellable_is_cancelled (cancellable)) {
GHashTable *all_uids;
all_uids = camel_folder_summary_get_hash (subfolder->summary);
vee_folder_merge_matching (vfolder, subfolder, all_uids, match, changes, FALSE);
g_hash_table_destroy (all_uids);
}
camel_folder_search_free (subfolder, match);
}
static void
vee_folder_rebuild_all (CamelVeeFolder *vfolder,
GCancellable *cancellable)
{
CamelFolderChangeInfo *changes;
GList *iter;
g_return_if_fail (CAMEL_IS_VEE_FOLDER (vfolder));
/* Unmatched folder cannot be rebuilt */
if (vee_folder_is_unmatched (vfolder))
return;
changes = camel_folder_change_info_new ();
g_rec_mutex_lock (&vfolder->priv->subfolder_lock);
for (iter = vfolder->priv->subfolders;
iter && !g_cancellable_is_cancelled (cancellable);
iter = iter->next) {
CamelFolder *subfolder = iter->data;
vee_folder_rebuild_folder_with_changes (vfolder, subfolder, changes, cancellable);
}
g_rec_mutex_unlock (&vfolder->priv->subfolder_lock);
if (camel_folder_change_info_changed (changes))
camel_folder_changed (CAMEL_FOLDER (vfolder), changes);
camel_folder_change_info_free (changes);
}
static void
vee_folder_subfolder_changed (CamelVeeFolder *vfolder,
CamelFolder *subfolder,
CamelFolderChangeInfo *subfolder_changes,
GCancellable *cancellable,
GError **error)
{
CamelVeeDataCache *data_cache;
CamelFolderChangeInfo *changes;
CamelFolder *v_folder;
CamelVeeSummary *vsummary;
gint ii;
g_return_if_fail (CAMEL_IS_VEE_FOLDER (vfolder));
g_return_if_fail (CAMEL_IS_FOLDER (subfolder));
g_return_if_fail (subfolder_changes != NULL);
g_rec_mutex_lock (&vfolder->priv->subfolder_lock);
if (!g_list_find (vfolder->priv->subfolders, subfolder)) {
g_rec_mutex_unlock (&vfolder->priv->subfolder_lock);
return;
}
g_rec_mutex_unlock (&vfolder->priv->subfolder_lock);
changes = camel_folder_change_info_new ();
data_cache = vee_folder_get_data_cache (vfolder);
v_folder = CAMEL_FOLDER (vfolder);
vsummary = CAMEL_VEE_SUMMARY (v_folder->summary);
camel_folder_freeze (v_folder);
for (ii = 0; ii < subfolder_changes->uid_removed->len; ii++) {
const gchar *orig_message_uid = subfolder_changes->uid_removed->pdata[ii];
vee_folder_remove_unmatched (vfolder, vsummary, data_cache, changes, subfolder, orig_message_uid, TRUE);
}
if (subfolder_changes->uid_added->len + subfolder_changes->uid_changed->len > 0) {
GPtrArray *test_uids, *match;
gboolean my_match = FALSE;
test_uids = g_ptr_array_sized_new (subfolder_changes->uid_added->len + subfolder_changes->uid_changed->len);
for (ii = 0; ii < subfolder_changes->uid_added->len; ii++) {
g_ptr_array_add (test_uids, subfolder_changes->uid_added->pdata[ii]);
}
for (ii = 0; ii < subfolder_changes->uid_changed->len; ii++) {
g_ptr_array_add (test_uids, subfolder_changes->uid_changed->pdata[ii]);
}
if (!vfolder->priv->expression) {
my_match = TRUE;
match = g_ptr_array_new ();
if (vee_folder_is_unmatched (vfolder)) {
CamelVeeMessageInfoData *mi_data;
const gchar *vuid;
/* all common from test_uids and stored uids
* in the unmatched folder should be updated */
for (ii = 0; ii < test_uids->len; ii++) {
mi_data = camel_vee_data_cache_get_message_info_data (data_cache, subfolder, test_uids->pdata[ii]);
if (!mi_data)
continue;
vuid = camel_vee_message_info_data_get_vee_message_uid (mi_data);
if (camel_folder_summary_check_uid (v_folder->summary, vuid))
g_ptr_array_add (match, (gpointer) camel_pstring_strdup (test_uids->pdata[ii]));
g_object_unref (mi_data);
}
}
} else {
/* sadly, if there are threads involved, then searching by uids doesn't work,
* because just changed uids can be brought in by the thread condition */
if (strstr (vfolder->priv->expression, "match-threads") != NULL)
match = camel_folder_search_by_expression (subfolder, vfolder->priv->expression, cancellable, NULL);
else
match = camel_folder_search_by_uids (subfolder, vfolder->priv->expression, test_uids, cancellable, NULL);
}
if (match) {
GHashTable *with_uids;
/* uids are taken from the string pool, thus use direct hashes */
with_uids = g_hash_table_new_full (g_direct_hash, g_direct_equal, (GDestroyNotify) camel_pstring_free, NULL);
for (ii = 0; ii < test_uids->len; ii++) {
g_hash_table_insert (with_uids, (gpointer) camel_pstring_strdup (test_uids->pdata[ii]), GINT_TO_POINTER (1));
}
vee_folder_merge_matching (vfolder, subfolder, with_uids, match, changes, TRUE);
g_hash_table_destroy (with_uids);
if (my_match) {
g_ptr_array_foreach (match, (GFunc) camel_pstring_free, NULL);
g_ptr_array_free (match, TRUE);
} else {
camel_folder_search_free (subfolder, match);
}
}
g_ptr_array_free (test_uids, TRUE);
}
camel_folder_thaw (v_folder);
if (camel_folder_change_info_changed (changes))
camel_folder_changed (v_folder, changes);
camel_folder_change_info_free (changes);
}
static void
vee_folder_process_changes (CamelSession *session,
GCancellable *cancellable,
CamelVeeFolder *vee_folder,
GError **error)
{
CamelFolder *folder;
FolderChangedData *data;
GAsyncQueue *change_queue;
const gchar *display_name;
const gchar *message;
folder = CAMEL_FOLDER (vee_folder);
change_queue = vee_folder->priv->change_queue;
message = _("Updating folder '%s'");
display_name = camel_folder_get_display_name (folder);
camel_operation_push_message (cancellable, message, display_name);
while ((data = g_async_queue_try_pop (change_queue)) != NULL) {
vee_folder_subfolder_changed (vee_folder, data->subfolder, data->changes, cancellable, error);
vee_folder_changed_data_free (data);
if (g_cancellable_is_cancelled (cancellable))
break;
}
vee_folder->priv->change_queue_busy = FALSE;
camel_operation_pop_message (cancellable);
}
static void
subfolder_changed (CamelFolder *subfolder,
CamelFolderChangeInfo *changes,
CamelVeeFolder *vfolder)
{
g_return_if_fail (vfolder != NULL);
g_return_if_fail (CAMEL_IS_VEE_FOLDER (vfolder));
g_rec_mutex_lock (&vfolder->priv->changed_lock);
if (g_hash_table_lookup (vfolder->priv->ignore_changed, subfolder) ||
!camel_vee_folder_get_auto_update (vfolder)) {
CamelFolderChangeInfo *my_changes;
g_hash_table_remove (vfolder->priv->ignore_changed, subfolder);
my_changes = g_hash_table_lookup (vfolder->priv->skipped_changes, subfolder);
if (!my_changes)
my_changes = camel_folder_change_info_new ();
camel_folder_change_info_cat (my_changes, changes);
g_hash_table_insert (vfolder->priv->skipped_changes, subfolder, my_changes);
g_rec_mutex_unlock (&vfolder->priv->changed_lock);
return;
}
g_rec_mutex_unlock (&vfolder->priv->changed_lock);
CAMEL_VEE_FOLDER_GET_CLASS (vfolder)->folder_changed (vfolder, subfolder, changes);
}
/* track vanishing folders */
static void
subfolder_deleted (CamelFolder *subfolder,
CamelVeeFolder *vfolder)
{
camel_vee_folder_remove_folder (vfolder, subfolder, NULL);
}
static void
vee_folder_dispose (GObject *object)
{
CamelFolder *folder;
folder = CAMEL_FOLDER (object);
/* parent's class frees summary on dispose, thus depend on it */
if (folder->summary) {
CamelVeeFolder *vfolder;
vfolder = CAMEL_VEE_FOLDER (object);
vfolder->priv->destroyed = TRUE;
camel_folder_freeze ((CamelFolder *) vfolder);
while (vfolder->priv->subfolders) {
CamelFolder *subfolder = vfolder->priv->subfolders->data;
camel_vee_folder_remove_folder (vfolder, subfolder, NULL);
}
camel_folder_thaw ((CamelFolder *) vfolder);
}
/* Chain up to parent's dispose () method. */
G_OBJECT_CLASS (camel_vee_folder_parent_class)->dispose (object);
}
static void
free_change_info_cb (gpointer folder,
gpointer change_info,
gpointer user_data)
{
camel_folder_change_info_free (change_info);
}
static void
vee_folder_finalize (GObject *object)
{
CamelVeeFolder *vf;
vf = CAMEL_VEE_FOLDER (object);
g_free (vf->priv->expression);
g_list_free (vf->priv->subfolders);
g_hash_table_foreach (vf->priv->skipped_changes, free_change_info_cb, NULL);
g_rec_mutex_clear (&vf->priv->subfolder_lock);
g_rec_mutex_clear (&vf->priv->changed_lock);
g_hash_table_destroy (vf->priv->ignore_changed);
g_hash_table_destroy (vf->priv->skipped_changes);
g_hash_table_destroy (vf->priv->unmatched_add_changed);
g_hash_table_destroy (vf->priv->unmatched_remove_changed);
g_async_queue_unref (vf->priv->change_queue);
if (vf->priv->vee_data_cache)
g_object_unref (vf->priv->vee_data_cache);
vf->priv->vee_data_cache = NULL;
/* Chain up to parent's finalize () method. */
G_OBJECT_CLASS (camel_vee_folder_parent_class)->finalize (object);
}
static void
vee_folder_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec)
{
switch (property_id) {
case PROP_AUTO_UPDATE:
g_value_set_boolean (
value, camel_vee_folder_get_auto_update (
CAMEL_VEE_FOLDER (object)));
return;
}
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
}
static void
vee_folder_set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec)
{
switch (property_id) {
case PROP_AUTO_UPDATE:
camel_vee_folder_set_auto_update (
CAMEL_VEE_FOLDER (object),
g_value_get_boolean (value));
return;
}
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
}
static void
vee_folder_propagate_skipped_changes (CamelVeeFolder *vf)
{
CamelVeeFolderClass *class;
CamelFolderChangeInfo *changes = NULL;
GHashTableIter iter;
gpointer psub, pchanges;
g_return_if_fail (vf != NULL);
class = CAMEL_VEE_FOLDER_GET_CLASS (vf);
g_rec_mutex_lock (&vf->priv->changed_lock);
/* this is for Unmatched folder only, other folders have unmatched_remove_changed always empty */
if (g_hash_table_size (vf->priv->unmatched_add_changed) +
g_hash_table_size (vf->priv->unmatched_remove_changed) > 0) {
gpointer pkey, pvalue;
CamelVeeSummary *vsummary;
CamelFolder *v_folder;
CamelVeeDataCache *data_cache;
changes = camel_folder_change_info_new ();
data_cache = vee_folder_get_data_cache (vf);
v_folder = CAMEL_FOLDER (vf);
vsummary = CAMEL_VEE_SUMMARY (v_folder->summary);
/* first remove ... */
g_hash_table_iter_init (&iter, vf->priv->unmatched_remove_changed);
while (g_hash_table_iter_next (&iter, &pkey, &pvalue)) {
CamelVeeMessageInfoData *mi_data = pkey;
CamelVeeSubfolderData *sf_data;
CamelFolder *subfolder;
sf_data = camel_vee_message_info_data_get_subfolder_data (mi_data);
subfolder = camel_vee_subfolder_data_get_folder (sf_data);
vee_folder_note_unmatch_uid (vf, vsummary, subfolder, data_cache, mi_data, changes);
}
g_hash_table_remove_all (vf->priv->unmatched_remove_changed);
/* ... then add */
g_hash_table_iter_init (&iter, vf->priv->unmatched_add_changed);
while (g_hash_table_iter_next (&iter, &pkey, &pvalue)) {
CamelVeeMessageInfoData *mi_data = pkey;
vee_folder_note_added_uid (vf, vsummary, mi_data, changes, FALSE);
}
g_hash_table_remove_all (vf->priv->unmatched_add_changed);
}
g_hash_table_iter_init (&iter, vf->priv->skipped_changes);
while (g_hash_table_iter_next (&iter, &psub, &pchanges)) {
g_warn_if_fail (pchanges != NULL);
if (!pchanges)
continue;
if (g_list_find (vf->priv->subfolders, psub) != NULL)
class->folder_changed (vf, psub, pchanges);
camel_folder_change_info_free (pchanges);
}
g_hash_table_remove_all (vf->priv->skipped_changes);
g_rec_mutex_unlock (&vf->priv->changed_lock);
if (changes) {
if (camel_folder_change_info_changed (changes))
camel_folder_changed (CAMEL_FOLDER (vf), changes);
camel_folder_change_info_free (changes);
}
}
static GPtrArray *
vee_folder_search_by_expression (CamelFolder *folder,
const gchar *expression,
GCancellable *cancellable,
GError **error)
{
CamelFolderSearch *search;
GPtrArray *matches;
search = camel_folder_search_new ();
camel_folder_search_set_folder (search, folder);
matches = camel_folder_search_search (search, expression, NULL, cancellable, error);
g_object_unref (search);
return matches;
}
static GPtrArray *
vee_folder_search_by_uids (CamelFolder *folder,
const gchar *expression,
GPtrArray *uids,
GCancellable *cancellable,
GError **error)
{
CamelFolderSearch *search;
GPtrArray *matches;
if (!uids || uids->len == 0)
return g_ptr_array_new ();
search = camel_folder_search_new ();
camel_folder_search_set_folder (search, folder);
matches = camel_folder_search_search (search, expression, uids, cancellable, error);
g_object_unref (search);
return matches;
}
static guint32
vee_folder_count_by_expression (CamelFolder *folder,
const gchar *expression,
GCancellable *cancellable,
GError **error)
{
CamelFolderSearch *search;
guint32 count;
search = camel_folder_search_new ();
camel_folder_search_set_folder (search, folder);
count = camel_folder_search_count (search, expression, cancellable, error);
g_object_unref (search);
return count;
}
static void
vee_folder_search_free (CamelFolder *folder,
GPtrArray *result)
{
camel_folder_search_free_result (NULL, result);
}
static void
vee_folder_delete (CamelFolder *folder)
{
CamelVeeFolder *vfolder;
g_return_if_fail (CAMEL_IS_VEE_FOLDER (folder));
vfolder = CAMEL_VEE_FOLDER (folder);
g_rec_mutex_lock (&vfolder->priv->subfolder_lock);
while (vfolder->priv->subfolders) {
CamelFolder *subfolder = vfolder->priv->subfolders->data;
g_object_ref (subfolder);
g_rec_mutex_unlock (&vfolder->priv->subfolder_lock);
camel_vee_folder_remove_folder (vfolder, subfolder, NULL);
g_object_unref (subfolder);
g_rec_mutex_lock (&vfolder->priv->subfolder_lock);
}
g_rec_mutex_unlock (&vfolder->priv->subfolder_lock);
((CamelFolderClass *) camel_vee_folder_parent_class)->delete_ (folder);
}
static void
vee_folder_freeze (CamelFolder *folder)
{
CamelVeeFolder *vfolder = CAMEL_VEE_FOLDER (folder);
GList *link;
if (vfolder->priv->parent_vee_store &&
!vee_folder_is_unmatched (vfolder)) {
CamelVeeFolder *unmatched_folder;
unmatched_folder = camel_vee_store_get_unmatched_folder (vfolder->priv->parent_vee_store);
if (unmatched_folder)
camel_folder_freeze (CAMEL_FOLDER (unmatched_folder));
}
g_rec_mutex_lock (&vfolder->priv->subfolder_lock);
for (link = vfolder->priv->subfolders; link; link = g_list_next (link)) {
CamelFolder *subfolder = link->data;
camel_folder_freeze (subfolder);
}
g_rec_mutex_unlock (&vfolder->priv->subfolder_lock);
/* call parent implementation */
CAMEL_FOLDER_CLASS (camel_vee_folder_parent_class)->freeze (folder);
}
static void
vee_folder_thaw (CamelFolder *folder)
{
CamelVeeFolder *vfolder = CAMEL_VEE_FOLDER (folder);
GList *link;
if (vfolder->priv->parent_vee_store &&
!vee_folder_is_unmatched (vfolder)) {
CamelVeeFolder *unmatched_folder;
unmatched_folder = camel_vee_store_get_unmatched_folder (vfolder->priv->parent_vee_store);
if (unmatched_folder)
camel_folder_thaw (CAMEL_FOLDER (unmatched_folder));
}
g_rec_mutex_lock (&vfolder->priv->subfolder_lock);
for (link = vfolder->priv->subfolders; link; link = g_list_next (link)) {
CamelFolder *subfolder = link->data;
camel_folder_thaw (subfolder);
}
g_rec_mutex_unlock (&vfolder->priv->subfolder_lock);
/* call parent implementation */
CAMEL_FOLDER_CLASS (camel_vee_folder_parent_class)->thaw (folder);
}
static gboolean
vee_folder_append_message_sync (CamelFolder *folder,
CamelMimeMessage *message,
CamelMessageInfo *info,
gchar **appended_uid,
GCancellable *cancellable,
GError **error)
{
g_set_error (
error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
_("Cannot copy or move messages into a Virtual Folder"));
return FALSE;
}
static gboolean
vee_folder_expunge_sync (CamelFolder *folder,
GCancellable *cancellable,
GError **error)
{
return CAMEL_FOLDER_GET_CLASS (folder)->
synchronize_sync (folder, TRUE, cancellable, error);
}
static CamelMimeMessage *
vee_folder_get_message_sync (CamelFolder *folder,
const gchar *uid,
GCancellable *cancellable,
GError **error)
{
CamelVeeMessageInfo *mi;
CamelMimeMessage *msg = NULL;
mi = (CamelVeeMessageInfo *) camel_folder_summary_get (folder->summary, uid);
if (mi) {
msg = camel_folder_get_message_sync (
camel_folder_summary_get_folder (mi->orig_summary), camel_message_info_uid (mi) + 8,
cancellable, error);
camel_message_info_unref (mi);
} else {
g_set_error (
error, CAMEL_FOLDER_ERROR,
CAMEL_FOLDER_ERROR_INVALID_UID,
_("No such message %s in %s"), uid,
camel_folder_get_display_name (folder));
}
return msg;
}
static gboolean
vee_folder_refresh_info_sync (CamelFolder *folder,
GCancellable *cancellable,
GError **error)
{
CamelVeeFolder *vf = (CamelVeeFolder *) folder;
vee_folder_propagate_skipped_changes (vf);
vee_folder_rebuild_all (vf, cancellable);
return TRUE;
}
static gboolean
vee_folder_synchronize_sync (CamelFolder *folder,
gboolean expunge,
GCancellable *cancellable,
GError **error)
{
CamelVeeFolder *vfolder = (CamelVeeFolder *) folder;
gboolean res = TRUE;
GList *iter;
g_return_val_if_fail (CAMEL_IS_VEE_FOLDER (folder), FALSE);
vee_folder_propagate_skipped_changes (vfolder);
/* basically no-op here, especially do not call synchronize on subfolders
* if not expunging, they are responsible for themselfs */
if (!expunge ||
vee_folder_is_unmatched (vfolder))
return TRUE;
g_rec_mutex_lock (&vfolder->priv->subfolder_lock);
for (iter = vfolder->priv->subfolders; iter && !g_cancellable_is_cancelled (cancellable); iter = iter->next) {
GError *local_error = NULL;
CamelFolder *subfolder = iter->data;
if (!camel_folder_synchronize_sync (subfolder, expunge, cancellable, &local_error)) {
if (local_error && strncmp (local_error->message, "no such table", 13) != 0 && error && !*error) {
const gchar *desc;
desc = camel_folder_get_description (subfolder);
g_propagate_prefixed_error (
error, local_error,
_("Error storing '%s': "), desc);
res = FALSE;
} else
g_clear_error (&local_error);
}
}
g_rec_mutex_unlock (&vfolder->priv->subfolder_lock);
return res;
}
static gboolean
vee_folder_transfer_messages_to_sync (CamelFolder *folder,
GPtrArray *uids,
CamelFolder *dest,
gboolean delete_originals,
GPtrArray **transferred_uids,
GCancellable *cancellable,
GError **error)
{
g_set_error (
error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
_("Cannot copy or move messages into a Virtual Folder"));
return FALSE;
}
static void
vee_folder_set_expression (CamelVeeFolder *vee_folder,
const gchar *query)
{
g_rec_mutex_lock (&vee_folder->priv->subfolder_lock);
/* no change, do nothing */
if ((vee_folder->priv->expression && query && strcmp (vee_folder->priv->expression, query) == 0)
|| (vee_folder->priv->expression == NULL && query == NULL)) {
g_rec_mutex_unlock (&vee_folder->priv->subfolder_lock);
return;
}
g_free (vee_folder->priv->expression);
if (query)
vee_folder->priv->expression = g_strdup (query);
vee_folder_rebuild_all (vee_folder, NULL);
g_rec_mutex_unlock (&vee_folder->priv->subfolder_lock);
}
static void
vee_folder_rebuild_folder (CamelVeeFolder *vfolder,
CamelFolder *subfolder,
GCancellable *cancellable)
{
CamelFolderChangeInfo *changes;
CamelFolder *v_folder;
v_folder = CAMEL_FOLDER (vfolder);
changes = camel_folder_change_info_new ();
camel_folder_freeze (v_folder);
vee_folder_rebuild_folder_with_changes (vfolder, subfolder, changes, cancellable);
camel_folder_thaw (v_folder);
if (camel_folder_change_info_changed (changes))
camel_folder_changed (CAMEL_FOLDER (vfolder), changes);
camel_folder_change_info_free (changes);
}
static void
vee_folder_add_folder (CamelVeeFolder *vfolder,
CamelFolder *subfolder,
GCancellable *cancellable)
{
if (vfolder->priv->parent_vee_store)
camel_vee_store_note_subfolder_used (vfolder->priv->parent_vee_store, subfolder, vfolder);
vee_folder_rebuild_folder (vfolder, subfolder, cancellable);
}
static gboolean
vee_folder_remove_from_unmatched_changed_cb (gpointer key,
gpointer value,
gpointer user_data)
{
CamelVeeMessageInfoData *mi_data = key;
CamelFolder *subfolder = user_data;
CamelVeeSubfolderData *sf_data;
g_return_val_if_fail (mi_data != NULL, TRUE);
sf_data = camel_vee_message_info_data_get_subfolder_data (mi_data);
return subfolder == camel_vee_subfolder_data_get_folder (sf_data);
}
static void
vee_folder_remove_folder (CamelVeeFolder *vfolder,
CamelFolder *subfolder,
GCancellable *cancellable)
{
CamelFolderChangeInfo *changes;
CamelFolder *v_folder;
GHashTable *uids;
v_folder = CAMEL_FOLDER (vfolder);
changes = camel_folder_change_info_new ();
camel_folder_freeze (v_folder);
uids = camel_vee_summary_get_uids_for_subfolder (CAMEL_VEE_SUMMARY (v_folder->summary), subfolder);
if (uids) {
struct RemoveUnmatchedData rud;
rud.vfolder = vfolder;
rud.vsummary = CAMEL_VEE_SUMMARY (v_folder->summary);
rud.subfolder = subfolder;
rud.data_cache = vee_folder_get_data_cache (vfolder);
rud.changes = changes;
rud.is_orig_message_uid = FALSE;
g_hash_table_foreach (uids, vee_folder_remove_unmatched_cb, &rud);
if (vee_folder_is_unmatched (vfolder) &&
!camel_vee_folder_get_auto_update (vfolder) &&
g_hash_table_size (vfolder->priv->unmatched_add_changed) +
g_hash_table_size (vfolder->priv->unmatched_remove_changed) > 0) {
/* forget about these in cached updates */
g_hash_table_foreach_remove (vfolder->priv->unmatched_add_changed,
vee_folder_remove_from_unmatched_changed_cb, subfolder);
g_hash_table_foreach_remove (vfolder->priv->unmatched_remove_changed,
vee_folder_remove_from_unmatched_changed_cb, subfolder);
}
g_hash_table_destroy (uids);
}
if (vfolder->priv->parent_vee_store)
camel_vee_store_note_subfolder_unused (vfolder->priv->parent_vee_store, subfolder, vfolder);
camel_folder_thaw (v_folder);
/* do not notify about changes in vfolder which
* is removing its subfolders in dispose */
if (!vfolder->priv->destroyed &&
camel_folder_change_info_changed (changes))
camel_folder_changed (CAMEL_FOLDER (vfolder), changes);
camel_folder_change_info_free (changes);
}
static void
vee_folder_folder_changed (CamelVeeFolder *vee_folder,
CamelFolder *subfolder,
CamelFolderChangeInfo *changes)
{
CamelVeeFolderPrivate *p = vee_folder->priv;
FolderChangedData *data;
CamelFolder *folder;
CamelStore *parent_store;
CamelSession *session;
if (p->destroyed)
return;
folder = CAMEL_FOLDER (vee_folder);
parent_store = camel_folder_get_parent_store (folder);
session = camel_service_ref_session (CAMEL_SERVICE (parent_store));
g_async_queue_lock (vee_folder->priv->change_queue);
data = vee_folder_changed_data_new (subfolder, changes);
g_async_queue_push_unlocked (vee_folder->priv->change_queue, data);
if (!vee_folder->priv->change_queue_busy) {
gchar *description;
description = g_strdup_printf ("Updating search folder '%s'", camel_folder_get_full_name (CAMEL_FOLDER (vee_folder)));
camel_session_submit_job (
session, description, (CamelSessionCallback)
vee_folder_process_changes,
g_object_ref (vee_folder),
(GDestroyNotify) g_object_unref);
vee_folder->priv->change_queue_busy = TRUE;
g_free (description);
}
g_async_queue_unlock (vee_folder->priv->change_queue);
g_object_unref (session);
}
static void
camel_vee_folder_class_init (CamelVeeFolderClass *class)
{
GObjectClass *object_class;
CamelFolderClass *folder_class;
g_type_class_add_private (class, sizeof (CamelVeeFolderPrivate));
object_class = G_OBJECT_CLASS (class);
object_class->dispose = vee_folder_dispose;
object_class->finalize = vee_folder_finalize;
object_class->get_property = vee_folder_get_property;
object_class->set_property = vee_folder_set_property;
folder_class = CAMEL_FOLDER_CLASS (class);
folder_class->search_by_expression = vee_folder_search_by_expression;
folder_class->search_by_uids = vee_folder_search_by_uids;
folder_class->count_by_expression = vee_folder_count_by_expression;
folder_class->search_free = vee_folder_search_free;
folder_class->delete_ = vee_folder_delete;
folder_class->freeze = vee_folder_freeze;
folder_class->thaw = vee_folder_thaw;
folder_class->append_message_sync = vee_folder_append_message_sync;
folder_class->expunge_sync = vee_folder_expunge_sync;
folder_class->get_message_sync = vee_folder_get_message_sync;
folder_class->refresh_info_sync = vee_folder_refresh_info_sync;
folder_class->synchronize_sync = vee_folder_synchronize_sync;
folder_class->transfer_messages_to_sync = vee_folder_transfer_messages_to_sync;
class->set_expression = vee_folder_set_expression;
class->add_folder = vee_folder_add_folder;
class->remove_folder = vee_folder_remove_folder;
class->rebuild_folder = vee_folder_rebuild_folder;
class->folder_changed = vee_folder_folder_changed;
g_object_class_install_property (
object_class,
PROP_AUTO_UPDATE,
g_param_spec_boolean (
"auto-update",
"Auto Update",
_("Automatically _update on change in source folders"),
TRUE,
G_PARAM_READWRITE |
CAMEL_PARAM_PERSISTENT));
}
static void
camel_vee_folder_init (CamelVeeFolder *vee_folder)
{
CamelFolder *folder = CAMEL_FOLDER (vee_folder);
vee_folder->priv = CAMEL_VEE_FOLDER_GET_PRIVATE (vee_folder);
folder->folder_flags |= CAMEL_FOLDER_HAS_SUMMARY_CAPABILITY;
/* FIXME: what to do about user flags if the subfolder doesn't support them? */
folder->permanent_flags = CAMEL_MESSAGE_ANSWERED |
CAMEL_MESSAGE_DELETED |
CAMEL_MESSAGE_DRAFT |
CAMEL_MESSAGE_FLAGGED |
CAMEL_MESSAGE_SEEN;
g_rec_mutex_init (&vee_folder->priv->subfolder_lock);
g_rec_mutex_init (&vee_folder->priv->changed_lock);
vee_folder->priv->auto_update = TRUE;
vee_folder->priv->ignore_changed = g_hash_table_new (g_direct_hash, g_direct_equal);
vee_folder->priv->skipped_changes = g_hash_table_new (g_direct_hash, g_direct_equal);
vee_folder->priv->unmatched_add_changed =
g_hash_table_new_full (g_direct_hash, g_direct_equal, g_object_unref, NULL);
vee_folder->priv->unmatched_remove_changed =
g_hash_table_new_full (g_direct_hash, g_direct_equal, g_object_unref, NULL);
vee_folder->priv->change_queue = g_async_queue_new_full (
(GDestroyNotify) vee_folder_changed_data_free);
}
void
camel_vee_folder_construct (CamelVeeFolder *vf,
guint32 flags)
{
CamelFolder *folder = (CamelFolder *) vf;
CamelStore *parent_store;
vf->flags = flags;
parent_store = camel_folder_get_parent_store (CAMEL_FOLDER (vf));
if (CAMEL_IS_VEE_STORE (parent_store))
vf->priv->parent_vee_store = CAMEL_VEE_STORE (parent_store);
else
vf->priv->vee_data_cache = camel_vee_data_cache_new ();
folder->summary = camel_vee_summary_new (folder);
/* only for subfolders of vee-store */
if (vf->priv->parent_vee_store) {
const gchar *user_data_dir;
gchar *state_file, *folder_name, *filename;
user_data_dir = camel_service_get_user_data_dir (CAMEL_SERVICE (parent_store));
folder_name = g_uri_escape_string (camel_folder_get_full_name (folder), NULL, TRUE);
filename = g_strconcat (folder_name, ".cmeta", NULL);
state_file = g_build_filename (user_data_dir, filename, NULL);
camel_object_set_state_filename (CAMEL_OBJECT (vf), state_file);
g_free (state_file);
g_free (filename);
g_free (folder_name);
/* set/load persistent state */
camel_object_state_read (CAMEL_OBJECT (vf));
}
}
/**
* camel_vee_folder_new:
* @parent_store: the parent CamelVeeStore
* @full: the full path to the vfolder.
* @flags: flags of some kind
*
* Create a new CamelVeeFolder object.
*
* Returns: A new CamelVeeFolder widget.
**/
CamelFolder *
camel_vee_folder_new (CamelStore *parent_store,
const gchar *full,
guint32 flags)
{
CamelVeeFolder *vf;
g_return_val_if_fail (CAMEL_IS_STORE (parent_store), NULL);
g_return_val_if_fail (full != NULL, NULL);
if (CAMEL_IS_VEE_STORE (parent_store) && strcmp (full, CAMEL_UNMATCHED_NAME) == 0) {
vf = camel_vee_store_get_unmatched_folder (CAMEL_VEE_STORE (parent_store));
if (vf)
g_object_ref (vf);
} else {
const gchar *name = strrchr (full, '/');
if (name == NULL)
name = full;
else
name++;
vf = g_object_new (
CAMEL_TYPE_VEE_FOLDER,
"display-name", name, "full-name", full,
"parent-store", parent_store, NULL);
camel_vee_folder_construct (vf, flags);
}
d (printf ("returning folder %s %p, count = %d\n", full, vf, camel_folder_get_message_count ((CamelFolder *) vf)));
return (CamelFolder *) vf;
}
void
camel_vee_folder_set_expression (CamelVeeFolder *vfolder,
const gchar *expr)
{
CAMEL_VEE_FOLDER_GET_CLASS (vfolder)->set_expression (vfolder, expr);
}
/**
* camel_vee_folder_get_expression:
*
* FIXME Document me!
*
* Since: 3.6
**/
const gchar *
camel_vee_folder_get_expression (CamelVeeFolder *vfolder)
{
g_return_val_if_fail (CAMEL_IS_VEE_FOLDER (vfolder), NULL);
return vfolder->priv->expression;
}
/**
* camel_vee_folder_add_folder:
* @vfolder: Virtual Folder object
* @subfolder: source CamelFolder to add to @vfolder
*
* Adds @subfolder as a source folder to @vfolder.
**/
void
camel_vee_folder_add_folder (CamelVeeFolder *vfolder,
CamelFolder *subfolder,
GCancellable *cancellable)
{
g_return_if_fail (CAMEL_IS_VEE_FOLDER (vfolder));
if (vfolder == (CamelVeeFolder *) subfolder) {
g_warning ("Adding a virtual folder to itself as source, ignored");
return;
}
g_rec_mutex_lock (&vfolder->priv->subfolder_lock);
if (g_list_find (vfolder->priv->subfolders, subfolder) == NULL) {
gint freeze_count;
vfolder->priv->subfolders = g_list_append (vfolder->priv->subfolders, g_object_ref (subfolder));
freeze_count = camel_folder_get_frozen_count (CAMEL_FOLDER (vfolder));
while (freeze_count > 0) {
camel_folder_freeze (subfolder);
freeze_count--;
}
} else {
/* nothing to do, it's already there */
g_rec_mutex_unlock (&vfolder->priv->subfolder_lock);
return;
}
g_rec_mutex_unlock (&vfolder->priv->subfolder_lock);
g_signal_connect (
subfolder, "changed",
G_CALLBACK (subfolder_changed), vfolder);
g_signal_connect (
subfolder, "deleted",
G_CALLBACK (subfolder_deleted), vfolder);
CAMEL_VEE_FOLDER_GET_CLASS (vfolder)->add_folder (vfolder, subfolder, cancellable);
}
/**
* camel_vee_folder_remove_folder:
* @vfolder: Virtual Folder object
* @subfolder: source CamelFolder to remove from @vfolder
*
* Removed the source folder, @subfolder, from the virtual folder, @vfolder.
**/
void
camel_vee_folder_remove_folder (CamelVeeFolder *vfolder,
CamelFolder *subfolder,
GCancellable *cancellable)
{
gint freeze_count;
g_return_if_fail (CAMEL_IS_VEE_FOLDER (vfolder));
g_rec_mutex_lock (&vfolder->priv->subfolder_lock);
if (g_list_find (vfolder->priv->subfolders, subfolder) == NULL) {
g_rec_mutex_unlock (&vfolder->priv->subfolder_lock);
return;
}
g_signal_handlers_disconnect_by_func (subfolder, subfolder_changed, vfolder);
g_signal_handlers_disconnect_by_func (subfolder, subfolder_deleted, vfolder);
vfolder->priv->subfolders = g_list_remove (vfolder->priv->subfolders, subfolder);
freeze_count = camel_folder_get_frozen_count (CAMEL_FOLDER (vfolder));
while (freeze_count > 0) {
camel_folder_thaw (subfolder);
freeze_count--;
}
g_rec_mutex_unlock (&vfolder->priv->subfolder_lock);
CAMEL_VEE_FOLDER_GET_CLASS (vfolder)->remove_folder (vfolder, subfolder, cancellable);
g_object_unref (subfolder);
}
/**
* camel_vee_folder_rebuild_folder:
* @vfolder: Virtual Folder object
* @subfolder: source CamelFolder to add to @vfolder
* @cancellable:
*
* Rebuild the folder @subfolder, if it should be.
**/
void
camel_vee_folder_rebuild_folder (CamelVeeFolder *vfolder,
CamelFolder *subfolder,
GCancellable *cancellable)
{
vee_folder_propagate_skipped_changes (vfolder);
CAMEL_VEE_FOLDER_GET_CLASS (vfolder)->rebuild_folder (vfolder, subfolder, cancellable);
}
static void
remove_folders (CamelFolder *folder,
CamelFolder *foldercopy,
CamelVeeFolder *vf)
{
camel_vee_folder_remove_folder (vf, folder, NULL);
g_object_unref (folder);
}
/**
* camel_vee_folder_set_folders:
* @vf:
* @folders:
*
* Set the whole list of folder sources on a vee folder.
**/
void
camel_vee_folder_set_folders (CamelVeeFolder *vf,
GList *folders,
GCancellable *cancellable)
{
CamelVeeFolderPrivate *p = CAMEL_VEE_FOLDER_GET_PRIVATE (vf);
GHashTable *remove = g_hash_table_new (NULL, NULL);
GList *l, *to_add = NULL;
CamelFolder *folder;
/* setup a table of all folders we have currently */
g_rec_mutex_lock (&vf->priv->subfolder_lock);
l = p->subfolders;
while (l) {
g_hash_table_insert (remove, l->data, l->data);
g_object_ref (l->data);
l = l->next;
}
g_rec_mutex_unlock (&vf->priv->subfolder_lock);
camel_folder_freeze (CAMEL_FOLDER (vf));
/* if we already have the folder, ignore it, otherwise mark to add it */
l = folders;
while (l) {
if ((folder = g_hash_table_lookup (remove, l->data))) {
g_hash_table_remove (remove, folder);
g_object_unref (folder);
} else {
to_add = g_list_prepend (to_add, g_object_ref (l->data));
}
l = l->next;
}
/* first remove any we still have */
g_hash_table_foreach (remove, (GHFunc) remove_folders, vf);
g_hash_table_destroy (remove);
/* then add those new */
for (l = to_add; l; l = l->next) {
camel_vee_folder_add_folder (vf, l->data, cancellable);
}
g_list_free_full (to_add, g_object_unref);
camel_folder_thaw (CAMEL_FOLDER (vf));
}
/**
* camel_vee_folder_add_vuid:
*
* FIXME Document me!
*
* Since: 3.6
**/
void
camel_vee_folder_add_vuid (CamelVeeFolder *vfolder,
CamelVeeMessageInfoData *mi_data,
CamelFolderChangeInfo *changes)
{
CamelVeeSummary *vsummary;
CamelVeeSubfolderData *sf_data;
CamelFolder *subfolder;
g_return_if_fail (CAMEL_IS_VEE_FOLDER (vfolder));
g_return_if_fail (mi_data != NULL);
g_return_if_fail (vee_folder_is_unmatched (vfolder));
sf_data = camel_vee_message_info_data_get_subfolder_data (mi_data);
subfolder = camel_vee_subfolder_data_get_folder (sf_data);
g_rec_mutex_lock (&vfolder->priv->changed_lock);
if (!camel_vee_folder_get_auto_update (vfolder) ||
g_hash_table_lookup (vfolder->priv->ignore_changed, subfolder) ||
g_hash_table_lookup (vfolder->priv->skipped_changes, subfolder)) {
g_hash_table_remove (vfolder->priv->unmatched_remove_changed, mi_data);
g_rec_mutex_lock (&vfolder->priv->subfolder_lock);
if (g_list_find (vfolder->priv->subfolders, subfolder)) {
/* postpone addition to the Unmatched folder, if the change was done
* in the Unmatched folder itself or auto-update is disabled */
g_hash_table_insert (
vfolder->priv->unmatched_add_changed,
g_object_ref (mi_data), GINT_TO_POINTER (1));
}
g_rec_mutex_unlock (&vfolder->priv->subfolder_lock);
g_rec_mutex_unlock (&vfolder->priv->changed_lock);
return;
}
g_rec_mutex_unlock (&vfolder->priv->changed_lock);
vsummary = CAMEL_VEE_SUMMARY (CAMEL_FOLDER (vfolder)->summary);
vee_folder_note_added_uid (vfolder, vsummary, mi_data, changes, FALSE);
}
/**
* camel_vee_folder_remove_vuid:
*
* FIXME Document me!
*
* Since: 3.6
**/
void
camel_vee_folder_remove_vuid (CamelVeeFolder *vfolder,
CamelVeeMessageInfoData *mi_data,
CamelFolderChangeInfo *changes)
{
CamelVeeSummary *vsummary;
CamelVeeSubfolderData *sf_data;
CamelVeeDataCache *data_cache;
CamelFolder *subfolder;
g_return_if_fail (CAMEL_IS_VEE_FOLDER (vfolder));
g_return_if_fail (mi_data != NULL);
g_return_if_fail (vee_folder_is_unmatched (vfolder));
sf_data = camel_vee_message_info_data_get_subfolder_data (mi_data);
subfolder = camel_vee_subfolder_data_get_folder (sf_data);
g_rec_mutex_lock (&vfolder->priv->changed_lock);
if (!camel_vee_folder_get_auto_update (vfolder) ||
g_hash_table_lookup (vfolder->priv->ignore_changed, subfolder) ||
g_hash_table_lookup (vfolder->priv->skipped_changes, subfolder)) {
g_hash_table_remove (vfolder->priv->unmatched_add_changed, mi_data);
g_rec_mutex_lock (&vfolder->priv->subfolder_lock);
if (g_list_find (vfolder->priv->subfolders, subfolder)) {
/* postpone removal from the Unmatched folder, if the change was done
* in the Unmatched folder itself or auto-update is disabled */
g_hash_table_insert (
vfolder->priv->unmatched_remove_changed,
g_object_ref (mi_data), GINT_TO_POINTER (1));
}
g_rec_mutex_unlock (&vfolder->priv->subfolder_lock);
g_rec_mutex_unlock (&vfolder->priv->changed_lock);
return;
}
g_rec_mutex_unlock (&vfolder->priv->changed_lock);
vsummary = CAMEL_VEE_SUMMARY (CAMEL_FOLDER (vfolder)->summary);
data_cache = vee_folder_get_data_cache (vfolder);
vee_folder_note_unmatch_uid (vfolder, vsummary, subfolder, data_cache, mi_data, changes);
}
/**
* camel_vee_folder_get_location:
* @vf:
* @vinfo:
* @realuid: if not NULL, set to the uid of the real message, must be
* g_free'd by caller.
*
* Find the real folder (and uid)
*
* Returns:
**/
CamelFolder *
camel_vee_folder_get_location (CamelVeeFolder *vf,
const CamelVeeMessageInfo *vinfo,
gchar **realuid)
{
CamelFolder *folder;
g_return_val_if_fail (CAMEL_IS_VEE_FOLDER (vf), NULL);
g_return_val_if_fail (vinfo != NULL, NULL);
folder = camel_folder_summary_get_folder (vinfo->orig_summary);
/* locking? yes? no? although the vfolderinfo is valid when obtained
* the folder in it might not necessarily be so ...? */
if (CAMEL_IS_VEE_FOLDER (folder)) {
CamelFolder *res;
const CamelVeeMessageInfo *vfinfo;
vfinfo = (CamelVeeMessageInfo *) camel_folder_get_message_info (folder, camel_message_info_uid (vinfo) + 8);
res = camel_vee_folder_get_location ((CamelVeeFolder *) folder, vfinfo, realuid);
camel_message_info_unref ((CamelMessageInfo *) vfinfo);
return res;
} else {
if (realuid)
*realuid = g_strdup (camel_message_info_uid (vinfo)+8);
return folder;
}
}
/**
* camel_vee_folder_get_vee_uid_folder:
*
* FIXME Document me!
*
* Since: 3.6
**/
CamelFolder *
camel_vee_folder_get_vee_uid_folder (CamelVeeFolder *vf,
const gchar *vee_message_uid)
{
CamelFolder *res;
CamelVeeDataCache *data_cache;
CamelVeeMessageInfoData *mi_data;
CamelVeeSubfolderData *sf_data;
g_return_val_if_fail (CAMEL_IS_VEE_FOLDER (vf), NULL);
g_return_val_if_fail (vee_message_uid, NULL);
res = NULL;
data_cache = vee_folder_get_data_cache (vf);
g_return_val_if_fail (data_cache != NULL, NULL);
mi_data = camel_vee_data_cache_get_message_info_data_by_vuid (data_cache, vee_message_uid);
if (mi_data) {
sf_data = camel_vee_message_info_data_get_subfolder_data (mi_data);
res = camel_vee_subfolder_data_get_folder (sf_data);
g_object_unref (mi_data);
}
return res;
}
/**
* camel_vee_folder_set_auto_update:
*
* FIXME Document me!
*
* Since: 3.6
**/
void
camel_vee_folder_set_auto_update (CamelVeeFolder *vfolder,
gboolean auto_update)
{
g_return_if_fail (CAMEL_IS_VEE_FOLDER (vfolder));
if (vfolder->priv->auto_update == auto_update)
return;
vfolder->priv->auto_update = auto_update;
g_object_notify (G_OBJECT (vfolder), "auto-update");
}
/**
* camel_vee_folder_get_auto_update:
*
* FIXME Document me!
*
* Since: 3.6
**/
gboolean
camel_vee_folder_get_auto_update (CamelVeeFolder *vfolder)
{
g_return_val_if_fail (CAMEL_IS_VEE_FOLDER (vfolder), FALSE);
return vfolder->priv->auto_update;
}
/**
* camel_vee_folder_ignore_next_changed_event:
* @vfolder: a #CamelVeeFolder
* @subfolder: a #CamelFolder folder
*
* The next @subfolder-'s 'changed' event will be silently ignored. This
* is usually used in virtual folders when the change was done in them,
* but it is neither vTrash nor vJunk folder. Doing this avoids unnecessary
* removals of messages which don't satisfy search criteria anymore,
* which could be done on asynchronous delivery of folder's 'changed' signal.
* These ignored changes are accumulated and used on folder refresh.
*
* Since: 3.2
**/
void
camel_vee_folder_ignore_next_changed_event (CamelVeeFolder *vfolder,
CamelFolder *subfolder)
{
g_return_if_fail (CAMEL_IS_VEE_FOLDER (vfolder));
g_return_if_fail (subfolder != NULL);
g_rec_mutex_lock (&vfolder->priv->changed_lock);
g_hash_table_insert (vfolder->priv->ignore_changed, subfolder, GINT_TO_POINTER (1));
g_rec_mutex_unlock (&vfolder->priv->changed_lock);
}
/**
* camel_vee_folder_remove_from_ignore_changed_event:
* @vfolder: a #CamelVeeFolder
* @subfolder: a #CamelFolder folder
*
* Make sure the next @subfolder-'s 'changed' event will not be silently ignored.
* This is a counter-part function of camel_vee_folder_ignore_next_changed_event(),
* when there was expected a change, which did not happen, to take back the previous
* ignore event request.
*
* Since: 3.12
**/
void
camel_vee_folder_remove_from_ignore_changed_event (CamelVeeFolder *vfolder,
CamelFolder *subfolder)
{
g_return_if_fail (CAMEL_IS_VEE_FOLDER (vfolder));
g_return_if_fail (subfolder != NULL);
g_rec_mutex_lock (&vfolder->priv->changed_lock);
g_hash_table_remove (vfolder->priv->ignore_changed, subfolder);
g_rec_mutex_unlock (&vfolder->priv->changed_lock);
}