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