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