2008-02-28 [colin] 3.3.1cvs5
[claws.git] / src / etpan / imap-thread.c
1 /*
2  * Claws Mail -- a GTK+ based, lightweight, and fast e-mail client
3  * Copyright (C) 2005-2007 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 #endif
23
24 #ifdef HAVE_LIBETPAN
25
26 #include "imap-thread.h"
27 #include <imap.h>
28 #include <sys/types.h>
29 #include <sys/stat.h>
30 #if (defined(__DragonFly__) || defined (__NetBSD__) || defined (__FreeBSD__) || defined (__OpenBSD__) || defined (__CYGWIN__))
31 #include <sys/socket.h>
32 #endif
33 #include <fcntl.h>
34 #ifndef G_OS_WIN32
35 #include <sys/mman.h>
36 #include <sys/wait.h>
37 #endif
38 #include <gtk/gtk.h>
39 #include <log.h>
40 #include "etpan-thread-manager.h"
41 #include "utils.h"
42 #include "mainwindow.h"
43 #include "ssl_certificate.h"
44 #include "socket.h"
45 #include "remotefolder.h"
46
47 #define DISABLE_LOG_DURING_LOGIN
48
49 static struct etpan_thread_manager * thread_manager = NULL;
50 static chash * courier_workaround_hash = NULL;
51 static chash * imap_hash = NULL;
52 static chash * session_hash = NULL;
53 static guint thread_manager_signal = 0;
54 static GIOChannel * io_channel = NULL;
55
56 static void delete_imap(Folder *folder, mailimap *imap)
57 {
58         chashdatum key;
59         chashdatum value;
60
61         key.data = &folder;
62         key.len = sizeof(folder);
63         value.data = imap;
64         value.len = 0;
65         chash_delete(session_hash, &key, NULL);
66         
67         key.data = &imap;
68         key.len = sizeof(imap);
69         chash_delete(courier_workaround_hash, &key, NULL);
70         if (imap && imap->imap_stream) {
71                 /* we don't want libetpan to logout */
72                 mailstream_close(imap->imap_stream);
73                 imap->imap_stream = NULL;
74         }
75         debug_print("removing mailimap %p\n", imap);
76         mailimap_free(imap);    
77 }
78
79 static gboolean thread_manager_event(GIOChannel * source,
80     GIOCondition condition,
81     gpointer data)
82 {
83         etpan_thread_manager_loop(thread_manager);
84         
85         return TRUE;
86 }
87
88 static void imap_logger_noop(int direction, const char * str, size_t size) 
89 {
90         /* inhibit logging */
91 }
92
93 static void imap_logger_cmd(int direction, const char * str, size_t size) 
94 {
95         gchar *buf;
96         gchar **lines;
97         int i = 0;
98
99         if (size > 8192) {
100                 log_print(LOG_PROTOCOL, "IMAP4%c [CMD data - %zd bytes]\n", direction?'>':'<', size);
101                 return;
102         }
103         buf = malloc(size+1);
104         memset(buf, 0, size+1);
105         strncpy(buf, str, size);
106         buf[size] = '\0';
107
108         if (!strncmp(buf, "<<<<<<<", 7) 
109         ||  !strncmp(buf, ">>>>>>>", 7)) {
110                 free(buf);
111                 return;
112         }
113         while (strstr(buf, "\r"))
114                 *strstr(buf, "\r") = ' ';
115         while (strlen(buf) > 0 && buf[strlen(buf)-1] == '\n')
116                 buf[strlen(buf)-1] = '\0';
117
118         lines = g_strsplit(buf, "\n", -1);
119
120         while (lines[i] && *lines[i]) {
121                 log_print(LOG_PROTOCOL, "IMAP4%c %s\n", direction?'>':'<', lines[i]);
122                 i++;
123         }
124         g_strfreev(lines);
125         free(buf);
126 }
127
128 static void imap_logger_fetch(int direction, const char * str, size_t size) 
129 {
130         gchar *buf;
131         gchar **lines;
132         int i = 0;
133
134         if (size > 128 && !direction) {
135                 log_print(LOG_PROTOCOL, "IMAP4%c [FETCH data - %zd bytes]\n", direction?'>':'<', size);
136                 return;
137         }
138         
139         buf = malloc(size+1);
140         memset(buf, 0, size+1);
141         strncpy(buf, str, size);
142         buf[size] = '\0';
143         if (!strncmp(buf, "<<<<<<<", 7) 
144         ||  !strncmp(buf, ">>>>>>>", 7)) {
145                 free(buf);
146                 return;
147         }
148         while (strstr(buf, "\r"))
149                 *strstr(buf, "\r") = ' ';
150         while (strlen(buf) > 0 && buf[strlen(buf)-1] == '\n')
151                 buf[strlen(buf)-1] = '\0';
152
153         lines = g_strsplit(buf, "\n", -1);
154
155         if (direction != 0 || (buf[0] == '*' && buf[1] == ' ') || size < 32) {
156                 while (lines[i] && *lines[i]) {
157                         log_print(LOG_PROTOCOL, "IMAP4%c %s\n", direction?'>':'<', lines[i]);
158                         i++;
159                 }
160         } else {
161                 log_print(LOG_PROTOCOL, "IMAP4%c [data - %zd bytes]\n", direction?'>':'<', size);
162         }
163         g_strfreev(lines);
164         free(buf);
165 }
166
167 static void imap_logger_uid(int direction, const char * str, size_t size) 
168 {
169         gchar *buf;
170         gchar **lines;
171         int i = 0;
172
173         if (size > 8192) {
174                 log_print(LOG_PROTOCOL, "IMAP4%c [UID data - %zd bytes]\n", direction?'>':'<', size);
175                 return;
176         }
177         buf = malloc(size+1);
178         memset(buf, 0, size+1);
179         strncpy(buf, str, size);
180         buf[size] = '\0';
181         if (!strncmp(buf, "<<<<<<<", 7) 
182         ||  !strncmp(buf, ">>>>>>>", 7)) {
183                 free(buf);
184                 return;
185         }
186         while (strstr(buf, "\r"))
187                 *strstr(buf, "\r") = ' ';
188         while (strlen(buf) > 0 && buf[strlen(buf)-1] == '\n')
189                 buf[strlen(buf)-1] = '\0';
190
191         lines = g_strsplit(buf, "\n", -1);
192
193         while (lines[i] && *lines[i]) {
194                 int llen = strlen(lines[i]);
195                 if (llen < 64)
196                         log_print(LOG_PROTOCOL, "IMAP4%c %s\n", direction?'>':'<', lines[i]);
197                 else {
198                         gchar tmp[64];
199                         strncpy2(tmp, lines[i], 63);
200                         log_print(LOG_PROTOCOL, "IMAP4%c %s[... - %d bytes more]\n", direction?'>':'<', tmp,
201                                   llen-64);
202                 }
203                 i++;
204         }
205         g_strfreev(lines);
206         free(buf);
207 }
208
209 static void imap_logger_append(int direction, const char * str, size_t size) 
210 {
211         gchar *buf;
212         gchar **lines;
213         int i = 0;
214
215         if (size > 8192) {
216                 log_print(LOG_PROTOCOL, "IMAP4%c [APPEND data - %zd bytes]\n", direction?'>':'<', size);
217                 return;
218         } else if (direction == 0 && size > 64) {
219                 log_print(LOG_PROTOCOL, "IMAP4%c [APPEND data - %zd bytes]\n", direction?'>':'<', size);
220                 return;
221         } 
222         buf = malloc(size+1);
223         memset(buf, 0, size+1);
224         strncpy(buf, str, size);
225         buf[size] = '\0';
226         if (!strncmp(buf, "<<<<<<<", 7) 
227         ||  !strncmp(buf, ">>>>>>>", 7)) {
228                 free(buf);
229                 return;
230         }
231         while (strstr(buf, "\r"))
232                 *strstr(buf, "\r") = ' ';
233         while (strlen(buf) > 0 && buf[strlen(buf)-1] == '\n')
234                 buf[strlen(buf)-1] = '\0';
235
236         lines = g_strsplit(buf, "\n", -1);
237
238         if (direction == 0 || (buf[0] == '*' && buf[1] == ' ') || size < 64) {
239                 while (lines[i] && *lines[i]) {
240                         log_print(LOG_PROTOCOL, "IMAP4%c %s\n", direction?'>':'<', lines[i]);
241                         i++;
242                 }
243         } else {
244                 log_print(LOG_PROTOCOL, "IMAP4%c [data - %zd bytes]\n", direction?'>':'<', size);
245         }
246         g_strfreev(lines);
247         free(buf);
248 }
249
250 #define ETPAN_DEFAULT_NETWORK_TIMEOUT 60
251 gboolean etpan_skip_ssl_cert_check = FALSE;
252 extern void mailsasl_ref(void);
253
254 void imap_main_init(gboolean skip_ssl_cert_check)
255 {
256         int fd_thread_manager;
257         
258         etpan_skip_ssl_cert_check = skip_ssl_cert_check;
259         mailstream_network_delay.tv_sec = ETPAN_DEFAULT_NETWORK_TIMEOUT;
260         mailstream_network_delay.tv_usec = 0;
261         
262         mailstream_debug = 1;
263         mailstream_logger = imap_logger_cmd;
264         mailsasl_ref();
265         
266         imap_hash = chash_new(CHASH_COPYKEY, CHASH_DEFAULTSIZE);
267         session_hash = chash_new(CHASH_COPYKEY, CHASH_DEFAULTSIZE);
268         courier_workaround_hash = chash_new(CHASH_COPYKEY, CHASH_DEFAULTSIZE);
269         
270         thread_manager = etpan_thread_manager_new();
271         
272         fd_thread_manager = etpan_thread_manager_get_fd(thread_manager);
273         
274         io_channel = g_io_channel_unix_new(fd_thread_manager);
275         
276         thread_manager_signal = g_io_add_watch_full(io_channel, 0, G_IO_IN,
277                                                     thread_manager_event,
278                                                     (gpointer) NULL,
279                                                     NULL);
280 }
281
282 void imap_main_set_timeout(int sec)
283 {
284         mailstream_network_delay.tv_sec = sec;
285         mailstream_network_delay.tv_usec = 0;
286 }
287
288 void imap_main_done(void)
289 {
290         etpan_thread_manager_stop(thread_manager);
291 #if defined(__NetBSD__) || defined(__OpenBSD__) || defined(__FreeBSD__)
292         return;
293 #endif
294         etpan_thread_manager_join(thread_manager);
295         
296         g_source_remove(thread_manager_signal);
297         g_io_channel_unref(io_channel);
298         
299         etpan_thread_manager_free(thread_manager);
300         
301         chash_free(courier_workaround_hash);
302         chash_free(session_hash);
303         chash_free(imap_hash);
304 }
305
306 void imap_init(Folder * folder)
307 {
308         struct etpan_thread * thread;
309         chashdatum key;
310         chashdatum value;
311         
312         thread = etpan_thread_manager_get_thread(thread_manager);
313         
314         key.data = &folder;
315         key.len = sizeof(folder);
316         value.data = thread;
317         value.len = 0;
318         
319         chash_set(imap_hash, &key, &value, NULL);
320 }
321
322 void imap_done(Folder * folder)
323 {
324         struct etpan_thread * thread;
325         chashdatum key;
326         chashdatum value;
327         int r;
328         
329         key.data = &folder;
330         key.len = sizeof(folder);
331         
332         r = chash_get(imap_hash, &key, &value);
333         if (r < 0)
334                 return;
335         
336         thread = value.data;
337         
338         etpan_thread_unbind(thread);
339         
340         chash_delete(imap_hash, &key, NULL);
341         
342         debug_print("remove thread");
343 }
344
345 static struct etpan_thread * get_thread(Folder * folder)
346 {
347         struct etpan_thread * thread;
348         chashdatum key;
349         chashdatum value;
350         
351         key.data = &folder;
352         key.len = sizeof(folder);
353         
354         chash_get(imap_hash, &key, &value);
355         thread = value.data;
356         
357         return thread;
358 }
359
360 static mailimap * get_imap(Folder * folder)
361 {
362         mailimap * imap;
363         chashdatum key;
364         chashdatum value;
365         int r;
366         
367         key.data = &folder;
368         key.len = sizeof(folder);
369         
370         r = chash_get(session_hash, &key, &value);
371         if (r < 0)
372                 return NULL;
373         
374         imap = value.data;
375         debug_print("found imap %p\n", imap);
376         return imap;
377 }
378
379 static gboolean cb_show_error(gpointer data)
380 {
381         mainwindow_show_error();
382         return FALSE;
383 }
384
385 static void generic_cb(int cancelled, void * result, void * callback_data)
386 {
387         struct etpan_thread_op * op;
388         
389         op = (struct etpan_thread_op *) callback_data;
390
391         debug_print("generic_cb\n");
392         if (op->imap && op->imap->imap_response_info &&
393             op->imap->imap_response_info->rsp_alert) {
394                 log_error(LOG_PROTOCOL, "IMAP4< Alert: %s\n", 
395                         op->imap->imap_response_info->rsp_alert);
396                 g_timeout_add(10, cb_show_error, NULL);
397         } 
398         op->finished = 1;
399 }
400
401 static void threaded_run(Folder * folder, void * param, void * result,
402                          void (* func)(struct etpan_thread_op * ))
403 {
404         struct etpan_thread_op * op;
405         struct etpan_thread * thread;
406         
407         imap_folder_ref(folder);
408
409         op = etpan_thread_op_new();
410         
411         op->imap = get_imap(folder);
412         op->param = param;
413         op->result = result;
414         
415         op->cancellable = 0;
416         op->run = func;
417         op->callback = generic_cb;
418         op->callback_data = op;
419         op->cleanup = NULL;
420         
421         op->finished = 0;
422         
423         thread = get_thread(folder);
424         etpan_thread_op_schedule(thread, op);
425         
426         while (!op->finished) {
427                 gtk_main_iteration();
428         }
429         
430         etpan_thread_op_free(op);
431
432         imap_folder_unref(folder);
433 }
434
435
436 /* connect */
437
438 struct connect_param {
439         mailimap * imap;
440         const char * server;
441         int port;
442 };
443
444 struct connect_result {
445         int error;
446 };
447
448 #define CHECK_IMAP() {                                          \
449         if (!param->imap) {                                     \
450                 result->error = MAILIMAP_ERROR_BAD_STATE;       \
451                 return;                                         \
452         }                                                       \
453 }
454
455 static void connect_run(struct etpan_thread_op * op)
456 {
457         int r;
458         struct connect_param * param;
459         struct connect_result * result;
460         
461         param = op->param;
462         result = op->result;
463         
464         CHECK_IMAP();
465
466         r = mailimap_socket_connect(param->imap,
467                                     param->server, param->port);
468         
469         result->error = r;
470 }
471
472
473 int imap_threaded_connect(Folder * folder, const char * server, int port)
474 {
475         struct connect_param param;
476         struct connect_result result;
477         chashdatum key;
478         chashdatum value;
479         mailimap * imap, * oldimap;
480         
481         oldimap = get_imap(folder);
482
483         imap = mailimap_new(0, NULL);
484         
485         if (oldimap) {
486                 debug_print("deleting old imap %p\n", oldimap);
487                 delete_imap(folder, oldimap);
488         }
489         
490         key.data = &folder;
491         key.len = sizeof(folder);
492         value.data = imap;
493         value.len = 0;
494         chash_set(session_hash, &key, &value, NULL);
495         
496         param.imap = imap;
497         param.server = server;
498         param.port = port;
499         
500         refresh_resolvers();
501         threaded_run(folder, &param, &result, connect_run);
502         
503         debug_print("connect ok %i with imap %p\n", result.error, imap);
504         
505         return result.error;
506 }
507
508 static int etpan_certificate_check(const unsigned char *certificate, int len, void *data)
509 {
510 #ifdef USE_OPENSSL
511         struct connect_param *param = (struct connect_param *)data;
512         X509 *cert = NULL;
513         
514         if (certificate == NULL || len < 0) {
515                 g_warning("no cert presented.\n");
516                 return 0;
517         }
518         cert = d2i_X509(NULL, (const unsigned char **)&certificate, len);
519         if (cert == NULL) {
520                 g_warning("IMAP: can't get cert\n");
521                 return 0;
522         } else if (ssl_certificate_check(cert, NULL,
523                 (gchar *)param->server, (gushort)param->port) == TRUE) {
524                 X509_free(cert);
525                 return 0;
526         } else {
527                 X509_free(cert);
528                 return -1;
529         }
530 #elif USE_GNUTLS
531         struct connect_param *param = (struct connect_param *)data;
532         gnutls_x509_crt cert = NULL;
533         gnutls_datum tmp;
534         
535         if (certificate == NULL || len < 0) {
536                 g_warning("no cert presented.\n");
537                 return 0;
538         }
539         
540         tmp.data = malloc(len);
541         memcpy(tmp.data, certificate, len);
542         tmp.size = len;
543         gnutls_x509_crt_init(&cert);
544         if (gnutls_x509_crt_import(cert, &tmp, GNUTLS_X509_FMT_DER) < 0) {
545                 g_warning("IMAP: can't get cert\n");
546                 return 0;
547         } else if (ssl_certificate_check(cert, (guint)-1, NULL,
548                 (gchar *)param->server, (gushort)param->port) == TRUE) {
549                 gnutls_x509_crt_deinit(cert);
550                 return 0;
551         } else {
552                 gnutls_x509_crt_deinit(cert);
553                 return -1;
554         }
555 #endif
556         return 0;
557 }
558
559 static void connect_ssl_run(struct etpan_thread_op * op)
560 {
561         int r;
562         struct connect_param * param;
563         struct connect_result * result;
564         
565         param = op->param;
566         result = op->result;
567         
568         CHECK_IMAP();
569
570         r = mailimap_ssl_connect(param->imap,
571                                  param->server, param->port);
572         result->error = r;
573 }
574
575 int imap_threaded_connect_ssl(Folder * folder, const char * server, int port)
576 {
577         struct connect_param param;
578         struct connect_result result;
579         chashdatum key;
580         chashdatum value;
581         mailimap * imap, * oldimap;
582         unsigned char *certificate = NULL;
583         int cert_len;
584         
585         oldimap = get_imap(folder);
586
587         imap = mailimap_new(0, NULL);
588         
589         if (oldimap) {
590                 debug_print("deleting old imap %p\n", oldimap);
591                 delete_imap(folder, oldimap);
592         }
593
594         key.data = &folder;
595         key.len = sizeof(folder);
596         value.data = imap;
597         value.len = 0;
598         chash_set(session_hash, &key, &value, NULL);
599         
600         param.imap = imap;
601         param.server = server;
602         param.port = port;
603         
604         refresh_resolvers();
605         threaded_run(folder, &param, &result, connect_ssl_run);
606
607         if ((result.error == MAILIMAP_NO_ERROR_AUTHENTICATED ||
608              result.error == MAILIMAP_NO_ERROR_NON_AUTHENTICATED) && !etpan_skip_ssl_cert_check) {
609                 cert_len = (int)mailstream_ssl_get_certificate(imap->imap_stream, &certificate);
610                 if (etpan_certificate_check(certificate, cert_len, &param) < 0)
611                         return -1;
612                 if (certificate) 
613                         free(certificate); 
614         }
615         debug_print("connect %d with imap %p\n", result.error, imap);
616         
617         return result.error;
618 }
619
620 struct capa_param {
621         mailimap * imap;
622 };
623
624 struct capa_result {
625         int error;
626         struct mailimap_capability_data *caps;
627 };
628
629 static void capability_run(struct etpan_thread_op * op)
630 {
631         int r;
632         struct capa_param * param;
633         struct capa_result * result;
634         struct mailimap_capability_data *caps;
635
636         param = op->param;
637         result = op->result;
638         
639         CHECK_IMAP();
640
641         r = mailimap_capability(param->imap, &caps);
642         
643         result->error = r;
644         result->caps = (r == 0 ? caps : NULL);
645 }
646
647
648 struct mailimap_capability_data * imap_threaded_capability(Folder *folder, int *ok)
649 {
650         struct capa_param param;
651         struct capa_result result;
652         mailimap *imap;
653         
654         imap = get_imap(folder);
655         
656         param.imap = imap;
657         
658         threaded_run(folder, &param, &result, capability_run);
659         
660         debug_print("capa %d\n", result.error);
661         
662         if (ok)
663                 *ok = result.error;
664
665         return result.caps;
666         
667 }
668         
669 struct disconnect_param {
670         mailimap * imap;
671 };
672
673 struct disconnect_result {
674         int error;
675 };
676
677 static void disconnect_run(struct etpan_thread_op * op)
678 {
679         int r;
680         struct disconnect_param * param;
681         struct disconnect_result * result;
682         
683         param = op->param;
684         result = op->result;
685         
686         CHECK_IMAP();
687
688         r = mailimap_logout(param->imap);
689         
690         result->error = r;
691 }
692
693 void imap_threaded_disconnect(Folder * folder)
694 {
695         struct connect_param param;
696         struct connect_result result;
697         mailimap * imap;
698         
699         imap = get_imap(folder);
700         if (imap == NULL) {
701                 debug_print("was disconnected\n");
702                 return;
703         }
704         
705         param.imap = imap;
706         
707         threaded_run(folder, &param, &result, disconnect_run);
708         
709         if (imap == get_imap(folder)) {
710                 debug_print("deleting old imap %p\n", imap);
711                 delete_imap(folder, imap);
712         } else {
713                 debug_print("imap already deleted %p\n", imap);
714         }
715         
716         debug_print("disconnect ok\n");
717 }
718
719
720 struct list_param {
721         mailimap * imap;
722         const char * base;
723         const char * wildcard;
724         gboolean sub_only;
725 };
726
727 struct list_result {
728         int error;
729         clist * list;
730 };
731
732 static void list_run(struct etpan_thread_op * op)
733 {
734         struct list_param * param;
735         struct list_result * result;
736         int r;
737         clist * list;
738         
739         param = op->param;
740         result = op->result;
741
742         CHECK_IMAP();
743
744         list = NULL;
745         
746         if (param->base == NULL || param->wildcard == NULL) {
747                 result->list = list;
748                 result->error = -1;
749                 debug_print("no base or wildcard (%p %p)\n", param->base, param->wildcard);
750                 return;
751         }
752         if (param->sub_only)
753                 r = mailimap_lsub(param->imap, param->base,
754                           param->wildcard, &list);
755         else
756                 r = mailimap_list(param->imap, param->base,
757                           param->wildcard, &list);
758         result->error = r;
759         result->list = list;
760         debug_print("imap list run - end\n");
761 }
762
763 int imap_threaded_list(Folder * folder, const char * base,
764                        const char * wildcard,
765                        clist ** p_result)
766 {
767         struct list_param param;
768         struct list_result result;
769         
770         debug_print("imap list - begin\n");
771         
772         param.imap = get_imap(folder);
773         param.base = base;
774         param.wildcard = wildcard;
775         param.sub_only = FALSE;
776
777         threaded_run(folder, &param, &result, list_run);
778         
779         * p_result = result.list;
780         
781         debug_print("imap list - end %p\n", result.list);
782         
783         return result.error;
784 }
785
786 int imap_threaded_lsub(Folder * folder, const char * base,
787                        const char * wildcard,
788                        clist ** p_result)
789 {
790         struct list_param param;
791         struct list_result result;
792         
793         debug_print("imap lsub - begin\n");
794         
795         param.imap = get_imap(folder);
796         param.base = base;
797         param.wildcard = wildcard;
798         param.sub_only = TRUE;
799         
800         threaded_run(folder, &param, &result, list_run);
801         
802         * p_result = result.list;
803         
804         debug_print("imap lsub - end %p\n", result.list);
805         
806         return result.error;
807 }
808
809 struct subscribe_param {
810         mailimap * imap;
811         const char * mb;
812         gboolean subscribe;
813 };
814
815 struct subscribe_result {
816         int error;
817 };
818
819 static void subscribe_run(struct etpan_thread_op * op)
820 {
821         struct subscribe_param * param;
822         struct subscribe_result * result;
823         int r;
824         
825         param = op->param;
826         result = op->result;
827
828         CHECK_IMAP();
829
830         if (param->mb == NULL) {
831                 result->error = -1;
832                 debug_print("no mb\n");
833                 return;
834         }
835         if (param->subscribe)
836                 r = mailimap_subscribe(param->imap, param->mb);
837         else
838                 r = mailimap_unsubscribe(param->imap, param->mb);
839         result->error = r;
840         debug_print("imap %ssubscribe run - end %d\n", param->subscribe?"":"un", r);
841 }
842
843 int imap_threaded_subscribe(Folder * folder, const char * mb,
844                        gboolean subscribe)
845 {
846         struct subscribe_param param;
847         struct subscribe_result result;
848         
849         debug_print("imap list - begin\n");
850         
851         param.imap = get_imap(folder);
852         param.mb = mb;
853         param.subscribe = subscribe;
854
855         threaded_run(folder, &param, &result, subscribe_run);
856         
857         return result.error;
858 }
859
860 struct login_param {
861         mailimap * imap;
862         const char * login;
863         const char * password;
864         const char * type;
865         const char * server;
866 };
867
868 struct login_result {
869         int error;
870 };
871
872 static void login_run(struct etpan_thread_op * op)
873 {
874         struct login_param * param;
875         struct login_result * result;
876         int r;
877 #ifdef DISABLE_LOG_DURING_LOGIN
878         int old_debug;
879 #endif
880         
881         param = op->param;
882         result = op->result;
883
884         CHECK_IMAP();
885
886 #ifdef DISABLE_LOG_DURING_LOGIN
887         old_debug = mailstream_debug;
888         mailstream_debug = 0;
889 #endif
890         if (!strcmp(param->type, "LOGIN"))
891                 r = mailimap_login(param->imap,
892                            param->login, param->password);
893         else if (!strcmp(param->type, "GSSAPI"))
894                 r = mailimap_authenticate(param->imap,
895                         param->type, param->server, NULL, NULL,
896                         param->login, param->login,
897                         param->password, NULL);
898         else 
899                 r = mailimap_authenticate(param->imap,
900                         param->type, NULL, NULL, NULL,
901                         param->login, param->login,
902                         param->password, NULL);
903 #ifdef DISABLE_LOG_DURING_LOGIN
904         mailstream_debug = old_debug;
905 #endif
906         
907         result->error = r;
908         if (param->imap->imap_response)
909                 imap_logger_cmd(0, param->imap->imap_response, strlen(param->imap->imap_response));
910         debug_print("imap login run - end %i\n", r);
911 }
912
913 int imap_threaded_login(Folder * folder,
914                         const char * login, const char * password,
915                         const char * type)
916 {
917         struct login_param param;
918         struct login_result result;
919         
920         debug_print("imap login - begin\n");
921         
922         param.imap = get_imap(folder);
923         param.login = login;
924         param.password = password;
925         param.type = type;
926         if (folder && folder->account)
927                 param.server = folder->account->recv_server;
928         else
929                 param.server = NULL;
930
931         threaded_run(folder, &param, &result, login_run);
932         
933         debug_print("imap login - end\n");
934         
935         return result.error;
936 }
937
938
939 struct status_param {
940         mailimap * imap;
941         const char * mb;
942         struct mailimap_status_att_list * status_att_list;
943 };
944
945 struct status_result {
946         int error;
947         struct mailimap_mailbox_data_status * data_status;
948 };
949
950 static void status_run(struct etpan_thread_op * op)
951 {
952         struct status_param * param;
953         struct status_result * result;
954         int r;
955         
956         param = op->param;
957         result = op->result;
958         
959         CHECK_IMAP();
960
961         r = mailimap_status(param->imap, param->mb,
962                             param->status_att_list,
963                             &result->data_status);
964         
965         result->error = r;
966         debug_print("imap status run - end %i\n", r);
967 }
968
969 int imap_threaded_status(Folder * folder, const char * mb,
970                          struct mailimap_mailbox_data_status ** data_status,
971                          guint mask)
972 {
973         struct status_param param;
974         struct status_result result;
975         struct mailimap_status_att_list * status_att_list;
976         
977         debug_print("imap status - begin\n");
978         
979         status_att_list = mailimap_status_att_list_new_empty();
980         if (mask & 1 << 0) {
981                 mailimap_status_att_list_add(status_att_list,
982                                      MAILIMAP_STATUS_ATT_MESSAGES);
983         }
984         if (mask & 1 << 1) {
985                 mailimap_status_att_list_add(status_att_list,
986                                      MAILIMAP_STATUS_ATT_RECENT);
987         }
988         if (mask & 1 << 2) {
989                 mailimap_status_att_list_add(status_att_list,
990                                      MAILIMAP_STATUS_ATT_UIDNEXT);
991         }
992         if (mask & 1 << 3) {
993                 mailimap_status_att_list_add(status_att_list,
994                                      MAILIMAP_STATUS_ATT_UIDVALIDITY);
995         }
996         if (mask & 1 << 4) {
997                 mailimap_status_att_list_add(status_att_list,
998                                      MAILIMAP_STATUS_ATT_UNSEEN);
999         }
1000         param.imap = get_imap(folder);
1001         param.mb = mb;
1002         param.status_att_list = status_att_list;
1003         
1004         threaded_run(folder, &param, &result, status_run);
1005         
1006         debug_print("imap status - end\n");
1007         
1008         * data_status = result.data_status;
1009         
1010         mailimap_status_att_list_free(status_att_list);
1011         
1012         return result.error;
1013 }
1014
1015
1016
1017 struct noop_param {
1018         mailimap * imap;
1019 };
1020
1021 struct noop_result {
1022         int error;
1023 };
1024
1025 static void noop_run(struct etpan_thread_op * op)
1026 {
1027         struct noop_param * param;
1028         struct noop_result * result;
1029         int r;
1030         
1031         param = op->param;
1032         result = op->result;
1033
1034         CHECK_IMAP();
1035
1036         r = mailimap_noop(param->imap);
1037         
1038         result->error = r;
1039         debug_print("imap noop run - end %i\n", r);
1040 }
1041
1042 int imap_threaded_noop(Folder * folder, unsigned int * p_exists, 
1043                        unsigned int *p_recent, 
1044                        unsigned int *p_expunge,
1045                        unsigned int *p_unseen,
1046                        unsigned int *p_uidnext,
1047                        unsigned int *p_uidval)
1048 {
1049         struct noop_param param;
1050         struct noop_result result;
1051         mailimap * imap;
1052         
1053         debug_print("imap noop - begin\n");
1054         
1055         imap = get_imap(folder);
1056         param.imap = imap;
1057
1058         threaded_run(folder, &param, &result, noop_run);
1059         
1060         if (result.error == 0 && imap && imap->imap_selection_info != NULL) {
1061                 * p_exists = imap->imap_selection_info->sel_exists;
1062                 * p_recent = imap->imap_selection_info->sel_recent;
1063                 * p_unseen = imap->imap_selection_info->sel_unseen;
1064                 * p_uidnext = imap->imap_selection_info->sel_uidnext;
1065                 * p_uidval = imap->imap_selection_info->sel_uidvalidity;
1066         } else {
1067                 * p_exists = 0;
1068                 * p_recent = 0;
1069                 * p_unseen = 0;
1070                 * p_uidnext = 0;
1071                 * p_uidval = 0;
1072         }
1073         if (result.error == 0 && imap && imap->imap_response_info != NULL &&
1074             imap->imap_response_info->rsp_expunged != NULL) {
1075                 * p_expunge = clist_count(imap->imap_response_info->rsp_expunged);
1076         } else {
1077                 * p_expunge = 0;
1078         }       
1079         debug_print("imap noop - end [EXISTS %d RECENT %d EXPUNGE %d UNSEEN %d UIDNEXT %d UIDVAL %d]\n",
1080                 *p_exists, *p_recent, *p_expunge, *p_unseen,
1081                 *p_uidnext, *p_uidval);
1082         
1083         return result.error;
1084 }
1085
1086
1087 struct starttls_result {
1088         int error;
1089 };
1090
1091 static void starttls_run(struct etpan_thread_op * op)
1092 {
1093         struct connect_param * param;
1094         struct starttls_result * result;
1095         int r;
1096
1097         param = op->param;
1098         result = op->result;
1099
1100         CHECK_IMAP();
1101
1102         r = mailimap_starttls(param->imap);
1103         
1104         result->error = r;
1105         debug_print("imap starttls run - end %i\n", r);
1106         
1107         if (r == 0) {
1108                 mailimap *imap = param->imap;
1109                 mailstream_low *plain_low = NULL;
1110                 mailstream_low *tls_low = NULL;
1111                 int fd = -1;
1112                 
1113                 plain_low = mailstream_get_low(imap->imap_stream);
1114                 fd = mailstream_low_get_fd(plain_low);
1115                 if (fd == -1) {
1116                         debug_print("imap starttls run - can't get fd\n");
1117                         result->error = MAILIMAP_ERROR_STREAM;
1118                         return;
1119                 }
1120
1121                 tls_low = mailstream_low_tls_open(fd);
1122                 if (tls_low == NULL) {
1123                         debug_print("imap starttls run - can't tls_open\n");
1124                         result->error = MAILIMAP_ERROR_STREAM;
1125                         return;
1126                 }
1127                 mailstream_low_free(plain_low);
1128                 mailstream_set_low(imap->imap_stream, tls_low);
1129         }
1130 }
1131
1132 int imap_threaded_starttls(Folder * folder, const gchar *host, int port)
1133 {
1134         struct connect_param param;
1135         struct starttls_result result;
1136         int cert_len;
1137         unsigned char *certificate;
1138         
1139         debug_print("imap starttls - begin\n");
1140         
1141         param.imap = get_imap(folder);
1142         param.server = host;
1143         param.port = port;
1144         
1145         threaded_run(folder, &param, &result, starttls_run);
1146         
1147         debug_print("imap starttls - end\n");
1148
1149         if (result.error == 0 && param.imap && !etpan_skip_ssl_cert_check) {
1150                 cert_len = (int)mailstream_ssl_get_certificate(param.imap->imap_stream, &certificate);
1151                 if (etpan_certificate_check(certificate, cert_len, &param) < 0)
1152                         result.error = MAILIMAP_ERROR_STREAM;
1153                 if (certificate) 
1154                         free(certificate); 
1155         }       
1156         return result.error;
1157 }
1158
1159
1160
1161 struct create_param {
1162         mailimap * imap;
1163         const char * mb;
1164 };
1165
1166 struct create_result {
1167         int error;
1168 };
1169
1170 static void create_run(struct etpan_thread_op * op)
1171 {
1172         struct create_param * param;
1173         struct create_result * result;
1174         int r;
1175         
1176         param = op->param;
1177         result = op->result;
1178
1179         CHECK_IMAP();
1180
1181         r = mailimap_create(param->imap, param->mb);
1182         
1183         result->error = r;
1184         debug_print("imap create run - end %i\n", r);
1185 }
1186
1187 int imap_threaded_create(Folder * folder, const char * mb)
1188 {
1189         struct create_param param;
1190         struct create_result result;
1191         
1192         debug_print("imap create - begin\n");
1193         
1194         param.imap = get_imap(folder);
1195         param.mb = mb;
1196         
1197         threaded_run(folder, &param, &result, create_run);
1198         
1199         debug_print("imap create - end\n");
1200         
1201         return result.error;
1202 }
1203
1204
1205
1206
1207 struct rename_param {
1208         mailimap * imap;
1209         const char * mb;
1210         const char * new_name;
1211 };
1212
1213 struct rename_result {
1214         int error;
1215 };
1216
1217 static void rename_run(struct etpan_thread_op * op)
1218 {
1219         struct rename_param * param;
1220         struct rename_result * result;
1221         int r;
1222         
1223         param = op->param;
1224         result = op->result;
1225
1226         CHECK_IMAP();
1227
1228         r = mailimap_rename(param->imap, param->mb, param->new_name);
1229         
1230         result->error = r;
1231         debug_print("imap rename run - end %i\n", r);
1232 }
1233
1234 int imap_threaded_rename(Folder * folder,
1235                          const char * mb, const char * new_name)
1236 {
1237         struct rename_param param;
1238         struct rename_result result;
1239         
1240         debug_print("imap rename - begin\n");
1241         
1242         param.imap = get_imap(folder);
1243         param.mb = mb;
1244         param.new_name = new_name;
1245         
1246         threaded_run(folder, &param, &result, rename_run);
1247         
1248         debug_print("imap rename - end\n");
1249         
1250         return result.error;
1251 }
1252
1253
1254
1255
1256 struct delete_param {
1257         mailimap * imap;
1258         const char * mb;
1259 };
1260
1261 struct delete_result {
1262         int error;
1263 };
1264
1265 static void delete_run(struct etpan_thread_op * op)
1266 {
1267         struct delete_param * param;
1268         struct delete_result * result;
1269         int r;
1270         
1271         param = op->param;
1272         result = op->result;
1273
1274         CHECK_IMAP();
1275
1276         r = mailimap_delete(param->imap, param->mb);
1277         
1278         result->error = r;
1279         debug_print("imap delete run - end %i\n", r);
1280 }
1281
1282 int imap_threaded_delete(Folder * folder, const char * mb)
1283 {
1284         struct delete_param param;
1285         struct delete_result result;
1286         
1287         debug_print("imap delete - begin\n");
1288         
1289         param.imap = get_imap(folder);
1290         param.mb = mb;
1291         
1292         threaded_run(folder, &param, &result, delete_run);
1293         
1294         debug_print("imap delete - end\n");
1295         
1296         return result.error;
1297 }
1298
1299
1300
1301 struct select_param {
1302         mailimap * imap;
1303         const char * mb;
1304 };
1305
1306 struct select_result {
1307         int error;
1308 };
1309
1310 static void select_run(struct etpan_thread_op * op)
1311 {
1312         struct select_param * param;
1313         struct select_result * result;
1314         int r;
1315         
1316         param = op->param;
1317         result = op->result;
1318
1319         CHECK_IMAP();
1320
1321         r = mailimap_select(param->imap, param->mb);
1322         
1323         result->error = r;
1324         debug_print("imap select run - end %i\n", r);
1325 }
1326
1327 int imap_threaded_select(Folder * folder, const char * mb,
1328                          gint * exists, gint * recent, gint * unseen,
1329                          guint32 * uid_validity,gint *can_create_flags,
1330                          GSList **ok_flags)
1331 {
1332         struct select_param param;
1333         struct select_result result;
1334         mailimap * imap;
1335
1336         debug_print("imap select - begin\n");
1337         
1338         imap = get_imap(folder);
1339         param.imap = imap;
1340         param.mb = mb;
1341         
1342         threaded_run(folder, &param, &result, select_run);
1343         
1344         if (result.error != MAILIMAP_NO_ERROR)
1345                 return result.error;
1346         
1347         if (!imap || imap->imap_selection_info == NULL)
1348                 return MAILIMAP_ERROR_PARSE;
1349         
1350         * exists = imap->imap_selection_info->sel_exists;
1351         * recent = imap->imap_selection_info->sel_recent;
1352         * unseen = imap->imap_selection_info->sel_unseen;
1353         * uid_validity = imap->imap_selection_info->sel_uidvalidity;
1354         * can_create_flags = FALSE;
1355
1356         if (imap->imap_selection_info->sel_perm_flags) {
1357                 GSList *t_flags = NULL;
1358                 clistiter *cur =
1359                         clist_begin(imap->imap_selection_info->sel_perm_flags);
1360                 for (; cur; cur = clist_next(cur)) {
1361                         struct mailimap_flag_perm *flag = (struct mailimap_flag_perm *)clist_content(cur);
1362                         if (flag->fl_type == MAILIMAP_FLAG_PERM_ALL)
1363                                 *can_create_flags = TRUE;
1364                         else if (flag->fl_flag && 
1365                                         flag->fl_flag->fl_type == 6 &&
1366                                         !strcmp(flag->fl_flag->fl_data.fl_extension, "*"))
1367                                 *can_create_flags = TRUE; 
1368                         if (ok_flags) {
1369                                 MsgPermFlags c_flag = 0;
1370                                 switch (flag->fl_flag->fl_type) {
1371                                 case MAILIMAP_FLAG_ANSWERED:
1372                                         c_flag = IMAP_FLAG_ANSWERED;
1373                                         break;
1374                                 case MAILIMAP_FLAG_FLAGGED:
1375                                         c_flag = IMAP_FLAG_FLAGGED;
1376                                         break;
1377                                 case MAILIMAP_FLAG_DELETED:
1378                                         c_flag = IMAP_FLAG_DELETED;
1379                                         break;
1380                                 case MAILIMAP_FLAG_DRAFT:
1381                                         c_flag = IMAP_FLAG_DRAFT;
1382                                         break;
1383                                 case MAILIMAP_FLAG_SEEN:
1384                                         c_flag = IMAP_FLAG_SEEN;
1385                                         break;
1386                                 case MAILIMAP_FLAG_KEYWORD:
1387                                         if (!strcasecmp(flag->fl_flag->fl_data.fl_keyword, "$Forwarded"))
1388                                                 c_flag = IMAP_FLAG_FORWARDED;
1389                                         break;
1390                                 default:
1391                                         break;
1392                                 }
1393                                 if (c_flag != 0) {
1394                                         t_flags = g_slist_prepend(t_flags, 
1395                                                 GUINT_TO_POINTER(c_flag));
1396                                 }
1397                         }
1398                 }
1399                 if (ok_flags)
1400                         *ok_flags = t_flags;
1401         }
1402         debug_print("imap select - end\n");
1403         
1404         return result.error;
1405 }
1406
1407 static void close_run(struct etpan_thread_op * op)
1408 {
1409         struct select_param * param;
1410         struct select_result * result;
1411         int r;
1412         
1413         param = op->param;
1414         result = op->result;
1415
1416         CHECK_IMAP();
1417
1418         r = mailimap_close(param->imap);
1419         
1420         result->error = r;
1421         debug_print("imap close run - end %i\n", r);
1422 }
1423
1424 int imap_threaded_close(Folder * folder)
1425 {
1426         struct select_param param;
1427         struct select_result result;
1428         mailimap * imap;
1429         
1430         debug_print("imap close - begin\n");
1431         
1432         imap = get_imap(folder);
1433         param.imap = imap;
1434         
1435         threaded_run(folder, &param, &result, close_run);
1436         
1437         if (result.error != MAILIMAP_NO_ERROR)
1438                 return result.error;
1439         
1440         debug_print("imap close - end\n");
1441         
1442         return result.error;
1443 }
1444
1445 struct examine_param {
1446         mailimap * imap;
1447         const char * mb;
1448 };
1449
1450 struct examine_result {
1451         int error;
1452 };
1453
1454 static void examine_run(struct etpan_thread_op * op)
1455 {
1456         struct examine_param * param;
1457         struct examine_result * result;
1458         int r;
1459         
1460         param = op->param;
1461         result = op->result;
1462
1463         CHECK_IMAP();
1464
1465         r = mailimap_examine(param->imap, param->mb);
1466         
1467         result->error = r;
1468         debug_print("imap examine run - end %i\n", r);
1469 }
1470
1471 int imap_threaded_examine(Folder * folder, const char * mb,
1472                           gint * exists, gint * recent, gint * unseen,
1473                           guint32 * uid_validity)
1474 {
1475         struct examine_param param;
1476         struct examine_result result;
1477         mailimap * imap;
1478         
1479         debug_print("imap examine - begin\n");
1480         
1481         imap = get_imap(folder);
1482         param.imap = imap;
1483         param.mb = mb;
1484         
1485         threaded_run(folder, &param, &result, examine_run);
1486         
1487         if (result.error != MAILIMAP_NO_ERROR)
1488                 return result.error;
1489         
1490         if (!imap || imap->imap_selection_info == NULL)
1491                 return MAILIMAP_ERROR_PARSE;
1492         
1493         * exists = imap->imap_selection_info->sel_exists;
1494         * recent = imap->imap_selection_info->sel_recent;
1495         * unseen = imap->imap_selection_info->sel_unseen;
1496         * uid_validity = imap->imap_selection_info->sel_uidvalidity;
1497         
1498         debug_print("imap examine - end\n");
1499         
1500         return result.error;
1501 }
1502
1503
1504
1505
1506 struct search_param {
1507         mailimap * imap;
1508         int type;
1509         struct mailimap_set * set;
1510 };
1511
1512 struct search_result {
1513         int error;
1514         clist * search_result;
1515 };
1516
1517 static struct mailimap_set_item *sc_mailimap_set_item_copy(struct mailimap_set_item *orig)
1518 {
1519         return mailimap_set_item_new(orig->set_first, orig->set_last);
1520 }
1521
1522 static struct mailimap_set *sc_mailimap_set_copy(struct mailimap_set *orig)
1523 {
1524         clist *list = orig ? orig->set_list : NULL;
1525         clist *newlist = clist_new();
1526         clistiter *cur;
1527         
1528         if (!orig)
1529                 return NULL;
1530         for (cur = clist_begin(list); cur; cur = clist_next(cur))
1531                 clist_append(newlist, 
1532                         sc_mailimap_set_item_copy(
1533                         (struct mailimap_set_item *)clist_content(cur)));
1534         return mailimap_set_new(newlist);
1535 }
1536
1537 static void search_run(struct etpan_thread_op * op)
1538 {
1539         struct search_param * param;
1540         struct search_result * result;
1541         int r;
1542         struct mailimap_search_key * key = NULL;
1543         struct mailimap_search_key * uid_key = NULL;
1544         struct mailimap_search_key * search_type_key;
1545         clist * search_result;
1546         
1547         param = op->param;
1548         result = op->result;
1549
1550         CHECK_IMAP();
1551
1552         /* we copy the mailimap_set because freeing the key is recursive */
1553         if (param->set != NULL) {
1554                 uid_key = mailimap_search_key_new_uid(sc_mailimap_set_copy(param->set));
1555         } else if (param->type == IMAP_SEARCH_TYPE_SIMPLE) {
1556                 uid_key = mailimap_search_key_new_all();
1557         }
1558         search_type_key = NULL;
1559         switch (param->type) {
1560         case IMAP_SEARCH_TYPE_SIMPLE:
1561                 search_type_key = NULL;
1562                 break;
1563                 
1564         case IMAP_SEARCH_TYPE_SEEN:
1565                 search_type_key = mailimap_search_key_new(MAILIMAP_SEARCH_KEY_SEEN,
1566                                                           NULL, NULL, NULL, NULL, NULL,
1567                                                           NULL, NULL, NULL, NULL, NULL,
1568                                                           NULL, NULL, NULL, NULL, 0,
1569                                                           NULL, NULL, NULL, NULL, NULL,
1570                                                           NULL, 0, NULL, NULL, NULL);
1571                 break;
1572                 
1573         case IMAP_SEARCH_TYPE_UNSEEN:
1574                 search_type_key = mailimap_search_key_new(MAILIMAP_SEARCH_KEY_UNSEEN,
1575                                                           NULL, NULL, NULL, NULL, NULL,
1576                                                           NULL, NULL, NULL, NULL, NULL,
1577                                                           NULL, NULL, NULL, NULL, 0,
1578                                                           NULL, NULL, NULL, NULL, NULL,
1579                                                           NULL, 0, NULL, NULL, NULL);
1580                 break;
1581                 
1582         case IMAP_SEARCH_TYPE_ANSWERED:
1583                 search_type_key = mailimap_search_key_new(MAILIMAP_SEARCH_KEY_ANSWERED,
1584                                                           NULL, NULL, NULL, NULL, NULL,
1585                                                           NULL, NULL, NULL, NULL, NULL,
1586                                                           NULL, NULL, NULL, NULL, 0,
1587                                                           NULL, NULL, NULL, NULL, NULL,
1588                                                           NULL, 0, NULL, NULL, NULL);
1589                 break;
1590                 
1591         case IMAP_SEARCH_TYPE_FLAGGED:
1592                 search_type_key = mailimap_search_key_new(MAILIMAP_SEARCH_KEY_FLAGGED,
1593                                                           NULL, NULL, NULL, NULL, NULL,
1594                                                           NULL, NULL, NULL, NULL, NULL,
1595                                                           NULL, NULL, NULL, NULL, 0,
1596                                                           NULL, NULL, NULL, NULL, NULL,
1597                                                           NULL, 0, NULL, NULL, NULL);
1598                 break;
1599         case IMAP_SEARCH_TYPE_DELETED:
1600                 search_type_key = mailimap_search_key_new(MAILIMAP_SEARCH_KEY_DELETED,
1601                                                           NULL, NULL, NULL, NULL, NULL,
1602                                                           NULL, NULL, NULL, NULL, NULL,
1603                                                           NULL, NULL, NULL, NULL, 0,
1604                                                           NULL, NULL, NULL, NULL, NULL,
1605                                                           NULL, 0, NULL, NULL, NULL);
1606                 break;
1607         case IMAP_SEARCH_TYPE_FORWARDED:
1608                 search_type_key = mailimap_search_key_new(MAILIMAP_SEARCH_KEY_KEYWORD,
1609                                                           NULL, NULL, NULL, NULL, NULL,
1610                                                           strdup("$Forwarded"), NULL, NULL, NULL, NULL,
1611                                                           NULL, NULL, NULL, NULL, 0,
1612                                                           NULL, NULL, NULL, NULL, NULL,
1613                                                           NULL, 0, NULL, NULL, NULL);
1614                 break;
1615         }
1616         
1617         if (search_type_key != NULL) {
1618                 if (param->set != NULL) {
1619                         key = mailimap_search_key_new_multiple_empty();
1620                         mailimap_search_key_multiple_add(key, search_type_key);
1621                         mailimap_search_key_multiple_add(key, uid_key);
1622                 } else {
1623                         key = search_type_key;
1624                 }
1625         } else if (uid_key != NULL) {
1626                 key = uid_key;
1627         }
1628         
1629         if (key == NULL) {
1630                 g_warning("no key!");
1631                 result = op->result;
1632                 result->error = -1;
1633                 result->search_result = NULL;
1634         } else {
1635                 mailstream_logger = imap_logger_uid;
1636
1637                 r = mailimap_uid_search(param->imap, NULL, key, &search_result);
1638
1639                 mailstream_logger = imap_logger_cmd;
1640
1641                 /* free the key (with the imapset) */
1642                 mailimap_search_key_free(key);
1643
1644                 result->error = r;
1645                 result->search_result = search_result;
1646         }
1647         debug_print("imap search run - end %i\n", result->error);
1648 }
1649
1650 int imap_threaded_search(Folder * folder, int search_type,
1651                          struct mailimap_set * set, clist ** search_result)
1652 {
1653         struct search_param param;
1654         struct search_result result;
1655         mailimap * imap;
1656         
1657         debug_print("imap search - begin\n");
1658
1659         imap = get_imap(folder);
1660         param.imap = imap;
1661         param.set = set;
1662         param.type = search_type;
1663         
1664         threaded_run(folder, &param, &result, search_run);
1665         
1666         if (result.error != MAILIMAP_NO_ERROR)
1667                 return result.error;
1668         
1669         debug_print("imap search - end\n");
1670         
1671         * search_result = result.search_result;
1672         
1673         return result.error;
1674 }
1675
1676
1677
1678 static int imap_get_msg_att_info(struct mailimap_msg_att * msg_att,
1679                                  uint32_t * puid,
1680                                  char ** pheaders,
1681                                  size_t * pref_size,
1682                                  struct mailimap_msg_att_dynamic ** patt_dyn);
1683
1684 static int
1685 result_to_uid_list(clist * fetch_result, carray ** result)
1686 {
1687         clistiter * cur;
1688         int r;
1689         int res;
1690         carray * tab;
1691         
1692         tab = carray_new(128);
1693         if (tab == NULL) {
1694                 res = MAILIMAP_ERROR_MEMORY;
1695                 goto err;
1696         }
1697         
1698         for(cur = clist_begin(fetch_result) ; cur != NULL ;
1699             cur = clist_next(cur)) {
1700                 struct mailimap_msg_att * msg_att;
1701                 uint32_t uid;
1702                 uint32_t * puid;
1703                 
1704                 msg_att = clist_content(cur);
1705                 
1706                 uid = 0;
1707                 imap_get_msg_att_info(msg_att, &uid, NULL, NULL, NULL);
1708                 
1709                 puid = malloc(sizeof(* puid));
1710                 if (puid == NULL) {
1711                         res = MAILIMAP_ERROR_MEMORY;
1712                         goto free_list;
1713                 }
1714                 * puid = uid;
1715                         
1716                 r = carray_add(tab, puid, NULL);
1717                 if (r < 0) {
1718                         free(puid);
1719                         res = MAILIMAP_ERROR_MEMORY;
1720                         goto free_list;
1721                 }
1722         }
1723                 
1724         * result = tab;
1725
1726         return MAILIMAP_NO_ERROR;
1727   
1728  free_list:
1729         imap_fetch_uid_list_free(tab);
1730  err:
1731         return res;
1732 }
1733
1734 static int imap_get_messages_list(mailimap * imap,
1735                                   uint32_t first_index,
1736                                   carray ** result)
1737 {
1738         carray * env_list;
1739         int r;
1740         struct mailimap_fetch_att * fetch_att;
1741         struct mailimap_fetch_type * fetch_type;
1742         struct mailimap_set * set;
1743         clist * fetch_result;
1744         int res;
1745         
1746         set = mailimap_set_new_interval(first_index, 0);
1747         if (set == NULL) {
1748                 res = MAILIMAP_ERROR_MEMORY;
1749                 goto err;
1750         }
1751
1752         fetch_type = mailimap_fetch_type_new_fetch_att_list_empty();
1753         if (fetch_type == NULL) {
1754                 res = MAILIMAP_ERROR_MEMORY;
1755                 goto free_set;
1756         }
1757
1758         fetch_att = mailimap_fetch_att_new_uid();
1759         if (fetch_att == NULL) {
1760                 res = MAILIMAP_ERROR_MEMORY;
1761                 goto free_fetch_type;
1762         }
1763
1764         r = mailimap_fetch_type_new_fetch_att_list_add(fetch_type, fetch_att);
1765         if (r != MAILIMAP_NO_ERROR) {
1766                 mailimap_fetch_att_free(fetch_att);
1767                 res = MAILIMAP_ERROR_MEMORY;
1768                 goto free_fetch_type;
1769         }
1770
1771         mailstream_logger = imap_logger_fetch;
1772         
1773         r = mailimap_uid_fetch(imap, set,
1774                                fetch_type, &fetch_result);
1775
1776         mailstream_logger = imap_logger_cmd;
1777         mailimap_fetch_type_free(fetch_type);
1778         mailimap_set_free(set);
1779
1780         if (r != MAILIMAP_NO_ERROR) {
1781                 res = r;
1782                 goto err;
1783         }
1784
1785         env_list = NULL;
1786         r = result_to_uid_list(fetch_result, &env_list);
1787         mailimap_fetch_list_free(fetch_result);
1788         
1789         * result = env_list;
1790
1791         return MAILIMAP_NO_ERROR;
1792
1793  free_fetch_type:
1794         mailimap_fetch_type_free(fetch_type);
1795  free_set:
1796         mailimap_set_free(set);
1797  err:
1798         return res;
1799 }
1800
1801
1802
1803
1804 struct fetch_uid_param {
1805         mailimap * imap;
1806         uint32_t first_index;
1807 };
1808
1809 struct fetch_uid_result {
1810         int error;
1811         carray * fetch_result;
1812 };
1813
1814 static void fetch_uid_run(struct etpan_thread_op * op)
1815 {
1816         struct fetch_uid_param * param;
1817         struct fetch_uid_result * result;
1818         carray * fetch_result;
1819         int r;
1820         
1821         param = op->param;
1822         result = op->result;
1823
1824         CHECK_IMAP();
1825
1826         fetch_result = NULL;
1827         mailstream_logger = imap_logger_noop;
1828         log_print(LOG_PROTOCOL, "IMAP4- [fetching UIDs...]\n");
1829
1830         r = imap_get_messages_list(param->imap, param->first_index,
1831                                    &fetch_result);
1832         
1833         mailstream_logger = imap_logger_cmd;
1834
1835         result->error = r;
1836         result->fetch_result = fetch_result;
1837         debug_print("imap fetch_uid run - end %i\n", r);
1838 }
1839
1840 int imap_threaded_fetch_uid(Folder * folder, uint32_t first_index,
1841                             carray ** fetch_result)
1842 {
1843         struct fetch_uid_param param;
1844         struct fetch_uid_result result;
1845         mailimap * imap;
1846         
1847         debug_print("imap fetch_uid - begin\n");
1848         
1849         imap = get_imap(folder);
1850         param.imap = imap;
1851         param.first_index = first_index;
1852         
1853         threaded_run(folder, &param, &result, fetch_uid_run);
1854         
1855         if (result.error != MAILIMAP_NO_ERROR)
1856                 return result.error;
1857         
1858         debug_print("imap fetch_uid - end\n");
1859         
1860         * fetch_result = result.fetch_result;
1861         
1862         return result.error;
1863 }
1864
1865
1866 void imap_fetch_uid_list_free(carray * uid_list)
1867 {
1868         unsigned int i;
1869         
1870         for(i = 0 ; i < carray_count(uid_list) ; i ++) {
1871                 uint32_t * puid;
1872                 
1873                 puid = carray_get(uid_list, i);
1874                 free(puid);
1875         }
1876         carray_free(uid_list);
1877 }
1878
1879
1880
1881
1882 static int imap_flags_to_flags(struct mailimap_msg_att_dynamic * att_dyn, GSList **tags);
1883
1884 static int
1885 result_to_uid_flags_list(clist * fetch_result, carray ** result)
1886 {
1887         clistiter * cur;
1888         int r;
1889         int res;
1890         carray * tab;
1891         GSList *tags = NULL;
1892
1893         tab = carray_new(128);
1894         if (tab == NULL) {
1895                 res = MAILIMAP_ERROR_MEMORY;
1896                 goto err;
1897         }
1898         
1899         for(cur = clist_begin(fetch_result) ; cur != NULL ;
1900             cur = clist_next(cur)) {
1901                 struct mailimap_msg_att * msg_att;
1902                 uint32_t uid;
1903                 uint32_t * puid;
1904                 struct mailimap_msg_att_dynamic * att_dyn;
1905                 int flags;
1906                 int * pflags;
1907                 
1908                 tags = NULL;
1909
1910                 msg_att = clist_content(cur);
1911                 
1912                 uid = 0;
1913                 att_dyn = NULL;
1914                 imap_get_msg_att_info(msg_att, &uid, NULL, NULL, &att_dyn);
1915                 if (uid == 0)
1916                         continue;
1917                 if (att_dyn == NULL)
1918                         continue;
1919                 
1920                 flags = imap_flags_to_flags(att_dyn, &tags);
1921                 
1922                 puid = malloc(sizeof(* puid));
1923                 if (puid == NULL) {
1924                         res = MAILIMAP_ERROR_MEMORY;
1925                         goto free_list;
1926                 }
1927                 * puid = uid;
1928                 
1929                 r = carray_add(tab, puid, NULL);
1930                 if (r < 0) {
1931                         free(puid);
1932                         res = MAILIMAP_ERROR_MEMORY;
1933                         goto free_list;
1934                 }
1935                 pflags = malloc(sizeof(* pflags));
1936                 if (pflags == NULL) {
1937                         res = MAILIMAP_ERROR_MEMORY;
1938                         goto free_list;
1939                 }
1940                 * pflags = flags;
1941                 r = carray_add(tab, pflags, NULL);
1942                 if (r < 0) {
1943                         free(pflags);
1944                         res = MAILIMAP_ERROR_MEMORY;
1945                         goto free_list;
1946                 }
1947                 r = carray_add(tab, tags, NULL);
1948                 if (r < 0) {
1949                         free(pflags);
1950                         res = MAILIMAP_ERROR_MEMORY;
1951                         goto free_list;
1952                 }
1953         }
1954                 
1955         * result = tab;
1956
1957         return MAILIMAP_NO_ERROR;
1958   
1959  free_list:
1960         imap_fetch_uid_flags_list_free(tab);
1961         slist_free_strings(tags);
1962         g_slist_free(tags);
1963  err:
1964         return res;
1965 }
1966
1967 static int imap_get_messages_flags_list(mailimap * imap,
1968                                         uint32_t first_index,
1969                                         carray ** result)
1970 {
1971         carray * env_list;
1972         int r;
1973         struct mailimap_fetch_att * fetch_att;
1974         struct mailimap_fetch_type * fetch_type;
1975         struct mailimap_set * set;
1976         clist * fetch_result;
1977         int res;
1978         
1979         set = mailimap_set_new_interval(first_index, 0);
1980         if (set == NULL) {
1981                 res = MAILIMAP_ERROR_MEMORY;
1982                 goto err;
1983         }
1984
1985         fetch_type = mailimap_fetch_type_new_fetch_att_list_empty();
1986         if (fetch_type == NULL) {
1987                 res = MAILIMAP_ERROR_MEMORY;
1988                 goto free_set;
1989         }
1990
1991         fetch_att = mailimap_fetch_att_new_flags();
1992         if (fetch_att == NULL) {
1993                 res = MAILIMAP_ERROR_MEMORY;
1994                 goto free_fetch_type;
1995         }
1996         
1997         r = mailimap_fetch_type_new_fetch_att_list_add(fetch_type, fetch_att);
1998         if (r != MAILIMAP_NO_ERROR) {
1999                 mailimap_fetch_att_free(fetch_att);
2000                 res = MAILIMAP_ERROR_MEMORY;
2001                 goto free_fetch_type;
2002         }
2003         
2004         fetch_att = mailimap_fetch_att_new_uid();
2005         if (fetch_att == NULL) {
2006                 res = MAILIMAP_ERROR_MEMORY;
2007                 goto free_fetch_type;
2008         }
2009
2010         r = mailimap_fetch_type_new_fetch_att_list_add(fetch_type, fetch_att);
2011         if (r != MAILIMAP_NO_ERROR) {
2012                 mailimap_fetch_att_free(fetch_att);
2013                 res = MAILIMAP_ERROR_MEMORY;
2014                 goto free_fetch_type;
2015         }
2016
2017         mailstream_logger = imap_logger_fetch;
2018         
2019         r = mailimap_uid_fetch(imap, set,
2020                                fetch_type, &fetch_result);
2021
2022         mailstream_logger = imap_logger_cmd;
2023         mailimap_fetch_type_free(fetch_type);
2024         mailimap_set_free(set);
2025
2026         if (r != MAILIMAP_NO_ERROR) {
2027                 res = r;
2028                 goto err;
2029         }
2030
2031         env_list = NULL;
2032         r = result_to_uid_flags_list(fetch_result, &env_list);
2033         mailimap_fetch_list_free(fetch_result);
2034         
2035         * result = env_list;
2036
2037         return MAILIMAP_NO_ERROR;
2038
2039  free_fetch_type:
2040         mailimap_fetch_type_free(fetch_type);
2041  free_set:
2042         mailimap_set_free(set);
2043  err:
2044         return res;
2045 }
2046
2047
2048
2049 static void fetch_uid_flags_run(struct etpan_thread_op * op)
2050 {
2051         struct fetch_uid_param * param;
2052         struct fetch_uid_result * result;
2053         carray * fetch_result;
2054         int r;
2055         
2056         param = op->param;
2057         result = op->result;
2058
2059         CHECK_IMAP();
2060
2061         fetch_result = NULL;
2062         r = imap_get_messages_flags_list(param->imap, param->first_index,
2063                                          &fetch_result);
2064         
2065         result->error = r;
2066         result->fetch_result = fetch_result;
2067         debug_print("imap fetch_uid run - end %i\n", r);
2068 }
2069
2070 int imap_threaded_fetch_uid_flags(Folder * folder, uint32_t first_index,
2071                                   carray ** fetch_result)
2072 {
2073         struct fetch_uid_param param;
2074         struct fetch_uid_result result;
2075         mailimap * imap;
2076         
2077         debug_print("imap fetch_uid - begin\n");
2078         
2079         imap = get_imap(folder);
2080         param.imap = imap;
2081         param.first_index = first_index;
2082         
2083         mailstream_logger = imap_logger_noop;
2084         log_print(LOG_PROTOCOL, "IMAP4- [fetching flags...]\n");
2085
2086         threaded_run(folder, &param, &result, fetch_uid_flags_run);
2087
2088         mailstream_logger = imap_logger_cmd;
2089
2090         
2091         if (result.error != MAILIMAP_NO_ERROR)
2092                 return result.error;
2093         
2094         debug_print("imap fetch_uid - end\n");
2095         
2096         * fetch_result = result.fetch_result;
2097         
2098         return result.error;
2099 }
2100
2101
2102 void imap_fetch_uid_flags_list_free(carray * uid_flags_list)
2103 {
2104         unsigned int i;
2105         
2106         for(i = 0 ; i < carray_count(uid_flags_list) ; i += 3) {
2107                 void * data;
2108                 
2109                 data = carray_get(uid_flags_list, i);
2110                 free(data);
2111                 data = carray_get(uid_flags_list, i + 1);
2112                 free(data);
2113         }
2114         carray_free(uid_flags_list);
2115 }
2116
2117
2118
2119 static int imap_fetch(mailimap * imap,
2120                       uint32_t msg_index,
2121                       char ** result,
2122                       size_t * result_len)
2123 {
2124         int r;
2125         struct mailimap_set * set;
2126         struct mailimap_fetch_att * fetch_att;
2127         struct mailimap_fetch_type * fetch_type;
2128         clist * fetch_result;
2129         struct mailimap_msg_att * msg_att;
2130         struct mailimap_msg_att_item * msg_att_item;
2131         char * text;
2132         size_t text_length;
2133         int res;
2134         clistiter * cur;
2135         struct mailimap_section * section;
2136
2137         set = mailimap_set_new_single(msg_index);
2138         if (set == NULL) {
2139                 res = MAILIMAP_ERROR_MEMORY;
2140                 goto err;
2141         }
2142
2143         section = mailimap_section_new(NULL);
2144         if (section == NULL) {
2145                 res = MAILIMAP_ERROR_MEMORY;
2146                 goto free_set;
2147         }
2148   
2149         fetch_att = mailimap_fetch_att_new_body_peek_section(section);
2150         if (fetch_att == NULL) {
2151                 mailimap_section_free(section);
2152                 res = MAILIMAP_ERROR_MEMORY;
2153                 goto free_set;
2154         }
2155   
2156         fetch_type = mailimap_fetch_type_new_fetch_att(fetch_att);
2157         if (fetch_type == NULL) {
2158                 res = MAILIMAP_ERROR_MEMORY;
2159                 goto free_fetch_att;
2160         }
2161
2162         mailstream_logger = imap_logger_fetch;
2163         
2164         r = mailimap_uid_fetch(imap, set,
2165                                fetch_type, &fetch_result);
2166   
2167         mailstream_logger = imap_logger_cmd;
2168         
2169         mailimap_fetch_type_free(fetch_type);
2170         mailimap_set_free(set);
2171   
2172         switch (r) {
2173         case MAILIMAP_NO_ERROR:
2174                 break;
2175         default:
2176                 return r;
2177         }
2178   
2179         if (clist_begin(fetch_result) == NULL) {
2180                 mailimap_fetch_list_free(fetch_result);
2181                 return MAILIMAP_ERROR_FETCH;
2182         }
2183
2184         msg_att = clist_begin(fetch_result)->data;
2185
2186         text = NULL;
2187         text_length = 0;
2188
2189         for(cur = clist_begin(msg_att->att_list) ; cur != NULL ;
2190             cur = clist_next(cur)) {
2191                 msg_att_item = clist_content(cur);
2192
2193                 if (msg_att_item->att_type == MAILIMAP_MSG_ATT_ITEM_STATIC) {
2194                         if (msg_att_item->att_data.att_static->att_type ==
2195                             MAILIMAP_MSG_ATT_BODY_SECTION) {
2196                                 text = msg_att_item->att_data.att_static->att_data.att_body_section->sec_body_part;
2197                                 /* detach */
2198                                 msg_att_item->att_data.att_static->att_data.att_body_section->sec_body_part = NULL;
2199                                 text_length =
2200                                         msg_att_item->att_data.att_static->att_data.att_body_section->sec_length;
2201                         }
2202                 }
2203         }
2204
2205         mailimap_fetch_list_free(fetch_result);
2206
2207         if (text == NULL)
2208                 return MAILIMAP_ERROR_FETCH;
2209
2210         * result = text;
2211         * result_len = text_length;
2212   
2213         return MAILIMAP_NO_ERROR;
2214
2215  free_fetch_att:
2216         mailimap_fetch_att_free(fetch_att);
2217  free_set:
2218         mailimap_set_free(set);
2219  err:
2220         return res;
2221 }
2222
2223 static int imap_fetch_header(mailimap * imap,
2224                              uint32_t msg_index,
2225                              char ** result,
2226                              size_t * result_len)
2227 {
2228   int r;
2229   struct mailimap_set * set;
2230   struct mailimap_fetch_att * fetch_att;
2231   struct mailimap_fetch_type * fetch_type;
2232   clist * fetch_result;
2233   struct mailimap_msg_att * msg_att;
2234   struct mailimap_msg_att_item * msg_att_item;
2235   char * text;
2236   size_t text_length;
2237   int res;
2238   clistiter * cur;
2239   struct mailimap_section * section;
2240   
2241   set = mailimap_set_new_single(msg_index);
2242   if (set == NULL) {
2243     res = MAILIMAP_ERROR_MEMORY;
2244     goto err;
2245   }
2246
2247   section = mailimap_section_new_header();
2248   if (section == NULL) {
2249     res = MAILIMAP_ERROR_MEMORY;
2250     goto free_set;
2251   }
2252   
2253   fetch_att = mailimap_fetch_att_new_body_peek_section(section);
2254   if (fetch_att == NULL) {
2255     mailimap_section_free(section);
2256     res = MAILIMAP_ERROR_MEMORY;
2257     goto free_set;
2258   }
2259   
2260   fetch_type = mailimap_fetch_type_new_fetch_att(fetch_att);
2261   if (fetch_type == NULL) {
2262     res = MAILIMAP_ERROR_MEMORY;
2263     goto free_fetch_att;
2264   }
2265
2266   mailstream_logger = imap_logger_fetch;
2267   
2268   r = mailimap_uid_fetch(imap, set, fetch_type, &fetch_result);
2269   
2270   mailstream_logger = imap_logger_cmd;
2271   mailimap_fetch_type_free(fetch_type);
2272   mailimap_set_free(set);
2273
2274   switch (r) {
2275   case MAILIMAP_NO_ERROR:
2276     break;
2277   default:
2278     return r;
2279   }
2280
2281   if (clist_begin(fetch_result) == NULL) {
2282     mailimap_fetch_list_free(fetch_result);
2283     return MAILIMAP_ERROR_FETCH;
2284   }
2285
2286   msg_att = clist_begin(fetch_result)->data;
2287
2288   text = NULL;
2289   text_length = 0;
2290
2291   for(cur = clist_begin(msg_att->att_list) ; cur != NULL ;
2292       cur = clist_next(cur)) {
2293     msg_att_item = clist_content(cur);
2294
2295     if (msg_att_item->att_type == MAILIMAP_MSG_ATT_ITEM_STATIC) {
2296       if (msg_att_item->att_data.att_static->att_type ==
2297           MAILIMAP_MSG_ATT_BODY_SECTION) {
2298         text = msg_att_item->att_data.att_static->att_data.att_body_section->sec_body_part;
2299         msg_att_item->att_data.att_static->att_data.att_body_section->sec_body_part = NULL;
2300         text_length =
2301           msg_att_item->att_data.att_static->att_data.att_body_section->sec_length;
2302       }
2303     }
2304   }
2305
2306   mailimap_fetch_list_free(fetch_result);
2307
2308   if (text == NULL)
2309     return MAILIMAP_ERROR_FETCH;
2310
2311   * result = text;
2312   * result_len = text_length;
2313
2314   return MAILIMAP_NO_ERROR;
2315
2316  free_fetch_att:
2317   mailimap_fetch_att_free(fetch_att);
2318  free_set:
2319   mailimap_set_free(set);
2320  err:
2321   return res;
2322 }
2323
2324
2325
2326 struct fetch_content_param {
2327         mailimap * imap;
2328         uint32_t msg_index;
2329         const char * filename;
2330         int with_body;
2331 };
2332
2333 struct fetch_content_result {
2334         int error;
2335 };
2336
2337 static void fetch_content_run(struct etpan_thread_op * op)
2338 {
2339         struct fetch_content_param * param;
2340         struct fetch_content_result * result;
2341         char * content;
2342         size_t content_size;
2343         int r;
2344         int fd;
2345         FILE * f;
2346         
2347         param = op->param;
2348         result = op->result;
2349
2350         CHECK_IMAP();
2351
2352         content = NULL;
2353         content_size = 0;
2354         if (param->with_body)
2355                 r = imap_fetch(param->imap, param->msg_index,
2356                                &content, &content_size);
2357         else
2358                 r = imap_fetch_header(param->imap, param->msg_index,
2359                                       &content, &content_size);
2360         
2361         result->error = r;
2362         
2363         if (r == MAILIMAP_NO_ERROR) {
2364                 fd = open(param->filename, O_RDWR | O_CREAT, 0600);
2365                 if (fd < 0) {
2366                         result->error = MAILIMAP_ERROR_FETCH;
2367                         goto free;
2368                 }
2369                 
2370                 f = fdopen(fd, "wb");
2371                 if (f == NULL) {
2372                         result->error = MAILIMAP_ERROR_FETCH;
2373                         goto close;
2374                 }
2375                 
2376                 r = fwrite(content, 1, content_size, f);
2377                 if (r < content_size) {
2378                         result->error = MAILIMAP_ERROR_FETCH;
2379                         goto fclose;
2380                 }
2381                 
2382                 r = fclose(f);
2383                 if (r == EOF) {
2384                         result->error = MAILIMAP_ERROR_FETCH;
2385                         goto unlink;
2386                 }
2387                 goto free;
2388                 
2389         fclose:
2390                 fclose(f);
2391                 goto unlink;
2392         close:
2393                 close(fd);
2394         unlink:
2395                 g_unlink(param->filename);
2396         
2397         free:
2398                 /* mmap_string_unref is a simple free in libetpan
2399                  * when it has MMAP_UNAVAILABLE defined */
2400                 if (mmap_string_unref(content) != 0)
2401                         free(content);
2402         }
2403         
2404         debug_print("imap fetch_content run - end %i\n", result->error);
2405 }
2406
2407 int imap_threaded_fetch_content(Folder * folder, uint32_t msg_index,
2408                                 int with_body,
2409                                 const char * filename)
2410 {
2411         struct fetch_content_param param;
2412         struct fetch_content_result result;
2413         mailimap * imap;
2414         
2415         debug_print("imap fetch_content - begin\n");
2416         
2417         imap = get_imap(folder);
2418         param.imap = imap;
2419         param.msg_index = msg_index;
2420         param.filename = filename;
2421         param.with_body = with_body;
2422         
2423         threaded_run(folder, &param, &result, fetch_content_run);
2424         
2425         if (result.error != MAILIMAP_NO_ERROR)
2426                 return result.error;
2427         
2428         debug_print("imap fetch_content - end\n");
2429         
2430         return result.error;
2431 }
2432
2433
2434
2435 static int imap_flags_to_flags(struct mailimap_msg_att_dynamic * att_dyn, GSList **s_tags)
2436 {
2437         int flags;
2438         clist * flag_list;
2439         clistiter * cur;
2440         GSList *tags = NULL;
2441
2442         flags = MSG_UNREAD;
2443         
2444         flag_list = att_dyn->att_list;
2445         if (flag_list == NULL)
2446                 return flags;
2447         
2448         for(cur = clist_begin(flag_list) ; cur != NULL ;
2449             cur = clist_next(cur)) {
2450                 struct mailimap_flag_fetch * flag_fetch;
2451                         
2452                 flag_fetch = clist_content(cur);
2453                 if (flag_fetch->fl_type == MAILIMAP_FLAG_FETCH_RECENT)
2454                         flags |= MSG_NEW;
2455                 else {
2456                         switch (flag_fetch->fl_flag->fl_type) {
2457                         case MAILIMAP_FLAG_ANSWERED:
2458                                 flags |= MSG_REPLIED;
2459                                 break;
2460                         case MAILIMAP_FLAG_FLAGGED:
2461                                 flags |= MSG_MARKED;
2462                                 break;
2463                         case MAILIMAP_FLAG_DELETED:
2464                                 flags |= MSG_DELETED;
2465                                 break;
2466                         case MAILIMAP_FLAG_SEEN:
2467                                 flags &= ~MSG_UNREAD;
2468                                 flags &= ~MSG_NEW;
2469                                 break;
2470                         case MAILIMAP_FLAG_KEYWORD:
2471                                 if (!strcasecmp(flag_fetch->fl_flag->fl_data.fl_keyword, "$Forwarded"))
2472                                         flags |= MSG_FORWARDED;
2473                                 else if (s_tags)
2474                                         tags = g_slist_prepend(tags, g_strdup(flag_fetch->fl_flag->fl_data.fl_keyword));
2475                                 break;
2476                         }
2477                 }
2478         }
2479         if (s_tags)
2480                 *s_tags = tags;
2481         return flags;
2482 }
2483
2484 static int imap_get_msg_att_info(struct mailimap_msg_att * msg_att,
2485                                  uint32_t * puid,
2486                                  char ** pheaders,
2487                                  size_t * pref_size,
2488                                  struct mailimap_msg_att_dynamic ** patt_dyn)
2489 {
2490   clistiter * item_cur;
2491   uint32_t uid;
2492   char * headers;
2493   size_t ref_size;
2494   struct mailimap_msg_att_dynamic * att_dyn;
2495
2496   uid = 0;
2497   headers = NULL;
2498   ref_size = 0;
2499   att_dyn = NULL;
2500
2501   for(item_cur = clist_begin(msg_att->att_list) ; item_cur != NULL ;
2502       item_cur = clist_next(item_cur)) {
2503     struct mailimap_msg_att_item * item;
2504
2505     item = clist_content(item_cur);
2506       
2507     switch (item->att_type) {
2508     case MAILIMAP_MSG_ATT_ITEM_STATIC:
2509       switch (item->att_data.att_static->att_type) {
2510       case MAILIMAP_MSG_ATT_UID:
2511         uid = item->att_data.att_static->att_data.att_uid;
2512         break;
2513
2514       case MAILIMAP_MSG_ATT_BODY_SECTION:
2515         if (headers == NULL) {
2516           headers = item->att_data.att_static->att_data.att_body_section->sec_body_part;
2517         }
2518         break;
2519       case MAILIMAP_MSG_ATT_RFC822_SIZE:
2520               ref_size = item->att_data.att_static->att_data.att_rfc822_size;
2521               break;
2522       }
2523       break;
2524       
2525     case MAILIMAP_MSG_ATT_ITEM_DYNAMIC:
2526       if (att_dyn == NULL) {
2527         att_dyn = item->att_data.att_dyn;
2528       }
2529       break;
2530     }
2531   }
2532
2533   if (puid != NULL)
2534     * puid = uid;
2535   if (pheaders != NULL)
2536     * pheaders = headers;
2537   if (pref_size != NULL)
2538     * pref_size = ref_size;
2539   if (patt_dyn != NULL)
2540     * patt_dyn = att_dyn;
2541
2542   return MAIL_NO_ERROR;
2543 }
2544
2545 static struct imap_fetch_env_info *
2546 fetch_to_env_info(struct mailimap_msg_att * msg_att, GSList **tags)
2547 {
2548         struct imap_fetch_env_info * info;
2549         uint32_t uid;
2550         char * headers;
2551         size_t size;
2552         struct mailimap_msg_att_dynamic * att_dyn;
2553
2554         imap_get_msg_att_info(msg_att, &uid, &headers, &size,
2555                               &att_dyn);
2556         
2557         if (!headers)
2558                 return NULL;
2559         info = malloc(sizeof(* info));
2560         info->uid = uid;
2561         info->headers = strdup(headers);
2562         info->size = size;
2563         info->flags = imap_flags_to_flags(att_dyn, tags);
2564         
2565         return info;
2566 }
2567
2568 static int
2569 imap_fetch_result_to_envelop_list(clist * fetch_result,
2570                                   carray ** p_env_list)
2571 {
2572         clistiter * cur;
2573         unsigned int i;
2574         carray * env_list;
2575
2576         i = 0;
2577         env_list = carray_new(16);
2578   
2579         for(cur = clist_begin(fetch_result) ; cur != NULL ;
2580             cur = clist_next(cur)) {
2581                 struct mailimap_msg_att * msg_att;
2582                 struct imap_fetch_env_info * env_info;
2583                 GSList *tags = NULL;
2584
2585                 msg_att = clist_content(cur);
2586
2587                 env_info = fetch_to_env_info(msg_att, &tags);
2588                 if (!env_info)
2589                         return MAILIMAP_ERROR_MEMORY;
2590                 carray_add(env_list, env_info, NULL);
2591                 carray_add(env_list, tags, NULL);
2592         }
2593   
2594         * p_env_list = env_list;
2595   
2596         return MAIL_NO_ERROR;
2597 }
2598
2599 static int imap_add_envelope_fetch_att(struct mailimap_fetch_type * fetch_type)
2600 {
2601         struct mailimap_fetch_att * fetch_att;
2602         int r;
2603         char * header;
2604         clist * hdrlist;
2605         struct mailimap_header_list * imap_hdrlist;
2606         struct mailimap_section * section;
2607
2608         hdrlist = clist_new();
2609   
2610         header = strdup("Date");
2611         r = clist_append(hdrlist, header);
2612         header = strdup("From");
2613         r = clist_append(hdrlist, header);
2614         header = strdup("To");
2615         r = clist_append(hdrlist, header);
2616         header = strdup("Cc");
2617         r = clist_append(hdrlist, header);
2618         header = strdup("Subject");
2619         r = clist_append(hdrlist, header);
2620         header = strdup("Message-ID");
2621         r = clist_append(hdrlist, header);
2622         header = strdup("References");
2623         r = clist_append(hdrlist, header);
2624         header = strdup("In-Reply-To");
2625         r = clist_append(hdrlist, header);
2626   
2627         imap_hdrlist = mailimap_header_list_new(hdrlist);
2628         section = mailimap_section_new_header_fields(imap_hdrlist);
2629         fetch_att = mailimap_fetch_att_new_body_peek_section(section);
2630         mailimap_fetch_type_new_fetch_att_list_add(fetch_type, fetch_att);
2631   
2632         return MAIL_NO_ERROR;
2633 }
2634
2635 static int imap_add_header_fetch_att(struct mailimap_fetch_type * fetch_type)
2636 {
2637         struct mailimap_fetch_att * fetch_att;
2638         struct mailimap_section * section;
2639         
2640         section = mailimap_section_new_header();
2641         fetch_att = mailimap_fetch_att_new_body_peek_section(section);
2642         mailimap_fetch_type_new_fetch_att_list_add(fetch_type, fetch_att);
2643         
2644         return MAIL_NO_ERROR;
2645 }
2646
2647 static int
2648 imap_get_envelopes_list(mailimap * imap, struct mailimap_set * set,
2649                         carray ** p_env_list)
2650 {
2651         struct mailimap_fetch_att * fetch_att;
2652         struct mailimap_fetch_type * fetch_type;
2653         int res;
2654         clist * fetch_result;
2655         int r;
2656         carray * env_list = NULL;
2657         chashdatum key;
2658         chashdatum value;
2659         
2660         fetch_type = mailimap_fetch_type_new_fetch_att_list_empty();
2661   
2662         /* uid */
2663         fetch_att = mailimap_fetch_att_new_uid();
2664         r = mailimap_fetch_type_new_fetch_att_list_add(fetch_type, fetch_att);
2665   
2666         /* flags */
2667         fetch_att = mailimap_fetch_att_new_flags();
2668         r = mailimap_fetch_type_new_fetch_att_list_add(fetch_type, fetch_att);
2669   
2670         /* rfc822 size */
2671         fetch_att = mailimap_fetch_att_new_rfc822_size();
2672         r = mailimap_fetch_type_new_fetch_att_list_add(fetch_type, fetch_att);
2673   
2674         /* headers */
2675         key.data = &imap;
2676         key.len = sizeof(imap);
2677         r = chash_get(courier_workaround_hash, &key, &value);
2678         if (r < 0)
2679                 r = imap_add_envelope_fetch_att(fetch_type);
2680         else
2681                 r = imap_add_header_fetch_att(fetch_type);
2682         
2683         mailstream_logger = imap_logger_fetch;
2684         
2685         r = mailimap_uid_fetch(imap, set, fetch_type, &fetch_result);
2686         
2687         mailstream_logger = imap_logger_cmd;
2688         switch (r) {
2689         case MAILIMAP_NO_ERROR:
2690                 break;
2691         default:
2692                 mailimap_fetch_type_free(fetch_type);
2693                 debug_print("uid_fetch: %d\n", r);
2694                 return r;
2695         }
2696         
2697         if (clist_begin(fetch_result) == NULL) {
2698                 res = MAILIMAP_ERROR_FETCH;
2699                 debug_print("clist_begin = NULL\n");
2700                 goto err;
2701         }
2702         
2703         r = imap_fetch_result_to_envelop_list(fetch_result, &env_list);
2704         mailimap_fetch_list_free(fetch_result);
2705         
2706         if (r != MAILIMAP_NO_ERROR) {
2707                 mailimap_fetch_type_free(fetch_type);
2708                 res = MAILIMAP_ERROR_MEMORY;
2709                 debug_print("fetch_result_to_envelop_list: %d\n", res);
2710                 goto err;
2711         }
2712         
2713         mailimap_fetch_type_free(fetch_type);
2714         
2715         * p_env_list = env_list;
2716         
2717         return MAILIMAP_NO_ERROR;
2718   
2719  err:
2720         return res;
2721 }
2722
2723 struct fetch_env_param {
2724         mailimap * imap;
2725         struct mailimap_set * set;
2726 };
2727
2728 struct fetch_env_result {
2729         carray * fetch_env_result;
2730         int error;
2731 };
2732
2733 static void fetch_env_run(struct etpan_thread_op * op)
2734 {
2735         struct fetch_env_param * param;
2736         struct fetch_env_result * result;
2737         carray * env_list;
2738         int r;
2739         
2740         param = op->param;
2741         result = op->result;
2742
2743         CHECK_IMAP();
2744
2745         env_list = NULL;
2746         r = imap_get_envelopes_list(param->imap, param->set,
2747                                     &env_list);
2748         
2749         result->error = r;
2750         result->fetch_env_result = env_list;
2751         
2752         debug_print("imap fetch_env run - end %i\n", r);
2753 }
2754
2755 int imap_threaded_fetch_env(Folder * folder, struct mailimap_set * set,
2756                             carray ** p_env_list)
2757 {
2758         struct fetch_env_param param;
2759         struct fetch_env_result result;
2760         mailimap * imap;
2761         
2762         debug_print("imap fetch_env - begin\n");
2763         
2764         imap = get_imap(folder);
2765         param.imap = imap;
2766         param.set = set;
2767         
2768         threaded_run(folder, &param, &result, fetch_env_run);
2769         
2770         if (result.error != MAILIMAP_NO_ERROR) {
2771                 chashdatum key;
2772                 chashdatum value;
2773                 int r;
2774                 
2775                 key.data = &imap;
2776                 key.len = sizeof(imap);
2777                 r = chash_get(courier_workaround_hash, &key, &value);
2778                 if (r < 0) {
2779                         value.data = NULL;
2780                         value.len = 0;
2781                         chash_set(courier_workaround_hash, &key, &value, NULL);
2782                         
2783                         threaded_run(folder, &param, &result, fetch_env_run);
2784                 }
2785         }
2786         
2787         if (result.error != MAILIMAP_NO_ERROR)
2788                 return result.error;
2789         
2790         debug_print("imap fetch_env - end\n");
2791         
2792         * p_env_list = result.fetch_env_result;
2793         
2794         return result.error;
2795 }
2796
2797 void imap_fetch_env_free(carray * env_list)
2798 {
2799         unsigned int i;
2800         
2801         for(i = 0 ; i < carray_count(env_list) ; i += 2) {
2802                 struct imap_fetch_env_info * env_info;
2803                 
2804                 env_info = carray_get(env_list, i);
2805                 free(env_info->headers);
2806                 free(env_info);
2807         }
2808         carray_free(env_list);
2809 }
2810
2811
2812
2813
2814
2815 struct append_param {
2816         mailimap * imap;
2817         const char * mailbox;
2818         const char * filename;
2819         struct mailimap_flag_list * flag_list;
2820 };
2821
2822 struct append_result {
2823         int error;
2824         int uid;
2825 };
2826
2827 static void append_run(struct etpan_thread_op * op)
2828 {
2829         struct append_param * param;
2830         struct append_result * result;
2831         int r;
2832         char * data;
2833         size_t size;
2834 #ifndef G_OS_WIN32
2835         struct stat stat_buf;
2836         int fd;
2837 #endif
2838         guint32 uid = 0, val = 0;
2839         
2840         param = op->param;
2841         result = op->result;
2842         
2843         CHECK_IMAP();
2844
2845 #ifndef G_OS_WIN32
2846         r = stat(param->filename, &stat_buf);
2847         if (r < 0) {
2848                 result->error = MAILIMAP_ERROR_APPEND;
2849                 return;
2850         }
2851         size = stat_buf.st_size;
2852         
2853         fd = open(param->filename, O_RDONLY);
2854         if (fd < 0) {
2855                 result->error = MAILIMAP_ERROR_APPEND;
2856                 return;
2857         }
2858         
2859         data = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0);
2860         if (data == (void *) MAP_FAILED) {
2861                 close(fd);
2862                 result->error = MAILIMAP_ERROR_APPEND;
2863                 return;
2864         }
2865 #else
2866         data = file_read_to_str_no_recode(param->filename);
2867         if (data == NULL) {
2868                 result->error = MAILIMAP_ERROR_APPEND;
2869                 return;
2870         }
2871         size = strlen(data);
2872 #endif
2873         mailstream_logger = imap_logger_append;
2874         
2875         r = mailimap_uidplus_append(param->imap, param->mailbox,
2876                             param->flag_list, NULL,
2877                             data, size, &val, &uid);
2878
2879         mailstream_logger = imap_logger_cmd;
2880         
2881 #ifndef G_OS_WIN32
2882         munmap(data, size);
2883         close(fd);
2884 #else
2885         g_free(data);
2886 #endif
2887         
2888         result->error = r;
2889         result->uid = uid;
2890         debug_print("imap append run - end %i uid %d\n", r, uid);
2891 }
2892
2893 int imap_threaded_append(Folder * folder, const char * mailbox,
2894                          const char * filename,
2895                          struct mailimap_flag_list * flag_list,
2896                          int *uid)
2897 {
2898         struct append_param param;
2899         struct append_result result;
2900         mailimap * imap;
2901         
2902         debug_print("imap append - begin\n");
2903         
2904         imap = get_imap(folder);
2905         param.imap = imap;
2906         param.mailbox = mailbox;
2907         param.filename = filename;
2908         param.flag_list = flag_list;
2909         
2910         threaded_run(folder, &param, &result, append_run);
2911         
2912         if (result.error != MAILIMAP_NO_ERROR)
2913                 return result.error;
2914         
2915         debug_print("imap append - end\n");
2916         if (uid != NULL)
2917                 *uid = result.uid;
2918
2919         return result.error;
2920 }
2921
2922
2923
2924
2925 struct expunge_param {
2926         mailimap * imap;
2927 };
2928
2929 struct expunge_result {
2930         int error;
2931 };
2932
2933 static void expunge_run(struct etpan_thread_op * op)
2934 {
2935         struct expunge_param * param;
2936         struct expunge_result * result;
2937         int r;
2938         
2939         param = op->param;
2940         result = op->result;
2941
2942         CHECK_IMAP();
2943
2944         r = mailimap_expunge(param->imap);
2945         
2946         result->error = r;
2947         debug_print("imap expunge run - end %i\n", r);
2948 }
2949
2950 int imap_threaded_expunge(Folder * folder)
2951 {
2952         struct expunge_param param;
2953         struct expunge_result result;
2954         
2955         debug_print("imap expunge - begin\n");
2956         
2957         param.imap = get_imap(folder);
2958         
2959         threaded_run(folder, &param, &result, expunge_run);
2960         
2961         debug_print("imap expunge - end\n");
2962         
2963         return result.error;
2964 }
2965
2966
2967 struct copy_param {
2968         mailimap * imap;
2969         struct mailimap_set * set;
2970         const char * mb;
2971 };
2972
2973 struct copy_result {
2974         int error;
2975         struct mailimap_set *source;
2976         struct mailimap_set *dest;
2977 };
2978
2979 static void copy_run(struct etpan_thread_op * op)
2980 {
2981         struct copy_param * param;
2982         struct copy_result * result;
2983         int r;
2984         guint32 val;
2985         struct mailimap_set *source = NULL, *dest = NULL;
2986
2987         param = op->param;
2988         result = op->result;
2989
2990         CHECK_IMAP();
2991
2992         r = mailimap_uidplus_uid_copy(param->imap, param->set, param->mb,
2993                 &val, &source, &dest);
2994         
2995         result->error = r;
2996         if (r == 0) {
2997                 result->source = source;
2998                 result->dest = dest;
2999         } else {
3000                 result->source = NULL;
3001                 result->dest = NULL;
3002         }
3003         debug_print("imap copy run - end %i\n", r);
3004 }
3005
3006 int imap_threaded_copy(Folder * folder, struct mailimap_set * set,
3007                        const char * mb, struct mailimap_set **source,
3008                        struct mailimap_set **dest)
3009 {
3010         struct copy_param param;
3011         struct copy_result result;
3012         mailimap * imap;
3013         
3014         debug_print("imap copy - begin\n");
3015         
3016         imap = get_imap(folder);
3017         param.imap = imap;
3018         param.set = set;
3019         param.mb = mb;
3020         
3021         threaded_run(folder, &param, &result, copy_run);
3022         *source = NULL;
3023         *dest = NULL;
3024         
3025         if (result.error != MAILIMAP_NO_ERROR)
3026                 return result.error;
3027         
3028         *source = result.source;
3029         *dest = result.dest;
3030
3031         debug_print("imap copy - end\n");
3032         
3033         return result.error;
3034 }
3035
3036
3037
3038 struct store_param {
3039         mailimap * imap;
3040         struct mailimap_set * set;
3041         struct mailimap_store_att_flags * store_att_flags;
3042 };
3043
3044 struct store_result {
3045         int error;
3046 };
3047
3048 static void store_run(struct etpan_thread_op * op)
3049 {
3050         struct store_param * param;
3051         struct store_result * result;
3052         int r;
3053         
3054         param = op->param;
3055         result = op->result;
3056
3057         CHECK_IMAP();
3058
3059         r = mailimap_uid_store(param->imap, param->set,
3060                                param->store_att_flags);
3061         
3062         result->error = r;
3063         
3064         debug_print("imap store run - end %i\n", r);
3065 }
3066
3067 int imap_threaded_store(Folder * folder, struct mailimap_set * set,
3068                         struct mailimap_store_att_flags * store_att_flags)
3069 {
3070         struct store_param param;
3071         struct store_result result;
3072         mailimap * imap;
3073         
3074         debug_print("imap store - begin\n");
3075         
3076         imap = get_imap(folder);
3077         param.imap = imap;
3078         param.set = set;
3079         param.store_att_flags = store_att_flags;
3080         
3081         threaded_run(folder, &param, &result, store_run);
3082         
3083         if (result.error != MAILIMAP_NO_ERROR)
3084                 return result.error;
3085         
3086         debug_print("imap store - end\n");
3087         
3088         return result.error;
3089 }
3090
3091
3092 #define ENV_BUFFER_SIZE 512
3093 #ifndef G_OS_WIN32
3094 static void do_exec_command(int fd, const char * command,
3095                             const char * servername, uint16_t port)
3096 {
3097         int i, maxopen;
3098 #ifdef SOLARIS
3099         char env_buffer[ENV_BUFFER_SIZE];
3100 #endif
3101         
3102         if (fork() > 0) {
3103                 /* Fork again to become a child of init rather than
3104                    the etpan client. */
3105                 exit(0);
3106         }
3107   
3108 #ifdef SOLARIS
3109         if (servername)
3110                 snprintf(env_buffer, ENV_BUFFER_SIZE,
3111                          "ETPANSERVER=%s", servername);
3112         else
3113                 snprintf(env_buffer, ENV_BUFFER_SIZE, "ETPANSERVER=");
3114         putenv(env_buffer);
3115 #else
3116         if (servername)
3117                 setenv("ETPANSERVER", servername, 1);
3118         else
3119                 unsetenv("ETPANSERVER");
3120 #endif
3121   
3122 #ifdef SOLARIS
3123         if (port)
3124                 snprintf(env_buffer, ENV_BUFFER_SIZE, "ETPANPORT=%d", port);
3125         else
3126                 snprintf(env_buffer, ENV_BUFFER_SIZE, "ETPANPORT=");
3127         putenv(env_buffer);
3128 #else
3129         if (port) {
3130                 char porttext[20];
3131                 
3132                 snprintf(porttext, sizeof(porttext), "%d", port);
3133                 setenv("ETPANPORT", porttext, 1);
3134         }
3135         else {
3136                 unsetenv("ETPANPORT");
3137         }
3138 #endif
3139                 
3140         /* Not a lot we can do if there's an error other than bail. */
3141         if (dup2(fd, 0) == -1)
3142                 exit(1);
3143         if (dup2(fd, 1) == -1)
3144                 exit(1);
3145   
3146         /* Should we close stderr and reopen /dev/null? */
3147   
3148         maxopen = sysconf(_SC_OPEN_MAX);
3149         for (i=3; i < maxopen; i++)
3150                 close(i);
3151   
3152 #ifdef TIOCNOTTY
3153         /* Detach from the controlling tty if we have one. Otherwise,
3154            SSH might do something stupid like trying to use it instead
3155            of running $SSH_ASKPASS. Doh. */
3156         fd = open("/dev/tty", O_RDONLY);
3157         if (fd != -1) {
3158                 ioctl(fd, TIOCNOTTY, NULL);
3159                 close(fd);
3160         }
3161 #endif /* TIOCNOTTY */
3162
3163         execl("/bin/sh", "/bin/sh", "-c", command, NULL);
3164   
3165         /* Eep. Shouldn't reach this */
3166         exit(1);
3167 }
3168
3169 static int subcommand_connect(const char *command,
3170                               const char *servername, uint16_t port)
3171 {
3172         /* SEB unsupported on Windows */
3173         int sockfds[2];
3174         pid_t childpid;
3175   
3176         if (socketpair(AF_UNIX, SOCK_STREAM, 0, sockfds))
3177                 return -1;
3178   
3179         childpid = fork();
3180         if (!childpid) {
3181                 do_exec_command(sockfds[1], command, servername, port);
3182         }
3183         else if (childpid == -1) {
3184                 close(sockfds[0]);
3185                 close(sockfds[1]);
3186                 return -1;
3187         }
3188   
3189         close(sockfds[1]);
3190   
3191         /* Reap child, leaving grandchild process to run */
3192         waitpid(childpid, NULL, 0);
3193   
3194         return sockfds[0];
3195 }
3196
3197 static int socket_connect_cmd(mailimap * imap, const char * command,
3198                        const char * server, int port)
3199 {
3200         int fd;
3201         mailstream * s;
3202         int r;
3203         
3204         fd = subcommand_connect(command, server, port);
3205         if (fd < 0)
3206                 return MAILIMAP_ERROR_STREAM;
3207         
3208         s = mailstream_socket_open(fd);
3209         if (s == NULL) {
3210                 close(fd);
3211                 return MAILIMAP_ERROR_STREAM;
3212         }
3213         
3214         r = mailimap_connect(imap, s);
3215         if (r != MAILIMAP_NO_ERROR_AUTHENTICATED
3216         &&  r != MAILIMAP_NO_ERROR_NON_AUTHENTICATED) {
3217                 mailstream_close(s);
3218                 if (imap)
3219                         imap->imap_stream = NULL;
3220                 return r;
3221         }
3222         
3223         return r;
3224 }
3225
3226 /* connect cmd */
3227
3228 struct connect_cmd_param {
3229         mailimap * imap;
3230         const char * command;
3231         const char * server;
3232         int port;
3233 };
3234
3235 struct connect_cmd_result {
3236         int error;
3237 };
3238
3239 static void connect_cmd_run(struct etpan_thread_op * op)
3240 {
3241         int r;
3242         struct connect_cmd_param * param;
3243         struct connect_cmd_result * result;
3244         
3245         param = op->param;
3246         result = op->result;
3247         
3248         CHECK_IMAP();
3249
3250         r = socket_connect_cmd(param->imap, param->command,
3251                                param->server, param->port);
3252         
3253         result->error = r;
3254 }
3255
3256
3257 int imap_threaded_connect_cmd(Folder * folder, const char * command,
3258                               const char * server, int port)
3259 {
3260         struct connect_cmd_param param;
3261         struct connect_cmd_result result;
3262         chashdatum key;
3263         chashdatum value;
3264         mailimap * imap, * oldimap;
3265         
3266         oldimap = get_imap(folder);
3267
3268         imap = mailimap_new(0, NULL);
3269         
3270         if (oldimap) {
3271                 debug_print("deleting old imap %p\n", oldimap);
3272                 delete_imap(folder, oldimap);
3273         }
3274
3275         key.data = &folder;
3276         key.len = sizeof(folder);
3277         value.data = imap;
3278         value.len = 0;
3279         chash_set(session_hash, &key, &value, NULL);
3280         
3281         param.imap = imap;
3282         param.command = command;
3283         param.server = server;
3284         param.port = port;
3285         
3286         threaded_run(folder, &param, &result, connect_cmd_run);
3287         
3288         debug_print("connect_cmd ok %i with imap %p\n", result.error, imap);
3289         
3290         return result.error;
3291 }
3292 #endif /* G_OS_WIN32 */
3293
3294 void imap_threaded_cancel(Folder * folder)
3295 {
3296         mailimap * imap;
3297         
3298         imap = get_imap(folder);
3299         if (imap->imap_stream != NULL)
3300                 mailstream_cancel(imap->imap_stream);
3301 }
3302
3303 #else
3304
3305 void imap_main_init(void)
3306 {
3307 }
3308 void imap_main_done(void)
3309 {
3310 }
3311 void imap_main_set_timeout(int sec)
3312 {
3313 }
3314
3315 void imap_threaded_cancel(Folder * folder);
3316 {
3317 }
3318
3319 #endif