Fix bug #2975: Implement setting -0000 timezone to hide sender localtime
[claws.git] / src / news.c
index e1505edfa7eb47ffd312d046be8126aa3a29fc2c..5081a0e369c319918fa3e10a230fcaa64fd75904 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-2012 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
@@ -19,6 +19,7 @@
 
 #ifdef HAVE_CONFIG_H
 #  include "config.h"
+#include "claws-features.h"
 #endif
 
 #ifdef HAVE_LIBETPAN
@@ -47,6 +48,7 @@
 #include "statusbar.h"
 #include "codeconv.h"
 #include "utils.h"
+#include "passwordstore.h"
 #include "prefs_common.h"
 #include "prefs_account.h"
 #include "inputdialog.h"
 #include "remotefolder.h"
 #include "alertpanel.h"
 #include "inc.h"
+#include "account.h"
 #ifdef USE_GNUTLS
 #  include "ssl.h"
 #endif
+#include "main.h"
 
 #define NNTP_PORT      119
 #ifdef USE_GNUTLS
@@ -82,7 +86,7 @@ struct _NewsFolder
 struct _NewsSession
 {
        Session session;
-
+       Folder *folder;
        gchar *group;
 };
 
@@ -124,8 +128,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,
@@ -159,6 +161,7 @@ FolderClass *news_get_class(void)
                news_class.type = F_NEWS;
                news_class.idstr = "news";
                news_class.uistr = "News";
+               news_class.supports_server_search = FALSE;
 
                /* Folder functions */
                news_class.new_folder = news_folder_new;
@@ -176,6 +179,7 @@ FolderClass *news_get_class(void)
                news_class.get_msginfos = news_get_msginfos;
                news_class.fetch_msg = news_fetch_msg;
                news_class.synchronise = news_synchronise;
+               news_class.search_msgs = folder_item_search_msgs_local;
                news_class.remove_msg = news_remove_msg;
                news_class.remove_cached_msg = news_remove_cached_msg;
        };
@@ -205,8 +209,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))
@@ -275,12 +279,47 @@ 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);
 }
 
+static gboolean nntp_ping(gpointer data)
+{
+       Session *session = (Session *)data;
+       NewsSession *news_session = NEWS_SESSION(session);
+       int r;
+       struct tm lt;
+
+       if (session->state != SESSION_READY || news_folder_locked(news_session->folder))
+               return FALSE;
+       
+       news_folder_lock(NEWS_FOLDER(news_session->folder));
+
+       if ((r = nntp_threaded_date(news_session->folder, &lt)) != NEWSNNTP_NO_ERROR) {
+               if (r != NEWSNNTP_ERROR_COMMAND_NOT_SUPPORTED &&
+                   r != NEWSNNTP_ERROR_COMMAND_NOT_UNDERSTOOD) {
+                       log_warning(LOG_PROTOCOL, _("NNTP connection to %s:%d has been"
+                             " disconnected.\n"),
+                           news_session->folder->account->nntp_server,
+                           news_session->folder->account->set_nntpport ?
+                           news_session->folder->account->nntpport : NNTP_PORT);
+                       REMOTE_FOLDER(news_session->folder)->session = NULL;
+                       news_folder_unlock(NEWS_FOLDER(news_session->folder));
+                       session->state = SESSION_DISCONNECTED;
+                       session->sock = NULL;
+                       session_destroy(session);
+                       return FALSE;
+               }
+       }
+
+       news_folder_unlock(NEWS_FOLDER(news_session->folder));
+       session_set_access_time(session);
+       return TRUE;
+}
+
+
 #ifdef USE_GNUTLS
 static Session *news_session_new(Folder *folder, const gchar *server, gushort port,
                                 const gchar *userid, const gchar *passwd,
@@ -292,14 +331,16 @@ 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);
+       log_message(LOG_PROTOCOL, _("Account '%s': Connecting to NNTP server: %s:%d...\n"),
+                                   folder->account->account_name, server, port);
 
        session = g_new0(NewsSession, 1);
        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;
        
