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