diff options
Diffstat (limited to 'src/camel/providers/pop3/camel-pop3-folder.c')
-rw-r--r-- | src/camel/providers/pop3/camel-pop3-folder.c | 1170 |
1 files changed, 1170 insertions, 0 deletions
diff --git a/src/camel/providers/pop3/camel-pop3-folder.c b/src/camel/providers/pop3/camel-pop3-folder.c new file mode 100644 index 000000000..bb7042e95 --- /dev/null +++ b/src/camel/providers/pop3/camel-pop3-folder.c @@ -0,0 +1,1170 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* camel-pop3-folder.c : class for a pop3 folder + * + * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) + * + * This library is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library. If not, see <http://www.gnu.org/licenses/>. + * + * Authors: Dan Winship <danw@ximian.com> + * Michael Zucchi <notzed@ximian.com> + */ + +#include "evolution-data-server-config.h" + +#include <stdlib.h> +#include <string.h> + +#include <glib/gi18n-lib.h> + +#include "camel-pop3-folder.h" +#include "camel-pop3-store.h" +#include "camel-pop3-settings.h" + +#define d(x) if (camel_debug("pop3")) x; + +typedef struct _CamelPOP3FolderInfo CamelPOP3FolderInfo; + +struct _CamelPOP3FolderInfo { + guint32 id; + guint32 size; + guint32 flags; + guint32 index; /* index of request */ + gchar *uid; + struct _CamelPOP3Command *cmd; + struct _CamelStream *stream; +}; + +G_DEFINE_TYPE (CamelPOP3Folder, camel_pop3_folder, CAMEL_TYPE_FOLDER) + +static void +cmd_uidl (CamelPOP3Engine *pe, + CamelPOP3Stream *stream, + GCancellable *cancellable, + GError **error, + gpointer data) +{ + gint ret; + guint len; + guchar *line; + gchar uid[1025]; + guint id; + CamelPOP3FolderInfo *fi; + CamelPOP3Folder *folder = data; + + do { + ret = camel_pop3_stream_line (stream, &line, &len, cancellable, error); + if (ret >= 0) { + if (strlen ((gchar *) line) > 1024) + line[1024] = 0; + if (sscanf ((gchar *) line, "%u %s", &id, uid) == 2) { + fi = g_hash_table_lookup (folder->uids_id, GINT_TO_POINTER (id)); + if (fi) { + camel_operation_progress (cancellable, (fi->index + 1) * 100 / folder->uids->len); + fi->uid = g_strdup (uid); + g_hash_table_insert (folder->uids_fi, fi->uid, fi); + } else { + g_warning ("ID %u (uid: %s) not in previous LIST output", id, uid); + } + } + } + } while (ret > 0); +} + +/* create a uid from md5 of 'top' output */ +static void +cmd_builduid (CamelPOP3Engine *pe, + CamelPOP3Stream *stream, + GCancellable *cancellable, + GError **error, + gpointer data) +{ + GChecksum *checksum; + CamelPOP3FolderInfo *fi = data; + struct _camel_header_raw *h; + CamelMimeParser *mp; + guint8 *digest; + gsize length; + + length = g_checksum_type_get_length (G_CHECKSUM_MD5); + digest = g_alloca (length); + + /* TODO; somehow work out the limit and use that for proper progress reporting + * We need a pointer to the folder perhaps? */ + /* camel_operation_progress (cancellable, fi->id); */ + + checksum = g_checksum_new (G_CHECKSUM_MD5); + mp = camel_mime_parser_new (); + camel_mime_parser_init_with_stream (mp, (CamelStream *) stream, NULL); + switch (camel_mime_parser_step (mp, NULL, NULL)) { + case CAMEL_MIME_PARSER_STATE_HEADER: + case CAMEL_MIME_PARSER_STATE_MESSAGE: + case CAMEL_MIME_PARSER_STATE_MULTIPART: + h = camel_mime_parser_headers_raw (mp); + while (h) { + if (g_ascii_strcasecmp (h->name, "status") != 0 + && g_ascii_strcasecmp (h->name, "x-status") != 0) { + g_checksum_update (checksum, (guchar *) h->name, -1); + g_checksum_update (checksum, (guchar *) h->value, -1); + } + h = h->next; + } + default: + break; + } + g_object_unref (mp); + g_checksum_get_digest (checksum, digest, &length); + g_checksum_free (checksum); + + fi->uid = g_base64_encode ((guchar *) digest, length); + + d (printf ("building uid for id '%d' = '%s'\n", fi->id, fi->uid)); +} + +static void +cmd_list (CamelPOP3Engine *pe, + CamelPOP3Stream *stream, + GCancellable *cancellable, + GError **error, + gpointer data) +{ + gint ret; + guint len, id, size; + guchar *line; + CamelFolder *folder = data; + CamelPOP3FolderInfo *fi; + CamelPOP3Folder *pop3_folder; + + g_return_if_fail (pe != NULL); + + pop3_folder = (CamelPOP3Folder *) folder; + + do { + ret = camel_pop3_stream_line (stream, &line, &len, cancellable, error); + if (ret >= 0) { + if (sscanf ((gchar *) line, "%u %u", &id, &size) == 2) { + fi = g_malloc0 (sizeof (*fi)); + fi->size = size; + fi->id = id; + fi->index = ((CamelPOP3Folder *) folder)->uids->len; + if ((pe->capa & CAMEL_POP3_CAP_UIDL) == 0) + fi->cmd = camel_pop3_engine_command_new ( + pe, + CAMEL_POP3_COMMAND_MULTI, + cmd_builduid, fi, + cancellable, error, + "TOP %u 0\r\n", id); + g_ptr_array_add (pop3_folder->uids, fi); + g_hash_table_insert ( + pop3_folder->uids_id, + GINT_TO_POINTER (id), fi); + } + } + } while (ret > 0); +} + +static void +cmd_tocache (CamelPOP3Engine *pe, + CamelPOP3Stream *stream, + GCancellable *cancellable, + GError **error, + gpointer data) +{ + CamelPOP3FolderInfo *fi = data; + gchar buffer[2048]; + gint w = 0, n; + GError *local_error = NULL; + + /* What if it fails? */ + + /* We write an '*' to the start of the stream to say its not complete yet */ + /* This should probably be part of the cache code */ + if ((n = camel_stream_write (fi->stream, "*", 1, cancellable, &local_error)) == -1) + goto done; + + while ((n = camel_stream_read ((CamelStream *) stream, buffer, sizeof (buffer), cancellable, &local_error)) > 0) { + n = camel_stream_write (fi->stream, buffer, n, cancellable, &local_error); + if (n == -1) + break; + + w += n; + if (w > fi->size) + w = fi->size; + if (fi->size != 0) + camel_operation_progress (cancellable, (w * 100) / fi->size); + } + + /* it all worked, output a '#' to say we're a-ok */ + if (local_error == NULL) { + g_seekable_seek ( + G_SEEKABLE (fi->stream), + 0, G_SEEK_SET, cancellable, NULL); + camel_stream_write (fi->stream, "#", 1, cancellable, &local_error); + } + +done: + if (local_error != NULL) { + g_propagate_error (error, local_error); + } + + g_object_unref (fi->stream); + fi->stream = NULL; +} + +static void +pop3_folder_dispose (GObject *object) +{ + CamelPOP3Folder *pop3_folder = CAMEL_POP3_FOLDER (object); + CamelPOP3Store *pop3_store = NULL; + CamelStore *parent_store; + + parent_store = camel_folder_get_parent_store (CAMEL_FOLDER (object)); + if (parent_store) + pop3_store = CAMEL_POP3_STORE (parent_store); + + if (pop3_folder->uids) { + gint i; + CamelPOP3FolderInfo **fi = (CamelPOP3FolderInfo **) pop3_folder->uids->pdata; + gboolean is_online = camel_service_get_connection_status (CAMEL_SERVICE (parent_store)) == CAMEL_SERVICE_CONNECTED; + + for (i = 0; i < pop3_folder->uids->len; i++, fi++) { + if (fi[0]->cmd && pop3_store && is_online) { + CamelPOP3Engine *pop3_engine; + + pop3_engine = camel_pop3_store_ref_engine (pop3_store); + + while (camel_pop3_engine_iterate (pop3_engine, fi[0]->cmd, NULL, NULL) > 0) + ; + camel_pop3_engine_command_free (pop3_engine, fi[0]->cmd); + + g_clear_object (&pop3_engine); + } + + g_free (fi[0]->uid); + g_free (fi[0]); + } + + g_ptr_array_free (pop3_folder->uids, TRUE); + pop3_folder->uids = NULL; + } + + if (pop3_folder->uids_fi) { + g_hash_table_destroy (pop3_folder->uids_fi); + pop3_folder->uids_fi = NULL; + } + + /* Chain up to parent's dispose() method. */ + G_OBJECT_CLASS (camel_pop3_folder_parent_class)->dispose (object); +} + +static gint +pop3_folder_get_message_count (CamelFolder *folder) +{ + CamelPOP3Folder *pop3_folder = CAMEL_POP3_FOLDER (folder); + + return pop3_folder->uids->len; +} + +static GPtrArray * +pop3_folder_get_uids (CamelFolder *folder) +{ + CamelPOP3Folder *pop3_folder = CAMEL_POP3_FOLDER (folder); + GPtrArray *uids = g_ptr_array_new (); + CamelPOP3FolderInfo **fi = (CamelPOP3FolderInfo **) pop3_folder->uids->pdata; + gint i; + + for (i = 0; i < pop3_folder->uids->len; i++,fi++) { + if (fi[0]->uid) + g_ptr_array_add (uids, fi[0]->uid); + } + + return uids; +} + +static GPtrArray * +pop3_get_uncached_uids (CamelFolder *folder, + GPtrArray *uids, + GError **error) +{ + CamelPOP3Folder *pop3_folder; + CamelPOP3Store *pop3_store; + GPtrArray *uncached_uids; + gint ii; + + g_return_val_if_fail (CAMEL_IS_POP3_FOLDER (folder), NULL); + g_return_val_if_fail (uids != NULL, NULL); + + pop3_folder = CAMEL_POP3_FOLDER (folder); + pop3_store = CAMEL_POP3_STORE (camel_folder_get_parent_store (folder)); + + uncached_uids = g_ptr_array_new (); + + for (ii = 0; ii < uids->len; ii++) { + const gchar *uid = uids->pdata[ii]; + CamelPOP3FolderInfo *fi; + gboolean uid_is_cached = FALSE; + + fi = g_hash_table_lookup (pop3_folder->uids_fi, uid); + + if (fi != NULL) { + uid_is_cached = camel_pop3_store_cache_has ( + pop3_store, fi->uid); + } + + if (!uid_is_cached) { + g_ptr_array_add ( + uncached_uids, (gpointer) + camel_pstring_strdup (uid)); + } + } + + return uncached_uids; +} + +static gchar * +pop3_folder_get_filename (CamelFolder *folder, + const gchar *uid, + GError **error) +{ + CamelStore *parent_store; + CamelPOP3Folder *pop3_folder; + CamelPOP3Store *pop3_store; + CamelDataCache *pop3_cache; + CamelPOP3FolderInfo *fi; + gchar *filename; + + parent_store = camel_folder_get_parent_store (folder); + + pop3_folder = CAMEL_POP3_FOLDER (folder); + pop3_store = CAMEL_POP3_STORE (parent_store); + + fi = g_hash_table_lookup (pop3_folder->uids_fi, uid); + if (fi == NULL) { + g_set_error ( + error, CAMEL_FOLDER_ERROR, + CAMEL_FOLDER_ERROR_INVALID_UID, + _("No message with UID %s"), uid); + return NULL; + } + + pop3_cache = camel_pop3_store_ref_cache (pop3_store); + if (!pop3_cache) { + g_warn_if_reached (); + return NULL; + } + + filename = camel_data_cache_get_filename ( + pop3_cache, "cache", fi->uid); + g_clear_object (&pop3_cache); + + return filename; +} + +static gboolean +pop3_folder_set_message_flags (CamelFolder *folder, + const gchar *uid, + CamelMessageFlags flags, + CamelMessageFlags set) +{ + CamelPOP3Folder *pop3_folder = CAMEL_POP3_FOLDER (folder); + CamelPOP3FolderInfo *fi; + gboolean res = FALSE; + + fi = g_hash_table_lookup (pop3_folder->uids_fi, uid); + if (fi) { + guint32 new = (fi->flags & ~flags) | (set & flags); + + if (fi->flags != new) { + fi->flags = new; + res = TRUE; + } + } + + return res; +} + +static CamelMimeMessage * +pop3_folder_get_message_internal_sync (CamelFolder *folder, + const gchar *uid, + gboolean already_locked, + GCancellable *cancellable, + GError **error) +{ + CamelStore *parent_store; + CamelMimeMessage *message = NULL; + CamelPOP3Store *pop3_store; + CamelPOP3Folder *pop3_folder; + CamelPOP3Engine *pop3_engine; + CamelPOP3Command *pcr; + CamelPOP3FolderInfo *fi; + gchar buffer[1]; + gint i, last; + CamelStream *stream = NULL; + CamelService *service; + CamelSettings *settings; + gboolean auto_fetch; + + g_return_val_if_fail (uid != NULL, NULL); + + parent_store = camel_folder_get_parent_store (folder); + + pop3_folder = CAMEL_POP3_FOLDER (folder); + pop3_store = CAMEL_POP3_STORE (parent_store); + + service = CAMEL_SERVICE (parent_store); + + settings = camel_service_ref_settings (service); + + g_object_get ( + settings, + "auto-fetch", &auto_fetch, + NULL); + + g_object_unref (settings); + + fi = g_hash_table_lookup (pop3_folder->uids_fi, uid); + if (fi == NULL) { + g_set_error ( + error, CAMEL_FOLDER_ERROR, + CAMEL_FOLDER_ERROR_INVALID_UID, + _("No message with UID %s"), uid); + return NULL; + } + + if (camel_service_get_connection_status (CAMEL_SERVICE (parent_store)) != CAMEL_SERVICE_CONNECTED) { + g_set_error ( + error, CAMEL_SERVICE_ERROR, + CAMEL_SERVICE_ERROR_UNAVAILABLE, + _("You must be working online to complete this operation")); + return NULL; + } + + /* Sigh, most of the crap in this function is so that the cancel button + * returns the proper exception code. Sigh. */ + + camel_operation_push_message ( + cancellable, _("Retrieving POP message %d"), fi->id); + + pop3_engine = camel_pop3_store_ref_engine (pop3_store); + + if (!already_locked && !camel_pop3_engine_busy_lock (pop3_engine, cancellable, error)) + goto fail; + + /* If we have an oustanding retrieve message running, wait for that to complete + * & then retrieve from cache, otherwise, start a new one, and similar */ + + if (fi->cmd != NULL) { + while ((i = camel_pop3_engine_iterate (pop3_engine, fi->cmd, cancellable, error)) > 0) + ; + + /* getting error code? */ + /*g_warn_if_fail (fi->cmd->state == CAMEL_POP3_COMMAND_DATA);*/ + camel_pop3_engine_command_free (pop3_engine, fi->cmd); + fi->cmd = NULL; + + if (i == -1) { + g_prefix_error ( + error, _("Cannot get message %s: "), uid); + if (!already_locked) + camel_pop3_engine_busy_unlock (pop3_engine); + goto fail; + } + } + + /* check to see if we have safely written flag set */ + stream = camel_pop3_store_cache_get (pop3_store, fi->uid, NULL); + if (!stream) { + GError *local_error = NULL; + + /* Initiate retrieval, if disk backing fails, use a memory backing */ + stream = camel_pop3_store_cache_add (pop3_store, fi->uid, NULL); + if (stream == NULL) + stream = camel_stream_mem_new (); + + /* ref it, the cache storage routine unref's when done */ + fi->stream = g_object_ref (stream); + pcr = camel_pop3_engine_command_new ( + pop3_engine, + CAMEL_POP3_COMMAND_MULTI, + cmd_tocache, fi, + cancellable, &local_error, + "RETR %u\r\n", fi->id); + + if (local_error) { + if (pcr) + camel_pop3_engine_command_free (pop3_engine, pcr); + + g_propagate_error (error, local_error); + g_prefix_error ( + error, _("Cannot get message %s: "), uid); + goto done; + } + + /* Also initiate retrieval of some of the following + * messages, assume we'll be receiving them. */ + if (auto_fetch) { + /* This should keep track of the last one retrieved, + * also how many are still oustanding incase of random + * access on large folders. */ + i = fi->index + 1; + last = MIN (i + 10, pop3_folder->uids->len); + for (; i < last; i++) { + CamelPOP3FolderInfo *pfi = pop3_folder->uids->pdata[i]; + + if (pfi->uid && pfi->cmd == NULL) { + pfi->stream = camel_pop3_store_cache_add ( + pop3_store, pfi->uid, NULL); + if (pfi->stream != NULL) { + pfi->cmd = camel_pop3_engine_command_new ( + pop3_engine, + CAMEL_POP3_COMMAND_MULTI, + cmd_tocache, pfi, + cancellable, &local_error, + "RETR %u\r\n", pfi->id); + + if (local_error) { + if (pcr) + camel_pop3_engine_command_free (pop3_engine, pcr); + + g_propagate_error (error, local_error); + g_prefix_error ( + error, _("Cannot get message %s: "), uid); + goto done; + } + } + } + } + } + + /* now wait for the first one to finish */ + while (!local_error && (i = camel_pop3_engine_iterate (pop3_engine, pcr, cancellable, &local_error)) > 0) + ; + + /* getting error code? */ + /*g_warn_if_fail (pcr->state == CAMEL_POP3_COMMAND_DATA);*/ + camel_pop3_engine_command_free (pop3_engine, pcr); + g_seekable_seek ( + G_SEEKABLE (stream), 0, G_SEEK_SET, NULL, NULL); + + /* Check to see we have safely written flag set */ + if (i == -1 || local_error) { + g_propagate_error (error, local_error); + g_prefix_error ( + error, _("Cannot get message %s: "), uid); + goto done; + } + + if (camel_stream_read (stream, buffer, 1, cancellable, error) == -1) + goto done; + + if (buffer[0] != '#') { + g_set_error ( + error, CAMEL_ERROR, CAMEL_ERROR_GENERIC, + _("Cannot get message %s: %s"), uid, + _("Unknown reason")); + goto done; + } + } + + message = camel_mime_message_new (); + if (stream != NULL && !camel_data_wrapper_construct_from_stream_sync ( + CAMEL_DATA_WRAPPER (message), stream, cancellable, error)) { + g_prefix_error (error, _("Cannot get message %s: "), uid); + g_object_unref (message); + message = NULL; + } else { + /* because the UID in the local store doesn't match with the UID in the pop3 store */ + camel_medium_add_header (CAMEL_MEDIUM (message), "X-Evolution-POP3-UID", uid); + } +done: + if (!already_locked) + camel_pop3_engine_busy_unlock (pop3_engine); + g_clear_object (&stream); +fail: + g_clear_object (&pop3_engine); + + camel_operation_pop_message (cancellable); + + return message; +} + +static CamelMimeMessage * +pop3_folder_get_message_sync (CamelFolder *folder, + const gchar *uid, + GCancellable *cancellable, + GError **error) +{ + return pop3_folder_get_message_internal_sync (folder, uid, FALSE, cancellable, error); +} + +static gboolean +pop3_folder_refresh_info_sync (CamelFolder *folder, + GCancellable *cancellable, + GError **error) +{ + CamelStore *parent_store; + CamelPOP3Store *pop3_store; + CamelPOP3Folder *pop3_folder = (CamelPOP3Folder *) folder; + CamelPOP3Engine *pop3_engine; + CamelPOP3Command *pcl, *pcu = NULL; + gboolean success = TRUE; + GError *local_error = NULL; + gint i; + + parent_store = camel_folder_get_parent_store (folder); + pop3_store = CAMEL_POP3_STORE (parent_store); + + if (camel_service_get_connection_status (CAMEL_SERVICE (parent_store)) != CAMEL_SERVICE_CONNECTED) { + g_set_error ( + error, CAMEL_SERVICE_ERROR, + CAMEL_SERVICE_ERROR_UNAVAILABLE, + _("You must be working online to complete this operation")); + return FALSE; + } + + pop3_engine = camel_pop3_store_ref_engine (pop3_store); + + if (!camel_pop3_engine_busy_lock (pop3_engine, cancellable, error)) { + g_clear_object (&pop3_engine); + return FALSE; + } + + camel_operation_push_message ( + cancellable, _("Retrieving POP summary")); + + /* Get rid of the old cache */ + if (pop3_folder->uids) { + gint i; + CamelPOP3FolderInfo *last_fi; + + if (pop3_folder->uids->len) { + last_fi = pop3_folder->uids->pdata[pop3_folder->uids->len - 1]; + if (last_fi) + pop3_folder->latest_id = last_fi->id; + else + pop3_folder->latest_id = -1; + } else + pop3_folder->latest_id = -1; + + for (i = 0; i < pop3_folder->uids->len; i++) { + CamelPOP3FolderInfo *fi = pop3_folder->uids->pdata[i]; + if (fi->cmd) { + camel_pop3_engine_command_free (pop3_engine, fi->cmd); + fi->cmd = NULL; + } + g_free (fi->uid); + g_free (fi); + } + + g_ptr_array_free (pop3_folder->uids, TRUE); + } + + if (pop3_folder->uids_fi) { + g_hash_table_destroy (pop3_folder->uids_fi); + pop3_folder->uids_fi = NULL; + } + + /* Get a new working set. */ + pop3_folder->uids = g_ptr_array_new (); + pop3_folder->uids_fi = g_hash_table_new (g_str_hash, g_str_equal); + + /* only used during setup */ + pop3_folder->uids_id = g_hash_table_new (NULL, NULL); + + pcl = camel_pop3_engine_command_new ( + pop3_engine, + CAMEL_POP3_COMMAND_MULTI, + cmd_list, folder, + cancellable, &local_error, + "LIST\r\n"); + if (!local_error && pop3_engine && (pop3_engine->capa & CAMEL_POP3_CAP_UIDL) != 0) + pcu = camel_pop3_engine_command_new ( + pop3_engine, + CAMEL_POP3_COMMAND_MULTI, + cmd_uidl, folder, + cancellable, &local_error, + "UIDL\r\n"); + while (!local_error && (i = camel_pop3_engine_iterate (pop3_engine, NULL, cancellable, &local_error)) > 0) + ; + + if (local_error) { + g_propagate_error (error, local_error); + g_prefix_error (error, _("Cannot get POP summary: ")); + success = FALSE; + } else if (i == -1) { + g_set_error_literal (error, CAMEL_ERROR, CAMEL_ERROR_GENERIC, _("Cannot get POP summary: ")); + success = FALSE; + } + + /* TODO: check every id has a uid & commands returned OK too? */ + + if (pcl) { + if (success && pcl->state == CAMEL_POP3_COMMAND_ERR) { + success = FALSE; + + if (pcl->error_str) + g_set_error_literal (error, CAMEL_ERROR, CAMEL_ERROR_GENERIC, pcl->error_str); + else + g_set_error_literal (error, CAMEL_ERROR, CAMEL_ERROR_GENERIC, _("Cannot get POP summary: ")); + } + + camel_pop3_engine_command_free (pop3_engine, pcl); + } + + if (pcu) { + if (success && pcu->state == CAMEL_POP3_COMMAND_ERR) { + success = FALSE; + + if (pcu->error_str) + g_set_error_literal (error, CAMEL_ERROR, CAMEL_ERROR_GENERIC, pcu->error_str); + else + g_set_error_literal (error, CAMEL_ERROR, CAMEL_ERROR_GENERIC, _("Cannot get POP summary: ")); + } + + camel_pop3_engine_command_free (pop3_engine, pcu); + } else { + for (i = 0; i < pop3_folder->uids->len; i++) { + CamelPOP3FolderInfo *fi = pop3_folder->uids->pdata[i]; + if (fi->cmd) { + if (success && fi->cmd->state == CAMEL_POP3_COMMAND_ERR) { + success = FALSE; + + if (fi->cmd->error_str) + g_set_error_literal (error, CAMEL_ERROR, CAMEL_ERROR_GENERIC, fi->cmd->error_str); + else + g_set_error_literal (error, CAMEL_ERROR, CAMEL_ERROR_GENERIC, _("Cannot get POP summary: ")); + } + + camel_pop3_engine_command_free (pop3_engine, fi->cmd); + fi->cmd = NULL; + } + if (fi->uid) { + g_hash_table_insert (pop3_folder->uids_fi, fi->uid, fi); + } + } + } + + /* dont need this anymore */ + g_hash_table_destroy (pop3_folder->uids_id); + pop3_folder->uids_id = NULL; + + camel_pop3_engine_busy_unlock (pop3_engine); + g_clear_object (&pop3_engine); + + camel_operation_pop_message (cancellable); + + return success; +} + +static gboolean +pop3_folder_synchronize_sync (CamelFolder *folder, + gboolean expunge, + GCancellable *cancellable, + GError **error) +{ + CamelService *service; + CamelSettings *settings; + CamelStore *parent_store; + CamelPOP3Folder *pop3_folder; + CamelPOP3Store *pop3_store; + CamelDataCache *pop3_cache; + CamelPOP3Engine *pop3_engine; + CamelPOP3FolderInfo *fi; + gint delete_after_days; + gboolean delete_expunged; + gboolean keep_on_server; + gboolean is_online; + gint i; + + parent_store = camel_folder_get_parent_store (folder); + + pop3_folder = CAMEL_POP3_FOLDER (folder); + pop3_store = CAMEL_POP3_STORE (parent_store); + + service = CAMEL_SERVICE (parent_store); + is_online = camel_service_get_connection_status (service) == CAMEL_SERVICE_CONNECTED; + + settings = camel_service_ref_settings (service); + + g_object_get ( + settings, + "delete-after-days", &delete_after_days, + "delete-expunged", &delete_expunged, + "keep-on-server", &keep_on_server, + NULL); + + g_object_unref (settings); + + if (is_online && delete_after_days > 0 && !expunge && !g_cancellable_is_cancelled (cancellable)) { + camel_operation_push_message ( + cancellable, _("Expunging old messages")); + + camel_pop3_folder_delete_old ( + folder, delete_after_days, cancellable, error); + + camel_operation_pop_message (cancellable); + } + + if (g_cancellable_is_cancelled (cancellable)) { + if (error && !*error) { + /* coverity[unchecked_value] */ + g_cancellable_set_error_if_cancelled (cancellable, error); + } + return FALSE; + } + + if (!expunge || (keep_on_server && !delete_expunged)) + return TRUE; + + if (!is_online) { + g_set_error ( + error, CAMEL_SERVICE_ERROR, + CAMEL_SERVICE_ERROR_UNAVAILABLE, + _("You must be working online to complete this operation")); + return FALSE; + } + + camel_operation_push_message ( + cancellable, _("Expunging deleted messages")); + + pop3_cache = camel_pop3_store_ref_cache (pop3_store); + pop3_engine = camel_pop3_store_ref_engine (pop3_store); + + if (!camel_pop3_engine_busy_lock (pop3_engine, cancellable, error)) { + g_clear_object (&pop3_cache); + g_clear_object (&pop3_engine); + + camel_operation_pop_message (cancellable); + + return FALSE; + } + + for (i = 0; i < pop3_folder->uids->len; i++) { + if (g_cancellable_set_error_if_cancelled (cancellable, error)) { + camel_pop3_engine_busy_unlock (pop3_engine); + g_clear_object (&pop3_cache); + g_clear_object (&pop3_engine); + + camel_operation_pop_message (cancellable); + + return FALSE; + } + + fi = pop3_folder->uids->pdata[i]; + /* busy already? wait for that to finish first */ + if (fi->cmd) { + while (camel_pop3_engine_iterate (pop3_engine, fi->cmd, cancellable, NULL) > 0) + ; + camel_pop3_engine_command_free (pop3_engine, fi->cmd); + fi->cmd = NULL; + } + + if (fi->flags & CAMEL_MESSAGE_DELETED) { + fi->cmd = camel_pop3_engine_command_new ( + pop3_engine, + 0, NULL, NULL, + cancellable, NULL, + "DELE %u\r\n", fi->id); + + /* also remove from cache */ + if (pop3_cache != NULL && fi->uid) + camel_data_cache_remove (pop3_cache, "cache", fi->uid, NULL); + } + } + + for (i = 0; i < pop3_folder->uids->len; i++) { + if (g_cancellable_set_error_if_cancelled (cancellable, error)) { + camel_pop3_engine_busy_unlock (pop3_engine); + g_clear_object (&pop3_cache); + g_clear_object (&pop3_engine); + + camel_operation_pop_message (cancellable); + + return FALSE; + } + + fi = pop3_folder->uids->pdata[i]; + /* wait for delete commands to finish */ + if (fi->cmd) { + while (camel_pop3_engine_iterate (pop3_engine, fi->cmd, cancellable, NULL) > 0) + ; + camel_pop3_engine_command_free (pop3_engine, fi->cmd); + fi->cmd = NULL; + } + camel_operation_progress ( + cancellable, (i + 1) * 100 / pop3_folder->uids->len); + } + + camel_pop3_engine_busy_unlock (pop3_engine); + g_clear_object (&pop3_cache); + g_clear_object (&pop3_engine); + + camel_operation_pop_message (cancellable); + + return camel_pop3_store_expunge (pop3_store, cancellable, error); +} + +static void +camel_pop3_folder_class_init (CamelPOP3FolderClass *class) +{ + GObjectClass *object_class; + CamelFolderClass *folder_class; + + object_class = G_OBJECT_CLASS (class); + object_class->dispose = pop3_folder_dispose; + + folder_class = CAMEL_FOLDER_CLASS (class); + folder_class->get_message_count = pop3_folder_get_message_count; + folder_class->get_uids = pop3_folder_get_uids; + folder_class->free_uids = camel_folder_free_shallow; + folder_class->get_uncached_uids = pop3_get_uncached_uids; + folder_class->get_filename = pop3_folder_get_filename; + folder_class->set_message_flags = pop3_folder_set_message_flags; + folder_class->get_message_sync = pop3_folder_get_message_sync; + folder_class->refresh_info_sync = pop3_folder_refresh_info_sync; + folder_class->synchronize_sync = pop3_folder_synchronize_sync; +} + +static void +camel_pop3_folder_init (CamelPOP3Folder *pop3_folder) +{ + pop3_folder->uids = g_ptr_array_new (); + pop3_folder->uids_fi = g_hash_table_new (g_str_hash, g_str_equal); +} + +CamelFolder * +camel_pop3_folder_new (CamelStore *parent, + GCancellable *cancellable, + GError **error) +{ + CamelFolder *folder; + CamelPOP3Folder *pop3_folder; + + d (printf ("opening pop3 INBOX folder\n")); + + folder = g_object_new ( + CAMEL_TYPE_POP3_FOLDER, + "full-name", "inbox", "display-name", "inbox", + "parent-store", parent, NULL); + + pop3_folder = (CamelPOP3Folder *) folder; + + pop3_folder->fetch_more = 0; + if (camel_service_get_connection_status (CAMEL_SERVICE (parent)) != CAMEL_SERVICE_CONNECTED) + return folder; + + /* mt-ok, since we dont have the folder-lock for new() */ + if (!camel_folder_refresh_info_sync (folder, cancellable, error)) { + g_object_unref (folder); + folder = NULL; + } + + return folder; +} + +static gboolean +pop3_get_message_time_from_cache (CamelFolder *folder, + const gchar *uid, + time_t *message_time) +{ + CamelStore *parent_store; + CamelPOP3Store *pop3_store; + CamelStream *stream = NULL; + gboolean res = FALSE; + + g_return_val_if_fail (folder != NULL, FALSE); + g_return_val_if_fail (uid != NULL, FALSE); + g_return_val_if_fail (message_time != NULL, FALSE); + + parent_store = camel_folder_get_parent_store (folder); + pop3_store = CAMEL_POP3_STORE (parent_store); + + stream = camel_pop3_store_cache_get (pop3_store, uid, NULL); + if (stream != NULL) { + CamelMimeMessage *message; + GError *error = NULL; + + message = camel_mime_message_new (); + camel_data_wrapper_construct_from_stream_sync ( + (CamelDataWrapper *) message, stream, NULL, &error); + if (error != NULL) { + g_warning (_("Cannot get message %s: %s"), uid, error->message); + g_error_free (error); + + g_object_unref (message); + message = NULL; + } + + if (message) { + res = TRUE; + *message_time = message->date + message->date_offset; + + g_object_unref (message); + } + + g_object_unref (stream); + } + + return res; +} + +gboolean +camel_pop3_folder_delete_old (CamelFolder *folder, + gint days_to_delete, + GCancellable *cancellable, + GError **error) +{ + CamelStore *parent_store; + CamelPOP3Folder *pop3_folder; + CamelPOP3FolderInfo *fi; + CamelPOP3Engine *pop3_engine; + CamelPOP3Store *pop3_store; + CamelDataCache *pop3_cache; + CamelMimeMessage *message; + time_t temp, message_time; + gint i; + + parent_store = camel_folder_get_parent_store (folder); + + if (camel_service_get_connection_status (CAMEL_SERVICE (parent_store)) != CAMEL_SERVICE_CONNECTED) { + g_set_error ( + error, CAMEL_SERVICE_ERROR, + CAMEL_SERVICE_ERROR_UNAVAILABLE, + _("You must be working online to complete this operation")); + return FALSE; + } + + if (g_cancellable_set_error_if_cancelled (cancellable, error)) + return FALSE; + + pop3_folder = CAMEL_POP3_FOLDER (folder); + pop3_store = CAMEL_POP3_STORE (parent_store); + pop3_cache = camel_pop3_store_ref_cache (pop3_store); + pop3_engine = camel_pop3_store_ref_engine (pop3_store); + + if (!camel_pop3_engine_busy_lock (pop3_engine, cancellable, error)) { + g_clear_object (&pop3_cache); + g_clear_object (&pop3_engine); + + return FALSE; + } + + temp = time (&temp); + + d (printf ("%s(%d): pop3_folder->uids->len=[%d]\n", __FILE__, __LINE__, pop3_folder->uids->len)); + for (i = 0; i < pop3_folder->uids->len; i++) { + message_time = 0; + fi = pop3_folder->uids->pdata[i]; + + if (g_cancellable_set_error_if_cancelled (cancellable, error)) { + camel_pop3_engine_busy_unlock (pop3_engine); + g_clear_object (&pop3_cache); + g_clear_object (&pop3_engine); + + return FALSE; + } + + if (fi->cmd) { + while (camel_pop3_engine_iterate (pop3_engine, fi->cmd, cancellable, NULL) > 0) { + ; /* do nothing - iterating until end */ + } + + camel_pop3_engine_command_free (pop3_engine, fi->cmd); + fi->cmd = NULL; + } + + /* continue, if message wasn't received yet */ + if (!fi->uid) + continue; + + d (printf ("%s(%d): fi->uid=[%s]\n", __FILE__, __LINE__, fi->uid)); + if (!pop3_get_message_time_from_cache (folder, fi->uid, &message_time)) { + d (printf ("could not get message time from cache, trying from pop3\n")); + message = pop3_folder_get_message_internal_sync ( + folder, fi->uid, TRUE, cancellable, error); + if (message) { + message_time = message->date + message->date_offset; + g_object_unref (message); + } + } + + if (message_time) { + gdouble time_diff = difftime (temp, message_time); + gint day_lag = time_diff / (60 * 60 * 24); + + d (printf ( + "%s(%d): message_time= [%" G_GINT64_FORMAT "]\n", + __FILE__, __LINE__, (gint64) message_time)); + d (printf ( + "%s(%d): day_lag=[%d] \t days_to_delete=[%d]\n", + __FILE__, __LINE__, day_lag, days_to_delete)); + + if (day_lag >= days_to_delete) { + if (g_cancellable_set_error_if_cancelled (cancellable, error)) { + camel_pop3_engine_busy_unlock (pop3_engine); + g_clear_object (&pop3_cache); + g_clear_object (&pop3_engine); + + return FALSE; + } + + if (fi->cmd) { + while (camel_pop3_engine_iterate (pop3_engine, fi->cmd, cancellable, NULL) > 0) { + ; /* do nothing - iterating until end */ + } + + camel_pop3_engine_command_free (pop3_engine, fi->cmd); + fi->cmd = NULL; + } + d (printf ( + "%s(%d): Deleting old messages\n", + __FILE__, __LINE__)); + fi->cmd = camel_pop3_engine_command_new ( + pop3_engine, + 0, NULL, NULL, + cancellable, NULL, + "DELE %u\r\n", fi->id); + /* also remove from cache */ + if (pop3_cache != NULL && fi->uid) { + camel_data_cache_remove (pop3_cache, "cache", fi->uid, NULL); + } + } + } + } + + for (i = 0; i < pop3_folder->uids->len; i++) { + if (g_cancellable_set_error_if_cancelled (cancellable, error)) { + camel_pop3_engine_busy_unlock (pop3_engine); + g_clear_object (&pop3_cache); + g_clear_object (&pop3_engine); + + return FALSE; + } + + fi = pop3_folder->uids->pdata[i]; + /* wait for delete commands to finish */ + if (fi->cmd) { + while (camel_pop3_engine_iterate (pop3_engine, fi->cmd, cancellable, NULL) > 0) + ; + camel_pop3_engine_command_free (pop3_engine, fi->cmd); + fi->cmd = NULL; + } + camel_operation_progress ( + cancellable, (i + 1) * 100 / pop3_folder->uids->len); + } + + camel_pop3_engine_busy_unlock (pop3_engine); + g_clear_object (&pop3_cache); + g_clear_object (&pop3_engine); + + return camel_pop3_store_expunge (pop3_store, cancellable, error); +} |