CamelIMAPXServer: moved handler code for untagged responses to individual functions
authorChristian Hilberg <chilberg@src.gnome.org>
Wed, 13 Jun 2012 11:07:36 +0000 (13:07 +0200)
committerMatthew Barnes <mbarnes@redhat.com>
Tue, 26 Jun 2012 15:20:28 +0000 (11:20 -0400)
* this is the first step towards an extensible CamelIMAPXServer
  w.r.t. untagged IMAP responses
* there is now one handler function per untagged response
* instead of one big switch statement, there will be a
  table with pointers to the new handler function, which
  will be indexed by the untagged response code
* this will eventually eliminate the switch statement
  in imapx_untagged()
* the gperf dependency will be dropped in the process

camel/camel-imapx-server.c
camel/camel-imapx-server.h

index 46d86a3..f6b6302 100644 (file)
@@ -167,6 +167,36 @@ enum {
        LAST_SIGNAL
 };
 
+/* structs for the untagged response handling */
+
+/* May need to turn this into separate,
+ * subclassable GObject with proper getter/setter
+ * functions so derived implementations can
+ * supply their own context information.
+ * The context supplied here, however, should
+ * not be exposed outside CamelIMAPXServer.
+ * An instance is created in imapx_untagged()
+ * with a lifetime of one run of this function.
+ * In order to supply a derived context instance,
+ * we would need to register a derived _new()
+ * function for it which will be called inside
+ * imapx_untagged().
+ *
+ * TODO: rethink this construct.
+ */
+typedef struct _CamelIMAPXServerUntaggedContext CamelIMAPXServerUntaggedContext;
+
+struct _CamelIMAPXServerUntaggedContext {
+       CamelSortType fetch_order;
+       guint id;
+       guint len;
+       guchar *token;
+       gint tok;
+       gboolean lsub;
+       struct _status_info *sinfo;
+};
+
+
 static guint signals[LAST_SIGNAL];
 
 void imapx_uidset_init (struct _uidset_state *ss, gint total, gint limit);
