support image/x-eps format via pdf_viewer
[claws.git] / src / etpan / nntp-thread.c
blob5119e34ab42b42b3e6ec208c78749333c674f035
1 /*
2 * Claws Mail -- a GTK based, lightweight, and fast e-mail client
3 * Copyright (C) 2005-2022 the Claws Mail team and DINH Viet Hoa
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.
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.
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/>.
19 #ifdef HAVE_CONFIG_H
20 # include "config.h"
21 #include "claws-features.h"
22 #endif
24 #ifdef HAVE_LIBETPAN
26 #include <glib.h>
27 #include <glib/gi18n.h>
28 #include "nntp-thread.h"
29 #include "news.h"
30 #include <sys/types.h>
31 #include <sys/stat.h>
32 #include <fcntl.h>
33 #ifndef G_OS_WIN32
34 #include <sys/mman.h>
35 #include <sys/wait.h>
36 #endif
37 #include <gtk/gtk.h>
38 #include <log.h>
39 #include "etpan-thread-manager.h"
40 #include "etpan-ssl.h"
41 #include "utils.h"
42 #include "mainwindow.h"
43 #include "ssl_certificate.h"
44 #include "socket.h"
45 #include "remotefolder.h"
46 #include "main.h"
47 #include "account.h"
48 #include "statusbar.h"
50 #define DISABLE_LOG_DURING_LOGIN
52 #define NNTP_BATCH_SIZE 5000
54 static struct etpan_thread_manager * thread_manager = NULL;
55 static chash * nntp_hash = NULL;
56 static chash * session_hash = NULL;
57 static guint thread_manager_signal = 0;
58 static GIOChannel * io_channel = NULL;
60 static int do_newsnntp_socket_connect(newsnntp * imap, const char * server,
61 gushort port, ProxyInfo * proxy_info)
63 SockInfo * sock;
64 mailstream * stream;
66 if (!proxy_info)
67 return newsnntp_socket_connect(imap, server, port);
69 if (port == 0)
70 port = 119;
72 sock = sock_connect(proxy_info->proxy_host, proxy_info->proxy_port);
74 if (sock == NULL)
75 return NEWSNNTP_ERROR_CONNECTION_REFUSED;
77 if (proxy_connect(sock, server, port, proxy_info) < 0) {
78 sock_close(sock, TRUE);
79 return NEWSNNTP_ERROR_CONNECTION_REFUSED;
82 stream = mailstream_socket_open_timeout(sock->sock,
83 imap->nntp_timeout);
84 if (stream == NULL) {
85 sock_close(sock, TRUE);
86 return NEWSNNTP_ERROR_MEMORY;
89 /* Libetpan now has the socket fd, and we're not interested in
90 * rest of the SockInfo struct. Let's free it, while not touching
91 * the socket itself. */
92 sock_close(sock, FALSE);
94 return newsnntp_connect(imap, stream);
97 #ifdef USE_GNUTLS
98 static int do_newsnntp_ssl_connect_with_callback(newsnntp * imap, const char * server,
99 gushort port,
100 void (* callback)(struct mailstream_ssl_context * ssl_context, void * data),
101 void * data,
102 ProxyInfo *proxy_info)
104 SockInfo * sock;
105 mailstream * stream;
107 if (!proxy_info)
108 return newsnntp_ssl_connect_with_callback(imap, server,
109 port, callback, data);
111 if (port == 0)
112 port = 563;
114 sock = sock_connect(proxy_info->proxy_host, proxy_info->proxy_port);
116 if (sock == NULL)
117 return NEWSNNTP_ERROR_CONNECTION_REFUSED;
119 if (proxy_connect(sock, server, port, proxy_info) < 0) {
120 sock_close(sock, TRUE);
121 return NEWSNNTP_ERROR_CONNECTION_REFUSED;
124 stream = mailstream_ssl_open_with_callback_timeout(sock->sock,
125 imap->nntp_timeout, callback, data);
126 if (stream == NULL) {
127 sock_close(sock, TRUE);
128 return NEWSNNTP_ERROR_SSL;
131 /* Libetpan now has the socket fd, and we're not interested in
132 * rest of the SockInfo struct. Let's free it, while not touching
133 * the socket itself. */
134 sock_close(sock, FALSE);
136 return newsnntp_connect(imap, stream);
138 #endif
140 static void nntp_logger(int direction, const char * str, size_t size)
142 gchar *buf;
143 gchar **lines;
144 int i = 0;
146 if (size > 256) {
147 log_print(LOG_PROTOCOL, "NNTP%c [data - %"G_GSIZE_FORMAT" bytes]\n", direction?'>':'<', size);
148 return;
150 buf = malloc(size+1);
151 memset(buf, 0, size+1);
152 strncpy(buf, str, size);
153 buf[size] = '\0';
155 if (!strncmp(buf, "<<<<<<<", 7)
156 || !strncmp(buf, ">>>>>>>", 7)) {
157 free(buf);
158 return;
160 while (strstr(buf, "\r"))
161 *strstr(buf, "\r") = ' ';
162 while (strlen(buf) > 0 && buf[strlen(buf)-1] == '\n')
163 buf[strlen(buf)-1] = '\0';
165 lines = g_strsplit(buf, "\n", -1);
167 while (lines[i] && *lines[i]) {
168 log_print(LOG_PROTOCOL, "NNTP%c %s\n", direction?'>':'<', lines[i]);
169 i++;
171 g_strfreev(lines);
172 free(buf);
175 static void delete_nntp(Folder *folder, newsnntp *nntp)
177 chashdatum key;
179 key.data = &folder;
180 key.len = sizeof(folder);
181 chash_delete(session_hash, &key, NULL);
183 key.data = &nntp;
184 key.len = sizeof(nntp);
185 if (nntp && nntp->nntp_stream) {
186 /* we don't want libetpan to logout */
187 mailstream_close(nntp->nntp_stream);
188 nntp->nntp_stream = NULL;
190 debug_print("removing newsnntp %p\n", nntp);
191 newsnntp_free(nntp);
194 static gboolean thread_manager_event(GIOChannel * source,
195 GIOCondition condition,
196 gpointer data)
198 #ifdef G_OS_WIN32
199 gsize bytes_read;
200 gchar ch;
202 if (condition & G_IO_IN)
203 g_io_channel_read_chars(source, &ch, 1, &bytes_read, NULL);
204 #endif
205 etpan_thread_manager_loop(thread_manager);
207 return TRUE;
210 #define ETPAN_DEFAULT_NETWORK_TIMEOUT 60
211 extern gboolean etpan_skip_ssl_cert_check;
213 void nntp_main_init(gboolean skip_ssl_cert_check)
215 int fd_thread_manager;
217 etpan_skip_ssl_cert_check = skip_ssl_cert_check;
219 nntp_hash = chash_new(CHASH_COPYKEY, CHASH_DEFAULTSIZE);
220 session_hash = chash_new(CHASH_COPYKEY, CHASH_DEFAULTSIZE);
222 thread_manager = etpan_thread_manager_new();
224 fd_thread_manager = etpan_thread_manager_get_fd(thread_manager);
226 #ifndef G_OS_WIN32
227 io_channel = g_io_channel_unix_new(fd_thread_manager);
228 #else
229 io_channel = g_io_channel_win32_new_fd(fd_thread_manager);
230 #endif
232 thread_manager_signal = g_io_add_watch_full(io_channel, 0, G_IO_IN,
233 thread_manager_event,
234 (gpointer) NULL,
235 NULL);
238 void nntp_main_done(gboolean have_connectivity)
240 nntp_disconnect_all(have_connectivity);
241 etpan_thread_manager_stop(thread_manager);
242 #if defined(__NetBSD__) || defined(__OpenBSD__) || defined(__FreeBSD__)
243 return;
244 #endif
245 etpan_thread_manager_join(thread_manager);
247 g_source_remove(thread_manager_signal);
248 g_io_channel_unref(io_channel);
250 etpan_thread_manager_free(thread_manager);
252 chash_free(session_hash);
253 chash_free(nntp_hash);
256 void nntp_init(Folder * folder)
258 struct etpan_thread * thread;
259 chashdatum key;
260 chashdatum value;
262 thread = etpan_thread_manager_get_thread(thread_manager);
264 key.data = &folder;
265 key.len = sizeof(folder);
266 value.data = thread;
267 value.len = 0;
269 chash_set(nntp_hash, &key, &value, NULL);
272 void nntp_done(Folder * folder)
274 struct etpan_thread * thread;
275 chashdatum key;
276 chashdatum value;
277 int r;
279 key.data = &folder;
280 key.len = sizeof(folder);
282 r = chash_get(nntp_hash, &key, &value);
283 if (r < 0)
284 return;
286 thread = value.data;
288 etpan_thread_unbind(thread);
290 chash_delete(nntp_hash, &key, NULL);
292 debug_print("remove thread\n");
295 static struct etpan_thread * get_thread(Folder * folder)
297 struct etpan_thread * thread;
298 chashdatum key;
299 chashdatum value;
300 int r;
302 key.data = &folder;
303 key.len = sizeof(folder);
305 r = chash_get(nntp_hash, &key, &value);
306 if (r < 0)
307 return NULL;
309 thread = value.data;
311 return thread;
314 static newsnntp * get_nntp(Folder * folder)
316 newsnntp * nntp;
317 chashdatum key;
318 chashdatum value;
319 int r;
321 key.data = &folder;
322 key.len = sizeof(folder);
324 r = chash_get(session_hash, &key, &value);
325 if (r < 0)
326 return NULL;
328 nntp = value.data;
329 debug_print("found nntp %p\n", nntp);
330 return nntp;
334 static void generic_cb(int cancelled, void * result, void * callback_data)
336 struct etpan_thread_op * op;
338 op = (struct etpan_thread_op *) callback_data;
340 debug_print("generic_cb\n");
341 op->finished = 1;
344 static void threaded_run(Folder * folder, void * param, void * result,
345 void (* func)(struct etpan_thread_op * ))
347 struct etpan_thread_op * op;
348 struct etpan_thread * thread;
349 void (*previous_stream_logger)(int direction,
350 const char * str, size_t size);
352 nntp_folder_ref(folder);
354 op = etpan_thread_op_new();
356 op->nntp = get_nntp(folder);
357 op->param = param;
358 op->result = result;
360 op->run = func;
361 op->callback = generic_cb;
362 op->callback_data = op;
364 previous_stream_logger = mailstream_logger;
365 mailstream_logger = nntp_logger;
367 thread = get_thread(folder);
368 etpan_thread_op_schedule(thread, op);
370 while (!op->finished) {
371 gtk_main_iteration();
374 mailstream_logger = previous_stream_logger;
376 etpan_thread_op_free(op);
378 nntp_folder_unref(folder);
382 /* connect */
384 struct connect_param {
385 newsnntp * nntp;
386 PrefsAccount *account;
387 const char * server;
388 int port;
389 ProxyInfo * proxy_info;
392 struct connect_result {
393 int error;
396 #define CHECK_NNTP() { \
397 if (!param->nntp) { \
398 result->error = NEWSNNTP_ERROR_BAD_STATE; \
399 return; \
403 static void connect_run(struct etpan_thread_op * op)
405 int r;
406 struct connect_param * param;
407 struct connect_result * result;
409 param = op->param;
410 result = op->result;
412 CHECK_NNTP();
414 r = do_newsnntp_socket_connect(param->nntp,
415 param->server, param->port,
416 param->proxy_info);
418 result->error = r;
422 int nntp_threaded_connect(Folder * folder, const char * server, int port, ProxyInfo *proxy_info)
424 struct connect_param param;
425 struct connect_result result;
426 chashdatum key;
427 chashdatum value;
428 newsnntp * nntp, * oldnntp;
430 oldnntp = get_nntp(folder);
432 nntp = newsnntp_new(0, NULL);
434 if (oldnntp) {
435 debug_print("deleting old nntp %p\n", oldnntp);
436 delete_nntp(folder, oldnntp);
439 key.data = &folder;
440 key.len = sizeof(folder);
441 value.data = nntp;
442 value.len = 0;
443 chash_set(session_hash, &key, &value, NULL);
445 param.nntp = nntp;
446 param.server = server;
447 param.port = port;
448 param.proxy_info = proxy_info;
450 refresh_resolvers();
451 threaded_run(folder, &param, &result, connect_run);
453 debug_print("connect ok %i with nntp %p\n", result.error, nntp);
455 return result.error;
457 #ifdef USE_GNUTLS
458 static void connect_ssl_run(struct etpan_thread_op * op)
460 int r;
461 struct connect_param * param;
462 struct connect_result * result;
464 param = op->param;
465 result = op->result;
467 CHECK_NNTP();
469 r = do_newsnntp_ssl_connect_with_callback(param->nntp,
470 param->server, param->port,
471 etpan_connect_ssl_context_cb, param->account,
472 param->proxy_info);
473 result->error = r;
476 int nntp_threaded_connect_ssl(Folder * folder, const char * server, int port, ProxyInfo *proxy_info)
478 struct connect_param param;
479 struct connect_result result;
480 chashdatum key;
481 chashdatum value;
482 newsnntp * nntp, * oldnntp;
483 gboolean accept_if_valid = FALSE;
485 oldnntp = get_nntp(folder);
487 nntp = newsnntp_new(0, NULL);
489 if (oldnntp) {
490 debug_print("deleting old nntp %p\n", oldnntp);
491 delete_nntp(folder, oldnntp);
494 key.data = &folder;
495 key.len = sizeof(folder);
496 value.data = nntp;
497 value.len = 0;
498 chash_set(session_hash, &key, &value, NULL);
500 param.nntp = nntp;
501 param.server = server;
502 param.port = port;
503 param.account = folder->account;
504 param.proxy_info = proxy_info;
506 if (folder->account)
507 accept_if_valid = folder->account->ssl_certs_auto_accept;
509 refresh_resolvers();
510 threaded_run(folder, &param, &result, connect_ssl_run);
512 if (result.error == NEWSNNTP_NO_ERROR && !etpan_skip_ssl_cert_check) {
513 if (etpan_certificate_check(nntp->nntp_stream, server, port,
514 accept_if_valid) != TRUE)
515 return -1;
517 debug_print("connect %d with nntp %p\n", result.error, nntp);
519 return result.error;
521 #endif
523 void nntp_threaded_disconnect(Folder * folder)
525 newsnntp * nntp;
527 nntp = get_nntp(folder);
528 if (nntp == NULL) {
529 debug_print("was disconnected\n");
530 return;
533 debug_print("deleting old nntp %p\n", nntp);
534 delete_nntp(folder, nntp);
536 debug_print("disconnect ok\n");
539 void nntp_threaded_cancel(Folder * folder)
541 newsnntp * nntp;
543 nntp = get_nntp(folder);
544 if (nntp->nntp_stream != NULL)
545 mailstream_cancel(nntp->nntp_stream);
549 struct login_param {
550 newsnntp * nntp;
551 const char * login;
552 const char * password;
555 struct login_result {
556 int error;
559 static void login_run(struct etpan_thread_op * op)
561 struct login_param * param;
562 struct login_result * result;
563 int r;
564 #ifdef DISABLE_LOG_DURING_LOGIN
565 int old_debug;
566 #endif
568 param = op->param;
569 result = op->result;
571 CHECK_NNTP();
573 #ifdef DISABLE_LOG_DURING_LOGIN
574 old_debug = mailstream_debug;
575 mailstream_debug = 0;
576 #endif
578 r = newsnntp_authinfo_username(param->nntp, param->login);
579 /* libetpan returning NO_ERROR means it received resp.code 281:
580 in this case auth. is already successful, no password is needed. */
581 if (r == NEWSNNTP_WARNING_REQUEST_AUTHORIZATION_PASSWORD) {
582 r = newsnntp_authinfo_password(param->nntp, param->password);
587 #ifdef DISABLE_LOG_DURING_LOGIN
588 mailstream_debug = old_debug;
589 #endif
591 result->error = r;
592 if (param->nntp->nntp_response)
593 nntp_logger(0, param->nntp->nntp_response, strlen(param->nntp->nntp_response));
595 debug_print("nntp login run - end %i\n", r);
598 int nntp_threaded_login(Folder * folder, const char * login, const char * password)
600 struct login_param param;
601 struct login_result result;
603 debug_print("nntp login - begin\n");
605 param.nntp = get_nntp(folder);
606 param.login = login;
607 param.password = password;
609 threaded_run(folder, &param, &result, login_run);
611 debug_print("nntp login - end\n");
613 return result.error;
616 struct date_param {
617 newsnntp * nntp;
618 struct tm * lt;
621 struct date_result {
622 int error;
625 static void date_run(struct etpan_thread_op * op)
627 struct date_param * param;
628 struct date_result * result;
629 int r;
631 param = op->param;
632 result = op->result;
634 CHECK_NNTP();
636 r = newsnntp_date(param->nntp, param->lt);
638 result->error = r;
639 debug_print("nntp date run - end %i\n", r);
642 int nntp_threaded_date(Folder * folder, struct tm *lt)
644 struct date_param param;
645 struct date_result result;
647 debug_print("nntp date - begin\n");
649 param.nntp = get_nntp(folder);
650 param.lt = lt;
652 threaded_run(folder, &param, &result, date_run);
654 debug_print("nntp date - end\n");
656 return result.error;
659 struct list_param {
660 newsnntp * nntp;
661 clist **grouplist;
664 struct list_result {
665 int error;
668 static void list_run(struct etpan_thread_op * op)
670 struct list_param * param;
671 struct list_result * result;
672 int r;
674 param = op->param;
675 result = op->result;
677 CHECK_NNTP();
679 r = newsnntp_list(param->nntp, param->grouplist);
681 result->error = r;
682 debug_print("nntp list run - end %i\n", r);
685 int nntp_threaded_list(Folder * folder, clist **grouplist)
687 struct list_param param;
688 struct list_result result;
690 debug_print("nntp list - begin\n");
692 param.nntp = get_nntp(folder);
693 param.grouplist = grouplist;
695 threaded_run(folder, &param, &result, list_run);
697 debug_print("nntp list - end\n");
699 return result.error;
702 struct post_param {
703 newsnntp * nntp;
704 char *contents;
705 size_t len;
708 struct post_result {
709 int error;
712 static void post_run(struct etpan_thread_op * op)
714 struct post_param * param;
715 struct post_result * result;
716 int r;
718 param = op->param;
719 result = op->result;
721 CHECK_NNTP();
723 r = newsnntp_post(param->nntp, param->contents, param->len);
725 result->error = r;
726 debug_print("nntp post run - end %i\n", r);
729 int nntp_threaded_post(Folder * folder, char *contents, size_t len)
731 struct post_param param;
732 struct post_result result;
734 debug_print("nntp post - begin\n");
736 param.nntp = get_nntp(folder);
737 param.contents = contents;
738 param.len = len;
740 threaded_run(folder, &param, &result, post_run);
742 debug_print("nntp post - end\n");
744 return result.error;
747 struct article_param {
748 newsnntp * nntp;
749 guint32 num;
750 char **contents;
751 size_t *len;
754 struct article_result {
755 int error;
758 static void article_run(struct etpan_thread_op * op)
760 struct article_param * param;
761 struct article_result * result;
762 int r;
764 param = op->param;
765 result = op->result;
767 CHECK_NNTP();
769 r = newsnntp_article(param->nntp, param->num, param->contents, param->len);
771 result->error = r;
772 debug_print("nntp article run - end %i\n", r);
775 int nntp_threaded_article(Folder * folder, guint32 num, char **contents, size_t *len)
777 struct article_param param;
778 struct article_result result;
780 debug_print("nntp article - begin\n");
782 param.nntp = get_nntp(folder);
783 param.num = num;
784 param.contents = contents;
785 param.len = len;
787 threaded_run(folder, &param, &result, article_run);
789 debug_print("nntp article - end\n");
791 return result.error;
794 struct group_param {
795 newsnntp * nntp;
796 const char *group;
797 struct newsnntp_group_info **info;
800 struct group_result {
801 int error;
804 static void group_run(struct etpan_thread_op * op)
806 struct group_param * param;
807 struct group_result * result;
808 int r;
810 param = op->param;
811 result = op->result;
813 CHECK_NNTP();
815 r = newsnntp_group(param->nntp, param->group, param->info);
817 result->error = r;
818 debug_print("nntp group run - end %i\n", r);
821 int nntp_threaded_group(Folder * folder, const char *group, struct newsnntp_group_info **info)
823 struct group_param param;
824 struct group_result result;
826 debug_print("nntp group - begin\n");
828 param.nntp = get_nntp(folder);
829 param.group = group;
830 param.info = info;
832 threaded_run(folder, &param, &result, group_run);
834 debug_print("nntp group - end\n");
836 return result.error;
839 struct mode_reader_param {
840 newsnntp * nntp;
843 struct mode_reader_result {
844 int error;
847 static void mode_reader_run(struct etpan_thread_op * op)
849 struct mode_reader_param * param;
850 struct mode_reader_result * result;
851 int r;
853 param = op->param;
854 result = op->result;
856 CHECK_NNTP();
858 r = newsnntp_mode_reader(param->nntp);
860 result->error = r;
861 debug_print("nntp mode_reader run - end %i\n", r);
864 int nntp_threaded_mode_reader(Folder * folder)
866 struct mode_reader_param param;
867 struct mode_reader_result result;
869 debug_print("nntp mode_reader - begin\n");
871 param.nntp = get_nntp(folder);
873 threaded_run(folder, &param, &result, mode_reader_run);
875 debug_print("nntp mode_reader - end\n");
877 return result.error;
880 struct xover_param {
881 newsnntp * nntp;
882 guint32 beg;
883 guint32 end;
884 struct newsnntp_xover_resp_item **result;
885 clist **msglist;
888 struct xover_result {
889 int error;
892 static void xover_run(struct etpan_thread_op * op)
894 struct xover_param * param;
895 struct xover_result * result;
896 int r;
898 param = op->param;
899 result = op->result;
901 CHECK_NNTP();
903 if (param->result) {
904 r = newsnntp_xover_single(param->nntp, param->beg, param->result);
905 } else {
906 r = newsnntp_xover_range(param->nntp, param->beg, param->end, param->msglist);
909 result->error = r;
910 debug_print("nntp xover run %d-%d - end %i\n",
911 param->beg, param->end, r);
914 int nntp_threaded_xover(Folder * folder, guint32 beg, guint32 end, struct newsnntp_xover_resp_item **single_result, clist **multiple_result)
916 struct xover_param param;
917 struct xover_result result;
918 clist *l = NULL, *h = NULL;
919 guint32 cbeg = 0, cend = 0;
921 debug_print("nntp xover - begin (%d-%d)\n", beg, end);
923 h = clist_new();
925 /* Request the overview in batches of NNTP_BATCH_SIZE, to prevent
926 * long stalls or libetpan choking on too large server response,
927 * and to allow updating any progress indicators while we work. */
928 cbeg = beg;
929 while (cbeg <= end && cend <= end) {
930 cend = cbeg + (NNTP_BATCH_SIZE - 1);
931 if (cend > end)
932 cend = end;
934 statusbar_progress_all(cbeg - beg, end - beg, 1);
935 GTK_EVENTS_FLUSH();
937 param.nntp = get_nntp(folder);
938 param.beg = cbeg;
939 param.end = cend;
940 param.result = single_result;
941 param.msglist = &l;
943 threaded_run(folder, &param, &result, xover_run);
945 /* Handle errors */
946 if (result.error != NEWSNNTP_NO_ERROR) {
947 log_warning(LOG_PROTOCOL, _("couldn't get xover range\n"));
948 debug_print("couldn't get xover for %d-%d\n", cbeg, cend);
949 if (l != NULL)
950 newsnntp_xover_resp_list_free(l);
951 newsnntp_xover_resp_list_free(h);
952 statusbar_progress_all(0, 0, 0);
953 return result.error;
956 /* Append the new data (l) to list of results (h). */
957 if (l != NULL) {
958 debug_print("total items so far %d, items this batch %d\n",
959 clist_count(h), clist_count(l));
960 clist_concat(h, l);
961 clist_free(l);
962 l = NULL;
965 cbeg += NNTP_BATCH_SIZE;
968 statusbar_progress_all(0, 0, 0);
970 debug_print("nntp xover - end\n");
972 *multiple_result = h;
974 return result.error;
977 struct xhdr_param {
978 newsnntp * nntp;
979 const char *header;
980 guint32 beg;
981 guint32 end;
982 clist **hdrlist;
985 struct xhdr_result {
986 int error;
989 static void xhdr_run(struct etpan_thread_op * op)
991 struct xhdr_param * param;
992 struct xhdr_result * result;
993 int r;
995 param = op->param;
996 result = op->result;
998 CHECK_NNTP();
1000 if (param->beg == param->end) {
1001 r = newsnntp_xhdr_single(param->nntp, param->header, param->beg, param->hdrlist);
1002 } else {
1003 r = newsnntp_xhdr_range(param->nntp, param->header, param->beg, param->end, param->hdrlist);
1006 result->error = r;
1007 debug_print("nntp xhdr '%s %d-%d' run - end %i\n",
1008 param->header, param->beg, param->end, r);
1011 int nntp_threaded_xhdr(Folder * folder, const char *header, guint32 beg, guint32 end, clist **hdrlist)
1013 struct xhdr_param param;
1014 struct xhdr_result result;
1015 clist *l = NULL;
1016 clist *h = *hdrlist;
1017 guint32 cbeg = 0, cend = 0;
1019 debug_print("nntp xhdr %s - begin (%d-%d)\n", header, beg, end);
1021 if (h == NULL)
1022 h = clist_new();
1024 /* Request the headers in batches of NNTP_BATCH_SIZE, to prevent
1025 * long stalls or libetpan choking on too large server response,
1026 * and to allow updating any progress indicators while we work. */
1027 cbeg = beg;
1028 while (cbeg <= end && cend <= end) {
1029 cend = cbeg + NNTP_BATCH_SIZE - 1;
1030 if (cend > end)
1031 cend = end;
1033 statusbar_progress_all(cbeg - beg, end - beg, 1);
1034 GTK_EVENTS_FLUSH();
1036 param.nntp = get_nntp(folder);
1037 param.header = header;
1038 param.beg = cbeg;
1039 param.end = cend;
1040 param.hdrlist = &l;
1042 threaded_run(folder, &param, &result, xhdr_run);
1044 /* Handle errors */
1045 if (result.error != NEWSNNTP_NO_ERROR) {
1046 log_warning(LOG_PROTOCOL, _("couldn't get xhdr range\n"));
1047 debug_print("couldn't get xhdr %s %d-%d\n", header, cbeg, cend);
1048 if (l != NULL)
1049 newsnntp_xhdr_free(l);
1050 newsnntp_xhdr_free(h);
1051 statusbar_progress_all(0, 0, 0);
1052 return result.error;
1055 /* Append the new data (l) to list of results (h). */
1056 if (l != NULL) {
1057 debug_print("total items so far %d, items this batch %d\n",
1058 clist_count(h), clist_count(l));
1059 clist_concat(h, l);
1060 clist_free(l);
1061 l = NULL;
1064 cbeg += NNTP_BATCH_SIZE;
1067 statusbar_progress_all(0, 0, 0);
1069 debug_print("nntp xhdr %s - end (%d-%d)\n", header, beg, end);
1071 *hdrlist = h;
1073 return result.error;
1076 void nntp_main_set_timeout(int sec)
1078 mailstream_network_delay.tv_sec = sec;
1079 mailstream_network_delay.tv_usec = 0;
1082 #else
1084 void nntp_main_init(void)
1087 void nntp_main_done(gboolean have_connectivity)
1090 void nntp_main_set_timeout(int sec)
1094 void nntp_threaded_cancel(Folder * folder);
1098 #endif