Fix FSF address (Tobias Mueller, #470445)
[platform/upstream/evolution-data-server.git] / camel / camel-disco-store.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /* camel-disco-store.c: abstract class for a disconnectable store */
3
4 /*
5  *  Authors: Dan Winship <danw@ximian.com>
6  *
7  *  Copyright 2001 Ximian, Inc.
8  *
9  * This program is free software; you can redistribute it and/or
10  * modify it under the terms of version 2 of the GNU Lesser General Public
11  * License as published by the Free Software Foundation.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this program; if not, write to the
20  * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21  * Boston, MA 02110-1301, USA.
22  *
23  */
24
25
26 #ifdef HAVE_CONFIG_H
27 #include <config.h>
28 #endif
29
30 #include <glib.h>
31 #include <glib/gi18n-lib.h>
32
33 #include "camel-disco-diary.h"
34 #include "camel-disco-folder.h"
35 #include "camel-disco-store.h"
36 #include "camel-exception.h"
37 #include "camel-session.h"
38
39 #define d(x) 
40
41 #define CDS_CLASS(o) (CAMEL_DISCO_STORE_CLASS (CAMEL_OBJECT_GET_CLASS (o)))
42
43 static CamelStoreClass *parent_class = NULL;
44
45 static void disco_construct (CamelService *service, CamelSession *session,
46                              CamelProvider *provider, CamelURL *url,
47                              CamelException *ex);
48 static gboolean disco_connect (CamelService *service, CamelException *ex);
49 static void disco_cancel_connect (CamelService *service);
50 static gboolean disco_disconnect (CamelService *service, gboolean clean, CamelException *ex);
51 static CamelFolder *disco_get_folder (CamelStore *store, const char *name,
52                                       guint32 flags, CamelException *ex);
53 static CamelFolderInfo *disco_get_folder_info (CamelStore *store,
54                                                const char *top, guint32 flags,
55                                                CamelException *ex);
56 static void set_status (CamelDiscoStore *disco_store,
57                         CamelDiscoStoreStatus status,
58                         CamelException *ex);
59 static gboolean can_work_offline (CamelDiscoStore *disco_store);
60
61 static int disco_setv (CamelObject *object, CamelException *ex, CamelArgV *args);
62 static int disco_getv (CamelObject *object, CamelException *ex, CamelArgGetV *args);
63
64 static void
65 camel_disco_store_class_init (CamelDiscoStoreClass *camel_disco_store_class)
66 {
67         CamelObjectClass *camel_object_class =
68                 CAMEL_OBJECT_CLASS (camel_disco_store_class);
69         CamelServiceClass *camel_service_class =
70                 CAMEL_SERVICE_CLASS (camel_disco_store_class);
71         CamelStoreClass *camel_store_class =
72                 CAMEL_STORE_CLASS (camel_disco_store_class);
73         
74         parent_class = CAMEL_STORE_CLASS (camel_type_get_global_classfuncs (camel_store_get_type ()));
75         
76         /* virtual method definition */
77         camel_disco_store_class->set_status = set_status;
78         camel_disco_store_class->can_work_offline = can_work_offline;
79         
80         /* virtual method overload */
81         camel_object_class->setv = disco_setv;
82         camel_object_class->getv = disco_getv;
83         
84         camel_service_class->construct = disco_construct;
85         camel_service_class->connect = disco_connect;
86         camel_service_class->disconnect = disco_disconnect;
87         camel_service_class->cancel_connect = disco_cancel_connect;
88         
89         camel_store_class->get_folder = disco_get_folder;
90         camel_store_class->get_folder_info = disco_get_folder_info;
91 }
92
93 CamelType
94 camel_disco_store_get_type (void)
95 {
96         static CamelType camel_disco_store_type = CAMEL_INVALID_TYPE;
97         
98         if (camel_disco_store_type == CAMEL_INVALID_TYPE) {
99                 camel_disco_store_type = camel_type_register (
100                         CAMEL_STORE_TYPE,
101                         "CamelDiscoStore",
102                         sizeof (CamelDiscoStore),
103                         sizeof (CamelDiscoStoreClass),
104                         (CamelObjectClassInitFunc) camel_disco_store_class_init,
105                         NULL,
106                         NULL,
107                         NULL);
108         }
109         
110         return camel_disco_store_type;
111 }
112
113 static int
114 disco_setv (CamelObject *object, CamelException *ex, CamelArgV *args)
115 {
116         /* CamelDiscoStore doesn't currently have anything to set */
117         return CAMEL_OBJECT_CLASS (parent_class)->setv (object, ex, args);
118 }
119
120 static int
121 disco_getv (CamelObject *object, CamelException *ex, CamelArgGetV *args)
122 {
123         /* CamelDiscoStore doesn't currently have anything to get */
124         return CAMEL_OBJECT_CLASS (parent_class)->getv (object, ex, args);
125 }
126
127 static void
128 disco_construct (CamelService *service, CamelSession *session,
129                  CamelProvider *provider, CamelURL *url,
130                  CamelException *ex)
131 {
132         CamelDiscoStore *disco = CAMEL_DISCO_STORE (service);
133
134         CAMEL_SERVICE_CLASS (parent_class)->construct (service, session, provider, url, ex);
135         if (camel_exception_is_set (ex))
136                 return;
137
138         disco->status = camel_session_is_online (session) ?
139                 CAMEL_DISCO_STORE_ONLINE : CAMEL_DISCO_STORE_OFFLINE;
140 }
141
142 static gboolean
143 disco_connect (CamelService *service, CamelException *ex)
144 {
145         CamelDiscoStore *store = CAMEL_DISCO_STORE (service);
146         CamelDiscoStoreStatus status;
147         struct _CamelDiscoDiary *diary;
148
149         status = camel_disco_store_status (store);
150         if (status != CAMEL_DISCO_STORE_OFFLINE) {
151                 if (!CAMEL_SERVICE_CLASS (parent_class)->connect (service, ex)) {
152                         status = camel_disco_store_status (store);
153                         if (status != CAMEL_DISCO_STORE_OFFLINE)
154                                 return FALSE;
155                         camel_exception_clear (ex);
156                 }
157         }
158
159         switch (status) {
160         case CAMEL_DISCO_STORE_ONLINE:
161         case CAMEL_DISCO_STORE_RESYNCING:
162                 if (!CDS_CLASS (service)->connect_online (service, ex))
163                         return FALSE;
164
165                 if (!store->diary)
166                         return TRUE;
167
168                 d(printf(" diary is %s\n", camel_disco_diary_empty(store->diary)?"empty":"not empty"));
169                 if (camel_disco_diary_empty (store->diary))
170                         return TRUE;
171
172                 /* Need to resync.  Note we do the ref thing since during the replay
173                    disconnect could be called, which will remove store->diary and unref it */
174                 store->status = CAMEL_DISCO_STORE_RESYNCING;
175                 diary = store->diary;
176                 camel_object_ref(diary);
177                 camel_disco_diary_replay(diary, ex);
178                 camel_object_unref(diary);
179                 store->status = CAMEL_DISCO_STORE_ONLINE;
180                 if (camel_exception_is_set (ex))
181                         return FALSE;
182
183                 if (!camel_service_disconnect (service, TRUE, ex))
184                         return FALSE;
185                 return camel_service_connect (service, ex);
186
187         case CAMEL_DISCO_STORE_OFFLINE:
188                 return CDS_CLASS (service)->connect_offline (service, ex);
189         }
190
191         g_assert_not_reached ();
192         return FALSE;
193 }
194
195 static void
196 disco_cancel_connect (CamelService *service)
197 {
198         CamelDiscoStore *store = CAMEL_DISCO_STORE (service);
199
200         /* Fall back */
201         store->status = CAMEL_DISCO_STORE_OFFLINE;
202         CAMEL_SERVICE_CLASS (parent_class)->cancel_connect (service);
203 }
204
205 static gboolean
206 disco_disconnect (CamelService *service, gboolean clean, CamelException *ex)
207 {
208         CamelDiscoStore *store = CAMEL_DISCO_STORE (service);
209
210         switch (camel_disco_store_status (store)) {
211         case CAMEL_DISCO_STORE_ONLINE:
212         case CAMEL_DISCO_STORE_RESYNCING:
213                 if (!CDS_CLASS (service)->disconnect_online (service, clean, ex))
214                         return FALSE;
215                 break;
216
217         case CAMEL_DISCO_STORE_OFFLINE:
218                 if (!CDS_CLASS (service)->disconnect_offline (service, clean, ex))
219                         return FALSE;
220                 break;
221
222         }
223
224         return CAMEL_SERVICE_CLASS (parent_class)->disconnect (service, clean, ex);
225 }
226
227 static CamelFolder *
228 disco_get_folder (CamelStore *store, const char *name,
229                   guint32 flags, CamelException *ex)
230 {
231         CamelDiscoStore *disco_store = CAMEL_DISCO_STORE (store);
232         
233         switch (camel_disco_store_status (disco_store)) {
234         case CAMEL_DISCO_STORE_ONLINE:
235                 return CDS_CLASS (store)->get_folder_online (store, name, flags, ex);
236                 
237         case CAMEL_DISCO_STORE_OFFLINE:
238                 return CDS_CLASS (store)->get_folder_offline (store, name, flags, ex);
239                 
240         case CAMEL_DISCO_STORE_RESYNCING:
241                 return CDS_CLASS (store)->get_folder_resyncing (store, name, flags, ex);        
242         }
243         
244         g_assert_not_reached ();
245         return NULL;
246 }
247
248 static CamelFolderInfo *
249 disco_get_folder_info (CamelStore *store, const char *top,
250                        guint32 flags, CamelException *ex)
251 {
252         CamelDiscoStore *disco_store = CAMEL_DISCO_STORE (store);
253
254         switch (camel_disco_store_status (disco_store)) {
255         case CAMEL_DISCO_STORE_ONLINE:
256                 return CDS_CLASS (store)->get_folder_info_online (store, top, flags, ex);
257                 
258         case CAMEL_DISCO_STORE_OFFLINE:
259                 /* Can't edit subscriptions while offline */
260                 if ((store->flags & CAMEL_STORE_SUBSCRIPTIONS) &&
261                     !(flags & CAMEL_STORE_FOLDER_INFO_SUBSCRIBED)) {
262                         camel_disco_store_check_online (disco_store, ex);
263                         return NULL;
264                 }
265                 
266                 return CDS_CLASS (store)->get_folder_info_offline (store, top, flags, ex);
267                 
268         case CAMEL_DISCO_STORE_RESYNCING:
269                 return CDS_CLASS (store)->get_folder_info_resyncing (store, top, flags, ex);
270         }
271         
272         g_assert_not_reached ();
273         return NULL;
274 }
275
276
277 /**
278  * camel_disco_store_status:
279  * @store: a disconnectable store
280  *
281  * Return value: the current online/offline status of @store.
282  **/
283 CamelDiscoStoreStatus
284 camel_disco_store_status (CamelDiscoStore *store)
285 {
286         CamelService *service = CAMEL_SERVICE (store);
287
288         g_return_val_if_fail (CAMEL_IS_DISCO_STORE (store), CAMEL_DISCO_STORE_ONLINE);
289
290         if (store->status != CAMEL_DISCO_STORE_OFFLINE
291             && !camel_session_is_online (service->session))
292                 store->status = CAMEL_DISCO_STORE_OFFLINE;
293
294         return store->status;
295 }
296
297 static void
298 set_status(CamelDiscoStore *disco_store, CamelDiscoStoreStatus status, CamelException *ex)
299 {
300         CamelException x;
301         CamelService *service = CAMEL_SERVICE (disco_store);
302         gboolean network_state = camel_session_get_network_state (service->session);
303
304         if (disco_store->status == status)
305                 return;
306
307         camel_exception_init(&x);
308         /* Sync the folder fully if we've been told to sync online for this store or this folder
309            and we're going offline */
310
311         if (network_state) {
312                 if (disco_store->status == CAMEL_DISCO_STORE_ONLINE
313                     && status == CAMEL_DISCO_STORE_OFFLINE) {
314                         if (((CamelStore *)disco_store)->folders) {
315                                 GPtrArray *folders;
316                                 CamelFolder *folder;
317                                 int i, sync;
318                                 
319                                 sync =  camel_url_get_param(((CamelService *)disco_store)->url, "offline_sync") != NULL;
320                                 
321                                 folders = camel_object_bag_list(((CamelStore *)disco_store)->folders);
322                                 for (i=0;i<folders->len;i++) {
323                                         folder = folders->pdata[i];
324                                         if (CAMEL_CHECK_TYPE(folder, CAMEL_DISCO_FOLDER_TYPE)
325                                             && (sync || ((CamelDiscoFolder *)folder)->offline_sync)) {
326                                                 camel_disco_folder_prepare_for_offline((CamelDiscoFolder *)folder, "(match-all)", &x);
327                                                 camel_exception_clear(&x);
328                                         }
329                                         camel_object_unref(folder);
330                                 }
331                                 g_ptr_array_free(folders, TRUE);
332                         }
333                 }
334                 
335                 camel_store_sync(CAMEL_STORE (disco_store), FALSE, &x);
336                 camel_exception_clear(&x);
337         }
338         
339         if (!camel_service_disconnect (CAMEL_SERVICE (disco_store), network_state, ex))
340                 return;
341
342         disco_store->status = status;
343         camel_service_connect (CAMEL_SERVICE (disco_store), ex);
344 }
345
346 /**
347  * camel_disco_store_set_status:
348  * @store: a disconnectable store
349  * @status: the new status
350  * @ex: a CamelException
351  *
352  * Sets @store to @status. If an error occurrs and the status cannot
353  * be set to @status, @ex will be set.
354  **/
355 void
356 camel_disco_store_set_status (CamelDiscoStore *store,
357                               CamelDiscoStoreStatus status,
358                               CamelException *ex)
359 {
360         d(printf("disco store set status: %s\n", status == CAMEL_DISCO_STORE_ONLINE?"online":"offline"));
361
362         CDS_CLASS (store)->set_status (store, status, ex);
363 }
364
365 static gboolean
366 can_work_offline (CamelDiscoStore *disco_store)
367 {
368         g_warning ("CamelDiscoStore::can_work_offline not implemented for `%s'",
369                    camel_type_to_name (CAMEL_OBJECT_GET_TYPE (disco_store)));
370         return FALSE;
371 }
372
373 /**
374  * camel_disco_store_can_work_offline:
375  * @store: a disconnectable store
376  *
377  * Return value: whether or not @store can be used offline. (Will be
378  * %FALSE if the store is not caching data to local disk, for example.)
379  **/
380 gboolean
381 camel_disco_store_can_work_offline (CamelDiscoStore *store)
382 {
383         return CDS_CLASS (store)->can_work_offline (store);
384 }
385
386
387 /**
388  * camel_disco_store_check_online:
389  * @store: a disconnectable store
390  * @ex: a CamelException
391  *
392  * This checks that @store is online, and sets @ex if it is not. This
393  * can be used as a simple way to set a generic error message in @ex
394  * for operations that won't work offline.
395  *
396  * Return value: whether or not @store is online.
397  **/
398 gboolean
399 camel_disco_store_check_online (CamelDiscoStore *store, CamelException *ex)
400 {
401         if (camel_disco_store_status (store) != CAMEL_DISCO_STORE_ONLINE) {
402                 camel_exception_set (ex, CAMEL_EXCEPTION_SERVICE_UNAVAILABLE,
403                                      _("You must be working online to "
404                                        "complete this operation"));
405                 return FALSE;
406         }
407
408         return TRUE;
409 }
410
411 void
412 camel_disco_store_prepare_for_offline(CamelDiscoStore *disco_store, CamelException *ex)
413 {
414         CamelException x;
415         CamelService *service = CAMEL_SERVICE (disco_store);
416         gboolean network_state = camel_session_get_network_state (service->session);
417
418         camel_exception_init(&x);
419         /* Sync the folder fully if we've been told to sync online for this store or this folder */
420
421         if (network_state) {
422                 if (disco_store->status == CAMEL_DISCO_STORE_ONLINE) {
423                         if (((CamelStore *)disco_store)->folders) {
424                                 GPtrArray *folders;
425                                 CamelFolder *folder;
426                                 int i, sync;
427                                 
428                                 sync =  camel_url_get_param(((CamelService *)disco_store)->url, "offline_sync") != NULL;
429                                 
430                                 folders = camel_object_bag_list(((CamelStore *)disco_store)->folders);
431                                 for (i=0;i<folders->len;i++) {
432                                         folder = folders->pdata[i];
433                                         if (CAMEL_CHECK_TYPE(folder, CAMEL_DISCO_FOLDER_TYPE)
434                                             && (sync || ((CamelDiscoFolder *)folder)->offline_sync)) {
435                                                 camel_disco_folder_prepare_for_offline((CamelDiscoFolder *)folder, "(match-all)", &x);
436                                                 camel_exception_clear(&x);
437                                         }
438                                         camel_object_unref(folder);
439                                 }
440                                 g_ptr_array_free(folders, TRUE);
441                         }
442                 }
443                 
444                 camel_store_sync(CAMEL_STORE (disco_store), FALSE, &x);
445                 camel_exception_clear(&x);
446         }
447 }
448