Make this return a GPtrArray rather than a GList.
[platform/upstream/evolution-data-server.git] / camel / camel-folder.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8; fill-column: 160 -*- */
2 /* camel-folder.c: Abstract class for an email folder */
3
4 /*
5  * Author:
6  *  Bertrand Guiheneuf <bertrand@helixcode.com>
7  *
8  * Copyright 1999, 2000 Helix Code, Inc. (http://www.helixcode.com)
9  *
10  * This program is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU General Public License as
12  * published by the Free Software Foundation; either version 2 of the
13  * License, or (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
23  * USA
24  */
25
26 #include <config.h>
27 #include <string.h>
28 #include "camel-folder.h"
29 #include "camel-exception.h"
30 #include "camel-store.h"
31 #include "camel-mime-message.h"
32 #include "string-utils.h"
33
34 static CamelObjectClass *parent_class = NULL;
35
36 /* Returns the class for a CamelFolder */
37 #define CF_CLASS(so) CAMEL_FOLDER_CLASS (GTK_OBJECT (so)->klass)
38
39
40 enum SIGNALS {
41         FOLDER_CHANGED,
42         MESSAGE_CHANGED,
43         LAST_SIGNAL
44 };
45
46 static guint signals[LAST_SIGNAL] = { 0 };
47
48
49 static void init (CamelFolder *folder, CamelStore *parent_store,
50                   CamelFolder *parent_folder, const gchar *name,
51                   gchar *separator, gboolean path_begins_with_sep,
52                   CamelException *ex);
53
54 static void finalize (GtkObject *object);
55
56
57 static void folder_sync (CamelFolder *folder, gboolean expunge,
58                          CamelException *ex);
59
60 static const gchar *get_name (CamelFolder *folder);
61 static const gchar *get_full_name (CamelFolder *folder);
62
63
64 static gboolean can_hold_folders (CamelFolder *folder);
65 static gboolean can_hold_messages (CamelFolder *folder);
66 static guint32 get_permanent_flags (CamelFolder *folder, CamelException *ex);
67 static guint32 get_message_flags (CamelFolder *folder, const char *uid,
68                                   CamelException *ex);
69 static void set_message_flags (CamelFolder *folder, const char *uid,
70                                guint32 flags, guint32 set, CamelException *ex);
71 static gboolean get_message_user_flag (CamelFolder *folder, const char *uid,
72                                        const char *name, CamelException *ex);
73 static void set_message_user_flag (CamelFolder *folder, const char *uid,
74                                    const char *name, gboolean value,
75                                    CamelException *ex);
76
77
78 static GPtrArray *get_subfolder_names (CamelFolder *folder,
79                                        CamelException *ex);
80 static CamelFolder *get_subfolder     (CamelFolder *folder,
81                                        const gchar *folder_name,
82                                        gboolean create,
83                                        CamelException *ex);
84 static CamelFolder *get_parent_folder (CamelFolder *folder,
85                                        CamelException *ex);
86 static CamelStore *get_parent_store   (CamelFolder *folder,
87                                        CamelException *ex);
88
89
90 static gint get_message_count (CamelFolder *folder, CamelException *ex);
91
92
93 static void expunge             (CamelFolder *folder,
94                                  CamelException *ex);
95
96
97 static void append_message (CamelFolder *folder, CamelMimeMessage *message,
98                             CamelException *ex);
99
100
101 static GPtrArray        *get_uids            (CamelFolder *folder,
102                                               CamelException *ex);
103 static void              free_uids           (CamelFolder *folder,
104                                               GPtrArray *array);
105 static GPtrArray        *get_summary         (CamelFolder *folder,
106                                               CamelException *ex);
107 static void              free_summary        (CamelFolder *folder,
108                                               GPtrArray *array);
109 static const gchar      *get_message_uid     (CamelFolder *folder,
110                                               CamelMimeMessage *message,
111                                               CamelException *ex);
112 static CamelMimeMessage *get_message         (CamelFolder *folder,
113                                               const gchar *uid,
114                                               CamelException *ex);
115 static void delete_message                   (CamelFolder *folder,
116                                               const gchar *uid,
117                                               CamelException *ex);
118
119 static const CamelMessageInfo *get_message_info (CamelFolder *folder,
120                                                  const char *uid);
121
122 static GPtrArray      *search_by_expression  (CamelFolder *folder,
123                                               const char *exp,
124                                               CamelException *ex);
125
126 static void            copy_message_to       (CamelFolder *source,
127                                               const char *uid,
128                                               CamelFolder *dest,
129                                               CamelException *ex);
130
131 static void            move_message_to       (CamelFolder *source,
132                                               const char *uid,
133                                               CamelFolder *dest,
134                                               CamelException *ex);
135
136 static void            freeze                (CamelFolder *folder);
137 static void            thaw                  (CamelFolder *folder);
138
139 static void            folder_changed        (CamelFolder *folder,
140                                               int type);
141 static void            message_changed       (CamelFolder *folder,
142                                               const char *uid);
143
144
145 static void
146 camel_folder_class_init (CamelFolderClass *camel_folder_class)
147 {
148         GtkObjectClass *gtk_object_class =
149                 GTK_OBJECT_CLASS (camel_folder_class);
150
151         parent_class = gtk_type_class (camel_object_get_type ());
152
153         /* virtual method definition */
154         camel_folder_class->init = init;
155         camel_folder_class->sync = folder_sync;
156         camel_folder_class->get_name = get_name;
157         camel_folder_class->get_full_name = get_full_name;
158         camel_folder_class->can_hold_folders = can_hold_folders;
159         camel_folder_class->can_hold_messages = can_hold_messages;
160         camel_folder_class->get_subfolder = get_subfolder;
161         camel_folder_class->get_parent_folder = get_parent_folder;
162         camel_folder_class->get_parent_store = get_parent_store;
163         camel_folder_class->get_subfolder_names = get_subfolder_names;
164         camel_folder_class->free_subfolder_names = free_uids;
165         camel_folder_class->expunge = expunge;
166         camel_folder_class->get_message_count = get_message_count;
167         camel_folder_class->append_message = append_message;
168         camel_folder_class->get_permanent_flags = get_permanent_flags;
169         camel_folder_class->get_message_flags = get_message_flags;
170         camel_folder_class->set_message_flags = set_message_flags;
171         camel_folder_class->get_message_user_flag = get_message_user_flag;
172         camel_folder_class->set_message_user_flag = set_message_user_flag;
173         camel_folder_class->get_message_uid = get_message_uid;
174         camel_folder_class->get_message = get_message;
175         camel_folder_class->delete_message = delete_message;
176         camel_folder_class->get_uids = get_uids;
177         camel_folder_class->free_uids = free_uids;
178         camel_folder_class->get_summary = get_summary;
179         camel_folder_class->free_summary = free_summary;
180         camel_folder_class->search_by_expression = search_by_expression;
181         camel_folder_class->get_message_info = get_message_info;
182         camel_folder_class->copy_message_to = copy_message_to;
183         camel_folder_class->move_message_to = move_message_to;
184         camel_folder_class->freeze = freeze;
185         camel_folder_class->thaw = thaw;
186         camel_folder_class->folder_changed = folder_changed;
187         camel_folder_class->message_changed = message_changed;
188
189         /* virtual method overload */
190         gtk_object_class->finalize = finalize;
191
192         signals[FOLDER_CHANGED] =
193                 gtk_signal_new ("folder_changed",
194                                 GTK_RUN_FIRST,
195                                 gtk_object_class->type,
196                                 GTK_SIGNAL_OFFSET (CamelFolderClass,
197                                                    folder_changed),
198                                 gtk_marshal_NONE__INT,
199                                 GTK_TYPE_NONE, 1, GTK_TYPE_INT);
200
201         signals[MESSAGE_CHANGED] =
202                 gtk_signal_new ("message_changed",
203                                 GTK_RUN_FIRST,
204                                 gtk_object_class->type,
205                                 GTK_SIGNAL_OFFSET (CamelFolderClass,
206                                                    message_changed),
207                                 gtk_marshal_NONE__STRING,
208                                 GTK_TYPE_NONE, 1, GTK_TYPE_STRING);
209
210         gtk_object_class_add_signals (gtk_object_class, signals, LAST_SIGNAL);
211
212 }
213
214
215 GtkType
216 camel_folder_get_type (void)
217 {
218         static GtkType camel_folder_type = 0;
219
220         if (!camel_folder_type) {
221                 GtkTypeInfo camel_folder_info =
222                 {
223                         "CamelFolder",
224                         sizeof (CamelFolder),
225                         sizeof (CamelFolderClass),
226                         (GtkClassInitFunc) camel_folder_class_init,
227                         (GtkObjectInitFunc) NULL,
228                                 /* reserved_1 */ NULL,
229                                 /* reserved_2 */ NULL,
230                         (GtkClassInitFunc) NULL,
231                 };
232
233                 camel_folder_type = gtk_type_unique (camel_object_get_type (),
234                                                      &camel_folder_info);
235         }
236
237         return camel_folder_type;
238 }
239
240
241 static void
242 finalize (GtkObject *object)
243 {
244         CamelFolder *camel_folder = CAMEL_FOLDER (object);
245         GList *m;
246
247         g_free (camel_folder->name);
248         g_free (camel_folder->full_name);
249
250         if (camel_folder->parent_store)
251                 gtk_object_unref (GTK_OBJECT (camel_folder->parent_store));
252         if (camel_folder->parent_folder)
253                 gtk_object_unref (GTK_OBJECT (camel_folder->parent_folder));
254
255         for (m = camel_folder->messages_changed; m; m = m->next)
256                 g_free (m->data);
257         g_list_free (camel_folder->messages_changed);
258
259         GTK_OBJECT_CLASS (parent_class)->finalize (object);
260 }
261
262
263 /**
264  * init: init the folder
265  * @folder: folder object to initialize
266  * @parent_store: parent store object of the folder
267  * @parent_folder: parent folder of the folder (may be NULL)
268  * @name: (short) name of the folder
269  * @separator: separator between the parent folder name and this name
270  * @ex: a CamelException
271  *
272  * Initalizes the folder by setting the parent store, parent folder,
273  * and name.
274  **/
275 static void
276 init (CamelFolder *folder, CamelStore *parent_store,
277       CamelFolder *parent_folder, const gchar *name,
278       gchar *separator, gboolean path_begins_with_sep,
279       CamelException *ex)
280 {
281         gchar *full_name;
282         const gchar *parent_full_name;
283
284         g_return_if_fail (CAMEL_IS_FOLDER (folder));
285         g_return_if_fail (CAMEL_IS_STORE (parent_store));
286         g_return_if_fail (parent_folder == NULL || CAMEL_IS_FOLDER (parent_folder));
287         g_return_if_fail (folder->parent_store == NULL);
288
289         folder->parent_store = parent_store;
290         gtk_object_ref (GTK_OBJECT (parent_store));
291
292         folder->parent_folder = parent_folder;
293         if (parent_folder)
294                 gtk_object_ref (GTK_OBJECT (parent_folder));
295
296         folder->separator = separator;
297         folder->path_begins_with_sep = path_begins_with_sep;
298
299         /* if the folder already has a name, free it */
300         g_free (folder->name);
301         g_free (folder->full_name);
302
303         /* set those fields to NULL now, so that if an
304            exception occurs, they will be set anyway */
305         folder->name = NULL;
306         folder->full_name = NULL;
307
308         if (folder->parent_folder) {
309                 parent_full_name =
310                         camel_folder_get_full_name (folder->parent_folder);
311
312                 full_name = g_strdup_printf ("%s%s%s", parent_full_name,
313                                              folder->separator, name);
314         } else {
315                 if (path_begins_with_sep)
316                         full_name = g_strdup_printf ("%s%s", folder->separator, name);
317                 else
318                         full_name = g_strdup (name);
319         }
320
321         folder->name = g_strdup (name);
322         folder->full_name = full_name;
323
324         folder->frozen = 0;
325         folder->folder_changed = FALSE;
326         folder->messages_changed = NULL;
327 }
328
329
330 static void
331 folder_sync (CamelFolder *folder, gboolean expunge, CamelException *ex)
332 {
333         g_warning ("CamelFolder::sync not implemented for `%s'",
334                    gtk_type_name (GTK_OBJECT_TYPE (folder)));
335 }
336
337 /**
338  * camel_folder_sync:
339  * @folder: The folder object
340  * @expunge: whether or not to expunge deleted messages
341  * @ex: exception object
342  *
343  * Sync changes made to a folder to its backing store, possibly expunging
344  * deleted messages as well.
345  **/
346 void
347 camel_folder_sync (CamelFolder *folder, gboolean expunge,
348                    CamelException *ex)
349 {
350         g_return_if_fail (CAMEL_IS_FOLDER (folder));
351
352         CF_CLASS (folder)->sync (folder, expunge, ex);
353 }
354
355
356 static const gchar *
357 get_name (CamelFolder *folder)
358 {
359         return folder->name;
360 }
361
362 /**
363  * camel_folder_get_name:
364  * @folder: a folder
365  *
366  * Get the (short) name of the folder. The fully qualified name
367  * can be obtained with the get_full_name method.
368  *
369  * Return value: name of the folder
370  **/
371 const gchar *
372 camel_folder_get_name (CamelFolder *folder)
373 {
374         g_return_val_if_fail (CAMEL_IS_FOLDER (folder), NULL);
375
376         return CF_CLASS (folder)->get_name (folder);
377 }
378
379
380 static const gchar *
381 get_full_name (CamelFolder *folder)
382 {
383         return folder->full_name;
384 }
385
386 /**
387  * camel_folder_get_full_name:
388  * @folder: a folder
389  *
390  * Get the (full) name of the folder.
391  *
392  * Return value: full name of the folder
393  **/
394 const gchar *
395 camel_folder_get_full_name (CamelFolder *folder)
396 {
397         g_return_val_if_fail (CAMEL_IS_FOLDER (folder), NULL);
398
399         return CF_CLASS (folder)->get_full_name (folder);
400 }
401
402
403 static gboolean
404 can_hold_folders (CamelFolder *folder)
405 {
406         return folder->can_hold_folders;
407 }
408
409 static gboolean
410 can_hold_messages (CamelFolder *folder)
411 {
412         return folder->can_hold_messages;
413 }
414
415
416 static CamelFolder *
417 get_subfolder (CamelFolder *folder, const gchar *folder_name,
418                gboolean create, CamelException *ex)
419 {
420         CamelFolder *new_folder;
421         gchar *full_name;
422         const gchar *current_folder_full_name;
423
424         g_return_val_if_fail (CAMEL_IS_STORE (folder->parent_store), NULL);
425
426         current_folder_full_name = camel_folder_get_full_name (folder);
427
428         full_name = g_strdup_printf ("%s%s%s", current_folder_full_name,
429                                      folder->separator, folder_name);
430         new_folder = camel_store_get_folder (folder->parent_store,
431                                              full_name, create, ex);
432         g_free (full_name);
433
434         return new_folder;
435 }
436
437 /**
438  * camel_folder_get_subfolder:
439  * @folder: a folder
440  * @folder_name: subfolder path
441  * @create: whether or not to create the folder if it doesn't exist
442  * @ex: a CamelException
443  *
444  * This method returns a folder object. This folder is a subfolder of
445  * the given folder. It is an error to ask for a folder whose name begins
446  * with the folder separator character.
447  *
448  * Return value: the requested folder, or %NULL if the subfolder object
449  * could not be obtained
450  **/
451 CamelFolder *
452 camel_folder_get_subfolder (CamelFolder *folder, const gchar *folder_name,
453                             gboolean create, CamelException *ex)
454 {
455         g_return_val_if_fail (CAMEL_IS_FOLDER (folder), NULL);
456         g_return_val_if_fail (folder_name != NULL, NULL);
457
458         return CF_CLASS (folder)->get_subfolder (folder, folder_name,
459                                                  create, ex);
460 }
461
462
463 static CamelFolder *
464 get_parent_folder (CamelFolder *folder, CamelException *ex)
465 {
466         return folder->parent_folder;
467 }
468
469 /**
470  * camel_folder_get_parent_folder:
471  * @folder: folder to get the parent of
472  * @ex: a CamelException
473  *
474  * Return value: the folder's parent
475  **/
476 CamelFolder *
477 camel_folder_get_parent_folder (CamelFolder *folder, CamelException *ex)
478 {
479         g_return_val_if_fail (CAMEL_IS_FOLDER (folder), NULL);
480
481         return CF_CLASS (folder)->get_parent_folder (folder, ex);
482 }
483
484
485 static CamelStore *
486 get_parent_store (CamelFolder *folder, CamelException *ex)
487 {
488         return folder->parent_store;
489 }
490
491 /**
492  * camel_folder_get_parent_store:
493  * @folder: folder to get the parent of
494  * @ex: a CamelException
495  *
496  * Return value: the parent store of the folder.
497  **/
498 CamelStore *
499 camel_folder_get_parent_store (CamelFolder *folder, CamelException *ex)
500 {
501         g_return_val_if_fail (CAMEL_IS_FOLDER (folder), NULL);
502
503         return CF_CLASS (folder)->get_parent_store (folder, ex);
504 }
505
506
507 static GPtrArray *
508 get_subfolder_names (CamelFolder *folder, CamelException *ex)
509 {
510         g_warning ("CamelFolder::get_subfolder_names not implemented for `%s'",
511                    gtk_type_name (GTK_OBJECT_TYPE (folder)));
512         return NULL;
513 }
514
515 /**
516  * camel_folder_get_subfolder_names:
517  * @folder: the folder
518  * @ex: a CamelException
519  *
520  * Return value: an array containing the names of the folder's
521  * subfolders. The array should not be modified and must be freed with
522  * camel_folder_free_subfolder_names().
523  **/
524 GPtrArray *
525 camel_folder_get_subfolder_names (CamelFolder *folder, CamelException *ex)
526 {
527         g_return_val_if_fail (CAMEL_IS_FOLDER (folder), NULL);
528
529         return CF_CLASS (folder)->get_subfolder_names (folder, ex);
530 }
531
532
533 /**
534  * camel_folder_free_subfolder_names:
535  * @folder: folder object
536  * @array: the array of subfolder names to free
537  *
538  * Frees the array of names returned by camel_folder_get_subfolder_names().
539  **/
540 void
541 camel_folder_free_subfolder_names (CamelFolder *folder, GPtrArray *array)
542 {
543         g_return_if_fail (CAMEL_IS_FOLDER (folder));
544
545         CF_CLASS (folder)->free_subfolder_names (folder, array);
546 }
547
548
549 static void
550 expunge (CamelFolder *folder, CamelException *ex)
551 {
552         g_warning ("CamelFolder::expunge not implemented for `%s'",
553                    gtk_type_name (GTK_OBJECT_TYPE (folder)));
554 }
555
556
557 /**
558  * camel_folder_expunge:
559  * @folder: the folder
560  * @ex: a CamelException
561  *
562  * Delete messages which have been marked as "DELETED"
563  **/
564 void
565 camel_folder_expunge (CamelFolder *folder, CamelException *ex)
566 {
567         g_return_if_fail (CAMEL_IS_FOLDER (folder));
568
569         return CF_CLASS (folder)->expunge (folder, ex);
570 }
571
572
573 static gint
574 get_message_count (CamelFolder *folder, CamelException *ex)
575 {
576         g_warning ("CamelFolder::get_message_count not implemented "
577                    "for `%s'", gtk_type_name (GTK_OBJECT_TYPE (folder)));
578         return -1;
579 }
580
581 /**
582  * camel_folder_get_message_count:
583  * @folder: A CamelFolder object
584  * @ex: a CamelException
585  *
586  * Return value: the number of messages in the folder, or -1 if unknown.
587  **/
588 gint
589 camel_folder_get_message_count (CamelFolder *folder, CamelException *ex)
590 {
591         g_return_val_if_fail (CAMEL_IS_FOLDER (folder), -1);
592
593         return CF_CLASS (folder)->get_message_count (folder, ex);
594 }
595
596
597 static void
598 append_message (CamelFolder *folder, CamelMimeMessage *message,
599                 CamelException *ex)
600 {
601         g_warning ("CamelFolder::append_message not implemented for `%s'",
602                    gtk_type_name (GTK_OBJECT_TYPE (folder)));
603         return;
604
605 }
606
607 /**
608  * camel_folder_append_message: add a message to a folder
609  * @folder: folder object to add the message to
610  * @message: message object
611  * @ex: exception object
612  *
613  * Add a message to a folder.
614  **/
615 void
616 camel_folder_append_message (CamelFolder *folder,
617                              CamelMimeMessage *message,
618                              CamelException *ex)
619 {
620         g_return_if_fail (CAMEL_IS_FOLDER (folder));
621
622         CF_CLASS (folder)->append_message (folder, message, ex);
623 }
624
625
626 static guint32
627 get_permanent_flags (CamelFolder *folder, CamelException *ex)
628 {
629         return folder->permanent_flags;
630 }
631
632 /**
633  * camel_folder_get_permanent_flags:
634  * @folder: a CamelFolder
635  * @ex: a CamelException
636  *
637  * Return value: the set of CamelMessageFlags that can be permanently
638  * stored on a message between sessions. If it includes %CAMEL_FLAG_USER,
639  * then user-defined flags will be remembered.
640  **/
641 guint32
642 camel_folder_get_permanent_flags (CamelFolder *folder, CamelException *ex)
643 {
644         g_return_val_if_fail (CAMEL_IS_FOLDER (folder), 0);
645
646         return CF_CLASS (folder)->get_permanent_flags (folder, ex);
647 }
648
649
650 static guint32
651 get_message_flags (CamelFolder *folder, const char *uid, CamelException *ex)
652 {
653         g_warning ("CamelFolder::get_message_flags not implemented for `%s'",
654                    gtk_type_name (GTK_OBJECT_TYPE (folder)));
655         return 0;
656 }
657
658 /**
659  * camel_folder_get_message_flags:
660  * @folder: a CamelFolder
661  * @uid: the UID of a message in @folder
662  * @ex: a CamelException
663  *
664  * Return value: the CamelMessageFlags that are set on the indicated
665  * message.
666  **/
667 guint32
668 camel_folder_get_message_flags (CamelFolder *folder, const char *uid,
669                                 CamelException *ex)
670 {
671         g_return_val_if_fail (CAMEL_IS_FOLDER (folder), 0);
672
673         return CF_CLASS (folder)->get_message_flags (folder, uid, ex);
674 }
675
676
677 static void
678 set_message_flags (CamelFolder *folder, const char *uid,
679                      guint32 flags, guint32 set, CamelException *ex)
680 {
681         g_warning ("CamelFolder::set_message_flags not implemented for `%s'",
682                    gtk_type_name (GTK_OBJECT_TYPE (folder)));
683 }
684
685 /**
686  * camel_folder_set_message_flags:
687  * @folder: a CamelFolder
688  * @uid: the UID of a message in @folder
689  * @flags: a set of CamelMessageFlag values to set
690  * @set: the mask of values in @flags to use.
691  * @ex: a CamelException
692  *
693  * Sets those flags specified by @set to the values specified by @flags
694  * on the indicated message. (This may or may not persist after the
695  * folder or store is closed. See camel_folder_get_permanent_flags().)
696  **/
697 void
698 camel_folder_set_message_flags (CamelFolder *folder, const char *uid,
699                                 guint32 flags, guint32 set,
700                                 CamelException *ex)
701 {
702         g_return_if_fail (CAMEL_IS_FOLDER (folder));
703
704         CF_CLASS (folder)->set_message_flags (folder, uid, flags, set, ex);
705 }
706
707
708 static gboolean
709 get_message_user_flag (CamelFolder *folder, const char *uid,
710                        const char *name, CamelException *ex)
711 {
712         g_warning ("CamelFolder::get_message_user_flag not implemented "
713                    "for `%s'", gtk_type_name (GTK_OBJECT_TYPE (folder)));
714         return FALSE;
715 }
716
717 /**
718  * camel_folder_get_message_user_flag:
719  * @folder: a CamelFolder
720  * @uid: the UID of a message in @folder
721  * @name: the name of a user flag
722  * @ex: a CamelException
723  *
724  * Return value: whether or not the given user flag is set on the message.
725  **/
726 gboolean
727 camel_folder_get_message_user_flag (CamelFolder *folder, const char *uid,
728                                     const char *name, CamelException *ex)
729 {
730         g_return_val_if_fail (CAMEL_IS_FOLDER (folder), 0);
731
732         return CF_CLASS (folder)->get_message_user_flag (folder, uid,
733                                                          name, ex);
734 }
735
736
737 static void
738 set_message_user_flag (CamelFolder *folder, const char *uid,
739                        const char *name, gboolean value, CamelException *ex)
740 {
741         g_warning ("CamelFolder::set_message_user_flag not implemented "
742                    "for `%s'", gtk_type_name (GTK_OBJECT_TYPE (folder)));
743 }
744
745 /**
746  * camel_folder_set_message_user_flag:
747  * @folder: a CamelFolder
748  * @uid: the UID of a message in @folder
749  * @name: the name of the user flag to set
750  * @value: the value to set it to
751  * @ex: a CamelException
752  *
753  * Sets the user flag specified by @name to the value specified by @value
754  * on the indicated message. (This may or may not persist after the
755  * folder or store is closed. See camel_folder_get_permanent_flags().)
756  **/
757 void
758 camel_folder_set_message_user_flag (CamelFolder *folder, const char *uid,
759                                     const char *name, gboolean value,
760                                     CamelException *ex)
761 {
762         g_return_if_fail (CAMEL_IS_FOLDER (folder));
763
764         CF_CLASS (folder)->set_message_user_flag (folder, uid, name,
765                                                   value, ex);
766 }
767
768
769 static const CamelMessageInfo *
770 get_message_info (CamelFolder *folder, const char *uid)
771 {
772         g_warning ("CamelFolder::get_message_info not implemented for `%s'",
773                    gtk_type_name (GTK_OBJECT_TYPE (folder)));
774         return NULL;
775 }
776
777 /**
778  * camel_folder_get_message_info:
779  * @folder: a CamelFolder
780  * @uid: the uid of a message
781  *
782  * Return value: the summary information for the indicated message
783  **/
784 const CamelMessageInfo *
785 camel_folder_get_message_info (CamelFolder *folder, const char *uid)
786 {
787         g_return_val_if_fail (CAMEL_IS_FOLDER (folder), NULL);
788         g_return_val_if_fail (uid != NULL, NULL);
789
790         return CF_CLASS (folder)->get_message_info (folder, uid);
791 }
792
793
794 /* TODO: is this function required anyway? */
795 gboolean
796 camel_folder_has_summary_capability (CamelFolder *folder)
797 {
798         g_return_val_if_fail (CAMEL_IS_FOLDER (folder), FALSE);
799
800         return folder->has_summary_capability;
801 }
802
803
804 /* UIDs stuff */
805
806 static const gchar *
807 get_message_uid (CamelFolder *folder, CamelMimeMessage *message,
808                  CamelException *ex)
809 {
810         g_warning ("CamelFolder::get_message_uid not implemented for `%s'",
811                    gtk_type_name (GTK_OBJECT_TYPE (folder)));
812         return NULL;
813 }
814
815 /**
816  * camel_folder_get_message_uid:
817  * @folder: Folder in which the UID must refer to
818  * @message: Message object
819  * @ex: a CamelException
820  *
821  * Return the UID of a message relatively to a folder.
822  * A message can have different UID, each one corresponding
823  * to a different folder, if the message is referenced in
824  * several folders.
825  *
826  * Return value: The UID of the message in the folder
827  **/
828 const gchar *
829 camel_folder_get_message_uid (CamelFolder *folder, CamelMimeMessage *message,
830                               CamelException *ex)
831 {
832         g_return_val_if_fail (CAMEL_IS_FOLDER (folder), NULL);
833         g_return_val_if_fail (CAMEL_IS_MIME_MESSAGE (message), NULL);
834
835         return CF_CLASS (folder)->get_message_uid (folder, message, ex);
836 }
837
838
839 static CamelMimeMessage *
840 get_message (CamelFolder *folder, const gchar *uid, CamelException *ex)
841 {
842         g_warning ("CamelFolder::get_message not implemented for `%s'",
843                    gtk_type_name (GTK_OBJECT_TYPE (folder)));
844         return NULL;
845 }
846
847 /**
848  * camel_folder_get_message:
849  * @folder: the folder object
850  * @uid: the UID
851  * @ex: a CamelException
852  *
853  * Get a message from its UID in the folder. Messages are cached
854  * within a folder, that is, asking twice for the same UID returns the
855  * same message object. (FIXME: is this true?)
856  *
857  * Return value: Message corresponding to the UID
858  **/
859 CamelMimeMessage *
860 camel_folder_get_message (CamelFolder *folder, const gchar *uid,
861                           CamelException *ex)
862 {
863         g_return_val_if_fail (CAMEL_IS_FOLDER (folder), NULL);
864
865         return CF_CLASS (folder)->get_message (folder, uid, ex);
866 }
867
868
869 static void
870 delete_message (CamelFolder *folder, const gchar *uid,
871                 CamelException *ex)
872 {
873         g_warning ("CamelFolder::delete_message not implemented "
874                    "for `%s'", gtk_type_name (GTK_OBJECT_TYPE (folder)));
875 }
876
877 /**
878  * camel_folder_delete_message:
879  * @folder: the folder object
880  * @uid: the UID
881  * @ex: a CamelException
882  *
883  * Delete a message from a folder given its UID.
884  **/
885 void
886 camel_folder_delete_message (CamelFolder *folder, const gchar *uid,
887                              CamelException *ex)
888 {
889         g_return_if_fail (CAMEL_IS_FOLDER (folder));
890
891         return CF_CLASS (folder)->delete_message (folder, uid, ex);
892 }
893
894
895 static GPtrArray *
896 get_uids (CamelFolder *folder, CamelException *ex)
897 {
898         g_warning ("CamelFolder::get_uids not implemented for `%s'",
899                    gtk_type_name (GTK_OBJECT_TYPE (folder)));
900         return NULL;
901 }
902
903 /**
904  * camel_folder_get_uids:
905  * @folder: folder object
906  * @ex: a CamelException
907  *
908  * Get the list of UIDs available in a folder. This routine is useful
909  * for finding what messages are available when the folder does not
910  * support summaries. The returned array shoudl not be modified, and
911  * must be freed by passing it to camel_folder_free_uids().
912  *
913  * Return value: GPtrArray of UIDs corresponding to the messages
914  * available in the folder.
915  **/
916 GPtrArray *
917 camel_folder_get_uids (CamelFolder *folder, CamelException *ex)
918 {
919         g_return_val_if_fail (CAMEL_IS_FOLDER (folder), NULL);
920
921         return CF_CLASS (folder)->get_uids (folder, ex);
922 }
923
924
925 /* This is also the default implementation of free_subfolder_names. */
926 static void
927 free_uids (CamelFolder *folder, GPtrArray *array)
928 {
929         int i;
930
931         /* Default implementation: free all of the strings and
932          * the array itself.
933          */
934         for (i = 0; i < array->len; i++)
935                 g_free (array->pdata[i]);
936         g_ptr_array_free (array, TRUE);
937 }
938
939 /**
940  * camel_folder_free_uids:
941  * @folder: folder object
942  * @array: the array of uids to free
943  *
944  * Frees the array of UIDs returned by camel_folder_get_uids().
945  **/
946 void
947 camel_folder_free_uids (CamelFolder *folder, GPtrArray *array)
948 {
949         g_return_if_fail (CAMEL_IS_FOLDER (folder));
950
951         CF_CLASS (folder)->free_uids (folder, array);
952 }
953
954
955 static GPtrArray *
956 get_summary (CamelFolder *folder, CamelException *ex)
957 {
958         g_warning ("CamelFolder::get_summary not implemented for `%s'",
959                    gtk_type_name (GTK_OBJECT_TYPE (folder)));
960         return NULL;
961 }
962
963 /**
964  * camel_folder_get_summary:
965  * @folder: a folder object
966  * @ex: a CamelException
967  *
968  * This returns the summary information for the folder. This array
969  * should not be modified, and must be freed with
970  * camel_folder_free_summary().
971  *
972  * Return value: an array of CamelMessageInfo
973  **/
974 GPtrArray *
975 camel_folder_get_summary (CamelFolder *folder, CamelException *ex)
976 {
977         g_return_val_if_fail (CAMEL_IS_FOLDER (folder), NULL);
978
979         return CF_CLASS (folder)->get_summary (folder, ex);
980 }
981
982
983 static void
984 free_summary (CamelFolder *folder, GPtrArray *array)
985 {
986         g_warning ("CamelFolder::free_summary not implemented for `%s'",
987                    gtk_type_name (GTK_OBJECT_TYPE (folder)));
988 }
989
990 /**
991  * camel_folder_free_summary:
992  * @folder: folder object
993  * @array: the summary array to free
994  *
995  * Frees the summary array returned by camel_folder_get_summary().
996  **/
997 void
998 camel_folder_free_summary (CamelFolder *folder, GPtrArray *array)
999 {
1000         g_return_if_fail (CAMEL_IS_FOLDER (folder));
1001
1002         CF_CLASS (folder)->free_summary (folder, array);
1003 }
1004
1005
1006 /**
1007  * camel_folder_has_search_capability:
1008  * @folder: Folder object
1009  *
1010  * Checks if a folder supports searching.
1011  *
1012  * Return value: %TRUE if the folder supports searching
1013  **/
1014 gboolean
1015 camel_folder_has_search_capability (CamelFolder *folder)
1016 {
1017         g_return_val_if_fail (CAMEL_IS_FOLDER (folder), FALSE);
1018
1019         return folder->has_search_capability;
1020 }
1021
1022 static GPtrArray *
1023 search_by_expression (CamelFolder *folder, const char *expression,
1024                       CamelException *ex)
1025 {
1026         g_warning ("CamelFolder::search_by_expression not implemented for "
1027                    "`%s'", gtk_type_name (GTK_OBJECT_TYPE (folder)));
1028         return NULL;
1029 }
1030
1031 /**
1032  * camel_folder_search_by_expression:
1033  * @folder: Folder object
1034  * @expression: a search expression
1035  * @ex: a CamelException
1036  *
1037  * Searches the folder for messages matching the given search expression.
1038  *
1039  * Return value: a list of uids of matching messages. The caller must
1040  * free the list and each of the elements when it is done.
1041  **/
1042 GPtrArray *
1043 camel_folder_search_by_expression (CamelFolder *folder, const char *expression,
1044                                    CamelException *ex)
1045 {
1046         g_return_val_if_fail (CAMEL_IS_FOLDER (folder), NULL);
1047         g_return_val_if_fail (folder->has_search_capability, NULL);
1048
1049         return CF_CLASS (folder)->search_by_expression (folder, expression, ex);
1050 }
1051
1052
1053 static void
1054 copy_message_to (CamelFolder *source, const char *uid, CamelFolder *dest,
1055                  CamelException *ex)
1056 {
1057         CamelMimeMessage *msg;
1058
1059         /* Default implementation. */
1060         
1061         msg = camel_folder_get_message (source, uid, ex);
1062         if (!msg)
1063                 return;
1064         camel_folder_append_message (dest, msg, ex);
1065         gtk_object_unref (GTK_OBJECT (msg));
1066         if (camel_exception_is_set (ex))
1067                 return;
1068 }
1069
1070 /**
1071  * camel_folder_copy_message_to:
1072  * @source: source folder
1073  * @uid: UID of message in @source
1074  * @dest: destination folder
1075  * @ex: a CamelException
1076  *
1077  * This copies a message from one folder to another. If the @source and
1078  * @dest folders have the same parent_store, this may be more efficient
1079  * than a camel_folder_append_message().
1080  **/
1081 void
1082 camel_folder_copy_message_to (CamelFolder *source, const char *uid,
1083                               CamelFolder *dest, CamelException *ex)
1084 {
1085         g_return_if_fail (CAMEL_IS_FOLDER (source));
1086         g_return_if_fail (CAMEL_IS_FOLDER (dest));
1087         g_return_if_fail (uid != NULL);
1088
1089         if (source->parent_store == dest->parent_store) {
1090                 return CF_CLASS (source)->copy_message_to (source, uid,
1091                                                            dest, ex);
1092         } else
1093                 return copy_message_to (source, uid, dest, ex);
1094 }
1095
1096
1097 static void
1098 move_message_to (CamelFolder *source, const char *uid, CamelFolder *dest,
1099                  CamelException *ex)
1100 {
1101         CamelMimeMessage *msg;
1102
1103         /* Default implementation. */
1104         
1105         msg = camel_folder_get_message (source, uid, ex);
1106         if (!msg)
1107                 return;
1108         camel_folder_append_message (dest, msg, ex);
1109         gtk_object_unref (GTK_OBJECT (msg));
1110         if (camel_exception_is_set (ex))
1111                 return;
1112         camel_folder_delete_message (source, uid, ex);
1113 }
1114
1115 /**
1116  * camel_folder_move_message_to:
1117  * @source: source folder
1118  * @uid: UID of message in @source
1119  * @dest: destination folder
1120  * @ex: a CamelException
1121  *
1122  * This moves a message from one folder to another. If the @source and
1123  * @dest folders have the same parent_store, this may be more efficient
1124  * than a camel_folder_append_message() followed by
1125  * camel_folder_delete_message().
1126  **/
1127 void
1128 camel_folder_move_message_to (CamelFolder *source, const char *uid,
1129                               CamelFolder *dest, CamelException *ex)
1130 {
1131         g_return_if_fail (CAMEL_IS_FOLDER (source));
1132         g_return_if_fail (CAMEL_IS_FOLDER (dest));
1133         g_return_if_fail (uid != NULL);
1134
1135         if (source->parent_store == dest->parent_store) {
1136                 return CF_CLASS (source)->move_message_to (source, uid,
1137                                                            dest, ex);
1138         } else
1139                 return move_message_to (source, uid, dest, ex);
1140 }
1141
1142
1143 static void
1144 freeze (CamelFolder *folder)
1145 {
1146         folder->frozen++;
1147 }
1148
1149 /**
1150  * camel_folder_freeze:
1151  * @folder: a folder
1152  *
1153  * Freezes the folder so that a series of operation can be performed
1154  * without "message_changed" and "folder_changed" signals being emitted.
1155  * When the folder is later thawed with camel_folder_thaw(), the
1156  * suppressed signals will be emitted.
1157  **/
1158 void
1159 camel_folder_freeze (CamelFolder *folder)
1160 {
1161         g_return_if_fail (CAMEL_IS_FOLDER (folder));
1162
1163         CF_CLASS (folder)->freeze (folder);
1164 }
1165
1166
1167 static void
1168 thaw (CamelFolder *folder)
1169 {
1170         GList *messages, *m;
1171
1172         folder->frozen--;
1173         if (folder->frozen != 0)
1174                 return;
1175
1176         /* Clear messages_changed now in case the signal handler ends
1177          * up calling freeze and thaw itself.
1178          */
1179         messages = folder->messages_changed;
1180         folder->messages_changed = NULL;
1181
1182         /* If the folder changed, emit that and ignore the individual
1183          * messages (since the UIDs may no longer be valid).
1184          */
1185         if (folder->folder_changed) {
1186                 folder->folder_changed = FALSE;
1187
1188                 gtk_signal_emit (GTK_OBJECT (folder),
1189                                  signals[FOLDER_CHANGED], 0);
1190         } else if (folder->messages_changed) {
1191                 /* FIXME: would be nice to not emit more than once for
1192                  * a given message
1193                  */
1194                 for (m = messages; m; m = m->next) {
1195                         gtk_signal_emit_by_name (GTK_OBJECT (folder),
1196                                                  "message_changed", m->data);
1197                         g_free (m->data);
1198                 }
1199                 g_list_free (messages);
1200                 return;
1201         }
1202
1203         if (messages) {
1204                 for (m = messages; m; m = m->next)
1205                         g_free (m->data);
1206                 g_list_free (messages);
1207         }
1208 }
1209
1210 /**
1211  * camel_folder_thaw:
1212  * @folder: a folder
1213  *
1214  * Thaws the folder and emits any pending folder_changed or
1215  * message_changed signals.
1216  **/
1217 void
1218 camel_folder_thaw (CamelFolder *folder)
1219 {
1220         g_return_if_fail (CAMEL_IS_FOLDER (folder));
1221         g_return_if_fail (folder->frozen != 0);
1222
1223         CF_CLASS (folder)->thaw (folder);
1224 }
1225
1226
1227 /* Default signal implementations, which block emission when we're
1228  * frozen.
1229  */
1230 static void
1231 folder_changed (CamelFolder *folder, int type)
1232 {
1233         if (folder->frozen) {
1234                 gtk_signal_emit_stop (GTK_OBJECT (folder),
1235                                       signals[FOLDER_CHANGED]);
1236                 folder->folder_changed = TRUE;
1237         }
1238 }
1239
1240 static void
1241 message_changed (CamelFolder *folder, const char *uid)
1242 {
1243         if (folder->frozen) {
1244                 gtk_signal_emit_stop (GTK_OBJECT (folder),
1245                                       signals[MESSAGE_CHANGED]);
1246
1247                 /* Only record the UID if it will be useful later. */
1248                 if (!folder->folder_changed &&
1249                     gtk_signal_handler_pending (GTK_OBJECT (folder),
1250                                                 signals[MESSAGE_CHANGED],
1251                                                 FALSE)) {
1252                         folder->messages_changed =
1253                                 g_list_prepend (folder->messages_changed,
1254                                                 g_strdup (uid));
1255                 }
1256         }
1257 }