2005-09-02 [paul] 1.9.13cvs80
[claws.git] / src / imap.c
index d65101c0fbad558099b3f311399dde3e3e26196e..e0fab6fb46410f4b8bb65992d1d7e6821890bb4b 100644 (file)
@@ -96,7 +96,7 @@ struct _IMAPSession
 
        gboolean authenticated;
 
-       gchar **capability;
+       GSList *capability;
        gboolean uidplus;
 
        gchar *mbox;
@@ -289,7 +289,8 @@ static void imap_free_capabilities  (IMAPSession    *session);
 /* low-level IMAP4rev1 commands */
 static gint imap_cmd_login     (IMAPSession    *session,
                                 const gchar    *user,
-                                const gchar    *pass);
+                                const gchar    *pass,
+                                const gchar    *type);
 static gint imap_cmd_logout    (IMAPSession    *session);
 static gint imap_cmd_noop      (IMAPSession    *session);
 #if USE_OPENSSL
@@ -526,19 +527,119 @@ static void imap_reset_uid_lists(Folder *folder)
        g_node_traverse(folder->node, G_IN_ORDER, G_TRAVERSE_ALL, -1, imap_reset_uid_lists_func, NULL); 
 }
 
+void imap_get_capabilities(IMAPSession *session)
+{
+       struct mailimap_capability_data *capabilities = NULL;
+       clistiter *cur;
+
+       if (session->capability != NULL)
+               return;
+
+       capabilities = imap_threaded_capability(session->folder);
+       for(cur = clist_begin(capabilities->cap_list) ; cur != NULL ;
+           cur = clist_next(cur)) {
+               struct mailimap_capability * cap = 
+                       clist_content(cur);
+               if (cap->cap_data.cap_name == NULL)
+                       continue;
+               session->capability = g_slist_append
+                               (session->capability,
+                                g_strdup(cap->cap_data.cap_name));
+               debug_print("got capa %s\n", cap->cap_data.cap_name);
+       }
+       mailimap_capability_data_free(capabilities);
+}
+
+gboolean imap_has_capability(IMAPSession *session, const gchar *cap) 
+{
+       GSList *cur;
+       for (cur = session->capability; cur; cur = cur->next) {
+               if (!g_ascii_strcasecmp(cur->data, cap))
+                       return TRUE;
+       }
+       return FALSE;
+}
+
 static gint imap_auth(IMAPSession *session, const gchar *user, const gchar *pass,
                      IMAPAuthType type)
 {
-       gint ok;
-
-       ok = imap_cmd_login(session, user, pass);
+       gint ok = IMAP_ERROR;
+       static time_t last_login_err = 0;
        
+       imap_get_capabilities(session);
+
+       switch(type) {
+       case IMAP_AUTH_CRAM_MD5:
+               ok = imap_cmd_login(session, user, pass, "CRAM-MD5");
+               break;
+       case IMAP_AUTH_LOGIN:
+               ok = imap_cmd_login(session, user, pass, "LOGIN");
+               break;
+       default:
+               debug_print("capabilities:\n"
+                               "\t CRAM-MD5 %d\n"
+                               "\t LOGIN %d\n", 
+                       imap_has_capability(session, "CRAM-MD5"),
+                       imap_has_capability(session, "LOGIN"));
+               if (imap_has_capability(session, "CRAM-MD5"))
+                       ok = imap_cmd_login(session, user, pass, "CRAM-MD5");
+               if (ok == IMAP_ERROR) /* we always try LOGIN before giving up */
+                       ok = imap_cmd_login(session, user, pass, "LOGIN");
+       }
        if (ok == IMAP_SUCCESS)
                session->authenticated = TRUE;
-
+       else {
+               gchar *ext_info = NULL;
+               
+               if (type == IMAP_AUTH_CRAM_MD5) {
+                       ext_info = _("\n\nCRAM-MD5 logins only work if libetpan has been "
+                                    "compiled with SASL support and the "
+                                    "CRAM-MD5 SASL plugin is installed.");
+               } else {
+                       ext_info = "";
+               }
+               
+               if (time(NULL) - last_login_err > 10) {
+                       alertpanel_error(_("Connection to %s failed: login refused.%s"),
+                                       SESSION(session)->server, ext_info);
+               }
+               last_login_err = time(NULL);
+       }
        return ok;
 }
 
