2008-09-01 [colin] 3.5.0cvs88
[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                                         if (!strcasecmp(flag->fl_flag->fl_data.fl_keyword, "Junk"))
1450                                                 c_flag = IMAP_FLAG_SPAM;
1451                                         if (!strcasecmp(flag->fl_flag->fl_data.fl_keyword, "NonJunk") ||
1452                                             !strcasecmp(flag->fl_flag->fl_data.fl_keyword, "NoJunk") ||
1453                                             !strcasecmp(flag->fl_flag->fl_data.fl_keyword, "NotJunk"))
1454                                                 c_flag = IMAP_FLAG_HAM;
1455                                         break;
1456                                 default:
1457                                         break;
1458                                 }
1459                                 if (c_flag != 0) {
1460                                         t_flags = g_slist_prepend(t_flags, 
1461                                                 GUINT_TO_POINTER(c_flag));
1462                                 }
1463                         }
1464                 }
1465                 if (ok_flags)
1466                         *ok_flags = t_flags;
1467         }
1468         debug_print("imap select - end\n");
1469         
1470         return result.error;
1471 }
1472
1473 static void close_run(struct etpan_thread_op * op)
1474 {
1475         struct select_param * param;
1476         struct select_result * result;
1477         int r;
1478         
1479         param = op->param;
1480         result = op->result;
1481
1482         CHECK_IMAP();
1483
1484         r = mailimap_close(param->imap);
1485         
1486         result->error = r;
1487         debug_print("imap close run - end %i\n", r);
1488 }
1489
1490 int imap_threaded_close(Folder * folder)
1491 {
1492         struct select_param param;
1493         struct select_result result;
1494         mailimap * imap;
1495         
1496         debug_print("imap close - begin\n");
1497         
1498         imap = get_imap(folder);
1499         param.imap = imap;
1500         
1501         threaded_run(folder, &param, &result, close_run);
1502         
1503         if (result.error != MAILIMAP_NO_ERROR)
1504                 return result.error;
1505         
1506         debug_print("imap close - end\n");
1507         
1508         return result.error;
1509 }
1510
1511 struct examine_param {
1512         mailimap * imap;
1513         const char * mb;
1514 };
1515
1516 struct examine_result {
1517         int error;
1518 };
1519
1520 static void examine_run(struct etpan_thread_op * op)
1521 {
1522         struct examine_param * param;
1523         struct examine_result * result;
1524         int r;
1525         
1526         param = op->param;
1527         result = op->result;
1528
1529         CHECK_IMAP();
1530
1531         r = mailimap_examine(param->imap, param->mb);
1532         
1533         result->error = r;
1534         debug_print("imap examine run - end %i\n", r);
1535 }
1536
1537 int imap_threaded_examine(Folder * folder, const char * mb,
1538                           gint * exists, gint * recent, gint * unseen,
1539                           guint32 * uid_validity)
1540 {
1541         struct examine_param param;
1542         struct examine_result result;
1543         mailimap * imap;
1544         
1545         debug_print("imap examine - begin\n");
1546         
1547         imap = get_imap(folder);
1548         param.imap = imap;
1549         param.mb = mb;
1550         
1551         threaded_run(folder, &param, &result, examine_run);
1552         
1553         if (result.error != MAILIMAP_NO_ERROR)
1554                 return result.error;
1555         
1556         if (!imap || imap->imap_selection_info == NULL)
1557                 return MAILIMAP_ERROR_PARSE;
1558         
1559         * exists = imap->imap_selection_info->sel_exists;
1560         * recent = imap->imap_selection_info->sel_recent;
1561         * unseen = imap->imap_selection_info->sel_unseen;
1562         * uid_validity = imap->imap_selection_info->sel_uidvalidity;
1563         
1564         debug_print("imap examine - end\n");
1565         
1566         return result.error;
1567 }
1568
1569
1570
1571
1572 struct search_param {
1573         mailimap * imap;
1574         int type;
1575         struct mailimap_set * set;
1576 };
1577
1578 struct search_result {
1579         int error;
1580         clist * search_result;
1581 };
1582
1583 static struct mailimap_set_item *sc_mailimap_set_item_copy(struct mailimap_set_item *orig)
1584 {
1585         return mailimap_set_item_new(orig->set_first, orig->set_last);
1586 }
1587
1588 static struct mailimap_set *sc_mailimap_set_copy(struct mailimap_set *orig)
1589 {
1590         clist *list = orig ? orig->set_list : NULL;
1591         clist *newlist = clist_new();
1592         clistiter *cur;
1593         
1594         if (!orig)
1595                 return NULL;
1596         for (cur = clist_begin(list); cur; cur = clist_next(cur))
1597                 clist_append(newlist, 
1598                         sc_mailimap_set_item_copy(
1599                         (struct mailimap_set_item *)clist_content(cur)));
1600         return mailimap_set_new(newlist);
1601 }
1602
1603 static void search_run(struct etpan_thread_op * op)
1604 {
1605         struct search_param * param;
1606         struct search_result * result;
1607         int r;
1608         struct mailimap_search_key * key = NULL;
1609         struct mailimap_search_key * uid_key = NULL;
1610         struct mailimap_search_key * search_type_key;
1611         clist * search_result;
1612         
1613         param = op->param;
1614         result = op->result;
1615
1616         CHECK_IMAP();
1617
1618         /* we copy the mailimap_set because freeing the key is recursive */
1619         if (param->set != NULL) {
1620                 uid_key = mailimap_search_key_new_uid(sc_mailimap_set_copy(param->set));
1621         } else if (param->type == IMAP_SEARCH_TYPE_SIMPLE) {
1622                 uid_key = mailimap_search_key_new_all();
1623         }
1624         search_type_key = NULL;
1625         switch (param->type) {
1626         case IMAP_SEARCH_TYPE_SIMPLE:
1627                 search_type_key = NULL;
1628                 break;
1629                 
1630         case IMAP_SEARCH_TYPE_SEEN:
1631                 search_type_key = mailimap_search_key_new(MAILIMAP_SEARCH_KEY_SEEN,
1632                                                           NULL, NULL, NULL, NULL, NULL,
1633                                                           NULL, NULL, NULL, NULL, NULL,
1634                                                           NULL, NULL, NULL, NULL, 0,
1635                                                           NULL, NULL, NULL, NULL, NULL,
1636                                                           NULL, 0, NULL, NULL, NULL);
1637                 break;
1638                 
1639         case IMAP_SEARCH_TYPE_UNSEEN:
1640                 search_type_key = mailimap_search_key_new(MAILIMAP_SEARCH_KEY_UNSEEN,
1641                                                           NULL, NULL, NULL, NULL, NULL,
1642                                                           NULL, NULL, NULL, NULL, NULL,
1643                                                           NULL, NULL, NULL, NULL, 0,
1644                                                           NULL, NULL, NULL, NULL, NULL,
1645                                                           NULL, 0, NULL, NULL, NULL);
1646                 break;
1647                 
1648         case IMAP_SEARCH_TYPE_ANSWERED:
1649                 search_type_key = mailimap_search_key_new(MAILIMAP_SEARCH_KEY_ANSWERED,
1650                                                           NULL, NULL, NULL, NULL, NULL,
1651                                                           NULL, NULL, NULL, NULL, NULL,
1652                                                           NULL, NULL, NULL, NULL, 0,
1653                                                           NULL, NULL, NULL, NULL, NULL,
1654                                                           NULL, 0, NULL, NULL, NULL);
1655                 break;
1656                 
1657         case IMAP_SEARCH_TYPE_FLAGGED:
1658                 search_type_key = mailimap_search_key_new(MAILIMAP_SEARCH_KEY_FLAGGED,
1659                                                           NULL, NULL, NULL, NULL, NULL,
1660                                                           NULL, NULL, NULL, NULL, NULL,
1661                                                           NULL, NULL, NULL, NULL, 0,
1662                                                           NULL, NULL, NULL, NULL, NULL,
1663                                                           NULL, 0, NULL, NULL, NULL);
1664                 break;
1665         case IMAP_SEARCH_TYPE_DELETED:
1666                 search_type_key = mailimap_search_key_new(MAILIMAP_SEARCH_KEY_DELETED,
1667                                                           NULL, NULL, NULL, NULL, NULL,
1668                                                           NULL, NULL, NULL, NULL, NULL,
1669                                                           NULL, NULL, NULL, NULL, 0,
1670                                                           NULL, NULL, NULL, NULL, NULL,
1671                                                           NULL, 0, NULL, NULL, NULL);
1672                 break;
1673         case IMAP_SEARCH_TYPE_FORWARDED:
1674                 search_type_key = mailimap_search_key_new(MAILIMAP_SEARCH_KEY_KEYWORD,
1675                                                           NULL, NULL, NULL, NULL, NULL,
1676                                                           strdup("$Forwarded"), NULL, NULL, NULL, NULL,
1677                                                           NULL, NULL, NULL, NULL, 0,
1678                                                           NULL, NULL, NULL, NULL, NULL,
1679                                                           NULL, 0, NULL, NULL, NULL);
1680                 break;
1681         case IMAP_SEARCH_TYPE_SPAM:
1682                 search_type_key = mailimap_search_key_new(MAILIMAP_SEARCH_KEY_KEYWORD,
1683                                                           NULL, NULL, NULL, NULL, NULL,
1684                                                           strdup("Junk"), NULL, NULL, NULL, NULL,
1685                                                           NULL, NULL, NULL, NULL, 0,
1686                                                           NULL, NULL, NULL, NULL, NULL,
1687                                                           NULL, 0, NULL, NULL, NULL);
1688                 break;
1689         }
1690         
1691         if (search_type_key != NULL) {
1692                 if (param->set != NULL) {
1693                         key = mailimap_search_key_new_multiple_empty();
1694                         mailimap_search_key_multiple_add(key, search_type_key);
1695                         mailimap_search_key_multiple_add(key, uid_key);
1696                 } else {
1697                         key = search_type_key;
1698                 }
1699         } else if (uid_key != NULL) {
1700                 key = uid_key;
1701         }
1702         
1703         if (key == NULL) {
1704                 g_warning("no key!");
1705                 result = op->result;
1706                 result->error = -1;
1707                 result->search_result = NULL;
1708         } else {
1709                 mailstream_logger = imap_logger_uid;
1710
1711                 r = mailimap_uid_search(param->imap, NULL, key, &search_result);
1712
1713                 mailstream_logger = imap_logger_cmd;
1714
1715                 /* free the key (with the imapset) */
1716                 mailimap_search_key_free(key);
1717
1718                 result->error = r;
1719                 result->search_result = search_result;
1720         }
1721         debug_print("imap search run - end %i\n", result->error);
1722 }
1723
1724 int imap_threaded_search(Folder * folder, int search_type,
1725                          struct mailimap_set * set, clist ** search_result)
1726 {
1727         struct search_param param;
1728         struct search_result result;
1729         mailimap * imap;
1730         
1731         debug_print("imap search - begin\n");
1732
1733         imap = get_imap(folder);
1734         param.imap = imap;
1735         param.set = set;
1736         param.type = search_type;
1737         
1738         threaded_run(folder, &param, &result, search_run);
1739         
1740         if (result.error != MAILIMAP_NO_ERROR)
1741                 return result.error;
1742         
1743         debug_print("imap search - end\n");
1744         
1745         * search_result = result.search_result;
1746         
1747         return result.error;
1748 }
1749
1750
1751
1752 static int imap_get_msg_att_info(struct mailimap_msg_att * msg_att,
1753                                  uint32_t * puid,
1754                                  char ** pheaders,
1755                                  size_t * pref_size,
1756                                  struct mailimap_msg_att_dynamic ** patt_dyn);
1757
1758 static int
1759 result_to_uid_list(clist * fetch_result, carray ** result)
1760 {
1761         clistiter * cur;
1762         int r;
1763         int res;
1764         carray * tab;
1765         
1766         tab = carray_new(128);
1767         if (tab == NULL) {
1768                 res = MAILIMAP_ERROR_MEMORY;
1769                 goto err;
1770         }
1771         
1772         for(cur = clist_begin(fetch_result) ; cur != NULL ;
1773             cur = clist_next(cur)) {
1774                 struct mailimap_msg_att * msg_att;
1775                 uint32_t uid;
1776                 uint32_t * puid;
1777                 
1778                 msg_att = clist_content(cur);
1779                 
1780                 uid = 0;
1781                 imap_get_msg_att_info(msg_att, &uid, NULL, NULL, NULL);
1782                 
1783                 puid = malloc(sizeof(* puid));
1784                 if (puid == NULL) {
1785                         res = MAILIMAP_ERROR_MEMORY;
1786                         goto free_list;
1787                 }
1788                 * puid = uid;
1789                         
1790                 r = carray_add(tab, puid, NULL);
1791                 if (r < 0) {
1792                         free(puid);
1793                         res = MAILIMAP_ERROR_MEMORY;
1794                         goto free_list;
1795                 }
1796         }
1797                 
1798         * result = tab;
1799
1800         return MAILIMAP_NO_ERROR;
1801   
1802  free_list:
1803         imap_fetch_uid_list_free(tab);
1804  err:
1805         return res;
1806 }
1807
1808 static int imap_get_messages_list(mailimap * imap,
1809                                   uint32_t first_index,
1810                                   carray ** result)
1811 {
1812         carray * env_list;
1813         int r;
1814         struct mailimap_fetch_att * fetch_att;
1815         struct mailimap_fetch_type * fetch_type;
1816         struct mailimap_set * set;
1817         clist * fetch_result;
1818         int res;
1819         
1820         set = mailimap_set_new_interval(first_index, 0);
1821         if (set == NULL) {
1822                 res = MAILIMAP_ERROR_MEMORY;
1823                 goto err;
1824         }
1825
1826         fetch_type = mailimap_fetch_type_new_fetch_att_list_empty();
1827         if (fetch_type == NULL) {
1828                 res = MAILIMAP_ERROR_MEMORY;
1829                 goto free_set;
1830         }
1831
1832         fetch_att = mailimap_fetch_att_new_uid();
1833         if (fetch_att == NULL) {
1834                 res = MAILIMAP_ERROR_MEMORY;
1835                 goto free_fetch_type;
1836         }
1837
1838         r = mailimap_fetch_type_new_fetch_att_list_add(fetch_type, fetch_att);
1839         if (r != MAILIMAP_NO_ERROR) {
1840                 mailimap_fetch_att_free(fetch_att);
1841                 res = MAILIMAP_ERROR_MEMORY;
1842                 goto free_fetch_type;
1843         }
1844
1845         mailstream_logger = imap_logger_fetch;
1846         
1847         r = mailimap_uid_fetch(imap, set,
1848                                fetch_type, &fetch_result);
1849
1850         mailstream_logger = imap_logger_cmd;
1851         mailimap_fetch_type_free(fetch_type);
1852         mailimap_set_free(set);
1853
1854         if (r != MAILIMAP_NO_ERROR) {
1855                 res = r;
1856                 goto err;
1857         }
1858
1859         env_list = NULL;
1860         r = result_to_uid_list(fetch_result, &env_list);
1861         mailimap_fetch_list_free(fetch_result);
1862         
1863         * result = env_list;
1864
1865         return MAILIMAP_NO_ERROR;
1866
1867  free_fetch_type:
1868         mailimap_fetch_type_free(fetch_type);
1869  free_set:
1870         mailimap_set_free(set);
1871  err:
1872         return res;
1873 }
1874
1875
1876
1877
1878 struct fetch_uid_param {
1879         mailimap * imap;
1880         uint32_t first_index;
1881 };
1882
1883 struct fetch_uid_result {
1884         int error;
1885         carray * fetch_result;
1886 };
1887
1888 static void fetch_uid_run(struct etpan_thread_op * op)
1889 {
1890         struct fetch_uid_param * param;
1891         struct fetch_uid_result * result;
1892         carray * fetch_result;
1893         int r;
1894         
1895         param = op->param;
1896         result = op->result;
1897
1898         CHECK_IMAP();
1899
1900         fetch_result = NULL;
1901         mailstream_logger = imap_logger_noop;
1902         log_print(LOG_PROTOCOL, "IMAP4- [fetching UIDs...]\n");
1903
1904         r = imap_get_messages_list(param->imap, param->first_index,
1905                                    &fetch_result);
1906         
1907         mailstream_logger = imap_logger_cmd;
1908
1909         result->error = r;
1910         result->fetch_result = fetch_result;
1911         debug_print("imap fetch_uid run - end %i\n", r);
1912 }
1913
1914 int imap_threaded_fetch_uid(Folder * folder, uint32_t first_index,
1915                             carray ** fetch_result)
1916 {
1917         struct fetch_uid_param param;
1918         struct fetch_uid_result result;
1919         mailimap * imap;
1920         
1921         debug_print("imap fetch_uid - begin\n");
1922         
1923         imap = get_imap(folder);
1924         param.imap = imap;
1925         param.first_index = first_index;
1926         
1927         threaded_run(folder, &param, &result, fetch_uid_run);
1928         
1929         if (result.error != MAILIMAP_NO_ERROR)
1930                 return result.error;
1931         
1932         debug_print("imap fetch_uid - end\n");
1933         
1934         * fetch_result = result.fetch_result;
1935         
1936         return result.error;
1937 }
1938
1939
1940 void imap_fetch_uid_list_free(carray * uid_list)
1941 {
1942         unsigned int i;
1943         
1944         for(i = 0 ; i < carray_count(uid_list) ; i ++) {
1945                 uint32_t * puid;
1946                 
1947                 puid = carray_get(uid_list, i);
1948                 free(puid);
1949         }
1950         carray_free(uid_list);
1951 }
1952
1953
1954
1955
1956 static int imap_flags_to_flags(struct mailimap_msg_att_dynamic * att_dyn, GSList **tags);
1957
1958 static int
1959 result_to_uid_flags_list(clist * fetch_result, carray ** result)
1960 {
1961         clistiter * cur;
1962         int r;
1963         int res;
1964         carray * tab;
1965         GSList *tags = NULL;
1966
1967         tab = carray_new(128);
1968         if (tab == NULL) {
1969                 res = MAILIMAP_ERROR_MEMORY;
1970                 goto err;
1971         }
1972         
1973         for(cur = clist_begin(fetch_result) ; cur != NULL ;
1974             cur = clist_next(cur)) {
1975                 struct mailimap_msg_att * msg_att;
1976                 uint32_t uid;
1977                 uint32_t * puid;
1978                 struct mailimap_msg_att_dynamic * att_dyn;
1979                 int flags;
1980                 int * pflags;
1981                 
1982                 tags = NULL;
1983
1984                 msg_att = clist_content(cur);
1985                 
1986                 uid = 0;
1987                 att_dyn = NULL;
1988                 imap_get_msg_att_info(msg_att, &uid, NULL, NULL, &att_dyn);
1989                 if (uid == 0)
1990                         continue;
1991                 if (att_dyn == NULL)
1992                         continue;
1993                 
1994                 flags = imap_flags_to_flags(att_dyn, &tags);
1995                 
1996                 puid = malloc(sizeof(* puid));
1997                 if (puid == NULL) {
1998                         res = MAILIMAP_ERROR_MEMORY;
1999                         goto free_list;
2000                 }
2001                 * puid = uid;
2002                 
2003                 r = carray_add(tab, puid, NULL);
2004                 if (r < 0) {
2005                         free(puid);
2006                         res = MAILIMAP_ERROR_MEMORY;
2007                         goto free_list;
2008                 }
2009                 pflags = malloc(sizeof(* pflags));
2010                 if (pflags == NULL) {
2011                         res = MAILIMAP_ERROR_MEMORY;
2012                         goto free_list;
2013                 }
2014                 * pflags = flags;
2015                 r = carray_add(tab, pflags, NULL);
2016                 if (r < 0) {
2017                         free(pflags);
2018                         res = MAILIMAP_ERROR_MEMORY;
2019                         goto free_list;
2020                 }
2021                 r = carray_add(tab, tags, NULL);
2022                 if (r < 0) {
2023                         free(pflags);
2024                         res = MAILIMAP_ERROR_MEMORY;
2025                         goto free_list;
2026                 }
2027         }
2028                 
2029         * result = tab;
2030
2031         return MAILIMAP_NO_ERROR;
2032   
2033  free_list:
2034         imap_fetch_uid_flags_list_free(tab);
2035         slist_free_strings(tags);
2036         g_slist_free(tags);
2037  err:
2038         return res;
2039 }
2040
2041 static int imap_get_messages_flags_list(mailimap * imap,
2042                                         uint32_t first_index,
2043                                         carray ** result)
2044 {
2045         carray * env_list;
2046         int r;
2047         struct mailimap_fetch_att * fetch_att;
2048         struct mailimap_fetch_type * fetch_type;
2049         struct mailimap_set * set;
2050         clist * fetch_result;
2051         int res;
2052         
2053         set = mailimap_set_new_interval(first_index, 0);
2054         if (set == NULL) {
2055                 res = MAILIMAP_ERROR_MEMORY;
2056                 goto err;
2057         }
2058
2059         fetch_type = mailimap_fetch_type_new_fetch_att_list_empty();
2060         if (fetch_type == NULL) {
2061                 res = MAILIMAP_ERROR_MEMORY;
2062                 goto free_set;
2063         }
2064
2065         fetch_att = mailimap_fetch_att_new_flags();
2066         if (fetch_att == NULL) {
2067                 res = MAILIMAP_ERROR_MEMORY;
2068                 goto free_fetch_type;
2069         }
2070         
2071         r = mailimap_fetch_type_new_fetch_att_list_add(fetch_type, fetch_att);
2072         if (r != MAILIMAP_NO_ERROR) {
2073                 mailimap_fetch_att_free(fetch_att);
2074                 res = MAILIMAP_ERROR_MEMORY;
2075                 goto free_fetch_type;
2076         }
2077         
2078         fetch_att = mailimap_fetch_att_new_uid();
2079         if (fetch_att == NULL) {
2080                 res = MAILIMAP_ERROR_MEMORY;
2081                 goto free_fetch_type;
2082         }
2083
2084         r = mailimap_fetch_type_new_fetch_att_list_add(fetch_type, fetch_att);
2085         if (r != MAILIMAP_NO_ERROR) {
2086                 mailimap_fetch_att_free(fetch_att);
2087                 res = MAILIMAP_ERROR_MEMORY;
2088                 goto free_fetch_type;
2089         }
2090
2091         mailstream_logger = imap_logger_fetch;
2092         
2093         r = mailimap_uid_fetch(imap, set,
2094                                fetch_type, &fetch_result);
2095
2096         mailstream_logger = imap_logger_cmd;
2097         mailimap_fetch_type_free(fetch_type);
2098         mailimap_set_free(set);
2099
2100         if (r != MAILIMAP_NO_ERROR) {
2101                 res = r;
2102                 goto err;
2103         }
2104
2105         env_list = NULL;
2106         r = result_to_uid_flags_list(fetch_result, &env_list);
2107         mailimap_fetch_list_free(fetch_result);
2108         
2109         * result = env_list;
2110
2111         return MAILIMAP_NO_ERROR;
2112
2113  free_fetch_type:
2114         mailimap_fetch_type_free(fetch_type);
2115  free_set:
2116         mailimap_set_free(set);
2117  err:
2118         return res;
2119 }
2120
2121
2122
2123 static void fetch_uid_flags_run(struct etpan_thread_op * op)
2124 {
2125         struct fetch_uid_param * param;
2126         struct fetch_uid_result * result;
2127         carray * fetch_result;
2128         int r;
2129         
2130         param = op->param;
2131         result = op->result;
2132
2133         CHECK_IMAP();
2134
2135         fetch_result = NULL;
2136         r = imap_get_messages_flags_list(param->imap, param->first_index,
2137                                          &fetch_result);
2138         
2139         result->error = r;
2140         result->fetch_result = fetch_result;
2141         debug_print("imap fetch_uid run - end %i\n", r);
2142 }
2143
2144 int imap_threaded_fetch_uid_flags(Folder * folder, uint32_t first_index,
2145                                   carray ** fetch_result)
2146 {
2147         struct fetch_uid_param param;
2148         struct fetch_uid_result result;
2149         mailimap * imap;
2150         
2151         debug_print("imap fetch_uid - begin\n");
2152         
2153         imap = get_imap(folder);
2154         param.imap = imap;
2155         param.first_index = first_index;
2156         
2157         mailstream_logger = imap_logger_noop;
2158         log_print(LOG_PROTOCOL, "IMAP4- [fetching flags...]\n");
2159
2160         threaded_run(folder, &param, &result, fetch_uid_flags_run);
2161
2162         mailstream_logger = imap_logger_cmd;
2163
2164         
2165         if (result.error != MAILIMAP_NO_ERROR)
2166                 return result.error;
2167         
2168         debug_print("imap fetch_uid - end\n");
2169         
2170         * fetch_result = result.fetch_result;
2171         
2172         return result.error;
2173 }
2174
2175
2176 void imap_fetch_uid_flags_list_free(carray * uid_flags_list)
2177 {
2178         unsigned int i;
2179         
2180         for(i = 0 ; i < carray_count(uid_flags_list) ; i += 3) {
2181                 void * data;
2182                 
2183                 data = carray_get(uid_flags_list, i);
2184                 free(data);
2185                 data = carray_get(uid_flags_list, i + 1);
2186                 free(data);
2187         }
2188         carray_free(uid_flags_list);
2189 }
2190
2191
2192
2193 static int imap_fetch(mailimap * imap,
2194                       uint32_t msg_index,
2195                       char ** result,
2196                       size_t * result_len)
2197 {
2198         int r;
2199         struct mailimap_set * set;
2200         struct mailimap_fetch_att * fetch_att;
2201         struct mailimap_fetch_type * fetch_type;
2202         clist * fetch_result;
2203         struct mailimap_msg_att * msg_att;
2204         struct mailimap_msg_att_item * msg_att_item;
2205         char * text;
2206         size_t text_length;
2207         int res;
2208         clistiter * cur;
2209         struct mailimap_section * section;
2210
2211         set = mailimap_set_new_single(msg_index);
2212         if (set == NULL) {
2213                 res = MAILIMAP_ERROR_MEMORY;
2214                 goto err;
2215         }
2216
2217         section = mailimap_section_new(NULL);
2218         if (section == NULL) {
2219                 res = MAILIMAP_ERROR_MEMORY;
2220                 goto free_set;
2221         }
2222   
2223         fetch_att = mailimap_fetch_att_new_body_peek_section(section);
2224         if (fetch_att == NULL) {
2225                 mailimap_section_free(section);
2226                 res = MAILIMAP_ERROR_MEMORY;
2227                 goto free_set;
2228         }
2229   
2230         fetch_type = mailimap_fetch_type_new_fetch_att(fetch_att);
2231         if (fetch_type == NULL) {
2232                 res = MAILIMAP_ERROR_MEMORY;
2233                 goto free_fetch_att;
2234         }
2235
2236         mailstream_logger = imap_logger_fetch;
2237         
2238         r = mailimap_uid_fetch(imap, set,
2239                                fetch_type, &fetch_result);
2240   
2241         mailstream_logger = imap_logger_cmd;
2242         
2243         mailimap_fetch_type_free(fetch_type);
2244         mailimap_set_free(set);
2245   
2246         switch (r) {
2247         case MAILIMAP_NO_ERROR:
2248                 break;
2249         default:
2250                 return r;
2251         }
2252   
2253         if (clist_begin(fetch_result) == NULL) {
2254                 mailimap_fetch_list_free(fetch_result);
2255                 return MAILIMAP_ERROR_FETCH;
2256         }
2257
2258         msg_att = clist_begin(fetch_result)->data;
2259
2260         text = NULL;
2261         text_length = 0;
2262
2263         for(cur = clist_begin(msg_att->att_list) ; cur != NULL ;
2264             cur = clist_next(cur)) {
2265                 msg_att_item = clist_content(cur);
2266
2267                 if (msg_att_item->att_type == MAILIMAP_MSG_ATT_ITEM_STATIC) {
2268                         if (msg_att_item->att_data.att_static->att_type ==
2269                             MAILIMAP_MSG_ATT_BODY_SECTION) {
2270                                 text = msg_att_item->att_data.att_static->att_data.att_body_section->sec_body_part;
2271                                 /* detach */
2272                                 msg_att_item->att_data.att_static->att_data.att_body_section->sec_body_part = NULL;
2273                                 text_length =
2274                                         msg_att_item->att_data.att_static->att_data.att_body_section->sec_length;
2275                         }
2276                 }
2277         }
2278
2279         mailimap_fetch_list_free(fetch_result);
2280
2281         if (text == NULL)
2282                 return MAILIMAP_ERROR_FETCH;
2283
2284         * result = text;
2285         * result_len = text_length;
2286   
2287         return MAILIMAP_NO_ERROR;
2288
2289  free_fetch_att:
2290         mailimap_fetch_att_free(fetch_att);
2291  free_set:
2292         mailimap_set_free(set);
2293  err:
2294         return res;
2295 }
2296
2297 static int imap_fetch_header(mailimap * imap,
2298                              uint32_t msg_index,
2299                              char ** result,
2300                              size_t * result_len)
2301 {
2302   int r;
2303   struct mailimap_set * set;
2304   struct mailimap_fetch_att * fetch_att;
2305   struct mailimap_fetch_type * fetch_type;
2306   clist * fetch_result;
2307   struct mailimap_msg_att * msg_att;
2308   struct mailimap_msg_att_item * msg_att_item;
2309   char * text;
2310   size_t text_length;
2311   int res;
2312   clistiter * cur;
2313   struct mailimap_section * section;
2314   
2315   set = mailimap_set_new_single(msg_index);
2316   if (set == NULL) {
2317     res = MAILIMAP_ERROR_MEMORY;
2318     goto err;
2319   }
2320
2321   section = mailimap_section_new_header();
2322   if (section == NULL) {
2323     res = MAILIMAP_ERROR_MEMORY;
2324     goto free_set;
2325   }
2326   
2327   fetch_att = mailimap_fetch_att_new_body_peek_section(section);
2328   if (fetch_att == NULL) {
2329     mailimap_section_free(section);
2330     res = MAILIMAP_ERROR_MEMORY;
2331     goto free_set;
2332   }
2333   
2334   fetch_type = mailimap_fetch_type_new_fetch_att(fetch_att);
2335   if (fetch_type == NULL) {
2336     res = MAILIMAP_ERROR_MEMORY;
2337     goto free_fetch_att;
2338   }
2339
2340   mailstream_logger = imap_logger_fetch;
2341   
2342   r = mailimap_uid_fetch(imap, set, fetch_type, &fetch_result);
2343   
2344   mailstream_logger = imap_logger_cmd;
2345   mailimap_fetch_type_free(fetch_type);
2346   mailimap_set_free(set);
2347
2348   switch (r) {
2349   case MAILIMAP_NO_ERROR:
2350     break;
2351   default:
2352     return r;
2353   }
2354
2355   if (clist_begin(fetch_result) == NULL) {
2356     mailimap_fetch_list_free(fetch_result);
2357     return MAILIMAP_ERROR_FETCH;
2358   }
2359
2360   msg_att = clist_begin(fetch_result)->data;
2361
2362   text = NULL;
2363   text_length = 0;
2364
2365   for(cur = clist_begin(msg_att->att_list) ; cur != NULL ;
2366       cur = clist_next(cur)) {
2367     msg_att_item = clist_content(cur);
2368
2369     if (msg_att_item->att_type == MAILIMAP_MSG_ATT_ITEM_STATIC) {
2370       if (msg_att_item->att_data.att_static->att_type ==
2371           MAILIMAP_MSG_ATT_BODY_SECTION) {
2372         text = msg_att_item->att_data.att_static->att_data.att_body_section->sec_body_part;
2373         msg_att_item->att_data.att_static->att_data.att_body_section->sec_body_part = NULL;
2374         text_length =
2375           msg_att_item->att_data.att_static->att_data.att_body_section->sec_length;
2376       }
2377     }
2378   }
2379
2380   mailimap_fetch_list_free(fetch_result);
2381
2382   if (text == NULL)
2383     return MAILIMAP_ERROR_FETCH;
2384
2385   * result = text;
2386   * result_len = text_length;
2387
2388   return MAILIMAP_NO_ERROR;
2389
2390  free_fetch_att:
2391   mailimap_fetch_att_free(fetch_att);
2392  free_set:
2393   mailimap_set_free(set);
2394  err:
2395   return res;
2396 }
2397
2398
2399
2400 struct fetch_content_param {
2401         mailimap * imap;
2402         uint32_t msg_index;
2403         const char * filename;
2404         int with_body;
2405 };
2406
2407 struct fetch_content_result {
2408         int error;
2409 };
2410
2411 static void fetch_content_run(struct etpan_thread_op * op)
2412 {
2413         struct fetch_content_param * param;
2414         struct fetch_content_result * result;
2415         char * content;
2416         size_t content_size;
2417         int r;
2418         int fd;
2419         FILE * f;
2420         
2421         param = op->param;
2422         result = op->result;
2423
2424         CHECK_IMAP();
2425
2426         content = NULL;
2427         content_size = 0;
2428         if (param->with_body)
2429                 r = imap_fetch(param->imap, param->msg_index,
2430                                &content, &content_size);
2431         else
2432                 r = imap_fetch_header(param->imap, param->msg_index,
2433                                       &content, &content_size);
2434         
2435         result->error = r;
2436         
2437         if (r == MAILIMAP_NO_ERROR) {
2438                 fd = open(param->filename, O_RDWR | O_CREAT, 0600);
2439                 if (fd < 0) {
2440                         result->error = MAILIMAP_ERROR_FETCH;
2441                         goto free;
2442                 }
2443                 
2444                 f = fdopen(fd, "wb");
2445                 if (f == NULL) {
2446                         result->error = MAILIMAP_ERROR_FETCH;
2447                         goto close;
2448                 }
2449                 
2450                 r = fwrite(content, 1, content_size, f);
2451                 if (r < content_size) {
2452                         result->error = MAILIMAP_ERROR_FETCH;
2453                         goto fclose;
2454                 }
2455                 
2456                 r = fclose(f);
2457                 if (r == EOF) {
2458                         result->error = MAILIMAP_ERROR_FETCH;
2459                         goto unlink;
2460                 }
2461                 goto free;
2462                 
2463         fclose:
2464                 fclose(f);
2465                 goto unlink;
2466         close:
2467                 close(fd);
2468         unlink:
2469                 claws_unlink(param->filename);
2470         
2471         free:
2472                 /* mmap_string_unref is a simple free in libetpan
2473                  * when it has MMAP_UNAVAILABLE defined */
2474                 if (mmap_string_unref(content) != 0)
2475                         free(content);
2476         }
2477         
2478         debug_print("imap fetch_content run - end %i\n", result->error);
2479 }
2480
2481 int imap_threaded_fetch_content(Folder * folder, uint32_t msg_index,
2482                                 int with_body,
2483                                 const char * filename)
2484 {
2485         struct fetch_content_param param;
2486         struct fetch_content_result result;
2487         mailimap * imap;
2488         
2489         debug_print("imap fetch_content - begin\n");
2490         
2491         imap = get_imap(folder);
2492         param.imap = imap;
2493         param.msg_index = msg_index;
2494         param.filename = filename;
2495         param.with_body = with_body;
2496         
2497         threaded_run(folder, &param, &result, fetch_content_run);
2498         
2499         if (result.error != MAILIMAP_NO_ERROR)
2500                 return result.error;
2501         
2502         debug_print("imap fetch_content - end\n");
2503         
2504         return result.error;
2505 }
2506
2507
2508
2509 static int imap_flags_to_flags(struct mailimap_msg_att_dynamic * att_dyn, GSList **s_tags)
2510 {
2511         int flags;
2512         clist * flag_list;
2513         clistiter * cur;
2514         GSList *tags = NULL;
2515
2516         flags = MSG_UNREAD;
2517         
2518         flag_list = att_dyn->att_list;
2519         if (flag_list == NULL)
2520                 return flags;
2521         
2522         for(cur = clist_begin(flag_list) ; cur != NULL ;
2523             cur = clist_next(cur)) {
2524                 struct mailimap_flag_fetch * flag_fetch;
2525                         
2526                 flag_fetch = clist_content(cur);
2527                 if (flag_fetch->fl_type == MAILIMAP_FLAG_FETCH_RECENT)
2528                         flags |= MSG_NEW;
2529                 else {
2530                         switch (flag_fetch->fl_flag->fl_type) {
2531                         case MAILIMAP_FLAG_ANSWERED:
2532                                 flags |= MSG_REPLIED;
2533                                 break;
2534                         case MAILIMAP_FLAG_FLAGGED:
2535                                 flags |= MSG_MARKED;
2536                                 break;
2537                         case MAILIMAP_FLAG_DELETED:
2538                                 flags |= MSG_DELETED;
2539                                 break;
2540                         case MAILIMAP_FLAG_SEEN:
2541                                 flags &= ~MSG_UNREAD;
2542                                 flags &= ~MSG_NEW;
2543                                 break;
2544                         case MAILIMAP_FLAG_KEYWORD:
2545                                 if (!strcasecmp(flag_fetch->fl_flag->fl_data.fl_keyword, "$Forwarded"))
2546                                         flags |= MSG_FORWARDED;
2547                                 else if (!strcasecmp(flag_fetch->fl_flag->fl_data.fl_keyword, "Junk")) 
2548                                         flags |= MSG_SPAM;
2549                                 else if (!strcasecmp(flag_fetch->fl_flag->fl_data.fl_keyword, "NonJunk") ||
2550                                          !strcasecmp(flag_fetch->fl_flag->fl_data.fl_keyword, "NoJunk") ||
2551                                          !strcasecmp(flag_fetch->fl_flag->fl_data.fl_keyword, "NotJunk")) 
2552                                         flags &= ~MSG_SPAM;
2553                                 else if (s_tags)
2554                                         tags = g_slist_prepend(tags, g_strdup(flag_fetch->fl_flag->fl_data.fl_keyword));
2555                                 break;
2556                         }
2557                 }
2558         }
2559         if (s_tags)
2560                 *s_tags = tags;
2561         return flags;
2562 }
2563
2564 static int imap_get_msg_att_info(struct mailimap_msg_att * msg_att,
2565                                  uint32_t * puid,
2566                                  char ** pheaders,
2567                                  size_t * pref_size,
2568                                  struct mailimap_msg_att_dynamic ** patt_dyn)
2569 {
2570   clistiter * item_cur;
2571   uint32_t uid;
2572   char * headers;
2573   size_t ref_size;
2574   struct mailimap_msg_att_dynamic * att_dyn;
2575
2576   uid = 0;
2577   headers = NULL;
2578   ref_size = 0;
2579   att_dyn = NULL;
2580
2581   for(item_cur = clist_begin(msg_att->att_list) ; item_cur != NULL ;
2582       item_cur = clist_next(item_cur)) {
2583     struct mailimap_msg_att_item * item;
2584
2585     item = clist_content(item_cur);
2586       
2587     switch (item->att_type) {
2588     case MAILIMAP_MSG_ATT_ITEM_STATIC:
2589       switch (item->att_data.att_static->att_type) {
2590       case MAILIMAP_MSG_ATT_UID:
2591         uid = item->att_data.att_static->att_data.att_uid;
2592         break;
2593
2594       case MAILIMAP_MSG_ATT_BODY_SECTION:
2595         if (headers == NULL) {
2596           headers = item->att_data.att_static->att_data.att_body_section->sec_body_part;
2597         }
2598         break;
2599       case MAILIMAP_MSG_ATT_RFC822_SIZE:
2600               ref_size = item->att_data.att_static->att_data.att_rfc822_size;
2601               break;
2602       }
2603       break;
2604       
2605     case MAILIMAP_MSG_ATT_ITEM_DYNAMIC:
2606       if (att_dyn == NULL) {
2607         att_dyn = item->att_data.att_dyn;
2608       }
2609       break;
2610     }
2611   }
2612
2613   if (puid != NULL)
2614     * puid = uid;
2615   if (pheaders != NULL)
2616     * pheaders = headers;
2617   if (pref_size != NULL)
2618     * pref_size = ref_size;
2619   if (patt_dyn != NULL)
2620     * patt_dyn = att_dyn;
2621
2622   return MAIL_NO_ERROR;
2623 }
2624
2625 static struct imap_fetch_env_info *
2626 fetch_to_env_info(struct mailimap_msg_att * msg_att, GSList **tags)
2627 {
2628         struct imap_fetch_env_info * info;
2629         uint32_t uid;
2630         char * headers;
2631         size_t size;
2632         struct mailimap_msg_att_dynamic * att_dyn;
2633
2634         imap_get_msg_att_info(msg_att, &uid, &headers, &size,
2635                               &att_dyn);
2636         
2637         if (!headers)
2638                 return NULL;
2639         info = malloc(sizeof(* info));
2640         info->uid = uid;
2641         info->headers = strdup(headers);
2642         info->size = size;
2643         info->flags = imap_flags_to_flags(att_dyn, tags);
2644         
2645         return info;
2646 }
2647
2648 static int
2649 imap_fetch_result_to_envelop_list(clist * fetch_result,
2650                                   carray ** p_env_list)
2651 {
2652         clistiter * cur;
2653         unsigned int i;
2654         carray * env_list;
2655
2656         i = 0;
2657         env_list = carray_new(16);
2658   
2659         for(cur = clist_begin(fetch_result) ; cur != NULL ;
2660             cur = clist_next(cur)) {
2661                 struct mailimap_msg_att * msg_att;
2662                 struct imap_fetch_env_info * env_info;
2663                 GSList *tags = NULL;
2664
2665                 msg_att = clist_content(cur);
2666
2667                 env_info = fetch_to_env_info(msg_att, &tags);
2668                 if (!env_info)
2669                         return MAILIMAP_ERROR_MEMORY;
2670                 carray_add(env_list, env_info, NULL);
2671                 carray_add(env_list, tags, NULL);
2672         }
2673   
2674         * p_env_list = env_list;
2675   
2676         return MAIL_NO_ERROR;
2677 }
2678
2679 static int imap_add_envelope_fetch_att(struct mailimap_fetch_type * fetch_type)
2680 {
2681         struct mailimap_fetch_att * fetch_att;
2682         int r;
2683         char * header;
2684         clist * hdrlist;
2685         struct mailimap_header_list * imap_hdrlist;
2686         struct mailimap_section * section;
2687
2688         hdrlist = clist_new();
2689   
2690         header = strdup("Date");
2691         r = clist_append(hdrlist, header);
2692         header = strdup("From");
2693         r = clist_append(hdrlist, header);
2694         header = strdup("To");
2695         r = clist_append(hdrlist, header);
2696         header = strdup("Cc");
2697         r = clist_append(hdrlist, header);
2698         header = strdup("Subject");
2699         r = clist_append(hdrlist, header);
2700         header = strdup("Message-ID");
2701         r = clist_append(hdrlist, header);
2702         header = strdup("References");
2703         r = clist_append(hdrlist, header);
2704         header = strdup("In-Reply-To");
2705         r = clist_append(hdrlist, header);
2706   
2707         imap_hdrlist = mailimap_header_list_new(hdrlist);
2708         section = mailimap_section_new_header_fields(imap_hdrlist);
2709         fetch_att = mailimap_fetch_att_new_body_peek_section(section);
2710         mailimap_fetch_type_new_fetch_att_list_add(fetch_type, fetch_att);
2711   
2712         return MAIL_NO_ERROR;
2713 }
2714
2715 static int imap_add_header_fetch_att(struct mailimap_fetch_type * fetch_type)
2716 {
2717         struct mailimap_fetch_att * fetch_att;
2718         struct mailimap_section * section;
2719         
2720         section = mailimap_section_new_header();
2721         fetch_att = mailimap_fetch_att_new_body_peek_section(section);
2722         mailimap_fetch_type_new_fetch_att_list_add(fetch_type, fetch_att);
2723         
2724         return MAIL_NO_ERROR;
2725 }
2726
2727 static int
2728 imap_get_envelopes_list(mailimap * imap, struct mailimap_set * set,
2729                         carray ** p_env_list)
2730 {
2731         struct mailimap_fetch_att * fetch_att;
2732         struct mailimap_fetch_type * fetch_type;
2733         int res;
2734         clist * fetch_result;
2735         int r;
2736         carray * env_list = NULL;
2737         chashdatum key;
2738         chashdatum value;
2739         
2740         fetch_type = mailimap_fetch_type_new_fetch_att_list_empty();
2741   
2742         /* uid */
2743         fetch_att = mailimap_fetch_att_new_uid();
2744         r = mailimap_fetch_type_new_fetch_att_list_add(fetch_type, fetch_att);
2745   
2746         /* flags */
2747         fetch_att = mailimap_fetch_att_new_flags();
2748         r = mailimap_fetch_type_new_fetch_att_list_add(fetch_type, fetch_att);
2749   
2750         /* rfc822 size */
2751         fetch_att = mailimap_fetch_att_new_rfc822_size();
2752         r = mailimap_fetch_type_new_fetch_att_list_add(fetch_type, fetch_att);
2753   
2754         /* headers */
2755         key.data = &imap;
2756         key.len = sizeof(imap);
2757         r = chash_get(courier_workaround_hash, &key, &value);
2758         if (r < 0)
2759                 r = imap_add_envelope_fetch_att(fetch_type);
2760         else
2761                 r = imap_add_header_fetch_att(fetch_type);
2762         
2763         mailstream_logger = imap_logger_fetch;
2764         
2765         r = mailimap_uid_fetch(imap, set, fetch_type, &fetch_result);
2766         
2767         mailstream_logger = imap_logger_cmd;
2768         switch (r) {
2769         case MAILIMAP_NO_ERROR:
2770                 break;
2771         default:
2772                 mailimap_fetch_type_free(fetch_type);
2773                 debug_print("uid_fetch: %d\n", r);
2774                 return r;
2775         }
2776         
2777         if (clist_begin(fetch_result) == NULL) {
2778                 res = MAILIMAP_ERROR_FETCH;
2779                 debug_print("clist_begin = NULL\n");
2780                 goto err;
2781         }
2782         
2783         r = imap_fetch_result_to_envelop_list(fetch_result, &env_list);
2784         mailimap_fetch_list_free(fetch_result);
2785         
2786         if (r != MAILIMAP_NO_ERROR) {
2787                 mailimap_fetch_type_free(fetch_type);
2788                 res = MAILIMAP_ERROR_MEMORY;
2789                 debug_print("fetch_result_to_envelop_list: %d\n", res);
2790                 goto err;
2791         }
2792         
2793         mailimap_fetch_type_free(fetch_type);
2794         
2795         * p_env_list = env_list;
2796         
2797         return MAILIMAP_NO_ERROR;
2798   
2799  err:
2800         return res;
2801 }
2802
2803 struct fetch_env_param {
2804         mailimap * imap;
2805         struct mailimap_set * set;
2806 };
2807
2808 struct fetch_env_result {
2809         carray * fetch_env_result;
2810         int error;
2811 };
2812
2813 static void fetch_env_run(struct etpan_thread_op * op)
2814 {
2815         struct fetch_env_param * param;
2816         struct fetch_env_result * result;
2817         carray * env_list;
2818         int r;
2819         
2820         param = op->param;
2821         result = op->result;
2822
2823         CHECK_IMAP();
2824
2825         env_list = NULL;
2826         r = imap_get_envelopes_list(param->imap, param->set,
2827                                     &env_list);
2828         
2829         result->error = r;
2830         result->fetch_env_result = env_list;
2831         
2832         debug_print("imap fetch_env run - end %i\n", r);
2833 }
2834
2835 int imap_threaded_fetch_env(Folder * folder, struct mailimap_set * set,
2836                             carray ** p_env_list)
2837 {
2838         struct fetch_env_param param;
2839         struct fetch_env_result result;
2840         mailimap * imap;
2841         
2842         debug_print("imap fetch_env - begin\n");
2843         
2844         imap = get_imap(folder);
2845         param.imap = imap;
2846         param.set = set;
2847         
2848         threaded_run(folder, &param, &result, fetch_env_run);
2849         
2850         if (result.error != MAILIMAP_NO_ERROR) {
2851                 chashdatum key;
2852                 chashdatum value;
2853                 int r;
2854                 
2855                 key.data = &imap;
2856                 key.len = sizeof(imap);
2857                 r = chash_get(courier_workaround_hash, &key, &value);
2858                 if (r < 0) {
2859                         value.data = NULL;
2860                         value.len = 0;
2861                         chash_set(courier_workaround_hash, &key, &value, NULL);
2862                         
2863                         threaded_run(folder, &param, &result, fetch_env_run);
2864                 }
2865         }
2866         
2867         if (result.error != MAILIMAP_NO_ERROR)
2868                 return result.error;
2869         
2870         debug_print("imap fetch_env - end\n");
2871         
2872         * p_env_list = result.fetch_env_result;
2873         
2874         return result.error;
2875 }
2876
2877 void imap_fetch_env_free(carray * env_list)
2878 {
2879         unsigned int i;
2880         
2881         for(i = 0 ; i < carray_count(env_list) ; i += 2) {
2882                 struct imap_fetch_env_info * env_info;
2883                 
2884                 env_info = carray_get(env_list, i);
2885                 free(env_info->headers);
2886                 free(env_info);
2887         }
2888         carray_free(env_list);
2889 }
2890
2891
2892
2893
2894
2895 struct append_param {
2896         mailimap * imap;
2897         const char * mailbox;
2898         const char * filename;
2899         struct mailimap_flag_list * flag_list;
2900 };
2901
2902 struct append_result {
2903         int error;
2904         int uid;
2905 };
2906
2907 static void append_run(struct etpan_thread_op * op)
2908 {
2909         struct append_param * param;
2910         struct append_result * result;
2911         int r;
2912         char * data;
2913         size_t size;
2914 #ifndef G_OS_WIN32
2915         struct stat stat_buf;
2916         int fd;
2917 #endif
2918         guint32 uid = 0, val = 0;
2919         
2920         param = op->param;
2921         result = op->result;
2922         
2923         CHECK_IMAP();
2924
2925 #ifndef G_OS_WIN32
2926         r = stat(param->filename, &stat_buf);
2927         if (r < 0) {
2928                 result->error = MAILIMAP_ERROR_APPEND;
2929                 return;
2930         }
2931         size = stat_buf.st_size;
2932         
2933         fd = open(param->filename, O_RDONLY);
2934         if (fd < 0) {
2935                 result->error = MAILIMAP_ERROR_APPEND;
2936                 return;
2937         }
2938         
2939         data = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0);
2940         if (data == (void *) MAP_FAILED) {
2941                 close(fd);
2942                 result->error = MAILIMAP_ERROR_APPEND;
2943                 return;
2944         }
2945 #else
2946         data = file_read_to_str_no_recode(param->filename);
2947         if (data == NULL) {
2948                 result->error = MAILIMAP_ERROR_APPEND;
2949                 return;
2950         }
2951         size = strlen(data);
2952 #endif
2953         mailstream_logger = imap_logger_append;
2954         
2955         r = mailimap_uidplus_append(param->imap, param->mailbox,
2956                             param->flag_list, NULL,
2957                             data, size, &val, &uid);
2958
2959         mailstream_logger = imap_logger_cmd;
2960         
2961 #ifndef G_OS_WIN32
2962         munmap(data, size);
2963         close(fd);
2964 #else
2965         g_free(data);
2966 #endif
2967         
2968         result->error = r;
2969         result->uid = uid;
2970         debug_print("imap append run - end %i uid %d\n", r, uid);
2971 }
2972
2973 int imap_threaded_append(Folder * folder, const char * mailbox,
2974                          const char * filename,
2975                          struct mailimap_flag_list * flag_list,
2976                          int *uid)
2977 {
2978         struct append_param param;
2979         struct append_result result;
2980         mailimap * imap;
2981         
2982         debug_print("imap append - begin\n");
2983         
2984         imap = get_imap(folder);
2985         param.imap = imap;
2986         param.mailbox = mailbox;
2987         param.filename = filename;
2988         param.flag_list = flag_list;
2989         
2990         threaded_run(folder, &param, &result, append_run);
2991         
2992         if (result.error != MAILIMAP_NO_ERROR)
2993                 return result.error;
2994         
2995         debug_print("imap append - end\n");
2996         if (uid != NULL)
2997                 *uid = result.uid;
2998
2999         return result.error;
3000 }
3001
3002
3003
3004
3005 struct expunge_param {
3006         mailimap * imap;
3007 };
3008
3009 struct expunge_result {
3010         int error;
3011 };
3012
3013 static void expunge_run(struct etpan_thread_op * op)
3014 {
3015         struct expunge_param * param;
3016         struct expunge_result * result;
3017         int r;
3018         
3019         param = op->param;
3020         result = op->result;
3021
3022         CHECK_IMAP();
3023
3024         r = mailimap_expunge(param->imap);
3025         
3026         result->error = r;
3027         debug_print("imap expunge run - end %i\n", r);
3028 }
3029
3030 int imap_threaded_expunge(Folder * folder)
3031 {
3032         struct expunge_param param;
3033         struct expunge_result result;
3034         
3035         debug_print("imap expunge - begin\n");
3036         
3037         param.imap = get_imap(folder);
3038         
3039         threaded_run(folder, &param, &result, expunge_run);
3040         
3041         debug_print("imap expunge - end\n");
3042         
3043         return result.error;
3044 }
3045
3046
3047 struct copy_param {
3048         mailimap * imap;
3049         struct mailimap_set * set;
3050         const char * mb;
3051 };
3052
3053 struct copy_result {
3054         int error;
3055         struct mailimap_set *source;
3056         struct mailimap_set *dest;
3057 };
3058
3059 static void copy_run(struct etpan_thread_op * op)
3060 {
3061         struct copy_param * param;
3062         struct copy_result * result;
3063         int r;
3064         guint32 val;
3065         struct mailimap_set *source = NULL, *dest = NULL;
3066
3067         param = op->param;
3068         result = op->result;
3069
3070         CHECK_IMAP();
3071
3072         r = mailimap_uidplus_uid_copy(param->imap, param->set, param->mb,
3073                 &val, &source, &dest);
3074         
3075         result->error = r;
3076         if (r == 0) {
3077                 result->source = source;
3078                 result->dest = dest;
3079         } else {
3080                 result->source = NULL;
3081                 result->dest = NULL;
3082         }
3083         debug_print("imap copy run - end %i\n", r);
3084 }
3085
3086 int imap_threaded_copy(Folder * folder, struct mailimap_set * set,
3087                        const char * mb, struct mailimap_set **source,
3088                        struct mailimap_set **dest)
3089 {
3090         struct copy_param param;
3091         struct copy_result result;
3092         mailimap * imap;
3093         
3094         debug_print("imap copy - begin\n");
3095         
3096         imap = get_imap(folder);
3097         param.imap = imap;
3098         param.set = set;
3099         param.mb = mb;
3100         
3101         threaded_run(folder, &param, &result, copy_run);
3102         *source = NULL;
3103         *dest = NULL;
3104         
3105         if (result.error != MAILIMAP_NO_ERROR)
3106                 return result.error;
3107         
3108         *source = result.source;
3109         *dest = result.dest;
3110
3111         debug_print("imap copy - end\n");
3112         
3113         return result.error;
3114 }
3115
3116
3117
3118 struct store_param {
3119         mailimap * imap;
3120         struct mailimap_set * set;
3121         struct mailimap_store_att_flags * store_att_flags;
3122 };
3123
3124 struct store_result {
3125         int error;
3126 };
3127
3128 static void store_run(struct etpan_thread_op * op)
3129 {
3130         struct store_param * param;
3131         struct store_result * result;
3132         int r;
3133         
3134         param = op->param;
3135         result = op->result;
3136
3137         CHECK_IMAP();
3138
3139         r = mailimap_uid_store(param->imap, param->set,
3140                                param->store_att_flags);
3141         
3142         result->error = r;
3143         
3144         debug_print("imap store run - end %i\n", r);
3145 }
3146
3147 int imap_threaded_store(Folder * folder, struct mailimap_set * set,
3148                         struct mailimap_store_att_flags * store_att_flags)
3149 {
3150         struct store_param param;
3151         struct store_result result;
3152         mailimap * imap;
3153         
3154         debug_print("imap store - begin\n");
3155         
3156         imap = get_imap(folder);
3157         param.imap = imap;
3158         param.set = set;
3159         param.store_att_flags = store_att_flags;
3160         
3161         threaded_run(folder, &param, &result, store_run);
3162         
3163         if (result.error != MAILIMAP_NO_ERROR)
3164                 return result.error;
3165         
3166         debug_print("imap store - end\n");
3167         
3168         return result.error;
3169 }
3170
3171
3172 #define ENV_BUFFER_SIZE 512
3173 #ifndef G_OS_WIN32
3174 static void do_exec_command(int fd, const char * command,
3175                             const char * servername, uint16_t port)
3176 {
3177         int i, maxopen;
3178 #ifdef SOLARIS
3179         char env_buffer[ENV_BUFFER_SIZE];
3180 #endif
3181         
3182         if (fork() > 0) {
3183                 /* Fork again to become a child of init rather than
3184                    the etpan client. */
3185                 exit(0);
3186         }
3187   
3188         if (servername)
3189                 g_setenv("ETPANSERVER", servername, TRUE);
3190         else
3191                 g_unsetenv("ETPANSERVER");
3192   
3193         if (port) {
3194                 char porttext[20];
3195                 
3196                 snprintf(porttext, sizeof(porttext), "%d", port);
3197                 g_setenv("ETPANPORT", porttext, TRUE);
3198         }
3199         else {
3200                 g_unsetenv("ETPANPORT");
3201         }
3202                 
3203         /* Not a lot we can do if there's an error other than bail. */
3204         if (dup2(fd, 0) == -1)
3205                 exit(1);
3206         if (dup2(fd, 1) == -1)
3207                 exit(1);
3208   
3209         /* Should we close stderr and reopen /dev/null? */
3210   
3211         maxopen = sysconf(_SC_OPEN_MAX);
3212         for (i=3; i < maxopen; i++)
3213                 close(i);
3214   
3215 #ifdef TIOCNOTTY
3216         /* Detach from the controlling tty if we have one. Otherwise,
3217            SSH might do something stupid like trying to use it instead
3218            of running $SSH_ASKPASS. Doh. */
3219         fd = open("/dev/tty", O_RDONLY);
3220         if (fd != -1) {
3221                 ioctl(fd, TIOCNOTTY, NULL);
3222                 close(fd);
3223         }
3224 #endif /* TIOCNOTTY */
3225
3226         execl("/bin/sh", "/bin/sh", "-c", command, NULL);
3227   
3228         /* Eep. Shouldn't reach this */
3229         exit(1);
3230 }
3231
3232 static int subcommand_connect(const char *command,
3233                               const char *servername, uint16_t port)
3234 {
3235         /* SEB unsupported on Windows */
3236         int sockfds[2];
3237         pid_t childpid;
3238   
3239         if (socketpair(AF_UNIX, SOCK_STREAM, 0, sockfds))
3240                 return -1;
3241   
3242         childpid = fork();
3243         if (!childpid) {
3244                 do_exec_command(sockfds[1], command, servername, port);
3245         }
3246         else if (childpid == -1) {
3247                 close(sockfds[0]);
3248                 close(sockfds[1]);
3249                 return -1;
3250         }
3251   
3252         close(sockfds[1]);
3253   
3254         /* Reap child, leaving grandchild process to run */
3255         waitpid(childpid, NULL, 0);
3256   
3257         return sockfds[0];
3258 }
3259
3260 static int socket_connect_cmd(mailimap * imap, const char * command,
3261                        const char * server, int port)
3262 {
3263         int fd;
3264         mailstream * s;
3265         int r;
3266         
3267         fd = subcommand_connect(command, server, port);
3268         if (fd < 0)
3269                 return MAILIMAP_ERROR_STREAM;
3270         
3271         s = mailstream_socket_open(fd);
3272         if (s == NULL) {
3273                 close(fd);
3274                 return MAILIMAP_ERROR_STREAM;
3275         }
3276         
3277         r = mailimap_connect(imap, s);
3278         if (r != MAILIMAP_NO_ERROR_AUTHENTICATED
3279         &&  r != MAILIMAP_NO_ERROR_NON_AUTHENTICATED) {
3280                 mailstream_close(s);
3281                 if (imap)
3282                         imap->imap_stream = NULL;
3283                 return r;
3284         }
3285         
3286         return r;
3287 }
3288
3289 /* connect cmd */
3290
3291 struct connect_cmd_param {
3292         mailimap * imap;
3293         const char * command;
3294         const char * server;
3295         int port;
3296 };
3297
3298 struct connect_cmd_result {
3299         int error;
3300 };
3301
3302 static void connect_cmd_run(struct etpan_thread_op * op)
3303 {
3304         int r;
3305         struct connect_cmd_param * param;
3306         struct connect_cmd_result * result;
3307         
3308         param = op->param;
3309         result = op->result;
3310         
3311         CHECK_IMAP();
3312
3313         r = socket_connect_cmd(param->imap, param->command,
3314                                param->server, param->port);
3315         
3316         result->error = r;
3317 }
3318
3319
3320 int imap_threaded_connect_cmd(Folder * folder, const char * command,
3321                               const char * server, int port)
3322 {
3323         struct connect_cmd_param param;
3324         struct connect_cmd_result result;
3325         chashdatum key;
3326         chashdatum value;
3327         mailimap * imap, * oldimap;
3328         
3329         oldimap = get_imap(folder);
3330
3331         imap = mailimap_new(0, NULL);
3332         
3333         if (oldimap) {
3334                 debug_print("deleting old imap %p\n", oldimap);
3335                 delete_imap(folder, oldimap);
3336         }
3337
3338         key.data = &folder;
3339         key.len = sizeof(folder);
3340         value.data = imap;
3341         value.len = 0;
3342         chash_set(session_hash, &key, &value, NULL);
3343         
3344         param.imap = imap;
3345         param.command = command;
3346         param.server = server;
3347         param.port = port;
3348         
3349         threaded_run(folder, &param, &result, connect_cmd_run);
3350         
3351         debug_print("connect_cmd ok %i with imap %p\n", result.error, imap);
3352         
3353         return result.error;
3354 }
3355 #endif /* G_OS_WIN32 */
3356
3357 void imap_threaded_cancel(Folder * folder)
3358 {
3359         mailimap * imap;
3360         
3361         imap = get_imap(folder);
3362         if (imap->imap_stream != NULL)
3363                 mailstream_cancel(imap->imap_stream);
3364 }
3365
3366 #else
3367
3368 void imap_main_init(void)
3369 {
3370 }
3371 void imap_main_done(void)
3372 {
3373 }
3374 void imap_main_set_timeout(int sec)
3375 {
3376 }
3377
3378 void imap_threaded_cancel(Folder * folder);
3379 {
3380 }
3381
3382 #endif