Fix FSF address (Tobias Mueller, #470445)
[platform/upstream/evolution-data-server.git] / camel / providers / groupwise / camel-groupwise-journal.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 2004 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
24 #ifdef HAVE_CONFIG_H
25 #include <config.h>
26 #endif
27
28 #include <ctype.h>
29 #include <errno.h>
30 #include <fcntl.h>
31 #include <stdio.h>
32 #include <string.h>
33 #include <unistd.h>
34 #include <sys/stat.h>
35 #include <sys/types.h>
36
37 #include <glib.h>
38 #include <glib/gi18n-lib.h>
39
40 #include "camel-data-cache.h"
41 #include "camel-file-utils.h"
42 #include "camel-folder-summary.h"
43 #include "camel-folder.h"
44
45 #include "camel-groupwise-folder.h"
46 #include "camel-groupwise-journal.h"
47 #include "camel-groupwise-store.h"
48
49 #define d(x) x
50
51
52 static void camel_groupwise_journal_class_init (CamelGroupwiseJournalClass *klass);
53 static void camel_groupwise_journal_init (CamelGroupwiseJournal *journal, CamelGroupwiseJournalClass *klass);
54 static void camel_groupwise_journal_finalize (CamelObject *object);
55
56 static void groupwise_entry_free (CamelOfflineJournal *journal, EDListNode *entry);
57 static EDListNode *groupwise_entry_load (CamelOfflineJournal *journal, FILE *in);
58 static int groupwise_entry_write (CamelOfflineJournal *journal, EDListNode *entry, FILE *out);
59 static int groupwise_entry_play (CamelOfflineJournal *journal, EDListNode *entry, CamelException *ex);
60
61
62 static CamelOfflineJournalClass *parent_class = NULL;
63
64
65 CamelType
66 camel_groupwise_journal_get_type (void)
67 {
68         static CamelType type = 0;
69         
70         if (!type) {
71                 type = camel_type_register (camel_offline_journal_get_type (),
72                                             "CamelGroupwiseJournal",
73                                             sizeof (CamelGroupwiseJournal),
74                                             sizeof (CamelGroupwiseJournalClass),
75                                             (CamelObjectClassInitFunc) camel_groupwise_journal_class_init,
76                                             NULL,
77                                             (CamelObjectInitFunc) camel_groupwise_journal_init,
78                                             (CamelObjectFinalizeFunc) camel_groupwise_journal_finalize);
79         }
80         
81         return type;
82 }
83
84 static void
85 camel_groupwise_journal_class_init (CamelGroupwiseJournalClass *klass)
86 {
87         CamelOfflineJournalClass *journal_class = (CamelOfflineJournalClass *) klass;
88         
89         parent_class = (CamelOfflineJournalClass *) camel_type_get_global_classfuncs (CAMEL_TYPE_OFFLINE_JOURNAL);
90         
91         journal_class->entry_free = groupwise_entry_free;
92         journal_class->entry_load = groupwise_entry_load;
93         journal_class->entry_write = groupwise_entry_write;
94         journal_class->entry_play = groupwise_entry_play;
95 }
96
97 static void
98 camel_groupwise_journal_init (CamelGroupwiseJournal *journal, CamelGroupwiseJournalClass *klass)
99 {
100         
101 }
102
103 static void
104 camel_groupwise_journal_finalize (CamelObject *object)
105 {
106         
107 }
108
109 static void
110 groupwise_entry_free (CamelOfflineJournal *journal, EDListNode *entry)
111 {
112         CamelGroupwiseJournalEntry *groupwise_entry = (CamelGroupwiseJournalEntry *) entry;
113         
114         g_free (groupwise_entry->uid);
115         g_free (groupwise_entry->original_uid);
116         g_free (groupwise_entry->source_container);
117         g_free (groupwise_entry);
118 }
119
120 static EDListNode *
121 groupwise_entry_load (CamelOfflineJournal *journal, FILE *in)
122 {
123         CamelGroupwiseJournalEntry *entry;
124         
125         entry = g_malloc0 (sizeof (CamelGroupwiseJournalEntry));
126         
127         if (camel_file_util_decode_uint32 (in, &entry->type) == -1)
128                 goto exception;
129         
130         switch (entry->type) {
131         case CAMEL_GROUPWISE_JOURNAL_ENTRY_APPEND:
132                 if (camel_file_util_decode_string (in, &entry->uid) == -1)
133                         goto exception;
134                 break;
135         case CAMEL_GROUPWISE_JOURNAL_ENTRY_TRANSFER:
136                 if (camel_file_util_decode_string (in, &entry->uid) == -1)
137                         goto exception;
138                 if (camel_file_util_decode_string (in, &entry->original_uid) == -1)
139                         goto exception;
140                 if (camel_file_util_decode_string (in, &entry->source_container) == -1)
141                         goto exception;
142                 break;
143         default:
144                 goto exception;
145         }
146         
147         return (EDListNode *) entry;
148         
149  exception:
150         
151         if (entry->type == CAMEL_GROUPWISE_JOURNAL_ENTRY_TRANSFER)
152                 g_free (entry->source_container);
153         
154         g_free (entry->uid);
155         g_free (entry);
156         
157         return NULL;
158 }
159
160 static int
161 groupwise_entry_write (CamelOfflineJournal *journal, EDListNode *entry, FILE *out)
162 {
163         CamelGroupwiseJournalEntry *groupwise_entry = (CamelGroupwiseJournalEntry *) entry;
164         
165         if (camel_file_util_encode_uint32 (out, groupwise_entry->type) == -1)
166                 return -1;
167         
168         switch (groupwise_entry->type) {
169         case CAMEL_GROUPWISE_JOURNAL_ENTRY_APPEND:
170                 if (camel_file_util_encode_string (out, groupwise_entry->uid))
171                         return -1;
172                 break;
173         case CAMEL_GROUPWISE_JOURNAL_ENTRY_TRANSFER:
174                 if (camel_file_util_encode_string (out, groupwise_entry->uid))
175                         return -1;
176                 if (camel_file_util_encode_string (out, groupwise_entry->original_uid))
177                         return -1;
178                 if (camel_file_util_encode_string (out, groupwise_entry->source_container))
179                         return -1;
180                 break;
181         default:
182                 g_assert_not_reached ();
183         }
184         
185         return 0;
186 }
187
188 static void
189 gw_message_info_dup_to (CamelMessageInfoBase *dest, CamelMessageInfoBase *src)
190 {
191         camel_flag_list_copy (&dest->user_flags, &src->user_flags);
192         camel_tag_list_copy (&dest->user_tags, &src->user_tags);
193         dest->date_received = src->date_received;
194         dest->date_sent = src->date_sent;
195         dest->flags = src->flags;
196         dest->size = src->size;
197 }
198
199 static int
200 groupwise_entry_play_append (CamelOfflineJournal *journal, CamelGroupwiseJournalEntry *entry, CamelException *ex)
201 {
202         CamelGroupwiseFolder *gw_folder = (CamelGroupwiseFolder *) journal->folder;
203         CamelFolder *folder = journal->folder;
204         CamelMimeMessage *message;
205         CamelMessageInfo *info;
206         CamelStream *stream;
207         CamelException lex;
208         
209         /* if the message isn't in the cache, the user went behind our backs so "not our problem" */
210         if (!gw_folder->cache || !(stream = camel_data_cache_get (gw_folder->cache, "cache", entry->uid, ex)))
211                 goto done;
212         
213         message = camel_mime_message_new ();
214         if (camel_data_wrapper_construct_from_stream ((CamelDataWrapper *) message, stream) == -1) {
215                 camel_object_unref (message);
216                 camel_object_unref (stream);
217                 goto done;
218         }
219         
220         camel_object_unref (stream);
221         
222         if (!(info = camel_folder_summary_uid (folder->summary, entry->uid))) {
223                 /* Note: this should never happen, but rather than crash lets make a new info */
224                 info = camel_message_info_new (NULL);
225         }
226         
227         camel_exception_init (&lex);
228         camel_folder_append_message (folder, message, info, NULL, &lex);
229         camel_message_info_free (info);
230         camel_object_unref (message);
231         
232         if (camel_exception_is_set (&lex)) {
233                 camel_exception_xfer (ex, &lex);
234                 return -1;
235         }
236         
237  done:
238         
239         camel_folder_summary_remove_uid (folder->summary, entry->uid);
240         camel_data_cache_remove (gw_folder->cache, "cache", entry->uid, NULL);
241         
242         return 0;
243 }
244
245 static int
246 groupwise_entry_play_transfer (CamelOfflineJournal *journal, CamelGroupwiseJournalEntry *entry, CamelException *ex)
247 {
248         CamelGroupwiseFolder *gw_folder = (CamelGroupwiseFolder *) journal->folder;
249         CamelFolder *folder = journal->folder;
250         CamelGroupwiseMessageInfo *real;
251         CamelMessageInfoBase *info;
252         GPtrArray *xuids, *uids;
253         CamelException lex;
254         CamelFolder *src;
255         const char *name;
256         
257         if (!(info = (CamelMessageInfoBase *) camel_folder_summary_uid (folder->summary, entry->uid))) {
258                 /* Note: this should never happen, but rather than crash lets make a new info */
259                 info = camel_message_info_new (NULL);
260         }
261         
262         name = camel_groupwise_store_folder_lookup ((CamelGroupwiseStore *) folder->parent_store, entry->source_container);
263         if (name && (src = camel_store_get_folder (folder->parent_store, name, 0, ex))) {
264                 uids = g_ptr_array_sized_new (1);
265                 g_ptr_array_add (uids, entry->original_uid);
266                 
267                 camel_exception_init (&lex);
268                 camel_folder_transfer_messages_to (src, uids, folder, &xuids, FALSE, &lex);
269                 if (!camel_exception_is_set (&lex)) {
270                         real = (CamelGroupwiseMessageInfo *) camel_folder_summary_uid (folder->summary, xuids->pdata[0]);
271                         
272                         /* transfer all the system flags, user flags/tags, etc */
273                         gw_message_info_dup_to ((CamelMessageInfoBase *) real, (CamelMessageInfoBase *) info);
274                         camel_message_info_free (real);
275                 } else {
276                         camel_exception_xfer (ex, &lex);
277                         goto exception;
278                 }
279                 
280                 g_ptr_array_free (xuids, TRUE);
281                 g_ptr_array_free (uids, TRUE);
282                 camel_object_unref (src);
283         } else if (!name) {
284                 camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, _("Cannot get folder container %s"),
285                                       entry->source_container);
286                 goto exception;
287         }
288         
289         /* message was successfully transferred, remove the fake item from the cache/summary */
290         camel_folder_summary_remove_uid (folder->summary, entry->uid);
291         camel_data_cache_remove (gw_folder->cache, "cache", entry->uid, NULL);
292         camel_message_info_free (info);
293         
294         return 0;
295         
296  exception:
297         
298         camel_message_info_free (info);
299         
300         return -1;
301 }
302
303 static int
304 groupwise_entry_play (CamelOfflineJournal *journal, EDListNode *entry, CamelException *ex)
305 {
306         CamelGroupwiseJournalEntry *groupwise_entry = (CamelGroupwiseJournalEntry *) entry;
307         
308         switch (groupwise_entry->type) {
309         case CAMEL_GROUPWISE_JOURNAL_ENTRY_APPEND:
310                 return groupwise_entry_play_append (journal, groupwise_entry, ex);
311         case CAMEL_GROUPWISE_JOURNAL_ENTRY_TRANSFER:
312                 return groupwise_entry_play_transfer (journal, groupwise_entry, ex);
313         default:
314                 g_assert_not_reached ();
315                 return -1;
316         }
317 }
318
319
320
321 CamelOfflineJournal *
322 camel_groupwise_journal_new (CamelGroupwiseFolder *folder, const char *filename)
323 {
324         CamelOfflineJournal *journal;
325         
326         g_return_val_if_fail (CAMEL_IS_GROUPWISE_FOLDER (folder), NULL);
327         
328         journal = (CamelOfflineJournal *) camel_object_new (camel_groupwise_journal_get_type ());
329         camel_offline_journal_construct (journal, (CamelFolder *) folder, filename);
330         
331         return journal;
332 }
333
334 static gboolean
335 update_cache (CamelGroupwiseJournal *groupwise_journal, CamelMimeMessage *message,
336               const CamelMessageInfo *mi, char **updated_uid, CamelException *ex)
337 {
338         CamelOfflineJournal *journal = (CamelOfflineJournal *) groupwise_journal;
339         CamelGroupwiseFolder *groupwise_folder = (CamelGroupwiseFolder *) journal->folder;
340         CamelFolder *folder = (CamelFolder *) journal->folder;
341         CamelMessageInfo *info;
342         CamelStream *cache;
343         guint32 nextuid;
344         char *uid;
345         
346         if (groupwise_folder->cache == NULL) {
347                 camel_exception_set (ex, CAMEL_EXCEPTION_SYSTEM,
348                                      _("Cannot append message in offline mode: cache unavailable"));
349                 return FALSE;
350         }
351         
352         nextuid = camel_folder_summary_next_uid (folder->summary);
353         uid = g_strdup_printf ("-%u", nextuid);
354         
355         if (!(cache = camel_data_cache_add (groupwise_folder->cache, "cache", uid, ex))) {
356                 folder->summary->nextuid--;
357                 g_free (uid);
358                 return FALSE;
359         }
360         
361         if (camel_data_wrapper_write_to_stream ((CamelDataWrapper *) message, cache) == -1
362             || camel_stream_flush (cache) == -1) {
363                 camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
364                                       _("Cannot append message in offline mode: %s"),
365                                       g_strerror (errno));
366                 camel_data_cache_remove (groupwise_folder->cache, "cache", uid, NULL);
367                 folder->summary->nextuid--;
368                 camel_object_unref (cache);
369                 g_free (uid);
370                 return FALSE;
371         }
372         
373         camel_object_unref (cache);
374         
375         info = camel_folder_summary_info_new_from_message (folder->summary, message);
376         g_free(info->uid);
377         info->uid = g_strdup (uid);
378         
379         gw_message_info_dup_to ((CamelMessageInfoBase *) info, (CamelMessageInfoBase *) mi);
380         
381         camel_folder_summary_add (folder->summary, info);
382         
383         if (updated_uid)
384                 *updated_uid = g_strdup (uid);
385         
386         g_free (uid);
387         
388         return TRUE;
389 }
390
391 void
392 camel_groupwise_journal_append (CamelGroupwiseJournal *groupwise_journal, CamelMimeMessage *message,
393                                 const CamelMessageInfo *mi, char **appended_uid, CamelException *ex)
394 {
395         CamelOfflineJournal *journal = (CamelOfflineJournal *) groupwise_journal;
396         CamelGroupwiseJournalEntry *entry;
397         char *uid;
398         
399         if (!update_cache (groupwise_journal, message, mi, &uid, ex))
400                 return;
401         
402         entry = g_new (CamelGroupwiseJournalEntry, 1);
403         entry->type = CAMEL_GROUPWISE_JOURNAL_ENTRY_APPEND;
404         entry->uid = uid;
405                                 
406         e_dlist_addtail (&journal->queue, (EDListNode *) entry);
407         
408         if (appended_uid)
409                 *appended_uid = g_strdup (uid);
410 }
411
412 void
413 camel_groupwise_journal_transfer (CamelGroupwiseJournal *groupwise_journal, CamelGroupwiseFolder *source_folder,
414                                   CamelMimeMessage *message,  const CamelMessageInfo *mi,
415                                   const char *original_uid, char **transferred_uid,
416                                   CamelException *ex)
417 {
418         CamelOfflineJournal *journal = (CamelOfflineJournal *) groupwise_journal;
419         CamelGroupwiseStore *gw_store= CAMEL_GROUPWISE_STORE(journal->folder->parent_store) ;
420         CamelGroupwiseJournalEntry *entry;
421         char *uid;
422         
423         if (!update_cache (groupwise_journal, message, mi, &uid, ex))
424                 return;
425         
426         entry = g_new (CamelGroupwiseJournalEntry, 1);
427         entry->type = CAMEL_GROUPWISE_JOURNAL_ENTRY_APPEND;
428         entry->uid = uid;
429         entry->original_uid = g_strdup (original_uid);
430         entry->source_container = g_strdup (camel_groupwise_store_container_id_lookup (gw_store, ((CamelFolder *)source_folder)->name));
431         
432         e_dlist_addtail (&journal->queue, (EDListNode *) entry);
433         
434         if (transferred_uid)
435                 *transferred_uid = g_strdup (uid);
436 }