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