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