Bug #680497 - POP3 re-adds messages to local Inbox
authorMilan Crha <mcrha@redhat.com>
Thu, 25 Oct 2012 10:02:13 +0000 (12:02 +0200)
committerMilan Crha <mcrha@redhat.com>
Thu, 25 Oct 2012 10:02:13 +0000 (12:02 +0200)
camel/providers/pop3/camel-pop3-engine.c
camel/providers/pop3/camel-pop3-engine.h
camel/providers/pop3/camel-pop3-folder.c
camel/providers/pop3/camel-pop3-store.c

index 916fa95..7ee2843 100644 (file)
@@ -41,7 +41,7 @@ extern CamelServiceAuthType camel_pop3_apop_authtype;
 
 #define dd(x) (camel_debug ("pop3")?(x):0)
 
-static void get_capabilities (CamelPOP3Engine *pe, GCancellable *cancellable);
+static gboolean get_capabilities (CamelPOP3Engine *pe, GCancellable *cancellable, GError **error);
 
 G_DEFINE_TYPE (CamelPOP3Engine, camel_pop3_engine, CAMEL_TYPE_OBJECT)
 
@@ -124,6 +124,7 @@ read_greeting (CamelPOP3Engine *pe,
  * @source: source stream
  * @flags: engine flags
  * @cancellable: optional #GCancellable object, or %NULL
+ * @error: optional #GError, or %NULL
  *
  * Returns a NULL stream.  A null stream is always at eof, and
  * always returns success for all reads and writes.
@@ -133,7 +134,8 @@ read_greeting (CamelPOP3Engine *pe,
 CamelPOP3Engine *
 camel_pop3_engine_new (CamelStream *source,
                        guint32 flags,
-                       GCancellable *cancellable)
+                       GCancellable *cancellable,
+                      GError **error)
 {
        CamelPOP3Engine *pe;
 
@@ -143,13 +145,12 @@ camel_pop3_engine_new (CamelStream *source,
        pe->state = CAMEL_POP3_ENGINE_AUTH;
        pe->flags = flags;
 
-       if (read_greeting (pe, cancellable) == -1) {
+       if (read_greeting (pe, cancellable) == -1 ||
+           !get_capabilities (pe, cancellable, error)) {
                g_object_unref (pe);
                return NULL;
        }
 
-       get_capabilities (pe, cancellable);
-
        return pe;
 }
 
@@ -157,16 +158,18 @@ camel_pop3_engine_new (CamelStream *source,
  * camel_pop3_engine_reget_capabilities:
  * @engine: pop3 engine
  * @cancellable: optional #GCancellable object, or %NULL
+ * @error: optional #GError, or %NULL
  *
  * Regets server capabilities (needed after a STLS command is issued for example).
  **/
-void
+gboolean
 camel_pop3_engine_reget_capabilities (CamelPOP3Engine *engine,
-                                      GCancellable *cancellable)
+                                      GCancellable *cancellable,
+                                     GError **error)
 {
-       g_return_if_fail (CAMEL_IS_POP3_ENGINE (engine));
+       g_return_val_if_fail (CAMEL_IS_POP3_ENGINE (engine), FALSE);
 
-       get_capabilities (engine, cancellable);
+       return get_capabilities (engine, cancellable, error);
 }
 
 /* TODO: read implementation too?
@@ -186,6 +189,7 @@ static void
 cmd_capa (CamelPOP3Engine *pe,
           CamelPOP3Stream *stream,
           GCancellable *cancellable,
+         GError **error,
           gpointer data)
 {
        guchar *line, *tok, *next;
@@ -199,7 +203,7 @@ cmd_capa (CamelPOP3Engine *pe,
        g_return_if_fail (pe != NULL);
 
        do {
-               ret = camel_pop3_stream_line (stream, &line, &len, cancellable, NULL);
+               ret = camel_pop3_stream_line (stream, &line, &len, cancellable, error);
                if (ret >= 0) {
                        if (strncmp ((gchar *) line, "SASL ", 5) == 0) {
                                tok = line + 5;
@@ -227,13 +231,15 @@ cmd_capa (CamelPOP3Engine *pe,
        } while (ret > 0);
 }
 
-static void
+static gboolean
 get_capabilities (CamelPOP3Engine *pe,
-                  GCancellable *cancellable)
+                  GCancellable *cancellable,
+                 GError **error)
 {
        CamelPOP3Command *pc;
+       GError *local_error = NULL;
 
-       g_return_if_fail (pe != NULL);
+       g_return_val_if_fail (pe != NULL, FALSE);
 
        if (!(pe->flags & CAMEL_POP3_ENGINE_DISABLE_EXTENSIONS)) {
                pc = camel_pop3_engine_command_new (pe, CAMEL_POP3_COMMAND_MULTI, cmd_capa, NULL, cancellable, NULL, "CAPA\r\n");
@@ -243,8 +249,8 @@ get_capabilities (CamelPOP3Engine *pe,
 
                if (pe->state == CAMEL_POP3_ENGINE_TRANSACTION && !(pe->capa & CAMEL_POP3_CAP_UIDL)) {
                        /* check for UIDL support manually */
-                       pc = camel_pop3_engine_command_new (pe, CAMEL_POP3_COMMAND_SIMPLE, NULL, NULL, cancellable, NULL, "UIDL 1\r\n");
-                       while (camel_pop3_engine_iterate (pe, pc, cancellable, NULL) > 0)
+                       pc = camel_pop3_engine_command_new (pe, CAMEL_POP3_COMMAND_SIMPLE, NULL, NULL, cancellable, &local_error, "UIDL 1\r\n");
+                       while (camel_pop3_engine_iterate (pe, pc, cancellable, &local_error) > 0)
                                ;
 
                        if (pc->state == CAMEL_POP3_COMMAND_OK)
@@ -253,6 +259,13 @@ get_capabilities (CamelPOP3Engine *pe,
                        camel_pop3_engine_command_free (pe, pc);
                }
        }
+
+       if (local_error) {
+               g_propagate_error (error, local_error);
+               return FALSE;
+       }
+
+       return TRUE;
 }
 
 /* returns true if the command was sent, false if it was just queued */
@@ -324,23 +337,28 @@ camel_pop3_engine_iterate (CamelPOP3Engine *pe,
                        camel_pop3_stream_set_mode (pe->stream, CAMEL_POP3_STREAM_DATA);
 
                        if (pc->func)
-                               pc->func (pe, pe->stream, cancellable, pc->func_data);
+                               pc->func (pe, pe->stream, cancellable, error, pc->func_data);
 
                        /* Make sure we get all data before going back to command mode */
-                       while (camel_pop3_stream_getd (pe->stream, &p, &len, cancellable, NULL) > 0)
+                       while (camel_pop3_stream_getd (pe->stream, &p, &len, cancellable, error) > 0)
                                ;
                        camel_pop3_stream_set_mode (pe->stream, CAMEL_POP3_STREAM_LINE);
                } else {
                        pc->state = CAMEL_POP3_COMMAND_OK;
                }
                break;
-       case '-':
+       case '-': {
+               const gchar *text = (const gchar *) p;
+
                pc->state = CAMEL_POP3_COMMAND_ERR;
+               pc->error_str = g_strdup (g_ascii_strncasecmp (text, "-ERR ", 5) == 0 ? text + 5 : text + 1);
+               }
                break;
        default:
                /* what do we do now?  f'knows! */
                g_warning ("Bad server response: %s\n", p);
                pc->state = CAMEL_POP3_COMMAND_ERR;
+               pc->error_str = g_strdup ((const gchar *) p + 1);
                break;
        }
 
