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