@@ -313,11 +354,13 @@ static Session *news_session_new(Folder *folder, const gchar *server, gushort po
                r = nntp_threaded_connect(folder, server, port);
        
        if (r != NEWSNNTP_NO_ERROR) {
-               log_error(LOG_PROTOCOL, _("Error logging in to %s:%d ...\n"), server, port);
+               log_error(LOG_PROTOCOL, _("Error logging in to %s:%d...\n"), server, port);
                session_destroy(SESSION(session));
                return NULL;
        }
-       
+
+       session->folder = folder;
+
        return SESSION(session);
 }
 
@@ -328,20 +371,12 @@ static Session *news_session_new_for_folder(Folder *folder)
        const gchar *userid = NULL;
        gchar *passwd = NULL;
        gushort port;
+       int r;
 
-       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]) {
-               userid = ac->userid;
-               if (ac->passwd && ac->passwd[0])
-                       passwd = g_strdup(ac->passwd);
-               else
-                       passwd = input_dialog_query_password_keep(ac->nntp_server,
-                                                                 userid,
-                                                                 &(ac->session_passwd));
-       }
 
 #ifdef USE_GNUTLS
        port = ac->set_nntpport ? ac->nntpport
@@ -366,10 +401,69 @@ static Session *news_session_new_for_folder(Folder *folder)
        session = news_session_new(folder, ac->nntp_server, port, userid, passwd);
 #endif
 
