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