1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /* camel-pop3-folder.c : class for a pop3 folder */
6 * Dan Winship <danw@ximian.com>
7 * Michael Zucchi <notzed@ximian.com>
9 * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
11 * This program is free software; you can redistribute it and/or
12 * modify it under the terms of version 2 of the GNU Lesser General Public
13 * License as published by the Free Software Foundation.
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 Lesser General Public License for more details.
20 * You should have received a copy of the GNU Lesser General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
33 #include <glib/gi18n-lib.h>
35 #include "camel-pop3-folder.h"
36 #include "camel-pop3-store.h"
37 #include "camel-pop3-settings.h"
39 #define d(x) if (camel_debug("pop3")) x;
41 G_DEFINE_TYPE (CamelPOP3Folder, camel_pop3_folder, CAMEL_TYPE_FOLDER)
44 free_fi (CamelPOP3Folder *pop3_folder,CamelPOP3FolderInfo *fi)
47 CamelPOP3Store *pop3_store;
50 store = camel_folder_get_parent_store ((CamelFolder *) pop3_folder);
51 pop3_store = CAMEL_POP3_STORE (store);
53 g_hash_table_remove (pop3_folder->uids_id, GINT_TO_POINTER (fi->id));
55 camel_pop3_engine_command_free (pop3_store->engine, fi->cmd);
63 cmd_uidl (CamelPOP3Engine *pe,
64 CamelPOP3Stream *stream,
65 GCancellable *cancellable,
73 CamelPOP3FolderInfo *fi;
74 CamelPOP3Folder *folder = data;
77 ret = camel_pop3_stream_line (stream, &line, &len, cancellable, NULL);
79 if (strlen ((gchar *) line) > 1024)
81 if (sscanf((gchar *) line, "%u %s", &id, uid) == 2) {
82 fi = g_hash_table_lookup (folder->uids_id, GINT_TO_POINTER (id));
84 camel_operation_progress (NULL, (fi->index + 1) * 100 / folder->uids->len);
85 fi->uid = g_strdup (uid);
86 g_hash_table_insert (folder->uids_fi, fi->uid, fi);
88 g_warning("ID %u (uid: %s) not in previous LIST output", id, uid);
95 /* create a uid from md5 of 'top' output */
97 cmd_builduid (CamelPOP3Engine *pe,
98 CamelPOP3Stream *stream,
99 GCancellable *cancellable,
103 CamelPOP3FolderInfo *fi = data;
104 struct _camel_header_raw *h;
109 length = g_checksum_type_get_length (G_CHECKSUM_MD5);
110 digest = g_alloca (length);
112 /* TODO; somehow work out the limit and use that for proper progress reporting
113 * We need a pointer to the folder perhaps? */
114 camel_operation_progress (NULL, fi->id);
116 checksum = g_checksum_new (G_CHECKSUM_MD5);
117 mp = camel_mime_parser_new ();
118 camel_mime_parser_init_with_stream (mp, (CamelStream *) stream, NULL);
119 switch (camel_mime_parser_step (mp, NULL, NULL)) {
120 case CAMEL_MIME_PARSER_STATE_HEADER:
121 case CAMEL_MIME_PARSER_STATE_MESSAGE:
122 case CAMEL_MIME_PARSER_STATE_MULTIPART:
123 h = camel_mime_parser_headers_raw (mp);
125 if (g_ascii_strcasecmp(h->name, "status") != 0
126 && g_ascii_strcasecmp(h->name, "x-status") != 0) {
127 g_checksum_update (checksum, (guchar *) h->name, -1);
128 g_checksum_update (checksum, (guchar *) h->value, -1);
136 g_checksum_get_digest (checksum, digest, &length);
137 g_checksum_free (checksum);
139 fi->uid = g_base64_encode ((guchar *) digest, length);
141 d(printf("building uid for id '%d' = '%s'\n", fi->id, fi->uid));
145 cmd_list (CamelPOP3Engine *pe,
146 CamelPOP3Stream *stream,
147 GCancellable *cancellable,
153 CamelFolder *folder = data;
154 CamelStore *parent_store;
155 CamelPOP3Store *pop3_store;
156 CamelPOP3FolderInfo *fi;
157 gint i = 0, total, last_uid=-1;
158 CamelPOP3Folder *pop3_folder;
159 CamelService *service;
160 CamelSettings *settings;
161 gint batch_fetch_count;
163 parent_store = camel_folder_get_parent_store (folder);
164 pop3_store = CAMEL_POP3_STORE (parent_store);
165 pop3_folder = (CamelPOP3Folder *) folder;
166 service = (CamelService *) parent_store;
168 settings = camel_service_ref_settings (service);
170 batch_fetch_count = camel_pop3_settings_get_batch_fetch_count (
171 CAMEL_POP3_SETTINGS (settings));
173 g_object_unref (settings);
176 ret = camel_pop3_stream_line (stream, &line, &len, cancellable, NULL);
178 if (sscanf((gchar *) line, "%u %u", &id, &size) == 2) {
179 fi = g_malloc0 (sizeof (*fi));
182 fi->index = ((CamelPOP3Folder *) folder)->uids->len;
183 if ((pop3_store->engine->capa & CAMEL_POP3_CAP_UIDL) == 0)
184 fi->cmd = camel_pop3_engine_command_new(pe, CAMEL_POP3_COMMAND_MULTI, cmd_builduid, fi, cancellable, NULL, "TOP %u 0\r\n", id);
185 g_ptr_array_add (pop3_folder->uids, fi);
186 g_hash_table_insert (pop3_folder->uids_id, GINT_TO_POINTER (id), fi);
191 /* Trim the list for mobile devices*/
192 if (pop3_folder->mobile_mode && pop3_folder->uids->len) {
194 gboolean save_uid = FALSE;
196 /* Preserve the first message's ID */
197 fi = pop3_folder->uids->pdata[0];
198 pop3_folder->first_id = fi->id;
200 total = pop3_folder->uids->len;
201 if (pop3_folder->key_file) {
202 last_uid = g_key_file_get_integer (pop3_folder->key_file, "UIDConfig", "last-saved-uid", NULL);
204 /* First time downloading the POP folder, lets just download only a batch. */
207 d(printf("Last stored' first uid: %d\n", last_uid));
213 for (i = total - 1; i >= 0; i--) {
214 fi = pop3_folder->uids->pdata[i];
216 if ((last_uid != -1 && last_uid >= fi->id) || (last_uid == -1 && i == total - batch_fetch_count)) {
217 if (last_uid != -1 && last_uid < fi->id)
218 i++; /* if the last uid was deleted on the server, then we need the last but 1 */
223 if (i> 0 && pop3_folder->fetch_type == CAMEL_FETCH_OLD_MESSAGES && pop3_folder->fetch_more) {
225 /* Lets pull another window of old messages */
227 /* Decrement 'i' by another batch count or till we reach the first message */
228 d(printf("Fetch more (%d): from %d", pop3_folder->fetch_more, i));
229 for (k = 0; k< pop3_folder->fetch_more && i>= 0; k++, i--);
230 d(printf(" to %d\n", i));
232 /* Don't load messages newer than the latest we already had. We had to just get old messages and not
234 for (y = i; y < total; y++) {
235 fi = pop3_folder->uids->pdata[y];
236 if (fi->id == pop3_folder->latest_id) {
237 /* Delete everything after this. */
239 for (y = k + 1; y < total; y++) {
240 fi = pop3_folder->uids->pdata[y];
241 free_fi (pop3_folder, fi);
243 g_ptr_array_remove_range (pop3_folder->uids, k + 1, total - k - 1);
248 } else if (pop3_folder->fetch_more == CAMEL_FETCH_NEW_MESSAGES && pop3_folder->fetch_more) {
249 /* We need to download new messages. */
252 for (k = i; k < total; k++) {
253 fi = pop3_folder->uids->pdata[k];
254 if (fi->id == pop3_folder->latest_id) {
255 /* We need to just download the specified number of messages. */
256 y= (k + pop3_folder->fetch_more) < total ? (k + pop3_folder->fetch_more) : total - 1;
263 /* Delete the unnecessary old messages */
266 /* i is the start of the last fetch UID, so remove everything else from 0 to i */
268 fi = pop3_folder->uids->pdata[j];
269 free_fi (pop3_folder, fi);
271 g_ptr_array_remove_range (pop3_folder->uids, 0, i);
272 d(printf("Removing %d uids that are old\n", i));
276 /* Delete the unnecessary new message references. */
280 for (k = y + 1; k < total; k++) {
281 fi = pop3_folder->uids->pdata[k];
282 free_fi (pop3_folder, fi);
284 g_ptr_array_remove_range (pop3_folder->uids, y + 1, total - y - 1);
293 /* Save the last fetched UID */
294 fi = pop3_folder->uids->pdata[0];
295 g_key_file_set_integer (pop3_folder->key_file, "UIDConfig", "last-saved-uid", fi->id);
296 contents = g_key_file_to_data (pop3_folder->key_file, &len, NULL);
297 root = camel_service_get_user_cache_dir (service);
298 path = g_build_filename (root, "uidconfig", NULL);
299 g_file_set_contents (path, contents, len, NULL);
300 g_key_file_load_from_file (pop3_folder->key_file, path, G_KEY_FILE_NONE, NULL);
303 d(printf("Saving last uid %d\n", fi->id));
312 cmd_tocache (CamelPOP3Engine *pe,
313 CamelPOP3Stream *stream,
314 GCancellable *cancellable,
317 CamelPOP3FolderInfo *fi = data;
320 GError *error = NULL;
322 /* What if it fails? */
324 /* We write an '*' to the start of the stream to say its not complete yet */
325 /* This should probably be part of the cache code */
326 if ((n = camel_stream_write (fi->stream, "*", 1, cancellable, &error)) == -1)
329 while ((n = camel_stream_read ((CamelStream *) stream, buffer, sizeof (buffer), cancellable, &error)) > 0) {
330 n = camel_stream_write (fi->stream, buffer, n, cancellable, &error);
338 camel_operation_progress (NULL, (w * 100) / fi->size);
341 /* it all worked, output a '#' to say we're a-ok */
344 G_SEEKABLE (fi->stream),
345 0, G_SEEK_SET, cancellable, NULL);
346 camel_stream_write (fi->stream, "#", 1, cancellable, &error);
351 g_warning ("POP3 retrieval failed: %s", error->message);
352 g_error_free (error);
355 g_object_unref (fi->stream);
360 pop3_folder_dispose (GObject *object)
362 CamelPOP3Folder *pop3_folder = CAMEL_POP3_FOLDER (object);
363 CamelPOP3Store *pop3_store = NULL;
364 CamelStore *parent_store;
366 parent_store = camel_folder_get_parent_store (CAMEL_FOLDER (object));
368 pop3_store = CAMEL_POP3_STORE (parent_store);
370 if (pop3_folder->uids) {
372 CamelPOP3FolderInfo **fi = (CamelPOP3FolderInfo **) pop3_folder->uids->pdata;
373 gboolean is_online = camel_service_get_connection_status (CAMEL_SERVICE (parent_store)) == CAMEL_SERVICE_CONNECTED;
375 for (i = 0; i < pop3_folder->uids->len; i++, fi++) {
376 if (fi[0]->cmd && pop3_store && is_online) {
377 while (camel_pop3_engine_iterate (pop3_store->engine, fi[0]->cmd, NULL, NULL) > 0)
379 camel_pop3_engine_command_free (pop3_store->engine, fi[0]->cmd);
386 g_ptr_array_free (pop3_folder->uids, TRUE);
387 pop3_folder->uids = NULL;
390 if (pop3_folder->uids_fi) {
391 g_hash_table_destroy (pop3_folder->uids_fi);
392 pop3_folder->uids_fi = NULL;
395 /* Chain up to parent's dispose() method. */
396 G_OBJECT_CLASS (camel_pop3_folder_parent_class)->dispose (object);
400 pop3_folder_get_message_count (CamelFolder *folder)
402 CamelPOP3Folder *pop3_folder = CAMEL_POP3_FOLDER (folder);
404 return pop3_folder->uids->len;
408 pop3_folder_get_uids (CamelFolder *folder)
410 CamelPOP3Folder *pop3_folder = CAMEL_POP3_FOLDER (folder);
411 GPtrArray *uids = g_ptr_array_new ();
412 CamelPOP3FolderInfo **fi = (CamelPOP3FolderInfo **) pop3_folder->uids->pdata;
415 for (i = 0; i < pop3_folder->uids->len; i++,fi++) {
417 g_ptr_array_add (uids, fi[0]->uid);
424 pop3_folder_get_filename (CamelFolder *folder,
428 CamelStore *parent_store;
429 CamelPOP3Folder *pop3_folder;
430 CamelPOP3Store *pop3_store;
431 CamelPOP3FolderInfo *fi;
433 parent_store = camel_folder_get_parent_store (folder);
435 pop3_folder = CAMEL_POP3_FOLDER (folder);
436 pop3_store = CAMEL_POP3_STORE (parent_store);
438 fi = g_hash_table_lookup (pop3_folder->uids_fi, uid);
441 error, CAMEL_FOLDER_ERROR,
442 CAMEL_FOLDER_ERROR_INVALID_UID,
443 _("No message with UID %s"), uid);
447 return camel_data_cache_get_filename (
448 pop3_store->cache, "cache", fi->uid);
452 pop3_folder_set_message_flags (CamelFolder *folder,
454 CamelMessageFlags flags,
455 CamelMessageFlags set)
457 CamelPOP3Folder *pop3_folder = CAMEL_POP3_FOLDER (folder);
458 CamelPOP3FolderInfo *fi;
459 gboolean res = FALSE;
461 fi = g_hash_table_lookup (pop3_folder->uids_fi, uid);
463 guint32 new = (fi->flags & ~flags) | (set & flags);
465 if (fi->flags != new) {
474 static CamelMimeMessage *
475 pop3_folder_get_message_sync (CamelFolder *folder,
477 GCancellable *cancellable,
480 CamelStore *parent_store;
481 CamelMimeMessage *message = NULL;
482 CamelPOP3Store *pop3_store;
483 CamelPOP3Folder *pop3_folder;
484 CamelPOP3Command *pcr;
485 CamelPOP3FolderInfo *fi;
488 CamelStream *stream = NULL;
489 CamelService *service;
490 CamelSettings *settings;
493 g_return_val_if_fail (uid != NULL, NULL);
495 parent_store = camel_folder_get_parent_store (folder);
497 pop3_folder = CAMEL_POP3_FOLDER (folder);
498 pop3_store = CAMEL_POP3_STORE (parent_store);
500 service = CAMEL_SERVICE (parent_store);
502 settings = camel_service_ref_settings (service);
506 "auto-fetch", &auto_fetch,
509 g_object_unref (settings);
511 fi = g_hash_table_lookup (pop3_folder->uids_fi, uid);
514 error, CAMEL_FOLDER_ERROR,
515 CAMEL_FOLDER_ERROR_INVALID_UID,
516 _("No message with UID %s"), uid);
520 if (camel_service_get_connection_status (CAMEL_SERVICE (parent_store)) != CAMEL_SERVICE_CONNECTED) {
522 error, CAMEL_SERVICE_ERROR,
523 CAMEL_SERVICE_ERROR_UNAVAILABLE,
524 _("You must be working online to complete this operation"));
528 /* Sigh, most of the crap in this function is so that the cancel button
529 * returns the proper exception code. Sigh. */
531 camel_operation_push_message (
532 cancellable, _("Retrieving POP message %d"), fi->id);
534 /* If we have an oustanding retrieve message running, wait for that to complete
535 * & then retrieve from cache, otherwise, start a new one, and similar */
537 if (fi->cmd != NULL) {
538 while ((i = camel_pop3_engine_iterate (pop3_store->engine, fi->cmd, cancellable, error)) > 0)
541 /* getting error code? */
542 /*g_assert (fi->cmd->state == CAMEL_POP3_COMMAND_DATA);*/
543 camel_pop3_engine_command_free (pop3_store->engine, fi->cmd);
548 error, _("Cannot get message %s: "), uid);
553 /* check to see if we have safely written flag set */
554 if (pop3_store->cache == NULL
555 || (stream = camel_data_cache_get(pop3_store->cache, "cache", fi->uid, NULL)) == NULL
556 || camel_stream_read (stream, buffer, 1, cancellable, NULL) != 1
557 || buffer[0] != '#') {
559 /* Initiate retrieval, if disk backing fails, use a memory backing */
560 if (pop3_store->cache == NULL
561 || (stream = camel_data_cache_add(pop3_store->cache, "cache", fi->uid, NULL)) == NULL)
562 stream = camel_stream_mem_new ();
564 /* ref it, the cache storage routine unref's when done */
565 fi->stream = g_object_ref (stream);
566 pcr = camel_pop3_engine_command_new(pop3_store->engine, CAMEL_POP3_COMMAND_MULTI, cmd_tocache, fi, cancellable, NULL, "RETR %u\r\n", fi->id);
568 /* Also initiate retrieval of some of the following messages, assume we'll be receiving them */
569 if (auto_fetch && pop3_store->cache != NULL) {
570 /* This should keep track of the last one retrieved, also how many are still
571 * oustanding incase of random access on large folders */
573 last = MIN (i + 10, pop3_folder->uids->len);
574 for (; i < last; i++) {
575 CamelPOP3FolderInfo *pfi = pop3_folder->uids->pdata[i];
577 if (pfi->uid && pfi->cmd == NULL) {
578 pfi->stream = camel_data_cache_add(pop3_store->cache, "cache", pfi->uid, NULL);
580 pfi->cmd = camel_pop3_engine_command_new (pop3_store->engine, CAMEL_POP3_COMMAND_MULTI,
581 cmd_tocache, pfi, cancellable, NULL, "RETR %u\r\n", pfi->id);
587 /* now wait for the first one to finish */
588 while ((i = camel_pop3_engine_iterate (pop3_store->engine, pcr, cancellable, error)) > 0)
591 /* getting error code? */
592 /*g_assert (pcr->state == CAMEL_POP3_COMMAND_DATA);*/
593 camel_pop3_engine_command_free (pop3_store->engine, pcr);
595 G_SEEKABLE (stream), 0, G_SEEK_SET, NULL, NULL);
597 /* Check to see we have safely written flag set */
600 error, _("Cannot get message %s: "), uid);
604 if (camel_stream_read (stream, buffer, 1, cancellable, error) == -1)
607 if (buffer[0] != '#') {
609 error, CAMEL_ERROR, CAMEL_ERROR_GENERIC,
610 _("Cannot get message %s: %s"), uid,
611 _("Unknown reason"));
616 message = camel_mime_message_new ();
617 if (!camel_data_wrapper_construct_from_stream_sync (
618 CAMEL_DATA_WRAPPER (message), stream, cancellable, error)) {
619 g_prefix_error (error, _("Cannot get message %s: "), uid);
620 g_object_unref (message);
623 /* because the UID in the local store doesn't match with the UID in the pop3 store */
624 camel_medium_add_header (CAMEL_MEDIUM (message), "X-Evolution-POP3-UID", uid);
627 g_object_unref (stream);
629 camel_operation_pop_message (cancellable);
635 pop3_folder_refresh_info_sync (CamelFolder *folder,
636 GCancellable *cancellable,
639 CamelStore *parent_store;
640 CamelPOP3Store *pop3_store;
641 CamelPOP3Folder *pop3_folder = (CamelPOP3Folder *) folder;
642 CamelPOP3Command *pcl, *pcu = NULL;
643 gboolean success = TRUE;
644 GError *local_error = NULL;
647 parent_store = camel_folder_get_parent_store (folder);
648 pop3_store = CAMEL_POP3_STORE (parent_store);
650 if (camel_service_get_connection_status (CAMEL_SERVICE (parent_store)) != CAMEL_SERVICE_CONNECTED) {
652 error, CAMEL_SERVICE_ERROR,
653 CAMEL_SERVICE_ERROR_UNAVAILABLE,
654 _("You must be working online to complete this operation"));
658 camel_operation_push_message (
659 cancellable, _("Retrieving POP summary"));
661 /* Get rid of the old cache */
662 if (pop3_folder->uids) {
664 CamelPOP3FolderInfo *last_fi;
666 if (pop3_folder->uids->len) {
667 last_fi = pop3_folder->uids->pdata[pop3_folder->uids->len - 1];
669 pop3_folder->latest_id = last_fi->id;
671 pop3_folder->latest_id = -1;
673 pop3_folder->latest_id = -1;
675 for (i = 0; i < pop3_folder->uids->len; i++) {
676 CamelPOP3FolderInfo *fi = pop3_folder->uids->pdata[i];
678 camel_pop3_engine_command_free (pop3_store->engine, fi->cmd);
685 g_ptr_array_free (pop3_folder->uids, TRUE);
688 if (pop3_folder->uids_fi) {
689 g_hash_table_destroy (pop3_folder->uids_fi);
690 pop3_folder->uids_fi = NULL;
693 /* Get a new working set. */
694 pop3_folder->uids = g_ptr_array_new ();
695 pop3_folder->uids_fi = g_hash_table_new (g_str_hash, g_str_equal);
697 /* only used during setup */
698 pop3_folder->uids_id = g_hash_table_new (NULL, NULL);
700 pcl = camel_pop3_engine_command_new(pop3_store->engine, CAMEL_POP3_COMMAND_MULTI, cmd_list, folder, cancellable, &local_error, "LIST\r\n");
701 if (!local_error && (pop3_store->engine->capa & CAMEL_POP3_CAP_UIDL) != 0)
702 pcu = camel_pop3_engine_command_new(pop3_store->engine, CAMEL_POP3_COMMAND_MULTI, cmd_uidl, folder, cancellable, &local_error, "UIDL\r\n");
703 while ((i = camel_pop3_engine_iterate (pop3_store->engine, NULL, cancellable, error)) > 0)
707 g_propagate_error (error, local_error);
709 } else if (i == -1) {
710 g_prefix_error (error, _("Cannot get POP summary: "));
714 /* TODO: check every id has a uid & commands returned OK too? */
717 camel_pop3_engine_command_free (pop3_store->engine, pcl);
720 camel_pop3_engine_command_free (pop3_store->engine, pcu);
722 for (i = 0; i < pop3_folder->uids->len; i++) {
723 CamelPOP3FolderInfo *fi = pop3_folder->uids->pdata[i];
725 camel_pop3_engine_command_free (pop3_store->engine, fi->cmd);
729 g_hash_table_insert (pop3_folder->uids_fi, fi->uid, fi);
734 /* dont need this anymore */
735 g_hash_table_destroy (pop3_folder->uids_id);
736 pop3_folder->uids_id = NULL;
738 camel_operation_pop_message (cancellable);
744 pop3_fetch_messages_sync (CamelFolder *folder,
747 GCancellable *cancellable,
750 CamelPOP3FolderInfo *fi;
751 CamelPOP3Folder *pop3_folder = (CamelPOP3Folder *) folder;
753 CamelStore *parent_store;
754 CamelService *service;
755 CamelSettings *settings;
756 gint batch_fetch_count;
758 parent_store = camel_folder_get_parent_store (folder);
759 service = (CamelService *) parent_store;
761 settings = camel_service_ref_settings (service);
763 batch_fetch_count = camel_pop3_settings_get_batch_fetch_count (
764 CAMEL_POP3_SETTINGS (settings));
766 g_object_unref (settings);
768 old_len = pop3_folder->uids->len;
770 /* If we have the first message already, then return FALSE */
771 fi = pop3_folder->uids->pdata[0];
772 if (type == CAMEL_FETCH_OLD_MESSAGES && fi->id == pop3_folder->first_id)
775 pop3_folder->fetch_type = type;
776 pop3_folder->fetch_more = (limit > 0) ? limit : batch_fetch_count;
777 pop3_folder_refresh_info_sync (folder, cancellable, error);
778 pop3_folder->fetch_more = 0;
780 /* Even if we downloaded the first/oldest message, just now, return TRUE so that we wont waste another cycle */
781 fi = pop3_folder->uids->pdata[0];
782 if (type == CAMEL_FETCH_OLD_MESSAGES && fi->id == pop3_folder->first_id)
784 else if (type == CAMEL_FETCH_NEW_MESSAGES && old_len == pop3_folder->uids->len)
785 return FALSE; /* We didnt fetch any new messages as there were none probably. */
791 pop3_folder_synchronize_sync (CamelFolder *folder,
793 GCancellable *cancellable,
796 CamelService *service;
797 CamelSettings *settings;
798 CamelStore *parent_store;
799 CamelPOP3Folder *pop3_folder;
800 CamelPOP3Store *pop3_store;
801 CamelPOP3FolderInfo *fi;
802 gint delete_after_days;
803 gboolean delete_expunged;
804 gboolean keep_on_server;
808 parent_store = camel_folder_get_parent_store (folder);
810 pop3_folder = CAMEL_POP3_FOLDER (folder);
811 pop3_store = CAMEL_POP3_STORE (parent_store);
813 service = CAMEL_SERVICE (parent_store);
814 is_online = camel_service_get_connection_status (service) == CAMEL_SERVICE_CONNECTED;
816 settings = camel_service_ref_settings (service);
820 "delete-after-days", &delete_after_days,
821 "delete-expunged", &delete_expunged,
822 "keep-on-server", &keep_on_server,
825 g_object_unref (settings);
827 if (is_online && delete_after_days > 0 && !expunge) {
828 camel_operation_push_message (
829 cancellable, _("Expunging old messages"));
831 camel_pop3_delete_old (
832 folder, delete_after_days, cancellable, error);
834 camel_operation_pop_message (cancellable);
837 if (!expunge || (keep_on_server && !delete_expunged))
842 error, CAMEL_SERVICE_ERROR,
843 CAMEL_SERVICE_ERROR_UNAVAILABLE,
844 _("You must be working online to complete this operation"));
848 camel_operation_push_message (
849 cancellable, _("Expunging deleted messages"));
851 for (i = 0; i < pop3_folder->uids->len; i++) {
852 fi = pop3_folder->uids->pdata[i];
853 /* busy already? wait for that to finish first */
855 while (camel_pop3_engine_iterate (pop3_store->engine, fi->cmd, cancellable, NULL) > 0)
857 camel_pop3_engine_command_free (pop3_store->engine, fi->cmd);
861 if (fi->flags & CAMEL_MESSAGE_DELETED) {
862 fi->cmd = camel_pop3_engine_command_new (pop3_store->engine,
870 /* also remove from cache */
871 if (pop3_store->cache && fi->uid)
872 camel_data_cache_remove(pop3_store->cache, "cache", fi->uid, NULL);
876 for (i = 0; i < pop3_folder->uids->len; i++) {
877 fi = pop3_folder->uids->pdata[i];
878 /* wait for delete commands to finish */
880 while (camel_pop3_engine_iterate (pop3_store->engine, fi->cmd, cancellable, NULL) > 0)
882 camel_pop3_engine_command_free (pop3_store->engine, fi->cmd);
885 camel_operation_progress (
886 cancellable, (i + 1) * 100 / pop3_folder->uids->len);
889 camel_operation_pop_message (cancellable);
891 return camel_pop3_store_expunge (pop3_store, cancellable, error);
895 camel_pop3_folder_class_init (CamelPOP3FolderClass *class)
897 GObjectClass *object_class;
898 CamelFolderClass *folder_class;
900 object_class = G_OBJECT_CLASS (class);
901 object_class->dispose = pop3_folder_dispose;
903 folder_class = CAMEL_FOLDER_CLASS (class);
904 folder_class->fetch_messages_sync = pop3_fetch_messages_sync;
905 folder_class->get_message_count = pop3_folder_get_message_count;
906 folder_class->get_uids = pop3_folder_get_uids;
907 folder_class->free_uids = camel_folder_free_shallow;
908 folder_class->get_filename = pop3_folder_get_filename;
909 folder_class->set_message_flags = pop3_folder_set_message_flags;
910 folder_class->get_message_sync = pop3_folder_get_message_sync;
911 folder_class->refresh_info_sync = pop3_folder_refresh_info_sync;
912 folder_class->synchronize_sync = pop3_folder_synchronize_sync;
916 camel_pop3_folder_init (CamelPOP3Folder *pop3_folder)
918 pop3_folder->uids = g_ptr_array_new ();
919 pop3_folder->uids_fi = g_hash_table_new (g_str_hash, g_str_equal);
923 camel_pop3_folder_new (CamelStore *parent,
924 GCancellable *cancellable,
928 CamelService *service;
929 CamelSettings *settings;
930 CamelPOP3Folder *pop3_folder;
932 service = CAMEL_SERVICE (parent);
934 d(printf("opening pop3 INBOX folder\n"));
936 folder = g_object_new (
937 CAMEL_TYPE_POP3_FOLDER,
938 "full-name", "inbox", "display-name", "inbox",
939 "parent-store", parent, NULL);
941 settings = camel_service_ref_settings (service);
943 pop3_folder = (CamelPOP3Folder *) folder;
944 pop3_folder->mobile_mode = camel_pop3_settings_get_mobile_mode (
945 CAMEL_POP3_SETTINGS (settings));
947 g_object_unref (settings);
949 pop3_folder->fetch_more = 0;
950 if (pop3_folder->mobile_mode) {
955 pop3_folder->key_file = g_key_file_new ();
956 root = camel_service_get_user_cache_dir (service);
957 path = g_build_filename (root, "uidconfig", NULL);
958 g_key_file_load_from_file (pop3_folder->key_file, path, G_KEY_FILE_NONE, NULL);
963 if (camel_service_get_connection_status (CAMEL_SERVICE (parent)) != CAMEL_SERVICE_CONNECTED)
966 /* mt-ok, since we dont have the folder-lock for new() */
967 if (!camel_folder_refresh_info_sync (folder, cancellable, error)) {
968 g_object_unref (folder);
976 pop3_get_message_time_from_cache (CamelFolder *folder,
978 time_t *message_time)
980 CamelStore *parent_store;
981 CamelPOP3Store *pop3_store;
982 CamelStream *stream = NULL;
984 gboolean res = FALSE;
986 g_return_val_if_fail (folder != NULL, FALSE);
987 g_return_val_if_fail (uid != NULL, FALSE);
988 g_return_val_if_fail (message_time != NULL, FALSE);
990 parent_store = camel_folder_get_parent_store (folder);
991 pop3_store = CAMEL_POP3_STORE (parent_store);
993 g_return_val_if_fail (pop3_store->cache != NULL, FALSE);
995 if ((stream = camel_data_cache_get (pop3_store->cache, "cache", uid, NULL)) != NULL
996 && camel_stream_read (stream, buffer, 1, NULL, NULL) == 1
997 && buffer[0] == '#') {
998 CamelMimeMessage *message;
999 GError *error = NULL;
1001 message = camel_mime_message_new ();
1002 camel_data_wrapper_construct_from_stream_sync (
1003 (CamelDataWrapper *) message, stream, NULL, &error);
1004 if (error != NULL) {
1005 g_warning (_("Cannot get message %s: %s"), uid, error->message);
1006 g_error_free (error);
1008 g_object_unref (message);
1014 *message_time = message->date + message->date_offset;
1016 g_object_unref (message);
1021 g_object_unref (stream);
1027 camel_pop3_delete_old (CamelFolder *folder,
1028 gint days_to_delete,
1029 GCancellable *cancellable,
1032 CamelStore *parent_store;
1033 CamelPOP3Folder *pop3_folder;
1034 CamelPOP3FolderInfo *fi;
1036 CamelPOP3Store *pop3_store;
1037 CamelMimeMessage *message;
1038 time_t temp, message_time;
1040 parent_store = camel_folder_get_parent_store (folder);
1042 if (camel_service_get_connection_status (CAMEL_SERVICE (parent_store)) != CAMEL_SERVICE_CONNECTED) {
1044 error, CAMEL_SERVICE_ERROR,
1045 CAMEL_SERVICE_ERROR_UNAVAILABLE,
1046 _("You must be working online to complete this operation"));
1050 pop3_folder = CAMEL_POP3_FOLDER (folder);
1051 pop3_store = CAMEL_POP3_STORE (parent_store);
1052 temp = time (&temp);
1054 d(printf("%s(%d): pop3_folder->uids->len=[%d]\n", __FILE__, __LINE__, pop3_folder->uids->len));
1055 for (i = 0; i < pop3_folder->uids->len; i++) {
1057 fi = pop3_folder->uids->pdata[i];
1060 while (camel_pop3_engine_iterate (pop3_store->engine, fi->cmd, cancellable, NULL) > 0) {
1061 ; /* do nothing - iterating until end */
1064 camel_pop3_engine_command_free (pop3_store->engine, fi->cmd);
1068 /* continue, if message wasn't received yet */
1072 d(printf("%s(%d): fi->uid=[%s]\n", __FILE__, __LINE__, fi->uid));
1073 if (!pop3_get_message_time_from_cache (folder, fi->uid, &message_time)) {
1074 d(printf("could not get message time from cache, trying from pop3\n"));
1075 message = pop3_folder_get_message_sync (
1076 folder, fi->uid, cancellable, error);
1078 message_time = message->date + message->date_offset;
1079 g_object_unref (message);
1084 gdouble time_diff = difftime (temp,message_time);
1085 gint day_lag = time_diff / (60 * 60 * 24);
1087 d(printf("%s(%d): message_time= [%ld]\n", __FILE__, __LINE__, message_time));
1088 d(printf("%s(%d): day_lag=[%d] \t days_to_delete=[%d]\n",
1089 __FILE__, __LINE__, day_lag, days_to_delete));
1091 if (day_lag > days_to_delete) {
1093 while (camel_pop3_engine_iterate (pop3_store->engine, fi->cmd, cancellable, NULL) > 0) {
1094 ; /* do nothing - iterating until end */
1097 camel_pop3_engine_command_free (pop3_store->engine, fi->cmd);
1100 d(printf("%s(%d): Deleting old messages\n", __FILE__, __LINE__));
1101 fi->cmd = camel_pop3_engine_command_new (pop3_store->engine,
1108 /* also remove from cache */
1109 if (pop3_store->cache && fi->uid) {
1110 camel_data_cache_remove(pop3_store->cache, "cache", fi->uid, NULL);
1116 for (i = 0; i < pop3_folder->uids->len; i++) {
1117 fi = pop3_folder->uids->pdata[i];
1118 /* wait for delete commands to finish */
1120 while (camel_pop3_engine_iterate (pop3_store->engine, fi->cmd, cancellable, NULL) > 0)
1122 camel_pop3_engine_command_free (pop3_store->engine, fi->cmd);
1125 camel_operation_progress (
1126 cancellable, (i + 1) * 100 / pop3_folder->uids->len);
1129 return camel_pop3_store_expunge (pop3_store, cancellable, error);