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