/* -*- 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: Jeffrey Stedfast
*/
#ifdef HAVE_CONFIG_H
#include
#endif
#include
#include "camel-folder.h"
#include "camel-network-service.h"
#include "camel-offline-folder.h"
#include "camel-offline-settings.h"
#include "camel-offline-store.h"
#include "camel-session.h"
#define CAMEL_OFFLINE_STORE_GET_PRIVATE(obj) \
(G_TYPE_INSTANCE_GET_PRIVATE \
((obj), CAMEL_TYPE_OFFLINE_STORE, CamelOfflineStorePrivate))
struct _CamelOfflineStorePrivate {
/* XXX The online flag stores whether the user has selected online or
* offline mode, but fetching the flag through the "get" function
* also takes into account CamelNetworkService's "host-reachable"
* property. So it's possible to set the "online" state to TRUE,
* but then immediately read back FALSE. Kinda weird, but mainly
* for temporary backward-compability. */
gboolean online;
};
enum {
PROP_0,
PROP_ONLINE
};
G_DEFINE_TYPE (CamelOfflineStore, camel_offline_store, CAMEL_TYPE_STORE)
static void
offline_store_constructed (GObject *object)
{
CamelOfflineStorePrivate *priv;
CamelSession *session;
priv = CAMEL_OFFLINE_STORE_GET_PRIVATE (object);
/* Chain up to parent's constructed() method. */
G_OBJECT_CLASS (camel_offline_store_parent_class)->constructed (object);
session = camel_service_ref_session (CAMEL_SERVICE (object));
priv->online = camel_session_get_online (session);
g_object_unref (session);
}
static void
offline_store_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec)
{
switch (property_id) {
case PROP_ONLINE:
g_value_set_boolean (
value, camel_offline_store_get_online (
CAMEL_OFFLINE_STORE (object)));
return;
}
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
}
static void
offline_store_notify (GObject *object,
GParamSpec *pspec)
{
if (g_strcmp0 (pspec->name, "host-reachable") == 0)
g_object_notify (object, "online");
/* Chain up to parent's notify() method. */
G_OBJECT_CLASS (camel_offline_store_parent_class)->
notify (object, pspec);
}
static void
camel_offline_store_class_init (CamelOfflineStoreClass *class)
{
GObjectClass *object_class;
CamelServiceClass *service_class;
g_type_class_add_private (class, sizeof (CamelOfflineStorePrivate));
object_class = G_OBJECT_CLASS (class);
object_class->constructed = offline_store_constructed;
object_class->get_property = offline_store_get_property;
object_class->notify = offline_store_notify;
service_class = CAMEL_SERVICE_CLASS (class);
service_class->settings_type = CAMEL_TYPE_OFFLINE_SETTINGS;
g_object_class_install_property (
object_class,
PROP_ONLINE,
g_param_spec_boolean (
"online",
"Online",
"Whether the store is online",
FALSE,
G_PARAM_READABLE));
}
static void
camel_offline_store_init (CamelOfflineStore *store)
{
store->priv = CAMEL_OFFLINE_STORE_GET_PRIVATE (store);
}
/**
* camel_offline_store_get_online:
* @store: a #CamelOfflineStore
*
* Returns %TRUE if @store is online.
*
* Since: 2.24
**/
gboolean
camel_offline_store_get_online (CamelOfflineStore *store)
{
g_return_val_if_fail (CAMEL_IS_OFFLINE_STORE (store), 0);
if (CAMEL_IS_NETWORK_SERVICE (store)) {
CamelNetworkService *service;
service = CAMEL_NETWORK_SERVICE (store);
/* Always return FALSE if the remote host is not reachable. */
if (!camel_network_service_get_host_reachable (service))
return FALSE;
}
return store->priv->online;
}
/**
* camel_offline_store_set_online_sync:
* @store: a #CamelOfflineStore
* @online: %TRUE for online, %FALSE for offline
* @cancellable: optional #GCancellable object, or %NULL
* @error: return location for a #GError, or %NULL
*
* Sets the online/offline state of @store according to @online.
**/
gboolean
camel_offline_store_set_online_sync (CamelOfflineStore *store,
gboolean online,
GCancellable *cancellable,
GError **error)
{
CamelService *service;
CamelSettings *settings;
CamelServiceConnectionStatus status;
gboolean host_reachable = TRUE;
gboolean store_is_online;
gboolean sync_store;
gboolean success = TRUE;
g_return_val_if_fail (CAMEL_IS_OFFLINE_STORE (store), FALSE);
if (store->priv->online == online)
return TRUE;
service = CAMEL_SERVICE (store);
status = camel_service_get_connection_status (service);
if (CAMEL_IS_NETWORK_SERVICE (store)) {
/* When going to set the 'online' state, then check with up-to-date
value, otherwise use the cached value. The cached value is
updated with few seconds timeout, thus it can be stale here. */
if (online)
host_reachable =
camel_network_service_can_reach_sync (
CAMEL_NETWORK_SERVICE (store),
cancellable, NULL);
else
host_reachable =
camel_network_service_get_host_reachable (
CAMEL_NETWORK_SERVICE (store));
}
store_is_online = camel_offline_store_get_online (store);
settings = camel_service_ref_settings (service);
sync_store = camel_offline_settings_get_stay_synchronized (
CAMEL_OFFLINE_SETTINGS (settings));
g_object_unref (settings);
/* Returning to online mode is the simpler case. */
if (!store_is_online) {
store->priv->online = online;
g_object_notify (G_OBJECT (store), "online");
if (status == CAMEL_SERVICE_CONNECTING)
return TRUE;
return camel_service_connect_sync (service, cancellable, error);
}
if (host_reachable) {
CamelSession *session;
session = camel_service_ref_session (service);
host_reachable = camel_session_get_online (session);
g_clear_object (&session);
}
if (host_reachable) {
GPtrArray *folders;
guint ii;
folders = camel_object_bag_list (
CAMEL_STORE (store)->folders);
for (ii = 0; ii < folders->len; ii++) {
CamelFolder *folder = folders->pdata[ii];
gboolean sync_folder;
if (!CAMEL_IS_OFFLINE_FOLDER (folder))
continue;
sync_folder =
camel_offline_folder_get_offline_sync (
CAMEL_OFFLINE_FOLDER (folder));
if (sync_store || sync_folder)
camel_offline_folder_downsync_sync (
CAMEL_OFFLINE_FOLDER (folder),
NULL, cancellable, NULL);
}
g_ptr_array_foreach (folders, (GFunc) g_object_unref, NULL);
g_ptr_array_free (folders, TRUE);
camel_store_synchronize_sync (
CAMEL_STORE (store), FALSE, cancellable, NULL);
}
if (status != CAMEL_SERVICE_DISCONNECTING) {
success = camel_service_disconnect_sync (
service, host_reachable, cancellable, error);
}
store->priv->online = online;
g_object_notify (G_OBJECT (store), "online");
return success;
}
/**
* camel_offline_store_prepare_for_offline_sync:
*
* Since: 2.22
**/
gboolean
camel_offline_store_prepare_for_offline_sync (CamelOfflineStore *store,
GCancellable *cancellable,
GError **error)
{
CamelService *service;
CamelSession *session;
CamelSettings *settings;
gboolean host_reachable = TRUE;
gboolean store_is_online;
gboolean sync_store;
g_return_val_if_fail (CAMEL_IS_OFFLINE_STORE (store), FALSE);
service = CAMEL_SERVICE (store);
session = camel_service_ref_session (service);
if (CAMEL_IS_NETWORK_SERVICE (store)) {
/* Check with up-to-date value. The cached value is updated with
few seconds timeout, thus it can be stale here. */
host_reachable =
camel_network_service_can_reach_sync (
CAMEL_NETWORK_SERVICE (store),
cancellable, NULL);
}
store_is_online = camel_offline_store_get_online (store);
settings = camel_service_ref_settings (service);
sync_store = camel_offline_settings_get_stay_synchronized (
CAMEL_OFFLINE_SETTINGS (settings));
g_object_unref (settings);
g_object_unref (session);
if (host_reachable && store_is_online) {
GPtrArray *folders;
guint ii;
folders = camel_object_bag_list (
CAMEL_STORE (store)->folders);
for (ii = 0; ii < folders->len; ii++) {
CamelFolder *folder = folders->pdata[ii];
gboolean sync_folder;
if (!CAMEL_IS_OFFLINE_FOLDER (folder))
continue;
sync_folder =
camel_offline_folder_get_offline_sync (
CAMEL_OFFLINE_FOLDER (folder));
if (sync_store || sync_folder) {
camel_offline_folder_downsync_sync (
CAMEL_OFFLINE_FOLDER (folder),
NULL, cancellable, NULL);
}
}
g_ptr_array_foreach (folders, (GFunc) g_object_unref, NULL);
g_ptr_array_free (folders, TRUE);
}
if (host_reachable)
camel_store_synchronize_sync (
CAMEL_STORE (store), FALSE, cancellable, NULL);
return TRUE;
}
/**
* camel_offline_store_requires_downsync:
* @store: a #CamelOfflineStore
*
* Check whether the @store requires synchronization for offline usage.
* This is not blocking, it only checks settings on the store and its
* currently opened folders.
*
* Returns %TRUE if the @store requires synchronization for offline usage
*
* Since: 3.12
**/
gboolean
camel_offline_store_requires_downsync (CamelOfflineStore *store)
{
CamelService *service;
CamelSettings *settings;
gboolean host_reachable = TRUE;
gboolean store_is_online;
gboolean sync_store;
g_return_val_if_fail (CAMEL_IS_OFFLINE_STORE (store), FALSE);
service = CAMEL_SERVICE (store);
if (CAMEL_IS_NETWORK_SERVICE (store)) {
host_reachable =
camel_network_service_get_host_reachable (
CAMEL_NETWORK_SERVICE (store));
}
store_is_online = camel_offline_store_get_online (store);
if (!store_is_online)
return FALSE;
settings = camel_service_ref_settings (service);
sync_store = camel_offline_settings_get_stay_synchronized (
CAMEL_OFFLINE_SETTINGS (settings));
g_object_unref (settings);
if (host_reachable) {
CamelSession *session;
session = camel_service_ref_session (service);
host_reachable = camel_session_get_online (session);
g_clear_object (&session);
}
if (host_reachable && !sync_store) {
GPtrArray *folders;
guint ii;
folders = camel_object_bag_list (
CAMEL_STORE (store)->folders);
for (ii = 0; ii < folders->len && !sync_store; ii++) {
CamelFolder *folder = folders->pdata[ii];
if (!CAMEL_IS_OFFLINE_FOLDER (folder))
continue;
sync_store = sync_store ||
camel_offline_folder_get_offline_sync (
CAMEL_OFFLINE_FOLDER (folder));
}
g_ptr_array_foreach (folders, (GFunc) g_object_unref, NULL);
g_ptr_array_free (folders, TRUE);
}
return sync_store && host_reachable;
}