Rename camel_service_get_settings().
[platform/upstream/evolution-data-server.git] / camel / camel-offline-folder.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3  *  Authors: Jeffrey Stedfast <fejj@novell.com>
4  *
5  *  Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
6  *
7  *  This program is free software; you can redistribute it and/or modify
8  *  it under the terms of the GNU Lesser General Public License as published by
9  *  the Free Software Foundation; either version 2 of the License, or
10  *  (at your option) any later version.
11  *
12  *  This program is distributed in the hope that it will be useful,
13  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  *  GNU Lesser General Public License for more details.
16  *
17  *  You should have received a copy of the GNU Lesser General Public License
18  *  along with this program; if not, write to the Free Software
19  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20  *
21  */
22
23 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif
26
27 #include <glib/gi18n-lib.h>
28
29 #include "camel-debug.h"
30 #include "camel-offline-folder.h"
31 #include "camel-offline-settings.h"
32 #include "camel-offline-store.h"
33 #include "camel-operation.h"
34 #include "camel-session.h"
35
36 #define CAMEL_OFFLINE_FOLDER_GET_PRIVATE(obj) \
37         (G_TYPE_INSTANCE_GET_PRIVATE \
38         ((obj), CAMEL_TYPE_OFFLINE_FOLDER, CamelOfflineFolderPrivate))
39
40 typedef struct _AsyncContext AsyncContext;
41 typedef struct _OfflineDownsyncData OfflineDownsyncData;
42
43 struct _CamelOfflineFolderPrivate {
44         gboolean offline_sync;
45 };
46
47 struct _AsyncContext {
48         /* arguments */
49         gchar *expression;
50 };
51
52 struct _OfflineDownsyncData {
53         CamelFolder *folder;
54         CamelFolderChangeInfo *changes;
55 };
56
57 /* The custom property ID is a CamelArg artifact.
58  * It still identifies the property in state files. */
59 enum {
60         PROP_0,
61         PROP_OFFLINE_SYNC = 0x2400
62 };
63
64 G_DEFINE_TYPE (CamelOfflineFolder, camel_offline_folder, CAMEL_TYPE_FOLDER)
65
66 static void
67 async_context_free (AsyncContext *async_context)
68 {
69         g_free (async_context->expression);
70
71         g_slice_free (AsyncContext, async_context);
72 }
73
74 static void
75 offline_downsync_data_free (OfflineDownsyncData *data)
76 {
77         if (data->changes != NULL)
78                 camel_folder_change_info_free (data->changes);
79
80         g_object_unref (data->folder);
81
82         g_slice_free (OfflineDownsyncData, data);
83 }
84
85 static void
86 offline_folder_downsync_background (CamelSession *session,
87                                     GCancellable *cancellable,
88                                     OfflineDownsyncData *data,
89                                     GError **error)
90 {
91         camel_operation_push_message (
92                 cancellable,
93                 _("Downloading new messages for offline mode"));
94
95         if (data->changes) {
96                 GPtrArray *uid_added;
97                 gboolean success = TRUE;
98                 gint ii;
99
100                 uid_added = data->changes->uid_added;
101
102                 for (ii = 0; success && ii < uid_added->len; ii++) {
103                         const gchar *uid;
104                         gint percent;
105
106                         percent = ii * 100 / uid_added->len;
107                         uid = g_ptr_array_index (uid_added, ii);
108
109                         camel_operation_progress (cancellable, percent);
110
111                         success = camel_folder_synchronize_message_sync (
112                                 data->folder, uid, cancellable, error);
113                 }
114         } else {
115                 camel_offline_folder_downsync_sync (
116                         CAMEL_OFFLINE_FOLDER (data->folder),
117                         "(match-all)", cancellable, error);
118         }
119
120         camel_operation_pop_message (cancellable);
121 }
122
123 static void
124 offline_folder_changed (CamelFolder *folder,
125                         CamelFolderChangeInfo *changes)
126 {
127         CamelStore *parent_store;
128         CamelService *service;
129         CamelSession *session;
130         CamelSettings *settings;
131         gboolean sync_store;
132         gboolean sync_folder;
133
134         parent_store = camel_folder_get_parent_store (folder);
135
136         service = CAMEL_SERVICE (parent_store);
137         session = camel_service_get_session (service);
138
139         settings = camel_service_ref_settings (service);
140
141         sync_store = camel_offline_settings_get_stay_synchronized (
142                 CAMEL_OFFLINE_SETTINGS (settings));
143
144         g_object_unref (settings);
145
146         sync_folder = camel_offline_folder_get_offline_sync (
147                 CAMEL_OFFLINE_FOLDER (folder));
148
149         if (changes->uid_added->len > 0 && (sync_store || sync_folder)) {
150                 OfflineDownsyncData *data;
151
152                 data = g_slice_new0 (OfflineDownsyncData);
153                 data->changes = camel_folder_change_info_new ();
154                 camel_folder_change_info_cat (data->changes, changes);
155                 data->folder = g_object_ref (folder);
156
157                 camel_session_submit_job (
158                         session, (CamelSessionCallback)
159                         offline_folder_downsync_background, data,
160                         (GDestroyNotify) offline_downsync_data_free);
161         }
162 }
163
164 static void
165 offline_folder_set_property (GObject *object,
166                              guint property_id,
167                              const GValue *value,
168                              GParamSpec *pspec)
169 {
170         switch (property_id) {
171                 case PROP_OFFLINE_SYNC:
172                         camel_offline_folder_set_offline_sync (
173                                 CAMEL_OFFLINE_FOLDER (object),
174                                 g_value_get_boolean (value));
175                         return;
176         }
177
178         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
179 }
180
181 static void
182 offline_folder_get_property (GObject *object,
183                              guint property_id,
184                              GValue *value,
185                              GParamSpec *pspec)
186 {
187         switch (property_id) {
188                 case PROP_OFFLINE_SYNC:
189                         g_value_set_boolean (
190                                 value, camel_offline_folder_get_offline_sync (
191                                 CAMEL_OFFLINE_FOLDER (object)));
192                         return;
193         }
194
195         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
196 }
197
198 static gboolean
199 offline_folder_downsync_sync (CamelOfflineFolder *offline,
200                               const gchar *expression,
201                               GCancellable *cancellable,
202                               GError **error)
203 {
204         CamelFolder *folder = (CamelFolder *) offline;
205         GPtrArray *uids, *uncached_uids = NULL;
206         const gchar *display_name;
207         const gchar *message;
208         gint i;
209
210         message = _("Syncing messages in folder '%s' to disk");
211         display_name = camel_folder_get_display_name (folder);
212         camel_operation_push_message (cancellable, message, display_name);
213
214         if (expression)
215                 uids = camel_folder_search_by_expression (folder, expression, cancellable, NULL);
216         else
217                 uids = camel_folder_get_uids (folder);
218
219         if (!uids)
220                 goto done;
221         uncached_uids = camel_folder_get_uncached_uids (folder, uids, NULL);
222         if (uids) {
223                 if (expression)
224                         camel_folder_search_free (folder, uids);
225                 else
226                         camel_folder_free_uids (folder, uids);
227         }
228
229         if (!uncached_uids)
230                 goto done;
231
232         for (i = 0; i < uncached_uids->len; i++) {
233                 camel_folder_synchronize_message_sync (
234                         folder, uncached_uids->pdata[i], cancellable, NULL);
235                 camel_operation_progress (
236                         cancellable, i * 100 / uncached_uids->len);
237         }
238
239 done:
240         if (uncached_uids)
241                 camel_folder_free_uids (folder, uncached_uids);
242
243         camel_operation_pop_message (cancellable);
244
245         return TRUE;
246 }
247
248 static void
249 offline_folder_downsync_thread (GSimpleAsyncResult *simple,
250                                 GObject *object,
251                                 GCancellable *cancellable)
252 {
253         AsyncContext *async_context;
254         GError *error = NULL;
255
256         async_context = g_simple_async_result_get_op_res_gpointer (simple);
257
258         camel_offline_folder_downsync_sync (
259                 CAMEL_OFFLINE_FOLDER (object), async_context->expression,
260                 cancellable, &error);
261
262         if (error != NULL)
263                 g_simple_async_result_take_error (simple, error);
264 }
265
266 static void
267 offline_folder_downsync (CamelOfflineFolder *folder,
268                          const gchar *expression,
269                          gint io_priority,
270                          GCancellable *cancellable,
271                          GAsyncReadyCallback callback,
272                          gpointer user_data)
273 {
274         GSimpleAsyncResult *simple;
275         AsyncContext *async_context;
276
277         async_context = g_slice_new0 (AsyncContext);
278         async_context->expression = g_strdup (expression);
279
280         simple = g_simple_async_result_new (
281                 G_OBJECT (folder), callback,
282                 user_data, offline_folder_downsync);
283
284         g_simple_async_result_set_check_cancellable (simple, cancellable);
285
286         g_simple_async_result_set_op_res_gpointer (
287                 simple, async_context, (GDestroyNotify) async_context_free);
288
289         g_simple_async_result_run_in_thread (
290                 simple, offline_folder_downsync_thread,
291                 io_priority, cancellable);
292
293         g_object_unref (simple);
294 }
295
296 static gboolean
297 offline_folder_downsync_finish (CamelOfflineFolder *folder,
298                                 GAsyncResult *result,
299                                 GError **error)
300 {
301         GSimpleAsyncResult *simple;
302
303         g_return_val_if_fail (
304                 g_simple_async_result_is_valid (
305                 result, G_OBJECT (folder), offline_folder_downsync), FALSE);
306
307         simple = G_SIMPLE_ASYNC_RESULT (result);
308
309         /* Assume success unless a GError is set. */
310         return !g_simple_async_result_propagate_error (simple, error);
311 }
312
313 static void
314 camel_offline_folder_class_init (CamelOfflineFolderClass *class)
315 {
316         GObjectClass *object_class;
317
318         g_type_class_add_private (class, sizeof (CamelOfflineFolderPrivate));
319
320         object_class = G_OBJECT_CLASS (class);
321         object_class->set_property = offline_folder_set_property;
322         object_class->get_property = offline_folder_get_property;
323
324         class->downsync_sync = offline_folder_downsync_sync;
325         class->downsync = offline_folder_downsync;
326         class->downsync_finish = offline_folder_downsync_finish;
327
328         g_object_class_install_property (
329                 object_class,
330                 PROP_OFFLINE_SYNC,
331                 g_param_spec_boolean (
332                         "offline-sync",
333                         "Offline Sync",
334                         _("Copy folder content locally for _offline operation"),
335                         FALSE,
336                         G_PARAM_READWRITE |
337                         CAMEL_PARAM_PERSISTENT));
338 }
339
340 static void
341 camel_offline_folder_init (CamelOfflineFolder *folder)
342 {
343         folder->priv = CAMEL_OFFLINE_FOLDER_GET_PRIVATE (folder);
344
345         g_signal_connect (
346                 folder, "changed",
347                 G_CALLBACK (offline_folder_changed), NULL);
348 }
349
350 /**
351  * camel_offline_folder_get_offline_sync:
352  * @folder: a #CamelOfflineFolder
353  *
354  * Since: 2.32
355  **/
356 gboolean
357 camel_offline_folder_get_offline_sync (CamelOfflineFolder *folder)
358 {
359         g_return_val_if_fail (CAMEL_IS_OFFLINE_FOLDER (folder), FALSE);
360
361         return folder->priv->offline_sync;
362 }
363
364 /**
365  * camel_offline_folder_set_offline_sync:
366  * @folder: a #CamelOfflineFolder
367  * @offline_sync: whether to synchronize for offline use
368  *
369  * Since: 2.32
370  **/
371 void
372 camel_offline_folder_set_offline_sync (CamelOfflineFolder *folder,
373                                        gboolean offline_sync)
374 {
375         g_return_if_fail (CAMEL_IS_OFFLINE_FOLDER (folder));
376
377         if ((folder->priv->offline_sync ? 1 : 0) == (offline_sync ? 1 : 0))
378                 return;
379
380         folder->priv->offline_sync = offline_sync;
381
382         g_object_notify (G_OBJECT (folder), "offline-sync");
383 }
384
385 /**
386  * camel_offline_folder_downsync_sync:
387  * @folder: a #CamelOfflineFolder
388  * @expression: search expression describing which set of messages
389  *              to downsync (%NULL for all)
390  * @cancellable: optional #GCancellable object, or %NULL
391  * @error: return location for a #GError, or %NULL
392  *
393  * Synchronizes messages in @folder described by the search @expression to
394  * the local machine for offline availability.
395  *
396  * Returns: %TRUE on success, %FALSE on error
397  *
398  * Since: 3.0
399  **/
400 gboolean
401 camel_offline_folder_downsync_sync (CamelOfflineFolder *folder,
402                                     const gchar *expression,
403                                     GCancellable *cancellable,
404                                     GError **error)
405 {
406         CamelOfflineFolderClass *class;
407         gboolean success;
408
409         g_return_val_if_fail (CAMEL_IS_OFFLINE_FOLDER (folder), FALSE);
410
411         class = CAMEL_OFFLINE_FOLDER_GET_CLASS (folder);
412         g_return_val_if_fail (class->downsync_sync != NULL, FALSE);
413
414         success = class->downsync_sync (
415                 folder, expression, cancellable, error);
416         CAMEL_CHECK_GERROR (folder, downsync_sync, success, error);
417
418         return success;
419 }
420
421 /**
422  * camel_offline_folder_downsync:
423  * @folder: a #CamelOfflineFolder
424  * @expression: search expression describing which set of messages
425  *              to downsync (%NULL for all)
426  * @io_priority: the I/O priority of the request
427  * @cancellable: optional #GCancellable object, or %NULl
428  * @callback: a #GAsyncReadyCallback to call when the request is satisfied
429  * @user_data: data to pass to the callback function
430  *
431  * Synchronizes messages in @folder described by the search @expression to
432  * the local machine asynchronously for offline availability.
433  *
434  * When the operation is finished, @callback will be called.  You can then
435  * call camel_offline_folder_downsync_finish() to get the result of the
436  * operation.
437  *
438  * Since: 3.0
439  **/
440 void
441 camel_offline_folder_downsync (CamelOfflineFolder *folder,
442                                const gchar *expression,
443                                gint io_priority,
444                                GCancellable *cancellable,
445                                GAsyncReadyCallback callback,
446                                gpointer user_data)
447 {
448         CamelOfflineFolderClass *class;
449
450         g_return_if_fail (CAMEL_IS_OFFLINE_FOLDER (folder));
451
452         class = CAMEL_OFFLINE_FOLDER_GET_CLASS (folder);
453         g_return_if_fail (class->downsync != NULL);
454
455         class->downsync (
456                 folder, expression, io_priority,
457                 cancellable, callback, user_data);
458 }
459
460 /**
461  * camel_offline_folder_downsync_finish:
462  * @folder: a #CamelOfflineFolder
463  * @result: a #GAsyncResult
464  * @error: return location for a #GError, or %NULL
465  *
466  * Finishes the operation started with camel_offline_folder_downsync().
467  *
468  * Returns: %TRUE on success, %FALSE on error
469  *
470  * Since: 3.0
471  **/
472 gboolean
473 camel_offline_folder_downsync_finish (CamelOfflineFolder *folder,
474                                       GAsyncResult *result,
475                                       GError **error)
476 {
477         CamelOfflineFolderClass *class;
478
479         g_return_val_if_fail (CAMEL_IS_OFFLINE_FOLDER (folder), FALSE);
480         g_return_val_if_fail (G_IS_ASYNC_RESULT (result), FALSE);
481
482         class = CAMEL_OFFLINE_FOLDER_GET_CLASS (folder);
483         g_return_val_if_fail (class->downsync_finish != NULL, FALSE);
484
485         return class->downsync_finish (folder, result, error);
486 }