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