2 * Claws Mail -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 2005-2016 DINH Viet Hoa and the Claws Mail team
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/>.
21 #include "claws-features.h"
27 #include <glib/gi18n.h>
28 #include "nntp-thread.h"
30 #include <sys/types.h>
32 #if (defined(__DragonFly__) || defined (__NetBSD__) || defined (__FreeBSD__) || defined (__OpenBSD__) || defined (__CYGWIN__))
33 #include <sys/socket.h>
42 #include "etpan-thread-manager.h"
43 #include "etpan-ssl.h"
45 #include "mainwindow.h"
46 #include "ssl_certificate.h"
48 #include "remotefolder.h"
52 #define DISABLE_LOG_DURING_LOGIN
54 #define NNTP_BATCH_SIZE 4999
56 static struct etpan_thread_manager * thread_manager = NULL;
57 static chash * nntp_hash = NULL;
58 static chash * session_hash = NULL;
59 static guint thread_manager_signal = 0;
60 static GIOChannel * io_channel = NULL;
62 static void nntp_logger(int direction, const char * str, size_t size)
69 log_print(LOG_PROTOCOL, "NNTP%c [data - %zd bytes]\n", direction?'>':'<', size);
73 memset(buf, 0, size+1);
74 strncpy(buf, str, size);
77 if (!strncmp(buf, "<<<<<<<", 7)
78 || !strncmp(buf, ">>>>>>>", 7)) {
82 while (strstr(buf, "\r"))
83 *strstr(buf, "\r") = ' ';
84 while (strlen(buf) > 0 && buf[strlen(buf)-1] == '\n')
85 buf[strlen(buf)-1] = '\0';
87 lines = g_strsplit(buf, "\n", -1);
89 while (lines[i] && *lines[i]) {
90 log_print(LOG_PROTOCOL, "NNTP%c %s\n", direction?'>':'<', lines[i]);
97 static void delete_nntp(Folder *folder, newsnntp *nntp)
102 key.len = sizeof(folder);
103 chash_delete(session_hash, &key, NULL);
106 key.len = sizeof(nntp);
107 if (nntp && nntp->nntp_stream) {
108 /* we don't want libetpan to logout */
109 mailstream_close(nntp->nntp_stream);
110 nntp->nntp_stream = NULL;
112 debug_print("removing newsnntp %p\n", nntp);
116 static gboolean thread_manager_event(GIOChannel * source,
117 GIOCondition condition,
124 if (condition & G_IO_IN)
125 g_io_channel_read_chars(source, &ch, 1, &bytes_read, NULL);
127 etpan_thread_manager_loop(thread_manager);
132 #define ETPAN_DEFAULT_NETWORK_TIMEOUT 60
133 extern gboolean etpan_skip_ssl_cert_check;
135 void nntp_main_init(gboolean skip_ssl_cert_check)
137 int fd_thread_manager;
139 etpan_skip_ssl_cert_check = skip_ssl_cert_check;
141 nntp_hash = chash_new(CHASH_COPYKEY, CHASH_DEFAULTSIZE);
142 session_hash = chash_new(CHASH_COPYKEY, CHASH_DEFAULTSIZE);
144 thread_manager = etpan_thread_manager_new();
146 fd_thread_manager = etpan_thread_manager_get_fd(thread_manager);
149 io_channel = g_io_channel_unix_new(fd_thread_manager);
151 io_channel = g_io_channel_win32_new_fd(fd_thread_manager);
154 thread_manager_signal = g_io_add_watch_full(io_channel, 0, G_IO_IN,
155 thread_manager_event,
160 void nntp_main_done(gboolean have_connectivity)
162 nntp_disconnect_all(have_connectivity);
163 etpan_thread_manager_stop(thread_manager);
164 #if defined(__NetBSD__) || defined(__OpenBSD__) || defined(__FreeBSD__)
167 etpan_thread_manager_join(thread_manager);
169 g_source_remove(thread_manager_signal);
170 g_io_channel_unref(io_channel);
172 etpan_thread_manager_free(thread_manager);
174 chash_free(session_hash);
175 chash_free(nntp_hash);
178 void nntp_init(Folder * folder)
180 struct etpan_thread * thread;
184 thread = etpan_thread_manager_get_thread(thread_manager);
187 key.len = sizeof(folder);
191 chash_set(nntp_hash, &key, &value, NULL);
194 void nntp_done(Folder * folder)
196 struct etpan_thread * thread;
202 key.len = sizeof(folder);
204 r = chash_get(nntp_hash, &key, &value);
210 etpan_thread_unbind(thread);
212 chash_delete(nntp_hash, &key, NULL);
214 debug_print("remove thread\n");
217 static struct etpan_thread * get_thread(Folder * folder)
219 struct etpan_thread * thread;
225 key.len = sizeof(folder);
227 r = chash_get(nntp_hash, &key, &value);
236 static newsnntp * get_nntp(Folder * folder)
244 key.len = sizeof(folder);
246 r = chash_get(session_hash, &key, &value);
251 debug_print("found nntp %p\n", nntp);
256 static void generic_cb(int cancelled, void * result, void * callback_data)
258 struct etpan_thread_op * op;
260 op = (struct etpan_thread_op *) callback_data;
262 debug_print("generic_cb\n");
266 static void threaded_run(Folder * folder, void * param, void * result,
267 void (* func)(struct etpan_thread_op * ))
269 struct etpan_thread_op * op;
270 struct etpan_thread * thread;
271 void (*previous_stream_logger)(int direction,
272 const char * str, size_t size);
274 nntp_folder_ref(folder);
276 op = etpan_thread_op_new();
278 op->nntp = get_nntp(folder);
283 op->callback = generic_cb;
284 op->callback_data = op;
286 previous_stream_logger = mailstream_logger;
287 mailstream_logger = nntp_logger;
289 thread = get_thread(folder);
290 etpan_thread_op_schedule(thread, op);
292 while (!op->finished) {
293 gtk_main_iteration();
296 mailstream_logger = previous_stream_logger;
298 etpan_thread_op_free(op);
300 nntp_folder_unref(folder);
306 struct connect_param {
308 PrefsAccount *account;
313 struct connect_result {
317 #define CHECK_NNTP() { \
318 if (!param->nntp) { \
319 result->error = NEWSNNTP_ERROR_BAD_STATE; \
324 static void connect_run(struct etpan_thread_op * op)
327 struct connect_param * param;
328 struct connect_result * result;
335 r = newsnntp_socket_connect(param->nntp,
336 param->server, param->port);
342 int nntp_threaded_connect(Folder * folder, const char * server, int port)
344 struct connect_param param;
345 struct connect_result result;
348 newsnntp * nntp, * oldnntp;
350 oldnntp = get_nntp(folder);
352 nntp = newsnntp_new(0, NULL);
355 debug_print("deleting old nntp %p\n", oldnntp);
356 delete_nntp(folder, oldnntp);
360 key.len = sizeof(folder);
363 chash_set(session_hash, &key, &value, NULL);
366 param.server = server;
370 threaded_run(folder, ¶m, &result, connect_run);
372 debug_print("connect ok %i with nntp %p\n", result.error, nntp);
377 static void connect_ssl_run(struct etpan_thread_op * op)
380 struct connect_param * param;
381 struct connect_result * result;
388 r = newsnntp_ssl_connect_with_callback(param->nntp,
389 param->server, param->port,
390 etpan_connect_ssl_context_cb, param->account);
394 int nntp_threaded_connect_ssl(Folder * folder, const char * server, int port)
396 struct connect_param param;
397 struct connect_result result;
400 newsnntp * nntp, * oldnntp;
401 gboolean accept_if_valid = FALSE;
403 oldnntp = get_nntp(folder);
405 nntp = newsnntp_new(0, NULL);
408 debug_print("deleting old nntp %p\n", oldnntp);
409 delete_nntp(folder, oldnntp);
413 key.len = sizeof(folder);
416 chash_set(session_hash, &key, &value, NULL);
419 param.server = server;
421 param.account = folder->account;
424 accept_if_valid = folder->account->ssl_certs_auto_accept;
427 threaded_run(folder, ¶m, &result, connect_ssl_run);
429 if (result.error == NEWSNNTP_NO_ERROR && !etpan_skip_ssl_cert_check) {
430 if (etpan_certificate_check(nntp->nntp_stream, server, port,
431 accept_if_valid) != TRUE)
434 debug_print("connect %d with nntp %p\n", result.error, nntp);
440 void nntp_threaded_disconnect(Folder * folder)
444 nntp = get_nntp(folder);
446 debug_print("was disconnected\n");
450 debug_print("deleting old nntp %p\n", nntp);
451 delete_nntp(folder, nntp);
453 debug_print("disconnect ok\n");
456 void nntp_threaded_cancel(Folder * folder)
460 nntp = get_nntp(folder);
461 if (nntp->nntp_stream != NULL)
462 mailstream_cancel(nntp->nntp_stream);
469 const char * password;
472 struct login_result {
476 static void login_run(struct etpan_thread_op * op)
478 struct login_param * param;
479 struct login_result * result;
481 #ifdef DISABLE_LOG_DURING_LOGIN
490 #ifdef DISABLE_LOG_DURING_LOGIN
491 old_debug = mailstream_debug;
492 mailstream_debug = 0;
495 r = newsnntp_authinfo_username(param->nntp, param->login);
496 /* libetpan returning NO_ERROR means it received resp.code 281:
497 in this case auth. is already successful, no password is needed. */
498 if (r == NEWSNNTP_WARNING_REQUEST_AUTHORIZATION_PASSWORD) {
499 r = newsnntp_authinfo_password(param->nntp, param->password);
504 #ifdef DISABLE_LOG_DURING_LOGIN
505 mailstream_debug = old_debug;
509 if (param->nntp->nntp_response)
510 nntp_logger(0, param->nntp->nntp_response, strlen(param->nntp->nntp_response));
512 debug_print("nntp login run - end %i\n", r);
515 int nntp_threaded_login(Folder * folder, const char * login, const char * password)
517 struct login_param param;
518 struct login_result result;
520 debug_print("nntp login - begin\n");
522 param.nntp = get_nntp(folder);
524 param.password = password;
526 threaded_run(folder, ¶m, &result, login_run);
528 debug_print("nntp login - end\n");
542 static void date_run(struct etpan_thread_op * op)
544 struct date_param * param;
545 struct date_result * result;
553 r = newsnntp_date(param->nntp, param->lt);
556 debug_print("nntp date run - end %i\n", r);
559 int nntp_threaded_date(Folder * folder, struct tm *lt)
561 struct date_param param;
562 struct date_result result;
564 debug_print("nntp date - begin\n");
566 param.nntp = get_nntp(folder);
569 threaded_run(folder, ¶m, &result, date_run);
571 debug_print("nntp date - end\n");
585 static void list_run(struct etpan_thread_op * op)
587 struct list_param * param;
588 struct list_result * result;
596 r = newsnntp_list(param->nntp, param->grouplist);
599 debug_print("nntp list run - end %i\n", r);
602 int nntp_threaded_list(Folder * folder, clist **grouplist)
604 struct list_param param;
605 struct list_result result;
607 debug_print("nntp list - begin\n");
609 param.nntp = get_nntp(folder);
610 param.grouplist = grouplist;
612 threaded_run(folder, ¶m, &result, list_run);
614 debug_print("nntp list - end\n");
629 static void post_run(struct etpan_thread_op * op)
631 struct post_param * param;
632 struct post_result * result;
640 r = newsnntp_post(param->nntp, param->contents, param->len);
643 debug_print("nntp post run - end %i\n", r);
646 int nntp_threaded_post(Folder * folder, char *contents, size_t len)
648 struct post_param param;
649 struct post_result result;
651 debug_print("nntp post - begin\n");
653 param.nntp = get_nntp(folder);
654 param.contents = contents;
657 threaded_run(folder, ¶m, &result, post_run);
659 debug_print("nntp post - end\n");
664 struct article_param {
671 struct article_result {
675 static void article_run(struct etpan_thread_op * op)
677 struct article_param * param;
678 struct article_result * result;
686 r = newsnntp_article(param->nntp, param->num, param->contents, param->len);
689 debug_print("nntp article run - end %i\n", r);
692 int nntp_threaded_article(Folder * folder, guint32 num, char **contents, size_t *len)
694 struct article_param param;
695 struct article_result result;
697 debug_print("nntp article - begin\n");
699 param.nntp = get_nntp(folder);
701 param.contents = contents;
704 threaded_run(folder, ¶m, &result, article_run);
706 debug_print("nntp article - end\n");
714 struct newsnntp_group_info **info;
717 struct group_result {
721 static void group_run(struct etpan_thread_op * op)
723 struct group_param * param;
724 struct group_result * result;
732 r = newsnntp_group(param->nntp, param->group, param->info);
735 debug_print("nntp group run - end %i\n", r);
738 int nntp_threaded_group(Folder * folder, const char *group, struct newsnntp_group_info **info)
740 struct group_param param;
741 struct group_result result;
743 debug_print("nntp group - begin\n");
745 param.nntp = get_nntp(folder);
749 threaded_run(folder, ¶m, &result, group_run);
751 debug_print("nntp group - end\n");
756 struct mode_reader_param {
760 struct mode_reader_result {
764 static void mode_reader_run(struct etpan_thread_op * op)
766 struct mode_reader_param * param;
767 struct mode_reader_result * result;
775 r = newsnntp_mode_reader(param->nntp);
778 debug_print("nntp mode_reader run - end %i\n", r);
781 int nntp_threaded_mode_reader(Folder * folder)
783 struct mode_reader_param param;
784 struct mode_reader_result result;
786 debug_print("nntp mode_reader - begin\n");
788 param.nntp = get_nntp(folder);
790 threaded_run(folder, ¶m, &result, mode_reader_run);
792 debug_print("nntp mode_reader - end\n");
801 struct newsnntp_xover_resp_item **result;
805 struct xover_result {
809 static void xover_run(struct etpan_thread_op * op)
811 struct xover_param * param;
812 struct xover_result * result;
821 r = newsnntp_xover_single(param->nntp, param->beg, param->result);
823 r = newsnntp_xover_range(param->nntp, param->beg, param->end, param->msglist);
827 debug_print("nntp xover run %d-%d - end %i\n",
828 param->beg, param->end, r);
831 int nntp_threaded_xover(Folder * folder, guint32 beg, guint32 end, struct newsnntp_xover_resp_item **single_result, clist **multiple_result)
833 struct xover_param param;
834 struct xover_result result;
835 clist *l = NULL, *h = NULL;
836 guint32 cbeg = 0, cend = 0;
838 debug_print("nntp xover - begin (%d-%d)\n", beg, end);
842 /* Request the overview in batches of NNTP_BATCH_SIZE, to prevent
843 * long stalls or libetpan choking on too large server response,
844 * and to allow updating any progress indicators while we work. */
846 while (cbeg <= end && cend <= end) {
847 cend = cbeg + NNTP_BATCH_SIZE;
851 param.nntp = get_nntp(folder);
854 param.result = single_result;
857 threaded_run(folder, ¶m, &result, xover_run);
860 if (result.error != NEWSNNTP_NO_ERROR) {
861 log_warning(LOG_PROTOCOL, _("couldn't get xover range\n"));
862 debug_print("couldn't get xover for %d-%d\n", cbeg, cend);
868 /* Append the new data (l) to list of results (h). */
870 debug_print("total items so far %d, items this batch %d\n",
871 clist_count(h), clist_count(l));
876 cbeg += NNTP_BATCH_SIZE + 1;
879 debug_print("nntp xover - end\n");
881 *multiple_result = h;
898 static void xhdr_run(struct etpan_thread_op * op)
900 struct xhdr_param * param;
901 struct xhdr_result * result;
909 if (param->beg == param->end) {
910 r = newsnntp_xhdr_single(param->nntp, param->header, param->beg, param->hdrlist);
912 r = newsnntp_xhdr_range(param->nntp, param->header, param->beg, param->end, param->hdrlist);
916 debug_print("nntp xhdr '%s %d-%d' run - end %i\n",
917 param->header, param->beg, param->end, r);
920 int nntp_threaded_xhdr(Folder * folder, const char *header, guint32 beg, guint32 end, clist **hdrlist)
922 struct xhdr_param param;
923 struct xhdr_result result;
926 guint32 cbeg = 0, cend = 0;
928 debug_print("nntp xhdr %s - begin (%d-%d)\n", header, beg, end);
933 /* Request the headers in batches of NNTP_BATCH_SIZE, to prevent
934 * long stalls or libetpan choking on too large server response,
935 * and to allow updating any progress indicators while we work. */
937 while (cbeg <= end && cend <= end) {
938 cend = cbeg + NNTP_BATCH_SIZE;
942 param.nntp = get_nntp(folder);
943 param.header = header;
948 threaded_run(folder, ¶m, &result, xhdr_run);
951 if (result.error != NEWSNNTP_NO_ERROR) {
952 log_warning(LOG_PROTOCOL, _("couldn't get xhdr range\n"));
953 debug_print("couldn't get xhdr %s %d-%d\n", header, cbeg, cend);
959 /* Append the new data (l) to list of results (h). */
961 debug_print("total items so far %d, items this batch %d\n",
962 clist_count(h), clist_count(l));
967 cbeg += NNTP_BATCH_SIZE + 1;
970 debug_print("nntp xhdr %s - end (%d-%d)\n", header, beg, end);
977 void nntp_main_set_timeout(int sec)
979 mailstream_network_delay.tv_sec = sec;
980 mailstream_network_delay.tv_usec = 0;
985 void nntp_main_init(void)
988 void nntp_main_done(gboolean have_connectivity)
991 void nntp_main_set_timeout(int sec)
995 void nntp_threaded_cancel(Folder * folder);