b775c0c9a003f815a2f7f7b59e97af3a69d59759
[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 "utils.h"
45 #include "mainwindow.h"
46 #include "ssl_certificate.h"
47 #include "socket.h"
48 #include "remotefolder.h"
49
50 #define DISABLE_LOG_DURING_LOGIN
51
52 static struct etpan_thread_manager * thread_manager = NULL;
53 static chash * nntp_hash = NULL;
54 static chash * session_hash = NULL;
55 static guint thread_manager_signal = 0;
56 static GIOChannel * io_channel = NULL;
57
58 static void (*previous_stream_logger)(int direction,
59     const char * str, size_t size);
60
61 static void nntp_logger(int direction, const char * str, size_t size) 
62 {
63         gchar *buf;
64         gchar **lines;
65         int i = 0;
66
67         if (size > 256) {
68                 log_print(LOG_PROTOCOL, "NNTP%c [data - %zd bytes]\n", direction?'>':'<', size);
69                 return;
70         }
71         buf = malloc(size+1);
72         memset(buf, 0, size+1);
73         strncpy(buf, str, size);
74         buf[size] = '\0';
75
76         if (!strncmp(buf, "<<<<<<<", 7) 
77         ||  !strncmp(buf, ">>>>>>>", 7)) {
78                 free(buf);
79                 return;
80         }
81         while (strstr(buf, "\r"))
82                 *strstr(buf, "\r") = ' ';
83         while (strlen(buf) > 0 && buf[strlen(buf)-1] == '\n')
84                 buf[strlen(buf)-1] = '\0';
85
86         lines = g_strsplit(buf, "\n", -1);
87
88         while (lines[i] && *lines[i]) {
89                 log_print(LOG_PROTOCOL, "NNTP%c %s\n", direction?'>':'<', lines[i]);
90                 i++;
91         }
92         g_strfreev(lines);
93         free(buf);
94 }
95
96 static void delete_nntp(Folder *folder, newsnntp *nntp)
97 {
98         chashdatum key;
99         chashdatum value;
100
101         key.data = &folder;
102         key.len = sizeof(folder);
103         value.data = nntp;
104         value.len = 0;
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         etpan_thread_manager_stop(thread_manager);
165 #if defined(__NetBSD__) || defined(__OpenBSD__) || defined(__FreeBSD__)
166         return;
167 #endif
168         etpan_thread_manager_join(thread_manager);
169         
170         g_source_remove(thread_manager_signal);
171         g_io_channel_unref(io_channel);
172         
173         etpan_thread_manager_free(thread_manager);
174         
175         chash_free(session_hash);
176         chash_free(nntp_hash);
177 }
178
179 void nntp_init(Folder * folder)
180 {
181         struct etpan_thread * thread;
182         chashdatum key;
183         chashdatum value;
184         
185         thread = etpan_thread_manager_get_thread(thread_manager);
186         
187         key.data = &folder;
188         key.len = sizeof(folder);
189         value.data = thread;
190         value.len = 0;
191         
192         chash_set(nntp_hash, &key, &value, NULL);
193 }
194
195 void nntp_done(Folder * folder)
196 {
197         struct etpan_thread * thread;
198         chashdatum key;
199         chashdatum value;
200         int r;
201         
202         key.data = &folder;
203         key.len = sizeof(folder);
204         
205         r = chash_get(nntp_hash, &key, &value);
206         if (r < 0)
207                 return;
208         
209         thread = value.data;
210         
211         etpan_thread_unbind(thread);
212         
213         chash_delete(nntp_hash, &key, NULL);
214         
215         debug_print("remove thread");
216 }
217
218 static struct etpan_thread * get_thread(Folder * folder)
219 {
220         struct etpan_thread * thread;
221         chashdatum key;
222         chashdatum value;
223         
224         key.data = &folder;
225         key.len = sizeof(folder);
226         
227         chash_get(nntp_hash, &key, &value);
228         thread = value.data;
229         
230         return thread;
231 }
232
233 static newsnntp * get_nntp(Folder * folder)
234 {
235         newsnntp * nntp;
236         chashdatum key;
237         chashdatum value;
238         int r;
239         
240         key.data = &folder;
241         key.len = sizeof(folder);
242         
243         r = chash_get(session_hash, &key, &value);
244         if (r < 0)
245                 return NULL;
246         
247         nntp = value.data;
248         debug_print("found nntp %p\n", nntp);
249         return nntp;
250 }
251
252
253 static void generic_cb(int cancelled, void * result, void * callback_data)
254 {
255         struct etpan_thread_op * op;
256         
257         op = (struct etpan_thread_op *) callback_data;
258
259         debug_print("generic_cb\n");
260         op->finished = 1;
261 }
262
263 static void threaded_run(Folder * folder, void * param, void * result,
264                          void (* func)(struct etpan_thread_op * ))
265 {
266         struct etpan_thread_op * op;
267         struct etpan_thread * thread;
268         
269         nntp_folder_ref(folder);
270
271         op = etpan_thread_op_new();
272         
273         op->nntp = get_nntp(folder);
274         op->param = param;
275         op->result = result;
276         
277         op->cancellable = 0;
278         op->run = func;
279         op->callback = generic_cb;
280         op->callback_data = op;
281         op->cleanup = NULL;
282         
283         op->finished = 0;
284         
285         previous_stream_logger = mailstream_logger;
286         mailstream_logger = nntp_logger;
287
288         thread = get_thread(folder);
289         etpan_thread_op_schedule(thread, op);
290         
291         while (!op->finished) {
292                 gtk_main_iteration();
293         }
294         
295         mailstream_logger = previous_stream_logger;
296
297         etpan_thread_op_free(op);
298
299         nntp_folder_unref(folder);
300 }
301
302
303 /* connect */
304
305 struct connect_param {
306         newsnntp * nntp;
307         PrefsAccount *account;
308         const char * server;
309         int port;
310 };
311
312 struct connect_result {
313         int error;
314 };
315
316 #define CHECK_NNTP() {                                          \
317         if (!param->nntp) {                                     \
318                 result->error = NEWSNNTP_ERROR_BAD_STATE;       \
319                 return;                                         \
320         }                                                       \
321 }
322
323 static void connect_run(struct etpan_thread_op * op)
324 {
325         int r;
326         struct connect_param * param;
327         struct connect_result * result;
328         
329         param = op->param;
330         result = op->result;
331         
332         CHECK_NNTP();
333
334         r = newsnntp_socket_connect(param->nntp,
335                                     param->server, param->port);
336         
337         result->error = r;
338 }
339
340
341 int nntp_threaded_connect(Folder * folder, const char * server, int port)
342 {
343         struct connect_param param;
344         struct connect_result result;
345         chashdatum key;
346         chashdatum value;
347         newsnntp * nntp, * oldnntp;
348         
349         oldnntp = get_nntp(folder);
350
351         nntp = newsnntp_new(0, NULL);
352         
353         if (oldnntp) {
354                 debug_print("deleting old nntp %p\n", oldnntp);
355                 delete_nntp(folder, oldnntp);
356         }
357         
358         key.data = &folder;
359         key.len = sizeof(folder);
360         value.data = nntp;
361         value.len = 0;
362         chash_set(session_hash, &key, &value, NULL);
363         
364         param.nntp = nntp;
365         param.server = server;
366         param.port = port;
367         
368         refresh_resolvers();
369         threaded_run(folder, &param, &result, connect_run);
370         
371         debug_print("connect ok %i with nntp %p\n", result.error, nntp);
372         
373         return result.error;
374 }
375
376 static int etpan_certificate_check(const unsigned char *certificate, int len, void *data)
377 {
378 #ifdef USE_GNUTLS
379         struct connect_param *param = (struct connect_param *)data;
380         gnutls_x509_crt cert = NULL;
381         gnutls_datum tmp;
382         
383         if (certificate == NULL || len < 0) {
384                 g_warning("no cert presented.\n");
385                 return 0;
386         }
387         
388         tmp.data = malloc(len);
389         memcpy(tmp.data, certificate, len);
390         tmp.size = len;
391         gnutls_x509_crt_init(&cert);
392         if (gnutls_x509_crt_import(cert, &tmp, GNUTLS_X509_FMT_DER) < 0) {
393                 g_warning("nntp: can't get cert\n");
394                 return 0;
395         } else if (ssl_certificate_check(cert, (guint)-1,
396                 (gchar *)param->server, (gushort)param->port) == TRUE) {
397                 gnutls_x509_crt_deinit(cert);
398                 return 0;
399         } else {
400                 gnutls_x509_crt_deinit(cert);
401                 return -1;
402         }
403 #endif
404         return 0;
405 }
406
407 static void connect_ssl_context_cb(struct mailstream_ssl_context * ssl_context, void * data)
408 {
409 #ifdef USE_GNUTLS
410         PrefsAccount *account = (PrefsAccount *)data;
411         const gchar *cert_path = NULL;
412         const gchar *password = NULL;
413         gnutls_x509_crt x509 = NULL;
414         gnutls_x509_privkey pkey = NULL;
415
416         if (account->in_ssl_client_cert_file && *account->in_ssl_client_cert_file)
417                 cert_path = account->in_ssl_client_cert_file;
418         if (account->in_ssl_client_cert_pass && *account->in_ssl_client_cert_pass)
419                 password = account->in_ssl_client_cert_pass;
420         
421         if (mailstream_ssl_set_client_certificate_data(ssl_context, NULL, 0) < 0 ||
422             mailstream_ssl_set_client_private_key_data(ssl_context, NULL, 0) < 0)
423                 debug_print("Impossible to set the client certificate.\n");
424         x509 = ssl_certificate_get_x509_from_pem_file(cert_path);
425         pkey = ssl_certificate_get_pkey_from_pem_file(cert_path);
426         if (!(x509 && pkey)) {
427                 /* try pkcs12 format */
428                 ssl_certificate_get_x509_and_pkey_from_p12_file(cert_path, password, &x509, &pkey);
429         }
430         if (x509 && pkey) {
431                 unsigned char *x509_der = NULL, *pkey_der = NULL;
432                 size_t x509_len, pkey_len;
433                 
434                 x509_len = (size_t)gnutls_i2d_X509(x509, &x509_der);
435                 pkey_len = (size_t)gnutls_i2d_PrivateKey(pkey, &pkey_der);
436                 if (x509_len > 0 && pkey_len > 0) {
437                         if (mailstream_ssl_set_client_certificate_data(ssl_context, x509_der, x509_len) < 0 ||
438                             mailstream_ssl_set_client_private_key_data(ssl_context, pkey_der, pkey_len) < 0) 
439                                 log_error(LOG_PROTOCOL, _("Impossible to set the client certificate.\n"));
440                         g_free(x509_der);
441                         g_free(pkey_der);
442                 }
443                 gnutls_x509_crt_deinit(x509);
444                 gnutls_x509_privkey_deinit(pkey);
445         }
446 #endif
447 }
448
449 static void connect_ssl_run(struct etpan_thread_op * op)
450 {
451         int r;
452         struct connect_param * param;
453         struct connect_result * result;
454         
455         param = op->param;
456         result = op->result;
457         
458         CHECK_NNTP();
459
460         r = newsnntp_ssl_connect_with_callback(param->nntp,
461                                  param->server, param->port,
462                                  connect_ssl_context_cb, param->account);
463         result->error = r;
464 }
465
466 int nntp_threaded_connect_ssl(Folder * folder, const char * server, int port)
467 {
468         struct connect_param param;
469         struct connect_result result;
470         chashdatum key;
471         chashdatum value;
472         newsnntp * nntp, * oldnntp;
473         unsigned char *certificate = NULL;
474         int cert_len;
475         
476         oldnntp = get_nntp(folder);
477
478         nntp = newsnntp_new(0, NULL);
479         
480         if (oldnntp) {
481                 debug_print("deleting old nntp %p\n", oldnntp);
482                 delete_nntp(folder, oldnntp);
483         }
484
485         key.data = &folder;
486         key.len = sizeof(folder);
487         value.data = nntp;
488         value.len = 0;
489         chash_set(session_hash, &key, &value, NULL);
490         
491         param.nntp = nntp;
492         param.server = server;
493         param.port = port;
494         param.account = folder->account;
495
496         refresh_resolvers();
497         threaded_run(folder, &param, &result, connect_ssl_run);
498
499         if (result.error == NEWSNNTP_NO_ERROR && !etpan_skip_ssl_cert_check) {
500                 cert_len = (int)mailstream_ssl_get_certificate(nntp->nntp_stream, &certificate);
501                 if (etpan_certificate_check(certificate, cert_len, &param) < 0)
502                         return -1;
503                 if (certificate) 
504                         free(certificate); 
505         }
506         debug_print("connect %d with nntp %p\n", result.error, nntp);
507         
508         return result.error;
509 }
510
511 void nntp_threaded_disconnect(Folder * folder)
512 {
513         newsnntp * nntp;
514         
515         nntp = get_nntp(folder);
516         if (nntp == NULL) {
517                 debug_print("was disconnected\n");
518                 return;
519         }
520         
521         debug_print("deleting old nntp %p\n", nntp);
522         delete_nntp(folder, nntp);
523         
524         debug_print("disconnect ok\n");
525 }
526
527 void nntp_threaded_cancel(Folder * folder)
528 {
529         newsnntp * nntp;
530         
531         nntp = get_nntp(folder);
532         if (nntp->nntp_stream != NULL)
533                 mailstream_cancel(nntp->nntp_stream);
534 }
535
536
537 struct login_param {
538         newsnntp * nntp;
539         const char * login;
540         const char * password;
541 };
542
543 struct login_result {
544         int error;
545 };
546
547 static void login_run(struct etpan_thread_op * op)
548 {
549         struct login_param * param;
550         struct login_result * result;
551         int r;
552 #ifdef DISABLE_LOG_DURING_LOGIN
553         int old_debug;
554 #endif
555         
556         param = op->param;
557         result = op->result;
558
559         CHECK_NNTP();
560
561 #ifdef DISABLE_LOG_DURING_LOGIN
562         old_debug = mailstream_debug;
563         mailstream_debug = 0;
564 #endif
565
566         r = newsnntp_authinfo_username(param->nntp, param->login);
567         /* libetpan returning NO_ERROR means it received resp.code 281:
568            in this case auth. is already successful, no password is needed. */
569         if (r == NEWSNNTP_WARNING_REQUEST_AUTHORIZATION_PASSWORD) {
570                 r = newsnntp_authinfo_password(param->nntp, param->password);
571         }
572         
573
574
575 #ifdef DISABLE_LOG_DURING_LOGIN
576         mailstream_debug = old_debug;
577 #endif
578         
579         result->error = r;
580         if (param->nntp->nntp_response)
581                 nntp_logger(0, param->nntp->nntp_response, strlen(param->nntp->nntp_response));
582
583         debug_print("nntp login run - end %i\n", r);
584 }
585
586 int nntp_threaded_login(Folder * folder, const char * login, const char * password)
587 {
588         struct login_param param;
589         struct login_result result;
590         
591         debug_print("nntp login - begin\n");
592         
593         param.nntp = get_nntp(folder);
594         param.login = login;
595         param.password = password;
596
597         threaded_run(folder, &param, &result, login_run);
598         
599         debug_print("nntp login - end\n");
600         
601         return result.error;
602 }
603
604 struct date_param {
605         newsnntp * nntp;
606         struct tm * lt;
607 };
608
609 struct date_result {
610         int error;
611 };
612
613 static void date_run(struct etpan_thread_op * op)
614 {
615         struct date_param * param;
616         struct date_result * result;
617         int r;
618         
619         param = op->param;
620         result = op->result;
621
622         CHECK_NNTP();
623
624         r = newsnntp_date(param->nntp, param->lt);
625         
626         result->error = r;
627         debug_print("nntp date run - end %i\n", r);
628 }
629
630 int nntp_threaded_date(Folder * folder, struct tm *lt)
631 {
632         struct date_param param;
633         struct date_result result;
634         
635         debug_print("nntp date - begin\n");
636         
637         param.nntp = get_nntp(folder);
638         param.lt = lt;
639
640         threaded_run(folder, &param, &result, date_run);
641         
642         debug_print("nntp date - end\n");
643         
644         return result.error;
645 }
646
647 struct list_param {
648         newsnntp * nntp;
649         clist **grouplist;
650 };
651
652 struct list_result {
653         int error;
654 };
655
656 static void list_run(struct etpan_thread_op * op)
657 {
658         struct list_param * param;
659         struct list_result * result;
660         int r;
661         
662         param = op->param;
663         result = op->result;
664
665         CHECK_NNTP();
666
667         r = newsnntp_list(param->nntp, param->grouplist);
668         
669         result->error = r;
670         debug_print("nntp list run - end %i\n", r);
671 }
672
673 int nntp_threaded_list(Folder * folder, clist **grouplist)
674 {
675         struct list_param param;
676         struct list_result result;
677         
678         debug_print("nntp list - begin\n");
679         
680         param.nntp = get_nntp(folder);
681         param.grouplist = grouplist;
682
683         threaded_run(folder, &param, &result, list_run);
684         
685         debug_print("nntp list - end\n");
686         
687         return result.error;
688 }
689
690 struct post_param {
691         newsnntp * nntp;
692         char *contents;
693         size_t len;
694 };
695
696 struct post_result {
697         int error;
698 };
699
700 static void post_run(struct etpan_thread_op * op)
701 {
702         struct post_param * param;
703         struct post_result * result;
704         int r;
705         
706         param = op->param;
707         result = op->result;
708
709         CHECK_NNTP();
710
711         r = newsnntp_post(param->nntp, param->contents, param->len);
712         
713         result->error = r;
714         debug_print("nntp post run - end %i\n", r);
715 }
716
717 int nntp_threaded_post(Folder * folder, char *contents, size_t len)
718 {
719         struct post_param param;
720         struct post_result result;
721         
722         debug_print("nntp post - begin\n");
723         
724         param.nntp = get_nntp(folder);
725         param.contents = contents;
726         param.len = len;
727
728         threaded_run(folder, &param, &result, post_run);
729         
730         debug_print("nntp post - end\n");
731         
732         return result.error;
733 }
734
735 struct article_param {
736         newsnntp * nntp;
737         guint32 num;
738         char **contents;
739         size_t *len;
740 };
741
742 struct article_result {
743         int error;
744 };
745
746 static void article_run(struct etpan_thread_op * op)
747 {
748         struct article_param * param;
749         struct article_result * result;
750         int r;
751         
752         param = op->param;
753         result = op->result;
754
755         CHECK_NNTP();
756
757         r = newsnntp_article(param->nntp, param->num, param->contents, param->len);
758         
759         result->error = r;
760         debug_print("nntp article run - end %i\n", r);
761 }
762
763 int nntp_threaded_article(Folder * folder, guint32 num, char **contents, size_t *len)
764 {
765         struct article_param param;
766         struct article_result result;
767         
768         debug_print("nntp article - begin\n");
769         
770         param.nntp = get_nntp(folder);
771         param.num = num;
772         param.contents = contents;
773         param.len = len;
774
775         threaded_run(folder, &param, &result, article_run);
776         
777         debug_print("nntp post - end\n");
778         
779         return result.error;
780 }
781
782 struct group_param {
783         newsnntp * nntp;
784         const char *group;
785         struct newsnntp_group_info **info;
786 };
787
788 struct group_result {
789         int error;
790 };
791
792 static void group_run(struct etpan_thread_op * op)
793 {
794         struct group_param * param;
795         struct group_result * result;
796         int r;
797         
798         param = op->param;
799         result = op->result;
800
801         CHECK_NNTP();
802
803         r = newsnntp_group(param->nntp, param->group, param->info);
804         
805         result->error = r;
806         debug_print("nntp group run - end %i\n", r);
807 }
808
809 int nntp_threaded_group(Folder * folder, const char *group, struct newsnntp_group_info **info)
810 {
811         struct group_param param;
812         struct group_result result;
813         
814         debug_print("nntp group - begin\n");
815         
816         param.nntp = get_nntp(folder);
817         param.group = group;
818         param.info = info;
819
820         threaded_run(folder, &param, &result, group_run);
821         
822         debug_print("nntp group - end\n");
823         
824         return result.error;
825 }
826
827 struct mode_reader_param {
828         newsnntp * nntp;
829 };
830
831 struct mode_reader_result {
832         int error;
833 };
834
835 static void mode_reader_run(struct etpan_thread_op * op)
836 {
837         struct mode_reader_param * param;
838         struct mode_reader_result * result;
839         int r;
840         
841         param = op->param;
842         result = op->result;
843
844         CHECK_NNTP();
845
846         r = newsnntp_mode_reader(param->nntp);
847         
848         result->error = r;
849         debug_print("nntp mode_reader run - end %i\n", r);
850 }
851
852 int nntp_threaded_mode_reader(Folder * folder)
853 {
854         struct mode_reader_param param;
855         struct mode_reader_result result;
856         
857         debug_print("nntp mode_reader - begin\n");
858         
859         param.nntp = get_nntp(folder);
860
861         threaded_run(folder, &param, &result, mode_reader_run);
862         
863         debug_print("nntp mode_reader - end\n");
864         
865         return result.error;
866 }
867
868 struct xover_param {
869         newsnntp * nntp;
870         guint32 beg;
871         guint32 end;
872         struct newsnntp_xover_resp_item **result;
873         clist **msglist;
874 };
875
876 struct xover_result {
877         int error;
878 };
879
880 static void xover_run(struct etpan_thread_op * op)
881 {
882         struct xover_param * param;
883         struct xover_result * result;
884         int r;
885         
886         param = op->param;
887         result = op->result;
888
889         CHECK_NNTP();
890         
891         if (param->result) {
892                 r = newsnntp_xover_single(param->nntp, param->beg, param->result);
893         } else {
894                 r = newsnntp_xover_range(param->nntp, param->beg, param->end, param->msglist);
895         }
896         
897         result->error = r;
898         debug_print("nntp xover run - end %i\n", r);
899 }
900
901 int nntp_threaded_xover(Folder * folder, guint32 beg, guint32 end, struct newsnntp_xover_resp_item **single_result, clist **multiple_result)
902 {
903         struct xover_param param;
904         struct xover_result result;
905         
906         debug_print("nntp xover - begin\n");
907         
908         param.nntp = get_nntp(folder);
909         param.beg = beg;
910         param.end = end;
911         param.result = single_result;
912         param.msglist = multiple_result;
913
914         threaded_run(folder, &param, &result, xover_run);
915         
916         debug_print("nntp xover - end\n");
917         
918         return result.error;
919 }
920
921 struct xhdr_param {
922         newsnntp * nntp;
923         const char *header;
924         guint32 beg;
925         guint32 end;
926         clist **hdrlist;
927 };
928
929 struct xhdr_result {
930         int error;
931 };
932
933 static void xhdr_run(struct etpan_thread_op * op)
934 {
935         struct xhdr_param * param;
936         struct xhdr_result * result;
937         int r;
938         
939         param = op->param;
940         result = op->result;
941
942         CHECK_NNTP();
943         
944         if (param->beg == param->end) {
945                 r = newsnntp_xhdr_single(param->nntp, param->header, param->beg, param->hdrlist);
946         } else {
947                 r = newsnntp_xhdr_range(param->nntp, param->header, param->beg, param->end, param->hdrlist);
948         }
949         
950         result->error = r;
951         debug_print("nntp xhdr run - end %i\n", r);
952 }
953
954 int nntp_threaded_xhdr(Folder * folder, const char *header, guint32 beg, guint32 end, clist **hdrlist)
955 {
956         struct xhdr_param param;
957         struct xhdr_result result;
958         
959         debug_print("nntp xhdr - begin\n");
960         
961         param.nntp = get_nntp(folder);
962         param.header = header;
963         param.beg = beg;
964         param.end = end;
965         param.hdrlist = hdrlist;
966
967         threaded_run(folder, &param, &result, xhdr_run);
968         
969         debug_print("nntp xhdr - end\n");
970         
971         return result.error;
972 }
973
974
975 #else
976
977 void nntp_main_init(void)
978 {
979 }
980 void nntp_main_done(gboolean have_connectivity)
981 {
982 }
983 void nntp_main_set_timeout(int sec)
984 {
985 }
986
987 void nntp_threaded_cancel(Folder * folder);
988 {
989 }
990
991 #endif