2007-08-10 [colin] 2.10.0cvs110
[claws.git] / src / imap.c
index 543a5037c9aea0225f7374f7329f0df7c74c8fe7..9a0202dc7e8a191492daada3cd8ef1afe289aee0 100644 (file)
@@ -4,7 +4,7 @@
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
+ * the Free Software Foundation; either version 3 of the License, or
  * (at your option) any later version.
  *
  * This program is distributed in the hope that it will be useful,
@@ -13,8 +13,8 @@
  * GNU General Public License for more details.
  *
  * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ * 
  */
 
 #ifdef HAVE_CONFIG_H
@@ -108,6 +108,12 @@ struct _IMAPSession
        /* CLAWS */
        gboolean folder_content_changed;
        guint exists;
+       guint recent;
+       guint expunge;
+       guint unseen;
+       guint uid_validity;
+       guint uid_next;
+
        Folder * folder;
        gboolean busy;
        gboolean cancelled;
@@ -161,16 +167,12 @@ struct _IMAPFolderItem
        GSList *uid_list;
        gboolean batching;
 
-       time_t use_cache;
-       gint c_messages;
-       guint32 c_uid_next;
-       guint32 c_uid_validity;
-       gint c_unseen;
-
        GHashTable *flags_set_table;
        GHashTable *flags_unset_table;
        guint32 last_change;
        guint32 last_sync;
+       gboolean should_update;
+       gboolean should_trash_cache;
 };
 
 static XMLTag *imap_item_get_xml(Folder *folder, FolderItem *item);
@@ -198,6 +200,9 @@ static gchar   *imap_fetch_msg_full (Folder         *folder,
                                         gint            uid,
                                         gboolean        headers,
                                         gboolean        body);
