[evolution-data-server] I#306 - Camel: Listen for change notifications on spool account
- From: Milan Crha <mcrha src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [evolution-data-server] I#306 - Camel: Listen for change notifications on spool account
- Date: Fri, 19 Mar 2021 08:30:45 +0000 (UTC)
commit 190f7b78f1ddd797d9ca451c2eeb775b445099a9
Author: Milan Crha <mcrha redhat com>
Date: Fri Mar 19 09:30:09 2021 +0100
I#306 - Camel: Listen for change notifications on spool account
Closes https://gitlab.gnome.org/GNOME/evolution-data-server/-/issues/306
src/camel/providers/local/camel-local-provider.c | 2 +
src/camel/providers/local/camel-spool-settings.c | 52 +++-
src/camel/providers/local/camel-spool-settings.h | 5 +
src/camel/providers/local/camel-spool-store.c | 298 +++++++++++++++++++++++
4 files changed, 356 insertions(+), 1 deletion(-)
---
diff --git a/src/camel/providers/local/camel-local-provider.c
b/src/camel/providers/local/camel-local-provider.c
index 0bc6c6cf6..c4fea527e 100644
--- a/src/camel/providers/local/camel-local-provider.c
+++ b/src/camel/providers/local/camel-local-provider.c
@@ -106,6 +106,8 @@ static CamelProvider maildir_provider = {
static CamelProviderConfEntry spool_conf_entries[] = {
{ CAMEL_PROVIDER_CONF_SECTION_START, "general", NULL, N_("Options") },
+ { CAMEL_PROVIDER_CONF_CHECKBOX, "listen-notifications", NULL,
+ N_("_Listen for change notifications"), "1" },
{ CAMEL_PROVIDER_CONF_CHECKBOX, "filter-inbox", NULL,
N_("_Apply filters to new messages in Inbox"), "0" },
{ CAMEL_PROVIDER_CONF_CHECKBOX, "filter-junk", NULL,
diff --git a/src/camel/providers/local/camel-spool-settings.c
b/src/camel/providers/local/camel-spool-settings.c
index ad0504f04..c931db898 100644
--- a/src/camel/providers/local/camel-spool-settings.c
+++ b/src/camel/providers/local/camel-spool-settings.c
@@ -19,11 +19,13 @@
struct _CamelSpoolSettingsPrivate {
gboolean use_xstatus_headers;
+ gboolean listen_notifications;
};
enum {
PROP_0,
- PROP_USE_XSTATUS_HEADERS
+ PROP_USE_XSTATUS_HEADERS,
+ PROP_LISTEN_NOTIFICATIONS
};
G_DEFINE_TYPE_WITH_PRIVATE (
@@ -43,6 +45,12 @@ spool_settings_set_property (GObject *object,
CAMEL_SPOOL_SETTINGS (object),
g_value_get_boolean (value));
return;
+
+ case PROP_LISTEN_NOTIFICATIONS:
+ camel_spool_settings_set_listen_notifications (
+ CAMEL_SPOOL_SETTINGS (object),
+ g_value_get_boolean (value));
+ return;
}
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
@@ -61,6 +69,13 @@ spool_settings_get_property (GObject *object,
camel_spool_settings_get_use_xstatus_headers (
CAMEL_SPOOL_SETTINGS (object)));
return;
+
+ case PROP_LISTEN_NOTIFICATIONS:
+ g_value_set_boolean (
+ value,
+ camel_spool_settings_get_listen_notifications (
+ CAMEL_SPOOL_SETTINGS (object)));
+ return;
}
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
@@ -87,6 +102,19 @@ camel_spool_settings_class_init (CamelSpoolSettingsClass *class)
G_PARAM_CONSTRUCT |
G_PARAM_EXPLICIT_NOTIFY |
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 file/directory change notifications",
+ TRUE,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT |
+ G_PARAM_EXPLICIT_NOTIFY |
+ G_PARAM_STATIC_STRINGS));
}
static void
@@ -137,3 +165,25 @@ camel_spool_settings_set_use_xstatus_headers (CamelSpoolSettings *settings,
g_object_notify (G_OBJECT (settings), "use-xstatus-headers");
}
+
+gboolean
+camel_spool_settings_get_listen_notifications (CamelSpoolSettings *settings)
+{
+ g_return_val_if_fail (CAMEL_IS_SPOOL_SETTINGS (settings), FALSE);
+
+ return settings->priv->listen_notifications;
+}
+
+void
+camel_spool_settings_set_listen_notifications (CamelSpoolSettings *settings,
+ gboolean listen_notifications)
+{
+ g_return_if_fail (CAMEL_IS_SPOOL_SETTINGS (settings));
+
+ if (settings->priv->listen_notifications == listen_notifications)
+ return;
+
+ settings->priv->listen_notifications = listen_notifications;
+
+ g_object_notify (G_OBJECT (settings), "listen-notifications");
+}
diff --git a/src/camel/providers/local/camel-spool-settings.h
b/src/camel/providers/local/camel-spool-settings.h
index 53d8d5a05..2bd40b5d5 100644
--- a/src/camel/providers/local/camel-spool-settings.h
+++ b/src/camel/providers/local/camel-spool-settings.h
@@ -63,6 +63,11 @@ gboolean camel_spool_settings_get_use_xstatus_headers
void camel_spool_settings_set_use_xstatus_headers
(CamelSpoolSettings *settings,
gboolean use_xstatus_headers);
+gboolean camel_spool_settings_get_listen_notifications
+ (CamelSpoolSettings *settings);
+void camel_spool_settings_set_listen_notifications
+ (CamelSpoolSettings *settings,
+ gboolean listen_notifications);
G_END_DECLS
diff --git a/src/camel/providers/local/camel-spool-store.c b/src/camel/providers/local/camel-spool-store.c
index e6bb8eed1..702451862 100644
--- a/src/camel/providers/local/camel-spool-store.c
+++ b/src/camel/providers/local/camel-spool-store.c
@@ -37,6 +37,8 @@
#define d(x)
+#define REFRESH_INTERVAL 2
+
typedef enum _camel_spool_store_t {
CAMEL_SPOOL_STORE_INVALID,
CAMEL_SPOOL_STORE_MBOX, /* a single mbox */
@@ -45,6 +47,10 @@ typedef enum _camel_spool_store_t {
struct _CamelSpoolStorePrivate {
camel_spool_store_t store_type;
+ GFileMonitor *monitor;
+ GMutex refresh_lock;
+ guint refresh_id;
+ gint64 last_modified_time;
};
G_DEFINE_TYPE_WITH_PRIVATE (
@@ -679,12 +685,302 @@ spool_store_get_meta_path (CamelLocalStore *ls,
return path;
}
+typedef struct _RefreshData {
+ GWeakRef *spool_weak_ref;
+ gchar *folder_name;
+} RefreshData;
+
+static void
+refresh_data_free (gpointer ptr)
+{
+ RefreshData *rd = ptr;
+
+ if (rd) {
+ camel_utils_weak_ref_free (rd->spool_weak_ref);
+ g_free (rd->folder_name);
+ g_slice_free (RefreshData, rd);
+ }
+}
+
+static void
+spool_store_refresh_folder_cb (CamelSession *session,
+ GCancellable *cancellable,
+ gpointer user_data,
+ GError **error)
+{
+ RefreshData *rd = user_data;
+ CamelFolder *folder;
+ CamelSpoolStore *spool;
+
+ g_return_if_fail (rd != NULL);
+
+ spool = g_weak_ref_get (rd->spool_weak_ref);
+ if (!spool)
+ return;
+
+ if (rd->folder_name)
+ folder = camel_store_get_folder_sync (CAMEL_STORE (spool), rd->folder_name,
CAMEL_STORE_FOLDER_NONE, cancellable, NULL);
+ else
+ folder = camel_store_get_inbox_folder_sync (CAMEL_STORE (spool), cancellable, NULL);
+
+ if (folder) {
+ CamelLocalFolder *lf;
+ GStatBuf st;
+
+ lf = CAMEL_LOCAL_FOLDER (folder);
+
+ if (g_stat (lf->folder_path, &st) == 0) {
+ CamelFolderSummary *summary;
+
+ summary = camel_folder_get_folder_summary (folder);
+
+ if (summary && camel_folder_summary_get_timestamp (summary) != st.st_mtime)
+ camel_folder_refresh_info_sync (folder, cancellable, error);
+ }
+
+ g_object_unref (folder);
+ }
+
+ g_object_unref (spool);
+}
+
+static gboolean
+spool_store_submit_refresh_job_cb (gpointer user_data)
+{
+ RefreshData *rd = user_data;
+ CamelSpoolStore *spool;
+ gboolean scheduled = FALSE;
+
+ g_return_val_if_fail (rd != NULL, FALSE);
+
+ if (g_source_is_destroyed (g_main_current_source ())) {
+ refresh_data_free (rd);
+ return FALSE;
+ }
+
+ spool = g_weak_ref_get (rd->spool_weak_ref);
+
+ if (spool) {
+ gboolean refresh = FALSE;
+
+ g_mutex_lock (&spool->priv->refresh_lock);
+ if (spool->priv->refresh_id == g_source_get_id (g_main_current_source ())) {
+ spool->priv->refresh_id = 0;
+ refresh = TRUE;
+ }
+ g_mutex_unlock (&spool->priv->refresh_lock);
+
+ if (refresh) {
+ CamelSession *session;
+
+ session = camel_service_ref_session (CAMEL_SERVICE (spool));
+
+ if (session) {
+ camel_session_submit_job (session, _("Refreshing spool folder"),
+ spool_store_refresh_folder_cb, rd, refresh_data_free);
+ scheduled = TRUE;
+ g_object_unref (session);
+ }
+ }
+
+ g_object_unref (spool);
+ }
+
+ if (!scheduled)
+ refresh_data_free (rd);
+
+ return FALSE;
+}
+
+static void
+spool_store_monitor_changed_cb (GFileMonitor *monitor,
+ GFile *file,
+ GFile *other_file,
+ GFileMonitorEvent event_type,
+ gpointer user_data)
+{
+ CamelSpoolStore *spool = user_data;
+ GStatBuf st;
+ const gchar *file_path;
+ gchar *full_path = NULL;
+ gchar *basename = NULL;
+ gboolean refresh = FALSE;
+
+ g_return_if_fail (CAMEL_IS_SPOOL_STORE (spool));
+
+ if (event_type != G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT)
+ return;
+
+ if (!file)
+ return;
+
+ file_path = g_file_peek_path (file);
+
+ switch (spool_store_get_type (spool, NULL)) {
+ case CAMEL_SPOOL_STORE_INVALID:
+ break;
+ case CAMEL_SPOOL_STORE_MBOX:
+ full_path = camel_local_store_get_full_path (CAMEL_LOCAL_STORE (spool), NULL);
+
+ if (g_strcmp0 (full_path, file_path) == 0)
+ refresh = TRUE;
+ break;
+ case CAMEL_SPOOL_STORE_ELM:
+ basename = g_file_get_basename (file);
+ full_path = camel_local_store_get_full_path (CAMEL_LOCAL_STORE (spool), basename);
+ if (g_strcmp0 (full_path, file_path) == 0)
+ refresh = TRUE;
+ break;
+ }
+
+ if (refresh && g_stat (file_path, &st) == 0 &&
+ st.st_mtime != spool->priv->last_modified_time) {
+ spool->priv->last_modified_time = st.st_mtime;
+
+ g_mutex_lock (&spool->priv->refresh_lock);
+
+ if (!spool->priv->refresh_id) {
+ RefreshData *rd;
+
+ rd = g_slice_new0 (RefreshData);
+ rd->spool_weak_ref = camel_utils_weak_ref_new (spool);
+ rd->folder_name = basename;
+ basename = NULL;
+
+ spool->priv->refresh_id = g_timeout_add_seconds (REFRESH_INTERVAL,
+ spool_store_submit_refresh_job_cb, rd);
+ }
+
+ g_mutex_unlock (&spool->priv->refresh_lock);
+ }
+
+ g_free (full_path);
+ g_free (basename);
+}
+
+static void
+spool_store_update_listen_notifications_cb (GObject *settings,
+ GParamSpec *param,
+ gpointer user_data)
+{
+ CamelSpoolStore *spool = user_data;
+ gchar *path = NULL;
+ gboolean listen_notifications = FALSE;
+
+ g_return_if_fail (CAMEL_IS_SPOOL_STORE (spool));
+
+ g_object_get (settings,
+ "path", &path,
+ "listen-notifications", &listen_notifications,
+ NULL);
+
+ g_clear_object (&spool->priv->monitor);
+
+ spool->priv->store_type = CAMEL_SPOOL_STORE_INVALID;
+
+ if (listen_notifications && path &&
+ g_file_test (path, G_FILE_TEST_EXISTS)) {
+ GFile *file;
+
+ file = g_file_new_for_path (path);
+ spool->priv->monitor = g_file_monitor (file, G_FILE_MONITOR_WATCH_MOUNTS, NULL, NULL);
+
+ if (spool->priv->monitor) {
+ g_signal_connect_object (spool->priv->monitor, "changed",
+ G_CALLBACK (spool_store_monitor_changed_cb), spool, 0);
+ }
+
+ g_object_unref (file);
+ }
+
+ g_free (path);
+}
+
+static void
+spool_store_connect_settings (GObject *object)
+{
+ CamelSettings *settings;
+
+ g_return_if_fail (CAMEL_IS_SPOOL_STORE (object));
+
+ settings = camel_service_ref_settings (CAMEL_SERVICE (object));
+ if (!settings)
+ return;
+
+ g_signal_connect_object (settings, "notify::listen-notifications",
+ G_CALLBACK (spool_store_update_listen_notifications_cb), object, 0);
+
+ g_signal_connect_object (settings, "notify::path",
+ G_CALLBACK (spool_store_update_listen_notifications_cb), object, 0);
+
+ spool_store_update_listen_notifications_cb (G_OBJECT (settings), NULL, object);
+
+ g_object_unref (settings);
+}
+
+static void
+spool_store_settings_changed_cb (GObject *object,
+ GParamSpec *param,
+ gpointer user_data)
+{
+ g_return_if_fail (CAMEL_IS_SPOOL_STORE (object));
+
+ spool_store_connect_settings (object);
+}
+
+static void
+spool_store_constructed (GObject *object)
+{
+ /* Chain up to parent's method. */
+ G_OBJECT_CLASS (camel_spool_store_parent_class)->constructed (object);
+
+ g_signal_connect (object, "notify::settings",
+ G_CALLBACK (spool_store_settings_changed_cb), NULL);
+
+ spool_store_connect_settings (object);
+}
+
+static void
+spool_store_dispose (GObject *object)
+{
+ CamelSpoolStore *spool = CAMEL_SPOOL_STORE (object);
+
+ g_mutex_lock (&spool->priv->refresh_lock);
+ if (spool->priv->refresh_id) {
+ g_source_remove (spool->priv->refresh_id);
+ spool->priv->refresh_id = 0;
+ }
+ g_mutex_unlock (&spool->priv->refresh_lock);
+
+ g_clear_object (&spool->priv->monitor);
+
+ /* Chain up to parent's method. */
+ G_OBJECT_CLASS (camel_spool_store_parent_class)->dispose (object);
+}
+
+static void
+spool_store_finalize (GObject *object)
+{
+ CamelSpoolStore *spool = CAMEL_SPOOL_STORE (object);
+
+ g_mutex_clear (&spool->priv->refresh_lock);
+
+ /* Chain up to parent's method. */
+ G_OBJECT_CLASS (camel_spool_store_parent_class)->finalize (object);
+}
+
static void
camel_spool_store_class_init (CamelSpoolStoreClass *class)
{
CamelServiceClass *service_class;
CamelStoreClass *store_class;
CamelLocalStoreClass *local_store_class;
+ GObjectClass *object_class;
+
+ object_class = G_OBJECT_CLASS (class);
+ object_class->constructed = spool_store_constructed;
+ object_class->dispose = spool_store_dispose;
+ object_class->finalize = spool_store_finalize;
service_class = CAMEL_SERVICE_CLASS (class);
service_class->settings_type = CAMEL_TYPE_SPOOL_SETTINGS;
@@ -707,4 +1003,6 @@ camel_spool_store_init (CamelSpoolStore *spool_store)
{
spool_store->priv = camel_spool_store_get_instance_private (spool_store);
spool_store->priv->store_type = CAMEL_SPOOL_STORE_INVALID;
+
+ g_mutex_init (&spool_store->priv->refresh_lock);
}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]