+       if (ac->use_nntp_auth && ac->userid && ac->userid[0]) {
+               userid = ac->userid;
+               if (password_get(userid, ac->nntp_server, "nntp", port, &passwd)) {
+                       /* NOP */;
+               } else if ((passwd = passwd_store_get_account(ac->account_id,
+                                       PWS_ACCOUNT_RECV)) == NULL) {
+                       passwd = input_dialog_query_password_keep(ac->nntp_server,
+                                                                 userid,
+                                                                 &(ac->session_passwd));
+               }
+       }
+
+       if (session != NULL)
+               r = nntp_threaded_mode_reader(folder);
+       else
+               r = NEWSNNTP_ERROR_CONNECTION_REFUSED;
+
+       if (r != NEWSNNTP_NO_ERROR) {
+           if (r == NEWSNNTP_WARNING_REQUEST_AUTHORIZATION_USERNAME) {
+               /*
+                  FIX ME when libetpan implements 480 to indicate authorization
+                  is required to use this capability. Libetpan treats a 480 as a
+                  381 which is clearly wrong.
+                  RFC 4643 section 2.
+                  Response code 480
+                  Generic response
+                  Meaning: command unavailable until the client
+                  has authenticated itself.
+               */
+               /* if the server does not advertise the capability MODE-READER,
+                  we normally should not send MODE READER. However this can't
+                  hurt: a transit-only server returns 502 and closes the cnx.
+                  Ref.: http://tools.ietf.org/html/rfc3977#section-5.3
+               */
+               log_error(LOG_PROTOCOL, _("Libetpan does not support return code 480 "
+               "so for now we choose to continue\n"));
+           }
+           else if (r == NEWSNNTP_ERROR_UNEXPECTED_RESPONSE) {
+               /* if the server does not advertise the capability MODE-READER,
+                  we normally should not send MODE READER. However this can't
+                  hurt: a transit-only server returns 502 and closes the cnx.
+                  Ref.: http://tools.ietf.org/html/rfc3977#section-5.3
+               */
+               log_error(LOG_PROTOCOL, _("Mode reader failed, continuing nevertheless\n")); 
+           }
+           else {
+               /* An error state bail out */
+               log_error(LOG_PROTOCOL, _("Error creating session with %s:%d\n"), ac->nntp_server, port);
+               if (session != NULL)
+                       session_destroy(SESSION(session));
+               g_free(passwd);
+               if (ac->session_passwd) {
+                       g_free(ac->session_passwd);
+                       ac->session_passwd = NULL;
+               }
+               return NULL;
+           }
+       }
+
        if ((session != NULL) && ac->use_nntp_auth) { /* FIXME:  && ac->use_nntp_auth_onconnect */
                if (nntp_threaded_login(folder, userid, passwd) !=
                        NEWSNNTP_NO_ERROR) {
-                       log_error(LOG_PROTOCOL, _("Error authenticating to %s:%d ...\n"), ac->nntp_server, port);
+                       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) {
@@ -387,12 +481,10 @@ static Session *news_session_new_for_folder(Folder *folder)
 static NewsSession *news_session_get(Folder *folder)
 {
        RemoteFolder *rfolder = REMOTE_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,
@@ -403,27 +495,30 @@ static NewsSession *news_session_get(Folder *folder)
 
        if (!rfolder->session) {
                rfolder->session = news_session_new_for_folder(folder);
+               session_register_ping(SESSION(rfolder->session), nntp_ping);
                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);
+               session_register_ping(SESSION(rfolder->session), nntp_ping);
+               goto newsession;
+       }
+       
        if (time(NULL) - rfolder->session->last_access_time <
                SESSION_TIMEOUT_INTERVAL) {
                return NEWS_SESSION(rfolder->session);
        }
 
-       if ((r = nntp_threaded_date(folder, &lt)) != NEWSNNTP_NO_ERROR) {
-               if (r != NEWSNNTP_ERROR_COMMAND_NOT_SUPPORTED &&
-                   r != NEWSNNTP_ERROR_COMMAND_NOT_UNDERSTOOD) {
-                       log_warning(LOG_PROTOCOL, _("NNTP connection to %s:%d has been"
-                             " disconnected. Reconnecting...\n"),
-                           folder->account->nntp_server,
-                           folder->account->set_nntpport ?
-                           folder->account->nntpport : NNTP_PORT);
-                       session_destroy(rfolder->session);
-                       rfolder->session = news_session_new_for_folder(folder);
-               }
+       if (!nntp_ping(rfolder->session)) {
+               rfolder->session = news_session_new_for_folder(folder);
+               session_register_ping(SESSION(rfolder->session), nntp_ping);
        }
-       
+
+newsession:
        if (rfolder->session)
                session_set_access_time(rfolder->session);
 
@@ -456,8 +551,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))
@@ -490,7 +585,7 @@ static gchar *news_fetch_msg(Folder *folder, FolderItem *item, gint num)
        ok = news_get_article(folder,
                              num, filename);
        if (ok != NEWSNNTP_NO_ERROR) {
-               g_warning("can't read article %d\n", num);
+               g_warning("can't read article %d", num);
                if (ok == NEWSNNTP_ERROR_STREAM) {
                        session_destroy(SESSION(session));
                        REMOTE_FOLDER(folder)->session = NULL;
@@ -536,8 +631,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))
@@ -575,25 +670,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));
@@ -659,15 +755,15 @@ 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);
        g_free(path);
 
        if (is_file_exist(filename)) {
-               if (remove(filename) < 0)
+               if (claws_unlink(filename) < 0)
                        FILE_OP_ERROR(filename, "remove");
        }
        g_free(filename);
