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