Rename camel_service_get_settings().
[platform/upstream/evolution-data-server.git] / camel / providers / nntp / camel-nntp-folder.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /* camel-nntp-folder.c : Class for a news folder
3  *
4  * Authors : Chris Toshok <toshok@ximian.com>
5  *           Michael Zucchi <notzed@ximian.com>
6  *
7  * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
8  *
9  * This program is free software; you can redistribute it and/or
10  * modify it under the terms of version 2 of the GNU Lesser General Public
11  * License as published by the Free Software Foundation.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
21  * USA
22  */
23
24 #ifdef HAVE_CONFIG_H
25 #include <config.h>
26 #endif
27
28 #include <stdlib.h>
29 #include <string.h>
30 #include <unistd.h>
31 #include <sys/types.h>
32
33 #include <glib/gi18n-lib.h>
34
35 #include "camel-nntp-folder.h"
36 #include "camel-nntp-private.h"
37 #include "camel-nntp-store.h"
38 #include "camel-nntp-summary.h"
39
40 /* The custom property ID is a CamelArg artifact.
41  * It still identifies the property in state files. */
42 enum {
43         PROP_0,
44         PROP_APPLY_FILTERS = 0x2501
45 };
46
47 #define CAMEL_NNTP_FOLDER_GET_PRIVATE(obj) \
48         (G_TYPE_INSTANCE_GET_PRIVATE \
49         ((obj), CAMEL_TYPE_NNTP_FOLDER, CamelNNTPFolderPrivate))
50
51 G_DEFINE_TYPE (CamelNNTPFolder, camel_nntp_folder, CAMEL_TYPE_DISCO_FOLDER)
52
53 static gboolean
54 nntp_folder_get_apply_filters (CamelNNTPFolder *folder)
55 {
56         g_return_val_if_fail (folder != NULL, FALSE);
57         g_return_val_if_fail (CAMEL_IS_NNTP_FOLDER (folder), FALSE);
58
59         return folder->priv->apply_filters;
60 }
61
62 static void
63 nntp_folder_set_apply_filters (CamelNNTPFolder *folder,
64                                gboolean apply_filters)
65 {
66         g_return_if_fail (folder != NULL);
67         g_return_if_fail (CAMEL_IS_NNTP_FOLDER (folder));
68
69         if ((folder->priv->apply_filters ? 1 : 0) == (apply_filters ? 1 : 0))
70                 return;
71
72         folder->priv->apply_filters = apply_filters;
73
74         g_object_notify (G_OBJECT (folder), "apply-filters");
75 }
76
77 static void
78 nntp_folder_set_property (GObject *object,
79                           guint property_id,
80                           const GValue *value,
81                           GParamSpec *pspec)
82 {
83         switch (property_id) {
84                 case PROP_APPLY_FILTERS:
85                         nntp_folder_set_apply_filters (
86                                 CAMEL_NNTP_FOLDER (object),
87                                 g_value_get_boolean (value));
88                         return;
89         }
90
91         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
92 }
93
94 static void
95 nntp_folder_get_property (GObject *object,
96                           guint property_id,
97                           GValue *value,
98                           GParamSpec *pspec)
99 {
100         switch (property_id) {
101                 case PROP_APPLY_FILTERS:
102                         g_value_set_boolean (
103                                 value, nntp_folder_get_apply_filters (
104                                 CAMEL_NNTP_FOLDER (object)));
105                         return;
106         }
107
108         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
109 }
110
111 static void
112 nntp_folder_dispose (GObject *object)
113 {
114         CamelStore *parent_store;
115         CamelNNTPFolder *nntp_folder = CAMEL_NNTP_FOLDER (object);
116
117         camel_folder_summary_save_to_db (
118                 CAMEL_FOLDER (nntp_folder)->summary, NULL);
119
120         parent_store = camel_folder_get_parent_store (CAMEL_FOLDER (nntp_folder));
121         if (parent_store) {
122                 camel_store_summary_disconnect_folder_summary (
123                         (CamelStoreSummary *) ((CamelNNTPStore *) parent_store)->summary,
124                         CAMEL_FOLDER (nntp_folder)->summary);
125         }
126
127         /* Chain up to parent's dispose() method. */
128         G_OBJECT_CLASS (camel_nntp_folder_parent_class)->dispose (object);
129 }
130
131 static void
132 nntp_folder_finalize (GObject *object)
133 {
134         CamelNNTPFolder *nntp_folder = CAMEL_NNTP_FOLDER (object);
135
136         g_mutex_free (nntp_folder->priv->search_lock);
137         g_mutex_free (nntp_folder->priv->cache_lock);
138
139         /* Chain up to parent's finalize() method. */
140         G_OBJECT_CLASS (camel_nntp_folder_parent_class)->finalize (object);
141 }
142
143 gboolean
144 camel_nntp_folder_selected (CamelNNTPFolder *nntp_folder,
145                             gchar *line,
146                             GCancellable *cancellable,
147                             GError **error)
148 {
149         CamelFolder *folder;
150         CamelStore *parent_store;
151         gboolean res;
152
153         folder = CAMEL_FOLDER (nntp_folder);
154         parent_store = camel_folder_get_parent_store (folder);
155
156         res = camel_nntp_summary_check (
157                 CAMEL_NNTP_SUMMARY (folder->summary),
158                 CAMEL_NNTP_STORE (parent_store),
159                 line, nntp_folder->changes,
160                 cancellable, error);
161
162         if (camel_folder_change_info_changed (nntp_folder->changes)) {
163                 CamelFolderChangeInfo *changes;
164
165                 changes = nntp_folder->changes;
166                 nntp_folder->changes = camel_folder_change_info_new ();
167
168                 camel_folder_changed (CAMEL_FOLDER (nntp_folder), changes);
169                 camel_folder_change_info_free (changes);
170         }
171
172         return res;
173 }
174
175 static gboolean
176 nntp_folder_refresh_info_online (CamelFolder *folder,
177                                  GCancellable *cancellable,
178                                  GError **error)
179 {
180         CamelStore *parent_store;
181         CamelNNTPStore *nntp_store;
182         CamelFolderChangeInfo *changes = NULL;
183         CamelNNTPFolder *nntp_folder;
184         gchar *line;
185         gboolean success;
186
187         parent_store = camel_folder_get_parent_store (folder);
188
189         nntp_folder = CAMEL_NNTP_FOLDER (folder);
190         nntp_store = CAMEL_NNTP_STORE (parent_store);
191
192         /* When invoked with no fmt, camel_nntp_command() just selects the folder
193          * and should return zero. */
194         success = !camel_nntp_command (
195                 nntp_store, cancellable, error, nntp_folder, &line, NULL);
196
197         if (camel_folder_change_info_changed (nntp_folder->changes)) {
198                 changes = nntp_folder->changes;
199                 nntp_folder->changes = camel_folder_change_info_new ();
200         }
201
202         if (changes) {
203                 camel_folder_changed (folder, changes);
204                 camel_folder_change_info_free (changes);
205         }
206
207         return success;
208 }
209
210 static void
211 unset_flagged_flag (const gchar *uid,
212                     CamelFolderSummary *summary)
213 {
214         CamelMessageInfo *info;
215
216         info = camel_folder_summary_get (summary, uid);
217         if (info) {
218                 CamelMessageInfoBase *base = (CamelMessageInfoBase *) info;
219
220                 if ((base->flags & CAMEL_MESSAGE_FOLDER_FLAGGED) != 0) {
221                         base->flags &= ~CAMEL_MESSAGE_FOLDER_FLAGGED;
222                         base->dirty = TRUE;
223                 }
224
225                 camel_message_info_free (info);
226         }
227 }
228
229 static gboolean
230 nntp_folder_sync (CamelFolder *folder,
231                   GError **error)
232 {
233         GPtrArray *changed;
234
235         changed = camel_folder_summary_get_changed (folder->summary);
236         if (changed) {
237                 g_ptr_array_foreach (changed, (GFunc) unset_flagged_flag, folder->summary);
238                 g_ptr_array_foreach (changed, (GFunc) camel_pstring_free, NULL);
239                 g_ptr_array_free (changed, TRUE);
240                 camel_folder_summary_touch (folder->summary);
241         }
242
243         return camel_folder_summary_save_to_db (folder->summary, error);
244 }
245
246 static gboolean
247 nntp_folder_sync_online (CamelFolder *folder,
248                          GError **error)
249 {
250         return nntp_folder_sync (folder, error);
251 }
252
253 static gboolean
254 nntp_folder_sync_offline (CamelFolder *folder,
255                           GError **error)
256 {
257         return nntp_folder_sync (folder, error);
258 }
259
260 static gchar *
261 nntp_get_filename (CamelFolder *folder,
262                    const gchar *uid,
263                    GError **error)
264 {
265         CamelStore *parent_store;
266         CamelNNTPStore *nntp_store;
267         gchar *article, *msgid;
268
269         parent_store = camel_folder_get_parent_store (folder);
270         nntp_store = CAMEL_NNTP_STORE (parent_store);
271
272         article = alloca (strlen (uid) + 1);
273         strcpy (article, uid);
274         msgid = strchr (article, ',');
275         if (msgid == NULL) {
276                 g_set_error (
277                         error, CAMEL_ERROR, CAMEL_ERROR_GENERIC,
278                         _("Internal error: UID in invalid format: %s"), uid);
279                 return NULL;
280         }
281         *msgid++ = 0;
282
283         return camel_data_cache_get_filename (nntp_store->cache, "cache", msgid);
284 }
285
286 static CamelStream *
287 nntp_folder_download_message (CamelNNTPFolder *nntp_folder,
288                               const gchar *id,
289                               const gchar *msgid,
290                               GCancellable *cancellable,
291                               GError **error)
292 {
293         CamelFolder *folder;
294         CamelStore *parent_store;
295         CamelNNTPStore *nntp_store;
296         CamelStream *stream = NULL;
297         gint ret;
298         gchar *line;
299
300         folder = CAMEL_FOLDER (nntp_folder);
301         parent_store = camel_folder_get_parent_store (folder);
302         nntp_store = CAMEL_NNTP_STORE (parent_store);
303
304         ret = camel_nntp_command (nntp_store, cancellable, error, nntp_folder, &line, "article %s", id);
305         if (ret == 220) {
306                 stream = camel_data_cache_add (nntp_store->cache, "cache", msgid, NULL);
307                 if (stream) {
308                         gboolean success;
309
310                         if (camel_stream_write_to_stream ((CamelStream *) nntp_store->stream, stream, cancellable, error) == -1)
311                                 goto fail;
312
313                         if ((error && *error) || g_cancellable_set_error_if_cancelled (cancellable, error))
314                                 goto fail;
315
316                         success = g_seekable_seek (
317                                 G_SEEKABLE (stream), 0,
318                                 G_SEEK_SET, cancellable, error);
319                         if (!success)
320                                 goto fail;
321                 } else {
322                         stream = g_object_ref (nntp_store->stream);
323                 }
324         } else if (ret == 423 || ret == 430) {
325                 g_set_error (
326                         error, CAMEL_FOLDER_ERROR,
327                         CAMEL_FOLDER_ERROR_INVALID_UID,
328                         _("Cannot get message %s: %s"), msgid, line);
329         } else if (ret != -1) {
330                 g_set_error (
331                         error, CAMEL_ERROR, CAMEL_ERROR_GENERIC,
332                         _("Cannot get message %s: %s"), msgid, line);
333         }
334
335         return stream;
336
337 fail:
338         camel_data_cache_remove (nntp_store->cache, "cache", msgid, NULL);
339         g_prefix_error (error, _("Cannot get message %s: "), msgid);
340
341         return NULL;
342 }
343
344 static gboolean
345 nntp_folder_cache_message (CamelDiscoFolder *disco_folder,
346                            const gchar *uid,
347                            GCancellable *cancellable,
348                            GError **error)
349 {
350         CamelStream *stream;
351         gchar *article, *msgid;
352         gboolean success = TRUE;
353
354         article = alloca (strlen (uid) + 1);
355         strcpy (article, uid);
356         msgid = strchr (article, ',');
357         if (!msgid) {
358                 g_set_error (
359                         error, CAMEL_ERROR, CAMEL_ERROR_GENERIC,
360                         _("Internal error: UID in invalid format: %s"), uid);
361                 return FALSE;
362         }
363         *msgid++ = 0;
364
365         stream = nntp_folder_download_message (
366                 (CamelNNTPFolder *) disco_folder, article, msgid, cancellable, error);
367         if (stream)
368                 g_object_unref (stream);
369         else
370                 success = FALSE;
371
372         return success;
373 }
374
375 static GPtrArray *
376 nntp_folder_search_by_expression (CamelFolder *folder,
377                                   const gchar *expression,
378                                   GCancellable *cancellable,
379                                   GError **error)
380 {
381         CamelNNTPFolder *nntp_folder = CAMEL_NNTP_FOLDER (folder);
382         GPtrArray *matches;
383
384         CAMEL_NNTP_FOLDER_LOCK (nntp_folder, search_lock);
385
386         if (nntp_folder->search == NULL)
387                 nntp_folder->search = camel_folder_search_new ();
388
389         camel_folder_search_set_folder (nntp_folder->search, folder);
390         matches = camel_folder_search_search (nntp_folder->search, expression, NULL, cancellable, error);
391
392         CAMEL_NNTP_FOLDER_UNLOCK (nntp_folder, search_lock);
393
394         return matches;
395 }
396
397 static guint32
398 nntp_folder_count_by_expression (CamelFolder *folder,
399                                  const gchar *expression,
400                                  GCancellable *cancellable,
401                                  GError **error)
402 {
403         CamelNNTPFolder *nntp_folder = CAMEL_NNTP_FOLDER (folder);
404         guint32 count;
405
406         CAMEL_NNTP_FOLDER_LOCK (nntp_folder, search_lock);
407
408         if (nntp_folder->search == NULL)
409                 nntp_folder->search = camel_folder_search_new ();
410
411         camel_folder_search_set_folder (nntp_folder->search, folder);
412         count = camel_folder_search_count (nntp_folder->search, expression, cancellable, error);
413
414         CAMEL_NNTP_FOLDER_UNLOCK (nntp_folder, search_lock);
415
416         return count;
417 }
418
419 static GPtrArray *
420 nntp_folder_search_by_uids (CamelFolder *folder,
421                             const gchar *expression,
422                             GPtrArray *uids,
423                             GCancellable *cancellable,
424                             GError **error)
425 {
426         CamelNNTPFolder *nntp_folder = (CamelNNTPFolder *) folder;
427         GPtrArray *matches;
428
429         if (uids->len == 0)
430                 return g_ptr_array_new ();
431
432         CAMEL_NNTP_FOLDER_LOCK (folder, search_lock);
433
434         if (nntp_folder->search == NULL)
435                 nntp_folder->search = camel_folder_search_new ();
436
437         camel_folder_search_set_folder (nntp_folder->search, folder);
438         matches = camel_folder_search_search (nntp_folder->search, expression, uids, cancellable, error);
439
440         CAMEL_NNTP_FOLDER_UNLOCK (folder, search_lock);
441
442         return matches;
443 }
444
445 static void
446 nntp_folder_search_free (CamelFolder *folder,
447                          GPtrArray *result)
448 {
449         CamelNNTPFolder *nntp_folder = CAMEL_NNTP_FOLDER (folder);
450
451         CAMEL_NNTP_FOLDER_LOCK (nntp_folder, search_lock);
452         camel_folder_search_free_result (nntp_folder->search, result);
453         CAMEL_NNTP_FOLDER_UNLOCK (nntp_folder, search_lock);
454 }
455
456 static CamelMimeMessage *
457 nntp_folder_get_message_sync (CamelFolder *folder,
458                               const gchar *uid,
459                               GCancellable *cancellable,
460                               GError **error)
461 {
462         CamelStore *parent_store;
463         CamelMimeMessage *message = NULL;
464         CamelNNTPStore *nntp_store;
465         CamelFolderChangeInfo *changes;
466         CamelNNTPFolder *nntp_folder;
467         CamelStream *stream = NULL;
468         gchar *article, *msgid;
469
470         parent_store = camel_folder_get_parent_store (folder);
471
472         nntp_folder = CAMEL_NNTP_FOLDER (folder);
473         nntp_store = CAMEL_NNTP_STORE (parent_store);
474
475         article = alloca (strlen (uid) + 1);
476         strcpy (article, uid);
477         msgid = strchr (article, ',');
478         if (msgid == NULL) {
479                 g_set_error (
480                         error, CAMEL_ERROR, CAMEL_ERROR_GENERIC,
481                         _("Internal error: UID in invalid format: %s"), uid);
482                 return NULL;
483         }
484         *msgid++ = 0;
485
486         /* Lookup in cache, NEWS is global messageid's so use a global cache path */
487         stream = camel_data_cache_get (nntp_store->cache, "cache", msgid, NULL);
488         if (stream == NULL) {
489                 if (camel_disco_store_status ((CamelDiscoStore *) nntp_store) == CAMEL_DISCO_STORE_OFFLINE) {
490                         g_set_error (
491                                 error, CAMEL_SERVICE_ERROR,
492                                 CAMEL_SERVICE_ERROR_UNAVAILABLE,
493                                 _("This message is not currently available"));
494                         goto fail;
495                 }
496
497                 stream = nntp_folder_download_message (nntp_folder, article, msgid, cancellable, error);
498                 if (stream == NULL)
499                         goto fail;
500         }
501
502         message = camel_mime_message_new ();
503         if (!camel_data_wrapper_construct_from_stream_sync ((CamelDataWrapper *) message, stream, cancellable, error)) {
504                 g_prefix_error (error, _("Cannot get message %s: "), uid);
505                 g_object_unref (message);
506                 message = NULL;
507         }
508
509         g_object_unref (stream);
510 fail:
511         if (camel_folder_change_info_changed (nntp_folder->changes)) {
512                 changes = nntp_folder->changes;
513                 nntp_folder->changes = camel_folder_change_info_new ();
514         } else {
515                 changes = NULL;
516         }
517
518         if (changes) {
519                 camel_folder_changed (folder, changes);
520                 camel_folder_change_info_free (changes);
521         }
522
523         return message;
524 }
525
526 static gboolean
527 nntp_folder_append_message_online (CamelFolder *folder,
528                                    CamelMimeMessage *mime_message,
529                                    const CamelMessageInfo *info,
530                                    gchar **appended_uid,
531                                    GCancellable *cancellable,
532                                    GError **error)
533 {
534         CamelStore *parent_store;
535         CamelNNTPStore *nntp_store;
536         CamelStream *filtered_stream;
537         CamelStream *stream;
538         CamelMimeFilter *crlffilter;
539         gint ret;
540         guint u;
541         struct _camel_header_raw *header, *savedhdrs, *n, *tail;
542         const gchar *full_name;
543         gchar *group, *line;
544         gboolean success = TRUE;
545
546         full_name = camel_folder_get_full_name (folder);
547         parent_store = camel_folder_get_parent_store (folder);
548
549         nntp_store = CAMEL_NNTP_STORE (parent_store);
550         stream = CAMEL_STREAM (nntp_store->stream);
551
552         /* send 'POST' command */
553         ret = camel_nntp_command (nntp_store, cancellable, error, NULL, &line, "post");
554         if (ret != 340) {
555                 if (ret == 440) {
556                         g_set_error (
557                                 error, CAMEL_FOLDER_ERROR,
558                                 CAMEL_FOLDER_ERROR_INSUFFICIENT_PERMISSION,
559                                 _("Posting failed: %s"), line);
560                         success = FALSE;
561                 } else if (ret != -1) {
562                         g_set_error (
563                                 error, CAMEL_ERROR,
564                                 CAMEL_ERROR_GENERIC,
565                                 _("Posting failed: %s"), line);
566                         success = FALSE;
567                 }
568                 return success;
569         }
570
571         /* the 'Newsgroups: ' header */
572         group = g_strdup_printf ("Newsgroups: %s\r\n", full_name);
573
574         /* setup stream filtering */
575         crlffilter = camel_mime_filter_crlf_new (CAMEL_MIME_FILTER_CRLF_ENCODE, CAMEL_MIME_FILTER_CRLF_MODE_CRLF_DOTS);
576         filtered_stream = camel_stream_filter_new (stream);
577         camel_stream_filter_add (
578                 CAMEL_STREAM_FILTER (filtered_stream), crlffilter);
579         g_object_unref (crlffilter);
580
581         /* remove mail 'To', 'CC', and 'BCC' headers */
582         savedhdrs = NULL;
583         tail = (struct _camel_header_raw *) &savedhdrs;
584
585         header = (struct _camel_header_raw *) &CAMEL_MIME_PART (mime_message)->headers;
586         n = header->next;
587         while (n != NULL) {
588                 if (!g_ascii_strcasecmp (n->name, "To") || !g_ascii_strcasecmp (n->name, "Cc") || !g_ascii_strcasecmp (n->name, "Bcc")) {
589                         header->next = n->next;
590                         tail->next = n;
591                         n->next = NULL;
592                         tail = n;
593                 } else {
594                         header = n;
595                 }
596
597                 n = header->next;
598         }
599
600         /* write the message */
601         if (camel_stream_write (stream, group, strlen (group), cancellable, error) == -1
602             || camel_data_wrapper_write_to_stream_sync (CAMEL_DATA_WRAPPER (mime_message), filtered_stream, cancellable, error) == -1
603             || camel_stream_flush (filtered_stream, cancellable, error) == -1
604             || camel_stream_write (stream, "\r\n.\r\n", 5, cancellable, error) == -1
605             || camel_nntp_stream_line (nntp_store->stream, (guchar **) &line, &u, cancellable, error) == -1) {
606                 g_prefix_error (error, _("Posting failed: "));
607                 success = FALSE;
608         } else if (atoi (line) != 240) {
609                 g_set_error (
610                         error, CAMEL_ERROR, CAMEL_ERROR_GENERIC,
611                         _("Posting failed: %s"), line);
612                 success = FALSE;
613         }
614
615         g_object_unref (filtered_stream);
616         g_free (group);
617         header->next = savedhdrs;
618
619         return success;
620 }
621
622 static gboolean
623 nntp_folder_append_message_offline (CamelFolder *folder,
624                                     CamelMimeMessage *mime_message,
625                                     const CamelMessageInfo *info,
626                                     gchar **appended_uid,
627                                     GCancellable *cancellable,
628                                     GError **error)
629 {
630         g_set_error (
631                 error, CAMEL_SERVICE_ERROR,
632                 CAMEL_SERVICE_ERROR_UNAVAILABLE,
633                 _("You cannot post NNTP messages while working offline!"));
634
635         return FALSE;
636 }
637
638 /* I do not know what to do this exactly. Looking at the IMAP implementation for this, it
639  * seems to assume the message is copied to a folder on the same store. In that case, an
640  * NNTP implementation doesn't seem to make any sense. */
641 static gboolean
642 nntp_folder_transfer_message (CamelFolder *source,
643                               GPtrArray *uids,
644                               CamelFolder *dest,
645                               GPtrArray **transferred_uids,
646                               gboolean delete_orig,
647                               GCancellable *cancellable,
648                               GError **error)
649 {
650         g_set_error (
651                 error, CAMEL_SERVICE_ERROR,
652                 CAMEL_SERVICE_ERROR_UNAVAILABLE,
653                 _("You cannot copy messages from a NNTP folder!"));
654
655         return FALSE;
656 }
657
658 static gboolean
659 nntp_folder_expunge_uids_offline (CamelFolder *folder,
660                                   GPtrArray *uids,
661                                   GError **error)
662 {
663         CamelFolderChangeInfo *changes;
664         gint ii;
665
666         g_return_val_if_fail (folder != NULL, FALSE);
667         g_return_val_if_fail (CAMEL_IS_NNTP_FOLDER (folder), FALSE);
668         g_return_val_if_fail (uids != NULL, FALSE);
669         g_return_val_if_fail (folder->summary != NULL, FALSE);
670
671         /* can only remove deleted messages from a local cache */
672
673         changes = camel_folder_change_info_new ();
674         for (ii = 0; ii < uids->len; ii++) {
675                 CamelMessageInfo *mi = camel_folder_summary_peek_loaded (folder->summary, uids->pdata[ii]);
676                 if (mi) {
677                         camel_folder_summary_remove (folder->summary, mi);
678                         camel_message_info_free (mi);
679                 } else {
680                         camel_folder_summary_remove_uid (folder->summary, uids->pdata[ii]);
681                 }
682
683                 camel_folder_change_info_remove_uid (changes, uids->pdata[ii]);
684         }
685
686         camel_folder_summary_save_to_db (folder->summary, NULL);
687         camel_folder_changed (folder, changes);
688         camel_folder_change_info_free (changes);
689
690         return TRUE;
691 }
692
693 static void
694 camel_nntp_folder_class_init (CamelNNTPFolderClass *class)
695 {
696         GObjectClass *object_class;
697         CamelFolderClass *folder_class;
698         CamelDiscoFolderClass *disco_folder_class;
699
700         g_type_class_add_private (class, sizeof (CamelNNTPFolderPrivate));
701
702         object_class = G_OBJECT_CLASS (class);
703         object_class->set_property = nntp_folder_set_property;
704         object_class->get_property = nntp_folder_get_property;
705         object_class->dispose = nntp_folder_dispose;
706         object_class->finalize = nntp_folder_finalize;
707
708         folder_class = CAMEL_FOLDER_CLASS (class);
709         folder_class->search_by_expression = nntp_folder_search_by_expression;
710         folder_class->count_by_expression = nntp_folder_count_by_expression;
711         folder_class->search_by_uids = nntp_folder_search_by_uids;
712         folder_class->search_free = nntp_folder_search_free;
713         folder_class->get_filename = nntp_get_filename;
714         folder_class->get_message_sync = nntp_folder_get_message_sync;
715
716         disco_folder_class = CAMEL_DISCO_FOLDER_CLASS (class);
717         disco_folder_class->sync_online = nntp_folder_sync_online;
718         disco_folder_class->sync_resyncing = nntp_folder_sync_offline;
719         disco_folder_class->sync_offline = nntp_folder_sync_offline;
720         disco_folder_class->cache_message = nntp_folder_cache_message;
721         disco_folder_class->append_online = nntp_folder_append_message_online;
722         disco_folder_class->append_resyncing = nntp_folder_append_message_online;
723         disco_folder_class->append_offline = nntp_folder_append_message_offline;
724         disco_folder_class->transfer_online = nntp_folder_transfer_message;
725         disco_folder_class->transfer_resyncing = nntp_folder_transfer_message;
726         disco_folder_class->transfer_offline = nntp_folder_transfer_message;
727         disco_folder_class->refresh_info_online = nntp_folder_refresh_info_online;
728         disco_folder_class->expunge_uids_online = nntp_folder_expunge_uids_offline;
729         disco_folder_class->expunge_uids_offline = nntp_folder_expunge_uids_offline;
730         disco_folder_class->expunge_uids_resyncing = nntp_folder_expunge_uids_offline;
731
732         g_object_class_install_property (
733                 object_class,
734                 PROP_APPLY_FILTERS,
735                 g_param_spec_boolean (
736                         "apply-filters",
737                         "Apply Filters",
738                         _("Apply message _filters to this folder"),
739                         FALSE,
740                         G_PARAM_READWRITE |
741                         CAMEL_PARAM_PERSISTENT));
742 }
743
744 static void
745 camel_nntp_folder_init (CamelNNTPFolder *nntp_folder)
746 {
747         nntp_folder->priv = CAMEL_NNTP_FOLDER_GET_PRIVATE (nntp_folder);
748
749         nntp_folder->changes = camel_folder_change_info_new ();
750         nntp_folder->priv->search_lock = g_mutex_new ();
751         nntp_folder->priv->cache_lock = g_mutex_new ();
752 }
753
754 CamelFolder *
755 camel_nntp_folder_new (CamelStore *parent,
756                        const gchar *folder_name,
757                        GCancellable *cancellable,
758                        GError **error)
759 {
760         CamelFolder *folder;
761         CamelNNTPFolder *nntp_folder;
762         gchar *root;
763         CamelService *service;
764         CamelSettings *settings;
765         CamelStoreInfo *si;
766         const gchar *user_cache_dir;
767         gboolean subscribed = TRUE;
768         gboolean filter_all;
769
770         service = CAMEL_SERVICE (parent);
771         user_cache_dir = camel_service_get_user_cache_dir (service);
772
773         settings = camel_service_ref_settings (service);
774
775         g_object_get (
776                 settings,
777                 "filter-all", &filter_all,
778                 NULL);
779
780         g_object_unref (settings);
781
782         folder = g_object_new (
783                 CAMEL_TYPE_NNTP_FOLDER,
784                 "display-name", folder_name,
785                 "full-name", folder_name,
786                 "parent-store", parent, NULL);
787         nntp_folder = (CamelNNTPFolder *) folder;
788
789         folder->folder_flags |= CAMEL_FOLDER_HAS_SUMMARY_CAPABILITY;
790
791         nntp_folder->storage_path =
792                 g_build_filename (user_cache_dir, folder_name, NULL);
793
794         root = g_strdup_printf ("%s.cmeta", nntp_folder->storage_path);
795         camel_object_set_state_filename (CAMEL_OBJECT (nntp_folder), root);
796         camel_object_state_read (CAMEL_OBJECT (nntp_folder));
797         g_free (root);
798
799         folder->summary = (CamelFolderSummary *) camel_nntp_summary_new (folder);
800
801         if (filter_all || nntp_folder_get_apply_filters (nntp_folder))
802                 folder->folder_flags |= CAMEL_FOLDER_FILTER_RECENT;
803
804         camel_folder_summary_load_from_db (folder->summary, NULL);
805
806         si = camel_store_summary_path ((CamelStoreSummary *) ((CamelNNTPStore *) parent)->summary, folder_name);
807         if (si) {
808                 subscribed = (si->flags & CAMEL_STORE_INFO_FOLDER_SUBSCRIBED) != 0;
809                 camel_store_summary_info_free ((CamelStoreSummary *) ((CamelNNTPStore *) parent)->summary, si);
810         }
811
812         camel_store_summary_connect_folder_summary (
813                 (CamelStoreSummary *) ((CamelNNTPStore *) parent)->summary,
814                 folder_name, folder->summary);
815
816         if (subscribed && !camel_folder_refresh_info_sync (
817                         folder, cancellable, error)) {
818                 g_object_unref (folder);
819                 folder = NULL;
820         }
821
822         return folder;
823 }