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