Add a per-account preference to allow automatically accepting unknown
[claws.git] / src / etpan / nntp-thread.c
1 /*
2  * Claws Mail -- a GTK+ based, lightweight, and fast e-mail client
3  * Copyright (C) 2005-2012 DINH Viet Hoa and the Claws Mail team
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 3 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program. If not, see <http://www.gnu.org/licenses/>.
17  * 
18  */
19
20 #ifdef HAVE_CONFIG_H
21 #  include "config.h"
22 #include "claws-features.h"
23 #endif
24
25 #ifdef HAVE_LIBETPAN
26
27 #include <glib.h>
28 #include <glib/gi18n.h>
29 #include "nntp-thread.h"
30 #include "news.h"
31 #include <sys/types.h>
32 #include <sys/stat.h>
33 #if (defined(__DragonFly__) || defined (__NetBSD__) || defined (__FreeBSD__) || defined (__OpenBSD__) || defined (__CYGWIN__))
34 #include <sys/socket.h>
35 #endif
36 #include <fcntl.h>
37 #ifndef G_OS_WIN32
38 #include <sys/mman.h>
39 #include <sys/wait.h>
40 #endif
41 #include <gtk/gtk.h>
42 #include <log.h>
43 #include "etpan-thread-manager.h"
44 #include "etpan-ssl.h"
45 #include "utils.h"
46 #include "mainwindow.h"
47 #include "ssl_certificate.h"
48 #include "socket.h"
49 #include "remotefolder.h"
50 #include "main.h"
51 #include "account.h"
52
53 #define DISABLE_LOG_DURING_LOGIN
54
55 static struct etpan_thread_manager * thread_manager = NULL;
56 static chash * nntp_hash = NULL;
57 static chash * session_hash = NULL;
58 static guint thread_manager_signal = 0;
59 static GIOChannel * io_channel = NULL;
60
61 static void (*previous_stream_logger)(int direction,
62     const char * str, size_t size);
63
64 static void nntp_logger(int direction, const char * str, size_t size) 
65 {
66         gchar *buf;
67         gchar **lines;
68         int i = 0;
69
70         if (size > 256) {
71                 log_print(LOG_PROTOCOL, "NNTP%c [data - %zd bytes]\n", direction?'>':'<', size);
72                 return;
73         }
74         buf = malloc(size+1);
75         memset(buf, 0, size+1);
76         strncpy(buf, str, size);
77         buf[size] = '\0';
78
79         if (!strncmp(buf, "<<<<<<<", 7) 
80         ||  !strncmp(buf, ">>>>>>>", 7)) {
81                 free(buf);
82                 return;
83         }
84         while (strstr(buf, "\r"))
85                 *strstr(buf, "\r") = ' ';
86         while (strlen(buf) > 0 && buf[strlen(buf)-1] == '\n')
87                 buf[strlen(buf)-1] = '\0';
88
89         lines = g_strsplit(buf, "\n", -1);
90
91         while (lines[i] && *lines[i]) {
92                 log_print(LOG_PROTOCOL, "NNTP%c %s\n", direction?'>':'<', lines[i]);
93                 i++;
94         }
95         g_strfreev(lines);
96         free(buf);
97 }
98
99 static void delete_nntp(Folder *folder, newsnntp *nntp)
100 {
101         chashdatum key;
102
103         key.data = &folder;
104         key.len = sizeof(folder);
105         chash_delete(session_hash, &key, NULL);
106         
107         key.data = &nntp;
108         key.len = sizeof(nntp);
109         if (nntp && nntp->nntp_stream) {
110                 /* we don't want libetpan to logout */
111                 mailstream_close(nntp->nntp_stream);
112                 nntp->nntp_stream = NULL;
113         }
114         debug_print("removing newsnntp %p\n", nntp);
115         newsnntp_free(nntp);    
116 }
117
118 static gboolean thread_manager_event(GIOChannel * source,
119     GIOCondition condition,
120     gpointer data)
121 {
122 #ifdef G_OS_WIN32
123         gsize bytes_read;
124         gchar ch;
125         
126         if (condition & G_IO_IN)
127                 g_io_channel_read_chars(source, &ch, 1, &bytes_read, NULL);
128 #endif
129         etpan_thread_manager_loop(thread_manager);
130         
131         return TRUE;
132 }
133
134 #define ETPAN_DEFAULT_NETWORK_TIMEOUT 60
135 extern gboolean etpan_skip_ssl_cert_check;
136
137 void nntp_main_init(gboolean skip_ssl_cert_check)
138 {
139         int fd_thread_manager;
140         
141         etpan_skip_ssl_cert_check = skip_ssl_cert_check;
142         
143         nntp_hash = chash_new(CHASH_COPYKEY, CHASH_DEFAULTSIZE);
144         session_hash = chash_new(CHASH_COPYKEY, CHASH_DEFAULTSIZE);
145         
146         thread_manager = etpan_thread_manager_new();
147         
148         fd_thread_manager = etpan_thread_manager_get_fd(thread_manager);
149         
150 #ifndef G_OS_WIN32
151         io_channel = g_io_channel_unix_new(fd_thread_manager);
152 #else
153         io_channel = g_io_channel_win32_new_fd(fd_thread_manager);
154 #endif
155         
156         thread_manager_signal = g_io_add_watch_full(io_channel, 0, G_IO_IN,
157                                                     thread_manager_event,
158                                                     (gpointer) NULL,
159                                                     NULL);
160 }
161
162 void nntp_main_done(gboolean have_connectivity)
163 {
164         nntp_disconnect_all(have_connectivity);
165         etpan_thread_manager_stop(thread_manager);
166 #if defined(__NetBSD__) || defined(__OpenBSD__) || defined(__FreeBSD__)
167         return;
168 #endif
169         etpan_thread_manager_join(thread_manager);
170         
171         g_source_remove(thread_manager_signal);
172         g_io_channel_unref(io_channel);
173         
174         etpan_thread_manager_free(thread_manager);
175         
176         chash_free(session_hash);
177         chash_free(nntp_hash);
178 }
179
180 void nntp_init(Folder * folder)
181 {
182         struct etpan_thread * thread;
183         chashdatum key;
184         chashdatum value;
185         
186         thread = etpan_thread_manager_get_thread(thread_manager);
187         
188         key.data = &folder;
189         key.len = sizeof(folder);
190         value.data = thread;
191         value.len = 0;
192         
193         chash_set(nntp_hash, &key, &value, NULL);
194 }
195
196 void nntp_done(Folder * folder)
197 {
198         struct etpan_thread * thread;
199         chashdatum key;
200         chashdatum value;
201         int r;
202         
203         key.data = &folder;
204         key.len = sizeof(folder);
205         
206         r = chash_get(nntp_hash, &key, &value);
207         if (r < 0)
208                 return;
209         
210         thread = value.data;
211         
212         etpan_thread_unbind(thread);
213         
214         chash_delete(nntp_hash, &key, NULL);
215         
216         debug_print("remove thread");
217 }
218
219 static struct etpan_thread * get_thread(Folder * folder)
220 {
221         struct etpan_thread * thread;
222         chashdatum key;
223         chashdatum value;
224         
225         key.data = &folder;
226         key.len = sizeof(folder);
227         
228         chash_get(nntp_hash, &key, &value);
229         thread = value.data;
230         
231         return thread;
232 }
233
234 static newsnntp * get_nntp(Folder * folder)
235 {
236         newsnntp * nntp;
237         chashdatum key;
238         chashdatum value;
239         int r;
240         
241         key.data = &folder;
242         key.len = sizeof(folder);
243         
244         r = chash_get(session_hash, &key, &value);
245         if (r < 0)
246                 return NULL;
247         
248         nntp = value.data;
249         debug_print("found nntp %p\n", nntp);
250         return nntp;
251 }
252
253
254 static void generic_cb(int cancelled, void * result, void * callback_data)
255 {
256         struct etpan_thread_op * op;
257         
258         op = (struct etpan_thread_op *) callback_data;
259
260         debug_print("generic_cb\n");
261         op->finished = 1;
262 }
263
264 static void threaded_run(Folder * folder, void * param, void * result,
265                          void (* func)(struct etpan_thread_op * ))
266 {
267         struct etpan_thread_op * op;
268         struct etpan_thread * thread;
269         
270         nntp_folder_ref(folder);
271
272         op = etpan_thread_op_new();
273         
274         op->nntp = get_nntp(folder);
275         op->param = param;
276         op->result = result;
277         
278         op->cancellable = 0;
279         op->run = func;
280         op->callback = generic_cb;
281         op->callback_data = op;
282         op->cleanup = NULL;
283         
284         op->finished = 0;
285         
286         previous_stream_logger = mailstream_logger;
287         mailstream_logger = nntp_logger;
288
289         thread = get_thread(folder);
290         etpan_thread_op_schedule(thread, op);
291         
292         while (!op->finished) {
293                 gtk_main_iteration();
294         }
295         
296         mailstream_logger = previous_stream_logger;
297
298         etpan_thread_op_free(op);
299
300         nntp_folder_unref(folder);
301 }
302
303
304 /* connect */
305
306 struct connect_param {
307         newsnntp * nntp;
308         PrefsAccount *account;
309         const char * server;
310         int port;
311 };
312
313 struct connect_result {
314         int error;
315 };
316
317 #define CHECK_NNTP() {                                          \
318         if (!param->nntp) {                                     \
319                 result->error = NEWSNNTP_ERROR_BAD_STATE;       \
320                 return;                                         \
321         }                                                       \
322 }
323
324 static void connect_run(struct etpan_thread_op * op)
325 {
326         int r;
327         struct connect_param * param;
328         struct connect_result * result;
329         
330         param = op->param;
331         result = op->result;
332         
333         CHECK_NNTP();
334
335         r = newsnntp_socket_connect(param->nntp,
336                                     param->server, param->port);
337         
338         result->error = r;
339 }
340
341
342 int nntp_threaded_connect(Folder * folder, const char * server, int port)
343 {
344         struct connect_param param;
345         struct connect_result result;
346         chashdatum key;
347         chashdatum value;
348         newsnntp * nntp, * oldnntp;
349         
350         oldnntp = get_nntp(folder);
351
352         nntp = newsnntp_new(0, NULL);
353         
354         if (oldnntp) {
355                 debug_print("deleting old nntp %p\n", oldnntp);
356                 delete_nntp(folder, oldnntp);
357         }
358         
359         key.data = &folder;
360         key.len = sizeof(folder);
361         value.data = nntp;
362         value.len = 0;
363         chash_set(session_hash, &key, &value, NULL);
364         
365         param.nntp = nntp;
366         param.server = server;
367         param.port = port;
368         
369         refresh_resolvers();
370         threaded_run(folder, &param, &result, connect_run);
371         
372         debug_print("connect ok %i with nntp %p\n", result.error, nntp);
373         
374         return result.error;
375 }
376
377 static void connect_ssl_run(struct etpan_thread_op * op)
378 {
379         int r;
380         struct connect_param * param;
381         struct connect_result * result;
382         
383         param = op->param;
384         result = op->result;
385         
386         CHECK_NNTP();
387
388         r = newsnntp_ssl_connect_with_callback(param->nntp,
389                                  param->server, param->port,
390                                  etpan_connect_ssl_context_cb, param->account);
391         result->error = r;
392 }
393
394 int nntp_threaded_connect_ssl(Folder * folder, const char * server, int port)
395 {
396         struct connect_param param;
397         struct connect_result result;
398         chashdatum key;
399         chashdatum value;
400         newsnntp * nntp, * oldnntp;
401         gboolean accept_if_valid = FALSE;
402
403         oldnntp = get_nntp(folder);
404
405         nntp = newsnntp_new(0, NULL);
406
407         if (oldnntp) {
408                 debug_print("deleting old nntp %p\n", oldnntp);
409                 delete_nntp(folder, oldnntp);
410         }
411
412         key.data = &folder;
413         key.len = sizeof(folder);
414         value.data = nntp;
415         value.len = 0;
416         chash_set(session_hash, &key, &value, NULL);
417
418         param.nntp = nntp;
419         param.server = server;
420         param.port = port;
421         param.account = folder->account;
422
423         if (folder->account)
424                 accept_if_valid = folder->account->ssl_certs_auto_accept;
425
426         refresh_resolvers();
427         threaded_run(folder, &param, &result, connect_ssl_run);
428
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)
432                         return -1;
433         }
434         debug_print("connect %d with nntp %p\n", result.error, nntp);
435         
436         return result.error;
437 }
438
439 void nntp_threaded_disconnect(Folder * folder)
440 {
441         newsnntp * nntp;
442         
443         nntp = get_nntp(folder);
444         if (nntp == NULL) {
445                 debug_print("was disconnected\n");
446                 return;
447         }
448         
449         debug_print("deleting old nntp %p\n", nntp);
450         delete_nntp(folder, nntp);
451         
452         debug_print("disconnect ok\n");
453 }
454
455 void nntp_threaded_cancel(Folder * folder)
456 {
457         newsnntp * nntp;
458         
459         nntp = get_nntp(folder);
460         if (nntp->nntp_stream != NULL)
461                 mailstream_cancel(nntp->nntp_stream);
462 }
463
464
465 struct login_param {
466         newsnntp * nntp;
467         const char * login;
468         const char * password;
469 };
470
471 struct login_result {
472         int error;
473 };
474
475 static void login_run(struct etpan_thread_op * op)
476 {
477         struct login_param * param;
478         struct login_result * result;
479         int r;
480 #ifdef DISABLE_LOG_DURING_LOGIN
481         int old_debug;
482 #endif
483         
484         param = op->param;
485         result = op->result;
486
487         CHECK_NNTP();
488
489 #ifdef DISABLE_LOG_DURING_LOGIN
490         old_debug = mailstream_debug;
491         mailstream_debug = 0;
492 #endif
493
494         r = newsnntp_authinfo_username(param->nntp, param->login);
495         /* libetpan returning NO_ERROR means it received resp.code 281:
496            in this case auth. is already successful, no password is needed. */
497         if (r == NEWSNNTP_WARNING_REQUEST_AUTHORIZATION_PASSWORD) {
498                 r = newsnntp_authinfo_password(param->nntp, param->password);
499         }
500         
501
502
503 #ifdef DISABLE_LOG_DURING_LOGIN
504         mailstream_debug = old_debug;
505 #endif
506         
507         result->error = r;
508         if (param->nntp->nntp_response)
509                 nntp_logger(0, param->nntp->nntp_response, strlen(param->nntp->nntp_response));
510
511         debug_print("nntp login run - end %i\n", r);
512 }
513
514 int nntp_threaded_login(Folder * folder, const char * login, const char * password)
515 {
516         struct login_param param;
517         struct login_result result;
518         
519         debug_print("nntp login - begin\n");
520         
521         param.nntp = get_nntp(folder);
522         param.login = login;
523         param.password = password;
524
525         threaded_run(folder, &param, &result, login_run);
526         
527         debug_print("nntp login - end\n");
528         
529         return result.error;
530 }
531
532 struct date_param {
533         newsnntp * nntp;
534         struct tm * lt;
535 };
536
537 struct date_result {
538         int error;
539 };
540
541 static void date_run(struct etpan_thread_op * op)
542 {
543         struct date_param * param;
544         struct date_result * result;
545         int r;
546         
547         param = op->param;
548         result = op->result;
549
550         CHECK_NNTP();
551
552         r = newsnntp_date(param->nntp, param->lt);
553         
554         result->error = r;
555         debug_print("nntp date run - end %i\n", r);
556 }
557
558 int nntp_threaded_date(Folder * folder, struct tm *lt)
559 {
560         struct date_param param;
561         struct date_result result;
562         
563         debug_print("nntp date - begin\n");
564         
565         param.nntp = get_nntp(folder);
566         param.lt = lt;
567
568         threaded_run(folder, &param, &result, date_run);
569         
570         debug_print("nntp date - end\n");
571         
572         return result.error;
573 }
574
575 struct list_param {
576         newsnntp * nntp;
577         clist **grouplist;
578 };
579
580 struct list_result {
581         int error;
582 };
583
584 static void list_run(struct etpan_thread_op * op)
585 {
586         struct list_param * param;
587         struct list_result * result;
588         int r;
589         
590         param = op->param;
591         result = op->result;
592
593         CHECK_NNTP();
594
595         r = newsnntp_list(param->nntp, param->grouplist);
596         
597         result->error = r;
598         debug_print("nntp list run - end %i\n", r);
599 }
600
601 int nntp_threaded_list(Folder * folder, clist **grouplist)
602 {
603         struct list_param param;
604         struct list_result result;
605         
606         debug_print("nntp list - begin\n");
607         
608         param.nntp = get_nntp(folder);
609         param.grouplist = grouplist;
610
611         threaded_run(folder, &param, &result, list_run);
612         
613         debug_print("nntp list - end\n");
614         
615         return result.error;
616 }
617
618 struct post_param {
619         newsnntp * nntp;
620         char *contents;
621         size_t len;
622 };
623
624 struct post_result {
625         int error;
626 };
627
628 static void post_run(struct etpan_thread_op * op)
629 {
630         struct post_param * param;
631         struct post_result * result;
632         int r;
633         
634         param = op->param;
635         result = op->result;
636
637         CHECK_NNTP();
638
639         r = newsnntp_post(param->nntp, param->contents, param->len);
640         
641         result->error = r;
642         debug_print("nntp post run - end %i\n", r);
643 }
644
645 int nntp_threaded_post(Folder * folder, char *contents, size_t len)
646 {
647         struct post_param param;
648         struct post_result result;
649         
650         debug_print("nntp post - begin\n");
651         
652         param.nntp = get_nntp(folder);
653         param.contents = contents;
654         param.len = len;
655
656         threaded_run(folder, &param, &result, post_run);
657         
658         debug_print("nntp post - end\n");
659         
660         return result.error;
661 }
662
663 struct article_param {
664         newsnntp * nntp;
665         guint32 num;
666         char **contents;
667         size_t *len;
668 };
669
670 struct article_result {
671         int error;
672 };
673
674 static void article_run(struct etpan_thread_op * op)
675 {
676         struct article_param * param;
677         struct article_result * result;
678         int r;
679         
680         param = op->param;
681         result = op->result;
682
683         CHECK_NNTP();
684
685         r = newsnntp_article(param->nntp, param->num, param->contents, param->len);
686         
687         result->error = r;
688         debug_print("nntp article run - end %i\n", r);
689 }
690
691 int nntp_threaded_article(Folder * folder, guint32 num, char **contents, size_t *len)
692 {
693         struct article_param param;
694         struct article_result result;
695         
696         debug_print("nntp article - begin\n");
697         
698         param.nntp = get_nntp(folder);
699         param.num = num;
700         param.contents = contents;
701         param.len = len;
702
703         threaded_run(folder, &param, &result, article_run);
704         
705         debug_print("nntp article - end\n");
706         
707         return result.error;
708 }
709
710 struct group_param {
711         newsnntp * nntp;
712         const char *group;
713         struct newsnntp_group_info **info;
714 };
715
716 struct group_result {
717         int error;
718 };
719
720 static void group_run(struct etpan_thread_op * op)
721 {
722         struct group_param * param;
723         struct group_result * result;
724         int r;
725         
726         param = op->param;
727         result = op->result;
728
729         CHECK_NNTP();
730
731         r = newsnntp_group(param->nntp, param->group, param->info);
732         
733         result->error = r;
734         debug_print("nntp group run - end %i\n", r);
735 }
736
737 int nntp_threaded_group(Folder * folder, const char *group, struct newsnntp_group_info **info)
738 {
739         struct group_param param;
740         struct group_result result;
741         
742         debug_print("nntp group - begin\n");
743         
744         param.nntp = get_nntp(folder);
745         param.group = group;
746         param.info = info;
747
748         threaded_run(folder, &param, &result, group_run);
749         
750         debug_print("nntp group - end\n");
751         
752         return result.error;
753 }
754
755 struct mode_reader_param {
756         newsnntp * nntp;
757 };
758
759 struct mode_reader_result {
760         int error;
761 };
762
763 static void mode_reader_run(struct etpan_thread_op * op)
764 {
765         struct mode_reader_param * param;
766         struct mode_reader_result * result;
767         int r;
768         
769         param = op->param;
770         result = op->result;
771
772         CHECK_NNTP();
773
774         r = newsnntp_mode_reader(param->nntp);
775         
776         result->error = r;
777         debug_print("nntp mode_reader run - end %i\n", r);
778 }
779
780 int nntp_threaded_mode_reader(Folder * folder)
781 {
782         struct mode_reader_param param;
783         struct mode_reader_result result;
784         
785         debug_print("nntp mode_reader - begin\n");
786         
787         param.nntp = get_nntp(folder);
788
789         threaded_run(folder, &param, &result, mode_reader_run);
790         
791         debug_print("nntp mode_reader - end\n");
792         
793         return result.error;
794 }
795
796 struct xover_param {
797         newsnntp * nntp;
798         guint32 beg;
799         guint32 end;
800         struct newsnntp_xover_resp_item **result;
801         clist **msglist;
802 };
803
804 struct xover_result {
805         int error;
806 };
807
808 static void xover_run(struct etpan_thread_op * op)
809 {
810         struct xover_param * param;
811         struct xover_result * result;
812         int r;
813         
814         param = op->param;
815         result = op->result;
816
817         CHECK_NNTP();
818         
819         if (param->result) {
820                 r = newsnntp_xover_single(param->nntp, param->beg, param->result);
821         } else {
822                 r = newsnntp_xover_range(param->nntp, param->beg, param->end, param->msglist);
823         }
824         
825         result->error = r;
826         debug_print("nntp xover run - end %i\n", r);
827 }
828
829 int nntp_threaded_xover(Folder * folder, guint32 beg, guint32 end, struct newsnntp_xover_resp_item **single_result, clist **multiple_result)
830 {
831         struct xover_param param;
832         struct xover_result result;
833         
834         debug_print("nntp xover - begin\n");
835         
836         param.nntp = get_nntp(folder);
837         param.beg = beg;
838         param.end = end;
839         param.result = single_result;
840         param.msglist = multiple_result;
841
842         threaded_run(folder, &param, &result, xover_run);
843         
844         debug_print("nntp xover - end\n");
845         
846         return result.error;
847 }
848
849 struct xhdr_param {
850         newsnntp * nntp;
851         const char *header;
852         guint32 beg;
853         guint32 end;
854         clist **hdrlist;
855 };
856
857 struct xhdr_result {
858         int error;
859 };
860
861 static void xhdr_run(struct etpan_thread_op * op)
862 {
863         struct xhdr_param * param;
864         struct xhdr_result * result;
865         int r;
866         
867         param = op->param;
868         result = op->result;
869
870         CHECK_NNTP();
871         
872         if (param->beg == param->end) {
873                 r = newsnntp_xhdr_single(param->nntp, param->header, param->beg, param->hdrlist);
874         } else {
875                 r = newsnntp_xhdr_range(param->nntp, param->header, param->beg, param->end, param->hdrlist);
876         }
877         
878         result->error = r;
879         debug_print("nntp xhdr run - end %i\n", r);
880 }
881
882 int nntp_threaded_xhdr(Folder * folder, const char *header, guint32 beg, guint32 end, clist **hdrlist)
883 {
884         struct xhdr_param param;
885         struct xhdr_result result;
886         
887         debug_print("nntp xhdr - begin\n");
888         
889         param.nntp = get_nntp(folder);
890         param.header = header;
891         param.beg = beg;
892         param.end = end;
893         param.hdrlist = hdrlist;
894
895         threaded_run(folder, &param, &result, xhdr_run);
896         
897         debug_print("nntp xhdr - end\n");
898         
899         return result.error;
900 }
901
902 void nntp_main_set_timeout(int sec)
903 {
904         mailstream_network_delay.tv_sec = sec;
905         mailstream_network_delay.tv_usec = 0;
906 }
907
908 #else
909
910 void nntp_main_init(void)
911 {
912 }
913 void nntp_main_done(gboolean have_connectivity)
914 {
915 }
916 void nntp_main_set_timeout(int sec)
917 {
918 }
919
920 void nntp_threaded_cancel(Folder * folder);
921 {
922 }
923
924 #endif