@@ -360,7 +378,7 @@ camel_pop3_engine_iterate (CamelPOP3Engine *pe,
                    && pe->current != NULL)
                        break;
 
-               if (camel_stream_write ((CamelStream *) pe->stream, pc->data, strlen (pc->data), cancellable, NULL) == -1)
+               if (camel_stream_write ((CamelStream *) pe->stream, pc->data, strlen (pc->data), cancellable, error) == -1)
                        goto ioerror;
 
                pe->sentlen += strlen (pc->data);
@@ -428,6 +446,7 @@ camel_pop3_engine_command_new (CamelPOP3Engine *pe,
        pc->data = g_strdup_vprintf (fmt, ap);
        va_end (ap);
        pc->state = CAMEL_POP3_COMMAND_IDLE;
+       pc->error_str = NULL;
 
        /* TODO: what about write errors? */
        engine_command_queue (pe, pc, cancellable, error);
@@ -441,6 +460,7 @@ camel_pop3_engine_command_free (CamelPOP3Engine *pe,
 {
        if (pe && pe->current != pc)
                g_queue_remove (&pe->done, pc);
+       g_free (pc->error_str);
        g_free (pc->data);
        g_free (pc);
 }
index 35ba1c7..cc58001 100644 (file)
@@ -94,11 +94,13 @@ enum {
 typedef void   (*CamelPOP3CommandFunc)         (CamelPOP3Engine *pe,
                                                 CamelPOP3Stream *stream,
                                                 GCancellable *cancellable,
+                                                GError **error,
                                                 gpointer data);
 
 struct _CamelPOP3Command {
        guint32 flags;
        camel_pop3_command_t state;
+       gchar *error_str;
 
        CamelPOP3CommandFunc func;
        gpointer func_data;
@@ -141,10 +143,12 @@ GType             camel_pop3_engine_get_type      (void);
 CamelPOP3Engine *
                camel_pop3_engine_new           (CamelStream *source,
                                                 guint32 flags,
-                                                GCancellable *cancellable);
-void           camel_pop3_engine_reget_capabilities
+                                                GCancellable *cancellable,
+                                                GError **error);
+gboolean       camel_pop3_engine_reget_capabilities
                                                (CamelPOP3Engine *engine,
-                                                GCancellable *cancellable);
+                                                GCancellable *cancellable,
+                                                GError **error);
 void           camel_pop3_engine_command_free  (CamelPOP3Engine *pe,
                                                 CamelPOP3Command *pc);
 gint           camel_pop3_engine_iterate       (CamelPOP3Engine *pe,
index 6eb6398..4c0a407 100644 (file)
@@ -63,6 +63,7 @@ static void
 cmd_uidl (CamelPOP3Engine *pe,
           CamelPOP3Stream *stream,
           GCancellable *cancellable,
+         GError **error,
           gpointer data)
 {
        gint ret;
@@ -74,7 +75,7 @@ cmd_uidl (CamelPOP3Engine *pe,
        CamelPOP3Folder *folder = data;
 
        do {
-               ret = camel_pop3_stream_line (stream, &line, &len, cancellable, NULL);
+               ret = camel_pop3_stream_line (stream, &line, &len, cancellable, error);
                if (ret >= 0) {
                        if (strlen ((gchar *) line) > 1024)
                                line[1024] = 0;
@@ -97,6 +98,7 @@ static void
 cmd_builduid (CamelPOP3Engine *pe,
               CamelPOP3Stream *stream,
               GCancellable *cancellable,
+             GError **error,
               gpointer data)
 {
        GChecksum *checksum;
@@ -145,6 +147,7 @@ static void
 cmd_list (CamelPOP3Engine *pe,
           CamelPOP3Stream *stream,
           GCancellable *cancellable,
+         GError **error,
           gpointer data)
 {
        gint ret;
@@ -173,7 +176,7 @@ cmd_list (CamelPOP3Engine *pe,
        g_object_unref (settings);
 
        do {
-               ret = camel_pop3_stream_line (stream, &line, &len, cancellable, NULL);
+               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));
@@ -185,7 +188,7 @@ cmd_list (CamelPOP3Engine *pe,
                                                pe,
                                                CAMEL_POP3_COMMAND_MULTI,
                                                cmd_builduid, fi,
-                                               cancellable, NULL,
+                                               cancellable, error,
                                                "TOP %u 0\r\n", id);
                                g_ptr_array_add (pop3_folder->uids, fi);
                                g_hash_table_insert (
@@ -319,22 +322,23 @@ 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 *error = NULL;
+       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, &error)) == -1)
+       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, &error)) > 0) {
-               n = camel_stream_write (fi->stream, buffer, n, cancellable, &error);
+       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;
 
@@ -346,17 +350,16 @@ cmd_tocache (CamelPOP3Engine *pe,
        }
 
        /* it all worked, output a '#' to say we're a-ok */
-       if (error == NULL) {
+       if (local_error == NULL) {
                g_seekable_seek (
                        G_SEEKABLE (fi->stream),
                        0, G_SEEK_SET, cancellable, NULL);
-               camel_stream_write (fi->stream, "#", 1, cancellable, &error);
+               camel_stream_write (fi->stream, "#", 1, cancellable, &local_error);
        }
 
 done:
-       if (error != NULL) {
-               g_warning ("POP3 retrieval failed: %s", error->message);
-               g_error_free (error);
+       if (local_error != NULL) {
+               g_propagate_error (error, local_error);
        }
 
        g_object_unref (fi->stream);
@@ -574,7 +577,7 @@ pop3_folder_get_message_sync (CamelFolder *folder,
                        pop3_store->engine,
                        CAMEL_POP3_COMMAND_MULTI,
                        cmd_tocache, fi,
-                       cancellable, NULL,
+                       cancellable, error,
                        "RETR %u\r\n", fi->id);
 
                /* Also initiate retrieval of some of the following
@@ -597,7 +600,7 @@ pop3_folder_get_message_sync (CamelFolder *folder,
                                                        pop3_store->engine,
                                                        CAMEL_POP3_COMMAND_MULTI,
                                                        cmd_tocache, pfi,
-                                                       cancellable, NULL,
+                                                       cancellable, error,
                                                        "RETR %u\r\n", pfi->id);
                                        }
                                }
@@ -730,28 +733,57 @@ pop3_folder_refresh_info_sync (CamelFolder *folder,
                        cmd_uidl, folder,
                        cancellable, &local_error,
                        "UIDL\r\n");
-       while ((i = camel_pop3_engine_iterate (pop3_store->engine, NULL, cancellable, error)) > 0)
+       while ((i = camel_pop3_engine_iterate (pop3_store->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_prefix_error (error, _("Cannot get POP summary: "));
+               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 (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_store->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_store->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_store->engine, fi->cmd);
                                fi->cmd = NULL;
                        }
index f721668..187d2e5 100644 (file)
@@ -102,6 +102,7 @@ connect_to_server (CamelService *service,
        gchar *host;
        guint32 flags = 0;
        gint ret;
+       GError *local_error = NULL;
 
        settings = camel_service_ref_settings (service);
 
@@ -133,11 +134,15 @@ connect_to_server (CamelService *service,
        if (disable_extensions)
                flags |= CAMEL_POP3_ENGINE_DISABLE_EXTENSIONS;
 
-       if (!(store->engine = camel_pop3_engine_new (tcp_stream, flags, cancellable))) {
-               g_set_error (
-                       error, CAMEL_ERROR, CAMEL_ERROR_GENERIC,
-                       _("Failed to read a valid greeting from POP server %s"),
-                       host);
+       if (!(store->engine = camel_pop3_engine_new (tcp_stream, flags, cancellable, &local_error)) ||
+           local_error != NULL) {
+               if (local_error)
+                       g_propagate_error (error, local_error);
+               else
+                       g_set_error (
+                               error, CAMEL_ERROR, CAMEL_ERROR_GENERIC,
+                               _("Failed to read a valid greeting from POP server %s"),
+                               host);
                g_object_unref (tcp_stream);
                success = FALSE;
                goto exit;
@@ -195,7 +200,8 @@ connect_to_server (CamelService *service,
 
        /* rfc2595, section 4 states that after a successful STLS
         * command, the client MUST discard prior CAPA responses */
-       camel_pop3_engine_reget_capabilities (store->engine, cancellable);
+       if (!camel_pop3_engine_reget_capabilities (store->engine, cancellable, error))
+               goto exception;
 
        goto exit;
 
@@ -212,6 +218,7 @@ stls_exception:
                camel_pop3_engine_command_free (store->engine, pc);
        }*/
 
+ exception:
        g_object_unref (store->engine);
        g_object_unref (tcp_stream);
        store->engine = NULL;
@@ -446,7 +453,8 @@ pop3_store_connect_sync (CamelService *service,
        /* Now that we are in the TRANSACTION state,
         * try regetting the capabilities */
        store->engine->state = CAMEL_POP3_ENGINE_TRANSACTION;
-       camel_pop3_engine_reget_capabilities (store->engine, cancellable);
+       if (!camel_pop3_engine_reget_capabilities (store->engine, cancellable, error))
+               success = FALSE;
 
 exit:
        g_free (mechanism);