summaryrefslogtreecommitdiff
path: root/libsylph/news.c
diff options
context:
space:
mode:
authorSimeon Simeonov2018-02-26 11:23:00 +0100
committerSimeon Simeonov2018-02-26 11:23:00 +0100
commit0b3cbf57875fd692e4ba0b336fefa4bee1ed00dc (patch)
treeaf916a30553c78ce9d4f2d8658656a175c5b6921 /libsylph/news.c
Initial commit for sylpheed 3.7.0
Diffstat (limited to 'libsylph/news.c')
-rw-r--r--libsylph/news.c1110
1 files changed, 1110 insertions, 0 deletions
diff --git a/libsylph/news.c b/libsylph/news.c
new file mode 100644
index 0000000..ffff9f9
--- /dev/null
+++ b/libsylph/news.c
@@ -0,0 +1,1110 @@
1/*
2 * LibSylph -- E-Mail client library
3 * Copyright (C) 1999-2012 Hiroyuki Yamamoto
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
9 *
10 * This library 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 GNU
13 * Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18 */
19
20#ifdef HAVE_CONFIG_H
21# include "config.h"
22#endif
23
24#include "defs.h"
25
26#include <glib.h>
27#include <glib/gi18n.h>
28#include <stdio.h>
29#include <string.h>
30#include <stdlib.h>
31#include <dirent.h>
32#include <unistd.h>
33#include <time.h>
34
35#include "news.h"
36#include "nntp.h"
37#include "socket.h"
38#include "recv.h"
39#include "procmsg.h"
40#include "procheader.h"
41#include "folder.h"
42#include "session.h"
43#include "codeconv.h"
44#include "utils.h"
45#include "prefs_common.h"
46#include "prefs_account.h"
47#if USE_SSL
48# include "ssl.h"
49#endif
50#include "socks.h"
51
52#define NNTP_PORT 119
53#if USE_SSL
54#define NNTPS_PORT 563
55#endif
56
57static void news_folder_init (Folder *folder,
58 const gchar *name,
59 const gchar *path);
60
61static Folder *news_folder_new (const gchar *name,
62 const gchar *folder);
63static void news_folder_destroy (Folder *folder);
64
65static GSList *news_get_article_list (Folder *folder,
66 FolderItem *item,
67 gboolean use_cache);
68static gchar *news_fetch_msg (Folder *folder,
69 FolderItem *item,
70 gint num);
71static MsgInfo *news_get_msginfo (Folder *folder,
72 FolderItem *item,
73 gint num);
74
75static gint news_close (Folder *folder,
76 FolderItem *item);
77
78static gint news_scan_group (Folder *folder,
79 FolderItem *item);
80
81#if USE_SSL
82static Session *news_session_new (const gchar *server,
83 gushort port,
84 SocksInfo *socks_info,
85 const gchar *userid,
86 const gchar *passwd,
87 SSLType ssl_type);
88#else
89static Session *news_session_new (const gchar *server,
90 gushort port,
91 SocksInfo *socks_info,
92 const gchar *userid,
93 const gchar *passwd);
94#endif
95
96static gint news_get_article_cmd (NNTPSession *session,
97 const gchar *cmd,
98 gint num,
99 gchar *filename);
100static gint news_get_article (NNTPSession *session,
101 gint num,
102 gchar *filename);
103#if 0
104static gint news_get_header (NNTPSession *session,
105 gint num,
106 gchar *filename);
107#endif
108
109static gint news_select_group (NNTPSession *session,
110 const gchar *group,
111 gint *num,
112 gint *first,
113 gint *last);
114static GSList *news_get_uncached_articles(NNTPSession *session,
115 FolderItem *item,
116 gint cache_last,
117 gint *rfirst,
118 gint *rlast);
119static MsgInfo *news_parse_xover (const gchar *xover_str);
120static gchar *news_parse_xhdr (const gchar *xhdr_str,
121 MsgInfo *msginfo);
122static GSList *news_delete_old_articles (GSList *alist,
123 FolderItem *item,
124 gint first);
125static void news_delete_all_articles (FolderItem *item);
126static void news_delete_expired_caches (GSList *alist,
127 FolderItem *item);
128
129static FolderClass news_class =
130{
131 F_NEWS,
132
133 news_folder_new,
134 news_folder_destroy,
135
136 NULL,
137 NULL,
138
139 news_get_article_list,
140 NULL,
141 news_fetch_msg,
142 news_get_msginfo,
143 NULL,
144 NULL,
145 NULL,
146 NULL,
147 NULL,
148 NULL,
149 NULL,
150 NULL,
151 NULL,
152 NULL,
153 NULL,
154 NULL,
155 news_close,
156 news_scan_group,
157
158 NULL,
159 NULL,
160 NULL,
161 NULL
162};
163
164
165FolderClass *news_get_class(void)
166{
167 return &news_class;
168}
169
170static Folder *news_folder_new(const gchar *name, const gchar *path)
171{
172 Folder *folder;
173
174 folder = (Folder *)g_new0(NewsFolder, 1);
175 news_folder_init(folder, name, path);
176
177 return folder;
178}
179
180static void news_folder_destroy(Folder *folder)
181{
182 if (REMOTE_FOLDER(folder)->remove_cache_on_destroy) {
183 gchar *dir;
184 gchar *server;
185
186 dir = folder_get_path(folder);
187 if (is_dir_exist(dir))
188 remove_dir_recursive(dir);
189 g_free(dir);
190
191 server = uriencode_for_filename(folder->account->nntp_server);
192 dir = g_strconcat(get_news_cache_dir(), G_DIR_SEPARATOR_S,
193 server, NULL);
194 if (is_dir_exist(dir))
195 g_rmdir(dir);
196 g_free(dir);
197 g_free(server);
198 }
199
200 folder_remote_folder_destroy(REMOTE_FOLDER(folder));
201}
202
203static void news_folder_init(Folder *folder, const gchar *name,
204 const gchar *path)
205{
206 folder->klass = news_get_class();
207 folder_remote_folder_init(folder, name, path);
208}
209
210#if USE_SSL
211static Session *news_session_new(const gchar *server, gushort port,
212 SocksInfo *socks_info,
213 const gchar *userid, const gchar *passwd,
214 SSLType ssl_type)
215#else
216static Session *news_session_new(const gchar *server, gushort port,
217 SocksInfo *socks_info,
218 const gchar *userid, const gchar *passwd)
219#endif
220{
221 gchar buf[NNTPBUFSIZE];
222 Session *session;
223
224 g_return_val_if_fail(server != NULL, NULL);
225
226 log_message(_("creating NNTP connection to %s:%d ...\n"), server, port);
227
228#if USE_SSL
229 session = nntp_session_new_full(server, port, socks_info, buf, userid, passwd, ssl_type);
230#else
231 session = nntp_session_new_full(server, port, socks_info, buf, userid, passwd);
232#endif
233
234 return session;
235}
236
237static Session *news_session_new_for_folder(Folder *folder)
238{
239 Session *session;
240 PrefsAccount *ac;
241 SocksInfo *socks_info = NULL;
242 const gchar *userid = NULL;
243 gchar *passwd = NULL;
244 gushort port;
245
246 g_return_val_if_fail(folder != NULL, NULL);
247 g_return_val_if_fail(folder->account != NULL, NULL);
248
249 ac = folder->account;
250 if (ac->use_nntp_auth && ac->userid && ac->userid[0]) {
251 userid = ac->userid;
252 if (ac->passwd && ac->passwd[0])
253 passwd = g_strdup(ac->passwd);
254 else
255 passwd = input_query_password(ac->nntp_server, userid);
256 }
257
258 if (ac->use_socks && ac->use_socks_for_recv && ac->proxy_host) {
259 socks_info = socks_info_new(ac->socks_type, ac->proxy_host, ac->proxy_port, ac->use_proxy_auth ? ac->proxy_name : NULL, ac->use_proxy_auth ? ac->proxy_pass : NULL);
260 }
261
262#if USE_SSL
263 port = ac->set_nntpport ? ac->nntpport
264 : ac->ssl_nntp ? NNTPS_PORT : NNTP_PORT;
265 session = news_session_new(ac->nntp_server, port, socks_info,
266 userid, passwd, ac->ssl_nntp);
267#else
268 port = ac->set_nntpport ? ac->nntpport : NNTP_PORT;
269 session = news_session_new(ac->nntp_server, port, socks_info,
270 userid, passwd);
271#endif
272
273 if (socks_info)
274 socks_info_free(socks_info);
275
276 g_free(passwd);
277
278 return session;
279}
280
281static NNTPSession *news_session_get(Folder *folder)
282{
283 RemoteFolder *rfolder = REMOTE_FOLDER(folder);
284
285 g_return_val_if_fail(folder != NULL, NULL);
286 g_return_val_if_fail(FOLDER_TYPE(folder) == F_NEWS, NULL);
287 g_return_val_if_fail(folder->account != NULL, NULL);
288
289 if (!prefs_common.online_mode)
290 return NULL;
291
292 if (!rfolder->session) {
293 rfolder->session = news_session_new_for_folder(folder);
294 return NNTP_SESSION(rfolder->session);
295 }
296
297 if (time(NULL) - rfolder->session->last_access_time <
298 SESSION_TIMEOUT_INTERVAL) {
299 return NNTP_SESSION(rfolder->session);
300 }
301
302 if (nntp_mode(NNTP_SESSION(rfolder->session), FALSE)
303 != NN_SUCCESS) {
304 log_warning(_("NNTP connection to %s:%d has been"
305 " disconnected. Reconnecting...\n"),
306 folder->account->nntp_server,
307 folder->account->set_nntpport ?
308 folder->account->nntpport : NNTP_PORT);
309 session_destroy(rfolder->session);
310 rfolder->session = news_session_new_for_folder(folder);
311 }
312
313 if (rfolder->session)
314 session_set_access_time(rfolder->session);
315
316 return NNTP_SESSION(rfolder->session);
317}
318
319static GSList *news_get_article_list(Folder *folder, FolderItem *item,
320 gboolean use_cache)
321{
322 GSList *alist;
323 NNTPSession *session;
324
325 g_return_val_if_fail(folder != NULL, NULL);
326 g_return_val_if_fail(item != NULL, NULL);
327 g_return_val_if_fail(FOLDER_TYPE(folder) == F_NEWS, NULL);
328
329 session = news_session_get(folder);
330
331 if (!session) {
332 alist = procmsg_read_cache(item, FALSE);
333 item->last_num = procmsg_get_last_num_in_msg_list(alist);
334 } else if (use_cache) {
335 GSList *newlist;
336 gint cache_last;
337 gint first, last;
338
339 alist = procmsg_read_cache(item, FALSE);
340
341 cache_last = procmsg_get_last_num_in_msg_list(alist);
342 newlist = news_get_uncached_articles
343 (session, item, cache_last, &first, &last);
344 if (newlist)
345 item->cache_dirty = TRUE;
346 if (first == 0 && last == 0) {
347 news_delete_all_articles(item);
348 procmsg_msg_list_free(alist);
349 alist = NULL;
350 item->cache_dirty = TRUE;
351 } else {
352 alist = news_delete_old_articles(alist, item, first);
353 news_delete_expired_caches(alist, item);
354 }
355
356 alist = g_slist_concat(alist, newlist);
357
358 item->last_num = last;
359 } else {
360 gint last;
361
362 alist = news_get_uncached_articles
363 (session, item, 0, NULL, &last);
364 news_delete_all_articles(item);
365 item->last_num = last;
366 item->cache_dirty = TRUE;
367 }
368
369 procmsg_set_flags(alist, item);
370
371 alist = procmsg_sort_msg_list(alist, item->sort_key, item->sort_type);
372
373 if (item->mark_queue)
374 item->mark_dirty = TRUE;
375
376 debug_print("cache_dirty: %d, mark_dirty: %d\n",
377 item->cache_dirty, item->mark_dirty);
378
379 if (!item->opened) {
380 if (item->cache_dirty)
381 procmsg_write_cache_list(item, alist);
382 if (item->mark_dirty)
383 procmsg_write_flags_list(item, alist);
384 }
385
386 return alist;
387}
388
389static gchar *news_fetch_msg(Folder *folder, FolderItem *item, gint num)
390{
391 gchar *path, *filename;
392 NNTPSession *session;
393 gchar nstr[16];
394 gint ok;
395
396 g_return_val_if_fail(folder != NULL, NULL);
397 g_return_val_if_fail(item != NULL, NULL);
398
399 path = folder_item_get_path(item);
400 if (!is_dir_exist(path))
401 make_dir_hier(path);
402 filename = g_strconcat(path, G_DIR_SEPARATOR_S, utos_buf(nstr, num),
403 NULL);
404 g_free(path);
405
406 if (is_file_exist(filename) && get_file_size(filename) > 0) {
407 debug_print(_("article %d has been already cached.\n"), num);
408 return filename;
409 }
410
411 session = news_session_get(folder);
412 if (!session) {
413 g_free(filename);
414 return NULL;
415 }
416
417 ok = news_select_group(session, item->path, NULL, NULL, NULL);
418 if (ok != NN_SUCCESS) {
419 if (ok == NN_SOCKET) {
420 session_destroy(SESSION(session));
421 REMOTE_FOLDER(folder)->session = NULL;
422 }
423 g_free(filename);
424 return NULL;
425 }
426
427 debug_print(_("getting article %d...\n"), num);
428 ok = news_get_article(NNTP_SESSION(REMOTE_FOLDER(folder)->session),
429 num, filename);
430 if (ok != NN_SUCCESS) {
431 g_warning(_("can't read article %d\n"), num);
432 if (ok == NN_SOCKET) {
433 session_destroy(SESSION(session));
434 REMOTE_FOLDER(folder)->session = NULL;
435 }
436 g_free(filename);
437 return NULL;
438 }
439
440 return filename;
441}
442
443static MsgInfo *news_get_msginfo(Folder *folder, FolderItem *item, gint num)
444{
445 MsgInfo *msginfo;
446 MsgFlags flags = {0, 0};
447 gchar *file;
448
449 g_return_val_if_fail(folder != NULL, NULL);
450 g_return_val_if_fail(item != NULL, NULL);
451
452 file = news_fetch_msg(folder, item, num);
453 if (!file) return NULL;
454
455 msginfo = procheader_parse_file(file, flags, FALSE);
456
457 g_free(file);
458
459 return msginfo;
460}
461
462static gint news_close(Folder *folder, FolderItem *item)
463{
464 return 0;
465}
466
467static gint news_scan_group(Folder *folder, FolderItem *item)
468{
469 NNTPSession *session;
470 gint num = 0, first = 0, last = 0;
471 gint new = 0, unread = 0, total = 0;
472 gint min = 0, max = 0;
473 gint ok;
474
475 g_return_val_if_fail(folder != NULL, -1);
476 g_return_val_if_fail(item != NULL, -1);
477
478 session = news_session_get(folder);
479 if (!session) return -1;
480
481 ok = news_select_group(session, item->path, &num, &first, &last);
482 if (ok != NN_SUCCESS) {
483 if (ok == NN_SOCKET) {
484 session_destroy(SESSION(session));
485 REMOTE_FOLDER(folder)->session = NULL;
486 }
487 return -1;
488 }
489
490 if (num == 0) {
491 item->new = item->unread = item->total = item->last_num = 0;
492 return 0;
493 }
494
495 procmsg_get_mark_sum(item, &new, &unread, &total, &min, &max, first);
496
497 if (max < first || last < min)
498 new = unread = total = num;
499 else {
500 if (min < first)
501 min = first;
502
503 if (last < max)
504 max = last;
505 else if (max < last) {
506 new += last - max;
507 unread += last - max;
508 }
509
510 if (new > num) new = num;
511 if (unread > num) unread = num;
512 }
513
514 item->new = new;
515 item->unread = unread;
516 item->total = num;
517 item->last_num = last;
518
519 return 0;
520}
521
522static NewsGroupInfo *news_group_info_new(const gchar *name,
523 gint first, gint last, gchar type)
524{
525 NewsGroupInfo *ginfo;
526
527 ginfo = g_new(NewsGroupInfo, 1);
528 ginfo->name = g_strdup(name);
529 ginfo->first = first;
530 ginfo->last = last;
531 ginfo->type = type;
532 ginfo->subscribed = FALSE;
533
534 return ginfo;
535}
536
537static void news_group_info_free(NewsGroupInfo *ginfo)
538{
539 g_free(ginfo->name);
540 g_free(ginfo);
541}
542
543static gint news_group_info_compare(NewsGroupInfo *ginfo1,
544 NewsGroupInfo *ginfo2)
545{
546 return g_ascii_strcasecmp(ginfo1->name, ginfo2->name);
547}
548
549GSList *news_get_group_list(Folder *folder)
550{
551 gchar *path, *filename;
552 FILE *fp;
553 GSList *list = NULL;
554 GSList *last = NULL;
555 gchar buf[NNTPBUFSIZE];
556
557 g_return_val_if_fail(folder != NULL, NULL);
558 g_return_val_if_fail(FOLDER_TYPE(folder) == F_NEWS, NULL);
559
560 path = folder_item_get_path(FOLDER_ITEM(folder->node->data));
561 if (!is_dir_exist(path))
562 make_dir_hier(path);
563 filename = g_strconcat(path, G_DIR_SEPARATOR_S, NEWSGROUP_LIST, NULL);
564 g_free(path);
565
566 if ((fp = g_fopen(filename, "rb")) == NULL) {
567 NNTPSession *session;
568 gint ok;
569
570 session = news_session_get(folder);
571 if (!session) {
572 g_free(filename);
573 return NULL;
574 }
575
576 ok = nntp_list(session);
577 if (ok != NN_SUCCESS) {
578 if (ok == NN_SOCKET) {
579 session_destroy(SESSION(session));
580 REMOTE_FOLDER(folder)->session = NULL;
581 }
582 g_free(filename);
583 return NULL;
584 }
585 if (recv_write_to_file(SESSION(session)->sock, filename) < 0) {
586 log_warning("can't retrieve newsgroup list\n");
587 session_destroy(SESSION(session));
588 REMOTE_FOLDER(folder)->session = NULL;
589 g_free(filename);
590 return NULL;
591 }
592
593 if ((fp = g_fopen(filename, "rb")) == NULL) {
594 FILE_OP_ERROR(filename, "fopen");
595 g_free(filename);
596 return NULL;
597 }
598 }
599
600 while (fgets(buf, sizeof(buf), fp) != NULL) {
601 gchar *p = buf;
602 gchar *name;
603 gint last_num;
604 gint first_num;
605 gchar type;
606 NewsGroupInfo *ginfo;
607
608 p = strchr(p, ' ');
609 if (!p) {
610 strretchomp(buf);
611 log_warning("invalid LIST response: %s\n", buf);
612 continue;
613 }
614 *p = '\0';
615 p++;
616 name = buf;
617
618 if (sscanf(p, "%d %d %c", &last_num, &first_num, &type) < 3) {
619 strretchomp(p);
620 log_warning("invalid LIST response: %s %s\n", name, p);
621 continue;
622 }
623
624 ginfo = news_group_info_new(name, first_num, last_num, type);
625
626 if (!last)
627 last = list = g_slist_append(NULL, ginfo);
628 else {
629 last = g_slist_append(last, ginfo);
630 last = last->next;
631 }
632 }
633
634 fclose(fp);
635 g_free(filename);
636
637 list = g_slist_sort(list, (GCompareFunc)news_group_info_compare);
638
639 return list;
640}
641
642void news_group_list_free(GSList *group_list)
643{
644 GSList *cur;
645
646 if (!group_list) return;
647
648 for (cur = group_list; cur != NULL; cur = cur->next)
649 news_group_info_free((NewsGroupInfo *)cur->data);
650 g_slist_free(group_list);
651}
652
653void news_remove_group_list_cache(Folder *folder)
654{
655 gchar *path, *filename;
656
657 g_return_if_fail(folder != NULL);
658 g_return_if_fail(FOLDER_TYPE(folder) == F_NEWS);
659
660 path = folder_item_get_path(FOLDER_ITEM(folder->node->data));
661 filename = g_strconcat(path, G_DIR_SEPARATOR_S, NEWSGROUP_LIST, NULL);
662 g_free(path);
663
664 if (is_file_exist(filename)) {
665 if (remove(filename) < 0)
666 FILE_OP_ERROR(filename, "remove");
667 }
668 g_free(filename);
669}
670
671gint news_post(Folder *folder, const gchar *file)
672{
673 FILE *fp;
674 gint ok;
675
676 g_return_val_if_fail(folder != NULL, -1);
677 g_return_val_if_fail(FOLDER_TYPE(folder) == F_NEWS, -1);
678 g_return_val_if_fail(file != NULL, -1);
679
680 if ((fp = g_fopen(file, "rb")) == NULL) {
681 FILE_OP_ERROR(file, "fopen");
682 return -1;
683 }
684
685 ok = news_post_stream(folder, fp);
686
687 fclose(fp);
688
689 return ok;
690}
691
692gint news_post_stream(Folder *folder, FILE *fp)
693{
694 NNTPSession *session;
695 gint ok;
696
697 g_return_val_if_fail(folder != NULL, -1);
698 g_return_val_if_fail(FOLDER_TYPE(folder) == F_NEWS, -1);
699 g_return_val_if_fail(fp != NULL, -1);
700
701 session = news_session_get(folder);
702 if (!session) return -1;
703
704 ok = nntp_post(session, fp);
705 if (ok != NN_SUCCESS) {
706 log_warning(_("can't post article.\n"));
707 if (ok == NN_SOCKET) {
708 session_destroy(SESSION(session));
709 REMOTE_FOLDER(folder)->session = NULL;
710 }
711 return -1;
712 }
713
714 return 0;
715}
716
717static gint news_get_article_cmd(NNTPSession *session, const gchar *cmd,
718 gint num, gchar *filename)
719{
720 gchar *msgid;
721 gint ok;
722
723 ok = nntp_get_article(session, cmd, num, &msgid);
724 if (ok != NN_SUCCESS)
725 return ok;
726
727 debug_print("Message-Id = %s, num = %d\n", msgid, num);
728 g_free(msgid);
729
730 ok = recv_write_to_file(SESSION(session)->sock, filename);
731 if (ok < 0) {
732 log_warning(_("can't retrieve article %d\n"), num);
733 if (ok == -2)
734 return NN_SOCKET;
735 else
736 return NN_IOERR;
737 }
738
739 return NN_SUCCESS;
740}
741
742static gint news_get_article(NNTPSession *session, gint num, gchar *filename)
743{
744 return news_get_article_cmd(session, "ARTICLE", num, filename);
745}
746
747#if 0
748static gint news_get_header(NNTPSession *session, gint num, gchar *filename)
749{
750 return news_get_article_cmd(session, "HEAD", num, filename);
751}
752#endif
753
754/**
755 * news_select_group:
756 * @session: Active NNTP session.
757 * @group: Newsgroup name.
758 * @num: Estimated number of articles.
759 * @first: First article number.
760 * @last: Last article number.
761 *
762 * Select newsgroup @group with the GROUP command if it is not already
763 * selected in @session, or article numbers need to be returned.
764 *
765 * Return value: NNTP result code.
766 **/
767static gint news_select_group(NNTPSession *session, const gchar *group,
768 gint *num, gint *first, gint *last)
769{
770 gint ok;
771 gint num_, first_, last_;
772
773 if (!num || !first || !last) {
774 if (session->group &&
775 g_ascii_strcasecmp(session->group, group) == 0)
776 return NN_SUCCESS;
777 num = &num_;
778 first = &first_;
779 last = &last_;
780 }
781
782 g_free(session->group);
783 session->group = NULL;
784
785 ok = nntp_group(session, group, num, first, last);
786 if (ok == NN_SUCCESS)
787 session->group = g_strdup(group);
788 else
789 log_warning(_("can't select group: %s\n"), group);
790
791 return ok;
792}
793
794static GSList *news_get_uncached_articles(NNTPSession *session,
795 FolderItem *item, gint cache_last,
796 gint *rfirst, gint *rlast)
797{
798 gint ok;
799 gint num = 0, first = 0, last = 0, begin = 0, end = 0;
800 gchar buf[NNTPBUFSIZE];
801 GSList *newlist = NULL;
802 GSList *llast = NULL;
803 MsgInfo *msginfo;
804 gint max_articles;
805
806 if (rfirst) *rfirst = -1;
807 if (rlast) *rlast = -1;
808
809 g_return_val_if_fail(session != NULL, NULL);
810 g_return_val_if_fail(item != NULL, NULL);
811 g_return_val_if_fail(item->folder != NULL, NULL);
812 g_return_val_if_fail(item->folder->account != NULL, NULL);
813 g_return_val_if_fail(FOLDER_TYPE(item->folder) == F_NEWS, NULL);
814
815 ok = news_select_group(session, item->path, &num, &first, &last);
816 if (ok != NN_SUCCESS) {
817 if (ok == NN_SOCKET) {
818 session_destroy(SESSION(session));
819 REMOTE_FOLDER(item->folder)->session = NULL;
820 }
821 return NULL;
822 }
823
824 /* calculate getting overview range */
825 if (first > last) {
826 log_warning(_("invalid article range: %d - %d\n"),
827 first, last);
828 return NULL;
829 }
830
831 if (rfirst) *rfirst = first;
832 if (rlast) *rlast = last;
833
834 if (cache_last < first)
835 begin = first;
836 else if (last < cache_last)
837 begin = first;
838 else if (last == cache_last) {
839 debug_print(_("no new articles.\n"));
840 return NULL;
841 } else
842 begin = cache_last + 1;
843 end = last;
844
845 max_articles = item->folder->account->max_nntp_articles;
846 if (max_articles > 0 && end - begin + 1 > max_articles)
847 begin = end - max_articles + 1;
848
849 log_message(_("getting xover %d - %d in %s...\n"),
850 begin, end, item->path);
851 ok = nntp_xover(session, begin, end);
852 if (ok != NN_SUCCESS) {
853 log_warning(_("can't get xover\n"));
854 if (ok == NN_SOCKET) {
855 session_destroy(SESSION(session));
856 REMOTE_FOLDER(item->folder)->session = NULL;
857 }
858 return NULL;
859 }
860
861 for (;;) {
862 if (sock_gets(SESSION(session)->sock, buf, sizeof(buf)) < 0) {
863 log_warning(_("error occurred while getting xover.\n"));
864 session_destroy(SESSION(session));
865 REMOTE_FOLDER(item->folder)->session = NULL;
866 return newlist;
867 }
868
869 if (buf[0] == '.' && buf[1] == '\r') break;
870
871 msginfo = news_parse_xover(buf);
872 if (!msginfo) {
873 log_warning(_("invalid xover line: %s\n"), buf);
874 continue;
875 }
876
877 msginfo->folder = item;
878 msginfo->flags.perm_flags = MSG_NEW|MSG_UNREAD;
879 msginfo->flags.tmp_flags = MSG_NEWS;
880 msginfo->newsgroups = g_strdup(item->path);
881
882 if (!newlist)
883 llast = newlist = g_slist_append(newlist, msginfo);
884 else {
885 llast = g_slist_append(llast, msginfo);
886 llast = llast->next;
887 }
888 }
889
890 ok = nntp_xhdr(session, "to", begin, end);
891 if (ok != NN_SUCCESS) {
892 log_warning(_("can't get xhdr\n"));
893 if (ok == NN_SOCKET) {
894 session_destroy(SESSION(session));
895 REMOTE_FOLDER(item->folder)->session = NULL;
896 }
897 return newlist;
898 }
899
900 llast = newlist;
901
902 for (;;) {
903 if (sock_gets(SESSION(session)->sock, buf, sizeof(buf)) < 0) {
904 log_warning(_("error occurred while getting xhdr.\n"));
905 session_destroy(SESSION(session));
906 REMOTE_FOLDER(item->folder)->session = NULL;
907 return newlist;
908 }
909
910 if (buf[0] == '.' && buf[1] == '\r') break;
911 if (!llast) {
912 g_warning("llast == NULL\n");
913 continue;
914 }
915
916 msginfo = (MsgInfo *)llast->data;
917 msginfo->to = news_parse_xhdr(buf, msginfo);
918
919 llast = llast->next;
920 }
921
922 ok = nntp_xhdr(session, "cc", begin, end);
923 if (ok != NN_SUCCESS) {
924 log_warning(_("can't get xhdr\n"));
925 if (ok == NN_SOCKET) {
926 session_destroy(SESSION(session));
927 REMOTE_FOLDER(item->folder)->session = NULL;
928 }
929 return newlist;
930 }
931
932 llast = newlist;
933
934 for (;;) {
935 if (sock_gets(SESSION(session)->sock, buf, sizeof(buf)) < 0) {
936 log_warning(_("error occurred while getting xhdr.\n"));
937 session_destroy(SESSION(session));
938 REMOTE_FOLDER(item->folder)->session = NULL;
939 return newlist;
940 }
941
942 if (buf[0] == '.' && buf[1] == '\r') break;
943 if (!llast) {
944 g_warning("llast == NULL\n");
945 continue;
946 }
947
948 msginfo = (MsgInfo *)llast->data;
949 msginfo->cc = news_parse_xhdr(buf, msginfo);
950
951 llast = llast->next;
952 }
953
954 session_set_access_time(SESSION(session));
955
956 return newlist;
957}
958
959#define PARSE_ONE_PARAM(p, srcp) \
960{ \
961 p = strchr(srcp, '\t'); \
962 if (!p) return NULL; \
963 else \
964 *p++ = '\0'; \
965}
966
967static MsgInfo *news_parse_xover(const gchar *xover_str)
968{
969 MsgInfo *msginfo;
970 gchar *subject, *sender, *size, *line, *date, *msgid, *ref, *tmp;
971 gchar *p;
972 gint num, size_int, line_int;
973 gchar *xover_buf;
974
975 Xstrdup_a(xover_buf, xover_str, return NULL);
976
977 PARSE_ONE_PARAM(subject, xover_buf);
978 PARSE_ONE_PARAM(sender, subject);
979 PARSE_ONE_PARAM(date, sender);
980 PARSE_ONE_PARAM(msgid, date);
981 PARSE_ONE_PARAM(ref, msgid);
982 PARSE_ONE_PARAM(size, ref);
983 PARSE_ONE_PARAM(line, size);
984
985 tmp = strchr(line, '\t');
986 if (!tmp) tmp = strchr(line, '\r');
987 if (!tmp) tmp = strchr(line, '\n');
988 if (tmp) *tmp = '\0';
989
990 num = atoi(xover_str);
991 size_int = atoi(size);
992 line_int = atoi(line);
993
994 /* set MsgInfo */
995 msginfo = g_new0(MsgInfo, 1);
996 msginfo->msgnum = num;
997 msginfo->size = size_int;
998
999 msginfo->date = g_strdup(date);
1000 msginfo->date_t = procheader_date_parse(NULL, date, 0);
1001
1002 msginfo->from = conv_unmime_header(sender, NULL);
1003 msginfo->fromname = procheader_get_fromname(msginfo->from);
1004
1005 msginfo->subject = conv_unmime_header(subject, NULL);
1006
1007 extract_parenthesis(msgid, '<', '>');
1008 remove_space(msgid);
1009 if (*msgid != '\0')
1010 msginfo->msgid = g_strdup(msgid);
1011
1012 eliminate_parenthesis(ref, '(', ')');
1013 if ((p = strrchr(ref, '<')) != NULL) {
1014 extract_parenthesis(p, '<', '>');
1015 remove_space(p);
1016 if (*p != '\0')
1017 msginfo->inreplyto = g_strdup(p);
1018 }
1019
1020 return msginfo;
1021}
1022
1023static gchar *news_parse_xhdr(const gchar *xhdr_str, MsgInfo *msginfo)
1024{
1025 gchar *p;
1026 gchar *tmp;
1027 gint num;
1028
1029 p = strchr(xhdr_str, ' ');
1030 if (!p)
1031 return NULL;
1032 else
1033 p++;
1034
1035 num = atoi(xhdr_str);
1036 if (msginfo->msgnum != num) return NULL;
1037
1038 tmp = strchr(p, '\r');
1039 if (!tmp) tmp = strchr(p, '\n');
1040
1041 if (tmp)
1042 return g_strndup(p, tmp - p);
1043 else
1044 return g_strdup(p);
1045}
1046
1047static GSList *news_delete_old_articles(GSList *alist, FolderItem *item,
1048 gint first)
1049{
1050 GSList *cur, *next;
1051 MsgInfo *msginfo;
1052 gchar *dir;
1053
1054 g_return_val_if_fail(item != NULL, alist);
1055 g_return_val_if_fail(item->folder != NULL, alist);
1056 g_return_val_if_fail(FOLDER_TYPE(item->folder) == F_NEWS, alist);
1057
1058 if (first < 2) return alist;
1059
1060 debug_print("Deleting cached articles 1 - %d ...\n", first - 1);
1061
1062 dir = folder_item_get_path(item);
1063 remove_numbered_files(dir, 1, first - 1);
1064 g_free(dir);
1065
1066 for (cur = alist; cur != NULL; ) {
1067 next = cur->next;
1068
1069 msginfo = (MsgInfo *)cur->data;
1070 if (msginfo && msginfo->msgnum < first) {
1071 procmsg_msginfo_free(msginfo);
1072 alist = g_slist_remove(alist, msginfo);
1073 item->cache_dirty = TRUE;
1074 }
1075
1076 cur = next;
1077 }
1078
1079 return alist;
1080}
1081
1082static void news_delete_all_articles(FolderItem *item)
1083{
1084 gchar *dir;
1085
1086 g_return_if_fail(item != NULL);
1087 g_return_if_fail(item->folder != NULL);
1088 g_return_if_fail(FOLDER_TYPE(item->folder) == F_NEWS);
1089
1090 debug_print("Deleting all cached articles...\n");
1091
1092 dir = folder_item_get_path(item);
1093 remove_all_numbered_files(dir);
1094 g_free(dir);
1095}
1096
1097static void news_delete_expired_caches(GSList *alist, FolderItem *item)
1098{
1099 gchar *dir;
1100
1101 g_return_if_fail(item != NULL);
1102 g_return_if_fail(item->folder != NULL);
1103 g_return_if_fail(FOLDER_TYPE(item->folder) == F_NEWS);
1104
1105 debug_print("Deleting expired cached articles...\n");
1106
1107 dir = folder_item_get_path(item);
1108 remove_expired_files(dir, 24 * 7);
1109 g_free(dir);
1110}