@@ -679,9 +775,9 @@ gint news_post(Folder *folder, const gchar *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)  {
@@ -691,11 +787,6 @@ 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) {
@@ -715,9 +806,11 @@ static gint news_get_article(Folder *folder, gint num, gchar *filename)
        r = nntp_threaded_article(folder, num, &result, &len);
        
        if (r == NEWSNNTP_NO_ERROR) {
-               if (str_write_to_file(result, filename) < 0)
+               if (str_write_to_file(result, filename) < 0) {
+                       mmap_string_unref(result);
                        return -1;
-               g_free(result);
+               }
+               mmap_string_unref(result);
        }
        
        return r;
@@ -744,7 +837,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)
@@ -759,14 +852,6 @@ static gint news_select_group(Folder *folder, const gchar *group,
 
        ok = nntp_threaded_group(folder, group, &info);
        
-       if (ok != NEWSNNTP_NO_ERROR && 
-           ok != NEWSNNTP_ERROR_STREAM && 
-           ok != NEWSNNTP_WARNING_REQUEST_AUTHORIZATION_USERNAME) {
-               ok = nntp_threaded_mode_reader(folder);
-               if (ok == NEWSNNTP_NO_ERROR)
-                       ok = nntp_threaded_group(folder, group, &info);
-       }
-
        if (ok == NEWSNNTP_NO_ERROR && info) {
                session->group = g_strdup(group);
                *num = info->grp_first;
@@ -791,10 +876,10 @@ static MsgInfo *news_parse_xover(struct newsnntp_xover_resp_item *item)
        msginfo->date = g_strdup(item->ovr_date);
        msginfo->date_t = procheader_date_parse(NULL, item->ovr_date, 0);
 
-        msginfo->from = conv_unmime_header(item->ovr_author, NULL);
+       msginfo->from = conv_unmime_header(item->ovr_author, NULL, TRUE);
        msginfo->fromname = procheader_get_fromname(msginfo->from);
 
-        msginfo->subject = conv_unmime_header(item->ovr_subject, NULL);
+       msginfo->subject = conv_unmime_header(item->ovr_subject, NULL, TRUE);
 
        remove_return(msginfo->from);
        remove_return(msginfo->fromname);
@@ -840,16 +925,6 @@ 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;
@@ -867,10 +942,13 @@ gint news_cancel_article(Folder * folder, MsgInfo * msginfo)
        }
        if (change_file_mode_rw(tmpfp, tmp) < 0) {
                FILE_OP_ERROR(tmp, "chmod");
-               g_warning("can't change file mode\n");
+               g_warning("can't change file mode");
        }
        
-       get_rfc822_date(buf, sizeof(buf));
+       if (prefs_common.hide_timezone)
+               get_rfc822_date_hide_tz(buf, sizeof(buf));
+       else
+               get_rfc822_date(buf, sizeof(buf));
        if (fprintf(tmpfp, "From: %s\r\n"
                       "Newsgroups: %s\r\n"
                       "Subject: cmsg cancel <%s>\r\n"
@@ -902,7 +980,7 @@ gint news_cancel_article(Folder * folder, MsgInfo * msginfo)
        }
 
        news_post(folder, tmp);
-       remove(tmp);
+       claws_unlink(tmp);
 
        g_free(tmp);
 
@@ -913,7 +991,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,
@@ -926,11 +1004,11 @@ 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);
+        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,
@@ -960,12 +1038,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;
        
@@ -1019,54 +1097,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) {
@@ -1074,14 +1160,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) {
@@ -1089,14 +1184,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)
@@ -1107,14 +1211,21 @@ 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);
 
        news_folder_lock(NEWS_FOLDER(item->folder));
        
+       ok = news_select_group(item->folder, item->path, NULL, NULL, NULL);
+       if (ok != NEWSNNTP_NO_ERROR) {
+               log_warning(LOG_PROTOCOL, _("couldn't set group: %s\n"), item->path);
+               news_folder_unlock(NEWS_FOLDER(item->folder));
+               return NULL;
+       }
+
        ok = nntp_threaded_xover(item->folder, begin, end, NULL, &msglist);
        
        if (ok != NEWSNNTP_NO_ERROR) {
@@ -1127,57 +1238,79 @@ 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;
        GSList *elem, *msginfo_list = NULL, *tmp_msgnum_list, *tmp_msginfo_list;
        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);
 
        progressindicator_start(PROGRESS_TYPE_NETWORK);
-       tofetch = g_slist_length(tmp_msgnum_list);
-       fetched = 0;
 
        first = GPOINTER_TO_INT(tmp_msgnum_list->data);
        last = first;
