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