Remove camel_folder_get_message_uid, which was not used, and not
[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 void      free_subfolder_names (CamelFolder *folder,
81                                        GPtrArray *array);
82 static CamelFolder *get_subfolder     (CamelFolder *folder,
83                                        const gchar *folder_name,
84                                        gboolean create,
85                                        CamelException *ex);
86 static CamelFolder *get_parent_folder (CamelFolder *folder,
87                                        CamelException *ex);
88 static CamelStore *get_parent_store   (CamelFolder *folder,
89                                        CamelException *ex);
90
91
92 static gint get_message_count (CamelFolder *folder, CamelException *ex);
93
94 static gint get_unread_message_count (CamelFolder *folder, CamelException *ex);
95
96 static void expunge             (CamelFolder *folder,
97                                  CamelException *ex);
98
99
100 static void append_message (CamelFolder *folder, CamelMimeMessage *message,
101                             guint32 flags, CamelException *ex);
102
103
104 static GPtrArray        *get_uids            (CamelFolder *folder,
105                                               CamelException *ex);
106 static void              free_uids           (CamelFolder *folder,
107                                               GPtrArray *array);
108 static GPtrArray        *get_summary         (CamelFolder *folder,
109                                               CamelException *ex);
110 static void              free_summary        (CamelFolder *folder,
111                                               GPtrArray *array);
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_subfolder_names;
165         camel_folder_class->expunge = expunge;
166         camel_folder_class->get_message_count = get_message_count;
167         camel_folder_class->get_unread_message_count = get_unread_message_count;
168         camel_folder_class->append_message = append_message;
169         camel_folder_class->get_permanent_flags = get_permanent_flags;
170         camel_folder_class->get_message_flags = get_message_flags;
171         camel_folder_class->set_message_flags = set_message_flags;
172         camel_folder_class->get_message_user_flag = get_message_user_flag;
173         camel_folder_class->set_message_user_flag = set_message_user_flag;
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 static void
534 free_subfolder_names (CamelFolder *folder, GPtrArray *array)
535 {
536         g_warning ("CamelFolder::free_subfolder_names not implemented "
537                    "for `%s'", gtk_type_name (GTK_OBJECT_TYPE (folder)));
538 }
539
540 /**
541  * camel_folder_free_subfolder_names:
542  * @folder: folder object
543  * @array: the array of subfolder names to free
544  *
545  * Frees the array of names returned by camel_folder_get_subfolder_names().
546  **/
547 void
548 camel_folder_free_subfolder_names (CamelFolder *folder, GPtrArray *array)
549 {
550         g_return_if_fail (CAMEL_IS_FOLDER (folder));
551
552         CF_CLASS (folder)->free_subfolder_names (folder, array);
553 }
554
555
556 static void
557 expunge (CamelFolder *folder, CamelException *ex)
558 {
559         g_warning ("CamelFolder::expunge not implemented for `%s'",
560                    gtk_type_name (GTK_OBJECT_TYPE (folder)));
561 }
562
563
564 /**
565  * camel_folder_expunge:
566  * @folder: the folder
567  * @ex: a CamelException
568  *
569  * Delete messages which have been marked as "DELETED"
570  **/
571 void
572 camel_folder_expunge (CamelFolder *folder, CamelException *ex)
573 {
574         g_return_if_fail (CAMEL_IS_FOLDER (folder));
575
576         CF_CLASS (folder)->expunge (folder, ex);
577 }
578
579
580 static gint
581 get_message_count (CamelFolder *folder, CamelException *ex)
582 {
583         g_warning ("CamelFolder::get_message_count not implemented "
584                    "for `%s'", gtk_type_name (GTK_OBJECT_TYPE (folder)));
585         return -1;
586 }
587
588 /**
589  * camel_folder_get_message_count:
590  * @folder: A CamelFolder object
591  * @ex: a CamelException
592  *
593  * Return value: the number of messages in the folder, or -1 if unknown.
594  **/
595 gint
596 camel_folder_get_message_count (CamelFolder *folder, CamelException *ex)
597 {
598         g_return_val_if_fail (CAMEL_IS_FOLDER (folder), -1);
599
600         return CF_CLASS (folder)->get_message_count (folder, ex);
601 }
602
603 static gint
604 get_unread_message_count (CamelFolder *folder, CamelException *ex)
605 {
606         g_warning ("CamelFolder::get_unread_message_count not implemented "
607                    "for `%s'", gtk_type_name (GTK_OBJECT_TYPE (folder)));
608         return -1;
609 }
610
611 /**
612  * camel_folder_unread_get_message_count:
613  * @folder: A CamelFolder object
614  * @ex: a CamelException
615  *
616  * Return value: the number of unread messages in the folder, or -1 if unknown.
617  **/
618 gint
619 camel_folder_get_unread_message_count (CamelFolder *folder, CamelException *ex)
620 {
621         g_return_val_if_fail (CAMEL_IS_FOLDER (folder), -1);
622
623         return CF_CLASS (folder)->get_message_count (folder, ex);
624 }
625
626
627 static void
628 append_message (CamelFolder *folder, CamelMimeMessage *message,
629                 guint32 flags, CamelException *ex)
630 {
631         g_warning ("CamelFolder::append_message not implemented for `%s'",
632                    gtk_type_name (GTK_OBJECT_TYPE (folder)));
633         return;
634
635 }
636
637 /**
638  * camel_folder_append_message: add a message to a folder
639  * @folder: folder object to add the message to
640  * @message: message object
641  * @ex: exception object
642  *
643  * Add a message to a folder.
644  **/
645 void
646 camel_folder_append_message (CamelFolder *folder,
647                              CamelMimeMessage *message,
648                              guint32 flags,
649                              CamelException *ex)
650 {
651         g_return_if_fail (CAMEL_IS_FOLDER (folder));
652
653         CF_CLASS (folder)->append_message (folder, message, flags, ex);
654 }
655
656
657 static guint32
658 get_permanent_flags (CamelFolder *folder, CamelException *ex)
659 {
660         return folder->permanent_flags;
661 }
662
663 /**
664  * camel_folder_get_permanent_flags:
665  * @folder: a CamelFolder
666  * @ex: a CamelException
667  *
668  * Return value: the set of CamelMessageFlags that can be permanently
669  * stored on a message between sessions. If it includes %CAMEL_FLAG_USER,
670  * then user-defined flags will be remembered.
671  **/
672 guint32
673 camel_folder_get_permanent_flags (CamelFolder *folder, CamelException *ex)
674 {
675         g_return_val_if_fail (CAMEL_IS_FOLDER (folder), 0);
676
677         return CF_CLASS (folder)->get_permanent_flags (folder, ex);
678 }
679
680
681 static guint32
682 get_message_flags (CamelFolder *folder, const char *uid, CamelException *ex)
683 {
684         g_warning ("CamelFolder::get_message_flags not implemented for `%s'",
685                    gtk_type_name (GTK_OBJECT_TYPE (folder)));
686         return 0;
687 }
688
689 /**
690  * camel_folder_get_message_flags:
691  * @folder: a CamelFolder
692  * @uid: the UID of a message in @folder
693  * @ex: a CamelException
694  *
695  * Return value: the CamelMessageFlags that are set on the indicated
696  * message.
697  **/
698 guint32
699 camel_folder_get_message_flags (CamelFolder *folder, const char *uid,
700                                 CamelException *ex)
701 {
702         g_return_val_if_fail (CAMEL_IS_FOLDER (folder), 0);
703
704         return CF_CLASS (folder)->get_message_flags (folder, uid, ex);
705 }
706
707
708 static void
709 set_message_flags (CamelFolder *folder, const char *uid,
710                      guint32 flags, guint32 set, CamelException *ex)
711 {
712         g_warning ("CamelFolder::set_message_flags not implemented for `%s'",
713                    gtk_type_name (GTK_OBJECT_TYPE (folder)));
714 }
715
716 /**
717  * camel_folder_set_message_flags:
718  * @folder: a CamelFolder
719  * @uid: the UID of a message in @folder
720  * @flags: a set of CamelMessageFlag values to set
721  * @set: the mask of values in @flags to use.
722  * @ex: a CamelException
723  *
724  * Sets those flags specified by @set to the values specified by @flags
725  * on the indicated message. (This may or may not persist after the
726  * folder or store is closed. See camel_folder_get_permanent_flags().)
727  **/
728 void
729 camel_folder_set_message_flags (CamelFolder *folder, const char *uid,
730                                 guint32 flags, guint32 set,
731                                 CamelException *ex)
732 {
733         g_return_if_fail (CAMEL_IS_FOLDER (folder));
734
735         CF_CLASS (folder)->set_message_flags (folder, uid, flags, set, ex);
736 }
737
738
739 static gboolean
740 get_message_user_flag (CamelFolder *folder, const char *uid,
741                        const char *name, CamelException *ex)
742 {
743         g_warning ("CamelFolder::get_message_user_flag not implemented "
744                    "for `%s'", gtk_type_name (GTK_OBJECT_TYPE (folder)));
745         return FALSE;
746 }
747
748 /**
749  * camel_folder_get_message_user_flag:
750  * @folder: a CamelFolder
751  * @uid: the UID of a message in @folder
752  * @name: the name of a user flag
753  * @ex: a CamelException
754  *
755  * Return value: whether or not the given user flag is set on the message.
756  **/
757 gboolean
758 camel_folder_get_message_user_flag (CamelFolder *folder, const char *uid,
759                                     const char *name, CamelException *ex)
760 {
761         g_return_val_if_fail (CAMEL_IS_FOLDER (folder), 0);
762
763         return CF_CLASS (folder)->get_message_user_flag (folder, uid,
764                                                          name, ex);
765 }
766
767
768 static void
769 set_message_user_flag (CamelFolder *folder, const char *uid,
770                        const char *name, gboolean value, CamelException *ex)
771 {
772         g_warning ("CamelFolder::set_message_user_flag not implemented "
773                    "for `%s'", gtk_type_name (GTK_OBJECT_TYPE (folder)));
774 }
775
776 /**
777  * camel_folder_set_message_user_flag:
778  * @folder: a CamelFolder
779  * @uid: the UID of a message in @folder
780  * @name: the name of the user flag to set
781  * @value: the value to set it to
782  * @ex: a CamelException
783  *
784  * Sets the user flag specified by @name to the value specified by @value
785  * on the indicated message. (This may or may not persist after the
786  * folder or store is closed. See camel_folder_get_permanent_flags().)
787  **/
788 void
789 camel_folder_set_message_user_flag (CamelFolder *folder, const char *uid,
790                                     const char *name, gboolean value,
791                                     CamelException *ex)
792 {
793         g_return_if_fail (CAMEL_IS_FOLDER (folder));
794
795         CF_CLASS (folder)->set_message_user_flag (folder, uid, name,
796                                                   value, ex);
797 }
798
799
800 static const CamelMessageInfo *
801 get_message_info (CamelFolder *folder, const char *uid)
802 {
803         g_warning ("CamelFolder::get_message_info not implemented for `%s'",
804                    gtk_type_name (GTK_OBJECT_TYPE (folder)));
805         return NULL;
806 }
807
808 /**
809  * camel_folder_get_message_info:
810  * @folder: a CamelFolder
811  * @uid: the uid of a message
812  *
813  * Return value: the summary information for the indicated message
814  **/
815 const CamelMessageInfo *
816 camel_folder_get_message_info (CamelFolder *folder, const char *uid)
817 {
818         g_return_val_if_fail (CAMEL_IS_FOLDER (folder), NULL);
819         g_return_val_if_fail (uid != NULL, NULL);
820
821         return CF_CLASS (folder)->get_message_info (folder, uid);
822 }
823
824
825 /* TODO: is this function required anyway? */
826 gboolean
827 camel_folder_has_summary_capability (CamelFolder *folder)
828 {
829         g_return_val_if_fail (CAMEL_IS_FOLDER (folder), FALSE);
830
831         return folder->has_summary_capability;
832 }
833
834
835 /* UIDs stuff */
836
837 static CamelMimeMessage *
838 get_message (CamelFolder *folder, const gchar *uid, CamelException *ex)
839 {
840         g_warning ("CamelFolder::get_message not implemented for `%s'",
841                    gtk_type_name (GTK_OBJECT_TYPE (folder)));
842         return NULL;
843 }
844
845 /**
846  * camel_folder_get_message:
847  * @folder: the folder object
848  * @uid: the UID
849  * @ex: a CamelException
850  *
851  * Get a message from its UID in the folder. Messages are cached
852  * within a folder, that is, asking twice for the same UID returns the
853  * same message object. (FIXME: is this true?)
854  *
855  * Return value: Message corresponding to the UID
856  **/
857 CamelMimeMessage *
858 camel_folder_get_message (CamelFolder *folder, const gchar *uid,
859                           CamelException *ex)
860 {
861         g_return_val_if_fail (CAMEL_IS_FOLDER (folder), NULL);
862
863         return CF_CLASS (folder)->get_message (folder, uid, ex);
864 }
865
866
867 static void
868 delete_message (CamelFolder *folder, const gchar *uid,
869                 CamelException *ex)
870 {
871         g_warning ("CamelFolder::delete_message not implemented "
872                    "for `%s'", gtk_type_name (GTK_OBJECT_TYPE (folder)));
873 }
874
875 /**
876  * camel_folder_delete_message:
877  * @folder: the folder object
878  * @uid: the UID
879  * @ex: a CamelException
880  *
881  * Delete a message from a folder given its UID.
882  **/
883 void
884 camel_folder_delete_message (CamelFolder *folder, const gchar *uid,
885                              CamelException *ex)
886 {
887         g_return_if_fail (CAMEL_IS_FOLDER (folder));
888
889         return CF_CLASS (folder)->delete_message (folder, uid, ex);
890 }
891
892
893 static GPtrArray *
894 get_uids (CamelFolder *folder, CamelException *ex)
895 {
896         g_warning ("CamelFolder::get_uids not implemented for `%s'",
897                    gtk_type_name (GTK_OBJECT_TYPE (folder)));
898         return NULL;
899 }
900
901 /**
902  * camel_folder_get_uids:
903  * @folder: folder object
904  * @ex: a CamelException
905  *
906  * Get the list of UIDs available in a folder. This routine is useful
907  * for finding what messages are available when the folder does not
908  * support summaries. The returned array shoudl not be modified, and
909  * must be freed by passing it to camel_folder_free_uids().
910  *
911  * Return value: GPtrArray of UIDs corresponding to the messages
912  * available in the folder.
913  **/
914 GPtrArray *
915 camel_folder_get_uids (CamelFolder *folder, CamelException *ex)
916 {
917         g_return_val_if_fail (CAMEL_IS_FOLDER (folder), NULL);
918
919         return CF_CLASS (folder)->get_uids (folder, ex);
920 }
921
922
923 /* This is also the default implementation of free_subfolder_names. */
924 static void
925 free_uids (CamelFolder *folder, GPtrArray *array)
926 {
927         g_warning ("CamelFolder::free_uids not implemented for `%s'",
928                    gtk_type_name (GTK_OBJECT_TYPE (folder)));
929 }
930
931 /**
932  * camel_folder_free_uids:
933  * @folder: folder object
934  * @array: the array of uids to free
935  *
936  * Frees the array of UIDs returned by camel_folder_get_uids().
937  **/
938 void
939 camel_folder_free_uids (CamelFolder *folder, GPtrArray *array)
940 {
941         g_return_if_fail (CAMEL_IS_FOLDER (folder));
942
943         CF_CLASS (folder)->free_uids (folder, array);
944 }
945
946
947 static GPtrArray *
948 get_summary (CamelFolder *folder, CamelException *ex)
949 {
950         g_warning ("CamelFolder::get_summary not implemented for `%s'",
951                    gtk_type_name (GTK_OBJECT_TYPE (folder)));
952         return NULL;
953 }
954
955 /**
956  * camel_folder_get_summary:
957  * @folder: a folder object
958  * @ex: a CamelException
959  *
960  * This returns the summary information for the folder. This array
961  * should not be modified, and must be freed with
962  * camel_folder_free_summary().
963  *
964  * Return value: an array of CamelMessageInfo
965  **/
966 GPtrArray *
967 camel_folder_get_summary (CamelFolder *folder, CamelException *ex)
968 {
969         g_return_val_if_fail (CAMEL_IS_FOLDER (folder), NULL);
970
971         return CF_CLASS (folder)->get_summary (folder, ex);
972 }
973
974
975 static void
976 free_summary (CamelFolder *folder, GPtrArray *array)
977 {
978         g_warning ("CamelFolder::free_summary not implemented for `%s'",
979                    gtk_type_name (GTK_OBJECT_TYPE (folder)));
980 }
981
982 /**
983  * camel_folder_free_summary:
984  * @folder: folder object
985  * @array: the summary array to free
986  *
987  * Frees the summary array returned by camel_folder_get_summary().
988  **/
989 void
990 camel_folder_free_summary (CamelFolder *folder, GPtrArray *array)
991 {
992         g_return_if_fail (CAMEL_IS_FOLDER (folder));
993
994         CF_CLASS (folder)->free_summary (folder, array);
995 }
996
997
998 /**
999  * camel_folder_has_search_capability:
1000  * @folder: Folder object
1001  *
1002  * Checks if a folder supports searching.
1003  *
1004  * Return value: %TRUE if the folder supports searching
1005  **/
1006 gboolean
1007 camel_folder_has_search_capability (CamelFolder *folder)
1008 {
1009         g_return_val_if_fail (CAMEL_IS_FOLDER (folder), FALSE);
1010
1011         return folder->has_search_capability;
1012 }
1013
1014 static GPtrArray *
1015 search_by_expression (CamelFolder *folder, const char *expression,
1016                       CamelException *ex)
1017 {
1018         g_warning ("CamelFolder::search_by_expression not implemented for "
1019                    "`%s'", gtk_type_name (GTK_OBJECT_TYPE (folder)));
1020         return NULL;
1021 }
1022
1023 /**
1024  * camel_folder_search_by_expression:
1025  * @folder: Folder object
1026  * @expression: a search expression
1027  * @ex: a CamelException
1028  *
1029  * Searches the folder for messages matching the given search expression.
1030  *
1031  * Return value: a list of uids of matching messages. The caller must
1032  * free the list and each of the elements when it is done.
1033  **/
1034 GPtrArray *
1035 camel_folder_search_by_expression (CamelFolder *folder, const char *expression,
1036                                    CamelException *ex)
1037 {
1038         g_return_val_if_fail (CAMEL_IS_FOLDER (folder), NULL);
1039         g_return_val_if_fail (folder->has_search_capability, NULL);
1040
1041         return CF_CLASS (folder)->search_by_expression (folder, expression, ex);
1042 }
1043
1044
1045 static void
1046 copy_message_to (CamelFolder *source, const char *uid, CamelFolder *dest,
1047                  CamelException *ex)
1048 {
1049         CamelMimeMessage *msg;
1050         const CamelMessageInfo *info;
1051
1052         /* Default implementation. */
1053         
1054         msg = camel_folder_get_message (source, uid, ex);
1055         if (!msg)
1056                 return;
1057         info = camel_folder_get_message_info (source, uid);
1058         camel_folder_append_message (dest, msg, info ? info->flags : 0, ex);
1059         gtk_object_unref (GTK_OBJECT (msg));
1060         if (camel_exception_is_set (ex))
1061                 return;
1062 }
1063
1064 /**
1065  * camel_folder_copy_message_to:
1066  * @source: source folder
1067  * @uid: UID of message in @source
1068  * @dest: destination folder
1069  * @ex: a CamelException
1070  *
1071  * This copies a message from one folder to another. If the @source and
1072  * @dest folders have the same parent_store, this may be more efficient
1073  * than a camel_folder_append_message().
1074  **/
1075 void
1076 camel_folder_copy_message_to (CamelFolder *source, const char *uid,
1077                               CamelFolder *dest, CamelException *ex)
1078 {
1079         g_return_if_fail (CAMEL_IS_FOLDER (source));
1080         g_return_if_fail (CAMEL_IS_FOLDER (dest));
1081         g_return_if_fail (uid != NULL);
1082
1083         if (source->parent_store == dest->parent_store) {
1084                 return CF_CLASS (source)->copy_message_to (source, uid,
1085                                                            dest, ex);
1086         } else
1087                 return copy_message_to (source, uid, dest, ex);
1088 }
1089
1090
1091 static void
1092 move_message_to (CamelFolder *source, const char *uid, CamelFolder *dest,
1093                  CamelException *ex)
1094 {
1095         CamelMimeMessage *msg;
1096         const CamelMessageInfo *info;
1097
1098         /* Default implementation. */
1099         
1100         msg = camel_folder_get_message (source, uid, ex);
1101         if (!msg)
1102                 return;
1103         info = camel_folder_get_message_info (source, uid);
1104         camel_folder_append_message (dest, msg, info ? info->flags : 0, ex);
1105         gtk_object_unref (GTK_OBJECT (msg));
1106         if (camel_exception_is_set (ex))
1107                 return;
1108         camel_folder_delete_message (source, uid, ex);
1109 }
1110
1111 /**
1112  * camel_folder_move_message_to:
1113  * @source: source folder
1114  * @uid: UID of message in @source
1115  * @dest: destination folder
1116  * @ex: a CamelException
1117  *
1118  * This moves a message from one folder to another. If the @source and
1119  * @dest folders have the same parent_store, this may be more efficient
1120  * than a camel_folder_append_message() followed by
1121  * camel_folder_delete_message().
1122  **/
1123 void
1124 camel_folder_move_message_to (CamelFolder *source, const char *uid,
1125                               CamelFolder *dest, CamelException *ex)
1126 {
1127         g_return_if_fail (CAMEL_IS_FOLDER (source));
1128         g_return_if_fail (CAMEL_IS_FOLDER (dest));
1129         g_return_if_fail (uid != NULL);
1130
1131         if (source->parent_store == dest->parent_store) {
1132                 return CF_CLASS (source)->move_message_to (source, uid,
1133                                                            dest, ex);
1134         } else
1135                 return move_message_to (source, uid, dest, ex);
1136 }
1137
1138
1139 static void
1140 freeze (CamelFolder *folder)
1141 {
1142         folder->frozen++;
1143 }
1144
1145 /**
1146  * camel_folder_freeze:
1147  * @folder: a folder
1148  *
1149  * Freezes the folder so that a series of operation can be performed
1150  * without "message_changed" and "folder_changed" signals being emitted.
1151  * When the folder is later thawed with camel_folder_thaw(), the
1152  * suppressed signals will be emitted.
1153  **/
1154 void
1155 camel_folder_freeze (CamelFolder *folder)
1156 {
1157         g_return_if_fail (CAMEL_IS_FOLDER (folder));
1158
1159         CF_CLASS (folder)->freeze (folder);
1160 }
1161
1162
1163 static void
1164 thaw (CamelFolder *folder)
1165 {
1166         GList *messages, *m;
1167
1168         folder->frozen--;
1169         if (folder->frozen != 0)
1170                 return;
1171
1172         /* Clear messages_changed now in case the signal handler ends
1173          * up calling freeze and thaw itself.
1174          */
1175         messages = folder->messages_changed;
1176         folder->messages_changed = NULL;
1177
1178         /* If the folder changed, emit that and ignore the individual
1179          * messages (since the UIDs may no longer be valid).
1180          */
1181         if (folder->folder_changed) {
1182                 folder->folder_changed = FALSE;
1183
1184                 gtk_signal_emit (GTK_OBJECT (folder),
1185                                  signals[FOLDER_CHANGED], 0);
1186         } else if (folder->messages_changed) {
1187                 /* FIXME: would be nice to not emit more than once for
1188                  * a given message
1189                  */
1190                 for (m = messages; m; m = m->next) {
1191                         gtk_signal_emit_by_name (GTK_OBJECT (folder),
1192                                                  "message_changed", m->data);
1193                         g_free (m->data);
1194                 }
1195                 g_list_free (messages);
1196                 return;
1197         }
1198
1199         if (messages) {
1200                 for (m = messages; m; m = m->next)
1201                         g_free (m->data);
1202                 g_list_free (messages);
1203         }
1204 }
1205
1206 /**
1207  * camel_folder_thaw:
1208  * @folder: a folder
1209  *
1210  * Thaws the folder and emits any pending folder_changed or
1211  * message_changed signals.
1212  **/
1213 void
1214 camel_folder_thaw (CamelFolder *folder)
1215 {
1216         g_return_if_fail (CAMEL_IS_FOLDER (folder));
1217         g_return_if_fail (folder->frozen != 0);
1218
1219         CF_CLASS (folder)->thaw (folder);
1220 }
1221
1222
1223 /* Default signal implementations, which block emission when we're
1224  * frozen.
1225  */
1226 static void
1227 folder_changed (CamelFolder *folder, int type)
1228 {
1229         if (folder->frozen) {
1230                 gtk_signal_emit_stop (GTK_OBJECT (folder),
1231                                       signals[FOLDER_CHANGED]);
1232                 folder->folder_changed = TRUE;
1233         }
1234 }
1235
1236 static void
1237 message_changed (CamelFolder *folder, const char *uid)
1238 {
1239         if (folder->frozen) {
1240                 gtk_signal_emit_stop (GTK_OBJECT (folder),
1241                                       signals[MESSAGE_CHANGED]);
1242
1243                 /* Only record the UID if it will be useful later. */
1244                 if (!folder->folder_changed &&
1245                     gtk_signal_handler_pending (GTK_OBJECT (folder),
1246                                                 signals[MESSAGE_CHANGED],
1247                                                 FALSE)) {
1248                         folder->messages_changed =
1249                                 g_list_prepend (folder->messages_changed,
1250                                                 g_strdup (uid));
1251                 }
1252         }
1253 }
1254
1255
1256 /**
1257  * camel_folder_free_nop:
1258  * @folder: a folder
1259  * @array: an array of uids, subfolder names, or CamelMessageInfo
1260  *
1261  * "Frees" the provided array by doing nothing. Used by CamelFolder
1262  * subclasses as an implementation for free_uids, free_summary,
1263  * or free_subfolder_names when the returned array is "static"
1264  * information and should not be freed.
1265  **/
1266 void
1267 camel_folder_free_nop (CamelFolder *folder, GPtrArray *array)
1268 {
1269         ;
1270 }
1271
1272 /**
1273  * camel_folder_free_shallow:
1274  * @folder: a folder
1275  * @array: an array of uids, subfolder names, or CamelMessageInfo
1276  *
1277  * Frees the provided array but not its contents. Used by CamelFolder
1278  * subclasses as an implementation for free_uids, free_summary, or
1279  * free_subfolder_names when the returned array needs to be freed
1280  * but its contents come from "static" information.
1281  **/
1282 void
1283 camel_folder_free_shallow (CamelFolder *folder, GPtrArray *array)
1284 {
1285         g_ptr_array_free (array, TRUE);
1286 }
1287
1288 /**
1289  * camel_folder_free_deep:
1290  * @folder: a folder
1291  * @array: an array of uids or subfolder names
1292  *
1293  * Frees the provided array and its contents. Used by CamelFolder
1294  * subclasses as an implementation for free_uids or
1295  * free_subfolder_names (but NOT free_summary) when the provided
1296  * information was created explicitly by the corresponding get_ call.
1297  **/
1298 void
1299 camel_folder_free_deep (CamelFolder *folder, GPtrArray *array)
1300 {
1301         g_strfreev ((gchar **)array->pdata);
1302         g_ptr_array_free (array, FALSE);
1303 }