@@ -1187,12 +1320,8 @@ static GSList *news_get_msginfos(Folder *folder, FolderItem *item, GSList *msgnu
        for(elem = g_slist_next(tmp_msgnum_list); elem != NULL; elem = g_slist_next(elem)) {
                next = GPOINTER_TO_INT(elem->data);
                if(next != (last + 1)) {
-/*                     session->fetch_base_percentage = ((gfloat) fetched) / ((gfloat) tofetch);
-                       session->fetch_total_percentage = ((gfloat) (last - first + 1)) / ((gfloat) tofetch);
-*/
                        tmp_msginfo_list = news_get_msginfos_for_range(session, item, first, last);
                        msginfo_list = g_slist_concat(msginfo_list, tmp_msginfo_list);
-                       fetched = last - first + 1;
                        first = next;
                }
                last = next;
@@ -1200,9 +1329,6 @@ static GSList *news_get_msginfos(Folder *folder, FolderItem *item, GSList *msgnu
        
        news_folder_unlock(NEWS_FOLDER(item->folder));
        
-/*     session->fetch_base_percentage = ((gfloat) fetched) / ((gfloat) tofetch);
-       session->fetch_total_percentage = ((gfloat) (last - first + 1)) / ((gfloat) tofetch);
-*/
        tmp_msginfo_list = news_get_msginfos_for_range(session, item, first, last);
        msginfo_list = g_slist_concat(msginfo_list, tmp_msginfo_list);
 
@@ -1228,10 +1354,10 @@ static gint news_rename_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);
-       g_return_val_if_fail(name != 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);
+       cm_return_val_if_fail(name != NULL, -1);
 
        path = folder_item_get_path(item);
        if (!is_dir_exist(path))
@@ -1247,13 +1373,13 @@ 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) {
-               g_warning("can't remove directory `%s'\n", path);
+               g_warning("can't remove directory '%s'", path);
                g_free(path);
                return -1;
        }
@@ -1263,6 +1389,48 @@ static gint news_remove_folder(Folder *folder, FolderItem *item)
        return 0;
 }
 
+void nntp_disconnect_all(gboolean have_connectivity)
+{
+       GList *list;
+       gboolean short_timeout;
+#ifdef HAVE_NETWORKMANAGER_SUPPORT
+       GError *error;
+#endif
+
+#ifdef HAVE_NETWORKMANAGER_SUPPORT
+       error = NULL;
+       short_timeout = !networkmanager_is_online(&error);
+       if(error) {
+               short_timeout = TRUE;
+               g_error_free(error);
+       }
+#else
+       short_timeout = TRUE;
+#endif
+
+       if(short_timeout)
+               nntp_main_set_timeout(1);
+
+       for (list = account_get_list(); list != NULL; list = list->next) {
+               PrefsAccount *account = list->data;
+               if (account->protocol == A_NNTP) {
+                       RemoteFolder *folder = (RemoteFolder *)account->folder;
+                       if (folder && folder->session) {
+                               NewsSession *session = (NewsSession *)folder->session;
+                               if (have_connectivity)
+                                       nntp_threaded_disconnect(FOLDER(folder));
+                               SESSION(session)->state = SESSION_DISCONNECTED;
+                               SESSION(session)->sock = NULL;
+                               session_destroy(SESSION(session));
+                               folder->session = NULL;
+                       }
+               }
+       }
+
+       if(short_timeout)
+               nntp_main_set_timeout(prefs_common.io_timeout_secs);
+}
+
 #else
 #include <glib.h>
 #include <glib/gi18n.h>
@@ -1281,7 +1449,7 @@ static void warn_etpan(void)
                        _("You have one or more News accounts "
                          "defined. However this version of "
                          "Claws Mail has been built without "
-                         "News support; your News account(s) are "
+                         "News support; your News accounts are "
                          "disabled.\n\n"
                          "You probably need to "
                          "install libetpan and recompile "
@@ -1339,5 +1507,8 @@ FolderClass *news_get_class(void)
        return &news_class;
 }
 
+void nntp_disconnect_all(gboolean have_connectivity)
+{
+}
 
 #endif