+static IMAPSession *imap_reconnect_if_possible(Folder *folder, IMAPSession *session)
+{
+       RemoteFolder *rfolder = REMOTE_FOLDER(folder);
+       /* Check if this is the first try to establish a
+          connection, if yes we don't try to reconnect */
+       debug_print("reconnecting\n");
+       if (rfolder->session == NULL) {
+               log_warning(_("Connecting to %s failed"),
+                           folder->account->recv_server);
+               session_destroy(SESSION(session));
+               session = NULL;
+       } else {
+               log_warning(_("IMAP4 connection to %s has been"
+                           " disconnected. Reconnecting...\n"),
+                           folder->account->recv_server);
+               statusbar_print_all(_("IMAP4 connection to %s has been"
+                           " disconnected. Reconnecting...\n"),
+                           folder->account->recv_server);
+               SESSION(session)->state = SESSION_DISCONNECTED;
+               session_destroy(SESSION(session));
+               /* Clear folders session to make imap_session_get create
+                  a new session, because of rfolder->session == NULL
+                  it will not try to reconnect again and so avoid an
+                  endless loop */
+               rfolder->session = NULL;
+               session = imap_session_get(folder);
+               rfolder->session = SESSION(session);
+               statusbar_pop_all();
+       }
+       return session;
+}
+
 static IMAPSession *imap_session_get(Folder *folder)
 {
        RemoteFolder *rfolder = REMOTE_FOLDER(folder);
@@ -589,29 +690,8 @@ static IMAPSession *imap_session_get(Folder *folder)
        if (time(NULL) - SESSION(session)->last_access_time > SESSION_TIMEOUT_INTERVAL) {
                /* verify that the session is still alive */
                if (imap_cmd_noop(session) != IMAP_SUCCESS) {
-                       /* Check if this is the first try to establish a
-                          connection, if yes we don't try to reconnect */
-                       if (rfolder->session == NULL) {
-                               log_warning(_("Connecting to %s failed"),
-                                           folder->account->recv_server);
-                               session_destroy(SESSION(session));
-                               session = NULL;
-                       } else {
-                               log_warning(_("IMAP4 connection to %s has been"
-                                           " disconnected. Reconnecting...\n"),
-                                           folder->account->recv_server);
-                               statusbar_print_all(_("IMAP4 connection to %s has been"
-                                           " disconnected. Reconnecting...\n"),
-                                           folder->account->recv_server);
-                               session_destroy(SESSION(session));
-                               /* Clear folders session to make imap_session_get create
-                                  a new session, because of rfolder->session == NULL
-                                  it will not try to reconnect again and so avoid an
-                                  endless loop */
-                               rfolder->session = NULL;
-                               session = imap_session_get(folder);
-                               statusbar_pop_all();
-                       }
+                       debug_print("disconnected!\n");
+                       session = imap_reconnect_if_possible(folder, session);
                }
        }
 
@@ -675,7 +755,10 @@ static IMAPSession *imap_session_new(Folder * folder,
                if(!prefs_common.no_recv_err_panel) {
                        alertpanel_error(_("Can't connect to IMAP4 server: %s:%d"),
                                         account->recv_server, port);
-               }
+               } else {
+                       log_error(_("Can't connect to IMAP4 server: %s:%d"),
+                                        account->recv_server, port);
+               } 
                
                return NULL;
        }
