strip whitespace from subscribed URLs
[claws.git] / src / news.c
1 /*
2  * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3  * Copyright (C) 1999-2012 Hiroyuki Yamamoto and the Claws Mail team
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 3 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program. If not, see <http://www.gnu.org/licenses/>.
17  * 
18  */
19
20 #ifdef HAVE_CONFIG_H
21 #  include "config.h"
22 #include "claws-features.h"
23 #endif
24
25 #ifdef HAVE_LIBETPAN
26
27 #include "defs.h"
28
29 #include <glib.h>
30 #include <glib/gi18n.h>
31 #include <stdio.h>
32 #include <string.h>
33 #include <stdlib.h>
34 #include <dirent.h>
35 #include <unistd.h>
36 #include <time.h>
37 #include <libetpan/libetpan.h>
38
39 #include "nntp-thread.h"
40 #include "news.h"
41 #include "news_gtk.h"
42 #include "socket.h"
43 #include "recv.h"
44 #include "procmsg.h"
45 #include "procheader.h"
46 #include "folder.h"
47 #include "session.h"
48 #include "statusbar.h"
49 #include "codeconv.h"
50 #include "utils.h"
51 #include "passwordstore.h"
52 #include "prefs_common.h"
53 #include "prefs_account.h"
54 #include "inputdialog.h"
55 #include "log.h"
56 #include "progressindicator.h"
57 #include "remotefolder.h"
58 #include "alertpanel.h"
59 #include "inc.h"
60 #include "account.h"
61 #ifdef USE_GNUTLS
62 #  include "ssl.h"
63 #endif
64 #include "main.h"
65
66 #define NNTP_PORT       119
67 #ifdef USE_GNUTLS
68 #define NNTPS_PORT      563
69 #endif
70
71 typedef struct _NewsFolder      NewsFolder;
72 typedef struct _NewsSession     NewsSession;
73
74 #define NEWS_FOLDER(obj)        ((NewsFolder *)obj)
75 #define NEWS_SESSION(obj)       ((NewsSession *)obj)
76
77 struct _NewsFolder
78 {
79         RemoteFolder rfolder;
80
81         gboolean use_auth;
82         gboolean lock_count;
83         guint refcnt;
84 };
85
86 struct _NewsSession
87 {
88         Session session;
89         Folder *folder;
90         gchar *group;
91 };
92
93 static void news_folder_init(Folder *folder, const gchar *name,
94                              const gchar *path);
95
96 static Folder   *news_folder_new        (const gchar    *name,
97                                          const gchar    *folder);
98 static void      news_folder_destroy    (Folder         *folder);
99
100 static gchar *news_fetch_msg            (Folder         *folder,
101                                          FolderItem     *item,
102                                          gint            num);
103 static void news_remove_cached_msg      (Folder         *folder, 
104                                          FolderItem     *item, 
105                                          MsgInfo        *msginfo);
106 #ifdef USE_GNUTLS
107 static Session *news_session_new         (Folder                *folder,
108                                           const PrefsAccount    *account,
109                                           gushort                port,
110                                           SSLType                ssl_type);
111 #else
112 static Session *news_session_new         (Folder                *folder,
113                                           const PrefsAccount    *account,
114                                           gushort                port);
115 #endif
116
117 static gint news_get_article             (Folder        *folder,
118                                           gint           num,
119                                           gchar         *filename);
120
121 static gint news_select_group            (Folder        *folder,
122                                           const gchar   *group,
123                                           gint          *num,
124                                           gint          *first,
125                                           gint          *last);
126 static MsgInfo *news_parse_xover         (struct newsnntp_xover_resp_item *item);
127 static gint news_get_num_list                    (Folder        *folder, 
128                                           FolderItem    *item,
129                                           GSList       **list,
130                                           gboolean      *old_uids_valid);
131 static MsgInfo *news_get_msginfo                 (Folder        *folder, 
132                                           FolderItem    *item,
133                                           gint           num);
134 static GSList *news_get_msginfos                 (Folder        *folder,
135                                           FolderItem    *item,
136                                           GSList        *msgnum_list);
137 static gboolean news_scan_required               (Folder        *folder,
138                                           FolderItem    *item);
139
140 static gchar *news_folder_get_path       (Folder        *folder);
141 static gchar *news_item_get_path                 (Folder        *folder,
142                                           FolderItem    *item);
143 static void news_synchronise             (FolderItem    *item, gint days);
144 static int news_remove_msg               (Folder        *folder, 
145                                           FolderItem    *item, 
146                                           gint           msgnum);
147 static gint news_rename_folder           (Folder *folder,
148                                           FolderItem *item,
149                                           const gchar *name);
150 static gint news_remove_folder           (Folder        *folder,
151                                           FolderItem    *item);
152 static FolderClass news_class;
153
154 FolderClass *news_get_class(void)
155 {
156         if (news_class.idstr == NULL) {
157                 news_class.type = F_NEWS;
158                 news_class.idstr = "news";
159                 news_class.uistr = "News";
160                 news_class.supports_server_search = FALSE;
161
162                 /* Folder functions */
163                 news_class.new_folder = news_folder_new;
164                 news_class.destroy_folder = news_folder_destroy;
165
166                 /* FolderItem functions */
167                 news_class.item_get_path = news_item_get_path;
168                 news_class.get_num_list = news_get_num_list;
169                 news_class.scan_required = news_scan_required;
170                 news_class.rename_folder = news_rename_folder;
171                 news_class.remove_folder = news_remove_folder;
172
173                 /* Message functions */
174                 news_class.get_msginfo = news_get_msginfo;
175                 news_class.get_msginfos = news_get_msginfos;
176                 news_class.fetch_msg = news_fetch_msg;
177                 news_class.synchronise = news_synchronise;
178                 news_class.search_msgs = folder_item_search_msgs_local;
179                 news_class.remove_msg = news_remove_msg;
180                 news_class.remove_cached_msg = news_remove_cached_msg;
181         };
182
183         return &news_class;
184 }
185
186 guint nntp_folder_get_refcnt(Folder *folder)
187 {
188         return ((NewsFolder *)folder)->refcnt;
189 }
190
191 void nntp_folder_ref(Folder *folder)
192 {
193         ((NewsFolder *)folder)->refcnt++;
194 }
195
196 void nntp_folder_unref(Folder *folder)
197 {
198         if (((NewsFolder *)folder)->refcnt > 0)
199                 ((NewsFolder *)folder)->refcnt--;
200 }
201
202 static int news_remove_msg               (Folder        *folder, 
203                                           FolderItem    *item, 
204                                           gint           msgnum)
205 {
206         gchar *path, *filename;
207
208         cm_return_val_if_fail(folder != NULL, -1);
209         cm_return_val_if_fail(item != NULL, -1);
210
211         path = folder_item_get_path(item);
212         if (!is_dir_exist(path))
213                 make_dir_hier(path);
214         
215         filename = g_strconcat(path, G_DIR_SEPARATOR_S, itos(msgnum), NULL);
216         g_free(path);
217         claws_unlink(filename);
218         g_free(filename);
219         return 0;
220 }
221
222 static void news_folder_lock(NewsFolder *folder)
223 {
224         folder->lock_count++;
225 }
226
227 static void news_folder_unlock(NewsFolder *folder)
228 {
229         if (folder->lock_count > 0)
230                 folder->lock_count--;
231 }
232
233 int news_folder_locked(Folder *folder)
234 {
235         if (folder == NULL)
236                 return 0;
237
238         return NEWS_FOLDER(folder)->lock_count;
239 }
240
241 static Folder *news_folder_new(const gchar *name, const gchar *path)
242 {
243         Folder *folder;
244
245         folder = (Folder *)g_new0(NewsFolder, 1);
246         folder->klass = &news_class;
247         news_folder_init(folder, name, path);
248
249         return folder;
250 }
251
252 static void news_folder_destroy(Folder *folder)
253 {
254         gchar *dir;
255
256         while (nntp_folder_get_refcnt(folder) > 0)
257                 gtk_main_iteration();
258
259         dir = news_folder_get_path(folder);
260         if (is_dir_exist(dir))
261                 remove_dir_recursive(dir);
262         g_free(dir);
263
264         nntp_done(folder);
265         folder_remote_folder_destroy(REMOTE_FOLDER(folder));
266 }
267
268 static void news_folder_init(Folder *folder, const gchar *name,
269                              const gchar *path)
270 {
271         folder_remote_folder_init(folder, name, path);
272 }
273
274 static void news_session_destroy(Session *session)
275 {
276         NewsSession *news_session = NEWS_SESSION(session);
277
278         cm_return_if_fail(session != NULL);
279
280         if (news_session->group)
281                 g_free(news_session->group);
282 }
283
284 static gboolean nntp_ping(gpointer data)
285 {
286         Session *session = (Session *)data;
287         NewsSession *news_session = NEWS_SESSION(session);
288         int r;
289         struct tm lt;
290
291         if (session->state != SESSION_READY || news_folder_locked(news_session->folder))
292                 return FALSE;
293         
294         news_folder_lock(NEWS_FOLDER(news_session->folder));
295
296         if ((r = nntp_threaded_date(news_session->folder, &lt)) != NEWSNNTP_NO_ERROR) {
297                 if (r != NEWSNNTP_ERROR_COMMAND_NOT_SUPPORTED &&
298                     r != NEWSNNTP_ERROR_COMMAND_NOT_UNDERSTOOD) {
299                         log_warning(LOG_PROTOCOL, _("NNTP connection to %s:%d has been"
300                               " disconnected.\n"),
301                             news_session->folder->account->nntp_server,
302                             news_session->folder->account->set_nntpport ?
303                             news_session->folder->account->nntpport : NNTP_PORT);
304                         REMOTE_FOLDER(news_session->folder)->session = NULL;
305                         news_folder_unlock(NEWS_FOLDER(news_session->folder));
306                         session->state = SESSION_DISCONNECTED;
307                         session->sock = NULL;
308                         session_destroy(session);
309                         return FALSE;
310                 }
311         }
312
313         news_folder_unlock(NEWS_FOLDER(news_session->folder));
314         session_set_access_time(session);
315         return TRUE;
316 }
317
318
319 #ifdef USE_GNUTLS
320 static Session *news_session_new(Folder *folder, const PrefsAccount *account, gushort port,
321                                  SSLType ssl_type)
322 #else
323 static Session *news_session_new(Folder *folder, const PrefsAccount *account, gushort port)
324 #endif
325 {
326         NewsSession *session;
327         const char *server = account->nntp_server;
328         int r = 0;
329         ProxyInfo *proxy_info = NULL;
330
331         cm_return_val_if_fail(server != NULL, NULL);
332
333         log_message(LOG_PROTOCOL,
334                         _("Account '%s': Connecting to NNTP server: %s:%d...\n"),
335                         folder->account->account_name, server, port);
336
337         session = g_new0(NewsSession, 1);
338         session_init(SESSION(session), folder->account, FALSE);
339         SESSION(session)->type             = SESSION_NEWS;
340         SESSION(session)->server           = g_strdup(server);
341         SESSION(session)->port             = port;
342         SESSION(session)->sock             = NULL;
343         SESSION(session)->destroy          = news_session_destroy;
344
345         if (account->use_proxy) {
346                 if (account->use_default_proxy) {
347                         proxy_info = (ProxyInfo *)&(prefs_common.proxy_info);
348                         if (proxy_info->use_proxy_auth)
349                                 proxy_info->proxy_pass = passwd_store_get(PWS_CORE, PWS_CORE_PROXY,
350                                         PWS_CORE_PROXY_PASS);
351                 } else {
352                         proxy_info = (ProxyInfo *)&(account->proxy_info);
353                         if (proxy_info->use_proxy_auth)
354                                 proxy_info->proxy_pass = passwd_store_get_account(account->account_id,
355                                         PWS_ACCOUNT_PROXY_PASS);
356                 }
357         }
358         SESSION(session)->proxy_info = proxy_info;
359
360         nntp_init(folder);
361
362 #ifdef USE_GNUTLS
363         if (ssl_type != SSL_NONE)
364                 r = nntp_threaded_connect_ssl(folder, server, port, proxy_info);
365         else
366 #endif
367                 r = nntp_threaded_connect(folder, server, port, proxy_info);
368         
369         if (r != NEWSNNTP_NO_ERROR) {
370                 log_error(LOG_PROTOCOL, _("Error logging in to %s:%d...\n"), server, port);
371                 session_destroy(SESSION(session));
372                 return NULL;
373         }
374
375         session->folder = folder;
376
377         return SESSION(session);
378 }
379
380 static Session *news_session_new_for_folder(Folder *folder)
381 {
382         Session *session;
383         PrefsAccount *ac;
384         const gchar *userid = NULL;
385         gchar *passwd = NULL;
386         gushort port;
387         int r;
388
389         cm_return_val_if_fail(folder != NULL, NULL);
390         cm_return_val_if_fail(folder->account != NULL, NULL);
391
392         ac = folder->account;
393
394 #ifdef USE_GNUTLS
395         port = ac->set_nntpport ? ac->nntpport
396                 : ac->ssl_nntp ? NNTPS_PORT : NNTP_PORT;
397         session = news_session_new(folder, ac, port, ac->ssl_nntp);
398 #else
399         if (ac->ssl_nntp != SSL_NONE) {
400                 if (alertpanel_full(_("Insecure connection"),
401                         _("This connection is configured to be secured "
402                           "using SSL/TLS, but SSL/TLS is not available "
403                           "in this build of Claws Mail. \n\n"
404                           "Do you want to continue connecting to this "
405                           "server? The communication would not be "
406                           "secure."),
407                           GTK_STOCK_CANCEL, _("Con_tinue connecting"), 
408                           NULL, FALSE, NULL, ALERT_WARNING,
409                           G_ALERTDEFAULT) != G_ALERTALTERNATE)
410                         return NULL;
411         }
412         port = ac->set_nntpport ? ac->nntpport : NNTP_PORT;
413         session = news_session_new(folder, ac, port);
414 #endif
415
416         if (ac->use_nntp_auth && ac->userid && ac->userid[0]) {
417                 userid = ac->userid;
418                 if (password_get(userid, ac->nntp_server, "nntp", port, &passwd)) {
419                         /* NOP */;
420                 } else if ((passwd = passwd_store_get_account(ac->account_id,
421                                         PWS_ACCOUNT_RECV)) == NULL) {
422                         passwd = input_dialog_query_password_keep(ac->nntp_server,
423                                                                   userid,
424                                                                   &(ac->session_passwd));
425                 }
426         }
427
428         if (session != NULL)
429                 r = nntp_threaded_mode_reader(folder);
430         else
431                 r = NEWSNNTP_ERROR_CONNECTION_REFUSED;
432
433         if (r != NEWSNNTP_NO_ERROR) {
434             if (r == NEWSNNTP_WARNING_REQUEST_AUTHORIZATION_USERNAME) {
435                 /*
436                    FIX ME when libetpan implements 480 to indicate authorization
437                    is required to use this capability. Libetpan treats a 480 as a
438                    381 which is clearly wrong.
439                    RFC 4643 section 2.
440                    Response code 480
441                    Generic response
442                    Meaning: command unavailable until the client
443                    has authenticated itself.
444                 */
445                 /* if the server does not advertise the capability MODE-READER,
446                    we normally should not send MODE READER. However this can't
447                    hurt: a transit-only server returns 502 and closes the cnx.
448                    Ref.: http://tools.ietf.org/html/rfc3977#section-5.3
449                 */
450                 log_error(LOG_PROTOCOL, _("Libetpan does not support return code 480 "
451                 "so for now we choose to continue\n"));
452             }
453             else if (r == NEWSNNTP_ERROR_UNEXPECTED_RESPONSE) {
454                 /* if the server does not advertise the capability MODE-READER,
455                    we normally should not send MODE READER. However this can't
456                    hurt: a transit-only server returns 502 and closes the cnx.
457                    Ref.: http://tools.ietf.org/html/rfc3977#section-5.3
458                 */
459                 log_error(LOG_PROTOCOL, _("Mode reader failed, continuing nevertheless\n")); 
460             }
461             else {
462                 /* An error state bail out */
463                 log_error(LOG_PROTOCOL, _("Error creating session with %s:%d\n"), ac->nntp_server, port);
464                 if (session != NULL)
465                         session_destroy(SESSION(session));
466                 g_free(passwd);
467                 if (ac->session_passwd) {
468                         g_free(ac->session_passwd);
469                         ac->session_passwd = NULL;
470                 }
471                 return NULL;
472             }
473         }
474
475         if ((session != NULL) && ac->use_nntp_auth) { /* FIXME:  && ac->use_nntp_auth_onconnect */
476                 if (nntp_threaded_login(folder, userid, passwd) !=
477                         NEWSNNTP_NO_ERROR) {
478                         log_error(LOG_PROTOCOL, _("Error authenticating to %s:%d...\n"), ac->nntp_server, port);
479                         session_destroy(SESSION(session));
480                         g_free(passwd);
481                         if (ac->session_passwd) {
482                                 g_free(ac->session_passwd);
483                                 ac->session_passwd = NULL;
484                         }
485                         return NULL;
486                 }
487         }
488         g_free(passwd);
489
490         return session;
491 }
492
493 static NewsSession *news_session_get(Folder *folder)
494 {
495         RemoteFolder *rfolder = REMOTE_FOLDER(folder);
496         
497         cm_return_val_if_fail(folder != NULL, NULL);
498         cm_return_val_if_fail(FOLDER_CLASS(folder) == &news_class, NULL);
499         cm_return_val_if_fail(folder->account != NULL, NULL);
500
501         if (prefs_common.work_offline && 
502             !inc_offline_should_override(FALSE,
503                 _("Claws Mail needs network access in order "
504                   "to access the News server."))) {
505                 return NULL;
506         }
507
508         if (!rfolder->session) {
509                 rfolder->session = news_session_new_for_folder(folder);
510                 session_register_ping(SESSION(rfolder->session), nntp_ping);
511                 return NEWS_SESSION(rfolder->session);
512         }
513
514         /* Handle port change (also ssl/nossl change) without needing to
515          * restart application. */
516         if (rfolder->session->port != folder->account->nntpport) {
517                 session_destroy(rfolder->session);
518                 rfolder->session = news_session_new_for_folder(folder);
519                 session_register_ping(SESSION(rfolder->session), nntp_ping);
520                 goto newsession;
521         }
522         
523         if (time(NULL) - rfolder->session->last_access_time <
524                 SESSION_TIMEOUT_INTERVAL) {
525                 return NEWS_SESSION(rfolder->session);
526         }
527
528         if (!nntp_ping(rfolder->session)) {
529                 rfolder->session = news_session_new_for_folder(folder);
530                 session_register_ping(SESSION(rfolder->session), nntp_ping);
531         }
532
533 newsession:
534         if (rfolder->session)
535                 session_set_access_time(rfolder->session);
536
537         return NEWS_SESSION(rfolder->session);
538 }
539
540 static void news_remove_cached_msg(Folder *folder, FolderItem *item, MsgInfo *msginfo)
541 {
542         gchar *path, *filename;
543
544         path = folder_item_get_path(item);
545
546         if (!is_dir_exist(path)) {
547                 g_free(path);
548                 return;
549         }
550
551         filename = g_strconcat(path, G_DIR_SEPARATOR_S, itos(msginfo->msgnum), NULL);
552         g_free(path);
553
554         if (is_file_exist(filename)) {
555                 claws_unlink(filename);
556         }
557         g_free(filename);
558 }
559
560 static gchar *news_fetch_msg(Folder *folder, FolderItem *item, gint num)
561 {
562         gchar *path, *filename;
563         NewsSession *session;
564         gint ok;
565
566         cm_return_val_if_fail(folder != NULL, NULL);
567         cm_return_val_if_fail(item != NULL, NULL);
568
569         path = folder_item_get_path(item);
570         if (!is_dir_exist(path))
571                 make_dir_hier(path);
572         filename = g_strconcat(path, G_DIR_SEPARATOR_S, itos(num), NULL);
573         g_free(path);
574
575         if (is_file_exist(filename)) {
576                 debug_print("article %d has been already cached.\n", num);
577                 return filename;
578         }
579
580         session = news_session_get(folder);
581         if (!session) {
582                 g_free(filename);
583                 return NULL;
584         }
585
586         ok = news_select_group(folder, item->path, NULL, NULL, NULL);
587         if (ok != NEWSNNTP_NO_ERROR) {
588                 if (ok == NEWSNNTP_ERROR_STREAM) {
589                         session_destroy(SESSION(session));
590                         REMOTE_FOLDER(folder)->session = NULL;
591                 }
592                 g_free(filename);
593                 return NULL;
594         }
595
596         debug_print("getting article %d...\n", num);
597         ok = news_get_article(folder,
598                               num, filename);
599         if (ok != NEWSNNTP_NO_ERROR) {
600                 g_warning("can't read article %d", num);
601                 if (ok == NEWSNNTP_ERROR_STREAM) {
602                         session_destroy(SESSION(session));
603                         REMOTE_FOLDER(folder)->session = NULL;
604                 }
605                 g_free(filename);
606                 return NULL;
607         }
608         GTK_EVENTS_FLUSH();
609         return filename;
610 }
611
612 static NewsGroupInfo *news_group_info_new(const gchar *name,
613                                           gint first, gint last, gchar type)
614 {
615         NewsGroupInfo *ginfo;
616
617         ginfo = g_new(NewsGroupInfo, 1);
618         ginfo->name = g_strdup(name);
619         ginfo->first = first;
620         ginfo->last = last;
621         ginfo->type = type;
622
623         return ginfo;
624 }
625
626 static void news_group_info_free(NewsGroupInfo *ginfo)
627 {
628         g_free(ginfo->name);
629         g_free(ginfo);
630 }
631
632 static gint news_group_info_compare(NewsGroupInfo *ginfo1,
633                                     NewsGroupInfo *ginfo2)
634 {
635         return g_ascii_strcasecmp(ginfo1->name, ginfo2->name);
636 }
637
638 GSList *news_get_group_list(Folder *folder)
639 {
640         gchar *path, *filename;
641         FILE *fp;
642         GSList *list = NULL;
643         GSList *last = NULL;
644         gchar buf[BUFFSIZE];
645
646         cm_return_val_if_fail(folder != NULL, NULL);
647         cm_return_val_if_fail(FOLDER_CLASS(folder) == &news_class, NULL);
648
649         path = folder_item_get_path(FOLDER_ITEM(folder->node->data));
650         if (!is_dir_exist(path))
651                 make_dir_hier(path);
652         filename = g_strconcat(path, G_DIR_SEPARATOR_S, NEWSGROUP_LIST, NULL);
653         g_free(path);
654
655         if ((fp = g_fopen(filename, "rb")) == NULL) {
656                 NewsSession *session;
657                 gint ok;
658                 clist *grouplist = NULL;
659                 clistiter *cur;
660                 fp = g_fopen(filename, "wb");
661                 
662                 if (!fp) {
663                         g_free(filename);
664                         return NULL;
665                 }
666                 session = news_session_get(folder);
667                 if (!session) {
668                         fclose(fp);
669                         g_free(filename);
670                         return NULL;
671                 }
672
673                 ok = nntp_threaded_list(folder, &grouplist);
674                 
675                 if (ok != NEWSNNTP_NO_ERROR) {
676                         if (ok == NEWSNNTP_ERROR_STREAM) {
677                                 session_destroy(SESSION(session));
678                                 REMOTE_FOLDER(folder)->session = NULL;
679                         }
680                         fclose(fp);
681                         g_free(filename);
682                         return NULL;
683                 }
684                 
685                 if (grouplist) {
686                         for (cur = clist_begin(grouplist); cur; cur = clist_next(cur)) {
687                                 struct newsnntp_group_info *info = (struct newsnntp_group_info *)
688                                                                         clist_content(cur);
689                                 if (fprintf(fp, "%s %d %d %c\n",
690                                         info->grp_name,
691                                         info->grp_last,
692                                         info->grp_first,
693                                         info->grp_type) < 0) {
694                                         log_error(LOG_PROTOCOL, ("Can't write newsgroup list\n"));
695                                         session_destroy(SESSION(session));
696                                         REMOTE_FOLDER(folder)->session = NULL;
697                                         fclose(fp);
698                                         g_free(filename);
699                                         newsnntp_list_free(grouplist);
700                                         return NULL;
701                                 }
702                         }
703                         newsnntp_list_free(grouplist);
704                 }
705                 if (fclose(fp) == EOF) {
706                         log_error(LOG_PROTOCOL, ("Can't write newsgroup list\n"));
707                         session_destroy(SESSION(session));
708                         REMOTE_FOLDER(folder)->session = NULL;
709                         g_free(filename);
710                         return NULL;
711                 }
712
713                 if ((fp = g_fopen(filename, "rb")) == NULL) {
714                         FILE_OP_ERROR(filename, "fopen");
715                         g_free(filename);
716                         return NULL;
717                 }
718         }
719
720         while (fgets(buf, sizeof(buf), fp) != NULL) {
721                 gchar *p = buf;
722                 gchar *name;
723                 gint last_num;
724                 gint first_num;
725                 gchar type;
726                 NewsGroupInfo *ginfo;
727
728                 p = strchr(p, ' ');
729                 if (!p) continue;
730                 *p = '\0';
731                 p++;
732                 name = buf;
733
734                 if (sscanf(p, "%d %d %c", &last_num, &first_num, &type) < 3)
735                         continue;
736
737                 ginfo = news_group_info_new(name, first_num, last_num, type);
738
739                 if (!last)
740                         last = list = g_slist_append(NULL, ginfo);
741                 else {
742                         last = g_slist_append(last, ginfo);
743                         last = last->next;
744                 }
745         }
746
747         fclose(fp);
748         g_free(filename);
749
750         list = g_slist_sort(list, (GCompareFunc)news_group_info_compare);
751
752         return list;
753 }
754
755 void news_group_list_free(GSList *group_list)
756 {
757         GSList *cur;
758
759         if (!group_list) return;
760
761         for (cur = group_list; cur != NULL; cur = cur->next)
762                 news_group_info_free((NewsGroupInfo *)cur->data);
763         g_slist_free(group_list);
764 }
765
766 void news_remove_group_list_cache(Folder *folder)
767 {
768         gchar *path, *filename;
769
770         cm_return_if_fail(folder != NULL);
771         cm_return_if_fail(FOLDER_CLASS(folder) == &news_class);
772
773         path = folder_item_get_path(FOLDER_ITEM(folder->node->data));
774         filename = g_strconcat(path, G_DIR_SEPARATOR_S, NEWSGROUP_LIST, NULL);
775         g_free(path);
776
777         if (is_file_exist(filename)) {
778                 if (claws_unlink(filename) < 0)
779                         FILE_OP_ERROR(filename, "remove");
780         }
781         g_free(filename);
782 }
783
784 gint news_post(Folder *folder, const gchar *file)
785 {
786         gint ok;
787         char *contents = file_read_to_str_no_recode(file);
788         NewsSession *session;
789
790         cm_return_val_if_fail(folder != NULL, -1);
791         cm_return_val_if_fail(FOLDER_CLASS(folder) == &news_class, -1);
792         cm_return_val_if_fail(contents != NULL, -1);
793         
794         session = news_session_get(folder);
795         if (!session)  {
796                 g_free(contents);
797                 return -1;
798         }
799         
800         ok = nntp_threaded_post(folder, contents, strlen(contents));
801
802         g_free(contents);
803
804         if (ok == NEWSNNTP_ERROR_STREAM) {
805                 session_destroy(SESSION(session));
806                 REMOTE_FOLDER(folder)->session = NULL;
807         }
808
809         return (ok == NEWSNNTP_NO_ERROR ? 0 : -1);
810 }
811
812 static gint news_get_article(Folder *folder, gint num, gchar *filename)
813 {
814         size_t len;
815         char *result = NULL;
816         int r;
817         
818         r = nntp_threaded_article(folder, num, &result, &len);
819         
820         if (r == NEWSNNTP_NO_ERROR) {
821                 if (str_write_to_file(result, filename) < 0) {
822                         mmap_string_unref(result);
823                         return -1;
824                 }
825                 mmap_string_unref(result);
826         }
827         
828         return r;
829 }
830
831 /**
832  * news_select_group:
833  * @session: Active NNTP session.
834  * @group: Newsgroup name.
835  * @num: Estimated number of articles.
836  * @first: First article number.
837  * @last: Last article number.
838  *
839  * Select newsgroup @group with the GROUP command if it is not already
840  * selected in @session, or article numbers need to be returned.
841  *
842  * Return value: NNTP result code.
843  **/
844 static gint news_select_group(Folder *folder, const gchar *group,
845                               gint *num, gint *first, gint *last)
846 {
847         gint ok;
848         gint num_, first_, last_;
849         struct newsnntp_group_info *info = NULL;
850         NewsSession *session = NEWS_SESSION(news_session_get(folder));
851
852         cm_return_val_if_fail(session != NULL, -1);
853         
854         if (!num || !first || !last) {
855                 if (session->group && g_ascii_strcasecmp(session->group, group) == 0)
856                         return NEWSNNTP_NO_ERROR;
857                 num = &num_;
858                 first = &first_;
859                 last = &last_;
860         }
861
862         g_free(session->group);
863         session->group = NULL;
864
865         ok = nntp_threaded_group(folder, group, &info);
866         
867         if (ok == NEWSNNTP_NO_ERROR && info) {
868                 session->group = g_strdup(group);
869                 *num = info->grp_first;
870                 *first = info->grp_first;
871                 *last = info->grp_last;
872                 newsnntp_group_free(info);
873         } else {
874                 log_warning(LOG_PROTOCOL, _("couldn't select group: %s\n"), group);
875         }
876         return ok;
877 }
878
879 static MsgInfo *news_parse_xover(struct newsnntp_xover_resp_item *item)
880 {
881         MsgInfo *msginfo;
882
883         /* set MsgInfo */
884         msginfo = procmsg_msginfo_new();
885         msginfo->msgnum = item->ovr_article;
886         msginfo->size = item->ovr_size;
887
888         msginfo->date = g_strdup(item->ovr_date);
889         msginfo->date_t = procheader_date_parse(NULL, item->ovr_date, 0);
890
891         msginfo->from = conv_unmime_header(item->ovr_author, NULL, TRUE);
892         msginfo->fromname = procheader_get_fromname(msginfo->from);
893
894         msginfo->subject = conv_unmime_header(item->ovr_subject, NULL, TRUE);
895
896         remove_return(msginfo->from);
897         remove_return(msginfo->fromname);
898         remove_return(msginfo->subject);
899
900         if (item->ovr_message_id) {
901                 gchar *tmp = g_strdup(item->ovr_message_id);
902                 extract_parenthesis(tmp, '<', '>');
903                 remove_space(tmp);
904                 if (*tmp != '\0')
905                         msginfo->msgid = g_strdup(tmp);
906                 g_free(tmp);
907         }                        
908
909         /* FIXME: this is a quick fix; references' meaning was changed
910          * into having the actual list of references in the References: header.
911          * We need a GSList here, so msginfo_free() and msginfo_copy() can do 
912          * their things properly. */ 
913         if (item->ovr_references && *(item->ovr_references)) {   
914                 gchar **ref_tokens = g_strsplit(item->ovr_references, " ", -1);
915                 guint i = 0;
916                 char *tmp;
917                 char *p;
918                 while (ref_tokens[i]) {
919                         gchar *cur_ref = ref_tokens[i];
920                         msginfo->references = references_list_append(msginfo->references, 
921                                         cur_ref);
922                         i++;
923                 }
924                 g_strfreev(ref_tokens);
925                 
926                 tmp = g_strdup(item->ovr_references);
927                 eliminate_parenthesis(tmp, '(', ')');
928                 if ((p = strrchr(tmp, '<')) != NULL) {
929                         extract_parenthesis(p, '<', '>');
930                         remove_space(p);
931                         if (*p != '\0')
932                                 msginfo->inreplyto = g_strdup(p);
933                 }
934                 g_free(tmp);
935         } 
936
937         return msginfo;
938 }
939
940 gint news_cancel_article(Folder * folder, MsgInfo * msginfo)
941 {
942         gchar * tmp;
943         FILE * tmpfp;
944         gchar date[RFC822_DATE_BUFFSIZE];
945
946         tmp = g_strdup_printf("%s%ccancel%p", get_tmp_dir(),
947                               G_DIR_SEPARATOR, msginfo);
948         if (tmp == NULL)
949                 return -1;
950
951         if ((tmpfp = g_fopen(tmp, "wb")) == NULL) {
952                 FILE_OP_ERROR(tmp, "fopen");
953                 return -1;
954         }
955         if (change_file_mode_rw(tmpfp, tmp) < 0) {
956                 FILE_OP_ERROR(tmp, "chmod");
957                 g_warning("can't change file mode");
958         }
959         
960         if (prefs_common.hide_timezone)
961                 get_rfc822_date_hide_tz(date, sizeof(date));
962         else
963                 get_rfc822_date(date, sizeof(date));
964         if (fprintf(tmpfp, "From: %s\r\n"
965                        "Newsgroups: %s\r\n"
966                        "Subject: cmsg cancel <%s>\r\n"
967                        "Control: cancel <%s>\r\n"
968                        "Approved: %s\r\n"
969                        "X-Cancelled-by: %s\r\n"
970                        "Date: %s\r\n"
971                        "\r\n"
972                        "removed with Claws Mail\r\n",
973                        msginfo->from,
974                        msginfo->newsgroups,
975                        msginfo->msgid,
976                        msginfo->msgid,
977                        msginfo->from,
978                        msginfo->from,
979                        date) < 0) {
980                 FILE_OP_ERROR(tmp, "fprintf");
981                 fclose(tmpfp);
982                 claws_unlink(tmp);
983                 g_free(tmp);
984                 return -1;
985         }
986
987         if (fclose(tmpfp) == EOF) {
988                 FILE_OP_ERROR(tmp, "fclose");
989                 claws_unlink(tmp);
990                 g_free(tmp);
991                 return -1;
992         }
993
994         news_post(folder, tmp);
995         claws_unlink(tmp);
996
997         g_free(tmp);
998
999         return 0;
1000 }
1001
1002 static gchar *news_folder_get_path(Folder *folder)
1003 {
1004         gchar *folder_path;
1005
1006         cm_return_val_if_fail(folder->account != NULL, NULL);
1007
1008         folder_path = g_strconcat(get_news_cache_dir(),
1009                                   G_DIR_SEPARATOR_S,
1010                                   folder->account->nntp_server,
1011                                   NULL);
1012         return folder_path;
1013 }
1014
1015 static gchar *news_item_get_path(Folder *folder, FolderItem *item)
1016 {
1017         gchar *folder_path, *path;
1018
1019         cm_return_val_if_fail(folder != NULL, NULL);
1020         cm_return_val_if_fail(item != NULL, NULL);
1021         folder_path = news_folder_get_path(folder);
1022
1023         cm_return_val_if_fail(folder_path != NULL, NULL);
1024         if (g_path_is_absolute(folder_path)) {
1025                 if (item->path)
1026                         path = g_strconcat(folder_path, G_DIR_SEPARATOR_S,
1027                                            item->path, NULL);
1028                 else
1029                         path = g_strdup(folder_path);
1030         } else {
1031                 if (item->path)
1032                         path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1033                                            folder_path, G_DIR_SEPARATOR_S,
1034                                            item->path, NULL);
1035                 else
1036                         path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1037                                            folder_path, NULL);
1038         }
1039         g_free(folder_path);
1040 #ifdef G_OS_WIN32
1041         while (strchr(path, '/'))
1042                 *strchr(path, '/') = '\\';
1043 #endif
1044         return path;
1045 }
1046
1047 static gint news_get_num_list(Folder *folder, FolderItem *item, GSList **msgnum_list, gboolean *old_uids_valid)
1048 {
1049         NewsSession *session;
1050         gint i, ok, num, first, last, nummsgs = 0;
1051         gchar *dir;
1052
1053         cm_return_val_if_fail(item != NULL, -1);
1054         cm_return_val_if_fail(item->folder != NULL, -1);
1055         cm_return_val_if_fail(FOLDER_CLASS(folder) == &news_class, -1);
1056
1057         session = news_session_get(folder);
1058         cm_return_val_if_fail(session != NULL, -1);
1059
1060         *old_uids_valid = TRUE;
1061         
1062         news_folder_lock(NEWS_FOLDER(item->folder));
1063
1064         ok = news_select_group(folder, item->path, &num, &first, &last);
1065         if (ok != NEWSNNTP_NO_ERROR) {
1066                 log_warning(LOG_PROTOCOL, _("couldn't set group: %s\n"), item->path);
1067                 news_folder_unlock(NEWS_FOLDER(item->folder));
1068                 return -1;
1069         }
1070
1071         dir = news_folder_get_path(folder);
1072         if (num <= 0)
1073                 remove_all_numbered_files(dir);
1074         else if (last < first)
1075                 log_warning(LOG_PROTOCOL, _("invalid article range: %d - %d\n"),
1076                             first, last);
1077         else {
1078                 for (i = first; i <= last; i++) {
1079                         *msgnum_list = g_slist_prepend(*msgnum_list, 
1080                                                        GINT_TO_POINTER(i));
1081                         nummsgs++;
1082                 }
1083                 debug_print("removing old messages from %d to %d in %s\n",
1084                             first, last, dir);
1085                 remove_numbered_files(dir, 1, first - 1);
1086         }
1087         g_free(dir);
1088         news_folder_unlock(NEWS_FOLDER(item->folder));
1089         return nummsgs;
1090 }
1091
1092 static void news_set_msg_flags(FolderItem *item, MsgInfo *msginfo)
1093 {
1094         msginfo->flags.tmp_flags = 0;
1095         if (item->folder->account->mark_crosspost_read && msginfo->msgid) {
1096                 if (item->folder->newsart &&
1097                     g_hash_table_lookup(item->folder->newsart, msginfo->msgid) != NULL) {
1098                         msginfo->flags.perm_flags = MSG_COLORLABEL_TO_FLAGS(item->folder->account->crosspost_col);
1099                                 
1100                 } else {
1101                         if (!item->folder->newsart) 
1102                                 item->folder->newsart = g_hash_table_new(g_str_hash, g_str_equal);
1103                         g_hash_table_insert(item->folder->newsart,
1104                                         g_strdup(msginfo->msgid), GINT_TO_POINTER(1));
1105                         msginfo->flags.perm_flags = MSG_NEW|MSG_UNREAD;
1106                 }
1107         } else {
1108                 msginfo->flags.perm_flags = MSG_NEW|MSG_UNREAD;
1109         }
1110 }
1111
1112 static void news_get_extra_fields(NewsSession *session, FolderItem *item, GSList *msglist)
1113 {
1114         MsgInfo *msginfo = NULL;
1115         gint ok;
1116         GSList *cur;
1117         clist *hdrlist = NULL;
1118         clistiter *hdr;
1119         gint first = -1, last = -1;
1120         GHashTable *hash_table;
1121         
1122         cm_return_if_fail(session != NULL);
1123         cm_return_if_fail(item != NULL);
1124         cm_return_if_fail(item->folder != NULL);
1125         cm_return_if_fail(FOLDER_CLASS(item->folder) == &news_class);
1126
1127         news_folder_lock(NEWS_FOLDER(item->folder));
1128
1129         hash_table = g_hash_table_new(g_direct_hash, g_direct_equal);
1130         
1131         for (cur = msglist; cur; cur = cur->next) {
1132                 msginfo = (MsgInfo *)cur->data;
1133                 if (first == -1 || msginfo->msgnum < first)
1134                         first = msginfo->msgnum;
1135                 if (last == -1 || msginfo->msgnum > last)
1136                         last = msginfo->msgnum;
1137                 g_hash_table_insert(hash_table,
1138                                 GINT_TO_POINTER(msginfo->msgnum), msginfo);
1139         }
1140
1141 /* Newsgroups */
1142         ok = nntp_threaded_xhdr(item->folder, "newsgroups", first, last, &hdrlist);
1143
1144         if (ok != NEWSNNTP_NO_ERROR) {
1145                 log_warning(LOG_PROTOCOL, _("couldn't get xhdr\n"));
1146                 if (ok == NEWSNNTP_ERROR_STREAM) {
1147                         session_destroy(SESSION(session));
1148                         REMOTE_FOLDER(item->folder)->session = NULL;
1149                 }
1150                 news_folder_unlock(NEWS_FOLDER(item->folder));
1151                 if (hdrlist != NULL)
1152                         newsnntp_xhdr_free(hdrlist);
1153                 return;
1154         }
1155
1156         for (hdr = clist_begin(hdrlist); hdr; hdr = clist_next(hdr)) {
1157                 struct newsnntp_xhdr_resp_item *hdrval = clist_content(hdr);
1158                 msginfo = g_hash_table_lookup(hash_table, GINT_TO_POINTER(hdrval->hdr_article));
1159                 if (msginfo) {
1160                         if (msginfo->newsgroups)
1161                                 g_free(msginfo->newsgroups);
1162                         msginfo->newsgroups = g_strdup(hdrval->hdr_value);
1163                 }
1164         }
1165         newsnntp_xhdr_free(hdrlist);
1166         hdrlist = NULL;
1167         
1168 /* To */
1169         ok = nntp_threaded_xhdr(item->folder, "to", first, last, &hdrlist);
1170
1171         if (ok != NEWSNNTP_NO_ERROR) {
1172                 log_warning(LOG_PROTOCOL, _("couldn't get xhdr\n"));
1173                 if (ok == NEWSNNTP_ERROR_STREAM) {
1174                         session_destroy(SESSION(session));
1175                         REMOTE_FOLDER(item->folder)->session = NULL;
1176                 }
1177                 news_folder_unlock(NEWS_FOLDER(item->folder));
1178                 if (hdrlist != NULL)
1179                         newsnntp_xhdr_free(hdrlist);
1180                 return;
1181         }
1182
1183         for (hdr = clist_begin(hdrlist); hdr; hdr = clist_next(hdr)) {
1184                 struct newsnntp_xhdr_resp_item *hdrval = clist_content(hdr);
1185                 msginfo = g_hash_table_lookup(hash_table, GINT_TO_POINTER(hdrval->hdr_article));
1186                 if (msginfo) {
1187                         if (msginfo->to)
1188                                 g_free(msginfo->to);
1189                         msginfo->to = g_strdup(hdrval->hdr_value);
1190                 }
1191         }
1192         newsnntp_xhdr_free(hdrlist);
1193         hdrlist = NULL;
1194         
1195 /* Cc */
1196         ok = nntp_threaded_xhdr(item->folder, "cc", first, last, &hdrlist);
1197
1198         if (ok != NEWSNNTP_NO_ERROR) {
1199                 log_warning(LOG_PROTOCOL, _("couldn't get xhdr\n"));
1200                 if (ok == NEWSNNTP_ERROR_STREAM) {
1201                         session_destroy(SESSION(session));
1202                         REMOTE_FOLDER(item->folder)->session = NULL;
1203                 }
1204                 news_folder_unlock(NEWS_FOLDER(item->folder));
1205                 if (hdrlist != NULL)
1206                         newsnntp_xhdr_free(hdrlist);
1207                 return;
1208         }
1209
1210         for (hdr = clist_begin(hdrlist); hdr; hdr = clist_next(hdr)) {
1211                 struct newsnntp_xhdr_resp_item *hdrval = clist_content(hdr);
1212                 msginfo = g_hash_table_lookup(hash_table, GINT_TO_POINTER(hdrval->hdr_article));
1213                 if (msginfo) {
1214                         if (msginfo->cc)
1215                                 g_free(msginfo->cc);
1216                         msginfo->cc = g_strdup(hdrval->hdr_value);
1217                 }
1218         }
1219         newsnntp_xhdr_free(hdrlist);
1220         hdrlist = NULL;
1221
1222         g_hash_table_destroy(hash_table);
1223         news_folder_unlock(NEWS_FOLDER(item->folder));
1224 }
1225
1226 static GSList *news_get_msginfos_for_range(NewsSession *session, FolderItem *item, guint begin, guint end)
1227 {
1228         GSList *newlist = NULL;
1229         GSList *llast = NULL;
1230         MsgInfo *msginfo;
1231         gint ok;
1232         clist *msglist = NULL;
1233         clistiter *cur;
1234         cm_return_val_if_fail(session != NULL, NULL);
1235         cm_return_val_if_fail(item != NULL, NULL);
1236
1237         log_message(LOG_PROTOCOL, _("getting xover %d - %d in %s...\n"),
1238                     begin, end, item->path);
1239
1240         news_folder_lock(NEWS_FOLDER(item->folder));
1241         
1242         ok = news_select_group(item->folder, item->path, NULL, NULL, NULL);
1243         if (ok != NEWSNNTP_NO_ERROR) {
1244                 log_warning(LOG_PROTOCOL, _("couldn't set group: %s\n"), item->path);
1245                 news_folder_unlock(NEWS_FOLDER(item->folder));
1246                 return NULL;
1247         }
1248
1249         ok = nntp_threaded_xover(item->folder, begin, end, NULL, &msglist);
1250         
1251         if (ok != NEWSNNTP_NO_ERROR) {
1252                 log_warning(LOG_PROTOCOL, _("couldn't get xover\n"));
1253                 if (ok == NEWSNNTP_ERROR_STREAM) {
1254                         session_destroy(SESSION(session));
1255                         REMOTE_FOLDER(item->folder)->session = NULL;
1256                 }
1257                 news_folder_unlock(NEWS_FOLDER(item->folder));
1258                 if (msglist != NULL)
1259                         newsnntp_xover_resp_list_free(msglist);
1260                 return NULL;
1261         }
1262
1263         if (msglist) {
1264                 for (cur = clist_begin(msglist); cur; cur = clist_next(cur)) {
1265                         struct newsnntp_xover_resp_item *ritem = (struct newsnntp_xover_resp_item *)clist_content(cur);
1266                         msginfo = news_parse_xover(ritem);
1267                         
1268                         if (!msginfo) {
1269                                 log_warning(LOG_PROTOCOL, _("invalid xover line\n"));
1270                                 continue;
1271                         }
1272
1273                         msginfo->folder = item;
1274                         news_set_msg_flags(item, msginfo);
1275                         msginfo->flags.tmp_flags |= MSG_NEWS;
1276
1277                         if (!newlist)
1278                                 llast = newlist = g_slist_append(newlist, msginfo);
1279                         else {
1280                                 llast = g_slist_append(llast, msginfo);
1281                                 llast = llast->next;
1282                         }
1283                 }
1284                 newsnntp_xover_resp_list_free(msglist);
1285         }
1286
1287         news_folder_unlock(NEWS_FOLDER(item->folder));
1288
1289         session_set_access_time(SESSION(session));
1290
1291         news_get_extra_fields(session, item, newlist);
1292         
1293         return newlist;
1294 }
1295
1296 static MsgInfo *news_get_msginfo(Folder *folder, FolderItem *item, gint num)
1297 {
1298         GSList *msglist = NULL;
1299         NewsSession *session;
1300         MsgInfo *msginfo = NULL;
1301
1302         session = news_session_get(folder);
1303         cm_return_val_if_fail(session != NULL, NULL);
1304         cm_return_val_if_fail(item != NULL, NULL);
1305         cm_return_val_if_fail(item->folder != NULL, NULL);
1306         cm_return_val_if_fail(FOLDER_CLASS(item->folder) == &news_class, NULL);
1307
1308         msglist = news_get_msginfos_for_range(session, item, num, num);
1309  
1310         if (msglist)
1311                 msginfo = msglist->data;
1312         
1313         g_slist_free(msglist);
1314         
1315         return msginfo;
1316 }
1317
1318 static GSList *news_get_msginfos(Folder *folder, FolderItem *item, GSList *msgnum_list)
1319 {
1320         NewsSession *session;
1321         GSList *elem, *msginfo_list = NULL, *tmp_msgnum_list, *tmp_msginfo_list;
1322         guint first, last, next;
1323         
1324         cm_return_val_if_fail(folder != NULL, NULL);
1325         cm_return_val_if_fail(FOLDER_CLASS(folder) == &news_class, NULL);
1326         cm_return_val_if_fail(msgnum_list != NULL, NULL);
1327         cm_return_val_if_fail(item != NULL, NULL);
1328         
1329         session = news_session_get(folder);
1330         cm_return_val_if_fail(session != NULL, NULL);
1331
1332         tmp_msgnum_list = g_slist_copy(msgnum_list);
1333         tmp_msgnum_list = g_slist_sort(tmp_msgnum_list, g_int_compare);
1334
1335         progressindicator_start(PROGRESS_TYPE_NETWORK);
1336
1337         first = GPOINTER_TO_INT(tmp_msgnum_list->data);
1338         last = first;
1339         
1340         news_folder_lock(NEWS_FOLDER(item->folder));
1341         
1342         for(elem = g_slist_next(tmp_msgnum_list); elem != NULL; elem = g_slist_next(elem)) {
1343                 next = GPOINTER_TO_INT(elem->data);
1344                 if(next != (last + 1)) {
1345                         tmp_msginfo_list = news_get_msginfos_for_range(session, item, first, last);
1346                         msginfo_list = g_slist_concat(msginfo_list, tmp_msginfo_list);
1347                         first = next;
1348                 }
1349                 last = next;
1350         }
1351         
1352         news_folder_unlock(NEWS_FOLDER(item->folder));
1353         
1354         tmp_msginfo_list = news_get_msginfos_for_range(session, item, first, last);
1355         msginfo_list = g_slist_concat(msginfo_list, tmp_msginfo_list);
1356
1357         g_slist_free(tmp_msgnum_list);
1358         
1359         progressindicator_stop(PROGRESS_TYPE_NETWORK);
1360
1361         return msginfo_list;
1362 }
1363
1364 static gboolean news_scan_required(Folder *folder, FolderItem *item)
1365 {
1366         return TRUE;
1367 }
1368
1369 void news_synchronise(FolderItem *item, gint days) 
1370 {
1371         news_gtk_synchronise(item, days);
1372 }
1373
1374 static gint news_rename_folder(Folder *folder, FolderItem *item,
1375                                 const gchar *name)
1376 {
1377         gchar *path;
1378          
1379         cm_return_val_if_fail(folder != NULL, -1);
1380         cm_return_val_if_fail(item != NULL, -1);
1381         cm_return_val_if_fail(item->path != NULL, -1);
1382         cm_return_val_if_fail(name != NULL, -1);
1383
1384         path = folder_item_get_path(item);
1385         if (!is_dir_exist(path))
1386                 make_dir_hier(path);
1387
1388         g_free(item->name);
1389         item->name = g_strdup(name);
1390
1391         return 0;
1392 }
1393
1394 static gint news_remove_folder(Folder *folder, FolderItem *item)
1395 {
1396         gchar *path;
1397
1398         cm_return_val_if_fail(folder != NULL, -1);
1399         cm_return_val_if_fail(item != NULL, -1);
1400         cm_return_val_if_fail(item->path != NULL, -1);
1401
1402         path = folder_item_get_path(item);
1403         if (remove_dir_recursive(path) < 0) {
1404                 g_warning("can't remove directory '%s'", path);
1405                 g_free(path);
1406                 return -1;
1407         }
1408
1409         g_free(path);
1410         folder_item_remove(item);
1411         return 0;
1412 }
1413
1414 void nntp_disconnect_all(gboolean have_connectivity)
1415 {
1416         GList *list;
1417         gboolean short_timeout;
1418 #ifdef HAVE_NETWORKMANAGER_SUPPORT
1419         GError *error;
1420 #endif
1421
1422 #ifdef HAVE_NETWORKMANAGER_SUPPORT
1423         error = NULL;
1424         short_timeout = !networkmanager_is_online(&error);
1425         if(error) {
1426                 short_timeout = TRUE;
1427                 g_error_free(error);
1428         }
1429 #else
1430         short_timeout = TRUE;
1431 #endif
1432
1433         if(short_timeout)
1434                 nntp_main_set_timeout(1);
1435
1436         for (list = account_get_list(); list != NULL; list = list->next) {
1437                 PrefsAccount *account = list->data;
1438                 if (account->protocol == A_NNTP) {
1439                         RemoteFolder *folder = (RemoteFolder *)account->folder;
1440                         if (folder && folder->session) {
1441                                 NewsSession *session = (NewsSession *)folder->session;
1442                                 if (have_connectivity)
1443                                         nntp_threaded_disconnect(FOLDER(folder));
1444                                 SESSION(session)->state = SESSION_DISCONNECTED;
1445                                 SESSION(session)->sock = NULL;
1446                                 session_destroy(SESSION(session));
1447                                 folder->session = NULL;
1448                         }
1449                 }
1450         }
1451
1452         if(short_timeout)
1453                 nntp_main_set_timeout(prefs_common.io_timeout_secs);
1454 }
1455
1456 #else
1457 #include <glib.h>
1458 #include <glib/gi18n.h>
1459 #include <gtk/gtk.h>
1460 #include "folder.h"
1461 #include "alertpanel.h"
1462
1463 static FolderClass news_class;
1464
1465 static void warn_etpan(void)
1466 {
1467         static gboolean missing_news_warning = TRUE;
1468         if (missing_news_warning) {
1469                 missing_news_warning = FALSE;
1470                 alertpanel_error(
1471                         _("You have one or more News accounts "
1472                           "defined. However this version of "
1473                           "Claws Mail has been built without "
1474                           "News support; your News accounts are "
1475                           "disabled.\n\n"
1476                           "You probably need to "
1477                           "install libetpan and recompile "
1478                           "Claws Mail."));
1479         }
1480 }
1481 static Folder *news_folder_new(const gchar *name, const gchar *path)
1482 {
1483         warn_etpan();
1484         return NULL;
1485 }
1486 void news_group_list_free(GSList *group_list)
1487 {
1488         warn_etpan();
1489 }
1490 void news_remove_group_list_cache(Folder *folder)
1491 {
1492         warn_etpan();
1493 }
1494 int news_folder_locked(Folder *folder)
1495 {
1496         warn_etpan();
1497         return 0;
1498 }
1499 gint news_post(Folder *folder, const gchar *file)
1500 {
1501         warn_etpan();
1502         return -1;
1503 }
1504
1505 gint news_cancel_article(Folder * folder, MsgInfo * msginfo)
1506 {
1507         warn_etpan();
1508         return -1;
1509 }
1510
1511 GSList *news_get_group_list(Folder *folder)
1512 {
1513         warn_etpan();
1514         return NULL;
1515 }
1516
1517
1518 FolderClass *news_get_class(void)
1519 {
1520         if (news_class.idstr == NULL) {
1521                 news_class.type = F_NEWS;
1522                 news_class.idstr = "news";
1523                 news_class.uistr = "News";
1524
1525                 /* Folder functions */
1526                 news_class.new_folder = news_folder_new;
1527         };
1528
1529         return &news_class;
1530 }
1531
1532 void nntp_disconnect_all(gboolean have_connectivity)
1533 {
1534 }
1535
1536 #endif