[evolution-mapi] Add "Listen for server notifications" option to mail/book/calendar
- From: Milan Crha <mcrha src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [evolution-mapi] Add "Listen for server notifications" option to mail/book/calendar
- Date: Wed, 30 Nov 2011 17:28:27 +0000 (UTC)
commit 85b81798cc422d9bd311a0ad872f14dc6fb7df98
Author: Milan Crha <mcrha redhat com>
Date: Wed Nov 30 18:27:38 2011 +0100
Add "Listen for server notifications" option to mail/book/calendar
src/account-setup-eplugin/e-mapi-account-setup.c | 3 +
src/addressbook/e-book-backend-mapi-contacts.c | 106 ++++++
src/addressbook/e-book-backend-mapi.c | 133 +++++---
src/addressbook/e-book-backend-mapi.h | 1 +
src/calendar/e-cal-backend-mapi.c | 103 +++++-
src/camel/Makefile.am | 5 +-
src/camel/camel-mapi-folder.c | 9 +-
src/camel/camel-mapi-notifications.c | 271 --------------
src/camel/camel-mapi-notifications.h | 36 --
src/camel/camel-mapi-private.h | 46 ---
src/camel/camel-mapi-provider.c | 2 +
src/camel/camel-mapi-store.c | 435 ++++++++++++++++++++--
src/camel/camel-mapi-store.h | 2 -
src/libexchangemapi/camel-mapi-settings.c | 49 +++-
src/libexchangemapi/camel-mapi-settings.h | 5 +
src/libexchangemapi/e-mapi-connection.c | 308 +++++++++++++---
src/libexchangemapi/e-mapi-connection.h | 20 +-
17 files changed, 1009 insertions(+), 525 deletions(-)
---
diff --git a/src/account-setup-eplugin/e-mapi-account-setup.c b/src/account-setup-eplugin/e-mapi-account-setup.c
index 8cb042c..d009229 100644
--- a/src/account-setup-eplugin/e-mapi-account-setup.c
+++ b/src/account-setup-eplugin/e-mapi-account-setup.c
@@ -35,6 +35,7 @@
#include <libedataserverui/e-passwords.h>
#include <libedataserver/e-account.h>
#include <e-util/e-dialog-utils.h>
+#include <e-util/e-plugin-util.h>
#include "mail/em-config.h"
#include "e-mapi-account-setup.h"
#include <addressbook/gui/widgets/eab-config.h>
@@ -708,6 +709,8 @@ e_mapi_create (GtkWidget *parent, ESource *source, EMapiFolderType folder_type)
return NULL;
}
+ e_plugin_util_add_check (parent, _("Lis_ten for server notifications"), source, "server-notification", "true", NULL);
+
folders = NULL;
group = e_source_peek_group (source);
profile = g_strdup (e_source_get_property (source, "profile"));
diff --git a/src/addressbook/e-book-backend-mapi-contacts.c b/src/addressbook/e-book-backend-mapi-contacts.c
index 076763d..8017276 100644
--- a/src/addressbook/e-book-backend-mapi-contacts.c
+++ b/src/addressbook/e-book-backend-mapi-contacts.c
@@ -584,6 +584,74 @@ gather_known_uids_cb (EMapiConnection *conn,
}
static void
+ebbmc_server_notification_cb (EMapiConnection *conn,
+ guint event_mask,
+ gpointer event_data,
+ gpointer user_data)
+{
+ EBookBackendMAPI *ebma = user_data;
+ EBookBackendMAPIContactsPrivate *priv;
+ mapi_id_t update_folder1 = 0, update_folder2 = 0;
+
+ g_return_if_fail (ebma != NULL);
+
+ switch (event_mask) {
+ case fnevNewMail:
+ case fnevNewMail | fnevMbit: {
+ struct NewMailNotification *newmail = event_data;
+
+ if (newmail)
+ update_folder1 = newmail->FID;
+ } break;
+ case fnevObjectCreated:
+ case fnevMbit | fnevObjectCreated: {
+ struct MessageCreatedNotification *msgcreated = event_data;
+
+ if (msgcreated)
+ update_folder1 = msgcreated->FID;
+ } break;
+ case fnevObjectModified:
+ case fnevMbit | fnevObjectModified: {
+ struct MessageModifiedNotification *msgmodified = event_data;
+
+ if (msgmodified)
+ update_folder1 = msgmodified->FID;
+ } break;
+ case fnevObjectDeleted:
+ case fnevMbit | fnevObjectDeleted: {
+ struct MessageDeletedNotification *msgdeleted = event_data;
+
+ if (msgdeleted)
+ update_folder1 = msgdeleted->FID;
+ } break;
+ case fnevObjectMoved:
+ case fnevMbit | fnevObjectMoved: {
+ struct MessageMoveCopyNotification *msgmoved = event_data;
+
+ if (msgmoved) {
+ update_folder1 = msgmoved->OldFID;
+ update_folder2 = msgmoved->FID;
+ }
+ } break;
+ case fnevObjectCopied:
+ case fnevMbit | fnevObjectCopied: {
+ struct MessageMoveCopyNotification *msgcopied = event_data;
+
+ if (msgcopied) {
+ update_folder1 = msgcopied->OldFID;
+ update_folder2 = msgcopied->FID;
+ }
+ } break;
+ default:
+ break;
+ }
+
+ priv = ((EBookBackendMAPIContacts *) ebma)->priv;
+ if (priv->fid == update_folder1 || priv->fid == update_folder2)
+ e_book_backend_mapi_refresh_cache (ebma);
+}
+
+static void
ebbm_contacts_open (EBookBackendMAPI *ebma, GCancellable *cancellable, gboolean only_if_exists, GError **perror)
{
ESource *source = e_backend_get_source (E_BACKEND (ebma));
@@ -612,7 +680,45 @@ ebbm_contacts_open (EBookBackendMAPI *ebma, GCancellable *cancellable, gboolean
static void
ebbm_contacts_connection_status_changed (EBookBackendMAPI *ebma, gboolean is_online)
{
+ ESource *source;
+ EBookBackendMAPIContactsPrivate *priv = ((EBookBackendMAPIContacts *) ebma)->priv;
+
e_book_backend_notify_readonly (E_BOOK_BACKEND (ebma), !is_online);
+
+ if (!is_online)
+ return;
+
+ source = e_backend_get_source (E_BACKEND (ebma));
+ if (source && g_strcmp0 (e_source_get_property (source, "server-notification"), "true") == 0) {
+ EMapiConnection *conn;
+ mapi_object_t obj_folder;
+ gboolean status;
+
+ e_book_backend_mapi_lock_connection (ebma);
+
+ conn = e_book_backend_mapi_get_connection (ebma);
+ if (!conn) {
+ e_book_backend_mapi_unlock_connection (ebma);
+ return;
+ }
+
+ if (priv->is_public_folder)
+ status = e_mapi_connection_open_public_folder (conn, priv->fid, &obj_folder, NULL, NULL);
+ else
+ status = e_mapi_connection_open_personal_folder (conn, priv->fid, &obj_folder, NULL, NULL);
+
+ if (status) {
+ e_mapi_connection_enable_notifications (conn, &obj_folder,
+ fnevObjectCreated | fnevObjectModified | fnevObjectDeleted | fnevObjectMoved | fnevObjectCopied,
+ NULL, NULL);
+
+ e_mapi_connection_close_folder (conn, &obj_folder, NULL, NULL);
+ }
+
+ g_signal_connect (conn, "server-notification", G_CALLBACK (ebbmc_server_notification_cb), ebma);
+
+ e_book_backend_mapi_unlock_connection (ebma);
+ }
}
static void
diff --git a/src/addressbook/e-book-backend-mapi.c b/src/addressbook/e-book-backend-mapi.c
index bf28e95..c344310 100644
--- a/src/addressbook/e-book-backend-mapi.c
+++ b/src/addressbook/e-book-backend-mapi.c
@@ -65,6 +65,7 @@ struct _EBookBackendMAPIPrivate
guint32 last_server_contact_count;
time_t last_modify_time;
+ gboolean server_dirty;
};
#define ELEMENT_TYPE_MASK 0xF /* mask where the real type of the element is stored */
@@ -219,7 +220,6 @@ ebbm_update_cache_cb (gpointer data)
EBookBackendMAPIPrivate *priv;
EBookBackendMAPIClass *ebmac;
guint32 server_stored_contacts = 0;
- GHashTable *local_known_uids, *server_known_uids;
time_t restr_tt = 0;
gboolean partial_update = FALSE;
GCancellable *cancellable;
@@ -236,76 +236,83 @@ ebbm_update_cache_cb (gpointer data)
ebmac = E_BOOK_BACKEND_MAPI_GET_CLASS (ebma);
g_return_val_if_fail (ebmac != NULL, NULL);
+
cancellable = priv->update_cache;
g_cancellable_reset (cancellable);
- local_known_uids = e_book_backend_sqlitedb_get_uids_and_rev (priv->db, EMA_EBB_CACHE_FOLDERID, &error);
- server_known_uids = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
-
- if (!error && !g_cancellable_is_cancelled (cancellable) && ebmac->op_get_contacts_count) {
- ebmac->op_get_contacts_count (ebma, &server_stored_contacts, cancellable, &error);
- }
+ do {
+ GHashTable *local_known_uids, *server_known_uids;
- if (!error && !g_cancellable_is_cancelled (cancellable) && ebmac->op_list_known_uids) {
- struct ListKnownUidsData lku;
+ priv->server_dirty = FALSE;
- restr_tt = priv->last_modify_time && server_stored_contacts == g_hash_table_size (local_known_uids) ? priv->last_modify_time + 1 : 0;
- partial_update = restr_tt > 0;
+ local_known_uids = e_book_backend_sqlitedb_get_uids_and_rev (priv->db, EMA_EBB_CACHE_FOLDERID, &error);
+ server_known_uids = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
- lku.uid_to_rev = server_known_uids;
- lku.latest_last_modify = priv->last_modify_time;
+ if (!error && !g_cancellable_is_cancelled (cancellable) && ebmac->op_get_contacts_count) {
+ ebmac->op_get_contacts_count (ebma, &server_stored_contacts, cancellable, &error);
+ }
- ebmac->op_list_known_uids (ebma, partial_update ? e_mapi_utils_build_last_modify_restriction : NULL, &restr_tt, &lku, cancellable, &error);
+ if (!error && !g_cancellable_is_cancelled (cancellable) && ebmac->op_list_known_uids) {
+ struct ListKnownUidsData lku;
- restr_tt = lku.latest_last_modify;
- }
+ restr_tt = priv->last_modify_time && server_stored_contacts == g_hash_table_size (local_known_uids) ? priv->last_modify_time + 1 : 0;
+ partial_update = restr_tt > 0;
- if (!error && !g_cancellable_is_cancelled (cancellable) && ebmac->op_transfer_contacts && local_known_uids) {
- GSList *uids = NULL;
- GHashTableIter iter;
- gpointer key, value;
+ lku.uid_to_rev = server_known_uids;
+ lku.latest_last_modify = priv->last_modify_time;
- g_hash_table_iter_init (&iter, server_known_uids);
- while (g_hash_table_iter_next (&iter, &key, &value)) {
- const gchar *uid = key, *rev = value, *local_rev;
+ ebmac->op_list_known_uids (ebma, partial_update ? e_mapi_utils_build_last_modify_restriction : NULL, &restr_tt, &lku, cancellable, &error);
- local_rev = g_hash_table_lookup (local_known_uids, uid);
- if (g_strcmp0 (local_rev, rev) != 0) {
- uids = g_slist_prepend (uids, (gpointer) uid);
- }
-
- g_hash_table_remove (local_known_uids, uid);
+ restr_tt = lku.latest_last_modify;
}
- if (uids)
- ebbm_transfer_contacts (ebma, uids, NULL, cancellable, &error);
+ if (!error && !g_cancellable_is_cancelled (cancellable) && ebmac->op_transfer_contacts && local_known_uids) {
+ GSList *uids = NULL;
+ GHashTableIter iter;
+ gpointer key, value;
- if (!error && !g_cancellable_is_cancelled (cancellable) && !partial_update) {
- g_hash_table_iter_init (&iter, local_known_uids);
+ g_hash_table_iter_init (&iter, server_known_uids);
while (g_hash_table_iter_next (&iter, &key, &value)) {
- const gchar *uid = key;
+ const gchar *uid = key, *rev = value, *local_rev;
- if (!uid)
- continue;
+ local_rev = g_hash_table_lookup (local_known_uids, uid);
+ if (g_strcmp0 (local_rev, rev) != 0) {
+ uids = g_slist_prepend (uids, (gpointer) uid);
+ }
- e_book_backend_mapi_notify_contact_removed (ebma, uid);
+ g_hash_table_remove (local_known_uids, uid);
}
- }
- priv->last_server_contact_count = server_stored_contacts;
- priv->last_modify_time = restr_tt;
+ if (uids)
+ ebbm_transfer_contacts (ebma, uids, NULL, cancellable, &error);
- /* has borrowed data from server_known_uids */
- g_slist_free (uids);
- }
+ if (!error && !g_cancellable_is_cancelled (cancellable) && !partial_update) {
+ g_hash_table_iter_init (&iter, local_known_uids);
+ while (g_hash_table_iter_next (&iter, &key, &value)) {
+ const gchar *uid = key;
- priv->last_update_cache = time(NULL);
+ if (!uid)
+ continue;
- g_hash_table_destroy (server_known_uids);
- if (local_known_uids)
- g_hash_table_destroy (local_known_uids);
- if (error)
- g_error_free (error);
+ e_book_backend_mapi_notify_contact_removed (ebma, uid);
+ }
+ }
+
+ priv->last_server_contact_count = server_stored_contacts;
+ priv->last_modify_time = restr_tt;
+
+ /* has borrowed data from server_known_uids */
+ g_slist_free (uids);
+ }
+
+ priv->last_update_cache = time(NULL);
+
+ g_hash_table_destroy (server_known_uids);
+ if (local_known_uids)
+ g_hash_table_destroy (local_known_uids);
+ } while (!error && priv->server_dirty && !g_cancellable_is_cancelled (cancellable));
+
+ g_clear_error (&error);
complete_views (ebma);
@@ -340,6 +347,8 @@ ebbm_maybe_invoke_cache_update (EBookBackendMAPI *ebma)
if (time (NULL) - priv->last_update_cache >= 60 * 10) {
g_object_ref (ebma);
+ g_cancellable_reset (priv->update_cache);
+ priv->server_dirty = FALSE;
priv->update_cache_thread = g_thread_create (ebbm_update_cache_cb, ebma, TRUE, NULL);
if (!priv->update_cache_thread)
g_object_unref (ebma);
@@ -1267,6 +1276,7 @@ e_book_backend_mapi_init (EBookBackendMAPI *ebma)
ebma->priv->last_update_cache = 0;
ebma->priv->last_server_contact_count = 0;
ebma->priv->last_modify_time = 0;
+ ebma->priv->server_dirty = FALSE;
g_signal_connect (
ebma, "notify::online",
@@ -1590,6 +1600,19 @@ e_book_backend_mapi_cache_get (EBookBackendMAPI *ebma, const gchar *key)
return e_book_backend_sqlitedb_get_key_value (ebma->priv->db, EMA_EBB_CACHE_FOLDERID, key, NULL);
}
+void
+e_book_backend_mapi_refresh_cache (EBookBackendMAPI *ebma)
+{
+ g_return_if_fail (ebma != NULL);
+ g_return_if_fail (E_IS_BOOK_BACKEND_MAPI (ebma));
+
+ ebma->priv->last_update_cache = 0;
+ ebma->priv->last_modify_time = 0;
+ ebma->priv->server_dirty = TRUE;
+
+ ebbm_maybe_invoke_cache_update (ebma);
+}
+
/* utility functions/macros */
/* 'data' is one of GET_ALL_KNOWN_IDS or GET_UIDS_ONLY */
@@ -1734,9 +1757,21 @@ mapi_book_utils_contact_from_props (EMapiConnection *conn, mapi_id_t fid, const
if (g_str_equal (get_str_proptag (PR_MESSAGE_CLASS), IPM_DISTLIST)) {
const struct mapi_SBinaryArray *members, *members_dlist;
+ const struct FILETIME *last_modification;
GSList *attrs = NULL, *a;
gint i;
+ last_modification = get_proptag (PidTagLastModificationTime);
+ if (last_modification) {
+ gchar *buff = NULL;
+
+ buff = mapi_book_utils_timet_to_string (e_mapi_util_filetime_to_time_t (last_modification));
+ if (buff)
+ e_contact_set (contact, E_CONTACT_REV, buff);
+
+ g_free (buff);
+ }
+
/* it's a contact list/distribution list, fetch members and return it */
e_contact_set (contact, E_CONTACT_IS_LIST, GINT_TO_POINTER (TRUE));
/* we do not support this option, same as GroupWise */
diff --git a/src/addressbook/e-book-backend-mapi.h b/src/addressbook/e-book-backend-mapi.h
index 4e84bcb..504312e 100644
--- a/src/addressbook/e-book-backend-mapi.h
+++ b/src/addressbook/e-book-backend-mapi.h
@@ -112,6 +112,7 @@ gboolean e_book_backend_mapi_notify_contact_update (EBookBackendMAPI *ebma, EDat
void e_book_backend_mapi_notify_contact_removed (EBookBackendMAPI *ebma, const gchar *uid);
void e_book_backend_mapi_cache_set (EBookBackendMAPI *ebma, const gchar *key, const gchar *value);
gchar *e_book_backend_mapi_cache_get (EBookBackendMAPI *ebma, const gchar *key);
+void e_book_backend_mapi_refresh_cache (EBookBackendMAPI *ebma);
/* utility functions/macros */
diff --git a/src/calendar/e-cal-backend-mapi.c b/src/calendar/e-cal-backend-mapi.c
index 0b29951..656c541 100644
--- a/src/calendar/e-cal-backend-mapi.c
+++ b/src/calendar/e-cal-backend-mapi.c
@@ -70,6 +70,7 @@ struct _ECalBackendMAPIPrivate {
mapi_id_t fid;
uint32_t olFolder;
gchar *profile;
+ gboolean is_public_folder;
EMapiConnection *conn;
/* These fields are entirely for access rights */
@@ -286,18 +287,15 @@ ecbm_remove (ECalBackend *backend, EDataCal *cal, GCancellable *cancellable, GEr
{
ECalBackendMAPI *cbmapi;
ECalBackendMAPIPrivate *priv;
- ESource *source = NULL;
cbmapi = E_CAL_BACKEND_MAPI (backend);
priv = cbmapi->priv;
- source = e_backend_get_source (E_BACKEND (cbmapi));
-
if (!e_backend_get_online (E_BACKEND (backend)) || !priv->conn || !e_mapi_connection_connected (priv->conn)) {
g_propagate_error (perror, EDC_ERROR (RepositoryOffline));
return;
}
- if (g_strcmp0 (e_source_get_property (source, "public"), "yes") != 0) {
+ if (!priv->is_public_folder) {
GError *mapi_error = NULL;
if (!e_mapi_connection_remove_folder (priv->conn, priv->fid, 0, cancellable, &mapi_error)) {
@@ -694,7 +692,6 @@ update_local_cache (ECalBackendMAPI *cbmapi, GCancellable *cancellable)
{
ECalBackendMAPIPrivate *priv;
EMapiConnection *conn;
- ESource *source = NULL;
struct ListCalendarObjectsData lco;
GSList *iter, *components;
mapi_object_t obj_folder;
@@ -705,7 +702,6 @@ update_local_cache (ECalBackendMAPI *cbmapi, GCancellable *cancellable)
struct FolderBasicPropertiesData fbp;
priv = cbmapi->priv;
- source = e_backend_get_source (E_BACKEND (cbmapi));
if (!e_backend_get_online (E_BACKEND (cbmapi)))
return FALSE;
@@ -717,7 +713,7 @@ update_local_cache (ECalBackendMAPI *cbmapi, GCancellable *cancellable)
conn = g_object_ref (priv->conn);
- if (g_strcmp0 (e_source_get_property (source, "public"), "yes") == 0) {
+ if (priv->is_public_folder) {
success = e_mapi_connection_open_public_folder (conn, priv->fid, &obj_folder, cancellable, &mapi_error);
} else {
success = e_mapi_connection_open_personal_folder (conn, priv->fid, &obj_folder, cancellable, &mapi_error);
@@ -1057,6 +1053,74 @@ ecbm_connect (ECalBackendMAPI *cbmapi, GError **perror)
}
static void
+ecbm_server_notification_cb (EMapiConnection *conn,
+ guint event_mask,
+ gpointer event_data,
+ gpointer user_data)
+{
+ ECalBackendMAPI *cbmapi = user_data;
+ ECalBackendMAPIPrivate *priv;
+ mapi_id_t update_folder1 = 0, update_folder2 = 0;
+
+ g_return_if_fail (cbmapi != NULL);
+
+ switch (event_mask) {
+ case fnevNewMail:
+ case fnevNewMail | fnevMbit: {
+ struct NewMailNotification *newmail = event_data;
+
+ if (newmail)
+ update_folder1 = newmail->FID;
+ } break;
+ case fnevObjectCreated:
+ case fnevMbit | fnevObjectCreated: {
+ struct MessageCreatedNotification *msgcreated = event_data;
+
+ if (msgcreated)
+ update_folder1 = msgcreated->FID;
+ } break;
+ case fnevObjectModified:
+ case fnevMbit | fnevObjectModified: {
+ struct MessageModifiedNotification *msgmodified = event_data;
+
+ if (msgmodified)
+ update_folder1 = msgmodified->FID;
+ } break;
+ case fnevObjectDeleted:
+ case fnevMbit | fnevObjectDeleted: {
+ struct MessageDeletedNotification *msgdeleted = event_data;
+
+ if (msgdeleted)
+ update_folder1 = msgdeleted->FID;
+ } break;
+ case fnevObjectMoved:
+ case fnevMbit | fnevObjectMoved: {
+ struct MessageMoveCopyNotification *msgmoved = event_data;
+
+ if (msgmoved) {
+ update_folder1 = msgmoved->OldFID;
+ update_folder2 = msgmoved->FID;
+ }
+ } break;
+ case fnevObjectCopied:
+ case fnevMbit | fnevObjectCopied: {
+ struct MessageMoveCopyNotification *msgcopied = event_data;
+
+ if (msgcopied) {
+ update_folder1 = msgcopied->OldFID;
+ update_folder2 = msgcopied->FID;
+ }
+ } break;
+ default:
+ break;
+ }
+
+ priv = cbmapi->priv;
+ if (priv->fid == update_folder1 || priv->fid == update_folder2)
+ run_delta_thread (cbmapi);
+}
+
+static void
ecbm_connect_user (ECalBackend *backend, GCancellable *cancellable, const gchar *password, GError **perror)
{
ECalBackendMAPI *cbmapi;
@@ -1084,7 +1148,29 @@ ecbm_connect_user (ECalBackend *backend, GCancellable *cancellable, const gchar
g_object_unref (old_conn);
if (priv->conn && e_mapi_connection_connected (priv->conn)) {
- /* Success */;
+ /* Success */
+ ESource *source;
+
+ source = e_backend_get_source (E_BACKEND (cbmapi));
+ if (source && g_strcmp0 (e_source_get_property (source, "server-notification"), "true") == 0) {
+ mapi_object_t obj_folder;
+ gboolean status;
+
+ if (priv->is_public_folder)
+ status = e_mapi_connection_open_public_folder (priv->conn, priv->fid, &obj_folder, NULL, NULL);
+ else
+ status = e_mapi_connection_open_personal_folder (priv->conn, priv->fid, &obj_folder, NULL, NULL);
+
+ if (status) {
+ e_mapi_connection_enable_notifications (priv->conn, &obj_folder,
+ fnevObjectCreated | fnevObjectModified | fnevObjectDeleted | fnevObjectMoved | fnevObjectCopied,
+ NULL, NULL);
+
+ e_mapi_connection_close_folder (priv->conn, &obj_folder, NULL, NULL);
+ }
+
+ g_signal_connect (priv->conn, "server-notification", G_CALLBACK (ecbm_server_notification_cb), cbmapi);
+ }
} else {
mapi_error_to_edc_error (perror, mapi_error, g_error_matches (mapi_error, E_MAPI_ERROR, MAPI_E_NETWORK_ERROR) ? OtherError : AuthenticationFailed, NULL);
if (mapi_error)
@@ -1197,6 +1283,7 @@ ecbm_open (ECalBackend *backend, EDataCal *cal, GCancellable *cancellable, gbool
e_mapi_util_mapi_id_from_string (fid, &priv->fid);
priv->olFolder = olFolder;
+ priv->is_public_folder = g_strcmp0 (e_source_get_property (esource, "public"), "yes") == 0;
krb_sso = e_source_get_property (esource, "kerberos");
g_mutex_unlock (priv->mutex);
diff --git a/src/camel/Makefile.am b/src/camel/Makefile.am
index d2cb200..ce4cc97 100644
--- a/src/camel/Makefile.am
+++ b/src/camel/Makefile.am
@@ -19,7 +19,6 @@ libcamelmapi_la_SOURCES = \
camel-mapi-folder-summary.c \
camel-mapi-store.c \
camel-mapi-store-summary.c \
- camel-mapi-notifications.c \
camel-mapi-transport.c
noinst_HEADERS = \
@@ -27,9 +26,7 @@ noinst_HEADERS = \
camel-mapi-folder-summary.h \
camel-mapi-store.h \
camel-mapi-store-summary.h \
- camel-mapi-transport.h \
- camel-mapi-notifications.h \
- camel-mapi-private.h
+ camel-mapi-transport.h
libcamelmapi_la_LDFLAGS = -avoid-version -module $(NO_UNDEFINED)
libcamelmapi_la_LIBADD = \
diff --git a/src/camel/camel-mapi-folder.c b/src/camel/camel-mapi-folder.c
index 646ab39..f44ffe4 100644
--- a/src/camel/camel-mapi-folder.c
+++ b/src/camel/camel-mapi-folder.c
@@ -40,13 +40,17 @@
#include "camel-mapi-store.h"
#include "camel-mapi-store-summary.h"
#include "camel-mapi-folder.h"
-#include "camel-mapi-private.h"
#include "camel-mapi-folder-summary.h"
#define DEBUG_FN( ) printf("----%p %s\n", g_thread_self(), G_STRFUNC);
#define SUMMARY_FETCH_BATCH_COUNT 150
#define d(x)
+#define CAMEL_MAPI_FOLDER_LOCK(f, l) \
+ (g_static_mutex_lock(&((CamelMapiFolder *)f)->priv->l))
+#define CAMEL_MAPI_FOLDER_UNLOCK(f, l) \
+ (g_static_mutex_unlock(&((CamelMapiFolder *)f)->priv->l))
+
extern gint camel_application_is_exiting;
struct _CamelMapiFolderPrivate {
@@ -460,7 +464,8 @@ gather_changed_objects_to_slist (EMapiConnection *conn,
if (info) {
CamelMapiMessageInfo *minfo = (CamelMapiMessageInfo *) info;
- if (minfo->last_modified != object_data->last_modified) {
+ if (minfo->last_modified != object_data->last_modified
+ && (object_data->msg_flags & MSGFLAG_UNMODIFIED) == 0) {
update = TRUE;
} else {
guint32 mask = CAMEL_MESSAGE_SEEN | CAMEL_MESSAGE_ATTACHMENTS, flags = 0;
diff --git a/src/camel/camel-mapi-provider.c b/src/camel/camel-mapi-provider.c
index 47a9b8f..e0a399f 100644
--- a/src/camel/camel-mapi-provider.c
+++ b/src/camel/camel-mapi-provider.c
@@ -57,6 +57,8 @@ static CamelProviderConfEntry mapi_conf_entries[] = {
N_("Check new messages for _Junk contents"), "0" },
{ CAMEL_PROVIDER_CONF_CHECKBOX, "filter-junk-inbox", "filter-junk",
N_("Only check for Junk messag_es in the Inbox folder"), "0" },
+ { CAMEL_PROVIDER_CONF_CHECKBOX, "listen-notifications", NULL,
+ N_("Lis_ten for server notifications"), "0" },
{ CAMEL_PROVIDER_CONF_SECTION_END },
{ CAMEL_PROVIDER_CONF_END }
diff --git a/src/camel/camel-mapi-store.c b/src/camel/camel-mapi-store.c
index a513910..97216d0 100644
--- a/src/camel/camel-mapi-store.c
+++ b/src/camel/camel-mapi-store.c
@@ -25,11 +25,17 @@
#include <config.h>
#endif
+#include <stdint.h>
+#include <stdbool.h>
+#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
+#include <sys/types.h>
#include <errno.h>
+#include <libmapi/libmapi.h>
+
#include <glib/gi18n-lib.h>
#include <glib/gstdio.h>
@@ -38,18 +44,10 @@
#include "camel-mapi-settings.h"
#include "camel-mapi-store-summary.h"
#include "camel-mapi-folder-summary.h"
-#include "camel-mapi-notifications.h"
#include "account-setup-eplugin/e-mapi-account-listener.h"
#include <e-mapi-utils.h>
-#include <sys/types.h>
-#include <stdint.h>
-#include <stdbool.h>
-#include <stdlib.h>
-#include <libmapi/libmapi.h>
-#include <param.h>
-
-#define d(x) printf("%s:%s:%s \n", G_STRLOC, G_STRFUNC, x)
+#define d(x)
struct _CamelMapiStorePrivate {
EMapiConnection *conn;
@@ -61,7 +59,12 @@ struct _CamelMapiStorePrivate {
GHashTable *default_folders; /*Default Type : Folder ID*/
gboolean folders_synced; /* whether were synced folder list already */
- gpointer notification_data; /* pointer to a notification data; can be only one */
+
+ GStaticRecMutex updates_lock;
+ GCancellable *updates_cancellable; /* cancelled on dispose or disconnect */
+ GSList *update_folder_names; /* gchar *foldername */
+ guint update_folder_id;
+ guint update_folder_list_id;
};
/* Forward Declarations */
@@ -82,6 +85,7 @@ static gboolean mapi_connect_sync(CamelService *, GCancellable *cancellable, GEr
static gboolean mapi_disconnect_sync(CamelService *, gboolean , GCancellable *cancellable, GError **);
static CamelAuthenticationResult mapi_authenticate_sync (CamelService *, const gchar *mechanism, GCancellable *, GError **);
static GList *mapi_query_auth_types_sync(CamelService *, GCancellable *cancellable, GError **);
+static void camel_mapi_store_server_notification_cb (EMapiConnection *conn, guint event_mask, gpointer event_data, gpointer user_data);
/* store methods */
static CamelFolderInfo * mapi_build_folder_info(CamelMapiStore *mapi_store, const gchar *parent_name, const gchar *folder_name);
@@ -797,13 +801,52 @@ mapi_rename_folder_infos (CamelMapiStore *mapi_store, const gchar *old_name, con
}
static void
+stop_pending_updates (CamelMapiStore *mapi_store)
+{
+ CamelMapiStorePrivate *priv;
+
+ g_return_if_fail (mapi_store != NULL);
+ g_return_if_fail (mapi_store->priv != NULL);
+
+ priv = mapi_store->priv;
+
+ g_static_rec_mutex_lock (&priv->updates_lock);
+ if (priv->updates_cancellable) {
+ g_cancellable_cancel (priv->updates_cancellable);
+ g_object_unref (priv->updates_cancellable);
+ priv->updates_cancellable = NULL;
+ }
+
+ if (priv->update_folder_names) {
+ g_slist_free_full (priv->update_folder_names, g_free);
+ priv->update_folder_names = NULL;
+ }
+
+ if (priv->update_folder_id) {
+ g_source_remove (priv->update_folder_id);
+ priv->update_folder_id = 0;
+ }
+
+ if (priv->update_folder_list_id) {
+ g_source_remove (priv->update_folder_list_id);
+ priv->update_folder_list_id = 0;
+ }
+
+ g_static_rec_mutex_unlock (&priv->updates_lock);
+}
+
+static void
mapi_store_dispose (GObject *object)
{
CamelMapiStorePrivate *priv;
priv = CAMEL_MAPI_STORE (object)->priv;
+ stop_pending_updates (CAMEL_MAPI_STORE (object));
+
if (priv->conn != NULL) {
+ g_signal_handlers_disconnect_by_func (priv->conn, camel_mapi_store_server_notification_cb, object);
+
g_object_unref (priv->conn);
priv->conn = NULL;
}
@@ -833,6 +876,8 @@ mapi_store_finalize (GObject *object)
if (priv->container_hash != NULL)
g_hash_table_destroy (priv->container_hash);
+ g_static_rec_mutex_free (&priv->updates_lock);
+
/* Chain up to parent's finalize() method. */
G_OBJECT_CLASS (camel_mapi_store_parent_class)->finalize (object);
}
@@ -1600,6 +1645,12 @@ static void
camel_mapi_store_init (CamelMapiStore *mapi_store)
{
mapi_store->priv = G_TYPE_INSTANCE_GET_PRIVATE (mapi_store, CAMEL_TYPE_MAPI_STORE, CamelMapiStorePrivate);
+
+ g_static_rec_mutex_init (&mapi_store->priv->updates_lock);
+ mapi_store->priv->updates_cancellable = NULL;
+ mapi_store->priv->update_folder_names = NULL;
+ mapi_store->priv->update_folder_id = 0;
+ mapi_store->priv->update_folder_list_id = 0;
}
/* service methods */
@@ -1677,7 +1728,6 @@ mapi_connect_sync (CamelService *service,
CamelServiceConnectionStatus status;
CamelSession *session;
gchar *name;
- guint16 event_mask = 0;
session = camel_service_get_session (service);
@@ -1708,14 +1758,6 @@ mapi_connect_sync (CamelService *service,
camel_offline_store_set_online_sync (
CAMEL_OFFLINE_STORE (store), TRUE, cancellable, NULL);
- /* Start event monitor */
- event_mask = fnevNewMail | fnevObjectCreated | fnevObjectDeleted |
- fnevObjectModified | fnevObjectMoved | fnevObjectCopied;
-
- /* use MAPI_LISTEN_NOTIFY=1 to enable notifications */
- if (!store->priv->notification_data && g_getenv ("MAPI_LISTEN_NOTIFY") != NULL)
- store->priv->notification_data = camel_mapi_notification_listener_start (store, event_mask, MAPI_EVENTS_USE_STORE);
-
camel_store_summary_save (store->summary);
camel_service_unlock (service, CAMEL_SERVICE_REC_CONNECT_LOCK);
@@ -1723,15 +1765,6 @@ mapi_connect_sync (CamelService *service,
return TRUE;
}
-void
-camel_mapi_store_unset_notification_data (CamelMapiStore *mstore)
-{
- g_return_if_fail (mstore != NULL);
- g_return_if_fail (CAMEL_IS_MAPI_STORE (mstore));
-
- mstore->priv->notification_data = NULL;
-}
-
static gboolean
mapi_disconnect_sync (CamelService *service,
gboolean clean,
@@ -1740,13 +1773,12 @@ mapi_disconnect_sync (CamelService *service,
{
CamelMapiStore *store = CAMEL_MAPI_STORE (service);
- /* Disconnect from event monitor */
- if (store->priv->notification_data) {
- camel_mapi_notification_listener_stop (store, store->priv->notification_data);
- store->priv->notification_data = NULL;
- }
+ stop_pending_updates (store);
if (store->priv->conn) {
+ g_signal_handlers_disconnect_by_func (store->priv->conn, camel_mapi_store_server_notification_cb, store);
+ e_mapi_connection_disable_notifications (store->priv->conn, NULL, cancellable, error);
+
/* Close the mapi subsystem */
g_object_unref (store->priv->conn);
store->priv->conn = NULL;
@@ -1757,6 +1789,335 @@ mapi_disconnect_sync (CamelService *service,
return TRUE;
}
+struct ScheduleUpdateData
+{
+ GCancellable *cancellable;
+ CamelMapiStore *mapi_store;
+ GSList *foldernames;
+ guint expected_id;
+};
+
+static void
+free_schedule_update_data (gpointer ptr)
+{
+ struct ScheduleUpdateData *sud = ptr;
+
+ if (!sud)
+ return;
+
+ if (sud->cancellable)
+ g_object_unref (sud->cancellable);
+ g_slist_free_full (sud->foldernames, g_free);
+ g_free (sud);
+}
+
+static gpointer
+camel_mapi_folder_update_thread (gpointer user_data)
+{
+ struct ScheduleUpdateData *sud = user_data;
+ CamelMapiStore *mapi_store;
+ GSList *fn;
+
+ g_return_val_if_fail (sud != NULL, NULL);
+
+ mapi_store = g_object_ref (sud->mapi_store);
+
+ for (fn = sud->foldernames; fn && !g_cancellable_is_cancelled (sud->cancellable); fn = fn->next) {
+ const gchar *foldername = fn->data;
+ CamelFolder *folder;
+
+ if (!foldername)
+ continue;
+
+ folder = camel_store_get_folder_sync (CAMEL_STORE (mapi_store), foldername, 0, sud->cancellable, NULL);
+ if (folder) {
+ camel_folder_refresh_info_sync (folder, sud->cancellable, NULL);
+ g_object_unref (folder);
+ }
+ }
+
+ if (!g_cancellable_is_cancelled (sud->cancellable) &&
+ !mapi_store->priv->folders_synced)
+ mapi_folders_sync (sud->mapi_store, CAMEL_STORE_FOLDER_INFO_RECURSIVE | CAMEL_STORE_FOLDER_INFO_SUBSCRIBED, sud->cancellable, NULL);
+
+ g_object_unref (mapi_store);
+
+ free_schedule_update_data (sud);
+
+ return NULL;
+}
+
+static void
+run_update_thread (CamelMapiStore *mapi_store,
+ GCancellable *cancellable,
+ GSList *foldernames)
+{
+ struct ScheduleUpdateData *sud;
+
+ g_return_if_fail (mapi_store != NULL);
+ g_return_if_fail (cancellable != NULL);
+
+ sud = g_new0 (struct ScheduleUpdateData, 1);
+ sud->mapi_store = mapi_store;
+ sud->cancellable = g_object_ref (cancellable);
+ sud->foldernames = foldernames;
+
+ g_thread_create (camel_mapi_folder_update_thread, sud, FALSE, NULL);
+}
+
+static gboolean
+folder_update_cb (gpointer user_data)
+{
+ struct ScheduleUpdateData *sud = user_data;
+ GSList *foldernames;
+
+ g_return_val_if_fail (sud != NULL, FALSE);
+
+ if (g_cancellable_is_cancelled (sud->cancellable))
+ return FALSE;
+
+ g_return_val_if_fail (sud->mapi_store != NULL, FALSE);
+ g_return_val_if_fail (sud->mapi_store->priv != NULL, FALSE);
+
+ g_static_rec_mutex_lock (&sud->mapi_store->priv->updates_lock);
+ if (sud->expected_id != sud->mapi_store->priv->update_folder_id) {
+ g_static_rec_mutex_unlock (&sud->mapi_store->priv->updates_lock);
+ return FALSE;
+ }
+
+ foldernames = sud->mapi_store->priv->update_folder_names;
+ sud->mapi_store->priv->update_folder_names = NULL;
+ sud->mapi_store->priv->update_folder_id = 0;
+
+ if (!g_cancellable_is_cancelled (sud->cancellable))
+ run_update_thread (sud->mapi_store, sud->cancellable, foldernames);
+ else
+ g_slist_free_full (foldernames, g_free);
+
+ g_static_rec_mutex_unlock (&sud->mapi_store->priv->updates_lock);
+
+ return FALSE;
+}
+
+static void
+schedule_folder_update (CamelMapiStore *mapi_store, mapi_id_t fid)
+{
+ gchar *fidstr;
+ const gchar *foldername;
+ struct ScheduleUpdateData *sud;
+ CamelStoreInfo *si;
+ CamelMapiStoreInfo *msi;
+
+ g_return_if_fail (mapi_store != NULL);
+ g_return_if_fail (mapi_store->priv != NULL);
+
+ si = camel_mapi_store_summary_get_folder_id (mapi_store->summary, fid);
+ if (!si)
+ return;
+
+ msi = (CamelMapiStoreInfo *) si;
+ if ((msi->mapi_folder_flags & CAMEL_MAPI_STORE_FOLDER_FLAG_MAIL) == 0) {
+ camel_store_summary_info_free (mapi_store->summary, si);
+ return;
+ }
+
+ camel_store_summary_info_free (mapi_store->summary, si);
+
+ fidstr = e_mapi_util_mapi_id_to_string (fid);
+ if (!fidstr)
+ return;
+
+ foldername = camel_mapi_store_folder_lookup (mapi_store, fidstr);
+ g_free (fidstr);
+
+ if (!foldername)
+ return;
+
+ g_static_rec_mutex_lock (&mapi_store->priv->updates_lock);
+ if (!mapi_store->priv->updates_cancellable ||
+ g_slist_find_custom (mapi_store->priv->update_folder_names, foldername, (GCompareFunc) g_ascii_strcasecmp) != 0) {
+ g_static_rec_mutex_unlock (&mapi_store->priv->updates_lock);
+ return;
+ }
+
+ sud = g_new0 (struct ScheduleUpdateData, 1);
+ sud->cancellable = g_object_ref (mapi_store->priv->updates_cancellable);
+ sud->mapi_store = mapi_store;
+
+ mapi_store->priv->update_folder_names = g_slist_prepend (mapi_store->priv->update_folder_names, g_strdup (foldername));
+ if (mapi_store->priv->update_folder_id)
+ g_source_remove (mapi_store->priv->update_folder_id);
+ mapi_store->priv->update_folder_id = g_timeout_add_seconds_full (G_PRIORITY_LOW, 5, folder_update_cb, sud, free_schedule_update_data);
+ sud->expected_id = mapi_store->priv->update_folder_id;
+
+ g_static_rec_mutex_unlock (&mapi_store->priv->updates_lock);
+}
+
+static gboolean
+folder_list_update_cb (gpointer user_data)
+{
+ struct ScheduleUpdateData *sud = user_data;
+
+ g_return_val_if_fail (sud != NULL, FALSE);
+
+ if (g_cancellable_is_cancelled (sud->cancellable))
+ return FALSE;
+
+ g_return_val_if_fail (sud->mapi_store != NULL, FALSE);
+ g_return_val_if_fail (sud->mapi_store->priv != NULL, FALSE);
+
+ g_static_rec_mutex_lock (&sud->mapi_store->priv->updates_lock);
+ if (sud->expected_id != sud->mapi_store->priv->update_folder_list_id) {
+ g_static_rec_mutex_unlock (&sud->mapi_store->priv->updates_lock);
+ return FALSE;
+ }
+
+ sud->mapi_store->priv->folders_synced = FALSE;
+ sud->mapi_store->priv->update_folder_list_id = 0;
+
+ if (!g_cancellable_is_cancelled (sud->cancellable))
+ run_update_thread (sud->mapi_store, sud->cancellable, NULL);
+
+ g_static_rec_mutex_unlock (&sud->mapi_store->priv->updates_lock);
+
+ return FALSE;
+}
+
+static void
+schedule_folder_list_update (CamelMapiStore *mapi_store)
+{
+ struct ScheduleUpdateData *sud;
+
+ g_return_if_fail (mapi_store != NULL);
+ g_return_if_fail (mapi_store->priv != NULL);
+
+ g_static_rec_mutex_lock (&mapi_store->priv->updates_lock);
+ if (!mapi_store->priv->updates_cancellable) {
+ g_static_rec_mutex_unlock (&mapi_store->priv->updates_lock);
+ return;
+ }
+
+ sud = g_new0 (struct ScheduleUpdateData, 1);
+ sud->cancellable = g_object_ref (mapi_store->priv->updates_cancellable);
+ sud->mapi_store = mapi_store;
+
+ if (mapi_store->priv->update_folder_list_id)
+ g_source_remove (mapi_store->priv->update_folder_list_id);
+ mapi_store->priv->update_folder_list_id = g_timeout_add_seconds_full (G_PRIORITY_LOW, 5, folder_list_update_cb, sud, free_schedule_update_data);
+ sud->expected_id = mapi_store->priv->update_folder_list_id;
+
+ g_static_rec_mutex_unlock (&mapi_store->priv->updates_lock);
+}
+
+static void
+camel_mapi_store_server_notification_cb (EMapiConnection *conn,
+ guint event_mask,
+ gpointer event_data,
+ gpointer user_data)
+{
+ CamelMapiStore *mapi_store = user_data;
+ mapi_id_t update_folder1 = 0, update_folder2 = 0;
+ gboolean update_folder_list = FALSE;
+
+ g_return_if_fail (mapi_store != NULL);
+ g_return_if_fail (mapi_store->priv != NULL);
+
+ switch (event_mask) {
+ /* -- Folder Events -- */
+ case fnevObjectCreated:
+ d (printf ("Event : Folder Created\n"));
+ d (mapidump_foldercreated (event_data, "\t"));
+ update_folder_list = TRUE;
+ break;
+ case fnevObjectDeleted:
+ d (printf ("Event : Folder Deleted\n"));
+ d (mapidump_folderdeleted (event_data, "\t"));
+ update_folder_list = TRUE;
+ break;
+ case fnevObjectMoved:
+ d (printf ("Event : Folder Moved\n"));
+ d (mapidump_foldermoved (event_data, "\t"));
+ update_folder_list = TRUE;
+ break;
+ case fnevObjectCopied:
+ d (printf ("Event : Folder Copied\n"));
+ d (mapidump_foldercopied (event_data, "\t"));
+ update_folder_list = TRUE;
+ break;
+
+ /* -- Message Events -- */
+ case fnevNewMail:
+ case fnevNewMail | fnevMbit: {
+ struct NewMailNotification *newmail = event_data;
+
+ d (printf ("Event : New mail\n"));
+ d (mapidump_newmail (event_data, "\t"));
+
+ if (newmail)
+ update_folder1 = newmail->FID;
+ } break;
+ case fnevMbit | fnevObjectCreated: {
+ struct MessageCreatedNotification *msgcreated = event_data;
+
+ d (printf ("Event : Message created\n"));
+ d (mapidump_messagecreated (event_data, "\t"));
+
+ if (msgcreated)
+ update_folder1 = msgcreated->FID;
+ } break;
+ case fnevMbit | fnevObjectModified: {
+ struct MessageModifiedNotification *msgmodified = event_data;
+
+ d (printf ("Event : Message modified\n"));
+ d (mapidump_messagemodified (event_data, "\t"));
+
+ if (msgmodified)
+ update_folder1 = msgmodified->FID;
+ } break;
+ case fnevMbit | fnevObjectDeleted: {
+ struct MessageDeletedNotification *msgdeleted = event_data;
+
+ d (printf ("Event : Message deleted\n"));
+ d (mapidump_messagedeleted (event_data, "\t"));
+
+ if (msgdeleted)
+ update_folder1 = msgdeleted->FID;
+ } break;
+ case fnevMbit | fnevObjectMoved: {
+ struct MessageMoveCopyNotification *msgmoved = event_data;
+
+ d (printf ("Event : Message moved\n"));
+ d (mapidump_messagemoved (event_data, "\t"));
+
+ if (msgmoved) {
+ update_folder1 = msgmoved->OldFID;
+ update_folder2 = msgmoved->FID;
+ }
+ } break;
+ case fnevMbit | fnevObjectCopied: {
+ struct MessageMoveCopyNotification *msgcopied = event_data;
+
+ d (printf ("Event : Message copied\n"));
+ d (mapidump_messagecopied (event_data, "\t"));
+
+ if (msgcopied) {
+ update_folder1 = msgcopied->OldFID;
+ update_folder2 = msgcopied->FID;
+ }
+ } break;
+ default:
+ /* Unsupported */
+ break;
+ }
+
+ if (update_folder1 > 0)
+ schedule_folder_update (mapi_store, update_folder1);
+ if (update_folder2 > 0)
+ schedule_folder_update (mapi_store, update_folder2);
+ if (update_folder_list)
+ schedule_folder_list_update (mapi_store);
+}
+
static CamelAuthenticationResult
mapi_authenticate_sync (CamelService *service,
const gchar *mechanism,
@@ -1803,6 +2164,14 @@ mapi_authenticate_sync (CamelService *service,
store->priv->conn = e_mapi_connection_new (profile, password, cancellable, &mapi_error);
if (store->priv->conn && e_mapi_connection_connected (store->priv->conn)) {
result = CAMEL_AUTHENTICATION_ACCEPTED;
+
+ if (!store->priv->updates_cancellable)
+ store->priv->updates_cancellable = g_cancellable_new ();
+
+ g_signal_connect (store->priv->conn, "server-notification", G_CALLBACK (camel_mapi_store_server_notification_cb), store);
+
+ if (camel_mapi_settings_get_listen_notifications (mapi_settings))
+ e_mapi_connection_enable_notifications (store->priv->conn, NULL, 0, NULL, NULL);
} else if (g_error_matches (mapi_error, E_MAPI_ERROR, MAPI_E_LOGON_FAILED)) {
g_clear_error (&mapi_error);
result = CAMEL_AUTHENTICATION_REJECTED;
diff --git a/src/camel/camel-mapi-store.h b/src/camel/camel-mapi-store.h
index d16c656..809ec48 100644
--- a/src/camel/camel-mapi-store.h
+++ b/src/camel/camel-mapi-store.h
@@ -78,8 +78,6 @@ const gchar * camel_mapi_store_folder_lookup (CamelMapiStore *mapi_store, const
const gchar * camel_mapi_store_system_folder_fid (CamelMapiStore *mapi_store, guint folder_type);
const gchar * mapi_folders_hash_table_name_lookup (CamelMapiStore *store, const gchar *fid, gboolean use_cache);
-void camel_mapi_store_unset_notification_data (CamelMapiStore *mstore);
-
EMapiConnection * camel_mapi_store_get_connection (CamelMapiStore *mapi_store);
G_END_DECLS
diff --git a/src/libexchangemapi/camel-mapi-settings.c b/src/libexchangemapi/camel-mapi-settings.c
index 7f9c55e..ffeec31 100644
--- a/src/libexchangemapi/camel-mapi-settings.c
+++ b/src/libexchangemapi/camel-mapi-settings.c
@@ -27,6 +27,7 @@ struct _CamelMapiSettingsPrivate {
gboolean filter_junk;
gboolean filter_junk_inbox;
gboolean kerberos;
+ gboolean listen_notifications;
gchar *domain;
gchar *profile;
@@ -46,7 +47,8 @@ enum {
PROP_PROFILE,
PROP_REALM,
PROP_SECURITY_METHOD,
- PROP_USER
+ PROP_USER,
+ PROP_LISTEN_NOTIFICATIONS
};
G_DEFINE_TYPE_WITH_CODE (
@@ -134,6 +136,13 @@ mapi_settings_set_property (GObject *object,
CAMEL_NETWORK_SETTINGS (object),
g_value_get_string (value));
return;
+
+ case PROP_LISTEN_NOTIFICATIONS:
+ camel_mapi_settings_set_listen_notifications (
+ CAMEL_MAPI_SETTINGS (object),
+ g_value_get_boolean (value));
+ return;
+
}
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
@@ -229,6 +238,13 @@ mapi_settings_get_property (GObject *object,
camel_network_settings_get_user (
CAMEL_NETWORK_SETTINGS (object)));
return;
+
+ case PROP_LISTEN_NOTIFICATIONS:
+ g_value_set_boolean (
+ value,
+ camel_mapi_settings_get_listen_notifications (
+ CAMEL_MAPI_SETTINGS (object)));
+ return;
}
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
@@ -361,6 +377,18 @@ camel_mapi_settings_class_init (CamelMapiSettingsClass *class)
G_PARAM_CONSTRUCT |
G_PARAM_STATIC_STRINGS));
+ g_object_class_install_property (
+ object_class,
+ PROP_LISTEN_NOTIFICATIONS,
+ g_param_spec_boolean (
+ "listen-notifications",
+ "Listen Notifications",
+ "Whether to listen for server notifications",
+ FALSE,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT |
+ G_PARAM_STATIC_STRINGS));
+
/* Inherited from CamelNetworkSettings. */
g_object_class_override_property (
object_class,
@@ -523,3 +551,22 @@ camel_mapi_settings_set_realm (CamelMapiSettings *settings,
g_object_notify (G_OBJECT (settings), "realm");
}
+
+gboolean
+camel_mapi_settings_get_listen_notifications (CamelMapiSettings *settings)
+{
+ g_return_val_if_fail (CAMEL_IS_MAPI_SETTINGS (settings), FALSE);
+
+ return settings->priv->listen_notifications;
+}
+
+void
+camel_mapi_settings_set_listen_notifications (CamelMapiSettings *settings,
+ gboolean listen_notifications)
+{
+ g_return_if_fail (CAMEL_IS_MAPI_SETTINGS (settings));
+
+ settings->priv->listen_notifications = listen_notifications;
+
+ g_object_notify (G_OBJECT (settings), "listen-notifications");
+}
diff --git a/src/libexchangemapi/camel-mapi-settings.h b/src/libexchangemapi/camel-mapi-settings.h
index 4db13cd..64e256c 100644
--- a/src/libexchangemapi/camel-mapi-settings.h
+++ b/src/libexchangemapi/camel-mapi-settings.h
@@ -86,6 +86,11 @@ void camel_mapi_settings_set_profile (CamelMapiSettings *settings,
const gchar * camel_mapi_settings_get_realm (CamelMapiSettings *settings);
void camel_mapi_settings_set_realm (CamelMapiSettings *settings,
const gchar *realm);
+gboolean camel_mapi_settings_get_listen_notifications
+ (CamelMapiSettings *settings);
+void camel_mapi_settings_set_listen_notifications
+ (CamelMapiSettings *settings,
+ gboolean listen_notifications);
G_END_DECLS
diff --git a/src/libexchangemapi/e-mapi-connection.c b/src/libexchangemapi/e-mapi-connection.c
index 30285cc..db7b2de 100644
--- a/src/libexchangemapi/e-mapi-connection.c
+++ b/src/libexchangemapi/e-mapi-connection.c
@@ -172,8 +172,70 @@ struct _EMapiConnectionPrivate {
GHashTable *named_ids; /* cache of named ids; key is a folder ID, value is a hash table
of named_id to prop_id in that respective folder */
+
+ GHashTable *known_notifications;/* mapi_id_t * -> uint32_t for Unsubscribe call */
+ GThread *notification_thread;
+ EFlag *notification_flag;
+ enum MAPISTATUS register_notification_result; /* MAPI_E_RESERVED if not called yet */
+ gint notification_poll_seconds; /* delay between polls, in seconds */
+};
+
+enum {
+ SERVER_NOTIFICATION,
+ LAST_SIGNAL
};
+static guint signals[LAST_SIGNAL];
+
+static gboolean
+stop_notification (EMapiConnectionPrivate *priv,
+ uint32_t conn_id,
+ GCancellable *cancellable,
+ GError **perror)
+{
+ enum MAPISTATUS ms;
+
+ e_return_val_mapi_error_if_fail (priv != NULL, MAPI_E_INVALID_PARAMETER, FALSE);
+ e_return_val_mapi_error_if_fail (priv->session != NULL, MAPI_E_INVALID_PARAMETER, FALSE);
+
+ LOCK ();
+
+ ms = Unsubscribe (priv->session, conn_id);
+ if (ms != MAPI_E_SUCCESS)
+ make_mapi_error (perror, "Unsubscribe", ms);
+
+ UNLOCK ();
+
+ return ms == MAPI_E_SUCCESS;
+}
+
+static void
+call_stop_notification (gpointer key,
+ gpointer value,
+ gpointer user_data)
+{
+ stop_notification (user_data, GPOINTER_TO_UINT (value), NULL, NULL);
+}
+
+static void
+stop_all_notifications (EMapiConnectionPrivate *priv)
+{
+ g_return_if_fail (priv != NULL);
+
+ if (!priv->notification_thread)
+ return;
+
+ LOCK ();
+ if (priv->session)
+ g_hash_table_foreach (priv->known_notifications, call_stop_notification, priv);
+ g_hash_table_remove_all (priv->known_notifications);
+ e_flag_set (priv->notification_flag);
+ UNLOCK ();
+
+ g_thread_join (priv->notification_thread);
+ priv->notification_thread = NULL;
+}
+
/* should have session_lock locked already, when calling this function */
static void
disconnect (EMapiConnectionPrivate *priv)
@@ -228,7 +290,7 @@ ensure_public_store (EMapiConnectionPrivate *priv, GError **perror)
}
static void
-e_mapi_connection_finalize (GObject *object)
+e_mapi_connection_dispose (GObject *object)
{
EMapiConnectionPrivate *priv;
@@ -237,6 +299,21 @@ e_mapi_connection_finalize (GObject *object)
priv = E_MAPI_CONNECTION (object)->priv;
if (priv) {
+ stop_all_notifications (priv);
+ }
+
+ if (G_OBJECT_CLASS (e_mapi_connection_parent_class)->dispose)
+ G_OBJECT_CLASS (e_mapi_connection_parent_class)->dispose (object);
+}
+
+static void
+e_mapi_connection_finalize (GObject *object)
+{
+ EMapiConnectionPrivate *priv;
+
+ priv = E_MAPI_CONNECTION (object)->priv;
+
+ if (priv) {
LOCK ();
disconnect (priv);
g_free (priv->profile);
@@ -252,6 +329,12 @@ e_mapi_connection_finalize (GObject *object)
e_mapi_utils_destroy_mapi_context (priv->mapi_ctx);
priv->mapi_ctx = NULL;
+
+ g_hash_table_destroy (priv->known_notifications);
+ priv->known_notifications = NULL;
+
+ e_flag_free (priv->notification_flag);
+ priv->notification_flag = NULL;
}
if (G_OBJECT_CLASS (e_mapi_connection_parent_class)->finalize)
@@ -266,7 +349,17 @@ e_mapi_connection_class_init (EMapiConnectionClass *klass)
g_type_class_add_private (klass, sizeof (EMapiConnectionPrivate));
object_class = G_OBJECT_CLASS (klass);
+ object_class->dispose = e_mapi_connection_dispose;
object_class->finalize = e_mapi_connection_finalize;
+
+ signals[SERVER_NOTIFICATION] = g_signal_new (
+ "server-notification",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_FIRST | G_SIGNAL_DETAILED | G_SIGNAL_ACTION,
+ 0, NULL, NULL,
+ g_cclosure_marshal_VOID__UINT_POINTER,
+ G_TYPE_NONE, 2,
+ G_TYPE_UINT, G_TYPE_POINTER);
}
static void
@@ -285,6 +378,18 @@ e_mapi_connection_init (EMapiConnection *conn)
conn->priv->named_ids = g_hash_table_new_full (g_int64_hash, g_int64_equal, g_free, (GDestroyNotify) g_hash_table_destroy);
+ conn->priv->known_notifications = g_hash_table_new_full (g_int64_hash, g_int64_equal, g_free, NULL);
+ conn->priv->notification_thread = NULL;
+ conn->priv->notification_flag = e_flag_new ();
+ conn->priv->register_notification_result = MAPI_E_RESERVED;
+ conn->priv->notification_poll_seconds = 60;
+
+ if (g_getenv ("MAPI_SERVER_POLL")) {
+ conn->priv->notification_poll_seconds = atoi (g_getenv ("MAPI_SERVER_POLL"));
+ if (conn->priv->notification_poll_seconds < 1)
+ conn->priv->notification_poll_seconds = 60;
+ }
+
register_connection (conn);
}
@@ -316,7 +421,7 @@ unregister_connection (EMapiConnection *conn)
G_LOCK (known_connections);
if (!g_slist_find (known_connections, conn)) {
G_UNLOCK (known_connections);
- g_return_if_reached ();
+ return;
}
known_connections = g_slist_remove (known_connections, conn);
@@ -5593,98 +5698,185 @@ e_mapi_connection_ex_to_smtp (EMapiConnection *conn,
return smtp_addr;
}
-gboolean
-e_mapi_connection_events_init (EMapiConnection *conn,
- GCancellable *cancellable,
- GError **perror)
+static gint
+emit_server_notification_signal (uint16_t event_type, gpointer event_data, gpointer user_data)
{
- enum MAPISTATUS ms;
+ EMapiConnection *conn = user_data;
+ guint uint_event_type = event_type;
- CHECK_CORRECT_CONN_AND_GET_PRIV (conn, FALSE);
- e_return_val_mapi_error_if_fail (priv->session != NULL, MAPI_E_INVALID_PARAMETER, FALSE);
+ g_signal_emit (conn, signals[SERVER_NOTIFICATION], 0, uint_event_type, event_data);
- LOCK ();
- ms = RegisterNotification (priv->session, 0);
- UNLOCK ();
+ return MAPI_E_SUCCESS;
+}
- if (ms != MAPI_E_SUCCESS)
- make_mapi_error (perror, "RegisterNotification", ms);
+static gpointer
+e_mapi_connection_notification_thread (gpointer user_data)
+{
+ EMapiConnection *conn = user_data;
+ EMapiConnectionPrivate *priv;
- return ms == MAPI_E_SUCCESS;
+ g_return_val_if_fail (conn != NULL, NULL);
+ g_return_val_if_fail (conn->priv != NULL, NULL);
+ g_return_val_if_fail (conn->priv->session != NULL, NULL);
+
+ priv = conn->priv;
+
+ while (g_hash_table_size (priv->known_notifications) > 0) {
+ GTimeVal tv;
+
+ LOCK ();
+ /* this returns MAPI_E_INVALID_PARAMETER when there
+ is no pending notification
+ */
+ DispatchNotifications (priv->session);
+ UNLOCK ();
+
+ /* poll not so often */
+ g_get_current_time (&tv);
+ g_time_val_add (&tv, G_USEC_PER_SEC * priv->notification_poll_seconds);
+
+ e_flag_clear (priv->notification_flag);
+ e_flag_timed_wait (priv->notification_flag, &tv);
+ }
+
+ return NULL;
}
+/* enables server notifications on a folder, or on a whole store, if obj_folder is NULL.
+ the event_mask can be 0 to obtain all notifications;
+ Pair function for this is e_mapi_connection_disable_notifications().
+ The notification is received to the caller with the "server-notification" signal.
+ Note that the signal is used for each notification, without distinction on the enable
+ object.
+*/
gboolean
-e_mapi_connection_events_subscribe (EMapiConnection *conn,
- guint32 options,
- guint16 event_mask,
- guint32 *events_conn_id,
- mapi_notify_callback_t callback,
- gpointer data,
- GCancellable *cancellable,
- GError **perror)
+e_mapi_connection_enable_notifications (EMapiConnection *conn,
+ mapi_object_t *obj_folder,
+ uint32_t event_mask,
+ GCancellable *cancellable,
+ GError **perror)
{
- enum MAPISTATUS ms = MAPI_E_CALL_FAILED;
- gboolean use_store = ((options & MAPI_EVENTS_USE_STORE) ||
- (options & MAPI_EVENTS_USE_PF_STORE));
+ enum MAPISTATUS ms;
+ mapi_id_t fid = 0;
+ uint32_t conn_id;
+ gint64 i64;
CHECK_CORRECT_CONN_AND_GET_PRIV (conn, FALSE);
e_return_val_mapi_error_if_fail (priv->session != NULL, MAPI_E_INVALID_PARAMETER, FALSE);
LOCK ();
- if (options & MAPI_EVENTS_USE_STORE) {
- ms = Subscribe (&priv->msg_store, events_conn_id, event_mask, use_store, (mapi_notify_callback_t) callback, data);
- } else if (options & MAPI_EVENTS_USE_PF_STORE) {
- if (!ensure_public_store (priv, perror)) {
- UNLOCK ();
- return FALSE;
- }
+ if (event_mask == 0)
+ event_mask = fnevNewMail |
+ fnevObjectCreated |
+ fnevObjectDeleted |
+ fnevObjectModified |
+ fnevObjectMoved;
- ms = Subscribe (&priv->public_store, events_conn_id, event_mask, use_store, (mapi_notify_callback_t) callback, data);
- } else if (options & MAPI_EVENTS_FOLDER) {
- /* TODO */
+ if (obj_folder)
+ fid = mapi_object_get_id (obj_folder);
+
+ i64 = (gint64) fid;
+ conn_id = GPOINTER_TO_UINT (g_hash_table_lookup (priv->known_notifications, &i64));
+ if (conn_id) {
+ stop_notification (priv, conn_id, cancellable, perror);
+ g_hash_table_remove (priv->known_notifications, &i64);
}
- UNLOCK ();
+ if (priv->register_notification_result == MAPI_E_RESERVED)
+ priv->register_notification_result = RegisterNotification (priv->session, fnevNewMail |
+ fnevObjectCreated |
+ fnevObjectDeleted |
+ fnevObjectModified |
+ fnevObjectMoved);
- if (ms != MAPI_E_SUCCESS)
+ if (priv->register_notification_result != MAPI_E_SUCCESS) {
+ make_mapi_error (perror, "RegisterNotification", priv->register_notification_result);
+ UNLOCK ();
+
+ return FALSE;
+ }
+
+ conn_id = 0;
+ ms = Subscribe (obj_folder ? obj_folder : &priv->msg_store, &conn_id, event_mask, obj_folder == NULL, emit_server_notification_signal, conn);
+ if (ms == MAPI_E_SUCCESS) {
+ gint64 *pi64;
+
+ pi64 = g_new0 (gint64, 1);
+ *pi64 = i64;
+
+ g_hash_table_insert (priv->known_notifications, pi64, GUINT_TO_POINTER (conn_id));
+
+ if (priv->notification_thread) {
+ e_flag_set (priv->notification_flag);
+ } else {
+ priv->notification_thread = g_thread_create (e_mapi_connection_notification_thread, conn, TRUE, NULL);
+ }
+ } else {
make_mapi_error (perror, "Subscribe", ms);
+ }
- return (ms == MAPI_E_SUCCESS);
+ UNLOCK ();
+
+ return ms == MAPI_E_SUCCESS;
}
gboolean
-e_mapi_connection_events_unsubscribe (EMapiConnection *conn, guint32 events_conn_id, GCancellable *cancellable, GError **perror)
+e_mapi_connection_disable_notifications (EMapiConnection *conn,
+ mapi_object_t *obj_folder,
+ GCancellable *cancellable,
+ GError **perror)
{
- enum MAPISTATUS ms;
+ mapi_id_t fid = 0;
+ uint32_t conn_id;
+ gint64 i64;
CHECK_CORRECT_CONN_AND_GET_PRIV (conn, FALSE);
e_return_val_mapi_error_if_fail (priv->session != NULL, MAPI_E_INVALID_PARAMETER, FALSE);
LOCK ();
- ms = Unsubscribe (priv->session, events_conn_id);
- UNLOCK ();
- if (ms != MAPI_E_SUCCESS)
- make_mapi_error (perror, "Unsubscribe", ms);
+ if (!priv->notification_thread) {
+ /* no notifications started, just return */
+ UNLOCK ();
- return (ms == MAPI_E_SUCCESS);
-}
+ return TRUE;
+ }
-/* Note : Blocking infinite loop. */
-gboolean
-e_mapi_connection_events_monitor (EMapiConnection *conn, struct mapi_notify_continue_callback_data *cb_data)
-{
- enum MAPISTATUS ms;
- /* to have this used in the below macros */
- GError **perror = NULL;
+ if (priv->register_notification_result != MAPI_E_SUCCESS) {
+ make_mapi_error (perror, "RegisterNotification", priv->register_notification_result);
+ UNLOCK ();
- CHECK_CORRECT_CONN_AND_GET_PRIV (conn, FALSE);
- e_return_val_mapi_error_if_fail (priv->session != NULL, MAPI_E_INVALID_PARAMETER, FALSE);
+ return FALSE;
+ }
- ms = MonitorNotification (priv->session, NULL, cb_data);
+ if (obj_folder)
+ fid = mapi_object_get_id (obj_folder);
- return ms == MAPI_E_SUCCESS;
+ i64 = (gint64) fid;
+ conn_id = GPOINTER_TO_UINT (g_hash_table_lookup (priv->known_notifications, &i64));
+ if (conn_id) {
+ gboolean stopped = stop_notification (priv, conn_id, cancellable, perror);
+ g_hash_table_remove (priv->known_notifications, &i64);
+
+ if (!stopped) {
+ UNLOCK ();
+ return FALSE;
+ }
+ } else {
+ make_mapi_error (perror, "e_mapi_connection_disable_notifications", MAPI_E_NOT_FOUND);
+ UNLOCK ();
+
+ return FALSE;
+ }
+
+ if (g_hash_table_size (priv->known_notifications) == 0) {
+ stop_all_notifications (priv);
+ }
+
+ UNLOCK ();
+
+ return TRUE;
}
/* profile related functions - begin */
diff --git a/src/libexchangemapi/e-mapi-connection.h b/src/libexchangemapi/e-mapi-connection.h
index eb7cfdd..07f40eb 100644
--- a/src/libexchangemapi/e-mapi-connection.h
+++ b/src/libexchangemapi/e-mapi-connection.h
@@ -526,25 +526,15 @@ gchar * e_mapi_connection_ex_to_smtp (EMapiConnection *conn,
GError **perror);
/* Push notifications APIs */
-typedef gboolean (*exchange_check_continue) (void);
-gboolean e_mapi_connection_events_init (EMapiConnection *conn,
- GCancellable *cancellable,
- GError **perror);
-gboolean e_mapi_connection_events_monitor (EMapiConnection *conn,
- struct mapi_notify_continue_callback_data *cb_data);
-
-gboolean e_mapi_connection_events_subscribe (EMapiConnection *conn,
- guint32 options,
- guint16 event_mask,
- guint32 *events_conn_id,
- mapi_notify_callback_t callback,
- gpointer data,
+gboolean e_mapi_connection_enable_notifications (EMapiConnection *conn,
+ mapi_object_t *obj_folder,
+ uint32_t event_mask,
GCancellable *cancellable,
GError **perror);
-gboolean e_mapi_connection_events_unsubscribe (EMapiConnection *conn,
- guint32 events_conn_id,
+gboolean e_mapi_connection_disable_notifications (EMapiConnection *conn,
+ mapi_object_t *obj_folder,
GCancellable *cancellable,
GError **perror);
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]