@@ -750,7 +833,8 @@ static void imap_session_authenticate(IMAPSession *session,
 
 static void imap_session_destroy(Session *session)
 {
-       imap_threaded_disconnect(IMAP_SESSION(session)->folder);
+       if (session->state != SESSION_DISCONNECTED)
+               imap_threaded_disconnect(IMAP_SESSION(session)->folder);
        
        imap_free_capabilities(IMAP_SESSION(session));
        g_free(IMAP_SESSION(session)->mbox);
@@ -763,7 +847,7 @@ static gchar *imap_fetch_msg(Folder *folder, FolderItem *item, gint uid)
        return imap_fetch_msg_full(folder, item, uid, TRUE, TRUE);
 }
 
-static guint get_size_with_lfs(MsgInfo *info) 
+static guint get_size_with_crs(MsgInfo *info) 
 {
        FILE *fp = NULL;
        guint cnt = 0;
@@ -811,14 +895,17 @@ static gchar *imap_fetch_msg_full(Folder *folder, FolderItem *item, gint uid,
                 * we have to update the local file (UNIX \n only) size */
                MsgInfo *msginfo = imap_parse_msg(filename, item);
                MsgInfo *cached = msgcache_get_msg(item->cache,uid);
-               guint have_size = get_size_with_lfs(msginfo);
-               debug_print("message %d has been already %scached (%d/%d).\n", uid,
+               guint have_size = get_size_with_crs(msginfo);
+
+               if (cached)
+                       debug_print("message %d has been already %scached (%d/%d).\n", uid,
                                have_size == cached->size ? "fully ":"",
-                               have_size, cached? (int)cached->size : -1);
+                               have_size, (int)cached->size);
                
                if (cached && (cached->size == have_size || !body)) {
                        procmsg_msginfo_free(cached);
                        procmsg_msginfo_free(msginfo);
+                       file_strip_crs(filename);
                        return filename;
                } else {
                        procmsg_msginfo_free(cached);
@@ -850,6 +937,7 @@ static gchar *imap_fetch_msg_full(Folder *folder, FolderItem *item, gint uid,
                return NULL;
        }
 
+       file_strip_crs(filename);
        return filename;
 }
 
@@ -908,10 +996,10 @@ static gint imap_add_msgs(Folder *folder, FolderItem *dest, GSList *file_list,
                                iflags |= IMAP_FLAG_SEEN;
                }
 
-               if (dest->stype == F_OUTBOX ||
-                   dest->stype == F_QUEUE  ||
-                   dest->stype == F_DRAFT  ||
-                   dest->stype == F_TRASH)
+               if (folder_has_parent_of_type(dest, F_QUEUE) ||
+                   folder_has_parent_of_type(dest, F_OUTBOX) ||
+                   folder_has_parent_of_type(dest, F_DRAFT) ||
+                   folder_has_parent_of_type(dest, F_TRASH))
                        iflags |= IMAP_FLAG_SEEN;
 
                ok = imap_cmd_append(session, destdir, fileinfo->file, iflags, 
@@ -1148,7 +1236,7 @@ static gint imap_remove_all_msg(Folder *folder, FolderItem *item)
 {
        GSList *list = folder_item_get_msg_list(item);
        gint res = imap_remove_msgs(folder, item, list, NULL);
-       g_slist_free(list);
+       procmsg_msg_list_free(list);
        return res;
 }
 
@@ -2123,7 +2211,8 @@ static gint imap_status(IMAPSession *session, IMAPFolder *folder,
 
 static void imap_free_capabilities(IMAPSession *session)
 {
-       g_strfreev(session->capability);
+       slist_free_strings(session->capability);
+       g_slist_free(session->capability);
        session->capability = NULL;
 }
 
@@ -2174,20 +2263,18 @@ static gint imap_cmd_authenticate(IMAPSession *session, const gchar *user,
 #endif
 
 static gint imap_cmd_login(IMAPSession *session,
-                          const gchar *user, const gchar *pass)
+                          const gchar *user, const gchar *pass,
+                          const gchar *type)
 {
        int r;
        gint ok;
-       static time_t last_login_err = 0;
 
-       log_print("IMAP4> Logging in to %s\n", SESSION(session)->server);
-       r = imap_threaded_login(session->folder, user, pass);
+       log_print("IMAP4> Logging %s to %s using %s\n", 
+                       user,
+                       SESSION(session)->server,
+                       type);
+       r = imap_threaded_login(session->folder, user, pass, type);
        if (r != MAILIMAP_NO_ERROR) {
-               if (time(NULL) - last_login_err > 10) {
-                       alertpanel_error(_("Connection to %s failed: login refused."),
-                                       SESSION(session)->server);
-               }
-               last_login_err = time(NULL);
                log_error("IMAP4< Error logging in to %s\n",
                                SESSION(session)->server);
                ok = IMAP_ERROR;
@@ -2356,8 +2443,9 @@ static gint imap_cmd_fetch(IMAPSession *session, guint32 uid,
                g_free(data);
                return -1;
        }
-
+       statusbar_print_all(_("Fetching message..."));
        result = GPOINTER_TO_INT(imap_cmd_fetch_thread(data));
+       statusbar_pop_all();
        g_free(data);
        return result;
 }
@@ -2373,9 +2461,10 @@ static gint imap_cmd_append(IMAPSession *session, const gchar *destfolder,
        g_return_val_if_fail(file != NULL, IMAP_ERROR);
 
        flag_list = imap_flag_to_lep(flags);
+       statusbar_print_all(_("Adding messages..."));
        r = imap_threaded_append(session->folder, destfolder,
                         file, flag_list);
-       
+       statusbar_pop_all();
        if (new_uid != NULL)
                *new_uid = 0;
 
@@ -2395,7 +2484,9 @@ static gint imap_cmd_copy(IMAPSession *session, struct mailimap_set * set,
        g_return_val_if_fail(set != NULL, IMAP_ERROR);
        g_return_val_if_fail(destfolder != NULL, IMAP_ERROR);
 
+       statusbar_print_all(_("Copying messages..."));
        r = imap_threaded_copy(session->folder, set, destfolder);
+       statusbar_pop_all();
        if (r != MAILIMAP_NO_ERROR) {
                
                return IMAP_ERROR;
@@ -2690,7 +2781,7 @@ static void *get_list_of_uids_thread(void *data)
        struct mailimap_set * set;
        clist * lep_uidlist;
        int r;
-       
+
        session = imap_session_get(folder);
        if (session == NULL) {
                stuff->done = TRUE;
@@ -2783,6 +2874,8 @@ gint imap_get_num_list(Folder *folder, FolderItem *_item, GSList **msgnum_list,
        gboolean selected_folder;
        
        debug_print("get_num_list\n");
+       statusbar_print_all("Scanning %s...\n", FOLDER_ITEM(item)->path 
+                               ? FOLDER_ITEM(item)->path:"");
        
        g_return_val_if_fail(folder != NULL, -1);
        g_return_val_if_fail(item != NULL, -1);
@@ -2798,8 +2891,12 @@ gint imap_get_num_list(Folder *folder, FolderItem *_item, GSList **msgnum_list,
        if (selected_folder) {
                ok = imap_cmd_noop(session);
                if (ok != IMAP_SUCCESS) {
-                       
-                       return -1;
+                       debug_print("disconnected!\n");
+                       session = imap_reconnect_if_possible(folder, session);
+                       if (session == NULL) {
+                               statusbar_pop_all();
+                               return -1;
+                       }
                }
                exists = session->exists;
 
@@ -2808,7 +2905,7 @@ gint imap_get_num_list(Folder *folder, FolderItem *_item, GSList **msgnum_list,
                ok = imap_status(session, IMAP_FOLDER(folder), item->item.path,
                                 &exists, &recent, &uid_next, &uid_val, &unseen, FALSE);
                if (ok != IMAP_SUCCESS) {
-                       
+                       statusbar_pop_all();
                        return -1;
                }
                if(item->item.mtime == uid_val)
@@ -2843,6 +2940,7 @@ gint imap_get_num_list(Folder *folder, FolderItem *_item, GSList **msgnum_list,
                   out which numbers have been removed */
                if (exists == nummsgs) {
                        *msgnum_list = g_slist_copy(item->uid_list);
+                       statusbar_pop_all();
                        return nummsgs;
                } else if (exists < nummsgs) {
                        debug_print("Freeing imap uid cache");
@@ -2854,13 +2952,14 @@ gint imap_get_num_list(Folder *folder, FolderItem *_item, GSList **msgnum_list,
 
        if (exists == 0) {
                *msgnum_list = NULL;
+               statusbar_pop_all();
                return 0;
        }
 
        nummsgs = get_list_of_uids(folder, item, &uidlist);
 
        if (nummsgs < 0) {
-               
+               statusbar_pop_all();
                return -1;
        }
 
@@ -2887,7 +2986,7 @@ gint imap_get_num_list(Folder *folder, FolderItem *_item, GSList **msgnum_list,
        g_free(dir);
        
        debug_print("get_num_list - ok - %i\n", nummsgs);
-       
+       statusbar_pop_all();
        return nummsgs;
 }
 
@@ -2902,9 +3001,9 @@ static MsgInfo *imap_parse_msg(const gchar *file, FolderItem *item)
        g_return_val_if_fail(item != NULL, NULL);
        g_return_val_if_fail(file != NULL, NULL);
 
-       if (item->stype == F_QUEUE) {
+       if (folder_has_parent_of_type(item, F_QUEUE)) {
                MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
-       } else if (item->stype == F_DRAFT) {
+       } else if (folder_has_parent_of_type(item, F_DRAFT)) {
                MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
        }
 
@@ -2939,7 +3038,8 @@ GSList *imap_get_msginfos(Folder *folder, FolderItem *item,
        if (ok != IMAP_SUCCESS)
                return NULL;
 
-       if (!(item->stype == F_QUEUE || item->stype == F_DRAFT)) {
+       if (!(folder_has_parent_of_type(item, F_DRAFT) || 
+             folder_has_parent_of_type(item, F_QUEUE))) {
                ret = g_slist_concat(ret,
                        imap_get_uncached_messages(session, item,
                                                   msgnum_list));
@@ -3028,8 +3128,12 @@ gboolean imap_scan_required(Folder *folder, FolderItem *_item)
                          (!strcmp(session->mbox, item->item.path));
        if (selected_folder) {
                ok = imap_cmd_noop(session);
-               if (ok != IMAP_SUCCESS)
-                       return FALSE;
+               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
                ||  session->exists != item->item.total_msgs)
@@ -3562,6 +3666,12 @@ static GSList * imap_list_from_lep(IMAPFolder * folder,
                        free(dup_name);
                        continue;
                }
+
+               if (dup_name[strlen(dup_name)-1] == '/') {
+                       g_free(base);
+                       free(dup_name);
+                       continue;
+               }
                
                loc_name = imap_modified_utf7_to_utf8(base);
                loc_path = imap_modified_utf7_to_utf8(dup_name);
@@ -3709,9 +3819,9 @@ static MsgInfo *imap_envelope_from_lep(struct imap_fetch_env_info * info,
        MsgFlags flags = {0, 0};
        
        MSG_SET_TMP_FLAGS(flags, MSG_IMAP);
-       if (item->stype == F_QUEUE) {
+       if (folder_has_parent_of_type(item, F_QUEUE)) {
                MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
-       } else if (item->stype == F_DRAFT) {
+       } else if (folder_has_parent_of_type(item, F_DRAFT)) {
                MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
        }
        flags.perm_flags = info->flags;