@@ -296,6 +326,12 @@ enum {
 
 static gboolean imapx_select (CamelIMAPXServer *is, CamelFolder *folder, gboolean force, GCancellable *cancellable, GError **error);
 
+typedef struct _CamelIMAPXServerPrivate CamelIMAPXServerPrivate;
+struct _CamelIMAPXServerPrivate {
+       CamelIMAPXServerUntaggedContext *context;
+};
+
+#define CAMEL_IMAPX_SERVER_GET_PRIVATE(obj)  (G_TYPE_INSTANCE_GET_PRIVATE ((obj), CAMEL_TYPE_IMAPX_SERVER, CamelIMAPXServerPrivate))
 G_DEFINE_TYPE (CamelIMAPXServer, camel_imapx_server, CAMEL_TYPE_OBJECT)
 
 static void
@@ -1063,561 +1099,809 @@ invalidate_local_cache (CamelIMAPXFolder *ifolder,
        camel_folder_change_info_free (changes);
 }
 
-/* handle any untagged responses */
+/* untagged response handler functions */
+
 static gboolean
-imapx_untagged (CamelIMAPXServer *is,
-                GCancellable *cancellable,
-                GError **error)
+imapx_untagged_capability (CamelIMAPXServer *is,
+                           GCancellable *cancellable,
+                           GError **error)
 {
-       CamelService *service;
-       CamelSettings *settings;
-       CamelSortType fetch_order;
-       guint id, len;
-       guchar *token, *p, c;
-       gint tok;
-       gboolean lsub = FALSE;
-       struct _status_info *sinfo;
+       g_assert (CAMEL_IS_IMAPX_SERVER (is));
+       /* cancellable may be NULL */
+       g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
 
-       service = CAMEL_SERVICE (is->store);
-       settings = camel_service_get_settings (service);
+       if (is->cinfo)
+               imapx_free_capability (is->cinfo);
+       is->cinfo = imapx_parse_capability (is->stream, cancellable, error);
+       if (is->cinfo == NULL)
+               return FALSE;
+       c(is->tagprefix, "got capability flags %08x\n", is->cinfo->capa);
+       return TRUE;
+}
 
-       fetch_order = camel_imapx_settings_get_fetch_order (
-               CAMEL_IMAPX_SETTINGS (settings));
+static gboolean
+imapx_untagged_expunge (CamelIMAPXServer *is,
+                        GCancellable *cancellable,
+                        GError **error)
+{
+       CamelIMAPXServerPrivate *priv = NULL;
+       guint32 expunge = 0;
+       CamelIMAPXJob *job = NULL;
+
+       g_assert (CAMEL_IS_IMAPX_SERVER (is));
+       /* cancellable may be NULL */
+       g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+
+       priv = CAMEL_IMAPX_SERVER_GET_PRIVATE (is);
+
+       expunge = priv->context->id;
+       job = imapx_match_active_job (is, IMAPX_JOB_EXPUNGE, NULL);
+
+       /* If there is a job running, let it handle the deletion */
+       if (job)
+               return TRUE;
+
+       c(is->tagprefix, "expunged: %d\n", priv->context->id);
+       if (is->select_folder) {
+               gchar *uid = NULL;
+
+               uid = imapx_get_uid_from_index (is->select_folder->summary, expunge - 1);
+               if (!uid)
+                       return TRUE;
+
+               imapx_expunge_uid_from_summary (is, uid, TRUE);
+       }
+
+       return TRUE;
+}
+
+static gboolean
+imapx_untagged_vanished (CamelIMAPXServer *is,
+                         GCancellable *cancellable,
+                         GError **error)
+{
+       GPtrArray *uids = NULL;
+       GList *uid_list = NULL;
+       gboolean unsolicited = TRUE;
+       gint i = 0;
+       guint len = 0;
+       guchar *token = NULL;
+       gint tok = 0;
+
+       g_assert (CAMEL_IS_IMAPX_SERVER (is));
+       /* cancellable may be NULL */
+       g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
 
-       e(is->tagprefix, "got untagged response\n");
-       id = 0;
        tok = camel_imapx_stream_token (is->stream, &token, &len, cancellable, error);
        if (tok < 0)
                return FALSE;
+       if (tok == '(') {
+               unsolicited = FALSE;
+               while (tok != ')') {
+                       /* We expect this to be 'EARLIER' */
+                       tok = camel_imapx_stream_token (is->stream, &token, &len, cancellable, error);
+                       if (tok < 0)
+                               return FALSE;
+               }
+       } else
+               camel_imapx_stream_ungettoken (is->stream, tok, token, len);
 
-       if (tok == IMAPX_TOK_INT) {
-               id = strtoul ((gchar *) token, NULL, 10);
-               tok = camel_imapx_stream_token (is->stream, &token, &len, cancellable, error);
-               if (tok < 0)
-                       return FALSE;
-       }
-
-       if (tok == '\n') {
-               g_set_error (
-                       error, CAMEL_IMAPX_ERROR, 1,
-                       "truncated server response");
+       uids = imapx_parse_uids (is->stream, cancellable, error);
+       if (uids == NULL)
                return FALSE;
+
+       if (unsolicited) {
+               CamelIMAPXFolder *ifolder = (CamelIMAPXFolder *) is->select_folder;
+
+               if (ifolder->exists_on_server < uids->len) {
+                       c(is->tagprefix, "Error: exists_on_folder %d is fewer than vanished %d\n",
+                         ifolder->exists_on_server, uids->len);
+                       ifolder->exists_on_server = 0;
+               } else
+                       ifolder->exists_on_server -= uids->len;
        }
+       if (is->changes == NULL)
+               is->changes = camel_folder_change_info_new ();
 
-       e(is->tagprefix, "Have token '%s' id %d\n", token, id);
-       p = token;
-       while ((c = *p))
-               *p++ = toupper((gchar) c);
+       for (i = 0; i < uids->len; i++) {
+               gchar *uid = g_strdup_printf("%u", GPOINTER_TO_UINT(g_ptr_array_index (uids, i)));
 
-       switch (imapx_tokenise ((const gchar *) token, len)) {
-       case IMAPX_CAPABILITY:
-               if (is->cinfo)
-                       imapx_free_capability (is->cinfo);
-               is->cinfo = imapx_parse_capability (is->stream, cancellable, error);
-               if (is->cinfo == NULL)
-                       return FALSE;
-               c(is->tagprefix, "got capability flags %08x\n", is->cinfo->capa);
-               return TRUE;
-       case IMAPX_EXPUNGE: {
-               guint32 expunge = id;
-               CamelIMAPXJob *job = imapx_match_active_job (is, IMAPX_JOB_EXPUNGE, NULL);
+               c(is->tagprefix, "vanished: %s\n", uid);
 
-               /* If there is a job running, let it handle the deletion */
-               if (job)
-                       break;
+               uid_list = g_list_append (uid_list, uid);
+               camel_folder_change_info_remove_uid (is->changes, uid);
+       }
+       camel_folder_summary_remove_uids (is->select_folder->summary, uid_list);
+       is->expunged = g_list_concat (is->expunged, uid_list);
+       g_ptr_array_free (uids, FALSE);
 
-               c(is->tagprefix, "expunged: %d\n", id);
-               if (is->select_folder) {
-                       gchar *uid = NULL;
+       return TRUE;
+}
 
-                       uid = imapx_get_uid_from_index (is->select_folder->summary, expunge - 1);
-                       if (!uid)
-                               break;
+static gboolean
+imapx_untagged_namespace (CamelIMAPXServer *is,
+                          GCancellable *cancellable,
+                          GError **error)
+{
+       CamelIMAPXNamespaceList *nsl = NULL;
+       CamelIMAPXStore *imapx_store = NULL;
+       CamelIMAPXStoreNamespace *ns = NULL;
 
-                       imapx_expunge_uid_from_summary (is, uid, TRUE);
-               }
+       g_assert (CAMEL_IS_IMAPX_SERVER (is));
+       /* cancellable may be NULL */
+       g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
 
-               break;
-       }
-       case IMAPX_VANISHED: {
-               GPtrArray *uids;
-               GList *uid_list = NULL;
-               gboolean unsolicited = TRUE;
-               gint i;
-               guint len;
-               guchar *token;
-               gint tok;
+       nsl = imapx_parse_namespace_list (is->stream, cancellable, error);
+       if (nsl == NULL)
+               return FALSE;
 
-               tok = camel_imapx_stream_token (is->stream, &token, &len, cancellable, error);
-               if (tok < 0)
-                       return FALSE;
-               if (tok == '(') {
-                       unsolicited = FALSE;
-                       while (tok != ')') {
-                               /* We expect this to be 'EARLIER' */
-                               tok = camel_imapx_stream_token (is->stream, &token, &len, cancellable, error);
-                               if (tok < 0)
-                                       return FALSE;
-                       }
-               } else
-                       camel_imapx_stream_ungettoken (is->stream, tok, token, len);
+       imapx_store = (CamelIMAPXStore *) is->store;
 
-               uids = imapx_parse_uids (is->stream, cancellable, error);
-               if (uids == NULL)
-                       return FALSE;
+       imapx_store->summary->namespaces = nsl;
+       camel_store_summary_touch ((CamelStoreSummary *) imapx_store->summary);
 
-               if (unsolicited) {
-                       CamelIMAPXFolder *ifolder = (CamelIMAPXFolder *) is->select_folder;
+       /* TODO Need to remove imapx_store->dir_sep to support multiple namespaces */
+       ns = nsl->personal;
+       if (ns)
+               imapx_store->dir_sep = ns->sep;
 
-                       if (ifolder->exists_on_server < uids->len) {
-                               c(is->tagprefix, "Error: exists_on_folder %d is fewer than vanished %d\n",
-                                 ifolder->exists_on_server, uids->len);
-                               ifolder->exists_on_server = 0;
-                       } else
-                               ifolder->exists_on_server -= uids->len;
-               }
-               if (is->changes == NULL)
-                       is->changes = camel_folder_change_info_new ();
+       return TRUE;
+}
 
-               for (i = 0; i < uids->len; i++) {
-                       gchar *uid = g_strdup_printf("%u", GPOINTER_TO_UINT(g_ptr_array_index (uids, i)));
+static gboolean
+imapx_untagged_exists (CamelIMAPXServer *is,
+                       GCancellable *cancellable,
+                       GError **error)
+{
+       CamelIMAPXServerPrivate *priv = NULL;
+       g_assert (CAMEL_IS_IMAPX_SERVER (is));
+       /* cancellable may be NULL */
+       g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
 
-                       c(is->tagprefix, "vanished: %s\n", uid);
+       priv = CAMEL_IMAPX_SERVER_GET_PRIVATE (is);
 
-                       uid_list = g_list_append (uid_list, uid);
-                       camel_folder_change_info_remove_uid (is->changes, uid);
-               }
-               camel_folder_summary_remove_uids (is->select_folder->summary, uid_list);
-               is->expunged = g_list_concat (is->expunged, uid_list);
-               g_ptr_array_free (uids, FALSE);
-               break;
+       c(is->tagprefix, "exists: %d\n", priv->context->id);
+       is->exists = priv->context->id;
+
+       if (is->select_folder)
+               ((CamelIMAPXFolder *) is->select_folder)->exists_on_server = priv->context->id;
+
+       if (imapx_idle_supported (is) && imapx_in_idle (is)) {
+               if (camel_folder_summary_count (is->select_folder->summary) < priv->context->id)
+                       imapx_stop_idle (is, error);
        }
-       case IMAPX_NAMESPACE: {
-               CamelIMAPXNamespaceList *nsl = NULL;
 
-               nsl = imapx_parse_namespace_list (is->stream, cancellable, error);
-               if (nsl != NULL) {
-                       CamelIMAPXStore *imapx_store = (CamelIMAPXStore *) is->store;
-                       CamelIMAPXStoreNamespace *ns;
+       return TRUE;
+}
 
-                       imapx_store->summary->namespaces = nsl;
-                       camel_store_summary_touch ((CamelStoreSummary *) imapx_store->summary);
+static gboolean
+imapx_untagged_flags (CamelIMAPXServer *is,
+                      GCancellable *cancellable,
+                      GError **error)
+{
+       guint32 flags;
 
-                       /* TODO Need to remove imapx_store->dir_sep to support multiple namespaces */
-                       ns = nsl->personal;
-                       if (ns)
-                               imapx_store->dir_sep = ns->sep;
-               }
+       g_assert (CAMEL_IS_IMAPX_SERVER (is));
+       /* cancellable may be NULL */
+       g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
 
-               return TRUE;
-       }
-       case IMAPX_EXISTS:
-               c(is->tagprefix, "exists: %d\n", id);
-               is->exists = id;
+       imapx_parse_flags (is->stream, &flags, NULL, cancellable, error);
+       c(is->tagprefix, "flags: %08x\n", flags);
 
-               if (is->select_folder)
-                       ((CamelIMAPXFolder *) is->select_folder)->exists_on_server = id;
+       return TRUE;
+}
 
-               if (imapx_idle_supported (is) && imapx_in_idle (is)) {
-                       if (camel_folder_summary_count (is->select_folder->summary) < id)
-                               imapx_stop_idle (is, error);
-               }
+static gboolean
+imapx_untagged_fetch (CamelIMAPXServer *is,
+                      GCancellable *cancellable,
+                      GError **error)
+{
+       CamelIMAPXServerPrivate *priv = NULL;
+       struct _fetch_info *finfo;
 
-               break;
-       case IMAPX_FLAGS: {
-               guint32 flags;
+       g_assert (CAMEL_IS_IMAPX_SERVER (is));
+       /* cancellable may be NULL */
+       g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
 
-               imapx_parse_flags (is->stream, &flags, NULL, cancellable, error);
+       priv = CAMEL_IMAPX_SERVER_GET_PRIVATE (is);
 
-               c(is->tagprefix, "flags: %08x\n", flags);
-               break;
+       finfo = imapx_parse_fetch (is->stream, cancellable, error);
+       if (finfo == NULL) {
+               imapx_free_fetch (finfo);
+               return FALSE;
        }
-       case IMAPX_FETCH: {
-               struct _fetch_info *finfo;
 
-               finfo = imapx_parse_fetch (is->stream, cancellable, error);
-               if (finfo == NULL) {
-                       imapx_free_fetch (finfo);
-                       return FALSE;
+       if ((finfo->got & (FETCH_BODY | FETCH_UID)) == (FETCH_BODY | FETCH_UID)) {
+               CamelIMAPXJob *job = imapx_match_active_job (is, IMAPX_JOB_GET_MESSAGE, finfo->uid);
+               GetMessageData *data;
+
+               data = camel_imapx_job_get_data (job);
+               g_return_val_if_fail (data != NULL, FALSE);
+
+               /* This must've been a get-message request, fill out the body stream,
+                * in the right spot */
+
+               if (job && job->error == NULL) {
+                       if (data->use_multi_fetch) {
+                               data->body_offset = finfo->offset;
+                               g_seekable_seek (G_SEEKABLE (data->stream), finfo->offset, G_SEEK_SET, NULL, NULL);
+                       }
+
+                       data->body_len = camel_stream_write_to_stream (finfo->body, data->stream, job->cancellable, &job->error);
+                       if (data->body_len == -1)
+                               g_prefix_error (&job->error,
+                                               _("Error writing to cache stream: "));
                }
+       }
 
-               if ((finfo->got & (FETCH_BODY | FETCH_UID)) == (FETCH_BODY | FETCH_UID)) {
-                       CamelIMAPXJob *job = imapx_match_active_job (is, IMAPX_JOB_GET_MESSAGE, finfo->uid);
-                       GetMessageData *data;
+       if ((finfo->got & FETCH_FLAGS) && !(finfo->got & FETCH_HEADER)) {
+               CamelIMAPXJob *job = imapx_match_active_job (is, IMAPX_JOB_FETCH_NEW_MESSAGES | IMAPX_JOB_REFRESH_INFO | IMAPX_JOB_FETCH_MESSAGES, NULL);
+               RefreshInfoData *data = NULL;
 
+               if (job) {
                        data = camel_imapx_job_get_data (job);
                        g_return_val_if_fail (data != NULL, FALSE);
+               }
 
-                       /* This must've been a get-message request, fill out the body stream,
-                        * in the right spot */
+               /* This is either a refresh_info job, check to see if it is and update
+                * if so, otherwise it must've been an unsolicited response, so update
+                * the summary to match */
+               if (data && (finfo->got & FETCH_UID) && data->scan_changes) {
+                       struct _refresh_info r;
+
+                       r.uid = finfo->uid;
+                       finfo->uid = NULL;
+                       r.server_flags = finfo->flags;
+                       r.server_user_flags = finfo->user_flags;
+                       finfo->user_flags = NULL;
+                       r.exists = FALSE;
+                       g_array_append_val (data->infos, r);
+               } else if (is->select_folder) {
+                       CamelFolder *folder;
+                       CamelMessageInfo *mi = NULL;
+                       gboolean changed = FALSE;
+                       gchar *uid = NULL;
+
+                       g_object_ref (is->select_folder);
+                       folder = is->select_folder;
+
+                       c(is->tagprefix, "flag changed: %d\n", priv->context->id);
 
-                       if (job && job->error == NULL) {
-                               if (data->use_multi_fetch) {
-                                       data->body_offset = finfo->offset;
-                                       g_seekable_seek (G_SEEKABLE (data->stream), finfo->offset, G_SEEK_SET, NULL, NULL);
+                       if (finfo->got & FETCH_UID) {
+                               uid = finfo->uid;
+                               finfo->uid = NULL;
+                       } else {
+                               uid = imapx_get_uid_from_index (folder->summary, priv->context->id - 1);
+                       }
+
+                       if (uid) {
+                               mi = camel_folder_summary_get (folder->summary, uid);
+                               if (mi) {
+                                       /* It's unsolicited _unless_ is->select_pending (i.e. during
+                                        * a QRESYNC SELECT */
+                                       changed = imapx_update_message_info_flags (mi, finfo->flags, finfo->user_flags, is->permanentflags, folder, !is->select_pending);
+                               } else {
+                                       /* This (UID + FLAGS for previously unknown message) might
+                                        * happen during a SELECT (QRESYNC). We should use it. */
+                                       c(is->tagprefix, "flags changed for unknown uid %s\n.", uid);
                                }
+                               finfo->user_flags = NULL;
+                       }
+
+                       if (changed) {
+                               if (is->changes == NULL)
+                                       is->changes = camel_folder_change_info_new ();
 
-                               data->body_len = camel_stream_write_to_stream (finfo->body, data->stream, job->cancellable, &job->error);
-                               if (data->body_len == -1)
-                                       g_prefix_error (
-                                               &job->error,
-                                               _("Error writing to cache stream: "));
+                               camel_folder_change_info_change_uid (is->changes, uid);
+                               g_free (uid);
                        }
+
+                       if (imapx_idle_supported (is) && changed && imapx_in_idle (is)) {
+                               camel_folder_summary_save_to_db (is->select_folder->summary, NULL);
+                               imapx_update_store_summary (is->select_folder);
+                               camel_folder_changed (is->select_folder, is->changes);
+                               camel_folder_change_info_clear (is->changes);
+                       }
+
+                       if (mi)
+                               camel_message_info_free (mi);
+                       g_object_unref (folder);
                }
+       }
 
-               if ((finfo->got & FETCH_FLAGS) && !(finfo->got & FETCH_HEADER)) {
-                       CamelIMAPXJob *job = imapx_match_active_job (is, IMAPX_JOB_FETCH_NEW_MESSAGES | IMAPX_JOB_REFRESH_INFO | IMAPX_JOB_FETCH_MESSAGES, NULL);
-                       RefreshInfoData *data = NULL;
+       if ((finfo->got & (FETCH_HEADER | FETCH_UID)) == (FETCH_HEADER | FETCH_UID)) {
+               CamelIMAPXJob *job = imapx_match_active_job (is, IMAPX_JOB_FETCH_NEW_MESSAGES | IMAPX_JOB_REFRESH_INFO | IMAPX_JOB_FETCH_MESSAGES, NULL);
 
-                       if (job) {
-                               data = camel_imapx_job_get_data (job);
-                               g_return_val_if_fail (data != NULL, FALSE);
-                       }
+               /* This must be a refresh info job as well, but it has asked for
+                * new messages to be added to the index */
 
-                       /* This is either a refresh_info job, check to see if it is and update
-                        * if so, otherwise it must've been an unsolicited response, so update
-                        * the summary to match */
-                       if (data && (finfo->got & FETCH_UID) && data->scan_changes) {
-                               struct _refresh_info r;
+               if (job) {
+                       CamelMimeParser *mp;
+                       CamelMessageInfo *mi;
 
-                               r.uid = finfo->uid;
-                               finfo->uid = NULL;
-                               r.server_flags = finfo->flags;
-                               r.server_user_flags = finfo->user_flags;
-                               finfo->user_flags = NULL;
-                               r.exists = FALSE;
-                               g_array_append_val (data->infos, r);
-                       } else if (is->select_folder) {
-                               CamelFolder *folder;
-                               CamelMessageInfo *mi = NULL;
-                               gboolean changed = FALSE;
-                               gchar *uid = NULL;
-
-                               g_object_ref (is->select_folder);
-                               folder = is->select_folder;
-
-                               c(is->tagprefix, "flag changed: %d\n", id);
-
-                               if (finfo->got & FETCH_UID) {
-                                       uid = finfo->uid;
-                                       finfo->uid = NULL;
+                       /* Do we want to save these headers for later too?  Do we care? */
+
+                       mp = camel_mime_parser_new ();
+                       camel_mime_parser_init_with_stream (mp, finfo->header, NULL);
+                       mi = camel_folder_summary_info_new_from_parser (job->folder->summary, mp);
+                       g_object_unref (mp);
+
+                       if (mi) {
+                               guint32 server_flags;
+                               CamelFlag *server_user_flags;
+                               CamelMessageInfoBase *binfo;
+                               gboolean free_user_flags = FALSE;
+
+                               mi->uid = camel_pstring_strdup (finfo->uid);
+
+                               if (!(finfo->got & FETCH_FLAGS)) {
+                                       RefreshInfoData *data;
+                                       struct _refresh_info *r = NULL;
+                                       gint min, max, mid;
+                                       gboolean found = FALSE;
+
+                                       data = camel_imapx_job_get_data (job);
+                                       g_return_val_if_fail (data != NULL, FALSE);
+
+                                       min = data->last_index;
+                                       max = data->index - 1;
+
+                                       /* array is sorted, so use a binary search */
+                                       do {
+                                               gint cmp = 0;
+
+                                               mid = (min + max) / 2;
+                                               r = &g_array_index (data->infos, struct _refresh_info, mid);
+                                               cmp = imapx_refresh_info_uid_cmp (finfo->uid, r->uid, priv->context->fetch_order == CAMEL_SORT_ASCENDING);
+
+                                               if (cmp > 0)
+                                                       min = mid + 1;
+                                               else if (cmp < 0)
+                                                       max = mid - 1;
+                                               else
+                                                       found = TRUE;
+
+                                       } while (!found && min <= max);
+
+                                       if (!found)
+                                               g_assert_not_reached ();
+
+                                       server_flags = r->server_flags;
+                                       server_user_flags = r->server_user_flags;
                                } else {
-                                       uid = imapx_get_uid_from_index (folder->summary, id - 1);
+                                       server_flags = finfo->flags;
+                                       server_user_flags = finfo->user_flags;
+                                       /* free user_flags ? */
+                                       finfo->user_flags = NULL;
+                                       free_user_flags = TRUE;
                                }
 
-                               if (uid) {
-                                       mi = camel_folder_summary_get (folder->summary, uid);
-                                       if (mi) {
-                                               /* It's unsolicited _unless_ is->select_pending (i.e. during
-                                                * a QRESYNC SELECT */
-                                               changed = imapx_update_message_info_flags (mi, finfo->flags, finfo->user_flags, is->permanentflags, folder, !is->select_pending);
+                               /* If the message is a really new one -- equal or higher than what
+                                * we know as UIDNEXT for the folder, then it came in since we last
+                                * fetched UIDNEXT and UNREAD count. We'll update UIDNEXT in the
+                                * command completion, but update UNREAD count now according to the
+                                * message SEEN flag */
+                               if (!(server_flags & CAMEL_MESSAGE_SEEN)) {
+                                       CamelIMAPXFolder *ifolder = (CamelIMAPXFolder *) job->folder;
+                                       guint64 uidl = strtoull (mi->uid, NULL, 10);
+
+                                       if (uidl >= ifolder->uidnext_on_server) {
+                                               c(is->tagprefix, "Updating unread count for new message %s\n", mi->uid);
+                                               ((CamelIMAPXFolder *) job->folder)->unread_on_server++;
                                        } else {
-                                               /* This (UID + FLAGS for previously unknown message) might
-                                                * happen during a SELECT (QRESYNC). We should use it. */
-                                               c(is->tagprefix, "flags changed for unknown uid %s\n.", uid);
+                                               c(is->tagprefix, "Not updating unread count for new message %s\n", mi->uid);
                                        }
-                                       finfo->user_flags = NULL;
                                }
 
-                               if (changed) {
-                                       if (is->changes == NULL)
-                                               is->changes = camel_folder_change_info_new ();
+                               binfo = (CamelMessageInfoBase *) mi;
+                               binfo->size = finfo->size;
 
-                                       camel_folder_change_info_change_uid (is->changes, uid);
-                                       g_free (uid);
-                               }
+                               if (!camel_folder_summary_check_uid (job->folder->summary, mi->uid)) {
+                                       RefreshInfoData *data;
+                                       CamelIMAPXFolder *ifolder = (CamelIMAPXFolder *) job->folder;
+                                       gint cnt;
+
+                                       data = camel_imapx_job_get_data (job);
+                                       g_return_val_if_fail (data != NULL, FALSE);
+
+                                       imapx_set_message_info_flags_for_new_message (mi, server_flags, server_user_flags, job->folder);
+                                       camel_folder_summary_add (job->folder->summary, mi);
+                                       camel_folder_change_info_add_uid (data->changes, mi->uid);
+
+                                       if (!g_hash_table_lookup (ifolder->ignore_recent, mi->uid)) {
+                                               camel_folder_change_info_recent_uid (data->changes, mi->uid);
+                                               g_hash_table_remove (ifolder->ignore_recent, mi->uid);
+                                       }
 
-                               if (imapx_idle_supported (is) && changed && imapx_in_idle (is)) {
-                                       camel_folder_summary_save_to_db (is->select_folder->summary, NULL);
-                                       imapx_update_store_summary (is->select_folder);
-                                       camel_folder_changed (is->select_folder, is->changes);
-                                       camel_folder_change_info_clear (is->changes);
+                                       cnt = (camel_folder_summary_count (job->folder->summary) * 100 ) / ifolder->exists_on_server;
+                                       camel_operation_progress (job->cancellable, cnt ? cnt : 1);
                                }
 
-                               if (mi)
-                                       camel_message_info_free (mi);
-                               g_object_unref (folder);
+                               if (free_user_flags && server_user_flags)
+                                       camel_flag_list_free (&server_user_flags);
+
                        }
                }
+       }
 
-               if ((finfo->got & (FETCH_HEADER | FETCH_UID)) == (FETCH_HEADER | FETCH_UID)) {
-                       CamelIMAPXJob *job = imapx_match_active_job (is, IMAPX_JOB_FETCH_NEW_MESSAGES | IMAPX_JOB_REFRESH_INFO | IMAPX_JOB_FETCH_MESSAGES, NULL);
+       imapx_free_fetch (finfo);
 
-                       /* This must be a refresh info job as well, but it has asked for
-                        * new messages to be added to the index */
+       return TRUE;
+}
 
-                       if (job) {
-                               CamelMimeParser *mp;
-                               CamelMessageInfo *mi;
+static gboolean
+imapx_untagged_lsub (CamelIMAPXServer *is,
+                     GCancellable *cancellable,
+                     GError **error)
+{
+       CamelIMAPXServerPrivate *priv = NULL;
 
-                               /* Do we want to save these headers for later too?  Do we care? */
+       g_assert (CAMEL_IS_IMAPX_SERVER (is));
+       /* cancellable may be NULL */
+       g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
 
-                               mp = camel_mime_parser_new ();
-                               camel_mime_parser_init_with_stream (mp, finfo->header, NULL);
-                               mi = camel_folder_summary_info_new_from_parser (job->folder->summary, mp);
-                               g_object_unref (mp);
+       priv = CAMEL_IMAPX_SERVER_GET_PRIVATE (is);
 
-                               if (mi) {
-                                       guint32 server_flags;
-                                       CamelFlag *server_user_flags;
-                                       CamelMessageInfoBase *binfo;
-                                       gboolean free_user_flags = FALSE;
+       priv->context->lsub = TRUE;
+       return TRUE;
+}
 
-                                       mi->uid = camel_pstring_strdup (finfo->uid);
+static gboolean
+imapx_untagged_list (CamelIMAPXServer *is,
+                     GCancellable *cancellable,
+                     GError **error)
+{
+       CamelIMAPXServerPrivate *priv = NULL;
+       struct _list_info *linfo = NULL;
+       CamelIMAPXJob *job = NULL;
+       ListData *data = NULL;
 
-                                       if (!(finfo->got & FETCH_FLAGS)) {
-                                               RefreshInfoData *data;
-                                               struct _refresh_info *r = NULL;
-                                               gint min, max, mid;
-                                               gboolean found = FALSE;
+       g_assert (CAMEL_IS_IMAPX_SERVER (is));
+       /* cancellable may be NULL */
+       g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
 
-                                               data = camel_imapx_job_get_data (job);
-                                               g_return_val_if_fail (data != NULL, FALSE);
+       priv = CAMEL_IMAPX_SERVER_GET_PRIVATE (is);
 
-                                               min = data->last_index;
-                                               max = data->index - 1;
+       linfo = imapx_parse_list (is->stream, cancellable, error);
+       if (!linfo)
+               return TRUE;
 
-                                               /* array is sorted, so use a binary search */
-                                               do {
-                                                       gint cmp = 0;
+       job = imapx_match_active_job (is, IMAPX_JOB_LIST, linfo->name);
 
-                                                       mid = (min + max) / 2;
-                                                       r = &g_array_index (data->infos, struct _refresh_info, mid);
-                                                       cmp = imapx_refresh_info_uid_cmp (finfo->uid, r->uid, fetch_order == CAMEL_SORT_ASCENDING);
+       data = camel_imapx_job_get_data (job);
+       g_return_val_if_fail (data != NULL, FALSE);
 
-                                                       if (cmp > 0)
-                                                               min = mid + 1;
-                                                       else if (cmp < 0)
-                                                               max = mid - 1;
-                                                       else
-                                                               found = TRUE;
+       // TODO: we want to make sure the names match?
 
-                                               } while (!found && min <= max);
+       if (data->flags & CAMEL_STORE_FOLDER_INFO_SUBSCRIBED) {
+               c(is->tagprefix, "lsub: '%s' (%c)\n", linfo->name, linfo->separator);
+       } else {
+               c(is->tagprefix, "list: '%s' (%c)\n", linfo->name, linfo->separator);
+       }
 
-                                               if (!found)
-                                                       g_assert_not_reached ();
+       if (job && g_hash_table_lookup (data->folders, linfo->name) == NULL) {
+               if (priv->context->lsub)
+                       linfo->flags |= CAMEL_FOLDER_SUBSCRIBED;
+               g_hash_table_insert (data->folders, linfo->name, linfo);
+       } else {
+               g_warning("got list response but no current listing job happening?\n");
+               imapx_free_list (linfo);
+       }
 
-                                               server_flags = r->server_flags;
-                                               server_user_flags = r->server_user_flags;
-                                       } else {
-                                               server_flags = finfo->flags;
-                                               server_user_flags = finfo->user_flags;
-                                               /* free user_flags ? */
-                                               finfo->user_flags = NULL;
-                                               free_user_flags = TRUE;
-                                       }
+       return TRUE;
+}
 
-                                       /* If the message is a really new one -- equal or higher than what
-                                        * we know as UIDNEXT for the folder, then it came in since we last
-                                        * fetched UIDNEXT and UNREAD count. We'll update UIDNEXT in the
-                                        * command completion, but update UNREAD count now according to the
-                                        * message SEEN flag */
-                                       if (!(server_flags & CAMEL_MESSAGE_SEEN)) {
-                                               CamelIMAPXFolder *ifolder = (CamelIMAPXFolder *) job->folder;
-                                               guint64 uidl = strtoull (mi->uid, NULL, 10);
-
-                                               if (uidl >= ifolder->uidnext_on_server) {
-                                                       c(is->tagprefix, "Updating unread count for new message %s\n", mi->uid);
-                                                       ((CamelIMAPXFolder *) job->folder)->unread_on_server++;
-                                               } else {
-                                                       c(is->tagprefix, "Not updating unread count for new message %s\n", mi->uid);
-                                               }
-                                       }
+static gboolean
+imapx_untagged_recent (CamelIMAPXServer *is,
+                       GCancellable *cancellable,
+                       GError **error)
+{
+       CamelIMAPXServerPrivate *priv = NULL;
 
-                                       binfo = (CamelMessageInfoBase *) mi;
-                                       binfo->size = finfo->size;
+       g_assert (CAMEL_IS_IMAPX_SERVER (is));
+       /* cancellable may be NULL */
+       g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
 
-                                       if (!camel_folder_summary_check_uid (job->folder->summary, mi->uid)) {
-                                               RefreshInfoData *data;
-                                               CamelIMAPXFolder *ifolder = (CamelIMAPXFolder *) job->folder;
-                                               gint cnt;
+       priv = CAMEL_IMAPX_SERVER_GET_PRIVATE (is);
 
-                                               data = camel_imapx_job_get_data (job);
-                                               g_return_val_if_fail (data != NULL, FALSE);
+       c(is->tagprefix, "recent: %d\n", priv->context->id);
+       is->recent = priv->context->id;
 
-                                               imapx_set_message_info_flags_for_new_message (mi, server_flags, server_user_flags, job->folder);
-                                               camel_folder_summary_add (job->folder->summary, mi);
-                                               camel_folder_change_info_add_uid (data->changes, mi->uid);
+       return TRUE;
+}
 
-                                               if (!g_hash_table_lookup (ifolder->ignore_recent, mi->uid)) {
-                                                       camel_folder_change_info_recent_uid (data->changes, mi->uid);
-                                                       g_hash_table_remove (ifolder->ignore_recent, mi->uid);
-                                               }
+static gboolean
+imapx_untagged_status (CamelIMAPXServer *is,
+                       GCancellable *cancellable,
+                       GError **error)
+{
+       struct _state_info *sinfo = NULL;
 
-                                               cnt = (camel_folder_summary_count (job->folder->summary) * 100 ) / ifolder->exists_on_server;
-                                               camel_operation_progress (job->cancellable, cnt ? cnt : 1);
-                                       }
+       g_assert (CAMEL_IS_IMAPX_SERVER (is));
+       /* cancellable may be NULL */
+       g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
 
-                                       if (free_user_flags && server_user_flags)
-                                               camel_flag_list_free (&server_user_flags);
+       sinfo = imapx_parse_status_info (is->stream, cancellable, error);
 
-                               }
+       if (sinfo) {
+               CamelIMAPXStoreSummary *s = ((CamelIMAPXStore *) is->store)->summary;
+               CamelIMAPXStoreNamespace *ns;
+               CamelIMAPXFolder *ifolder = NULL;;
+
+               ns = camel_imapx_store_summary_namespace_find_full (s, sinfo->name);
+               if (ns) {
+                       gchar *path_name;
+
+                       path_name = camel_imapx_store_summary_full_to_path (s, sinfo->name, ns->sep);
+                       c(is->tagprefix, "Got folder path '%s' for full '%s'\n", path_name, sinfo->name);
+                       if (path_name) {
+                               ifolder = (gpointer) camel_store_get_folder_sync (is->store, path_name, 0, cancellable, error);
+                               g_free (path_name);
                        }
                }
+               if (ifolder) {
+                       CamelFolder *cfolder = CAMEL_FOLDER (ifolder);
+
+                       ifolder->unread_on_server = sinfo->unseen;
+                       ifolder->exists_on_server = sinfo->messages;
+                       ifolder->modseq_on_server = sinfo->highestmodseq;
+                       ifolder->uidnext_on_server = sinfo->uidnext;
+                       ifolder->uidvalidity_on_server = sinfo->uidvalidity;
+                       if (sinfo->uidvalidity && sinfo->uidvalidity != ((CamelIMAPXSummary *) cfolder->summary)->validity)
+                               invalidate_local_cache (ifolder, sinfo->uidvalidity);
+               } else {
+                       c(is->tagprefix, "Received STATUS for unknown folder '%s'\n", sinfo->name);
+               }
 
-               imapx_free_fetch (finfo);
-               break;
+               g_free (sinfo->name);
+               g_free (sinfo);
        }
-       case IMAPX_LSUB:
-               lsub = TRUE;
-       case IMAPX_LIST: {
-               struct _list_info *linfo = imapx_parse_list (is->stream, cancellable, error);
-               CamelIMAPXJob *job;
-               ListData *data;
 
-               if (!linfo)
-                       break;
+       return TRUE;
+}
 
-               job = imapx_match_active_job (is, IMAPX_JOB_LIST, linfo->name);
+static gboolean
+imapx_untagged_bye (CamelIMAPXServer *is,
+                    GCancellable *cancellable,
+                    GError **error)
+{
+       guchar *token = NULL;
 
-               data = camel_imapx_job_get_data (job);
-               g_return_val_if_fail (data != NULL, FALSE);
+       g_assert (CAMEL_IS_IMAPX_SERVER (is));
+       /* cancellable may be NULL */
+       g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
 
-               // TODO: we want to make sure the names match?
+       if (camel_imapx_stream_text (is->stream, &token, cancellable, NULL)) {
+               c(is->tagprefix, "BYE: %s\n", token);
+               g_set_error (error, CAMEL_IMAPX_ERROR, 1,
+                            "IMAP server said BYE: %s", token);
+       }
+       is->state = IMAPX_SHUTDOWN;
 
-               if (data->flags & CAMEL_STORE_FOLDER_INFO_SUBSCRIBED) {
-                       c(is->tagprefix, "lsub: '%s' (%c)\n", linfo->name, linfo->separator);
+       return FALSE;
+}
 
-               } else {
-                       c(is->tagprefix, "list: '%s' (%c)\n", linfo->name, linfo->separator);
-               }
+static gboolean
+imapx_untagged_preauth (CamelIMAPXServer *is,
+                        GCancellable *cancellable,
+                        GError **error)
+{
+       g_assert (CAMEL_IS_IMAPX_SERVER (is));
+       /* cancellable may be NULL */
+       g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
 
-               if (job && g_hash_table_lookup (data->folders, linfo->name) == NULL) {
-                       if (lsub)
-                               linfo->flags |= CAMEL_FOLDER_SUBSCRIBED;
-                       g_hash_table_insert (data->folders, linfo->name, linfo);
-               } else {
-                       g_warning("got list response but no current listing job happening?\n");
-                       imapx_free_list (linfo);
+       c(is->tagprefix, "preauthenticated\n");
+       if (is->state < IMAPX_AUTHENTICATED)
+               is->state = IMAPX_AUTHENTICATED;
+
+       return TRUE;
+}
+
+static gboolean
+imapx_untagged_ok_no_bad (CamelIMAPXServer *is,
+                          GCancellable *cancellable,
+                          GError **error)
+{
+       CamelIMAPXServerPrivate *priv = NULL;
+
+       g_assert (CAMEL_IS_IMAPX_SERVER (is));
+       /* cancellable may be NULL */
+       g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+
+       priv = CAMEL_IMAPX_SERVER_GET_PRIVATE (is);
+
+       /* TODO: validate which ones of these can happen as unsolicited responses */
+       /* TODO: handle bye/preauth differently */
+       camel_imapx_stream_ungettoken (is->stream,
+                                      priv->context->tok,
+                                      priv->context->token,
+                                      priv->context->len);
+       priv->context->sinfo = imapx_parse_status (is->stream, cancellable, error);
+       if (priv->context->sinfo == NULL)
+               return FALSE;
+       switch (priv->context->sinfo->condition) {
+       case IMAPX_CLOSED:
+               c(is->tagprefix, "previously selected folder is now closed\n");
+               if (is->select_pending && !is->select_folder) {
+                       is->select_folder = is->select_pending;
                }
                break;
-       }
-       case IMAPX_RECENT:
-               c(is->tagprefix, "recent: %d\n", id);
-               is->recent = id;
+       case IMAPX_READ_WRITE:
+               is->mode = IMAPX_MODE_READ | IMAPX_MODE_WRITE;
+               c(is->tagprefix, "folder is read-write\n");
                break;
-       case IMAPX_STATUS: {
-               struct _state_info *sinfo = imapx_parse_status_info (is->stream, cancellable, error);
-               if (sinfo) {
-                       CamelIMAPXStoreSummary *s = ((CamelIMAPXStore *) is->store)->summary;
-                       CamelIMAPXStoreNamespace *ns;
-                       CamelIMAPXFolder *ifolder = NULL;;
-
-                       ns = camel_imapx_store_summary_namespace_find_full (s, sinfo->name);
-                       if (ns) {
-                               gchar *path_name;
-
-                               path_name = camel_imapx_store_summary_full_to_path (s, sinfo->name, ns->sep);
-                               c(is->tagprefix, "Got folder path '%s' for full '%s'\n", path_name, sinfo->name);
-                               if (path_name) {
-                                       ifolder = (gpointer) camel_store_get_folder_sync (is->store, path_name, 0, cancellable, error);
-                                       g_free (path_name);
-                               }
-                       }
-                       if (ifolder) {
-                               CamelFolder *cfolder = CAMEL_FOLDER (ifolder);
-
-                               ifolder->unread_on_server = sinfo->unseen;
-                               ifolder->exists_on_server = sinfo->messages;
-                               ifolder->modseq_on_server = sinfo->highestmodseq;
-                               ifolder->uidnext_on_server = sinfo->uidnext;
-                               ifolder->uidvalidity_on_server = sinfo->uidvalidity;
-                               if (sinfo->uidvalidity && sinfo->uidvalidity != ((CamelIMAPXSummary *) cfolder->summary)->validity)
-                                       invalidate_local_cache (ifolder, sinfo->uidvalidity);
-                       } else {
-                               c(is->tagprefix, "Received STATUS for unknown folder '%s'\n", sinfo->name);
-                       }
-
-                       g_free (sinfo->name);
-                       g_free (sinfo);
+       case IMAPX_READ_ONLY:
+               is->mode = IMAPX_MODE_READ;
+               c(is->tagprefix, "folder is read-only\n");
+               break;
+       case IMAPX_UIDVALIDITY:
+               is->uidvalidity = priv->context->sinfo->u.uidvalidity;
+               break;
+       case IMAPX_UNSEEN:
+               is->unseen = priv->context->sinfo->u.unseen;
+               break;
+       case IMAPX_HIGHESTMODSEQ:
+               is->highestmodseq = priv->context->sinfo->u.highestmodseq;
+               break;
+       case IMAPX_PERMANENTFLAGS:
+               is->permanentflags = priv->context->sinfo->u.permanentflags;
+               break;
+       case IMAPX_UIDNEXT:
+               is->uidnext = priv->context->sinfo->u.uidnext;
+               break;
+       case IMAPX_ALERT:
+               c(is->tagprefix, "ALERT!: %s\n", priv->context->sinfo->text);
+               break;
+       case IMAPX_PARSE:
+               c(is->tagprefix, "PARSE: %s\n", priv->context->sinfo->text);
+               break;
+       case IMAPX_CAPABILITY:
+               if (priv->context->sinfo->u.cinfo) {
+                       struct _capability_info *cinfo = is->cinfo;
+                       is->cinfo = priv->context->sinfo->u.cinfo;
+                       priv->context->sinfo->u.cinfo = NULL;
+                       if (cinfo)
+                               imapx_free_capability (cinfo);
+                       c(is->tagprefix, "got capability flags %08x\n", is->cinfo->capa);
                }
                break;
+       default:
+               break;
        }
-       case IMAPX_BYE: {
-               guchar *token;
+       imapx_free_status (priv->context->sinfo);
 
-               if (camel_imapx_stream_text (is->stream, &token, cancellable, NULL)) {
-                       c(is->tagprefix, "BYE: %s\n", token);
-                       g_set_error (
-                               error, CAMEL_IMAPX_ERROR, 1,
-                               "IMAP server said BYE: %s", token);
-               }
-               is->state = IMAPX_SHUTDOWN;
-               return FALSE;
+       return TRUE;
+}
+
+/* handle any untagged responses */
+static gboolean
+imapx_untagged (CamelIMAPXServer *is,
+                GCancellable *cancellable,
+                GError **error)
+{
+       CamelIMAPXServerPrivate *priv = NULL;
+       CamelService *service = NULL;
+       CamelSettings *settings = NULL;
+       guchar *p = NULL, c;
+       gboolean ok = FALSE;
+
+       service = CAMEL_SERVICE (is->store);
+       settings = camel_service_get_settings (service);
+       priv = CAMEL_IMAPX_SERVER_GET_PRIVATE (is);
+
+       if (priv->context != NULL)
+               g_free (priv->context);
+       priv->context = g_new0 (CamelIMAPXServerUntaggedContext, 1);
+
+       priv->context->lsub = FALSE;
+       priv->context->fetch_order = camel_imapx_settings_get_fetch_order (
+               CAMEL_IMAPX_SETTINGS (settings));
+
+       e(is->tagprefix, "got untagged response\n");
+       priv->context->id = 0;
+       priv->context->tok = camel_imapx_stream_token (is->stream,
+                                                      &(priv->context->token),
+                                                      &(priv->context->len),
+                                                      cancellable,
+                                                      error);
+       if (priv->context->tok < 0)
+               goto exit;
+
+       if (priv->context->tok == IMAPX_TOK_INT) {
+               priv->context->id = strtoul ((gchar *) priv->context->token, NULL, 10);
+               priv->context->tok = camel_imapx_stream_token (is->stream,
+                                                              &(priv->context->token),
+                                                              &(priv->context->len),
+                                                              cancellable,
+                                                              error);
+               if (priv->context->tok < 0)
+                       goto exit;
+       }
+
+       if (priv->context->tok == '\n') {
+               g_set_error (error, CAMEL_IMAPX_ERROR, 1,
+                       "truncated server response");
+               goto exit;
        }
+
+       e(is->tagprefix, "Have token '%s' id %d\n", priv->context->token, priv->context->id);
+       p = priv->context->token;
+       while ((c = *p))
+               *p++ = toupper((gchar) c);
+
+       switch (imapx_tokenise ((const gchar *) priv->context->token, priv->context->len)) {
+       case IMAPX_CAPABILITY:
+               ok = imapx_untagged_capability (is, cancellable, error);
+               goto exit;
+       case IMAPX_EXPUNGE:
+               ok = imapx_untagged_expunge (is, cancellable, error);
+               if (! ok)
+                       goto exit;
+               break;
+       case IMAPX_VANISHED:
+               ok = imapx_untagged_vanished (is, cancellable, error);
+               if (! ok)
+                       goto exit;
+               break;
+       case IMAPX_NAMESPACE:
+               ok = imapx_untagged_namespace (is, cancellable, error);
+               goto exit;
+       case IMAPX_EXISTS:
+               ok = imapx_untagged_exists (is, cancellable, error);
+               if (! ok)
+                       goto exit;
+               break;
+       case IMAPX_FLAGS:
+               ok = imapx_untagged_flags (is, cancellable, error);
+               if (! ok)
+                       goto exit;
+               break;
+       case IMAPX_FETCH:
+               ok = imapx_untagged_fetch (is, cancellable, error);
+               if (! ok)
+                       goto exit;
+               break;
+       case IMAPX_LSUB:
+               ok = imapx_untagged_lsub (is, cancellable, error);
+               if (! ok)
+                       goto exit;
+               /* fall through... */
+       case IMAPX_LIST:
+               ok = imapx_untagged_list (is, cancellable, error);
+               if (! ok)
+                       goto exit;
+               break;
+       case IMAPX_RECENT:
+               ok = imapx_untagged_recent (is, cancellable, error);
+               if (! ok)
+                       goto exit;
+               break;
+       case IMAPX_STATUS:
+               if (! imapx_untagged_status (is, cancellable, error))
+                       return FALSE;
+               break;
+       case IMAPX_BYE:
+               ok = imapx_untagged_bye (is, cancellable, error);
+               goto exit;
        case IMAPX_PREAUTH:
-               c(is->tagprefix, "preauthenticated\n");
-               if (is->state < IMAPX_AUTHENTICATED)
-                       is->state = IMAPX_AUTHENTICATED;
+               ok = imapx_untagged_preauth (is, cancellable, error);
+               if (! ok)
+                       goto exit;
                /* fall through... */
        case IMAPX_OK: case IMAPX_NO: case IMAPX_BAD:
-               /* TODO: validate which ones of these can happen as unsolicited responses */
-               /* TODO: handle bye/preauth differently */
-               camel_imapx_stream_ungettoken (is->stream, tok, token, len);
-               sinfo = imapx_parse_status (is->stream, cancellable, error);
-               if (sinfo == NULL)
-                       return FALSE;
-               switch (sinfo->condition) {
-               case IMAPX_CLOSED:
-                       c(is->tagprefix, "previously selected folder is now closed\n");
-                       if (is->select_pending && !is->select_folder) {
-                               is->select_folder = is->select_pending;
-                       }
-                       break;
-               case IMAPX_READ_WRITE:
-                       is->mode = IMAPX_MODE_READ | IMAPX_MODE_WRITE;
-                       c(is->tagprefix, "folder is read-write\n");
-                       break;
-               case IMAPX_READ_ONLY:
-                       is->mode = IMAPX_MODE_READ;
-                       c(is->tagprefix, "folder is read-only\n");
-                       break;
-               case IMAPX_UIDVALIDITY:
-                       is->uidvalidity = sinfo->u.uidvalidity;
-                       break;
-               case IMAPX_UNSEEN:
-                       is->unseen = sinfo->u.unseen;
-                       break;
-               case IMAPX_HIGHESTMODSEQ:
-                       is->highestmodseq = sinfo->u.highestmodseq;
-                       break;
-               case IMAPX_PERMANENTFLAGS:
-                       is->permanentflags = sinfo->u.permanentflags;
-                       break;
-               case IMAPX_UIDNEXT:
-                       is->uidnext = sinfo->u.uidnext;
-                       break;
-               case IMAPX_ALERT:
-                       c(is->tagprefix, "ALERT!: %s\n", sinfo->text);
-                       break;
-               case IMAPX_PARSE:
-                       c(is->tagprefix, "PARSE: %s\n", sinfo->text);
-                       break;
-               case IMAPX_CAPABILITY:
-                       if (sinfo->u.cinfo) {
-                               struct _capability_info *cinfo = is->cinfo;
-                               is->cinfo = sinfo->u.cinfo;
-                               sinfo->u.cinfo = NULL;
-                               if (cinfo)
-                                       imapx_free_capability (cinfo);
-                               c(is->tagprefix, "got capability flags %08x\n", is->cinfo->capa);
-                       }
-                       break;
-               default:
-                       break;
-               }
-               imapx_free_status (sinfo);
-               return TRUE;
+               ok = imapx_untagged_ok_no_bad (is, cancellable, error);
+               goto exit;
        default:
-               /* If there is a extended untagged response handler registered, call it */
-               if (is->untagged_handler_func)
-                       return is->untagged_handler_func (is, cancellable, error);
-
                /* unknown response, just ignore it */
-               c(is->tagprefix, "unknown token: %s\n", token);
+               c(is->tagprefix, "unknown token: %s\n", priv->context->token);
        }
 
-       return (camel_imapx_stream_skip (is->stream, cancellable, error) == 0);
+       ok = (camel_imapx_stream_skip (is->stream, cancellable, error) == 0);
+ exit:
+       g_free (priv->context);
+       priv->context = NULL;
+
+       return ok;
 }
 
 /* handle any continuation requests
@@ -3784,7 +4068,7 @@ imapx_command_step_fetch_done (CamelIMAPXServer *is,
                camel_imapx_command_set_job (ic, job);
                ic->pri = job->pri - 1;
 
-               //printf("Total: %d: %d, %d, %d\n", total, fetch_limit, i, data->last_index);   
+               //printf("Total: %d: %d, %d, %d\n", total, fetch_limit, i, data->last_index);
                data->last_index = i;
 
                /* If its mobile client and  when total=0 (new account setup) fetch only one batch of mails,
@@ -4048,7 +4332,7 @@ imapx_job_scan_changes_done (CamelIMAPXServer *is,
        /* There's no sane way to get the server-side unseen count on the
         * select mailbox. So just work it out from the flags if its not in
         * mobile mode. In mobile mode we would have this filled up already
-        * with a STATUS command. 
+        * with a STATUS command.
         **/
        if (!mobile_mode)
                ((CamelIMAPXFolder *) job->folder)->unread_on_server = camel_folder_summary_get_unread_count (job->folder->summary);
@@ -4305,16 +4589,16 @@ imapx_job_fetch_messages_start (CamelIMAPXJob *job,
                        /* This means that we are fetching limited number of new mails */
                        uid = g_strdup_printf("%d", total);
                } else {
-                       /* For empty accounts, we always fetch the specified number of new mails independent of 
+                       /* For empty accounts, we always fetch the specified number of new mails independent of
                         * being asked to fetch old or new.
                         */
-                       uid = g_strdup ("1"); 
+                       uid = g_strdup ("1");
                }
 
                if (ftype == CAMEL_FETCH_NEW_MESSAGES) {
                        /* We need to issue Status command to get the total unread count */
                        ic = camel_imapx_command_new (
-                               is, "STATUS", NULL, 
+                               is, "STATUS", NULL,
                                "STATUS %f (MESSAGES UNSEEN UIDVALIDITY UIDNEXT)", folder);
                        camel_imapx_command_set_job (ic, job);
                        ic->pri = job->pri;
@@ -4340,7 +4624,7 @@ imapx_job_fetch_messages_start (CamelIMAPXJob *job,
                        data->fetch_msg_limit, camel_folder_get_full_name (folder));
                /* New account and fetching old messages, we would return just the limited number of newest messages */
                ic = camel_imapx_command_new (
-                       is, "FETCH", job->folder, 
+                       is, "FETCH", job->folder,
                        "UID FETCH %s:* (UID FLAGS)", uid);
 
                imapx_uidset_init (&data->uidset, uidset_size, 0);
@@ -4515,7 +4799,7 @@ imapx_job_refresh_info_start (CamelIMAPXJob *job,
                CamelIMAPXCommand *ic;
 
                ic = camel_imapx_command_new (
-                       is, "STATUS", NULL, 
+                       is, "STATUS", NULL,
                        "STATUS %f (MESSAGES UNSEEN UIDVALIDITY UIDNEXT)", folder);
                camel_imapx_command_set_job (ic, job);
                ic->pri = job->pri;
@@ -5479,6 +5763,7 @@ static void
 imapx_server_finalize (GObject *object)
 {
        CamelIMAPXServer *is = CAMEL_IMAPX_SERVER (object);
+       CamelIMAPXServerPrivate *priv = CAMEL_IMAPX_SERVER_GET_PRIVATE (is);
 
        g_static_rec_mutex_free (&is->queue_lock);
        g_static_rec_mutex_free (&is->ostream_lock);
@@ -5487,6 +5772,9 @@ imapx_server_finalize (GObject *object)
 
        camel_folder_change_info_free (is->changes);
 
+       if (priv->context != NULL)
+               g_free (priv->context);
+
        /* Chain up to parent's finalize() method. */
        G_OBJECT_CLASS (camel_imapx_server_parent_class)->finalize (object);
 }
@@ -5519,6 +5807,8 @@ camel_imapx_server_class_init (CamelIMAPXServerClass *class)
        class->select_changed = NULL;
        class->shutdown = NULL;
 
+       g_type_class_add_private (class, sizeof (CamelIMAPXServerPrivate));
+
        /**
         * CamelIMAPXServer::select_changed
         * @server: the #CamelIMAPXServer which emitted the signal
@@ -5551,6 +5841,9 @@ camel_imapx_server_class_init (CamelIMAPXServerClass *class)
 static void
 camel_imapx_server_init (CamelIMAPXServer *is)
 {
+       CamelIMAPXServerPrivate *priv = CAMEL_IMAPX_SERVER_GET_PRIVATE (is);
+       priv->context = NULL;
+
        is->queue = camel_imapx_command_queue_new ();
        is->active = camel_imapx_command_queue_new ();
        is->done = camel_imapx_command_queue_new ();
@@ -6565,12 +6858,3 @@ camel_imapx_server_get_job_queue_info (CamelIMAPXServer *is)
 
        return jinfo;
 }
-
-void
-camel_imapx_server_set_extended_token_handler (CamelIMAPXServer *is,
-                                                 IMAPXExtUntaggedResponseHander handler_func)
-{
-       g_return_if_fail (is != NULL);
-
-       is->untagged_handler_func = handler_func;
-}
index f8bd937..54b42df 100644 (file)
@@ -79,8 +79,6 @@ struct _CamelIMAPXServer {
 
        CamelIMAPXNamespaceList *nsl;
 
-       IMAPXExtUntaggedResponseHander untagged_handler_func;
-
        /* incoming jobs */
        GQueue jobs;
 
@@ -240,10 +238,6 @@ struct _IMAPXJobQueueInfo *
                camel_imapx_server_get_job_queue_info
                                                (CamelIMAPXServer *is);
 
-void           camel_imapx_server_set_extended_token_handler
-                                               (CamelIMAPXServer *is,
-                                                IMAPXExtUntaggedResponseHander handler_func);
-
 G_END_DECLS
 
 #endif /* CAMEL_IMAPX_SERVER_H */