2010-10-26 [ticho] 3.7.6cvs59
[claws.git] / src / news.c
index 10fb69a1b1e6e8132f504d112a3e21856bbf44b3..5457efa0c11899db6c99c87d62e1b21b4a117bb3 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
- * Copyright (C) 1999-2007 Hiroyuki Yamamoto and the Claws Mail team
+ * Copyright (C) 1999-2009 Hiroyuki Yamamoto and the Claws Mail team
  *
  * 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
@@ -37,7 +37,6 @@
 
 #include "nntp-thread.h"
 #include "news.h"
-#include "news.h"
 #include "news_gtk.h"
 #include "socket.h"
 #include "recv.h"
 #include "remotefolder.h"
 #include "alertpanel.h"
 #include "inc.h"
-#if (defined(USE_OPENSSL) || defined (USE_GNUTLS))
+#ifdef USE_GNUTLS
 #  include "ssl.h"
 #endif
 
 #define NNTP_PORT      119
-#if (defined(USE_OPENSSL) || defined (USE_GNUTLS))
+#ifdef USE_GNUTLS
 #define NNTPS_PORT     563
 #endif
 
@@ -100,7 +99,7 @@ static gchar *news_fetch_msg         (Folder         *folder,
 static void news_remove_cached_msg     (Folder         *folder, 
                                         FolderItem     *item, 
                                         MsgInfo        *msginfo);
-#if (defined(USE_OPENSSL) || defined (USE_GNUTLS))
+#ifdef USE_GNUTLS
 static Session *news_session_new        (Folder        *folder,
                                          const gchar   *server,
                                          gushort        port,
@@ -125,8 +124,6 @@ static gint news_select_group                (Folder        *folder,
                                          gint          *first,
                                          gint          *last);
 static MsgInfo *news_parse_xover        (struct newsnntp_xover_resp_item *item);
-static gchar *news_parse_xhdr           (clist         *list,
-                                         MsgInfo       *msginfo);
 static gint news_get_num_list                   (Folder        *folder, 
                                          FolderItem    *item,
                                          GSList       **list,
@@ -147,6 +144,9 @@ static void news_synchronise                 (FolderItem    *item, gint days);
 static int news_remove_msg              (Folder        *folder, 
                                          FolderItem    *item, 
                                          gint           msgnum);
+static gint news_rename_folder          (Folder *folder,
+                                         FolderItem *item,
+                                         const gchar *name);
 static gint news_remove_folder          (Folder        *folder,
                                          FolderItem    *item);
 static FolderClass news_class;
@@ -166,6 +166,7 @@ FolderClass *news_get_class(void)
                news_class.item_get_path = news_item_get_path;
                news_class.get_num_list = news_get_num_list;
                news_class.scan_required = news_scan_required;
+               news_class.rename_folder = news_rename_folder;
                news_class.remove_folder = news_remove_folder;
 
                /* Message functions */
@@ -202,8 +203,8 @@ static int news_remove_msg           (Folder        *folder,
 {
        gchar *path, *filename;
 
-       g_return_val_if_fail(folder != NULL, -1);
-       g_return_val_if_fail(item != NULL, -1);
+       cm_return_val_if_fail(folder != NULL, -1);
+       cm_return_val_if_fail(item != NULL, -1);
 
        path = folder_item_get_path(item);
        if (!is_dir_exist(path))
@@ -211,7 +212,7 @@ static int news_remove_msg           (Folder        *folder,
        
        filename = g_strconcat(path, G_DIR_SEPARATOR_S, itos(msgnum), NULL);
        g_free(path);
-       g_unlink(filename);
+       claws_unlink(filename);
        g_free(filename);
        return 0;
 }
@@ -272,13 +273,13 @@ static void news_session_destroy(Session *session)
 {
        NewsSession *news_session = NEWS_SESSION(session);
 
-       g_return_if_fail(session != NULL);
+       cm_return_if_fail(session != NULL);
 
        if (news_session->group)
                g_free(news_session->group);
 }
 
-#if (defined(USE_OPENSSL) || defined (USE_GNUTLS))
+#ifdef USE_GNUTLS
 static Session *news_session_new(Folder *folder, const gchar *server, gushort port,
                                 const gchar *userid, const gchar *passwd,
                                 SSLType ssl_type)
@@ -289,20 +290,21 @@ static Session *news_session_new(Folder *folder, const gchar *server, gushort po
 {
        NewsSession *session;
        int r = 0;
-       g_return_val_if_fail(server != NULL, NULL);
+       cm_return_val_if_fail(server != NULL, NULL);
 
        log_message(LOG_PROTOCOL, _("creating NNTP connection to %s:%d ...\n"), server, port);
 
        session = g_new0(NewsSession, 1);
-       session_init(SESSION(session));
+       session_init(SESSION(session), folder->account, FALSE);
        SESSION(session)->type             = SESSION_NEWS;
        SESSION(session)->server           = g_strdup(server);
+       SESSION(session)->port             = port;
        SESSION(session)->sock             = NULL;
        SESSION(session)->destroy          = news_session_destroy;
        
        nntp_init(folder);
 
-#if (defined(USE_OPENSSL) || defined (USE_GNUTLS))
+#ifdef USE_GNUTLS
        if (ssl_type != SSL_NONE)
                r = nntp_threaded_connect_ssl(folder, server, port);
        else
@@ -326,8 +328,8 @@ static Session *news_session_new_for_folder(Folder *folder)
        gchar *passwd = NULL;
        gushort port;
 
-       g_return_val_if_fail(folder != NULL, NULL);
-       g_return_val_if_fail(folder->account != NULL, NULL);
+       cm_return_val_if_fail(folder != NULL, NULL);
+       cm_return_val_if_fail(folder->account != NULL, NULL);
 
        ac = folder->account;
        if (ac->use_nntp_auth && ac->userid && ac->userid[0]) {
@@ -335,11 +337,12 @@ static Session *news_session_new_for_folder(Folder *folder)
                if (ac->passwd && ac->passwd[0])
                        passwd = g_strdup(ac->passwd);
                else
-                       passwd = input_dialog_query_password(ac->nntp_server,
-                                                            userid);
+                       passwd = input_dialog_query_password_keep(ac->nntp_server,
+                                                                 userid,
+                                                                 &(ac->session_passwd));
        }
 
-#if (defined(USE_OPENSSL) || defined (USE_GNUTLS))
+#ifdef USE_GNUTLS
        port = ac->set_nntpport ? ac->nntpport
                : ac->ssl_nntp ? NNTPS_PORT : NNTP_PORT;
        session = news_session_new(folder, ac->nntp_server, port, userid, passwd,
@@ -368,6 +371,10 @@ static Session *news_session_new_for_folder(Folder *folder)
                        log_error(LOG_PROTOCOL, _("Error authenticating to %s:%d ...\n"), ac->nntp_server, port);
                        session_destroy(SESSION(session));
                        g_free(passwd);
+                       if (ac->session_passwd) {
+                               g_free(ac->session_passwd);
+                               ac->session_passwd = NULL;
+                       }
                        return NULL;
                }
        }
@@ -382,9 +389,9 @@ static NewsSession *news_session_get(Folder *folder)
        struct tm lt;
        int r;
        
-       g_return_val_if_fail(folder != NULL, NULL);
-       g_return_val_if_fail(FOLDER_CLASS(folder) == &news_class, NULL);
-       g_return_val_if_fail(folder->account != NULL, NULL);
+       cm_return_val_if_fail(folder != NULL, NULL);
+       cm_return_val_if_fail(FOLDER_CLASS(folder) == &news_class, NULL);
+       cm_return_val_if_fail(folder->account != NULL, NULL);
 
        if (prefs_common.work_offline && 
            !inc_offline_should_override(FALSE,
@@ -398,6 +405,14 @@ static NewsSession *news_session_get(Folder *folder)
                return NEWS_SESSION(rfolder->session);
        }
 
+       /* Handle port change (also ssl/nossl change) without needing to
+        * restart application. */
+       if (rfolder->session->port != folder->account->nntpport) {
+               session_destroy(rfolder->session);
+               rfolder->session = news_session_new_for_folder(folder);
+               goto newsession;
+       }
+       
        if (time(NULL) - rfolder->session->last_access_time <
                SESSION_TIMEOUT_INTERVAL) {
                return NEWS_SESSION(rfolder->session);
@@ -415,7 +430,8 @@ static NewsSession *news_session_get(Folder *folder)
                        rfolder->session = news_session_new_for_folder(folder);
                }
        }
-       
+
+newsession:
        if (rfolder->session)
                session_set_access_time(rfolder->session);
 
@@ -437,7 +453,7 @@ static void news_remove_cached_msg(Folder *folder, FolderItem *item, MsgInfo *ms
        g_free(path);
 
        if (is_file_exist(filename)) {
-               g_unlink(filename);
+               claws_unlink(filename);
        }
        g_free(filename);
 }
@@ -448,8 +464,8 @@ static gchar *news_fetch_msg(Folder *folder, FolderItem *item, gint num)
        NewsSession *session;
        gint ok;
 
-       g_return_val_if_fail(folder != NULL, NULL);
-       g_return_val_if_fail(item != NULL, NULL);
+       cm_return_val_if_fail(folder != NULL, NULL);
+       cm_return_val_if_fail(item != NULL, NULL);
 
        path = folder_item_get_path(item);
        if (!is_dir_exist(path))
@@ -528,8 +544,8 @@ GSList *news_get_group_list(Folder *folder)
        GSList *last = NULL;
        gchar buf[BUFFSIZE];
 
-       g_return_val_if_fail(folder != NULL, NULL);
-       g_return_val_if_fail(FOLDER_CLASS(folder) == &news_class, NULL);
+       cm_return_val_if_fail(folder != NULL, NULL);
+       cm_return_val_if_fail(FOLDER_CLASS(folder) == &news_class, NULL);
 
        path = folder_item_get_path(FOLDER_ITEM(folder->node->data));
        if (!is_dir_exist(path))
@@ -542,7 +558,7 @@ GSList *news_get_group_list(Folder *folder)
                gint ok;
                clist *grouplist = NULL;
                clistiter *cur;
-               fp = fopen(filename, "wb");
+               fp = g_fopen(filename, "wb");
                
                if (!fp) {
                        g_free(filename);
@@ -567,25 +583,26 @@ GSList *news_get_group_list(Folder *folder)
                        return NULL;
                }
                
-               for (cur = clist_begin(grouplist); cur; cur = clist_next(cur)) {
-                       struct newsnntp_group_info *info = (struct newsnntp_group_info *)
-                                                               clist_content(cur);
-                       if (fprintf(fp, "%s %d %d %c\n",
-                               info->grp_name,
-                               info->grp_last,
-                               info->grp_first,
-                               info->grp_type) < 0) {
-                               log_error(LOG_PROTOCOL, ("Can't write newsgroup list\n"));
-                               session_destroy(SESSION(session));
-                               REMOTE_FOLDER(folder)->session = NULL;
-                               fclose(fp);
-                               g_free(filename);
-                               newsnntp_list_free(grouplist);
-                               return NULL;
+               if (grouplist) {
+                       for (cur = clist_begin(grouplist); cur; cur = clist_next(cur)) {
+                               struct newsnntp_group_info *info = (struct newsnntp_group_info *)
+                                                                       clist_content(cur);
+                               if (fprintf(fp, "%s %d %d %c\n",
+                                       info->grp_name,
+                                       info->grp_last,
+                                       info->grp_first,
+                                       info->grp_type) < 0) {
+                                       log_error(LOG_PROTOCOL, ("Can't write newsgroup list\n"));
+                                       session_destroy(SESSION(session));
+                                       REMOTE_FOLDER(folder)->session = NULL;
+                                       fclose(fp);
+                                       g_free(filename);
+                                       newsnntp_list_free(grouplist);
+                                       return NULL;
+                               }
                        }
+                       newsnntp_list_free(grouplist);
                }
-               newsnntp_list_free(grouplist);
-
                if (fclose(fp) == EOF) {
                        log_error(LOG_PROTOCOL, ("Can't write newsgroup list\n"));
                        session_destroy(SESSION(session));
@@ -651,8 +668,8 @@ void news_remove_group_list_cache(Folder *folder)
 {
        gchar *path, *filename;
 
-       g_return_if_fail(folder != NULL);
-       g_return_if_fail(FOLDER_CLASS(folder) == &news_class);
+       cm_return_if_fail(folder != NULL);
+       cm_return_if_fail(FOLDER_CLASS(folder) == &news_class);
 
        path = folder_item_get_path(FOLDER_ITEM(folder->node->data));
        filename = g_strconcat(path, G_DIR_SEPARATOR_S, NEWSGROUP_LIST, NULL);
@@ -668,12 +685,12 @@ void news_remove_group_list_cache(Folder *folder)
 gint news_post(Folder *folder, const gchar *file)
 {
        gint ok;
-       char *contents = file_read_to_str(file);
+       char *contents = file_read_to_str_no_recode(file);
        NewsSession *session;
 
-       g_return_val_if_fail(folder != NULL, -1);
-       g_return_val_if_fail(FOLDER_CLASS(folder) == &news_class, -1);
-       g_return_val_if_fail(contents != NULL, -1);
+       cm_return_val_if_fail(folder != NULL, -1);
+       cm_return_val_if_fail(FOLDER_CLASS(folder) == &news_class, -1);
+       cm_return_val_if_fail(contents != NULL, -1);
        
        session = news_session_get(folder);
        if (!session)  {
@@ -682,14 +699,20 @@ gint news_post(Folder *folder, const gchar *file)
        }
        
        ok = nntp_threaded_post(folder, contents, strlen(contents));
-       
+
+       if (ok != NEWSNNTP_NO_ERROR && ok != NEWSNNTP_ERROR_STREAM) {
+               ok = nntp_threaded_mode_reader(folder);
+               if (ok == NEWSNNTP_NO_ERROR)
+                       ok = nntp_threaded_post(folder, contents, strlen(contents));
+       }
        g_free(contents);
+
        if (ok == NEWSNNTP_ERROR_STREAM) {
                session_destroy(SESSION(session));
                REMOTE_FOLDER(folder)->session = NULL;
        }
 
-       return ok;
+       return (ok == NEWSNNTP_NO_ERROR ? 0 : -1);
 }
 
 static gint news_get_article(Folder *folder, gint num, gchar *filename)
@@ -703,9 +726,9 @@ static gint news_get_article(Folder *folder, gint num, gchar *filename)
        if (r == NEWSNNTP_NO_ERROR) {
                if (str_write_to_file(result, filename) < 0)
                        return -1;
+               g_free(result);
        }
        
-       g_free(result);
        return r;
 }
 
@@ -730,7 +753,7 @@ static gint news_select_group(Folder *folder, const gchar *group,
        struct newsnntp_group_info *info = NULL;
        NewsSession *session = NEWS_SESSION(news_session_get(folder));
 
-       g_return_val_if_fail(session != NULL, -1);
+       cm_return_val_if_fail(session != NULL, -1);
        
        if (!num || !first || !last) {
                if (session->group && g_ascii_strcasecmp(session->group, group) == 0)
@@ -826,23 +849,13 @@ static MsgInfo *news_parse_xover(struct newsnntp_xover_resp_item *item)
        return msginfo;
 }
 
-static gchar *news_parse_xhdr(clist *hdrlist, MsgInfo *msginfo)
-{
-       struct newsnntp_xhdr_resp_item *hdr;
-       
-       hdr = clist_content(clist_begin(hdrlist));
-       if (hdr->hdr_article != msginfo->msgnum)
-               return NULL;
-       return g_strdup(hdr->hdr_value);
-}
-
 gint news_cancel_article(Folder * folder, MsgInfo * msginfo)
 {
        gchar * tmp;
        FILE * tmpfp;
        gchar buf[BUFFSIZE];
 
-       tmp = g_strdup_printf("%s%ctmp%p", g_get_tmp_dir(),
+       tmp = g_strdup_printf("%s%ccancel%p", get_tmp_dir(),
                              G_DIR_SEPARATOR, msginfo);
        if (tmp == NULL)
                return -1;
@@ -875,14 +888,14 @@ gint news_cancel_article(Folder * folder, MsgInfo * msginfo)
                       buf) < 0) {
                FILE_OP_ERROR(tmp, "fprintf");
                fclose(tmpfp);
-               g_unlink(tmp);
+               claws_unlink(tmp);
                g_free(tmp);
                return -1;
        }
 
        if (fclose(tmpfp) == EOF) {
                FILE_OP_ERROR(tmp, "fclose");
-               g_unlink(tmp);
+               claws_unlink(tmp);
                g_free(tmp);
                return -1;
        }
@@ -899,7 +912,7 @@ static gchar *news_folder_get_path(Folder *folder)
 {
        gchar *folder_path;
 
-        g_return_val_if_fail(folder->account != NULL, NULL);
+        cm_return_val_if_fail(folder->account != NULL, NULL);
 
         folder_path = g_strconcat(get_news_cache_dir(),
                                   G_DIR_SEPARATOR_S,
@@ -912,12 +925,12 @@ static gchar *news_item_get_path(Folder *folder, FolderItem *item)
 {
        gchar *folder_path, *path;
 
-       g_return_val_if_fail(folder != NULL, NULL);
-       g_return_val_if_fail(item != NULL, NULL);
+       cm_return_val_if_fail(folder != NULL, NULL);
+       cm_return_val_if_fail(item != NULL, NULL);
        folder_path = news_folder_get_path(folder);
 
-        g_return_val_if_fail(folder_path != NULL, NULL);
-        if (folder_path[0] == G_DIR_SEPARATOR) {
+        cm_return_val_if_fail(folder_path != NULL, NULL);
+        if (g_path_is_absolute(folder_path)) {
                 if (item->path)
                         path = g_strconcat(folder_path, G_DIR_SEPARATOR_S,
                                            item->path, NULL);
@@ -933,7 +946,10 @@ static gchar *news_item_get_path(Folder *folder, FolderItem *item)
                                            folder_path, NULL);
         }
         g_free(folder_path);
-
+#ifdef G_OS_WIN32
+       while (strchr(path, '/'))
+               *strchr(path, '/') = '\\';
+#endif
        return path;
 }
 
@@ -943,12 +959,12 @@ static gint news_get_num_list(Folder *folder, FolderItem *item, GSList **msgnum_
        gint i, ok, num, first, last, nummsgs = 0;
        gchar *dir;
 
-       g_return_val_if_fail(item != NULL, -1);
-       g_return_val_if_fail(item->folder != NULL, -1);
-       g_return_val_if_fail(FOLDER_CLASS(folder) == &news_class, -1);
+       cm_return_val_if_fail(item != NULL, -1);
+       cm_return_val_if_fail(item->folder != NULL, -1);
+       cm_return_val_if_fail(FOLDER_CLASS(folder) == &news_class, -1);
 
        session = news_session_get(folder);
-       g_return_val_if_fail(session != NULL, -1);
+       cm_return_val_if_fail(session != NULL, -1);
 
        *old_uids_valid = TRUE;
        
@@ -1002,54 +1018,62 @@ static void news_set_msg_flags(FolderItem *item, MsgInfo *msginfo)
        }
 }
 
-static MsgInfo *news_get_msginfo(Folder *folder, FolderItem *item, gint num)
+static void news_get_extra_fields(NewsSession *session, FolderItem *item, GSList *msglist)
 {
-       NewsSession *session;
        MsgInfo *msginfo = NULL;
        gint ok;
-       struct newsnntp_xover_resp_item *result = NULL;
+       GSList *cur;
        clist *hdrlist = NULL;
+       clistiter *hdr;
+       gint first = -1, last = -1;
+       GHashTable *hash_table;
+       
+       cm_return_if_fail(session != NULL);
+       cm_return_if_fail(item != NULL);
+       cm_return_if_fail(item->folder != NULL);
+       cm_return_if_fail(FOLDER_CLASS(item->folder) == &news_class);
 
-       session = news_session_get(folder);
-       g_return_val_if_fail(session != NULL, NULL);
-       g_return_val_if_fail(item != NULL, NULL);
-       g_return_val_if_fail(item->folder != NULL, NULL);
-       g_return_val_if_fail(FOLDER_CLASS(item->folder) == &news_class, NULL);
-
-       log_message(LOG_PROTOCOL, _("getting xover %d in %s...\n"),
-                   num, item->path);
        news_folder_lock(NEWS_FOLDER(item->folder));
 
-       ok = nntp_threaded_xover(folder, num, num, &result, NULL);
+       hash_table = g_hash_table_new(g_direct_hash, g_direct_equal);
+       
+       for (cur = msglist; cur; cur = cur->next) {
+               msginfo = (MsgInfo *)cur->data;
+               if (first == -1 || msginfo->msgnum < first)
+                       first = msginfo->msgnum;
+               if (last == -1 || msginfo->msgnum > last)
+                       last = msginfo->msgnum;
+               g_hash_table_insert(hash_table,
+                               GINT_TO_POINTER(msginfo->msgnum), msginfo);
+       }
+
+/* Newsgroups */
+       ok = nntp_threaded_xhdr(item->folder, "newsgroups", first, last, &hdrlist);
+
        if (ok != NEWSNNTP_NO_ERROR) {
-               log_warning(LOG_PROTOCOL, _("couldn't get xover\n"));
+               log_warning(LOG_PROTOCOL, _("couldn't get xhdr\n"));
                if (ok == NEWSNNTP_ERROR_STREAM) {
                        session_destroy(SESSION(session));
                        REMOTE_FOLDER(item->folder)->session = NULL;
                }
                news_folder_unlock(NEWS_FOLDER(item->folder));
-               return NULL;
-       }
-       
-       msginfo = news_parse_xover(result);
-       xover_resp_item_free(result);
-       if (!msginfo) {
-               log_warning(LOG_PROTOCOL, _("invalid xover line\n"));
+               return;
        }
 
-       if(!msginfo) {
-               news_folder_unlock(NEWS_FOLDER(item->folder));
-               return NULL;
+       for (hdr = clist_begin(hdrlist); hdr; hdr = clist_next(hdr)) {
+               struct newsnntp_xhdr_resp_item *hdrval = clist_content(hdr);
+               msginfo = g_hash_table_lookup(hash_table, GINT_TO_POINTER(hdrval->hdr_article));
+               if (msginfo) {
+                       if (msginfo->newsgroups)
+                               g_free(msginfo->newsgroups);
+                       msginfo->newsgroups = g_strdup(hdrval->hdr_value);
+               }
        }
-
-       msginfo->folder = item;
+       newsnntp_xhdr_free(hdrlist);
        
-       news_set_msg_flags(item, msginfo);
-       msginfo->flags.tmp_flags |= MSG_NEWS;
-       msginfo->newsgroups = g_strdup(item->path);
+/* To */
+       ok = nntp_threaded_xhdr(item->folder, "to", first, last, &hdrlist);
 
-       ok = nntp_threaded_xhdr(folder, "to", num, num, &hdrlist);
-       
        if (ok != NEWSNNTP_NO_ERROR) {
                log_warning(LOG_PROTOCOL, _("couldn't get xhdr\n"));
                if (ok == NEWSNNTP_ERROR_STREAM) {
@@ -1057,14 +1081,23 @@ static MsgInfo *news_get_msginfo(Folder *folder, FolderItem *item, gint num)
                        REMOTE_FOLDER(item->folder)->session = NULL;
                }
                news_folder_unlock(NEWS_FOLDER(item->folder));
-               return msginfo;
+               return;
        }
 
-       msginfo->to = news_parse_xhdr(hdrlist, msginfo);
+       for (hdr = clist_begin(hdrlist); hdr; hdr = clist_next(hdr)) {
+               struct newsnntp_xhdr_resp_item *hdrval = clist_content(hdr);
+               msginfo = g_hash_table_lookup(hash_table, GINT_TO_POINTER(hdrval->hdr_article));
+               if (msginfo) {
+                       if (msginfo->to)
+                               g_free(msginfo->to);
+                       msginfo->to = g_strdup(hdrval->hdr_value);
+               }
+       }
        newsnntp_xhdr_free(hdrlist);
-
-       ok = nntp_threaded_xhdr(folder, "cc", num, num, &hdrlist);
        
+/* Cc */
+       ok = nntp_threaded_xhdr(item->folder, "cc", first, last, &hdrlist);
+
        if (ok != NEWSNNTP_NO_ERROR) {
                log_warning(LOG_PROTOCOL, _("couldn't get xhdr\n"));
                if (ok == NEWSNNTP_ERROR_STREAM) {
@@ -1072,14 +1105,23 @@ static MsgInfo *news_get_msginfo(Folder *folder, FolderItem *item, gint num)
                        REMOTE_FOLDER(item->folder)->session = NULL;
                }
                news_folder_unlock(NEWS_FOLDER(item->folder));
-               return msginfo;
+               return;
        }
 
-       msginfo->cc = news_parse_xhdr(hdrlist, msginfo);
+       for (hdr = clist_begin(hdrlist); hdr; hdr = clist_next(hdr)) {
+               struct newsnntp_xhdr_resp_item *hdrval = clist_content(hdr);
+               msginfo = g_hash_table_lookup(hash_table, GINT_TO_POINTER(hdrval->hdr_article));
+               if (msginfo) {
+                       if (msginfo->cc)
+                               g_free(msginfo->cc);
+                       msginfo->cc = g_strdup(hdrval->hdr_value);
+               }
+       }
        newsnntp_xhdr_free(hdrlist);
+       
 
+       g_hash_table_destroy(hash_table);
        news_folder_unlock(NEWS_FOLDER(item->folder));
-       return msginfo;
 }
 
 static GSList *news_get_msginfos_for_range(NewsSession *session, FolderItem *item, guint begin, guint end)
@@ -1090,8 +1132,8 @@ static GSList *news_get_msginfos_for_range(NewsSession *session, FolderItem *ite
        gint ok;
        clist *msglist = NULL;
        clistiter *cur;
-       g_return_val_if_fail(session != NULL, NULL);
-       g_return_val_if_fail(item != NULL, NULL);
+       cm_return_val_if_fail(session != NULL, NULL);
+       cm_return_val_if_fail(item != NULL, NULL);
 
        log_message(LOG_PROTOCOL, _("getting xover %d - %d in %s...\n"),
                    begin, end, item->path);
@@ -1110,36 +1152,61 @@ static GSList *news_get_msginfos_for_range(NewsSession *session, FolderItem *ite
                return NULL;
        }
 
-       for (cur = clist_begin(msglist); cur; cur = clist_next(cur)) {
-               struct newsnntp_xover_resp_item *ritem = (struct newsnntp_xover_resp_item *)clist_content(cur);
-               msginfo = news_parse_xover(ritem);
-               
-               if (!msginfo) {
-                       log_warning(LOG_PROTOCOL, _("invalid xover line\n"));
-                       continue;
-               }
+       if (msglist) {
+               for (cur = clist_begin(msglist); cur; cur = clist_next(cur)) {
+                       struct newsnntp_xover_resp_item *ritem = (struct newsnntp_xover_resp_item *)clist_content(cur);
+                       msginfo = news_parse_xover(ritem);
+                       
+                       if (!msginfo) {
+                               log_warning(LOG_PROTOCOL, _("invalid xover line\n"));
+                               continue;
+                       }
 
-               msginfo->folder = item;
-               news_set_msg_flags(item, msginfo);
-               msginfo->flags.tmp_flags |= MSG_NEWS;
-               msginfo->newsgroups = g_strdup(item->path);
+                       msginfo->folder = item;
+                       news_set_msg_flags(item, msginfo);
+                       msginfo->flags.tmp_flags |= MSG_NEWS;
 
-               if (!newlist)
-                       llast = newlist = g_slist_append(newlist, msginfo);
-               else {
-                       llast = g_slist_append(llast, msginfo);
-                       llast = llast->next;
+                       if (!newlist)
+                               llast = newlist = g_slist_append(newlist, msginfo);
+                       else {
+                               llast = g_slist_append(llast, msginfo);
+                               llast = llast->next;
+                       }
                }
+               newsnntp_xover_resp_list_free(msglist);
        }
-       newsnntp_xover_resp_list_free(msglist);
 
        news_folder_unlock(NEWS_FOLDER(item->folder));
 
        session_set_access_time(SESSION(session));
 
+       news_get_extra_fields(session, item, newlist);
+       
        return newlist;
 }
 
+static MsgInfo *news_get_msginfo(Folder *folder, FolderItem *item, gint num)
+{
+       GSList *msglist = NULL;
+       NewsSession *session;
+       MsgInfo *msginfo = NULL;
+
+       session = news_session_get(folder);
+       cm_return_val_if_fail(session != NULL, NULL);
+       cm_return_val_if_fail(item != NULL, NULL);
+       cm_return_val_if_fail(item->folder != NULL, NULL);
+       cm_return_val_if_fail(FOLDER_CLASS(item->folder) == &news_class, NULL);
+
+       msglist = news_get_msginfos_for_range(session, item, num, num);
+       if (msglist)
+               msginfo = msglist->data;
+       
+       g_slist_free(msglist);
+       
+       return msginfo;
+}
+
 static GSList *news_get_msginfos(Folder *folder, FolderItem *item, GSList *msgnum_list)
 {
        NewsSession *session;
@@ -1147,13 +1214,13 @@ static GSList *news_get_msginfos(Folder *folder, FolderItem *item, GSList *msgnu
        guint first, last, next;
        guint tofetch, fetched;
        
-       g_return_val_if_fail(folder != NULL, NULL);
-       g_return_val_if_fail(FOLDER_CLASS(folder) == &news_class, NULL);
-       g_return_val_if_fail(msgnum_list != NULL, NULL);
-       g_return_val_if_fail(item != NULL, NULL);
+       cm_return_val_if_fail(folder != NULL, NULL);
+       cm_return_val_if_fail(FOLDER_CLASS(folder) == &news_class, NULL);
+       cm_return_val_if_fail(msgnum_list != NULL, NULL);
+       cm_return_val_if_fail(item != NULL, NULL);
        
        session = news_session_get(folder);
-       g_return_val_if_fail(session != NULL, NULL);
+       cm_return_val_if_fail(session != NULL, NULL);
 
        tmp_msgnum_list = g_slist_copy(msgnum_list);
        tmp_msgnum_list = g_slist_sort(tmp_msgnum_list, g_int_compare);
@@ -1206,13 +1273,33 @@ void news_synchronise(FolderItem *item, gint days)
        news_gtk_synchronise(item, days);
 }
 
+static gint news_rename_folder(Folder *folder, FolderItem *item,
+                               const gchar *name)
+{
+       gchar *path;
+        
+       cm_return_val_if_fail(folder != NULL, -1);
+       cm_return_val_if_fail(item != NULL, -1);
+       cm_return_val_if_fail(item->path != NULL, -1);
+       cm_return_val_if_fail(name != NULL, -1);
+
+       path = folder_item_get_path(item);
+       if (!is_dir_exist(path))
+               make_dir_hier(path);
+
+       g_free(item->name);
+       item->name = g_strdup(name);
+
+       return 0;
+}
+
 static gint news_remove_folder(Folder *folder, FolderItem *item)
 {
        gchar *path;
 
-       g_return_val_if_fail(folder != NULL, -1);
-       g_return_val_if_fail(item != NULL, -1);
-       g_return_val_if_fail(item->path != NULL, -1);
+       cm_return_val_if_fail(folder != NULL, -1);
+       cm_return_val_if_fail(item != NULL, -1);
+       cm_return_val_if_fail(item->path != NULL, -1);
 
        path = folder_item_get_path(item);
        if (remove_dir_recursive(path) < 0) {