0266ce88f0ff3c7e39b3e2ec6ff0c7796825b7ff
[claws.git] / src / etpan / imap-thread.c
1 /*
2  * Claws Mail -- a GTK+ based, lightweight, and fast e-mail client
3  * Copyright (C) 2005-2012 DINH Viet Hoa and the Claws Mail team
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 3 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program. If not, see <http://www.gnu.org/licenses/>.
17  * 
18  */
19
20 #ifdef HAVE_CONFIG_H
21 #  include "config.h"
22 #include "claws-features.h"
23 #endif
24
25 #ifdef HAVE_LIBETPAN
26
27 #include <glib.h>
28 #include <glib/gi18n.h>
29 #include "imap-thread.h"
30 #include <imap.h>
31 #include <sys/types.h>
32 #include <sys/stat.h>
33 #if (defined(__DragonFly__) || defined (__NetBSD__) || defined (__FreeBSD__) || defined (__OpenBSD__) || defined (__CYGWIN__))
34 #include <sys/socket.h>
35 #endif
36 #include <fcntl.h>
37 #ifndef G_OS_WIN32
38 #include <sys/mman.h>
39 #include <sys/wait.h>
40 #endif
41 #include <gtk/gtk.h>
42 #include <log.h>
43 #include "etpan-thread-manager.h"
44 #include "utils.h"
45 #include "mainwindow.h"
46 #include "ssl.h"
47 #include "ssl_certificate.h"
48 #include "socket.h"
49 #include "remotefolder.h"
50 #include "tags.h"
51
52 #define DISABLE_LOG_DURING_LOGIN
53
54 static struct etpan_thread_manager * thread_manager = NULL;
55 static chash * courier_workaround_hash = NULL;
56 static chash * imap_hash = NULL;
57 static chash * session_hash = NULL;
58 static guint thread_manager_signal = 0;
59 static GIOChannel * io_channel = NULL;
60
61 static void delete_imap(Folder *folder, mailimap *imap)
62 {
63         chashdatum key;
64         chashdatum value;
65
66         key.data = &folder;
67         key.len = sizeof(folder);
68         value.data = imap;
69         value.len = 0;
70         chash_delete(session_hash, &key, NULL);
71         
72         key.data = &imap;
73         key.len = sizeof(imap);
74         chash_delete(courier_workaround_hash, &key, NULL);
75         if (imap && imap->imap_stream) {
76                 /* we don't want libetpan to logout */
77                 mailstream_close(imap->imap_stream);
78                 imap->imap_stream = NULL;
79         }
80         debug_print("removing mailimap %p\n", imap);
81         mailimap_free(imap);    
82 }
83
84 static gboolean thread_manager_event(GIOChannel * source,
85     GIOCondition condition,
86     gpointer data)
87 {
88 #ifdef G_OS_WIN32
89         gsize bytes_read;
90         gchar ch;
91         
92         if (condition & G_IO_IN)
93                 g_io_channel_read_chars(source, &ch, 1, &bytes_read, NULL);
94 #endif
95         etpan_thread_manager_loop(thread_manager);
96         
97         return TRUE;
98 }
99
100 static void imap_logger_noop(int direction, const char * str, size_t size) 
101 {
102         /* inhibit logging */
103 }
104
105 static void imap_logger_cmd(int direction, const char * str, size_t size) 
106 {
107         gchar *buf;
108         gchar **lines;
109         int i = 0;
110
111         if (size > 8192) {
112                 log_print(LOG_PROTOCOL, "IMAP4%c [CMD data - %zd bytes]\n", direction?'>':'<', size);
113                 return;
114         }
115         buf = malloc(size+1);
116         memset(buf, 0, size+1);
117         strncpy(buf, str, size);
118         buf[size] = '\0';
119
120         if (!strncmp(buf, "<<<<<<<", 7) 
121         ||  !strncmp(buf, ">>>>>>>", 7)) {
122                 free(buf);
123                 return;
124         }
125         while (strstr(buf, "\r"))
126                 *strstr(buf, "\r") = ' ';
127         while (strlen(buf) > 0 && buf[strlen(buf)-1] == '\n')
128                 buf[strlen(buf)-1] = '\0';
129
130         lines = g_strsplit(buf, "\n", -1);
131
132         while (lines[i] && *lines[i]) {
133                 log_print(LOG_PROTOCOL, "IMAP4%c %s\n", direction?'>':'<', lines[i]);
134                 i++;
135         }
136         g_strfreev(lines);
137         free(buf);
138 }
139
140 static void imap_logger_fetch(int direction, const char * str, size_t size) 
141 {
142         gchar *buf;
143         gchar **lines;
144         int i = 0;
145
146         if (size > 128 && !direction) {
147                 log_print(LOG_PROTOCOL, "IMAP4%c [FETCH data - %zd bytes]\n", direction?'>':'<', size);
148                 return;
149         }
150         
151         buf = malloc(size+1);
152         memset(buf, 0, size+1);
153         strncpy(buf, str, size);
154         buf[size] = '\0';
155         if (!strncmp(buf, "<<<<<<<", 7) 
156         ||  !strncmp(buf, ">>>>>>>", 7)) {
157                 free(buf);
158                 return;
159         }
160         while (strstr(buf, "\r"))
161                 *strstr(buf, "\r") = ' ';
162         while (strlen(buf) > 0 && buf[strlen(buf)-1] == '\n')
163                 buf[strlen(buf)-1] = '\0';
164
165         lines = g_strsplit(buf, "\n", -1);
166
167         if (direction != 0 || (buf[0] == '*' && buf[1] == ' ') || size < 32) {
168                 while (lines[i] && *lines[i]) {
169                         log_print(LOG_PROTOCOL, "IMAP4%c %s\n", direction?'>':'<', lines[i]);
170                         i++;
171                 }
172         } else {
173                 log_print(LOG_PROTOCOL, "IMAP4%c [data - %zd bytes]\n", direction?'>':'<', size);
174         }
175         g_strfreev(lines);
176         free(buf);
177 }
178
179 static void imap_logger_uid(int direction, const char * str, size_t size) 
180 {
181         gchar *buf;
182         gchar **lines;
183         int i = 0;
184
185         if (size > 8192) {
186                 log_print(LOG_PROTOCOL, "IMAP4%c [UID data - %zd bytes]\n", direction?'>':'<', size);
187                 return;
188         }
189         buf = malloc(size+1);
190         memset(buf, 0, size+1);
191         strncpy(buf, str, size);
192         buf[size] = '\0';
193         if (!strncmp(buf, "<<<<<<<", 7) 
194         ||  !strncmp(buf, ">>>>>>>", 7)) {
195                 free(buf);
196                 return;
197         }
198         while (strstr(buf, "\r"))
199                 *strstr(buf, "\r") = ' ';
200         while (strlen(buf) > 0 && buf[strlen(buf)-1] == '\n')
201                 buf[strlen(buf)-1] = '\0';
202
203         lines = g_strsplit(buf, "\n", -1);
204
205         while (lines[i] && *lines[i]) {
206                 int llen = strlen(lines[i]);
207                 if (llen < 64)
208                         log_print(LOG_PROTOCOL, "IMAP4%c %s\n", direction?'>':'<', lines[i]);
209                 else {
210                         gchar tmp[64];
211                         strncpy2(tmp, lines[i], 63);
212                         log_print(LOG_PROTOCOL, "IMAP4%c %s[... - %d bytes more]\n", direction?'>':'<', tmp,
213                                   llen-64);
214                 }
215                 i++;
216         }
217         g_strfreev(lines);
218         free(buf);
219 }
220
221 static void imap_logger_append(int direction, const char * str, size_t size) 
222 {
223         gchar *buf;
224         gchar **lines;
225         int i = 0;
226
227         if (size > 8192) {
228                 log_print(LOG_PROTOCOL, "IMAP4%c [APPEND data - %zd bytes]\n", direction?'>':'<', size);
229                 return;
230         } else if (direction == 0 && size > 64) {
231                 log_print(LOG_PROTOCOL, "IMAP4%c [APPEND data - %zd bytes]\n", direction?'>':'<', size);
232                 return;
233         } 
234         buf = malloc(size+1);
235         memset(buf, 0, size+1);
236         strncpy(buf, str, size);
237         buf[size] = '\0';
238         if (!strncmp(buf, "<<<<<<<", 7) 
239         ||  !strncmp(buf, ">>>>>>>", 7)) {
240                 free(buf);
241                 return;
242         }
243         while (strstr(buf, "\r"))
244                 *strstr(buf, "\r") = ' ';
245         while (strlen(buf) > 0 && buf[strlen(buf)-1] == '\n')
246                 buf[strlen(buf)-1] = '\0';
247
248         lines = g_strsplit(buf, "\n", -1);
249
250         if (direction == 0 || (buf[0] == '*' && buf[1] == ' ') || size < 64) {
251                 while (lines[i] && *lines[i]) {
252                         log_print(LOG_PROTOCOL, "IMAP4%c %s\n", direction?'>':'<', lines[i]);
253                         i++;
254                 }
255         } else {
256                 log_print(LOG_PROTOCOL, "IMAP4%c [data - %zd bytes]\n", direction?'>':'<', size);
257         }
258         g_strfreev(lines);
259         free(buf);
260 }
261
262 #define ETPAN_DEFAULT_NETWORK_TIMEOUT 60
263 gboolean etpan_skip_ssl_cert_check = FALSE;
264 extern void mailsasl_ref(void);
265
266 void imap_main_init(gboolean skip_ssl_cert_check)
267 {
268         int fd_thread_manager;
269         
270         etpan_skip_ssl_cert_check = skip_ssl_cert_check;
271         mailstream_network_delay.tv_sec = ETPAN_DEFAULT_NETWORK_TIMEOUT;
272         mailstream_network_delay.tv_usec = 0;
273         
274         mailstream_debug = 1;
275         mailstream_logger = imap_logger_cmd;
276         mailsasl_ref();
277         
278         imap_hash = chash_new(CHASH_COPYKEY, CHASH_DEFAULTSIZE);
279         session_hash = chash_new(CHASH_COPYKEY, CHASH_DEFAULTSIZE);
280         courier_workaround_hash = chash_new(CHASH_COPYKEY, CHASH_DEFAULTSIZE);
281         
282         thread_manager = etpan_thread_manager_new();
283         
284         fd_thread_manager = etpan_thread_manager_get_fd(thread_manager);
285         
286 #ifndef G_OS_WIN32
287         io_channel = g_io_channel_unix_new(fd_thread_manager);
288 #else
289         io_channel = g_io_channel_win32_new_fd(fd_thread_manager);
290 #endif
291         thread_manager_signal = g_io_add_watch_full(io_channel, 0, G_IO_IN,
292                                                     thread_manager_event,
293                                                     (gpointer) NULL,
294                                                     NULL);
295 }
296
297 void imap_main_set_timeout(int sec)
298 {
299         mailstream_network_delay.tv_sec = sec;
300         mailstream_network_delay.tv_usec = 0;
301 }
302
303 void imap_main_done(gboolean have_connectivity)
304 {
305         imap_disconnect_all(have_connectivity);
306         etpan_thread_manager_stop(thread_manager);
307 #if defined(__NetBSD__) || defined(__OpenBSD__) || defined(__FreeBSD__)
308         return;
309 #endif
310         etpan_thread_manager_join(thread_manager);
311         
312         g_source_remove(thread_manager_signal);
313         g_io_channel_unref(io_channel);
314         
315         etpan_thread_manager_free(thread_manager);
316         
317         chash_free(courier_workaround_hash);
318         chash_free(session_hash);
319         chash_free(imap_hash);
320 }
321
322 void imap_init(Folder * folder)
323 {
324         struct etpan_thread * thread;
325         chashdatum key;
326         chashdatum value;
327         
328         thread = etpan_thread_manager_get_thread(thread_manager);
329         
330         key.data = &folder;
331         key.len = sizeof(folder);
332         value.data = thread;
333         value.len = 0;
334         
335         chash_set(imap_hash, &key, &value, NULL);
336 }
337
338 void imap_done(Folder * folder)
339 {
340         struct etpan_thread * thread;
341         chashdatum key;
342         chashdatum value;
343         int r;
344         
345         key.data = &folder;
346         key.len = sizeof(folder);
347         
348         r = chash_get(imap_hash, &key, &value);
349         if (r < 0)
350                 return;
351         
352         thread = value.data;
353         
354         etpan_thread_unbind(thread);
355         
356         chash_delete(imap_hash, &key, NULL);
357         
358         debug_print("remove thread");
359 }
360
361 static struct etpan_thread * get_thread(Folder * folder)
362 {
363         struct etpan_thread * thread;
364         chashdatum key;
365         chashdatum value;
366         
367         key.data = &folder;
368         key.len = sizeof(folder);
369         
370         chash_get(imap_hash, &key, &value);
371         thread = value.data;
372         
373         return thread;
374 }
375
376 static mailimap * get_imap(Folder * folder)
377 {
378         mailimap * imap;
379         chashdatum key;
380         chashdatum value;
381         int r;
382         
383         key.data = &folder;
384         key.len = sizeof(folder);
385         
386         r = chash_get(session_hash, &key, &value);
387         if (r < 0)
388                 return NULL;
389         
390         imap = value.data;
391         debug_print("found imap %p\n", imap);
392         return imap;
393 }
394
395 static gboolean cb_show_error(gpointer data)
396 {
397         mainwindow_show_error();
398         return FALSE;
399 }
400
401 static void generic_cb(int cancelled, void * result, void * callback_data)
402 {
403         struct etpan_thread_op * op;
404         
405         op = (struct etpan_thread_op *) callback_data;
406
407         debug_print("generic_cb\n");
408         if (op->imap && op->imap->imap_response_info &&
409             op->imap->imap_response_info->rsp_alert) {
410                 log_error(LOG_PROTOCOL, "IMAP4< Alert: %s\n", 
411                         op->imap->imap_response_info->rsp_alert);
412                 g_timeout_add(10, cb_show_error, NULL);
413         } 
414         op->finished = 1;
415 }
416
417 static void threaded_run(Folder * folder, void * param, void * result,
418                          void (* func)(struct etpan_thread_op * ))
419 {
420         struct etpan_thread_op * op;
421         struct etpan_thread * thread;
422         
423         imap_folder_ref(folder);
424
425         op = etpan_thread_op_new();
426         
427         op->imap = get_imap(folder);
428         op->param = param;
429         op->result = result;
430         
431         op->cancellable = 0;
432         op->run = func;
433         op->callback = generic_cb;
434         op->callback_data = op;
435         op->cleanup = NULL;
436         
437         op->finished = 0;
438         
439         thread = get_thread(folder);
440         etpan_thread_op_schedule(thread, op);
441         
442         while (!op->finished) {
443                 gtk_main_iteration();
444         }
445         
446         etpan_thread_op_free(op);
447
448         imap_folder_unref(folder);
449 }
450
451
452 /* connect */
453
454 struct connect_param {
455         mailimap * imap;
456         PrefsAccount *account;
457         const char * server;
458         int port;
459 };
460
461 struct connect_result {
462         int error;
463 };
464
465 #define CHECK_IMAP() {                                          \
466         if (!param->imap) {                                     \
467                 result->error = MAILIMAP_ERROR_BAD_STATE;       \
468                 return;                                         \
469         }                                                       \
470 }
471
472 static void connect_run(struct etpan_thread_op * op)
473 {
474         int r;
475         struct connect_param * param;
476         struct connect_result * result;
477         
478         param = op->param;
479         result = op->result;
480         
481         CHECK_IMAP();
482
483         r = mailimap_socket_connect(param->imap,
484                                     param->server, param->port);
485         
486         result->error = r;
487 }
488
489
490 int imap_threaded_connect(Folder * folder, const char * server, int port)
491 {
492         struct connect_param param;
493         struct connect_result result;
494         chashdatum key;
495         chashdatum value;
496         mailimap * imap, * oldimap;
497         
498         oldimap = get_imap(folder);
499
500         imap = mailimap_new(0, NULL);
501         
502         if (oldimap) {
503                 debug_print("deleting old imap %p\n", oldimap);
504                 delete_imap(folder, oldimap);
505         }
506         
507         key.data = &folder;
508         key.len = sizeof(folder);
509         value.data = imap;
510         value.len = 0;
511         chash_set(session_hash, &key, &value, NULL);
512         
513         param.imap = imap;
514         param.server = server;
515         param.port = port;
516         
517         refresh_resolvers();
518         threaded_run(folder, &param, &result, connect_run);
519         
520         debug_print("connect ok %i with imap %p\n", result.error, imap);
521         
522         return result.error;
523 }
524
525 static int etpan_certificate_check(const unsigned char *certificate, int len, void *data)
526 {
527 #ifdef USE_GNUTLS
528         struct connect_param *param = (struct connect_param *)data;
529         gnutls_x509_crt cert = NULL;
530         gnutls_datum tmp;
531         
532         if (certificate == NULL || len < 0) {
533                 g_warning("no cert presented.\n");
534                 return 0;
535         }
536         
537         tmp.data = malloc(len);
538         memcpy(tmp.data, certificate, len);
539         tmp.size = len;
540         gnutls_x509_crt_init(&cert);
541         if (gnutls_x509_crt_import(cert, &tmp, GNUTLS_X509_FMT_DER) < 0) {
542                 g_warning("IMAP: can't get cert\n");
543                 return 0;
544         } else if (ssl_certificate_check(cert, (guint)-1, (gchar *)param->server,
545                         (gushort)param->port) == TRUE) {
546                 gnutls_x509_crt_deinit(cert);
547                 return 0;
548         } else {
549                 gnutls_x509_crt_deinit(cert);
550                 return -1;
551         }
552 #endif
553         return 0;
554 }
555
556 static void connect_ssl_context_cb(struct mailstream_ssl_context * ssl_context, void * data)
557 {
558 #ifdef USE_GNUTLS
559         PrefsAccount *account = (PrefsAccount *)data;
560         const gchar *cert_path = NULL;
561         const gchar *password = NULL;
562         gnutls_x509_crt x509 = NULL;
563         gnutls_x509_privkey pkey = NULL;
564
565         if (account->in_ssl_client_cert_file && *account->in_ssl_client_cert_file)
566                 cert_path = account->in_ssl_client_cert_file;
567         if (account->in_ssl_client_cert_pass && *account->in_ssl_client_cert_pass)
568                 password = account->in_ssl_client_cert_pass;
569         
570         if (mailstream_ssl_set_client_certificate_data(ssl_context, NULL, 0) < 0 ||
571             mailstream_ssl_set_client_private_key_data(ssl_context, NULL, 0) < 0)
572                 debug_print("Impossible to set the client certificate.\n");
573         x509 = ssl_certificate_get_x509_from_pem_file(cert_path);
574         pkey = ssl_certificate_get_pkey_from_pem_file(cert_path);
575         if (!(x509 && pkey)) {
576                 /* try pkcs12 format */
577                 ssl_certificate_get_x509_and_pkey_from_p12_file(cert_path, password, &x509, &pkey);
578         }
579         if (x509 && pkey) {
580                 unsigned char *x509_der = NULL, *pkey_der = NULL;
581                 size_t x509_len, pkey_len;
582                 
583                 x509_len = (size_t)gnutls_i2d_X509(x509, &x509_der);
584                 pkey_len = (size_t)gnutls_i2d_PrivateKey(pkey, &pkey_der);
585                 if (x509_len > 0 && pkey_len > 0) {
586                         if (mailstream_ssl_set_client_certificate_data(ssl_context, x509_der, x509_len) < 0 ||
587                             mailstream_ssl_set_client_private_key_data(ssl_context, pkey_der, pkey_len) < 0) 
588                                 log_error(LOG_PROTOCOL, _("Impossible to set the client certificate.\n"));
589                         g_free(x509_der);
590                         g_free(pkey_der);
591                 }
592                 gnutls_x509_crt_deinit(x509);
593                 gnutls_x509_privkey_deinit(pkey);
594         }
595 #endif
596 }
597
598 static void connect_ssl_run(struct etpan_thread_op * op)
599 {
600         int r;
601         struct connect_param * param;
602         struct connect_result * result;
603         
604         param = op->param;
605         result = op->result;
606         
607         CHECK_IMAP();
608
609         r = mailimap_ssl_connect_with_callback(param->imap,
610                                                 param->server, param->port,
611                                                 connect_ssl_context_cb, param->account);
612         result->error = r;
613 }
614
615 int imap_threaded_connect_ssl(Folder * folder, const char * server, int port)
616 {
617         struct connect_param param;
618         struct connect_result result;
619         chashdatum key;
620         chashdatum value;
621         mailimap * imap, * oldimap;
622         unsigned char *certificate = NULL;
623         int cert_len;
624         
625         oldimap = get_imap(folder);
626
627         imap = mailimap_new(0, NULL);
628         
629         if (oldimap) {
630                 debug_print("deleting old imap %p\n", oldimap);
631                 delete_imap(folder, oldimap);
632         }
633
634         key.data = &folder;
635         key.len = sizeof(folder);
636         value.data = imap;
637         value.len = 0;
638         chash_set(session_hash, &key, &value, NULL);
639         
640         param.imap = imap;
641         param.server = server;
642         param.port = port;
643         param.account = folder->account;
644
645         refresh_resolvers();
646         threaded_run(folder, &param, &result, connect_ssl_run);
647
648         if ((result.error == MAILIMAP_NO_ERROR_AUTHENTICATED ||
649              result.error == MAILIMAP_NO_ERROR_NON_AUTHENTICATED) && !etpan_skip_ssl_cert_check) {
650                 cert_len = (int)mailstream_ssl_get_certificate(imap->imap_stream, &certificate);
651                 if (etpan_certificate_check(certificate, cert_len, &param) < 0)
652                         return -1;
653                 if (certificate) 
654                         free(certificate); 
655         }
656         debug_print("connect %d with imap %p\n", result.error, imap);
657         
658         return result.error;
659 }
660
661 struct capa_param {
662         mailimap * imap;
663 };
664
665 struct capa_result {
666         int error;
667         struct mailimap_capability_data *caps;
668 };
669
670 static void capability_run(struct etpan_thread_op * op)
671 {
672         int r;
673         struct capa_param * param;
674         struct capa_result * result;
675         struct mailimap_capability_data *caps;
676
677         param = op->param;
678         result = op->result;
679         
680         CHECK_IMAP();
681
682         r = mailimap_capability(param->imap, &caps);
683         
684         result->error = r;
685         result->caps = (r == 0 ? caps : NULL);
686 }
687
688
689 struct mailimap_capability_data * imap_threaded_capability(Folder *folder, int *ok)
690 {
691         struct capa_param param;
692         struct capa_result result;
693         mailimap *imap;
694         
695         imap = get_imap(folder);
696         
697         param.imap = imap;
698         
699         threaded_run(folder, &param, &result, capability_run);
700         
701         debug_print("capa %d\n", result.error);
702         
703         if (ok)
704                 *ok = result.error;
705
706         return result.caps;
707         
708 }
709         
710 struct disconnect_param {
711         mailimap * imap;
712 };
713
714 struct disconnect_result {
715         int error;
716 };
717
718 static void disconnect_run(struct etpan_thread_op * op)
719 {
720         int r;
721         struct disconnect_param * param;
722         struct disconnect_result * result;
723         
724         param = op->param;
725         result = op->result;
726         
727         CHECK_IMAP();
728
729         r = mailimap_logout(param->imap);
730         
731         result->error = r;
732 }
733
734 void imap_threaded_disconnect(Folder * folder)
735 {
736         struct connect_param param;
737         struct connect_result result;
738         mailimap * imap;
739         
740         imap = get_imap(folder);
741         if (imap == NULL) {
742                 debug_print("was disconnected\n");
743                 return;
744         }
745         
746         param.imap = imap;
747         
748         threaded_run(folder, &param, &result, disconnect_run);
749         
750         if (imap == get_imap(folder)) {
751                 debug_print("deleting old imap %p\n", imap);
752                 delete_imap(folder, imap);
753         } else {
754                 debug_print("imap already deleted %p\n", imap);
755         }
756         
757         debug_print("disconnect ok\n");
758 }
759
760
761 struct list_param {
762         mailimap * imap;
763         const char * base;
764         const char * wildcard;
765         gboolean sub_only;
766 };
767
768 struct list_result {
769         int error;
770         clist * list;
771 };
772
773 static void list_run(struct etpan_thread_op * op)
774 {
775         struct list_param * param;
776         struct list_result * result;
777         int r;
778         clist * list;
779         
780         param = op->param;
781         result = op->result;
782
783         CHECK_IMAP();
784
785         list = NULL;
786         
787         if (param->base == NULL || param->wildcard == NULL) {
788                 result->list = list;
789                 result->error = -1;
790                 debug_print("no base or wildcard (%p %p)\n", param->base, param->wildcard);
791                 return;
792         }
793         if (param->sub_only)
794                 r = mailimap_lsub(param->imap, param->base,
795                           param->wildcard, &list);
796         else
797                 r = mailimap_list(param->imap, param->base,
798                           param->wildcard, &list);
799         result->error = r;
800         result->list = list;
801         debug_print("imap list run - end\n");
802 }
803
804 int imap_threaded_list(Folder * folder, const char * base,
805                        const char * wildcard,
806                        clist ** p_result)
807 {
808         struct list_param param;
809         struct list_result result;
810         
811         debug_print("imap list - begin\n");
812         
813         param.imap = get_imap(folder);
814         param.base = base;
815         param.wildcard = wildcard;
816         param.sub_only = FALSE;
817
818         threaded_run(folder, &param, &result, list_run);
819         
820         * p_result = result.list;
821         
822         debug_print("imap list - end %p\n", result.list);
823         
824         return result.error;
825 }
826
827 int imap_threaded_lsub(Folder * folder, const char * base,
828                        const char * wildcard,
829                        clist ** p_result)
830 {
831         struct list_param param;
832         struct list_result result;
833         
834         debug_print("imap lsub - begin\n");
835         
836         param.imap = get_imap(folder);
837         param.base = base;
838         param.wildcard = wildcard;
839         param.sub_only = TRUE;
840         
841         threaded_run(folder, &param, &result, list_run);
842         
843         * p_result = result.list;
844         
845         debug_print("imap lsub - end %p\n", result.list);
846         
847         return result.error;
848 }
849
850 struct subscribe_param {
851         mailimap * imap;
852         const char * mb;
853         gboolean subscribe;
854 };
855
856 struct subscribe_result {
857         int error;
858 };
859
860 static void subscribe_run(struct etpan_thread_op * op)
861 {
862         struct subscribe_param * param;
863         struct subscribe_result * result;
864         int r;
865         
866         param = op->param;
867         result = op->result;
868
869         CHECK_IMAP();
870
871         if (param->mb == NULL) {
872                 result->error = -1;
873                 debug_print("no mb\n");
874                 return;
875         }
876         if (param->subscribe)
877                 r = mailimap_subscribe(param->imap, param->mb);
878         else
879                 r = mailimap_unsubscribe(param->imap, param->mb);
880         result->error = r;
881         debug_print("imap %ssubscribe run - end %d\n", param->subscribe?"":"un", r);
882 }
883
884 int imap_threaded_subscribe(Folder * folder, const char * mb,
885                        gboolean subscribe)
886 {
887         struct subscribe_param param;
888         struct subscribe_result result;
889         
890         debug_print("imap list - begin\n");
891         
892         param.imap = get_imap(folder);
893         param.mb = mb;
894         param.subscribe = subscribe;
895
896         threaded_run(folder, &param, &result, subscribe_run);
897         
898         return result.error;
899 }
900
901 struct login_param {
902         mailimap * imap;
903         const char * login;
904         const char * password;
905         const char * type;
906         const char * server;
907 };
908
909 struct login_result {
910         int error;
911 };
912
913 static void login_run(struct etpan_thread_op * op)
914 {
915         struct login_param * param;
916         struct login_result * result;
917         int r;
918 #ifdef DISABLE_LOG_DURING_LOGIN
919         int old_debug;
920 #endif
921         
922         param = op->param;
923         result = op->result;
924
925         CHECK_IMAP();
926
927 #ifdef DISABLE_LOG_DURING_LOGIN
928         old_debug = mailstream_debug;
929         mailstream_debug = 0;
930 #endif
931         if (!strcmp(param->type, "LOGIN"))
932                 r = mailimap_login(param->imap,
933                            param->login, param->password);
934         else if (!strcmp(param->type, "GSSAPI"))
935                 r = mailimap_authenticate(param->imap,
936                         param->type, param->server, NULL, NULL,
937                         param->login, param->login,
938                         param->password, NULL);
939         else 
940                 r = mailimap_authenticate(param->imap,
941                         param->type, NULL, NULL, NULL,
942                         param->login, param->login,
943                         param->password, NULL);
944 #ifdef DISABLE_LOG_DURING_LOGIN
945         mailstream_debug = old_debug;
946 #endif
947         
948         result->error = r;
949         if (param->imap->imap_response)
950                 imap_logger_cmd(0, param->imap->imap_response, strlen(param->imap->imap_response));
951         debug_print("imap login run - end %i\n", r);
952 }
953
954 int imap_threaded_login(Folder * folder,
955                         const char * login, const char * password,
956                         const char * type)
957 {
958         struct login_param param;
959         struct login_result result;
960         
961         debug_print("imap login - begin\n");
962         
963         param.imap = get_imap(folder);
964         param.login = login;
965         param.password = password;
966         param.type = type;
967         if (folder && folder->account)
968                 param.server = folder->account->recv_server;
969         else
970                 param.server = NULL;
971
972         threaded_run(folder, &param, &result, login_run);
973         
974         debug_print("imap login - end\n");
975         
976         return result.error;
977 }
978
979
980 struct status_param {
981         mailimap * imap;
982         const char * mb;
983         struct mailimap_status_att_list * status_att_list;
984 };
985
986 struct status_result {
987         int error;
988         struct mailimap_mailbox_data_status * data_status;
989 };
990
991 static void status_run(struct etpan_thread_op * op)
992 {
993         struct status_param * param;
994         struct status_result * result;
995         int r;
996         
997         param = op->param;
998         result = op->result;
999         
1000         CHECK_IMAP();
1001
1002         r = mailimap_status(param->imap, param->mb,
1003                             param->status_att_list,
1004                             &result->data_status);
1005         
1006         result->error = r;
1007         debug_print("imap status run - end %i\n", r);
1008 }
1009
1010 int imap_threaded_status(Folder * folder, const char * mb,
1011                          struct mailimap_mailbox_data_status ** data_status,
1012                          guint mask)
1013 {
1014         struct status_param param;
1015         struct status_result result;
1016         struct mailimap_status_att_list * status_att_list;
1017         
1018         debug_print("imap status - begin\n");
1019         
1020         status_att_list = mailimap_status_att_list_new_empty();
1021         if (mask & 1 << 0) {
1022                 mailimap_status_att_list_add(status_att_list,
1023                                      MAILIMAP_STATUS_ATT_MESSAGES);
1024         }
1025         if (mask & 1 << 1) {
1026                 mailimap_status_att_list_add(status_att_list,
1027                                      MAILIMAP_STATUS_ATT_RECENT);
1028         }
1029         if (mask & 1 << 2) {
1030                 mailimap_status_att_list_add(status_att_list,
1031                                      MAILIMAP_STATUS_ATT_UIDNEXT);
1032         }
1033         if (mask & 1 << 3) {
1034                 mailimap_status_att_list_add(status_att_list,
1035                                      MAILIMAP_STATUS_ATT_UIDVALIDITY);
1036         }
1037         if (mask & 1 << 4) {
1038                 mailimap_status_att_list_add(status_att_list,
1039                                      MAILIMAP_STATUS_ATT_UNSEEN);
1040         }
1041         param.imap = get_imap(folder);
1042         param.mb = mb;
1043         param.status_att_list = status_att_list;
1044         
1045         threaded_run(folder, &param, &result, status_run);
1046         
1047         debug_print("imap status - end\n");
1048         
1049         * data_status = result.data_status;
1050         
1051         mailimap_status_att_list_free(status_att_list);
1052         
1053         return result.error;
1054 }
1055
1056
1057
1058 struct noop_param {
1059         mailimap * imap;
1060 };
1061
1062 struct noop_result {
1063         int error;
1064 };
1065
1066 static void noop_run(struct etpan_thread_op * op)
1067 {
1068         struct noop_param * param;
1069         struct noop_result * result;
1070         int r;
1071         
1072         param = op->param;
1073         result = op->result;
1074
1075         CHECK_IMAP();
1076
1077         r = mailimap_noop(param->imap);
1078         
1079         result->error = r;
1080         debug_print("imap noop run - end %i\n", r);
1081 }
1082
1083 int imap_threaded_noop(Folder * folder, unsigned int * p_exists, 
1084                        unsigned int *p_recent, 
1085                        unsigned int *p_expunge,
1086                        unsigned int *p_unseen,
1087                        unsigned int *p_uidnext,
1088                        unsigned int *p_uidval)
1089 {
1090         struct noop_param param;
1091         struct noop_result result;
1092         mailimap * imap;
1093         
1094         debug_print("imap noop - begin\n");
1095         
1096         imap = get_imap(folder);
1097         param.imap = imap;
1098
1099         threaded_run(folder, &param, &result, noop_run);
1100         
1101         if (result.error == 0 && imap && imap->imap_selection_info != NULL) {
1102                 * p_exists = imap->imap_selection_info->sel_exists;
1103                 * p_recent = imap->imap_selection_info->sel_recent;
1104                 * p_unseen = imap->imap_selection_info->sel_unseen;
1105                 * p_uidnext = imap->imap_selection_info->sel_uidnext;
1106                 * p_uidval = imap->imap_selection_info->sel_uidvalidity;
1107         } else {
1108                 * p_exists = 0;
1109                 * p_recent = 0;
1110                 * p_unseen = 0;
1111                 * p_uidnext = 0;
1112                 * p_uidval = 0;
1113         }
1114         if (result.error == 0 && imap && imap->imap_response_info != NULL &&
1115             imap->imap_response_info->rsp_expunged != NULL) {
1116                 * p_expunge = clist_count(imap->imap_response_info->rsp_expunged);
1117         } else {
1118                 * p_expunge = 0;
1119         }       
1120         debug_print("imap noop - end [EXISTS %d RECENT %d EXPUNGE %d UNSEEN %d UIDNEXT %d UIDVAL %d]\n",
1121                 *p_exists, *p_recent, *p_expunge, *p_unseen,
1122                 *p_uidnext, *p_uidval);
1123         
1124         return result.error;
1125 }
1126
1127
1128 struct starttls_result {
1129         int error;
1130 };
1131
1132 static void starttls_run(struct etpan_thread_op * op)
1133 {
1134         struct connect_param * param;
1135         struct starttls_result * result;
1136         int r;
1137
1138         param = op->param;
1139         result = op->result;
1140
1141         CHECK_IMAP();
1142
1143         r = mailimap_starttls(param->imap);
1144         
1145         result->error = r;
1146         debug_print("imap starttls run - end %i\n", r);
1147         
1148         if (r == 0) {
1149                 mailimap *imap = param->imap;
1150                 mailstream_low *plain_low = NULL;
1151                 mailstream_low *tls_low = NULL;
1152                 int fd = -1;
1153                 
1154                 plain_low = mailstream_get_low(imap->imap_stream);
1155                 fd = mailstream_low_get_fd(plain_low);
1156                 if (fd == -1) {
1157                         debug_print("imap starttls run - can't get fd\n");
1158                         result->error = MAILIMAP_ERROR_STREAM;
1159                         return;
1160                 }
1161
1162                 tls_low = mailstream_low_tls_open_with_callback(fd, connect_ssl_context_cb, param->account);
1163                 if (tls_low == NULL) {
1164                         debug_print("imap starttls run - can't tls_open\n");
1165                         result->error = MAILIMAP_ERROR_STREAM;
1166                         return;
1167                 }
1168                 mailstream_low_free(plain_low);
1169                 mailstream_set_low(imap->imap_stream, tls_low);
1170         }
1171 }
1172
1173 int imap_threaded_starttls(Folder * folder, const gchar *host, int port)
1174 {
1175         struct connect_param param;
1176         struct starttls_result result;
1177         int cert_len;
1178         unsigned char *certificate = NULL;
1179         
1180         debug_print("imap starttls - begin\n");
1181         
1182         param.imap = get_imap(folder);
1183         param.server = host;
1184         param.port = port;
1185         param.account = folder->account;
1186
1187         threaded_run(folder, &param, &result, starttls_run);
1188         
1189         debug_print("imap starttls - end\n");
1190
1191         if (result.error == 0 && param.imap && !etpan_skip_ssl_cert_check) {
1192                 cert_len = (int)mailstream_ssl_get_certificate(param.imap->imap_stream, &certificate);
1193                 if (etpan_certificate_check(certificate, cert_len, &param) < 0)
1194                         result.error = MAILIMAP_ERROR_STREAM;
1195                 if (certificate) 
1196                         free(certificate); 
1197         }       
1198         return result.error;
1199 }
1200
1201
1202
1203 struct create_param {
1204         mailimap * imap;
1205         const char * mb;
1206 };
1207
1208 struct create_result {
1209         int error;
1210 };
1211
1212 static void create_run(struct etpan_thread_op * op)
1213 {
1214         struct create_param * param;
1215         struct create_result * result;
1216         int r;
1217         
1218         param = op->param;
1219         result = op->result;
1220
1221         CHECK_IMAP();
1222
1223         r = mailimap_create(param->imap, param->mb);
1224         
1225         result->error = r;
1226         debug_print("imap create run - end %i\n", r);
1227 }
1228
1229 int imap_threaded_create(Folder * folder, const char * mb)
1230 {
1231         struct create_param param;
1232         struct create_result result;
1233         
1234         debug_print("imap create - begin\n");
1235         
1236         param.imap = get_imap(folder);
1237         param.mb = mb;
1238         
1239         threaded_run(folder, &param, &result, create_run);
1240         
1241         debug_print("imap create - end\n");
1242         
1243         return result.error;
1244 }
1245
1246
1247
1248
1249 struct rename_param {
1250         mailimap * imap;
1251         const char * mb;
1252         const char * new_name;
1253 };
1254
1255 struct rename_result {
1256         int error;
1257 };
1258
1259 static void rename_run(struct etpan_thread_op * op)
1260 {
1261         struct rename_param * param;
1262         struct rename_result * result;
1263         int r;
1264         
1265         param = op->param;
1266         result = op->result;
1267
1268         CHECK_IMAP();
1269
1270         r = mailimap_rename(param->imap, param->mb, param->new_name);
1271         
1272         result->error = r;
1273         debug_print("imap rename run - end %i\n", r);
1274 }
1275
1276 int imap_threaded_rename(Folder * folder,
1277                          const char * mb, const char * new_name)
1278 {
1279         struct rename_param param;
1280         struct rename_result result;
1281         
1282         debug_print("imap rename - begin\n");
1283         
1284         param.imap = get_imap(folder);
1285         param.mb = mb;
1286         param.new_name = new_name;
1287         
1288         threaded_run(folder, &param, &result, rename_run);
1289         
1290         debug_print("imap rename - end\n");
1291         
1292         return result.error;
1293 }
1294
1295
1296
1297
1298 struct delete_param {
1299         mailimap * imap;
1300         const char * mb;
1301 };
1302
1303 struct delete_result {
1304         int error;
1305 };
1306
1307 static void delete_run(struct etpan_thread_op * op)
1308 {
1309         struct delete_param * param;
1310         struct delete_result * result;
1311         int r;
1312         
1313         param = op->param;
1314         result = op->result;
1315
1316         CHECK_IMAP();
1317
1318         r = mailimap_delete(param->imap, param->mb);
1319         
1320         result->error = r;
1321         debug_print("imap delete run - end %i\n", r);
1322 }
1323
1324 int imap_threaded_delete(Folder * folder, const char * mb)
1325 {
1326         struct delete_param param;
1327         struct delete_result result;
1328         
1329         debug_print("imap delete - begin\n");
1330         
1331         param.imap = get_imap(folder);
1332         param.mb = mb;
1333         
1334         threaded_run(folder, &param, &result, delete_run);
1335         
1336         debug_print("imap delete - end\n");
1337         
1338         return result.error;
1339 }
1340
1341
1342
1343 struct select_param {
1344         mailimap * imap;
1345         const char * mb;
1346 };
1347
1348 struct select_result {
1349         int error;
1350 };
1351
1352 static void select_run(struct etpan_thread_op * op)
1353 {
1354         struct select_param * param;
1355         struct select_result * result;
1356         int r;
1357         
1358         param = op->param;
1359         result = op->result;
1360
1361         CHECK_IMAP();
1362
1363         r = mailimap_select(param->imap, param->mb);
1364         
1365         result->error = r;
1366         debug_print("imap select run - end %i\n", r);
1367 }
1368
1369 int imap_threaded_select(Folder * folder, const char * mb,
1370                          gint * exists, gint * recent, gint * unseen,
1371                          guint32 * uid_validity,gint *can_create_flags,
1372                          GSList **ok_flags)
1373 {
1374         struct select_param param;
1375         struct select_result result;
1376         mailimap * imap;
1377
1378         debug_print("imap select - begin\n");
1379         
1380         imap = get_imap(folder);
1381         param.imap = imap;
1382         param.mb = mb;
1383         
1384         threaded_run(folder, &param, &result, select_run);
1385         
1386         if (result.error != MAILIMAP_NO_ERROR)
1387                 return result.error;
1388         
1389         if (!imap || imap->imap_selection_info == NULL)
1390                 return MAILIMAP_ERROR_PARSE;
1391         
1392         * exists = imap->imap_selection_info->sel_exists;
1393         * recent = imap->imap_selection_info->sel_recent;
1394         * unseen = imap->imap_selection_info->sel_unseen;
1395         * uid_validity = imap->imap_selection_info->sel_uidvalidity;
1396         * can_create_flags = FALSE;
1397
1398         if (imap->imap_selection_info->sel_perm_flags) {
1399                 GSList *t_flags = NULL;
1400                 clistiter *cur = NULL;
1401                 if (imap->imap_selection_info->sel_perm_flags)
1402                         cur = clist_begin(imap->imap_selection_info->sel_perm_flags);
1403
1404                 for (; cur; cur = clist_next(cur)) {
1405                         struct mailimap_flag_perm *flag = (struct mailimap_flag_perm *)clist_content(cur);
1406                         if (flag->fl_type == MAILIMAP_FLAG_PERM_ALL)
1407                                 *can_create_flags = TRUE;
1408                         else if (flag->fl_flag && 
1409                                         flag->fl_flag->fl_type == 6 &&
1410                                         !strcmp(flag->fl_flag->fl_data.fl_extension, "*"))
1411                                 *can_create_flags = TRUE; 
1412                         if (flag->fl_flag && ok_flags) {
1413                                 MsgPermFlags c_flag = 0;
1414                                 switch (flag->fl_flag->fl_type) {
1415                                 case MAILIMAP_FLAG_ANSWERED:
1416                                         c_flag = IMAP_FLAG_ANSWERED;
1417                                         break;
1418                                 case MAILIMAP_FLAG_FLAGGED:
1419                                         c_flag = IMAP_FLAG_FLAGGED;
1420                                         break;
1421                                 case MAILIMAP_FLAG_DELETED:
1422                                         c_flag = IMAP_FLAG_DELETED;
1423                                         break;
1424                                 case MAILIMAP_FLAG_DRAFT:
1425                                         c_flag = IMAP_FLAG_DRAFT;
1426                                         break;
1427                                 case MAILIMAP_FLAG_SEEN:
1428                                         c_flag = IMAP_FLAG_SEEN;
1429                                         break;
1430                                 case MAILIMAP_FLAG_KEYWORD:
1431                                         if (!strcasecmp(flag->fl_flag->fl_data.fl_keyword, RTAG_FORWARDED))
1432                                                 c_flag = IMAP_FLAG_FORWARDED;
1433                                         if (!strcasecmp(flag->fl_flag->fl_data.fl_keyword, RTAG_JUNK))
1434                                                 c_flag = IMAP_FLAG_SPAM;
1435                                         if (!strcasecmp(flag->fl_flag->fl_data.fl_keyword, RTAG_NON_JUNK) ||
1436                                             !strcasecmp(flag->fl_flag->fl_data.fl_keyword, RTAG_NO_JUNK) ||
1437                                             !strcasecmp(flag->fl_flag->fl_data.fl_keyword, RTAG_NOT_JUNK))
1438                                                 c_flag = IMAP_FLAG_HAM;
1439                                         break;
1440                                 default:
1441                                         break;
1442                                 }
1443                                 if (c_flag != 0) {
1444                                         t_flags = g_slist_prepend(t_flags, 
1445                                                 GUINT_TO_POINTER(c_flag));
1446                                 }
1447                         }
1448                 }
1449                 if (ok_flags)
1450                         *ok_flags = t_flags;
1451         }
1452         debug_print("imap select - end\n");
1453         
1454         return result.error;
1455 }
1456
1457 static void close_run(struct etpan_thread_op * op)
1458 {
1459         struct select_param * param;
1460         struct select_result * result;
1461         int r;
1462         
1463         param = op->param;
1464         result = op->result;
1465
1466         CHECK_IMAP();
1467
1468         r = mailimap_close(param->imap);
1469         
1470         result->error = r;
1471         debug_print("imap close run - end %i\n", r);
1472 }
1473
1474 int imap_threaded_close(Folder * folder)
1475 {
1476         struct select_param param;
1477         struct select_result result;
1478         mailimap * imap;
1479         
1480         debug_print("imap close - begin\n");
1481         
1482         imap = get_imap(folder);
1483         param.imap = imap;
1484         
1485         threaded_run(folder, &param, &result, close_run);
1486         
1487         if (result.error != MAILIMAP_NO_ERROR)
1488                 return result.error;
1489         
1490         debug_print("imap close - end\n");
1491         
1492         return result.error;
1493 }
1494
1495 struct examine_param {
1496         mailimap * imap;
1497         const char * mb;
1498 };
1499
1500 struct examine_result {
1501         int error;
1502 };
1503
1504 static void examine_run(struct etpan_thread_op * op)
1505 {
1506         struct examine_param * param;
1507         struct examine_result * result;
1508         int r;
1509         
1510         param = op->param;
1511         result = op->result;
1512
1513         CHECK_IMAP();
1514
1515         r = mailimap_examine(param->imap, param->mb);
1516         
1517         result->error = r;
1518         debug_print("imap examine run - end %i\n", r);
1519 }
1520
1521 int imap_threaded_examine(Folder * folder, const char * mb,
1522                           gint * exists, gint * recent, gint * unseen,
1523                           guint32 * uid_validity)
1524 {
1525         struct examine_param param;
1526         struct examine_result result;
1527         mailimap * imap;
1528         
1529         debug_print("imap examine - begin\n");
1530         
1531         imap = get_imap(folder);
1532         param.imap = imap;
1533         param.mb = mb;
1534         
1535         threaded_run(folder, &param, &result, examine_run);
1536         
1537         if (result.error != MAILIMAP_NO_ERROR)
1538                 return result.error;
1539         
1540         if (!imap || imap->imap_selection_info == NULL)
1541                 return MAILIMAP_ERROR_PARSE;
1542         
1543         * exists = imap->imap_selection_info->sel_exists;
1544         * recent = imap->imap_selection_info->sel_recent;
1545         * unseen = imap->imap_selection_info->sel_unseen;
1546         * uid_validity = imap->imap_selection_info->sel_uidvalidity;
1547         
1548         debug_print("imap examine - end\n");
1549         
1550         return result.error;
1551 }
1552
1553
1554
1555
1556 struct search_param {
1557         mailimap * imap;
1558         int type;
1559         struct mailimap_set * set;
1560 };
1561
1562 struct search_result {
1563         int error;
1564         clist * search_result;
1565 };
1566
1567 static struct mailimap_set_item *sc_mailimap_set_item_copy(struct mailimap_set_item *orig)
1568 {
1569         return mailimap_set_item_new(orig->set_first, orig->set_last);
1570 }
1571
1572 static struct mailimap_set *sc_mailimap_set_copy(struct mailimap_set *orig)
1573 {
1574         clist *list = orig ? orig->set_list : NULL;
1575         clist *newlist = clist_new();
1576         clistiter *cur;
1577         
1578         if (!orig)
1579                 return NULL;
1580         for (cur = clist_begin(list); cur; cur = clist_next(cur))
1581                 clist_append(newlist, 
1582                         sc_mailimap_set_item_copy(
1583                         (struct mailimap_set_item *)clist_content(cur)));
1584         return mailimap_set_new(newlist);
1585 }
1586
1587 static void search_run(struct etpan_thread_op * op)
1588 {
1589         struct search_param * param;
1590         struct search_result * result;
1591         int r;
1592         struct mailimap_search_key * key = NULL;
1593         struct mailimap_search_key * uid_key = NULL;
1594         struct mailimap_search_key * search_type_key;
1595         clist * search_result;
1596         
1597         param = op->param;
1598         result = op->result;
1599
1600         CHECK_IMAP();
1601
1602         /* we copy the mailimap_set because freeing the key is recursive */
1603         if (param->set != NULL) {
1604                 uid_key = mailimap_search_key_new_uid(sc_mailimap_set_copy(param->set));
1605         } else if (param->type == IMAP_SEARCH_TYPE_SIMPLE) {
1606                 uid_key = mailimap_search_key_new_all();
1607         }
1608         search_type_key = NULL;
1609         switch (param->type) {
1610         case IMAP_SEARCH_TYPE_SIMPLE:
1611                 search_type_key = NULL;
1612                 break;
1613                 
1614         case IMAP_SEARCH_TYPE_SEEN:
1615                 search_type_key = mailimap_search_key_new(MAILIMAP_SEARCH_KEY_SEEN,
1616                                                           NULL, NULL, NULL, NULL, NULL,
1617                                                           NULL, NULL, NULL, NULL, NULL,
1618                                                           NULL, NULL, NULL, NULL, 0,
1619                                                           NULL, NULL, NULL, NULL, NULL,
1620                                                           NULL, 0, NULL, NULL, NULL);
1621                 break;
1622                 
1623         case IMAP_SEARCH_TYPE_UNSEEN:
1624                 search_type_key = mailimap_search_key_new(MAILIMAP_SEARCH_KEY_UNSEEN,
1625                                                           NULL, NULL, NULL, NULL, NULL,
1626                                                           NULL, NULL, NULL, NULL, NULL,
1627                                                           NULL, NULL, NULL, NULL, 0,
1628                                                           NULL, NULL, NULL, NULL, NULL,
1629                                                           NULL, 0, NULL, NULL, NULL);
1630                 break;
1631                 
1632         case IMAP_SEARCH_TYPE_ANSWERED:
1633                 search_type_key = mailimap_search_key_new(MAILIMAP_SEARCH_KEY_ANSWERED,
1634                                                           NULL, NULL, NULL, NULL, NULL,
1635                                                           NULL, NULL, NULL, NULL, NULL,
1636                                                           NULL, NULL, NULL, NULL, 0,
1637                                                           NULL, NULL, NULL, NULL, NULL,
1638                                                           NULL, 0, NULL, NULL, NULL);
1639                 break;
1640                 
1641         case IMAP_SEARCH_TYPE_FLAGGED:
1642                 search_type_key = mailimap_search_key_new(MAILIMAP_SEARCH_KEY_FLAGGED,
1643                                                           NULL, NULL, NULL, NULL, NULL,
1644                                                           NULL, NULL, NULL, NULL, NULL,
1645                                                           NULL, NULL, NULL, NULL, 0,
1646                                                           NULL, NULL, NULL, NULL, NULL,
1647                                                           NULL, 0, NULL, NULL, NULL);
1648                 break;
1649         case IMAP_SEARCH_TYPE_DELETED:
1650                 search_type_key = mailimap_search_key_new(MAILIMAP_SEARCH_KEY_DELETED,
1651                                                           NULL, NULL, NULL, NULL, NULL,
1652                                                           NULL, NULL, NULL, NULL, NULL,
1653                                                           NULL, NULL, NULL, NULL, 0,
1654                                                           NULL, NULL, NULL, NULL, NULL,
1655                                                           NULL, 0, NULL, NULL, NULL);
1656                 break;
1657         case IMAP_SEARCH_TYPE_FORWARDED:
1658                 search_type_key = mailimap_search_key_new(MAILIMAP_SEARCH_KEY_KEYWORD,
1659                                                           NULL, NULL, NULL, NULL, NULL,
1660                                                           strdup(RTAG_FORWARDED), 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_SPAM:
1666                 search_type_key = mailimap_search_key_new(MAILIMAP_SEARCH_KEY_KEYWORD,
1667                                                           NULL, NULL, NULL, NULL, NULL,
1668                                                           strdup(RTAG_JUNK), 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         }
1674         
1675         if (search_type_key != NULL) {
1676                 if (param->set != NULL) {
1677                         key = mailimap_search_key_new_multiple_empty();
1678                         mailimap_search_key_multiple_add(key, search_type_key);
1679                         mailimap_search_key_multiple_add(key, uid_key);
1680                 } else {
1681                         key = search_type_key;
1682                 }
1683         } else if (uid_key != NULL) {
1684                 key = uid_key;
1685         }
1686         
1687         if (key == NULL) {
1688                 g_warning("no key!");
1689                 result = op->result;
1690                 result->error = -1;
1691                 result->search_result = NULL;
1692         } else {
1693                 mailstream_logger = imap_logger_uid;
1694
1695                 r = mailimap_uid_search(param->imap, NULL, key, &search_result);
1696
1697                 mailstream_logger = imap_logger_cmd;
1698
1699                 /* free the key (with the imapset) */
1700                 mailimap_search_key_free(key);
1701
1702                 result->error = r;
1703                 result->search_result = search_result;
1704         }
1705         debug_print("imap search run - end %i\n", result->error);
1706 }
1707
1708 int imap_threaded_search(Folder * folder, int search_type,
1709                          struct mailimap_set * set, clist ** search_result)
1710 {
1711         struct search_param param;
1712         struct search_result result;
1713         mailimap * imap;
1714         
1715         debug_print("imap search - begin\n");
1716
1717         imap = get_imap(folder);
1718         param.imap = imap;
1719         param.set = set;
1720         param.type = search_type;
1721         
1722         threaded_run(folder, &param, &result, search_run);
1723         
1724         if (result.error != MAILIMAP_NO_ERROR)
1725                 return result.error;
1726         
1727         debug_print("imap search - end\n");
1728         
1729         * search_result = result.search_result;
1730         
1731         return result.error;
1732 }
1733
1734
1735
1736 static int imap_get_msg_att_info(struct mailimap_msg_att * msg_att,
1737                                  uint32_t * puid,
1738                                  char ** pheaders,
1739                                  size_t * pref_size,
1740                                  struct mailimap_msg_att_dynamic ** patt_dyn);
1741
1742 static int
1743 result_to_uid_list(clist * fetch_result, carray ** result)
1744 {
1745         clistiter * cur = NULL;
1746         int r;
1747         int res;
1748         carray * tab;
1749         
1750         tab = carray_new(128);
1751         if (tab == NULL) {
1752                 res = MAILIMAP_ERROR_MEMORY;
1753                 goto err;
1754         }
1755         
1756         if (fetch_result)
1757                 cur = clist_begin(fetch_result);
1758
1759         for(; cur != NULL ; cur = clist_next(cur)) {
1760                 struct mailimap_msg_att * msg_att;
1761                 uint32_t uid;
1762                 uint32_t * puid;
1763                 
1764                 msg_att = clist_content(cur);
1765                 
1766                 uid = 0;
1767                 imap_get_msg_att_info(msg_att, &uid, NULL, NULL, NULL);
1768                 
1769                 puid = malloc(sizeof(* puid));
1770                 if (puid == NULL) {
1771                         res = MAILIMAP_ERROR_MEMORY;
1772                         goto free_list;
1773                 }
1774                 * puid = uid;
1775                         
1776                 r = carray_add(tab, puid, NULL);
1777                 if (r < 0) {
1778                         free(puid);
1779                         res = MAILIMAP_ERROR_MEMORY;
1780                         goto free_list;
1781                 }
1782         }
1783                 
1784         * result = tab;
1785
1786         return MAILIMAP_NO_ERROR;
1787   
1788  free_list:
1789         imap_fetch_uid_list_free(tab);
1790  err:
1791         return res;
1792 }
1793
1794 static int imap_get_messages_list(mailimap * imap,
1795                                   uint32_t first_index,
1796                                   carray ** result)
1797 {
1798         carray * env_list;
1799         int r;
1800         struct mailimap_fetch_att * fetch_att;
1801         struct mailimap_fetch_type * fetch_type;
1802         struct mailimap_set * set;
1803         clist * fetch_result;
1804         int res;
1805         
1806         set = mailimap_set_new_interval(first_index, 0);
1807         if (set == NULL) {
1808                 res = MAILIMAP_ERROR_MEMORY;
1809                 goto err;
1810         }
1811
1812         fetch_type = mailimap_fetch_type_new_fetch_att_list_empty();
1813         if (fetch_type == NULL) {
1814                 res = MAILIMAP_ERROR_MEMORY;
1815                 goto free_set;
1816         }
1817
1818         fetch_att = mailimap_fetch_att_new_uid();
1819         if (fetch_att == NULL) {
1820                 res = MAILIMAP_ERROR_MEMORY;
1821                 goto free_fetch_type;
1822         }
1823
1824         r = mailimap_fetch_type_new_fetch_att_list_add(fetch_type, fetch_att);
1825         if (r != MAILIMAP_NO_ERROR) {
1826                 mailimap_fetch_att_free(fetch_att);
1827                 res = MAILIMAP_ERROR_MEMORY;
1828                 goto free_fetch_type;
1829         }
1830
1831         mailstream_logger = imap_logger_fetch;
1832         
1833         r = mailimap_uid_fetch(imap, set,
1834                                fetch_type, &fetch_result);
1835
1836         mailstream_logger = imap_logger_cmd;
1837         mailimap_fetch_type_free(fetch_type);
1838         mailimap_set_free(set);
1839
1840         if (r != MAILIMAP_NO_ERROR) {
1841                 res = r;
1842                 goto err;
1843         }
1844
1845         env_list = NULL;
1846         r = result_to_uid_list(fetch_result, &env_list);
1847         mailimap_fetch_list_free(fetch_result);
1848         
1849         * result = env_list;
1850
1851         return MAILIMAP_NO_ERROR;
1852
1853  free_fetch_type:
1854         mailimap_fetch_type_free(fetch_type);
1855  free_set:
1856         mailimap_set_free(set);
1857  err:
1858         return res;
1859 }
1860
1861
1862
1863
1864 struct fetch_uid_param {
1865         mailimap * imap;
1866         uint32_t first_index;
1867 };
1868
1869 struct fetch_uid_result {
1870         int error;
1871         carray * fetch_result;
1872 };
1873
1874 static void fetch_uid_run(struct etpan_thread_op * op)
1875 {
1876         struct fetch_uid_param * param;
1877         struct fetch_uid_result * result;
1878         carray * fetch_result;
1879         int r;
1880         
1881         param = op->param;
1882         result = op->result;
1883
1884         CHECK_IMAP();
1885
1886         fetch_result = NULL;
1887         mailstream_logger = imap_logger_noop;
1888         log_print(LOG_PROTOCOL, "IMAP4- [fetching UIDs...]\n");
1889
1890         r = imap_get_messages_list(param->imap, param->first_index,
1891                                    &fetch_result);
1892         
1893         mailstream_logger = imap_logger_cmd;
1894
1895         result->error = r;
1896         result->fetch_result = fetch_result;
1897         debug_print("imap fetch_uid run - end %i\n", r);
1898 }
1899
1900 int imap_threaded_fetch_uid(Folder * folder, uint32_t first_index,
1901                             carray ** fetch_result)
1902 {
1903         struct fetch_uid_param param;
1904         struct fetch_uid_result result;
1905         mailimap * imap;
1906         
1907         debug_print("imap fetch_uid - begin\n");
1908         
1909         imap = get_imap(folder);
1910         param.imap = imap;
1911         param.first_index = first_index;
1912         
1913         threaded_run(folder, &param, &result, fetch_uid_run);
1914         
1915         if (result.error != MAILIMAP_NO_ERROR)
1916                 return result.error;
1917         
1918         debug_print("imap fetch_uid - end\n");
1919         
1920         * fetch_result = result.fetch_result;
1921         
1922         return result.error;
1923 }
1924
1925
1926 void imap_fetch_uid_list_free(carray * uid_list)
1927 {
1928         unsigned int i;
1929         
1930         for(i = 0 ; i < carray_count(uid_list) ; i ++) {
1931                 uint32_t * puid;
1932                 
1933                 puid = carray_get(uid_list, i);
1934                 free(puid);
1935         }
1936         carray_free(uid_list);
1937 }
1938
1939
1940
1941
1942 static int imap_flags_to_flags(struct mailimap_msg_att_dynamic * att_dyn, GSList **tags);
1943
1944 static int
1945 result_to_uid_flags_list(clist * fetch_result, carray ** result)
1946 {
1947         clistiter * cur = NULL;
1948         int r;
1949         int res;
1950         carray * tab;
1951         GSList *tags = NULL;
1952
1953         tab = carray_new(128);
1954         if (tab == NULL) {
1955                 res = MAILIMAP_ERROR_MEMORY;
1956                 goto err;
1957         }
1958
1959         if (fetch_result)
1960                 cur = clist_begin(fetch_result);
1961
1962         for(; cur != NULL ; cur = clist_next(cur)) {
1963                 struct mailimap_msg_att * msg_att;
1964                 uint32_t uid;
1965                 uint32_t * puid;
1966                 struct mailimap_msg_att_dynamic * att_dyn;
1967                 int flags;
1968                 int * pflags;
1969                 
1970                 tags = NULL;
1971
1972                 msg_att = clist_content(cur);
1973                 
1974                 uid = 0;
1975                 att_dyn = NULL;
1976                 imap_get_msg_att_info(msg_att, &uid, NULL, NULL, &att_dyn);
1977                 if (uid == 0)
1978                         continue;
1979                 if (att_dyn == NULL)
1980                         continue;
1981                 
1982                 flags = imap_flags_to_flags(att_dyn, &tags);
1983                 
1984                 puid = malloc(sizeof(* puid));
1985                 if (puid == NULL) {
1986                         res = MAILIMAP_ERROR_MEMORY;
1987                         goto free_list;
1988                 }
1989                 * puid = uid;
1990                 
1991                 r = carray_add(tab, puid, NULL);
1992                 if (r < 0) {
1993                         free(puid);
1994                         res = MAILIMAP_ERROR_MEMORY;
1995                         goto free_list;
1996                 }
1997                 pflags = malloc(sizeof(* pflags));
1998                 if (pflags == NULL) {
1999                         res = MAILIMAP_ERROR_MEMORY;
2000                         goto free_list;
2001                 }
2002                 * pflags = flags;
2003                 r = carray_add(tab, pflags, NULL);
2004                 if (r < 0) {
2005                         free(pflags);
2006                         res = MAILIMAP_ERROR_MEMORY;
2007                         goto free_list;
2008                 }
2009                 r = carray_add(tab, tags, NULL);
2010                 if (r < 0) {
2011                         free(pflags);
2012                         res = MAILIMAP_ERROR_MEMORY;
2013                         goto free_list;
2014                 }
2015         }
2016                 
2017         * result = tab;
2018
2019         return MAILIMAP_NO_ERROR;
2020   
2021  free_list:
2022         imap_fetch_uid_flags_list_free(tab);
2023         slist_free_strings(tags);
2024         g_slist_free(tags);
2025  err:
2026         return res;
2027 }
2028
2029 static int imap_get_messages_flags_list(mailimap * imap,
2030                                         uint32_t first_index,
2031                                         carray ** result)
2032 {
2033         carray * env_list;
2034         int r;
2035         struct mailimap_fetch_att * fetch_att;
2036         struct mailimap_fetch_type * fetch_type;
2037         struct mailimap_set * set;
2038         clist * fetch_result;
2039         int res;
2040         
2041         set = mailimap_set_new_interval(first_index, 0);
2042         if (set == NULL) {
2043                 res = MAILIMAP_ERROR_MEMORY;
2044                 goto err;
2045         }
2046
2047         fetch_type = mailimap_fetch_type_new_fetch_att_list_empty();
2048         if (fetch_type == NULL) {
2049                 res = MAILIMAP_ERROR_MEMORY;
2050                 goto free_set;
2051         }
2052
2053         fetch_att = mailimap_fetch_att_new_flags();
2054         if (fetch_att == NULL) {
2055                 res = MAILIMAP_ERROR_MEMORY;
2056                 goto free_fetch_type;
2057         }
2058         
2059         r = mailimap_fetch_type_new_fetch_att_list_add(fetch_type, fetch_att);
2060         if (r != MAILIMAP_NO_ERROR) {
2061                 mailimap_fetch_att_free(fetch_att);
2062                 res = MAILIMAP_ERROR_MEMORY;
2063                 goto free_fetch_type;
2064         }
2065         
2066         fetch_att = mailimap_fetch_att_new_uid();
2067         if (fetch_att == NULL) {
2068                 res = MAILIMAP_ERROR_MEMORY;
2069                 goto free_fetch_type;
2070         }
2071
2072         r = mailimap_fetch_type_new_fetch_att_list_add(fetch_type, fetch_att);
2073         if (r != MAILIMAP_NO_ERROR) {
2074                 mailimap_fetch_att_free(fetch_att);
2075                 res = MAILIMAP_ERROR_MEMORY;
2076                 goto free_fetch_type;
2077         }
2078
2079         mailstream_logger = imap_logger_fetch;
2080         
2081         r = mailimap_uid_fetch(imap, set,
2082                                fetch_type, &fetch_result);
2083
2084         mailstream_logger = imap_logger_cmd;
2085         mailimap_fetch_type_free(fetch_type);
2086         mailimap_set_free(set);
2087
2088         if (r != MAILIMAP_NO_ERROR) {
2089                 res = r;
2090                 goto err;
2091         }
2092
2093         env_list = NULL;
2094         r = result_to_uid_flags_list(fetch_result, &env_list);
2095         mailimap_fetch_list_free(fetch_result);
2096         
2097         * result = env_list;
2098
2099         return MAILIMAP_NO_ERROR;
2100
2101  free_fetch_type:
2102         mailimap_fetch_type_free(fetch_type);
2103  free_set:
2104         mailimap_set_free(set);
2105  err:
2106         return res;
2107 }
2108
2109
2110
2111 static void fetch_uid_flags_run(struct etpan_thread_op * op)
2112 {
2113         struct fetch_uid_param * param;
2114         struct fetch_uid_result * result;
2115         carray * fetch_result;
2116         int r;
2117         
2118         param = op->param;
2119         result = op->result;
2120
2121         CHECK_IMAP();
2122
2123         fetch_result = NULL;
2124         r = imap_get_messages_flags_list(param->imap, param->first_index,
2125                                          &fetch_result);
2126         
2127         result->error = r;
2128         result->fetch_result = fetch_result;
2129         debug_print("imap fetch_uid run - end %i\n", r);
2130 }
2131
2132 int imap_threaded_fetch_uid_flags(Folder * folder, uint32_t first_index,
2133                                   carray ** fetch_result)
2134 {
2135         struct fetch_uid_param param;
2136         struct fetch_uid_result result;
2137         mailimap * imap;
2138         
2139         debug_print("imap fetch_uid - begin\n");
2140         
2141         imap = get_imap(folder);
2142         param.imap = imap;
2143         param.first_index = first_index;
2144         
2145         mailstream_logger = imap_logger_noop;
2146         log_print(LOG_PROTOCOL, "IMAP4- [fetching flags...]\n");
2147
2148         threaded_run(folder, &param, &result, fetch_uid_flags_run);
2149
2150         mailstream_logger = imap_logger_cmd;
2151
2152         
2153         if (result.error != MAILIMAP_NO_ERROR)
2154                 return result.error;
2155         
2156         debug_print("imap fetch_uid - end\n");
2157         
2158         * fetch_result = result.fetch_result;
2159         
2160         return result.error;
2161 }
2162
2163
2164 void imap_fetch_uid_flags_list_free(carray * uid_flags_list)
2165 {
2166         unsigned int i;
2167         
2168         for(i = 0 ; i < carray_count(uid_flags_list) ; i += 3) {
2169                 void * data;
2170                 
2171                 data = carray_get(uid_flags_list, i);
2172                 free(data);
2173                 data = carray_get(uid_flags_list, i + 1);
2174                 free(data);
2175         }
2176         carray_free(uid_flags_list);
2177 }
2178
2179
2180
2181 static int imap_fetch(mailimap * imap,
2182                       uint32_t msg_index,
2183                       char ** result,
2184                       size_t * result_len)
2185 {
2186         int r;
2187         struct mailimap_set * set;
2188         struct mailimap_fetch_att * fetch_att;
2189         struct mailimap_fetch_type * fetch_type;
2190         clist * fetch_result;
2191         struct mailimap_msg_att * msg_att;
2192         struct mailimap_msg_att_item * msg_att_item;
2193         char * text;
2194         size_t text_length;
2195         int res;
2196         clistiter * cur;
2197         struct mailimap_section * section;
2198
2199         set = mailimap_set_new_single(msg_index);
2200         if (set == NULL) {
2201                 res = MAILIMAP_ERROR_MEMORY;
2202                 goto err;
2203         }
2204
2205         section = mailimap_section_new(NULL);
2206         if (section == NULL) {
2207                 res = MAILIMAP_ERROR_MEMORY;
2208                 goto free_set;
2209         }
2210   
2211         fetch_att = mailimap_fetch_att_new_body_peek_section(section);
2212         if (fetch_att == NULL) {
2213                 mailimap_section_free(section);
2214                 res = MAILIMAP_ERROR_MEMORY;
2215                 goto free_set;
2216         }
2217   
2218         fetch_type = mailimap_fetch_type_new_fetch_att(fetch_att);
2219         if (fetch_type == NULL) {
2220                 res = MAILIMAP_ERROR_MEMORY;
2221                 goto free_fetch_att;
2222         }
2223
2224         mailstream_logger = imap_logger_fetch;
2225         
2226         r = mailimap_uid_fetch(imap, set,
2227                                fetch_type, &fetch_result);
2228   
2229         mailstream_logger = imap_logger_cmd;
2230         
2231         mailimap_fetch_type_free(fetch_type);
2232         mailimap_set_free(set);
2233   
2234         switch (r) {
2235         case MAILIMAP_NO_ERROR:
2236                 break;
2237         default:
2238                 return r;
2239         }
2240   
2241         if (fetch_result == NULL || clist_begin(fetch_result) == NULL) {
2242                 mailimap_fetch_list_free(fetch_result);
2243                 return MAILIMAP_ERROR_FETCH;
2244         }
2245
2246         msg_att = clist_begin(fetch_result)->data;
2247
2248         text = NULL;
2249         text_length = 0;
2250
2251         if (msg_att->att_list)
2252                 cur = clist_begin(msg_att->att_list);
2253         else
2254                 cur = NULL;
2255
2256         for(; cur != NULL ; cur = clist_next(cur)) {
2257                 msg_att_item = clist_content(cur);
2258
2259                 if (msg_att_item->att_type == MAILIMAP_MSG_ATT_ITEM_STATIC) {
2260                         if (msg_att_item->att_data.att_static->att_type ==
2261                             MAILIMAP_MSG_ATT_BODY_SECTION) {
2262                                 text = msg_att_item->att_data.att_static->att_data.att_body_section->sec_body_part;
2263                                 /* detach */
2264                                 msg_att_item->att_data.att_static->att_data.att_body_section->sec_body_part = NULL;
2265                                 text_length =
2266                                         msg_att_item->att_data.att_static->att_data.att_body_section->sec_length;
2267                         }
2268                 }
2269         }
2270
2271         mailimap_fetch_list_free(fetch_result);
2272
2273         if (text == NULL)
2274                 return MAILIMAP_ERROR_FETCH;
2275
2276         * result = text;
2277         * result_len = text_length;
2278   
2279         return MAILIMAP_NO_ERROR;
2280
2281  free_fetch_att:
2282         mailimap_fetch_att_free(fetch_att);
2283  free_set:
2284         mailimap_set_free(set);
2285  err:
2286         return res;
2287 }
2288
2289 static int imap_fetch_header(mailimap * imap,
2290                              uint32_t msg_index,
2291                              char ** result,
2292                              size_t * result_len)
2293 {
2294   int r;
2295   struct mailimap_set * set;
2296   struct mailimap_fetch_att * fetch_att;
2297   struct mailimap_fetch_type * fetch_type;
2298   clist * fetch_result;
2299   struct mailimap_msg_att * msg_att;
2300   struct mailimap_msg_att_item * msg_att_item;
2301   char * text;
2302   size_t text_length;
2303   int res;
2304   clistiter * cur;
2305   struct mailimap_section * section;
2306   
2307   set = mailimap_set_new_single(msg_index);
2308   if (set == NULL) {
2309     res = MAILIMAP_ERROR_MEMORY;
2310     goto err;
2311   }
2312
2313   section = mailimap_section_new_header();
2314   if (section == NULL) {
2315     res = MAILIMAP_ERROR_MEMORY;
2316     goto free_set;
2317   }
2318   
2319   fetch_att = mailimap_fetch_att_new_body_peek_section(section);
2320   if (fetch_att == NULL) {
2321     mailimap_section_free(section);
2322     res = MAILIMAP_ERROR_MEMORY;
2323     goto free_set;
2324   }
2325   
2326   fetch_type = mailimap_fetch_type_new_fetch_att(fetch_att);
2327   if (fetch_type == NULL) {
2328     res = MAILIMAP_ERROR_MEMORY;
2329     goto free_fetch_att;
2330   }
2331
2332   mailstream_logger = imap_logger_fetch;
2333   
2334   r = mailimap_uid_fetch(imap, set, fetch_type, &fetch_result);
2335   
2336   mailstream_logger = imap_logger_cmd;
2337   mailimap_fetch_type_free(fetch_type);
2338   mailimap_set_free(set);
2339
2340   switch (r) {
2341   case MAILIMAP_NO_ERROR:
2342     break;
2343   default:
2344     return r;
2345   }
2346
2347   if (fetch_result == NULL || clist_begin(fetch_result) == NULL) {
2348     mailimap_fetch_list_free(fetch_result);
2349     return MAILIMAP_ERROR_FETCH;
2350   }
2351
2352   msg_att = clist_begin(fetch_result)->data;
2353
2354   text = NULL;
2355   text_length = 0;
2356
2357   if (msg_att->att_list)
2358      cur = clist_begin(msg_att->att_list);
2359   else
2360      cur = NULL;
2361
2362   for(; cur != NULL ; cur = clist_next(cur)) {
2363     msg_att_item = clist_content(cur);
2364
2365     if (msg_att_item->att_type == MAILIMAP_MSG_ATT_ITEM_STATIC) {
2366       if (msg_att_item->att_data.att_static->att_type ==
2367           MAILIMAP_MSG_ATT_BODY_SECTION) {
2368         text = msg_att_item->att_data.att_static->att_data.att_body_section->sec_body_part;
2369         msg_att_item->att_data.att_static->att_data.att_body_section->sec_body_part = NULL;
2370         text_length =
2371           msg_att_item->att_data.att_static->att_data.att_body_section->sec_length;
2372       }
2373     }
2374   }
2375
2376   mailimap_fetch_list_free(fetch_result);
2377
2378   if (text == NULL)
2379     return MAILIMAP_ERROR_FETCH;
2380
2381   * result = text;
2382   * result_len = text_length;
2383
2384   return MAILIMAP_NO_ERROR;
2385
2386  free_fetch_att:
2387   mailimap_fetch_att_free(fetch_att);
2388  free_set:
2389   mailimap_set_free(set);
2390  err:
2391   return res;
2392 }
2393
2394
2395
2396 struct fetch_content_param {
2397         mailimap * imap;
2398         uint32_t msg_index;
2399         const char * filename;
2400         int with_body;
2401 };
2402
2403 struct fetch_content_result {
2404         int error;
2405 };
2406
2407 static void fetch_content_run(struct etpan_thread_op * op)
2408 {
2409         struct fetch_content_param * param;
2410         struct fetch_content_result * result;
2411         char * content;
2412         size_t content_size;
2413         int r;
2414         int fd;
2415         FILE * f;
2416         
2417         param = op->param;
2418         result = op->result;
2419
2420         CHECK_IMAP();
2421
2422         content = NULL;
2423         content_size = 0;
2424         if (param->with_body)
2425                 r = imap_fetch(param->imap, param->msg_index,
2426                                &content, &content_size);
2427         else
2428                 r = imap_fetch_header(param->imap, param->msg_index,
2429                                       &content, &content_size);
2430         
2431         result->error = r;
2432         
2433         if (r == MAILIMAP_NO_ERROR) {
2434                 fd = g_open(param->filename, O_RDWR | O_CREAT, 0600);
2435                 if (fd < 0) {
2436                         result->error = MAILIMAP_ERROR_FETCH;
2437                         goto free;
2438                 }
2439                 
2440                 f = fdopen(fd, "wb");
2441                 if (f == NULL) {
2442                         result->error = MAILIMAP_ERROR_FETCH;
2443                         goto close;
2444                 }
2445                 
2446                 r = fwrite(content, 1, content_size, f);
2447                 if (r < content_size) {
2448                         result->error = MAILIMAP_ERROR_FETCH;
2449                         goto fclose;
2450                 }
2451                 
2452                 r = fclose(f);
2453                 if (r == EOF) {
2454                         result->error = MAILIMAP_ERROR_FETCH;
2455                         goto unlink;
2456                 }
2457                 goto free;
2458                 
2459         fclose:
2460                 fclose(f);
2461                 goto unlink;
2462         close:
2463                 close(fd);
2464         unlink:
2465                 claws_unlink(param->filename);
2466         
2467         free:
2468                 /* mmap_string_unref is a simple free in libetpan
2469                  * when it has MMAP_UNAVAILABLE defined */
2470                 if (mmap_string_unref(content) != 0)
2471                         free(content);
2472         }
2473         
2474         debug_print("imap fetch_content run - end %i\n", result->error);
2475 }
2476
2477 int imap_threaded_fetch_content(Folder * folder, uint32_t msg_index,
2478                                 int with_body,
2479                                 const char * filename)
2480 {
2481         struct fetch_content_param param;
2482         struct fetch_content_result result;
2483         mailimap * imap;
2484         
2485         debug_print("imap fetch_content - begin\n");
2486         
2487         imap = get_imap(folder);
2488         param.imap = imap;
2489         param.msg_index = msg_index;
2490         param.filename = filename;
2491         param.with_body = with_body;
2492         
2493         threaded_run(folder, &param, &result, fetch_content_run);
2494         
2495         if (result.error != MAILIMAP_NO_ERROR)
2496                 return result.error;
2497         
2498         debug_print("imap fetch_content - end\n");
2499         
2500         return result.error;
2501 }
2502
2503
2504
2505 static int imap_flags_to_flags(struct mailimap_msg_att_dynamic * att_dyn, GSList **s_tags)
2506 {
2507         int flags;
2508         clist * flag_list;
2509         clistiter * cur;
2510         GSList *tags = NULL;
2511
2512         flags = MSG_UNREAD;
2513         
2514         flag_list = att_dyn->att_list;
2515         if (flag_list == NULL)
2516                 return flags;
2517         
2518         for(cur = clist_begin(flag_list) ; cur != NULL ;
2519             cur = clist_next(cur)) {
2520                 struct mailimap_flag_fetch * flag_fetch;
2521                         
2522                 flag_fetch = clist_content(cur);
2523                 if (flag_fetch->fl_type == MAILIMAP_FLAG_FETCH_RECENT)
2524                         flags |= MSG_NEW;
2525                 else {
2526                         switch (flag_fetch->fl_flag->fl_type) {
2527                         case MAILIMAP_FLAG_ANSWERED:
2528                                 flags |= MSG_REPLIED;
2529                                 break;
2530                         case MAILIMAP_FLAG_FLAGGED:
2531                                 flags |= MSG_MARKED;
2532                                 break;
2533                         case MAILIMAP_FLAG_DELETED:
2534                                 flags |= MSG_DELETED;
2535                                 break;
2536                         case MAILIMAP_FLAG_SEEN:
2537                                 flags &= ~MSG_UNREAD;
2538                                 flags &= ~MSG_NEW;
2539                                 break;
2540                         case MAILIMAP_FLAG_KEYWORD:
2541                                 if (!strcasecmp(flag_fetch->fl_flag->fl_data.fl_keyword, RTAG_FORWARDED))
2542                                         flags |= MSG_FORWARDED;
2543                                 else if (!strcasecmp(flag_fetch->fl_flag->fl_data.fl_keyword, RTAG_JUNK)) 
2544                                         flags |= MSG_SPAM;
2545                                 else if (!strcasecmp(flag_fetch->fl_flag->fl_data.fl_keyword, RTAG_NON_JUNK) ||
2546                                          !strcasecmp(flag_fetch->fl_flag->fl_data.fl_keyword, RTAG_NO_JUNK) ||
2547                                          !strcasecmp(flag_fetch->fl_flag->fl_data.fl_keyword, RTAG_NOT_JUNK)) 
2548                                         flags &= ~MSG_SPAM;
2549                                 else if (s_tags)
2550                                         tags = g_slist_prepend(tags, g_strdup(flag_fetch->fl_flag->fl_data.fl_keyword));
2551                                 break;
2552                         }
2553                 }
2554         }
2555         if (s_tags)
2556                 *s_tags = tags;
2557         return flags;
2558 }
2559
2560 static int imap_get_msg_att_info(struct mailimap_msg_att * msg_att,
2561                                  uint32_t * puid,
2562                                  char ** pheaders,
2563                                  size_t * pref_size,
2564                                  struct mailimap_msg_att_dynamic ** patt_dyn)
2565 {
2566   clistiter * item_cur;
2567   uint32_t uid;
2568   char * headers;
2569   size_t ref_size;
2570   struct mailimap_msg_att_dynamic * att_dyn;
2571
2572   uid = 0;
2573   headers = NULL;
2574   ref_size = 0;
2575   att_dyn = NULL;
2576
2577   if (msg_att->att_list)
2578      item_cur = clist_begin(msg_att->att_list);
2579   else
2580      item_cur = NULL;
2581   for(; item_cur != NULL ; item_cur = clist_next(item_cur)) {
2582     struct mailimap_msg_att_item * item;
2583
2584     item = clist_content(item_cur);
2585       
2586     switch (item->att_type) {
2587     case MAILIMAP_MSG_ATT_ITEM_STATIC:
2588       switch (item->att_data.att_static->att_type) {
2589       case MAILIMAP_MSG_ATT_UID:
2590         uid = item->att_data.att_static->att_data.att_uid;
2591         break;
2592
2593       case MAILIMAP_MSG_ATT_BODY_SECTION:
2594         if (headers == NULL) {
2595           headers = item->att_data.att_static->att_data.att_body_section->sec_body_part;
2596         }
2597         break;
2598       case MAILIMAP_MSG_ATT_RFC822_SIZE:
2599               ref_size = item->att_data.att_static->att_data.att_rfc822_size;
2600               break;
2601       }
2602       break;
2603       
2604     case MAILIMAP_MSG_ATT_ITEM_DYNAMIC:
2605       if (att_dyn == NULL) {
2606         att_dyn = item->att_data.att_dyn;
2607       }
2608       break;
2609     }
2610   }
2611
2612   if (puid != NULL)
2613     * puid = uid;
2614   if (pheaders != NULL)
2615     * pheaders = headers;
2616   if (pref_size != NULL)
2617     * pref_size = ref_size;
2618   if (patt_dyn != NULL)
2619     * patt_dyn = att_dyn;
2620
2621   return MAIL_NO_ERROR;
2622 }
2623
2624 static struct imap_fetch_env_info *
2625 fetch_to_env_info(struct mailimap_msg_att * msg_att, GSList **tags)
2626 {
2627         struct imap_fetch_env_info * info;
2628         uint32_t uid;
2629         char * headers;
2630         size_t size;
2631         struct mailimap_msg_att_dynamic * att_dyn;
2632
2633         imap_get_msg_att_info(msg_att, &uid, &headers, &size,
2634                               &att_dyn);
2635         
2636         if (!headers)
2637                 return NULL;
2638         info = malloc(sizeof(* info));
2639         info->uid = uid;
2640         info->headers = strdup(headers);
2641         info->size = size;
2642         info->flags = imap_flags_to_flags(att_dyn, tags);
2643         
2644         return info;
2645 }
2646
2647 static int
2648 imap_fetch_result_to_envelop_list(clist * fetch_result,
2649                                   carray ** p_env_list)
2650 {
2651         clistiter * cur;
2652         carray * env_list;
2653
2654         env_list = carray_new(16);
2655   
2656         if (fetch_result) {
2657                 for(cur = clist_begin(fetch_result) ; cur != NULL ;
2658                     cur = clist_next(cur)) {
2659                         struct mailimap_msg_att * msg_att;
2660                         struct imap_fetch_env_info * env_info;
2661                         GSList *tags = NULL;
2662
2663                         msg_att = clist_content(cur);
2664
2665                         env_info = fetch_to_env_info(msg_att, &tags);
2666                         if (!env_info)
2667                                 return MAILIMAP_ERROR_MEMORY;
2668                         carray_add(env_list, env_info, NULL);
2669                         carray_add(env_list, tags, NULL);
2670                 }
2671                 * p_env_list = env_list;
2672         } else {
2673                 * p_env_list = NULL;
2674         }
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 (fetch_result == NULL || 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 = g_open(param->filename, O_RDONLY, 0);
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 = g_open("/dev/tty", O_RDONLY, 0);
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(gboolean have_connectivity)
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