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