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