+static void    imap_remove_cached_msg  (Folder         *folder, 
+                                        FolderItem     *item, 
+                                        MsgInfo        *msginfo);
 static gint    imap_add_msg            (Folder         *folder,
                                         FolderItem     *dest,
                                         const gchar    *file, 
@@ -303,7 +308,8 @@ static gchar imap_get_path_separator                (IMAPSession    *session,
 static gchar *imap_get_real_path               (IMAPSession    *session,
                                                 IMAPFolder     *folder,
                                                 const gchar    *path);
-static void imap_synchronise           (FolderItem     *item);
+static void imap_synchronise           (FolderItem     *item, gint days);
+static gboolean imap_is_busy           (Folder *folder);
 
 static void imap_free_capabilities     (IMAPSession    *session);
 
@@ -323,6 +329,7 @@ static gint imap_cmd_select (IMAPSession    *session,
                                 gint           *unseen,
                                 guint32        *uid_validity,
                                 gboolean        block);
+static gint imap_cmd_close     (IMAPSession    *session);
 static gint imap_cmd_examine   (IMAPSession    *session,
                                 const gchar    *folder,
                                 gint           *exists,
@@ -400,6 +407,8 @@ static GSList * imap_get_lep_set_from_numlist(MsgNumberList *numlist);
 static GSList * imap_get_lep_set_from_msglist(MsgInfoList *msglist);
 static GSList * imap_uid_list_from_lep(clist * list);
 static GSList * imap_uid_list_from_lep_tab(carray * list);
+static void imap_flags_hash_from_lep_uid_flags_tab(carray * list,
+                                                  GHashTable * hash);
 static MsgInfo *imap_envelope_from_lep(struct imap_fetch_env_info * info,
                                       FolderItem *item);
 static void imap_lep_set_free(GSList *seq_list);
@@ -468,6 +477,7 @@ FolderClass *imap_get_class(void)
                imap_class.get_flags = imap_get_flags;
                imap_class.set_batch = imap_set_batch;
                imap_class.synchronise = imap_synchronise;
+               imap_class.remove_cached_msg = imap_remove_cached_msg;
 #ifdef USE_PTREAD
                pthread_mutex_init(&imap_mutex, NULL);
 #endif
@@ -788,8 +798,8 @@ static IMAPSession *imap_session_get(Folder *folder)
                        debug_print("disconnected!\n");
                        session = imap_reconnect_if_possible(folder, session);
                }
-               
-               session->cancelled = FALSE;
+               if (session)
+                       session->cancelled = FALSE;
        }
 
        rfolder->session = SESSION(session);
@@ -892,6 +902,9 @@ static IMAPSession *imap_session_new(Folder * folder,
        
        session->authenticated = authenticated;
        session->mbox = NULL;
+       session->exists = 0;
+       session->recent = 0;
+       session->expunge = 0;
        session->cmd_count = 0;
        session->folder = folder;
        IMAP_FOLDER(session->folder)->last_seen_separator = 0;
@@ -1003,6 +1016,26 @@ static guint get_file_size_with_crs(const gchar *filename)
        return cnt;
 }
 
+static void imap_remove_cached_msg(Folder *folder, FolderItem *item, MsgInfo *msginfo)
+{
+       gchar *path, *filename;
+
+       path = folder_item_get_path(item);
+
+       if (!is_dir_exist(path)) {
+               g_free(path);
+               return;
+       }
+
+       filename = g_strconcat(path, G_DIR_SEPARATOR_S, itos(msginfo->msgnum), NULL);
+       g_free(path);
+
+       if (is_file_exist(filename)) {
+               g_unlink(filename);
+       }
+       g_free(filename);
+}
+
 static gchar *imap_fetch_msg_full(Folder *folder, FolderItem *item, gint uid,
                                  gboolean headers, gboolean body)
 {
@@ -1158,7 +1191,7 @@ static gint imap_add_msgs(Folder *folder, FolderItem *dest, GSList *file_list,
        MsgFileInfo *fileinfo;
        gint ok;
        gint curnum = 0, total = 0;
-
+       gboolean missing_uids = FALSE;
 
        g_return_val_if_fail(folder != NULL, -1);
        g_return_val_if_fail(dest != NULL, -1);
@@ -1215,6 +1248,11 @@ static gint imap_add_msgs(Folder *folder, FolderItem *dest, GSList *file_list,
                        debug_print("appended new message as %d\n", new_uid);
                        /* put the local file in the imapcache, so that we don't
                         * have to fetch it back later. */
+                       
+                       if (new_uid == 0) {
+                               missing_uids = TRUE;
+                               debug_print("Missing UID (0)\n");
+                       }
                        if (new_uid > 0) {
                                gchar *cache_path = folder_item_get_path(dest);
                                if (!is_dir_exist(cache_path))
@@ -1224,7 +1262,7 @@ static gint imap_add_msgs(Folder *folder, FolderItem *dest, GSList *file_list,
                                                cache_path, G_DIR_SEPARATOR_S, 
                                                itos(new_uid), NULL);
                                        copy_file(real_file, cache_file, TRUE);
-                                       debug_print("copied to cache: %s\n", cache_file);
+                                       debug_print("got UID %d, copied to cache: %s\n", new_uid, cache_file);
                                        g_free(cache_file);
                                }
                                g_free(cache_path);
@@ -1234,27 +1272,57 @@ static gint imap_add_msgs(Folder *folder, FolderItem *dest, GSList *file_list,
                if (relation != NULL)
                        g_relation_insert(relation, fileinfo->msginfo != NULL ? 
                                          (gpointer) fileinfo->msginfo : (gpointer) fileinfo,
-                                         GINT_TO_POINTER(dest->last_num + 1));
-               if (new_uid == 0) {
-                       new_uid = dest->last_num+1;
-               }
+                                         GINT_TO_POINTER(new_uid));
                if (last_uid < new_uid) {
                        last_uid = new_uid;
                }
 
                g_free(real_file);
        }
+       
        statusbar_progress_all(0,0,0);
        statusbar_pop_all();
        
-       imap_cmd_expunge(session);
        unlock_session(session);
        
        g_free(destdir);
 
+       imap_scan_required(folder, dest);
+
+       if (missing_uids) {
+               gint a;
+               ok = imap_select(session, IMAP_FOLDER(folder), dest->path,
+                        &a, NULL, NULL, NULL, FALSE);
+       }
        return last_uid;
 }
 
+static GSList *flatten_mailimap_set(struct mailimap_set * set) 
+{
+       GSList *result = NULL;
+       clistiter *list;
+       int start, end, t;
+       GSList *cur;
+
+       for (list = clist_begin(set->set_list); list; list = clist_next(list)) {
+               struct mailimap_set_item *item = (struct mailimap_set_item *)clist_content(list);
+               start = item->set_first;
+               end = item->set_last;
+               for (t = start; t <= end; t++) {
+                       result = g_slist_prepend(result, GINT_TO_POINTER(t));
+               }
+       }
+       result = g_slist_reverse(result);
+       if (debug_get_mode()) {
+               debug_print("flat imap set: ");
+               for (cur = result; cur; cur = cur->next) {
+                       debug_print("%d ", GPOINTER_TO_INT(cur->data));
+               }
+               debug_print("\n");
+       }
+       
+       return result;
+}
 static gint imap_do_copy_msgs(Folder *folder, FolderItem *dest, 
                              MsgInfoList *msglist, GRelation *relation)
 {
@@ -1337,16 +1405,23 @@ static gint imap_do_copy_msgs(Folder *folder, FolderItem *dest,
                        &source, &dest);
                
                if (ok == IMAP_SUCCESS) {
-                       if (single && relation && source && dest) {
-                               clistiter *l = clist_begin(source->set_list);
-                               struct mailimap_set_item *i = (struct mailimap_set_item *)clist_content(l);
-                               int snum = i->set_first;
-                               int dnum = 0;
-                               l = clist_begin(dest->set_list);
-                               i = (struct mailimap_set_item *)clist_content(l);
-                               dnum = i->set_first;
-                               g_relation_insert(uid_mapping, GINT_TO_POINTER(snum), 
-                                       GINT_TO_POINTER(dnum));
+                       if (relation && source && dest) {
+                               GSList *s_list = flatten_mailimap_set(source);
+                               GSList *d_list = flatten_mailimap_set(dest);
+                               GSList *s_cur, *d_cur;
+                               if (g_slist_length(s_list) == g_slist_length(d_list)) {
+
+                                       for (s_cur = s_list, d_cur = d_list; 
+                                            s_cur && d_cur; 
+                                            s_cur = s_cur->next, d_cur = d_cur->next) {
+                                               g_relation_insert(uid_mapping, s_cur->data, d_cur->data);
+                                       }
+
+                               } else {
+                                       debug_print("hhhmm, source list length != dest list length.\n");
+                               }
+                               g_slist_free(s_list);
+                               g_slist_free(d_list);
                        }
                }
 
@@ -1378,7 +1453,7 @@ static gint imap_do_copy_msgs(Folder *folder, FolderItem *dest,
                                          GINT_TO_POINTER(num));
                        if (num > last_num)
                                last_num = num;
-                       debug_print("copied new message as %d\n", num);
+                       debug_print("copied message %d as %d\n", msginfo->msgnum, num);
                        /* put the local file in the imapcache, so that we don't
                         * have to fetch it back later. */
                        if (num > 0) {
@@ -2140,6 +2215,7 @@ static FolderItem *imap_create_folder(Folder *folder, FolderItem *parent,
 
        if (exist) {
                /* folder existed, scan it */
+               imap_scan_required(folder, new_item);
                folder_item_scan_full(new_item, FALSE);
        }
 
@@ -2184,6 +2260,9 @@ static gint imap_rename_folder(Folder *folder, FolderItem *item,
 
        g_free(session->mbox);
        session->mbox = NULL;
+       session->exists = 0;
+       session->recent = 0;
+       session->expunge = 0;
        ok = imap_cmd_examine(session, "INBOX",
                              &exists, &recent, &unseen, &uid_validity, FALSE);
        if (ok != IMAP_SUCCESS) {
@@ -2273,6 +2352,7 @@ static gint imap_remove_folder_real(Folder *folder, FolderItem *item)
        IMAPSession *session;
        gchar *path;
        gchar *cache_dir;
+       gboolean selected_folder;
 
        g_return_val_if_fail(folder != NULL, -1);
        g_return_val_if_fail(item != NULL, -1);
@@ -2286,6 +2366,16 @@ static gint imap_remove_folder_real(Folder *folder, FolderItem *item)
        path = imap_get_real_path(session, IMAP_FOLDER(folder), item->path);
 
        imap_threaded_subscribe(folder, path, FALSE);
+
+       selected_folder = (session->mbox != NULL) &&
+                         (!strcmp(session->mbox, item->path));
+       if (selected_folder) {
+               ok = imap_cmd_close(session);
+               if (ok != MAILIMAP_NO_ERROR) {
+                       debug_print("close err %d\n", ok);
+                       return IMAP_ERROR;
+               }
+       }
        ok = imap_cmd_delete(session, path);
        if (ok != IMAP_SUCCESS) {
                gchar *tmp = g_strdup_printf("%s%c", path, 
@@ -2656,6 +2746,9 @@ static gint imap_select(IMAPSession *session, IMAPFolder *folder,
 
        g_free(session->mbox);
        session->mbox = NULL;
+       session->exists = 0;
+       session->recent = 0;
+       session->expunge = 0;
 
        real_path = imap_get_real_path(session, folder, path);
 
@@ -2666,6 +2759,14 @@ static gint imap_select(IMAPSession *session, IMAPFolder *folder,
        else {
                session->mbox = g_strdup(path);
                session->folder_content_changed = FALSE;
+               session->exists = *exists;
+               session->recent = *recent;
+               session->expunge = 0;
+               session->unseen = *unseen;
+               session->uid_validity = *uid_validity;
+               debug_print("select: exists %d recent %d expunge %d uid_validity %d\n", 
+                       session->exists, session->recent, session->expunge,
+                       session->uid_validity);
        }
        g_free(real_path);
 
@@ -2703,6 +2804,16 @@ static gint imap_status(IMAPSession *session, IMAPFolder *folder,
                mask |= 1 << 4;
                *unseen = 0;
        }
+       
+       if (session->mbox != NULL &&
+           !strcmp(session->mbox, item->item.path)) {
+               r = imap_cmd_close(session);
+               if (r != MAILIMAP_NO_ERROR) {
+                       debug_print("close err %d\n", r);
+                       return IMAP_ERROR;
+               }
+       }
+       
        r = imap_threaded_status(FOLDER(folder), real_path, 
                &data_status, mask);
 
@@ -2829,14 +2940,36 @@ static gint imap_cmd_login(IMAPSession *session,
 static gint imap_cmd_noop(IMAPSession *session)
 {
        int r;
-       unsigned int exists;
+       unsigned int exists, recent, expunge, unseen, uidnext, uidval;
        
-       r = imap_threaded_noop(session->folder, &exists);
+       r = imap_threaded_noop(session->folder, &exists, &recent, &expunge, &unseen, &uidnext, &uidval);
        if (r != MAILIMAP_NO_ERROR) {
                debug_print("noop err %d\n", r);
                return IMAP_ERROR;
        }
+
+       session->folder_content_changed = FALSE;
+
+       if ((exists && exists != session->exists)
+        || (recent && recent != session->recent)
+        || (expunge && expunge != session->expunge)
+        || (unseen && unseen != session->unseen)) {
+               session->folder_content_changed = TRUE;
+       }
+       if (uidnext != 0 && uidnext != session->uid_next) {
+               session->uid_next = uidnext;
+               session->folder_content_changed = TRUE;
+       }
+       if (uidval != 0 && uidval != session->uid_validity) {
+               session->uid_validity = uidval;
+               session->folder_content_changed = TRUE;
+       }
+
        session->exists = exists;
+       session->recent = recent;
+       session->expunge = expunge;
+       session->unseen = unseen;
+
        session_set_access_time(SESSION(session));
 
        return IMAP_SUCCESS;
@@ -2872,6 +3005,23 @@ static gint imap_cmd_select(IMAPSession *session, const gchar *folder,
        return IMAP_SUCCESS;
 }
 
+static gint imap_cmd_close(IMAPSession *session)
+{
+       int r;
+
+       r = imap_threaded_close(session->folder);
+       if (r != MAILIMAP_NO_ERROR) {
+               debug_print("close err %d\n", r);
+               return IMAP_ERROR;
+       }
+       g_free(session->mbox);
+       session->mbox = NULL;
+       session->exists = 0;
+       session->recent = 0;
+       session->expunge = 0;
+       return IMAP_SUCCESS;
+}
+
 static gint imap_cmd_examine(IMAPSession *session, const gchar *folder,
                             gint *exists, gint *recent, gint *unseen,
                             guint32 *uid_validity, gboolean block)
@@ -3329,8 +3479,8 @@ static void *get_list_of_uids_thread(void *data)
        gint ok, nummsgs = 0, lastuid_old;
        IMAPSession *session;
        GSList *uidlist, *elem;
+       int r = -1;
        clist * lep_uidlist;
-       int r;
 
        session = stuff->session;
        if (session == NULL) {
@@ -3345,51 +3495,49 @@ static void *get_list_of_uids_thread(void *data)
                return GINT_TO_POINTER(-1);
        }
 
+       g_slist_free(item->uid_list);
+       item->uid_list = NULL;
+
        uidlist = NULL;
        
-       r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_SIMPLE, NULL,
+       if (folder->account && folder->account->low_bandwidth) {
+               r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_SIMPLE, NULL,
                                 &lep_uidlist);
+       }
        
        if (r == MAILIMAP_NO_ERROR) {
-               GSList * fetchuid_list;
-               
-               fetchuid_list =
+               GSList * fetchuid_list =
                        imap_uid_list_from_lep(lep_uidlist);
                mailimap_search_result_free(lep_uidlist);
                
                uidlist = g_slist_concat(fetchuid_list, uidlist);
-       }
-       else {
-               GSList * fetchuid_list;
+       } else {
                carray * lep_uidtab;
-               
-               r = imap_threaded_fetch_uid(folder, item->lastuid + 1,
-                                           &lep_uidtab);
+               r = imap_threaded_fetch_uid(folder, 1,
+                                   &lep_uidtab);
                if (r == MAILIMAP_NO_ERROR) {
-                       fetchuid_list =
+                       GSList * fetchuid_list =
                                imap_uid_list_from_lep_tab(lep_uidtab);
                        imap_fetch_uid_list_free(lep_uidtab);
                        uidlist = g_slist_concat(fetchuid_list, uidlist);
                }
        }
        
+       if (r != MAILIMAP_NO_ERROR) {
+               stuff->done = TRUE;
+               return GINT_TO_POINTER(-1);
+       }
+
        lastuid_old = item->lastuid;
-       *msgnum_list = g_slist_copy(item->uid_list);
-       nummsgs = g_slist_length(*msgnum_list);
-       debug_print("Got %d uids from cache\n", g_slist_length(item->uid_list));
 
        for (elem = uidlist; elem != NULL; elem = g_slist_next(elem)) {
                guint msgnum;
 
                msgnum = GPOINTER_TO_INT(elem->data);
-               if (msgnum > lastuid_old) {
-                       *msgnum_list = g_slist_prepend(*msgnum_list, GINT_TO_POINTER(msgnum));
-                       item->uid_list = g_slist_prepend(item->uid_list, GINT_TO_POINTER(msgnum));
-                       nummsgs++;
 
-                       if(msgnum > item->lastuid)
-                               item->lastuid = msgnum;
-               }
+               *msgnum_list = g_slist_prepend(*msgnum_list, GINT_TO_POINTER(msgnum));
+               item->uid_list = g_slist_prepend(item->uid_list, GINT_TO_POINTER(msgnum));
+               nummsgs++;
        }
        g_slist_free(uidlist);
        stuff->done = TRUE;
@@ -3423,11 +3571,11 @@ gint imap_get_num_list(Folder *folder, FolderItem *_item, GSList **msgnum_list,
 {
        IMAPFolderItem *item = (IMAPFolderItem *)_item;
        IMAPSession *session;
-       gint ok, nummsgs = 0, exists;
-       guint32 uid_next = 0, uid_val = 0;
+       gint nummsgs;
        GSList *uidlist = NULL;
        gchar *dir;
        gboolean selected_folder;
+       gint known_list_len = 0;
        debug_print("get_num_list\n");
        
        g_return_val_if_fail(folder != NULL, -1);
@@ -3436,6 +3584,18 @@ gint imap_get_num_list(Folder *folder, FolderItem *_item, GSList **msgnum_list,
        g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
        g_return_val_if_fail(folder->account != NULL, -1);
 
+       known_list_len = g_slist_length(item->uid_list);
+       if (!item->should_update) {
+               debug_print("get_num_list: nothing to update\n");
+               *old_uids_valid = TRUE;
+               if (known_list_len == item->item.total_msgs) {
+                       *msgnum_list = g_slist_copy(item->uid_list);
+                       return known_list_len;
+               } else {
+                       debug_print("don't know the list length...\n");
+               }
+       }
+       
        debug_print("getting session...\n");
        session = imap_session_get(folder);
        g_return_val_if_fail(session != NULL, -1);
@@ -3451,93 +3611,23 @@ gint imap_get_num_list(Folder *folder, FolderItem *_item, GSList **msgnum_list,
 
        selected_folder = (session->mbox != NULL) &&
                          (!strcmp(session->mbox, item->item.path));
-       if (selected_folder && time(NULL) - item->use_cache < 2) {
-               ok = imap_cmd_noop(session);
-               if (ok != IMAP_SUCCESS) {
-                       debug_print("disconnected!\n");
-                       session = imap_reconnect_if_possible(folder, session);
-                       if (session == NULL) {
-                               statusbar_pop_all();
-                               unlock_session(session);
-                               return -1;
-                       }
-               }
-               exists = session->exists;
+       
+       if (item->should_trash_cache) {
+               *old_uids_valid = FALSE;
+               debug_print("get_num_list: trashing num list\n");
+               debug_print("Freeing imap uid cache\n");
+               item->lastuid = 0;
+               g_slist_free(item->uid_list);
+               item->uid_list = NULL;
 
-               uid_next = item->c_uid_next;
-               uid_val = item->c_uid_validity;
-               *old_uids_valid = TRUE;
+               imap_delete_all_cached_messages((FolderItem *)item);
        } else {
-               if (item->use_cache && time(NULL) - item->use_cache < 2) {
-                       exists = item->c_messages;
-                       uid_next = item->c_uid_next;
-                       uid_val = item->c_uid_validity;
-                       ok = IMAP_SUCCESS;
-                       debug_print("using cache %d %d %d\n", exists, uid_next, uid_val);
-               } else {
-                       ok = imap_status(session, IMAP_FOLDER(folder), item->item.path, item,
-                                &exists, &uid_next, &uid_val, NULL, FALSE);
-               }
-               item->item.last_num = uid_next - 1;
-               
-               item->use_cache = (time_t)0;
-               if (ok != IMAP_SUCCESS) {
-                       statusbar_pop_all();
-                       unlock_session(session);
-                       return -1;
-               }
-               if(item->item.mtime == uid_val)
-                       *old_uids_valid = TRUE;
-               else {
-                       *old_uids_valid = FALSE;
-
-                       debug_print("Freeing imap uid cache (%d != %d)\n",
-                                       (int)item->item.mtime, uid_val);
-                       item->lastuid = 0;
-                       g_slist_free(item->uid_list);
-                       item->uid_list = NULL;
-               
-                       item->item.mtime = uid_val;
-
-                       imap_delete_all_cached_messages((FolderItem *)item);
-               }
-       }
-
-       /* If old uid_next matches new uid_next we can be sure no message
-          was added to the folder */
-       debug_print("uid_next is %d and item->uid_next %d \n", 
-               uid_next, item->uid_next);
-       if (uid_next == item->uid_next) {
-               nummsgs = g_slist_length(item->uid_list);
-
-               /* If number of messages is still the same we
-                   know our caches message numbers are still valid,
-                   otherwise if the number of messages has decrease
-                  we discard our cache to start a new scan to find
-                  out which numbers have been removed */
-               if (exists == nummsgs) {
-                       debug_print("exists == nummsgs\n");
-                       *msgnum_list = g_slist_copy(item->uid_list);
-                       statusbar_pop_all();
-                       unlock_session(session);
-                       return nummsgs;
-               } else if (exists < nummsgs) {
-                       debug_print("Freeing imap uid cache");
-                       item->lastuid = 0;
-                       g_slist_free(item->uid_list);
-                       item->uid_list = NULL;
-               }
-       }
-
-       if (exists == 0) {
-               *msgnum_list = NULL;
-               statusbar_pop_all();
-               unlock_session(session);
-               return 0;
+               debug_print("get_num_list: updating num list\n");
+               *old_uids_valid = TRUE;
        }
 
-       item->last_change = time(NULL);
        nummsgs = get_list_of_uids(session, folder, item, &uidlist);
+       debug_print("get_num_list: got %d msgs\n", nummsgs);
 
        if (nummsgs < 0) {
                statusbar_pop_all();
@@ -3545,21 +3635,6 @@ gint imap_get_num_list(Folder *folder, FolderItem *_item, GSList **msgnum_list,
                return -1;
        }
 
-       if (nummsgs != exists) {
-               /* Cache contains more messages then folder, we have cached
-                   an old UID of a message that was removed and new messages
-                   have been added too, otherwise the uid_next check would
-                  not have failed */
-               debug_print("Freeing imap uid cache");
-               item->lastuid = 0;
-               g_slist_free(item->uid_list);
-               item->uid_list = NULL;
-
-               g_slist_free(*msgnum_list);
-
-               nummsgs = get_list_of_uids(session, folder, item, &uidlist);
-       }
-
        *msgnum_list = uidlist;
 
        dir = folder_item_get_path((FolderItem *)item);
@@ -3567,11 +3642,11 @@ gint imap_get_num_list(Folder *folder, FolderItem *_item, GSList **msgnum_list,
        remove_numbered_files_not_in_list(dir, *msgnum_list);
        g_free(dir);
        
-       item->uid_next = uid_next;
-       
        debug_print("get_num_list - ok - %i\n", nummsgs);
        statusbar_pop_all();
        unlock_session(session);
+       item->should_trash_cache = FALSE;
+       item->should_update = FALSE;
        return nummsgs;
 }
 
@@ -3649,8 +3724,9 @@ GSList *imap_get_msginfos(Folder *folder, FolderItem *item,
                                int i;
                                for (i = startnum; i <= lastnum; ++i) {
                                        gchar *file;
-                       
+                                       unlock_session(session);
                                        file = imap_fetch_msg(folder, item, i);
+                                       lock_session(session);
                                        if (file != NULL) {
                                                MsgInfo *msginfo = imap_parse_msg(file, item);
                                                if (msginfo != NULL) {
@@ -3715,24 +3791,45 @@ gboolean imap_scan_required(Folder *folder, FolderItem *_item)
        if (item->item.path == NULL)
                return FALSE;
 
+       if (item->should_update) {
+               debug_print("scan already required\n");
+               return TRUE;
+       }
        debug_print("getting session...\n");
        session = imap_session_get(folder);
        g_return_val_if_fail(session != NULL, FALSE);
 
        selected_folder = (session->mbox != NULL) &&
                          (!strcmp(session->mbox, item->item.path));
-       if (selected_folder && time(NULL) - item->use_cache < 2) {
-               ok = imap_cmd_noop(session);
-               if (ok != IMAP_SUCCESS) {
-                       debug_print("disconnected!\n");
-                       session = imap_reconnect_if_possible(folder, session);
-                       if (session == NULL)
-                               return FALSE;
-               }
+       if (selected_folder) {
+               if (!session->folder_content_changed) {
+                       ok = imap_cmd_noop(session);
+                       if (ok != IMAP_SUCCESS) {
+                               debug_print("disconnected!\n");
+                               session = imap_reconnect_if_possible(folder, session);
+                               if (session == NULL)
+                                       return FALSE;
+                       }
+
+                       if (session->folder_content_changed) {
+                               debug_print("CHANGED (self-noop)! scan_required\n");
+                               unlock_session(session);
+                               item->should_update = TRUE;
+                               if (session->uid_validity && session->uid_validity != item->item.mtime) {
+                                       item->item.mtime = session->uid_validity;
+                                       item->should_trash_cache = TRUE;
+                               }
 
-               if (session->folder_content_changed
-               ||  session->exists != item->item.total_msgs) {
+                               return TRUE;
+                       }
+               } else {
+                       debug_print("CHANGED (previous noop)! scan_required\n");
                        unlock_session(session);
+                       item->should_update = TRUE;
+                       if (session->uid_validity && session->uid_validity != item->item.mtime) {
+                               item->item.mtime = session->uid_validity;
+                               item->should_trash_cache = TRUE;
+                       }
                        return TRUE;
                }
        } else {
@@ -3743,21 +3840,25 @@ gboolean imap_scan_required(Folder *folder, FolderItem *_item)
                        return FALSE;
                }
 
-               item->use_cache = time(NULL);
-               item->c_messages = exists;
-               item->c_uid_next = uid_next;
-               item->c_uid_validity = uid_val;
-               item->c_unseen = unseen;
-               item->item.last_num = uid_next - 1;
-               debug_print("uidnext %d, item->uid_next %d, exists %d, item->item.total_msgs %d\n", 
-                       uid_next, item->uid_next, exists, item->item.total_msgs);
-               if ((uid_next != item->uid_next) || (exists != item->item.total_msgs)
-                   || unseen != item->item.unread_msgs || uid_val != item->item.mtime) {
+               debug_print("exists %d, item->item.total_msgs %d\n", 
+                       exists, item->item.total_msgs);
+               if (exists != item->item.total_msgs
+                   || unseen != item->item.unread_msgs 
+                   || uid_next != item->uid_next
+                   || uid_val != item->item.mtime) {
                        unlock_session(session);
+                       debug_print("CHANGED (status)! scan_required\n");
                        item->last_change = time(NULL);
+                       item->should_update = TRUE;
+                       item->uid_next = uid_next;
+                       if (uid_val != item->item.mtime) {
+                               item->item.mtime = uid_val;
+                               item->should_trash_cache = TRUE;
+                       }
                        return TRUE;
                }
        }
+       item->should_update = FALSE;
        unlock_session(session);
        return FALSE;
 }
@@ -3972,39 +4073,34 @@ static /*gint*/ void *imap_get_flags_thread(void *data)
 {
        get_flags_data *stuff = (get_flags_data *)data;
        Folder *folder = stuff->folder;
-       FolderItem *item = stuff->item;
+       FolderItem *fitem = (FolderItem *) stuff->item;
        MsgInfoList *msginfo_list = stuff->msginfo_list;
        GRelation *msgflags = stuff->msgflags;
-       gboolean full_search = stuff->full_search;
+       GSList *elem;
+       carray * lep_uidtab;
        IMAPSession *session;
+       gint ok;
+       int r;
+       GHashTable *flags_hash = NULL;
+       gboolean full_search = stuff->full_search;
        GSList *sorted_list = NULL;
        GSList *unseen = NULL, *answered = NULL, *flagged = NULL, *deleted = NULL;
        GSList *p_unseen, *p_answered, *p_flagged, *p_deleted;
-       GSList *elem;
        GSList *seq_list, *cur;
        gboolean reverse_seen = FALSE;
-       GString *cmd_buf;
-       gint ok;
-       gint exists_cnt, unseen_cnt;
        gboolean selected_folder;
+       gint exists_cnt, unseen_cnt;
        
-       if (folder == NULL || item == NULL) {
-               stuff->done = TRUE;
-               return GINT_TO_POINTER(-1);
-       }
-
-       debug_print("getting session...\n");
        session = imap_session_get(folder);
        if (session == NULL) {
                stuff->done = TRUE;
                return GINT_TO_POINTER(-1);
        }
-
        selected_folder = (session->mbox != NULL) &&
-                         (!strcmp(session->mbox, item->path));
+                         (!strcmp(session->mbox, fitem->path));
 
        if (!selected_folder) {
-               ok = imap_select(session, IMAP_FOLDER(folder), item->path,
+               ok = imap_select(session, IMAP_FOLDER(folder), fitem->path,
                        &exists_cnt, NULL, &unseen_cnt, NULL, TRUE);
                if (ok != IMAP_SUCCESS) {
                        stuff->done = TRUE;
@@ -4016,12 +4112,10 @@ static /*gint*/ void *imap_get_flags_thread(void *data)
                        reverse_seen = TRUE;
        } 
        else {
-               if (item->unread_msgs > item->total_msgs / 2)
+               if (fitem->unread_msgs > fitem->total_msgs / 2)
                        reverse_seen = TRUE;
        }
 
-       cmd_buf = g_string_new(NULL);
-
        sorted_list = g_slist_sort(g_slist_copy(msginfo_list), compare_msginfo);
        if (!full_search) {
                seq_list = imap_get_lep_set_from_msglist(msginfo_list);
@@ -4031,54 +4125,32 @@ static /*gint*/ void *imap_get_flags_thread(void *data)
                seq_list = g_slist_append(NULL, set);
        }
 
-       for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
-               struct mailimap_set * imapset;
-               clist * lep_uidlist;
-               int r;
-               
-               imapset = cur->data;
-               if (reverse_seen) {
-                       r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_SEEN,
-                                                full_search ? NULL:imapset, &lep_uidlist);
-               }
-               else {
-                       r = imap_threaded_search(folder,
-                                                IMAP_SEARCH_TYPE_UNSEEN,
-                                                full_search ? NULL:imapset, &lep_uidlist);
-               }
-               if (r == MAILIMAP_NO_ERROR) {
-                       GSList * uidlist;
-                       
-                       uidlist = imap_uid_list_from_lep(lep_uidlist);
-                       mailimap_search_result_free(lep_uidlist);
-                       
-                       unseen = g_slist_concat(unseen, uidlist);
-               }
-               
-               r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_FLAGGED,
-                                        full_search ? NULL:imapset, &lep_uidlist);
-               if (r == MAILIMAP_NO_ERROR) {
-                       GSList * uidlist;
-
-                       uidlist = imap_uid_list_from_lep(lep_uidlist);
-                       mailimap_search_result_free(lep_uidlist);
-
-                       flagged = g_slist_concat(flagged, uidlist);
-               }
+       if (folder->account && folder->account->low_bandwidth) {
+               for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
+                       struct mailimap_set * imapset;
+                       clist * lep_uidlist;
+                       int r;
 
-               if (item->opened || item->processing_pending || item == folder->inbox) {
-                       r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_ANSWERED,
-                                                full_search ? NULL:imapset, &lep_uidlist);
+                       imapset = cur->data;
+                       if (reverse_seen) {
+                               r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_SEEN,
+                                                        full_search ? NULL:imapset, &lep_uidlist);
+                       }
+                       else {
+                               r = imap_threaded_search(folder,
+                                                        IMAP_SEARCH_TYPE_UNSEEN,
+                                                        full_search ? NULL:imapset, &lep_uidlist);
+                       }
                        if (r == MAILIMAP_NO_ERROR) {
                                GSList * uidlist;
 
                                uidlist = imap_uid_list_from_lep(lep_uidlist);
                                mailimap_search_result_free(lep_uidlist);
 
-                               answered = g_slist_concat(answered, uidlist);
+                               unseen = g_slist_concat(unseen, uidlist);
                        }
 
-                       r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_DELETED,
+                       r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_FLAGGED,
                                                 full_search ? NULL:imapset, &lep_uidlist);
                        if (r == MAILIMAP_NO_ERROR) {
                                GSList * uidlist;
@@ -4086,56 +4158,110 @@ static /*gint*/ void *imap_get_flags_thread(void *data)
                                uidlist = imap_uid_list_from_lep(lep_uidlist);
                                mailimap_search_result_free(lep_uidlist);
 
-                               deleted = g_slist_concat(deleted, uidlist);
+                               flagged = g_slist_concat(flagged, uidlist);
                        }
-               }
-       }
 
-       p_unseen = unseen;
-       p_answered = answered;
-       p_flagged = flagged;
-       p_deleted = deleted;
+                       if (fitem->opened || fitem->processing_pending || fitem == folder->inbox) {
+                               r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_ANSWERED,
+                                                        full_search ? NULL:imapset, &lep_uidlist);
+                               if (r == MAILIMAP_NO_ERROR) {
+                                       GSList * uidlist;
+
+                                       uidlist = imap_uid_list_from_lep(lep_uidlist);
+                                       mailimap_search_result_free(lep_uidlist);
+
+                                       answered = g_slist_concat(answered, uidlist);
+                               }
+
+                               r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_DELETED,
+                                                        full_search ? NULL:imapset, &lep_uidlist);
+                               if (r == MAILIMAP_NO_ERROR) {
+                                       GSList * uidlist;
+
+                                       uidlist = imap_uid_list_from_lep(lep_uidlist);
+                                       mailimap_search_result_free(lep_uidlist);
+
+                                       deleted = g_slist_concat(deleted, uidlist);
+                               }
+                       }
+               }
+               p_unseen = unseen;
+               p_answered = answered;
+               p_flagged = flagged;
+               p_deleted = deleted;
 
+       } else {
+               r = imap_threaded_fetch_uid_flags(folder, 1, &lep_uidtab);
+               if (r == MAILIMAP_NO_ERROR) {
+                       flags_hash = g_hash_table_new_full(g_int_hash, g_int_equal, free, NULL);
+                       imap_flags_hash_from_lep_uid_flags_tab(lep_uidtab, flags_hash);
+                       imap_fetch_uid_flags_list_free(lep_uidtab);
+               }
+       }
        for (elem = sorted_list; elem != NULL; elem = g_slist_next(elem)) {
                MsgInfo *msginfo;
-               MsgPermFlags flags;
+               MsgPermFlags flags, oldflags;
                gboolean wasnew;
-               
+
                msginfo = (MsgInfo *) elem->data;
                flags = msginfo->flags.perm_flags;
                wasnew = (flags & MSG_NEW);
-               if (item->opened || item->processing_pending || item == folder->inbox) {
-                       flags &= ~((reverse_seen ? 0 : MSG_UNREAD | MSG_NEW) | MSG_REPLIED | MSG_MARKED);
-               } else {
-                       flags &= ~((reverse_seen ? 0 : MSG_UNREAD | MSG_NEW | MSG_MARKED));
-               }
-               if (reverse_seen)
-                       flags |= MSG_UNREAD | (wasnew ? MSG_NEW : 0);
-               if (gslist_find_next_num(&p_unseen, msginfo->msgnum) == msginfo->msgnum) {
-                       if (!reverse_seen) {
-                               flags |= MSG_UNREAD | (wasnew ? MSG_NEW : 0);
+               oldflags = flags & ~(MSG_NEW|MSG_UNREAD|MSG_REPLIED|MSG_MARKED|MSG_DELETED);
+
+               if (folder->account && folder->account->low_bandwidth) {
+                       if (fitem->opened || fitem->processing_pending || fitem == folder->inbox) {
+                               flags &= ~((reverse_seen ? 0 : MSG_UNREAD | MSG_NEW) | MSG_REPLIED | MSG_MARKED);
                        } else {
-                               flags &= ~(MSG_UNREAD | MSG_NEW);
+                               flags &= ~((reverse_seen ? 0 : MSG_UNREAD | MSG_NEW | MSG_MARKED));
+                       }
+                       if (reverse_seen)
+                               flags |= MSG_UNREAD | (wasnew ? MSG_NEW : 0);
+                       if (gslist_find_next_num(&p_unseen, msginfo->msgnum) == msginfo->msgnum) {
+                               if (!reverse_seen) {
+                                       flags |= MSG_UNREAD | (wasnew ? MSG_NEW : 0);
+                               } else {
+                                       flags &= ~(MSG_UNREAD | MSG_NEW);
+                               }
                        }
-               }
-               
-               if (gslist_find_next_num(&p_flagged, msginfo->msgnum) == msginfo->msgnum)
-                       flags |= MSG_MARKED;
-               else
-                       flags &= ~MSG_MARKED;
 
-               if (item->opened || item->processing_pending || item == folder->inbox) {
-                       if (gslist_find_next_num(&p_answered, msginfo->msgnum) == msginfo->msgnum)
-                               flags |= MSG_REPLIED;
+                       if (gslist_find_next_num(&p_flagged, msginfo->msgnum) == msginfo->msgnum)
+                               flags |= MSG_MARKED;
                        else
-                               flags &= ~MSG_REPLIED;
-                       if (gslist_find_next_num(&p_deleted, msginfo->msgnum) == msginfo->msgnum)
-                               flags |= MSG_DELETED;
-                       else
-                               flags &= ~MSG_DELETED;
+                               flags &= ~MSG_MARKED;
+
+                       if (fitem->opened || fitem->processing_pending || fitem == folder->inbox) {
+                               if (gslist_find_next_num(&p_answered, msginfo->msgnum) == msginfo->msgnum)
+                                       flags |= MSG_REPLIED;
+                               else
+                                       flags &= ~MSG_REPLIED;
+                               if (gslist_find_next_num(&p_deleted, msginfo->msgnum) == msginfo->msgnum)
+                                       flags |= MSG_DELETED;
+                               else
+                                       flags &= ~MSG_DELETED;
+                       }
+               } else {
+                       if (flags_hash != NULL) {
+                               gint * puid;
+
+                               puid = malloc(sizeof(* puid));
+                               * puid = msginfo->msgnum;
+
+                               flags = GPOINTER_TO_INT(g_hash_table_lookup(flags_hash, puid));
+                               free(puid);
+                       }
+
+                       if ((flags & MSG_UNREAD) == 0)
+                               flags &= ~MSG_NEW;
+                       else if (wasnew)
+                               flags |= MSG_NEW;
+                       flags |= oldflags;
                }
+
                g_relation_insert(msgflags, msginfo, GINT_TO_POINTER(flags));
        }
+       
+       if (flags_hash)
+               g_hash_table_destroy(flags_hash);
 
        imap_lep_set_free(seq_list);
        g_slist_free(flagged);
@@ -4143,10 +4269,9 @@ static /*gint*/ void *imap_get_flags_thread(void *data)
        g_slist_free(answered);
        g_slist_free(unseen);
        g_slist_free(sorted_list);
-       g_string_free(cmd_buf, TRUE);
 
-       stuff->done = TRUE;
        unlock_session(session);
+       stuff->done = TRUE;
        return GINT_TO_POINTER(0);
 }
 
@@ -4510,6 +4635,28 @@ static GSList * imap_uid_list_from_lep_tab(carray * list)
        return result;
 }
 
+static void imap_flags_hash_from_lep_uid_flags_tab(carray * list,
+                                                  GHashTable * hash)
+{
+       unsigned int i;
+       GSList * result;
+       
+       result = NULL;
+       
+       for(i = 0 ; i < carray_count(list) ; i += 2) {
+               uint32_t * puid;
+               int * pflags;
+               gint * pguid;
+               
+               puid = carray_get(list, i);
+               pflags = carray_get(list, i + 1);
+               pguid = malloc(sizeof(* pguid));
+               * pguid = * puid;
+               
+               g_hash_table_insert(hash, pguid, GINT_TO_POINTER(* pflags));
+       }
+}
+
 static MsgInfo *imap_envelope_from_lep(struct imap_fetch_env_info * info,
                                       FolderItem *item)
 {
@@ -4631,7 +4778,8 @@ void imap_cancel_all(void)
                                imap_threaded_cancel(folder);
                                rfolder = (RemoteFolder *) folder;
                                imap_session = (IMAPSession *) rfolder->session;
-                               imap_session->cancelled = 1;
+                               if (imap_session)
+                                       imap_session->cancelled = 1;
                        }
                }
        }
@@ -4656,7 +4804,7 @@ gboolean imap_cancel_all_enabled(void)
        return FALSE;
 }
 
-gboolean imap_is_busy(Folder *folder)
+static gboolean imap_is_busy(Folder *folder)
 {
        IMAPSession *imap_session;
        RemoteFolder *rfolder;
@@ -4770,22 +4918,19 @@ gboolean imap_cancel_all_enabled(void)
        return FALSE;
 }
 
-gboolean imap_is_busy(Folder *folder)
-{
-       return FALSE;
-}
-
 #endif
 
-void imap_synchronise(FolderItem *item) 
+void imap_synchronise(FolderItem *item, gint days
 {
+#ifdef HAVE_LIBETPAN
        if (IMAP_FOLDER_ITEM(item)->last_sync == IMAP_FOLDER_ITEM(item)->last_change) {
                debug_print("%s already synced\n", item->path?item->path:item->name);
                return;
        }
        debug_print("syncing %s\n", item->path?item->path:item->name);
-       imap_gtk_synchronise(item);
+       imap_gtk_synchronise(item, days);
        IMAP_FOLDER_ITEM(item)->last_sync = IMAP_FOLDER_ITEM(item)->last_change;
+#endif
 }
 
 static void imap_item_set_xml(Folder *folder, FolderItem *item, XMLTag *tag)