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