97dee7c1f8d182e0ba7440c8c3ff813bab3ac696
[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                          guint mask)
577 {
578         struct status_param param;
579         struct status_result result;
580         struct mailimap_status_att_list * status_att_list;
581         
582         debug_print("imap status - begin\n");
583         
584         status_att_list = mailimap_status_att_list_new_empty();
585         if (mask & 1 << 0) {
586                 mailimap_status_att_list_add(status_att_list,
587                                      MAILIMAP_STATUS_ATT_MESSAGES);
588         }
589         if (mask & 1 << 1) {
590                 mailimap_status_att_list_add(status_att_list,
591                                      MAILIMAP_STATUS_ATT_RECENT);
592         }
593         if (mask & 1 << 2) {
594                 mailimap_status_att_list_add(status_att_list,
595                                      MAILIMAP_STATUS_ATT_UIDNEXT);
596         }
597         if (mask & 1 << 3) {
598                 mailimap_status_att_list_add(status_att_list,
599                                      MAILIMAP_STATUS_ATT_UIDVALIDITY);
600         }
601         if (mask & 1 << 4) {
602                 mailimap_status_att_list_add(status_att_list,
603                                      MAILIMAP_STATUS_ATT_UNSEEN);
604         }
605         param.imap = get_imap(folder);
606         param.mb = mb;
607         param.status_att_list = status_att_list;
608         
609         threaded_run(folder, &param, &result, status_run);
610         
611         debug_print("imap status - end\n");
612         
613         * data_status = result.data_status;
614         
615         return result.error;
616 }
617
618
619
620 struct noop_param {
621         mailimap * imap;
622 };
623
624 struct noop_result {
625         int error;
626 };
627
628 static void noop_run(struct etpan_thread_op * op)
629 {
630         struct noop_param * param;
631         struct noop_result * result;
632         int r;
633         
634         param = op->param;
635         r = mailimap_noop(param->imap);
636         
637         result = op->result;
638         result->error = r;
639         debug_print("imap noop run - end %i\n", r);
640 }
641
642 int imap_threaded_noop(Folder * folder, unsigned int * p_exists)
643 {
644         struct noop_param param;
645         struct noop_result result;
646         mailimap * imap;
647         
648         debug_print("imap noop - begin\n");
649         
650         imap = get_imap(folder);
651         param.imap = imap;
652         
653         threaded_run(folder, &param, &result, noop_run);
654         
655         if (imap->imap_selection_info != NULL) {
656                 * p_exists = imap->imap_selection_info->sel_exists;
657         }
658         else {
659                 * p_exists = 0;
660         }
661         
662         debug_print("imap noop - end\n");
663         
664         return result.error;
665 }
666
667
668 struct starttls_param {
669         mailimap * imap;
670 };
671
672 struct starttls_result {
673         int error;
674 };
675
676 static void starttls_run(struct etpan_thread_op * op)
677 {
678         struct starttls_param * param;
679         struct starttls_result * result;
680         int r;
681         
682         param = op->param;
683         r = mailimap_starttls(param->imap);
684         
685         result = op->result;
686         result->error = r;
687         debug_print("imap starttls run - end %i\n", r);
688         
689         if (r == 0) {
690                 mailimap *imap = param->imap;
691                 mailstream_low *plain_low = NULL;
692                 mailstream_low *tls_low = NULL;
693                 int fd = -1;
694                 
695                 plain_low = mailstream_get_low(imap->imap_stream);
696                 fd = mailstream_low_get_fd(plain_low);
697                 if (fd == -1) {
698                         debug_print("imap starttls run - can't get fd\n");
699                         result->error = MAILIMAP_ERROR_STREAM;
700                         return;
701                 }
702                 tls_low = mailstream_low_ssl_open(fd);
703                 if (tls_low == NULL) {
704                         debug_print("imap starttls run - can't ssl_open\n");
705                         result->error = MAILIMAP_ERROR_STREAM;
706                         return;
707                 }
708                 mailstream_low_free(plain_low);
709                 mailstream_set_low(imap->imap_stream, tls_low);
710         }
711 }
712
713 int imap_threaded_starttls(Folder * folder)
714 {
715         struct starttls_param param;
716         struct starttls_result result;
717         
718         debug_print("imap starttls - begin\n");
719         
720         param.imap = get_imap(folder);
721         
722         threaded_run(folder, &param, &result, starttls_run);
723         
724         debug_print("imap starttls - end\n");
725         
726         return result.error;
727 }
728
729
730
731 struct create_param {
732         mailimap * imap;
733         const char * mb;
734 };
735
736 struct create_result {
737         int error;
738 };
739
740 static void create_run(struct etpan_thread_op * op)
741 {
742         struct create_param * param;
743         struct create_result * result;
744         int r;
745         
746         param = op->param;
747         r = mailimap_create(param->imap, param->mb);
748         
749         result = op->result;
750         result->error = r;
751         debug_print("imap create run - end %i\n", r);
752 }
753
754 int imap_threaded_create(Folder * folder, const char * mb)
755 {
756         struct create_param param;
757         struct create_result result;
758         
759         debug_print("imap create - begin\n");
760         
761         param.imap = get_imap(folder);
762         param.mb = mb;
763         
764         threaded_run(folder, &param, &result, create_run);
765         
766         debug_print("imap create - end\n");
767         
768         return result.error;
769 }
770
771
772
773
774 struct rename_param {
775         mailimap * imap;
776         const char * mb;
777         const char * new_name;
778 };
779
780 struct rename_result {
781         int error;
782 };
783
784 static void rename_run(struct etpan_thread_op * op)
785 {
786         struct rename_param * param;
787         struct rename_result * result;
788         int r;
789         
790         param = op->param;
791         r = mailimap_rename(param->imap, param->mb, param->new_name);
792         
793         result = op->result;
794         result->error = r;
795         debug_print("imap rename run - end %i\n", r);
796 }
797
798 int imap_threaded_rename(Folder * folder,
799                          const char * mb, const char * new_name)
800 {
801         struct rename_param param;
802         struct rename_result result;
803         
804         debug_print("imap rename - begin\n");
805         
806         param.imap = get_imap(folder);
807         param.mb = mb;
808         param.new_name = new_name;
809         
810         threaded_run(folder, &param, &result, rename_run);
811         
812         debug_print("imap rename - end\n");
813         
814         return result.error;
815 }
816
817
818
819
820 struct delete_param {
821         mailimap * imap;
822         const char * mb;
823 };
824
825 struct delete_result {
826         int error;
827 };
828
829 static void delete_run(struct etpan_thread_op * op)
830 {
831         struct delete_param * param;
832         struct delete_result * result;
833         int r;
834         
835         param = op->param;
836         r = mailimap_delete(param->imap, param->mb);
837         
838         result = op->result;
839         result->error = r;
840         debug_print("imap delete run - end %i\n", r);
841 }
842
843 int imap_threaded_delete(Folder * folder, const char * mb)
844 {
845         struct delete_param param;
846         struct delete_result result;
847         
848         debug_print("imap delete - begin\n");
849         
850         param.imap = get_imap(folder);
851         param.mb = mb;
852         
853         threaded_run(folder, &param, &result, delete_run);
854         
855         debug_print("imap delete - end\n");
856         
857         return result.error;
858 }
859
860
861
862 struct select_param {
863         mailimap * imap;
864         const char * mb;
865 };
866
867 struct select_result {
868         int error;
869 };
870
871 static void select_run(struct etpan_thread_op * op)
872 {
873         struct select_param * param;
874         struct select_result * result;
875         int r;
876         
877         param = op->param;
878         r = mailimap_select(param->imap, param->mb);
879         
880         result = op->result;
881         result->error = r;
882         debug_print("imap select run - end %i\n", r);
883 }
884
885 int imap_threaded_select(Folder * folder, const char * mb,
886                          gint * exists, gint * recent, gint * unseen,
887                          guint32 * uid_validity)
888 {
889         struct select_param param;
890         struct select_result result;
891         mailimap * imap;
892         
893         debug_print("imap select - begin\n");
894         
895         imap = get_imap(folder);
896         param.imap = imap;
897         param.mb = mb;
898         
899         threaded_run(folder, &param, &result, select_run);
900         
901         if (result.error != MAILIMAP_NO_ERROR)
902                 return result.error;
903         
904         if (imap->imap_selection_info == NULL)
905                 return MAILIMAP_ERROR_PARSE;
906         
907         * exists = imap->imap_selection_info->sel_exists;
908         * recent = imap->imap_selection_info->sel_recent;
909         * unseen = imap->imap_selection_info->sel_unseen;
910         * uid_validity = imap->imap_selection_info->sel_uidvalidity;
911         
912         debug_print("imap select - end\n");
913         
914         return result.error;
915 }
916
917
918
919 struct examine_param {
920         mailimap * imap;
921         const char * mb;
922 };
923
924 struct examine_result {
925         int error;
926 };
927
928 static void examine_run(struct etpan_thread_op * op)
929 {
930         struct examine_param * param;
931         struct examine_result * result;
932         int r;
933         
934         param = op->param;
935         r = mailimap_examine(param->imap, param->mb);
936         
937         result = op->result;
938         result->error = r;
939         debug_print("imap examine run - end %i\n", r);
940 }
941
942 int imap_threaded_examine(Folder * folder, const char * mb,
943                           gint * exists, gint * recent, gint * unseen,
944                           guint32 * uid_validity)
945 {
946         struct examine_param param;
947         struct examine_result result;
948         mailimap * imap;
949         
950         debug_print("imap examine - begin\n");
951         
952         imap = get_imap(folder);
953         param.imap = imap;
954         param.mb = mb;
955         
956         threaded_run(folder, &param, &result, examine_run);
957         
958         if (result.error != MAILIMAP_NO_ERROR)
959                 return result.error;
960         
961         if (imap->imap_selection_info == NULL)
962                 return MAILIMAP_ERROR_PARSE;
963         
964         * exists = imap->imap_selection_info->sel_exists;
965         * recent = imap->imap_selection_info->sel_recent;
966         * unseen = imap->imap_selection_info->sel_unseen;
967         * uid_validity = imap->imap_selection_info->sel_uidvalidity;
968         
969         debug_print("imap examine - end\n");
970         
971         return result.error;
972 }
973
974
975
976
977 struct search_param {
978         mailimap * imap;
979         int type;
980         struct mailimap_set * set;
981 };
982
983 struct search_result {
984         int error;
985         clist * search_result;
986 };
987
988 static void search_run(struct etpan_thread_op * op)
989 {
990         struct search_param * param;
991         struct search_result * result;
992         int r;
993         struct mailimap_search_key * key;
994         struct mailimap_search_key * uid_key;
995         struct mailimap_search_key * search_type_key;
996         clist * search_result;
997         
998         param = op->param;
999         
1000         uid_key = mailimap_search_key_new_uid(param->set);
1001         
1002         search_type_key = NULL;
1003         switch (param->type) {
1004         case IMAP_SEARCH_TYPE_SIMPLE:
1005                 search_type_key = NULL;
1006                 break;
1007                 
1008         case IMAP_SEARCH_TYPE_SEEN:
1009                 search_type_key = mailimap_search_key_new(MAILIMAP_SEARCH_KEY_SEEN,
1010                                                           NULL, NULL, NULL, NULL, NULL,
1011                                                           NULL, NULL, NULL, NULL, NULL,
1012                                                           NULL, NULL, NULL, NULL, 0,
1013                                                           NULL, NULL, NULL, NULL, NULL,
1014                                                           NULL, 0, NULL, NULL, NULL);
1015                 break;
1016                 
1017         case IMAP_SEARCH_TYPE_UNSEEN:
1018                 search_type_key = mailimap_search_key_new(MAILIMAP_SEARCH_KEY_UNSEEN,
1019                                                           NULL, NULL, NULL, NULL, NULL,
1020                                                           NULL, NULL, NULL, NULL, NULL,
1021                                                           NULL, NULL, NULL, NULL, 0,
1022                                                           NULL, NULL, NULL, NULL, NULL,
1023                                                           NULL, 0, NULL, NULL, NULL);
1024                 break;
1025                 
1026         case IMAP_SEARCH_TYPE_ANSWERED:
1027                 search_type_key = mailimap_search_key_new(MAILIMAP_SEARCH_KEY_ANSWERED,
1028                                                           NULL, NULL, NULL, NULL, NULL,
1029                                                           NULL, NULL, NULL, NULL, NULL,
1030                                                           NULL, NULL, NULL, NULL, 0,
1031                                                           NULL, NULL, NULL, NULL, NULL,
1032                                                           NULL, 0, NULL, NULL, NULL);
1033                 break;
1034                 
1035         case IMAP_SEARCH_TYPE_FLAGGED:
1036                 search_type_key = mailimap_search_key_new(MAILIMAP_SEARCH_KEY_FLAGGED,
1037                                                           NULL, NULL, NULL, NULL, NULL,
1038                                                           NULL, NULL, NULL, NULL, NULL,
1039                                                           NULL, NULL, NULL, NULL, 0,
1040                                                           NULL, NULL, NULL, NULL, NULL,
1041                                                           NULL, 0, NULL, NULL, NULL);
1042                 break;
1043         case IMAP_SEARCH_TYPE_DELETED:
1044                 search_type_key = mailimap_search_key_new(MAILIMAP_SEARCH_KEY_DELETED,
1045                                                           NULL, NULL, NULL, NULL, NULL,
1046                                                           NULL, NULL, NULL, NULL, NULL,
1047                                                           NULL, NULL, NULL, NULL, 0,
1048                                                           NULL, NULL, NULL, NULL, NULL,
1049                                                           NULL, 0, NULL, NULL, NULL);
1050                 break;
1051         }
1052         
1053         if (search_type_key != NULL) {
1054                 key = mailimap_search_key_new_multiple_empty();
1055                 mailimap_search_key_multiple_add(key, search_type_key);
1056                 mailimap_search_key_multiple_add(key, uid_key);
1057         }
1058         else {
1059                 key = uid_key;
1060         }
1061         
1062         r = mailimap_uid_search(param->imap, NULL, key, &search_result);
1063         
1064         result = op->result;
1065         result->error = r;
1066         result->search_result = search_result;
1067         debug_print("imap search run - end %i\n", r);
1068 }
1069
1070 int imap_threaded_search(Folder * folder, int search_type,
1071                          struct mailimap_set * set, clist ** search_result)
1072 {
1073         struct search_param param;
1074         struct search_result result;
1075         mailimap * imap;
1076         
1077         debug_print("imap search - begin\n");
1078
1079         imap = get_imap(folder);
1080         param.imap = imap;
1081         param.set = set;
1082         param.type = search_type;
1083         
1084         threaded_run(folder, &param, &result, search_run);
1085         
1086         if (result.error != MAILIMAP_NO_ERROR)
1087                 return result.error;
1088         
1089         debug_print("imap search - end\n");
1090         
1091         * search_result = result.search_result;
1092         
1093         return result.error;
1094 }
1095
1096
1097
1098
1099 static int
1100 uid_list_to_env_list(clist * fetch_result, carray ** result)
1101 {
1102         clistiter * cur;
1103         int r;
1104         int res;
1105         carray * tab;
1106         unsigned int i;
1107         
1108         tab = carray_new(128);
1109         if (tab == NULL) {
1110                 res = MAILIMAP_ERROR_MEMORY;
1111                 goto err;
1112         }
1113         
1114         for(cur = clist_begin(fetch_result) ; cur != NULL ;
1115             cur = clist_next(cur)) {
1116                 struct mailimap_msg_att * msg_att;
1117                 clistiter * item_cur;
1118                 uint32_t uid;
1119                 size_t size;
1120                 uint32_t * puid;
1121                 
1122                 msg_att = clist_content(cur);
1123                 
1124                 uid = 0;
1125                 size = 0;
1126                 for(item_cur = clist_begin(msg_att->att_list) ;
1127                     item_cur != NULL ;
1128                     item_cur = clist_next(item_cur)) {
1129                         struct mailimap_msg_att_item * item;
1130                         
1131                         item = clist_content(item_cur);
1132                         
1133                         switch (item->att_type) {
1134                         case MAILIMAP_MSG_ATT_ITEM_STATIC:
1135                                 switch (item->att_data.att_static->att_type) {
1136                                 case MAILIMAP_MSG_ATT_UID:
1137                                         uid = item->att_data.att_static->att_data.att_uid;
1138                                         break;
1139                                 }
1140                         }
1141                 }
1142                 
1143                 puid = malloc(sizeof(* puid));
1144                 if (puid == NULL) {
1145                         res = MAILIMAP_ERROR_MEMORY;
1146                         goto free_list;
1147                 }
1148                 * puid = uid;
1149                         
1150                 r = carray_add(tab, puid, NULL);
1151                 if (r < 0) {
1152                         free(puid);
1153                         res = MAILIMAP_ERROR_MEMORY;
1154                         goto free_list;
1155                 }
1156         }
1157                 
1158         * result = tab;
1159
1160         return MAILIMAP_NO_ERROR;
1161   
1162  free_list:
1163         for(i = 0 ; i < carray_count(tab) ; i++)
1164                 mailmessage_free(carray_get(tab, i));
1165  err:
1166         return res;
1167 }
1168
1169 static int imap_get_messages_list(mailimap * imap,
1170                                   uint32_t first_index,
1171                                   carray ** result)
1172 {
1173         carray * env_list;
1174         int r;
1175         struct mailimap_fetch_att * fetch_att;
1176         struct mailimap_fetch_type * fetch_type;
1177         struct mailimap_set * set;
1178         clist * fetch_result;
1179         int res;
1180         
1181         set = mailimap_set_new_interval(first_index, 0);
1182         if (set == NULL) {
1183                 res = MAILIMAP_ERROR_MEMORY;
1184                 goto err;
1185         }
1186
1187         fetch_type = mailimap_fetch_type_new_fetch_att_list_empty();
1188         if (fetch_type == NULL) {
1189                 res = MAILIMAP_ERROR_MEMORY;
1190                 goto free_set;
1191         }
1192
1193         fetch_att = mailimap_fetch_att_new_uid();
1194         if (fetch_att == NULL) {
1195                 res = MAILIMAP_ERROR_MEMORY;
1196                 goto free_fetch_type;
1197         }
1198
1199         r = mailimap_fetch_type_new_fetch_att_list_add(fetch_type, fetch_att);
1200         if (r != MAILIMAP_NO_ERROR) {
1201                 mailimap_fetch_att_free(fetch_att);
1202                 res = MAILIMAP_ERROR_MEMORY;
1203                 goto free_fetch_type;
1204         }
1205
1206         r = mailimap_uid_fetch(imap, set,
1207                                fetch_type, &fetch_result);
1208
1209         mailimap_fetch_type_free(fetch_type);
1210         mailimap_set_free(set);
1211
1212         if (r != MAILIMAP_NO_ERROR) {
1213                 res = r;
1214                 goto err;
1215         }
1216
1217         env_list = NULL;
1218         r = uid_list_to_env_list(fetch_result, &env_list);
1219         mailimap_fetch_list_free(fetch_result);
1220         
1221         * result = env_list;
1222
1223         return MAILIMAP_NO_ERROR;
1224
1225  free_fetch_type:
1226         mailimap_fetch_type_free(fetch_type);
1227  free_set:
1228         mailimap_set_free(set);
1229  err:
1230         return res;
1231 }
1232
1233
1234
1235
1236 struct fetch_uid_param {
1237         mailimap * imap;
1238         uint32_t first_index;
1239 };
1240
1241 struct fetch_uid_result {
1242         int error;
1243         carray * fetch_result;
1244 };
1245
1246 static void fetch_uid_run(struct etpan_thread_op * op)
1247 {
1248         struct fetch_uid_param * param;
1249         struct fetch_uid_result * result;
1250         carray * fetch_result;
1251         int r;
1252         
1253         param = op->param;
1254         
1255         fetch_result = NULL;
1256         r = imap_get_messages_list(param->imap, param->first_index,
1257                                    &fetch_result);
1258         
1259         result = op->result;
1260         result->error = r;
1261         result->fetch_result = fetch_result;
1262         debug_print("imap fetch_uid run - end %i\n", r);
1263 }
1264
1265 int imap_threaded_fetch_uid(Folder * folder, uint32_t first_index,
1266                             carray ** fetch_result)
1267 {
1268         struct fetch_uid_param param;
1269         struct fetch_uid_result result;
1270         mailimap * imap;
1271         
1272         debug_print("imap fetch_uid - begin\n");
1273         
1274         imap = get_imap(folder);
1275         param.imap = imap;
1276         param.first_index = first_index;
1277         
1278         threaded_run(folder, &param, &result, fetch_uid_run);
1279         
1280         if (result.error != MAILIMAP_NO_ERROR)
1281                 return result.error;
1282         
1283         debug_print("imap fetch_uid - end\n");
1284         
1285         * fetch_result = result.fetch_result;
1286         
1287         return result.error;
1288 }
1289
1290
1291 void imap_fetch_uid_list_free(carray * uid_list)
1292 {
1293         unsigned int i;
1294         
1295         for(i = 0 ; i < carray_count(uid_list) ; i ++) {
1296                 uint32_t * puid;
1297                 
1298                 puid = carray_get(uid_list, i);
1299                 free(puid);
1300         }
1301         carray_free(uid_list);
1302 }
1303
1304
1305
1306 static int imap_fetch(mailimap * imap,
1307                       uint32_t msg_index,
1308                       char ** result,
1309                       size_t * result_len)
1310 {
1311         int r;
1312         struct mailimap_set * set;
1313         struct mailimap_fetch_att * fetch_att;
1314         struct mailimap_fetch_type * fetch_type;
1315         clist * fetch_result;
1316         struct mailimap_msg_att * msg_att;
1317         struct mailimap_msg_att_item * msg_att_item;
1318         char * text;
1319         size_t text_length;
1320         int res;
1321         clistiter * cur;
1322         struct mailimap_section * section;
1323
1324         set = mailimap_set_new_single(msg_index);
1325         if (set == NULL) {
1326                 res = MAILIMAP_ERROR_MEMORY;
1327                 goto err;
1328         }
1329
1330         section = mailimap_section_new(NULL);
1331         if (section == NULL) {
1332                 res = MAILIMAP_ERROR_MEMORY;
1333                 goto free_set;
1334         }
1335   
1336         fetch_att = mailimap_fetch_att_new_body_peek_section(section);
1337         if (fetch_att == NULL) {
1338                 mailimap_section_free(section);
1339                 res = MAILIMAP_ERROR_MEMORY;
1340                 goto free_set;
1341         }
1342   
1343         fetch_type = mailimap_fetch_type_new_fetch_att(fetch_att);
1344         if (fetch_type == NULL) {
1345                 res = MAILIMAP_ERROR_MEMORY;
1346                 goto free_fetch_att;
1347         }
1348
1349         r = mailimap_uid_fetch(imap, set,
1350                                fetch_type, &fetch_result);
1351   
1352         mailimap_fetch_type_free(fetch_type);
1353         mailimap_set_free(set);
1354   
1355         switch (r) {
1356         case MAILIMAP_NO_ERROR:
1357                 break;
1358         default:
1359                 return r;
1360         }
1361   
1362         if (clist_begin(fetch_result) == NULL) {
1363                 mailimap_fetch_list_free(fetch_result);
1364                 return MAILIMAP_ERROR_FETCH;
1365         }
1366
1367         msg_att = clist_begin(fetch_result)->data;
1368
1369         text = NULL;
1370         text_length = 0;
1371
1372         for(cur = clist_begin(msg_att->att_list) ; cur != NULL ;
1373             cur = clist_next(cur)) {
1374                 msg_att_item = clist_content(cur);
1375
1376                 if (msg_att_item->att_type == MAILIMAP_MSG_ATT_ITEM_STATIC) {
1377                         if (msg_att_item->att_data.att_static->att_type ==
1378                             MAILIMAP_MSG_ATT_BODY_SECTION) {
1379                                 text = msg_att_item->att_data.att_static->att_data.att_body_section->sec_body_part;
1380                                 /* detach */
1381                                 msg_att_item->att_data.att_static->att_data.att_body_section->sec_body_part = NULL;
1382                                 text_length =
1383                                         msg_att_item->att_data.att_static->att_data.att_body_section->sec_length;
1384                         }
1385                 }
1386         }
1387
1388         mailimap_fetch_list_free(fetch_result);
1389
1390         if (text == NULL)
1391                 return MAILIMAP_ERROR_FETCH;
1392
1393         * result = text;
1394         * result_len = text_length;
1395   
1396         return MAILIMAP_NO_ERROR;
1397
1398  free_fetch_att:
1399         mailimap_fetch_att_free(fetch_att);
1400  free_set:
1401         mailimap_set_free(set);
1402  err:
1403         return res;
1404 }
1405
1406 static int imap_fetch_header(mailimap * imap,
1407                              uint32_t msg_index,
1408                              char ** result,
1409                              size_t * result_len)
1410 {
1411   int r;
1412   struct mailimap_set * set;
1413   struct mailimap_fetch_att * fetch_att;
1414   struct mailimap_fetch_type * fetch_type;
1415   clist * fetch_result;
1416   struct mailimap_msg_att * msg_att;
1417   struct mailimap_msg_att_item * msg_att_item;
1418   char * text;
1419   size_t text_length;
1420   int res;
1421   clistiter * cur;
1422   struct mailimap_section * section;
1423   
1424   set = mailimap_set_new_single(msg_index);
1425   if (set == NULL) {
1426     res = MAILIMAP_ERROR_MEMORY;
1427     goto err;
1428   }
1429
1430   section = mailimap_section_new_header();
1431   if (section == NULL) {
1432     res = MAILIMAP_ERROR_MEMORY;
1433     goto free_set;
1434   }
1435   
1436   fetch_att = mailimap_fetch_att_new_body_peek_section(section);
1437   if (fetch_att == NULL) {
1438     mailimap_section_free(section);
1439     res = MAILIMAP_ERROR_MEMORY;
1440     goto free_set;
1441   }
1442   
1443   fetch_type = mailimap_fetch_type_new_fetch_att(fetch_att);
1444   if (fetch_type == NULL) {
1445     res = MAILIMAP_ERROR_MEMORY;
1446     goto free_fetch_att;
1447   }
1448
1449   r = mailimap_uid_fetch(imap, set, fetch_type, &fetch_result);
1450   
1451   mailimap_fetch_type_free(fetch_type);
1452   mailimap_set_free(set);
1453
1454   switch (r) {
1455   case MAILIMAP_NO_ERROR:
1456     break;
1457   default:
1458     return r;
1459   }
1460
1461   if (clist_begin(fetch_result) == NULL) {
1462     mailimap_fetch_list_free(fetch_result);
1463     return MAILIMAP_ERROR_FETCH;
1464   }
1465
1466   msg_att = clist_begin(fetch_result)->data;
1467
1468   text = NULL;
1469   text_length = 0;
1470
1471   for(cur = clist_begin(msg_att->att_list) ; cur != NULL ;
1472       cur = clist_next(cur)) {
1473     msg_att_item = clist_content(cur);
1474
1475     if (msg_att_item->att_type == MAILIMAP_MSG_ATT_ITEM_STATIC) {
1476       if (msg_att_item->att_data.att_static->att_type ==
1477           MAILIMAP_MSG_ATT_BODY_SECTION) {
1478         text = msg_att_item->att_data.att_static->att_data.att_body_section->sec_body_part;
1479         msg_att_item->att_data.att_static->att_data.att_body_section->sec_body_part = NULL;
1480         text_length =
1481           msg_att_item->att_data.att_static->att_data.att_body_section->sec_length;
1482       }
1483     }
1484   }
1485
1486   mailimap_fetch_list_free(fetch_result);
1487
1488   if (text == NULL)
1489     return MAILIMAP_ERROR_FETCH;
1490
1491   * result = text;
1492   * result_len = text_length;
1493
1494   return MAILIMAP_NO_ERROR;
1495
1496  free_fetch_att:
1497   mailimap_fetch_att_free(fetch_att);
1498  free_set:
1499   mailimap_set_free(set);
1500  err:
1501   return res;
1502 }
1503
1504
1505
1506 struct fetch_content_param {
1507         mailimap * imap;
1508         uint32_t msg_index;
1509         const char * filename;
1510         int with_body;
1511 };
1512
1513 struct fetch_content_result {
1514         int error;
1515 };
1516
1517 static void fetch_content_run(struct etpan_thread_op * op)
1518 {
1519         struct fetch_content_param * param;
1520         struct fetch_content_result * result;
1521         char * content;
1522         size_t content_size;
1523         int r;
1524         int fd;
1525         FILE * f;
1526         
1527         param = op->param;
1528         
1529         content = NULL;
1530         content_size = 0;
1531         if (param->with_body)
1532                 r = imap_fetch(param->imap, param->msg_index,
1533                                &content, &content_size);
1534         else
1535                 r = imap_fetch_header(param->imap, param->msg_index,
1536                                       &content, &content_size);
1537         
1538         result = op->result;
1539         result->error = r;
1540         
1541         if (r == MAILIMAP_NO_ERROR) {
1542                 fd = open(param->filename, O_RDWR | O_CREAT, 0600);
1543                 if (fd < 0) {
1544                         result->error = MAILIMAP_ERROR_FETCH;
1545                         goto free;
1546                 }
1547                 
1548                 f = fdopen(fd, "wb");
1549                 if (f == NULL) {
1550                         result->error = MAILIMAP_ERROR_FETCH;
1551                         goto close;
1552                 }
1553                 
1554                 r = fwrite(content, 1, content_size, f);
1555                 if (r == 0) {
1556                         goto fclose;
1557                 }
1558                 
1559                 r = fclose(f);
1560                 if (r == EOF) {
1561                         g_unlink(param->filename);
1562                         goto close;
1563                 }
1564                 goto free;
1565                 
1566         fclose:
1567                 fclose(f);
1568                 goto unlink;
1569         close:
1570                 close(fd);
1571         unlink:
1572                 g_unlink(param->filename);
1573         
1574         free:
1575                 if (mmap_string_unref(content) != 0)
1576                         free(content);
1577         }
1578         
1579         debug_print("imap fetch_content run - end %i\n", r);
1580 }
1581
1582 int imap_threaded_fetch_content(Folder * folder, uint32_t msg_index,
1583                                 int with_body,
1584                                 const char * filename)
1585 {
1586         struct fetch_content_param param;
1587         struct fetch_content_result result;
1588         mailimap * imap;
1589         
1590         debug_print("imap fetch_content - begin\n");
1591         
1592         imap = get_imap(folder);
1593         param.imap = imap;
1594         param.msg_index = msg_index;
1595         param.filename = filename;
1596         param.with_body = with_body;
1597         
1598         threaded_run(folder, &param, &result, fetch_content_run);
1599         
1600         if (result.error != MAILIMAP_NO_ERROR)
1601                 return result.error;
1602         
1603         debug_print("imap fetch_content - end\n");
1604         
1605         return result.error;
1606 }
1607
1608
1609
1610 static int imap_flags_to_flags(struct mailimap_msg_att_dynamic * att_dyn)
1611 {
1612         int flags;
1613         clist * flag_list;
1614         clistiter * cur;
1615         
1616         flags = MSG_UNREAD;
1617         
1618         flag_list = att_dyn->att_list;
1619         if (flag_list == NULL)
1620                 return flags;
1621         
1622         for(cur = clist_begin(flag_list) ; cur != NULL ;
1623             cur = clist_next(cur)) {
1624                 struct mailimap_flag_fetch * flag_fetch;
1625                         
1626                 flag_fetch = clist_content(cur);
1627                 if (flag_fetch->fl_type == MAILIMAP_FLAG_FETCH_RECENT)
1628                         flags |= MSG_NEW;
1629                 else {
1630                         switch (flag_fetch->fl_flag->fl_type) {
1631                         case MAILIMAP_FLAG_ANSWERED:
1632                                 flags |= MSG_REPLIED;
1633                                 break;
1634                         case MAILIMAP_FLAG_FLAGGED:
1635                                 flags |= MSG_MARKED;
1636                                 break;
1637                         case MAILIMAP_FLAG_DELETED:
1638                                 flags |= MSG_DELETED;
1639                                 break;
1640                         case MAILIMAP_FLAG_SEEN:
1641                                 flags &= ~MSG_UNREAD;
1642                                 flags &= ~MSG_NEW;
1643                                 break;
1644                         }
1645                 }
1646         }
1647         
1648         return flags;
1649 }
1650
1651 static int imap_get_msg_att_info(struct mailimap_msg_att * msg_att,
1652                                  uint32_t * puid,
1653                                  char ** pheaders,
1654                                  size_t * pref_size,
1655                                  struct mailimap_msg_att_dynamic ** patt_dyn)
1656 {
1657   clistiter * item_cur;
1658   uint32_t uid;
1659   char * headers;
1660   size_t ref_size;
1661   struct mailimap_msg_att_dynamic * att_dyn;
1662
1663   uid = 0;
1664   headers = NULL;
1665   ref_size = 0;
1666   att_dyn = NULL;
1667
1668   for(item_cur = clist_begin(msg_att->att_list) ; item_cur != NULL ;
1669       item_cur = clist_next(item_cur)) {
1670     struct mailimap_msg_att_item * item;
1671
1672     item = clist_content(item_cur);
1673       
1674     switch (item->att_type) {
1675     case MAILIMAP_MSG_ATT_ITEM_STATIC:
1676       switch (item->att_data.att_static->att_type) {
1677       case MAILIMAP_MSG_ATT_UID:
1678         uid = item->att_data.att_static->att_data.att_uid;
1679         break;
1680
1681       case MAILIMAP_MSG_ATT_BODY_SECTION:
1682         if (headers == NULL) {
1683           headers = item->att_data.att_static->att_data.att_body_section->sec_body_part;
1684         }
1685         break;
1686       case MAILIMAP_MSG_ATT_RFC822_SIZE:
1687               ref_size = item->att_data.att_static->att_data.att_rfc822_size;
1688               break;
1689       }
1690       break;
1691       
1692     case MAILIMAP_MSG_ATT_ITEM_DYNAMIC:
1693       if (att_dyn == NULL) {
1694         att_dyn = item->att_data.att_dyn;
1695       }
1696       break;
1697     }
1698   }
1699
1700   if (puid != NULL)
1701     * puid = uid;
1702   if (pheaders != NULL)
1703     * pheaders = headers;
1704   if (pref_size != NULL)
1705     * pref_size = ref_size;
1706   if (patt_dyn != NULL)
1707     * patt_dyn = att_dyn;
1708
1709   return MAIL_NO_ERROR;
1710 }
1711
1712 static struct imap_fetch_env_info *
1713 fetch_to_env_info(struct mailimap_msg_att * msg_att)
1714 {
1715         struct imap_fetch_env_info * info;
1716         uint32_t uid;
1717         char * headers;
1718         size_t size;
1719         struct mailimap_msg_att_dynamic * att_dyn;
1720         
1721         imap_get_msg_att_info(msg_att, &uid, &headers, &size,
1722                               &att_dyn);
1723         
1724         info = malloc(sizeof(* info));
1725         info->uid = uid;
1726         info->headers = strdup(headers);
1727         info->size = size;
1728         info->flags = imap_flags_to_flags(att_dyn);
1729         
1730         return info;
1731 }
1732
1733 static int
1734 imap_fetch_result_to_envelop_list(clist * fetch_result,
1735                                   carray ** p_env_list)
1736 {
1737         clistiter * cur;
1738         unsigned int i;
1739         carray * env_list;
1740   
1741         i = 0;
1742   
1743         env_list = carray_new(16);
1744   
1745         for(cur = clist_begin(fetch_result) ; cur != NULL ;
1746             cur = clist_next(cur)) {
1747                 struct mailimap_msg_att * msg_att;
1748                 struct imap_fetch_env_info * env_info;
1749     
1750                 msg_att = clist_content(cur);
1751
1752                 env_info = fetch_to_env_info(msg_att);
1753                 carray_add(env_list, env_info, NULL);
1754         }
1755   
1756         * p_env_list = env_list;
1757   
1758         return MAIL_NO_ERROR;
1759 }
1760
1761 int imap_add_envelope_fetch_att(struct mailimap_fetch_type * fetch_type)
1762 {
1763         struct mailimap_fetch_att * fetch_att;
1764         int r;
1765         char * header;
1766         clist * hdrlist;
1767         struct mailimap_header_list * imap_hdrlist;
1768         struct mailimap_section * section;
1769
1770         hdrlist = clist_new();
1771   
1772         header = strdup("Date");
1773         r = clist_append(hdrlist, header);
1774         header = strdup("From");
1775         r = clist_append(hdrlist, header);
1776         header = strdup("To");
1777         r = clist_append(hdrlist, header);
1778         header = strdup("Cc");
1779         r = clist_append(hdrlist, header);
1780         header = strdup("Subject");
1781         r = clist_append(hdrlist, header);
1782         header = strdup("Message-ID");
1783         r = clist_append(hdrlist, header);
1784         header = strdup("References");
1785         r = clist_append(hdrlist, header);
1786         header = strdup("In-Reply-To");
1787         r = clist_append(hdrlist, header);
1788   
1789         imap_hdrlist = mailimap_header_list_new(hdrlist);
1790         section = mailimap_section_new_header_fields(imap_hdrlist);
1791         fetch_att = mailimap_fetch_att_new_body_peek_section(section);
1792         mailimap_fetch_type_new_fetch_att_list_add(fetch_type, fetch_att);
1793   
1794         return MAIL_NO_ERROR;
1795 }
1796
1797 int imap_add_header_fetch_att(struct mailimap_fetch_type * fetch_type)
1798 {
1799         struct mailimap_fetch_att * fetch_att;
1800         struct mailimap_section * section;
1801         
1802         section = mailimap_section_new_header();
1803         fetch_att = mailimap_fetch_att_new_body_peek_section(section);
1804         mailimap_fetch_type_new_fetch_att_list_add(fetch_type, fetch_att);
1805         
1806         return MAIL_NO_ERROR;
1807 }
1808
1809 static int
1810 imap_get_envelopes_list(mailimap * imap, struct mailimap_set * set,
1811                         carray ** p_env_list)
1812 {
1813         struct mailimap_fetch_att * fetch_att;
1814         struct mailimap_fetch_type * fetch_type;
1815         int res;
1816         clist * fetch_result;
1817         int r;
1818         carray * env_list;
1819         chashdatum key;
1820         chashdatum value;
1821         
1822         fetch_type = mailimap_fetch_type_new_fetch_att_list_empty();
1823   
1824         /* uid */
1825         fetch_att = mailimap_fetch_att_new_uid();
1826         r = mailimap_fetch_type_new_fetch_att_list_add(fetch_type, fetch_att);
1827   
1828         /* flags */
1829         fetch_att = mailimap_fetch_att_new_flags();
1830         r = mailimap_fetch_type_new_fetch_att_list_add(fetch_type, fetch_att);
1831   
1832         /* rfc822 size */
1833         fetch_att = mailimap_fetch_att_new_rfc822_size();
1834         r = mailimap_fetch_type_new_fetch_att_list_add(fetch_type, fetch_att);
1835   
1836         /* headers */
1837         key.data = &imap;
1838         key.len = sizeof(imap);
1839         r = chash_get(courier_workaround_hash, &key, &value);
1840         if (r < 0)
1841                 r = imap_add_envelope_fetch_att(fetch_type);
1842         else
1843                 r = imap_add_header_fetch_att(fetch_type);
1844         
1845         r = mailimap_uid_fetch(imap, set, fetch_type, &fetch_result);
1846         
1847         switch (r) {
1848         case MAILIMAP_NO_ERROR:
1849                 break;
1850         default:
1851                 mailimap_fetch_type_free(fetch_type);
1852                 return r;
1853         }
1854         
1855         if (clist_begin(fetch_result) == NULL) {
1856                 res = MAILIMAP_ERROR_FETCH;
1857                 goto err;
1858         }
1859         
1860         r = imap_fetch_result_to_envelop_list(fetch_result, &env_list);
1861         mailimap_fetch_list_free(fetch_result);
1862         
1863         if (r != MAILIMAP_NO_ERROR) {
1864                 mailimap_fetch_type_free(fetch_type);
1865                 res = MAILIMAP_ERROR_MEMORY;
1866                 goto err;
1867         }
1868         
1869         mailimap_fetch_type_free(fetch_type);
1870         
1871         * p_env_list = env_list;
1872         
1873         return MAILIMAP_NO_ERROR;
1874   
1875  err:
1876         return res;
1877 }
1878
1879 struct fetch_env_param {
1880         mailimap * imap;
1881         struct mailimap_set * set;
1882 };
1883
1884 struct fetch_env_result {
1885         carray * fetch_env_result;
1886         int error;
1887 };
1888
1889 static void fetch_env_run(struct etpan_thread_op * op)
1890 {
1891         struct fetch_env_param * param;
1892         struct fetch_env_result * result;
1893         carray * env_list;
1894         int r;
1895         
1896         param = op->param;
1897         
1898         env_list = NULL;
1899         r = imap_get_envelopes_list(param->imap, param->set,
1900                                     &env_list);
1901         
1902         result = op->result;
1903         result->error = r;
1904         result->fetch_env_result = env_list;
1905         
1906         debug_print("imap fetch_env run - end %i\n", r);
1907 }
1908
1909 int imap_threaded_fetch_env(Folder * folder, struct mailimap_set * set,
1910                             carray ** p_env_list)
1911 {
1912         struct fetch_env_param param;
1913         struct fetch_env_result result;
1914         mailimap * imap;
1915         
1916         debug_print("imap fetch_env - begin\n");
1917         
1918         imap = get_imap(folder);
1919         param.imap = imap;
1920         param.set = set;
1921         
1922         threaded_run(folder, &param, &result, fetch_env_run);
1923         
1924         if (result.error != MAILIMAP_NO_ERROR) {
1925                 chashdatum key;
1926                 chashdatum value;
1927                 int r;
1928                 
1929                 key.data = &imap;
1930                 key.len = sizeof(imap);
1931                 r = chash_get(courier_workaround_hash, &key, &value);
1932                 if (r < 0) {
1933                         value.data = NULL;
1934                         value.len = 0;
1935                         chash_set(courier_workaround_hash, &key, &value, NULL);
1936                         
1937                         threaded_run(folder, &param, &result, fetch_env_run);
1938                 }
1939         }
1940         
1941         if (result.error != MAILIMAP_NO_ERROR)
1942                 return result.error;
1943         
1944         debug_print("imap fetch_env - end\n");
1945         
1946         * p_env_list = result.fetch_env_result;
1947         
1948         return result.error;
1949 }
1950
1951 void imap_fetch_env_free(carray * env_list)
1952 {
1953         unsigned int i;
1954         
1955         for(i = 0 ; i < carray_count(env_list) ; i ++) {
1956                 struct imap_fetch_env_info * env_info;
1957                 
1958                 env_info = carray_get(env_list, i);
1959                 free(env_info->headers);
1960                 free(env_info);
1961         }
1962         carray_free(env_list);
1963 }
1964
1965
1966
1967
1968
1969 struct append_param {
1970         mailimap * imap;
1971         const char * mailbox;
1972         const char * filename;
1973         struct mailimap_flag_list * flag_list;
1974 };
1975
1976 struct append_result {
1977         int error;
1978 };
1979
1980 static void append_run(struct etpan_thread_op * op)
1981 {
1982         struct append_param * param;
1983         struct append_result * result;
1984         int r;
1985         char * data;
1986         size_t size;
1987         struct stat stat_buf;
1988         int fd;
1989         
1990         param = op->param;
1991         result = op->result;
1992         
1993         r = stat(param->filename, &stat_buf);
1994         if (r < 0) {
1995                 result->error = MAILIMAP_ERROR_APPEND;
1996                 return;
1997         }
1998         size = stat_buf.st_size;
1999         
2000         fd = open(param->filename, O_RDONLY);
2001         if (fd < 0) {
2002                 result->error = MAILIMAP_ERROR_APPEND;
2003                 return;
2004         }
2005         
2006         data = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0);
2007         if (data == (void *) MAP_FAILED) {
2008                 close(fd);
2009                 result->error = MAILIMAP_ERROR_APPEND;
2010                 return;
2011         }
2012         
2013         r = mailimap_append(param->imap, param->mailbox,
2014                             param->flag_list, NULL,
2015                             data, size);
2016         
2017         munmap(data, size);
2018         close(fd);
2019         
2020         result->error = r;
2021         
2022         debug_print("imap append run - end %i\n", r);
2023 }
2024
2025 int imap_threaded_append(Folder * folder, const char * mailbox,
2026                          const char * filename,
2027                          struct mailimap_flag_list * flag_list)
2028 {
2029         struct append_param param;
2030         struct append_result result;
2031         mailimap * imap;
2032         
2033         debug_print("imap append - begin\n");
2034         
2035         imap = get_imap(folder);
2036         param.imap = imap;
2037         param.mailbox = mailbox;
2038         param.filename = filename;
2039         param.flag_list = flag_list;
2040         
2041         threaded_run(folder, &param, &result, append_run);
2042         
2043         if (result.error != MAILIMAP_NO_ERROR)
2044                 return result.error;
2045         
2046         debug_print("imap append - end\n");
2047         
2048         return result.error;
2049 }
2050
2051
2052
2053
2054 struct expunge_param {
2055         mailimap * imap;
2056 };
2057
2058 struct expunge_result {
2059         int error;
2060 };
2061
2062 static void expunge_run(struct etpan_thread_op * op)
2063 {
2064         struct expunge_param * param;
2065         struct expunge_result * result;
2066         int r;
2067         
2068         param = op->param;
2069         r = mailimap_expunge(param->imap);
2070         
2071         result = op->result;
2072         result->error = r;
2073         debug_print("imap expunge run - end %i\n", r);
2074 }
2075
2076 int imap_threaded_expunge(Folder * folder)
2077 {
2078         struct expunge_param param;
2079         struct expunge_result result;
2080         
2081         debug_print("imap expunge - begin\n");
2082         
2083         param.imap = get_imap(folder);
2084         
2085         threaded_run(folder, &param, &result, expunge_run);
2086         
2087         debug_print("imap expunge - end\n");
2088         
2089         return result.error;
2090 }
2091
2092
2093 struct copy_param {
2094         mailimap * imap;
2095         struct mailimap_set * set;
2096         const char * mb;
2097 };
2098
2099 struct copy_result {
2100         int error;
2101 };
2102
2103 static void copy_run(struct etpan_thread_op * op)
2104 {
2105         struct copy_param * param;
2106         struct copy_result * result;
2107         int r;
2108         
2109         param = op->param;
2110         
2111         r = mailimap_uid_copy(param->imap, param->set, param->mb);
2112         
2113         result = op->result;
2114         result->error = r;
2115         
2116         debug_print("imap copy run - end %i\n", r);
2117 }
2118
2119 int imap_threaded_copy(Folder * folder, struct mailimap_set * set,
2120                        const char * mb)
2121 {
2122         struct copy_param param;
2123         struct copy_result result;
2124         mailimap * imap;
2125         
2126         debug_print("imap copy - begin\n");
2127         
2128         imap = get_imap(folder);
2129         param.imap = imap;
2130         param.set = set;
2131         param.mb = mb;
2132         
2133         threaded_run(folder, &param, &result, copy_run);
2134         
2135         if (result.error != MAILIMAP_NO_ERROR)
2136                 return result.error;
2137         
2138         debug_print("imap copy - end\n");
2139         
2140         return result.error;
2141 }
2142
2143
2144
2145 struct store_param {
2146         mailimap * imap;
2147         struct mailimap_set * set;
2148         struct mailimap_store_att_flags * store_att_flags;
2149 };
2150
2151 struct store_result {
2152         int error;
2153 };
2154
2155 static void store_run(struct etpan_thread_op * op)
2156 {
2157         struct store_param * param;
2158         struct store_result * result;
2159         int r;
2160         
2161         param = op->param;
2162         
2163         r = mailimap_uid_store(param->imap, param->set,
2164                                param->store_att_flags);
2165         
2166         result = op->result;
2167         result->error = r;
2168         
2169         debug_print("imap store run - end %i\n", r);
2170 }
2171
2172 int imap_threaded_store(Folder * folder, struct mailimap_set * set,
2173                         struct mailimap_store_att_flags * store_att_flags)
2174 {
2175         struct store_param param;
2176         struct store_result result;
2177         mailimap * imap;
2178         
2179         debug_print("imap store - begin\n");
2180         
2181         imap = get_imap(folder);
2182         param.imap = imap;
2183         param.set = set;
2184         param.store_att_flags = store_att_flags;
2185         
2186         threaded_run(folder, &param, &result, store_run);
2187         
2188         if (result.error != MAILIMAP_NO_ERROR)
2189                 return result.error;
2190         
2191         debug_print("imap store - end\n");
2192         
2193         return result.error;
2194 }
2195
2196
2197 #define ENV_BUFFER_SIZE 512
2198
2199 static void do_exec_command(int fd, const char * command,
2200                             const char * servername, uint16_t port)
2201 {
2202         int i, maxopen;
2203 #ifdef SOLARIS
2204         char env_buffer[ENV_BUFFER_SIZE];
2205 #endif
2206         
2207         if (fork() > 0) {
2208                 /* Fork again to become a child of init rather than
2209                    the etpan client. */
2210                 exit(0);
2211         }
2212   
2213 #ifdef SOLARIS
2214         if (servername)
2215                 snprintf(env_buffer, ENV_BUFFER_SIZE,
2216                          "ETPANSERVER=%s", servername);
2217         else
2218                 snprintf(env_buffer, ENV_BUFFER_SIZE, "ETPANSERVER=");
2219         putenv(env_buffer);
2220 #else
2221         if (servername)
2222                 setenv("ETPANSERVER", servername, 1);
2223         else
2224                 unsetenv("ETPANSERVER");
2225 #endif
2226   
2227 #ifdef SOLARIS
2228         if (port)
2229                 snprintf(env_buffer, ENV_BUFFER_SIZE, "ETPANPORT=%d", port);
2230         else
2231                 snprintf(env_buffer, ENV_BUFFER_SIZE, "ETPANPORT=");
2232         putenv(env_buffer);
2233 #else
2234         if (port) {
2235                 char porttext[20];
2236                 
2237                 snprintf(porttext, sizeof(porttext), "%d", port);
2238                 setenv("ETPANPORT", porttext, 1);
2239         }
2240         else {
2241                 unsetenv("ETPANPORT");
2242         }
2243 #endif
2244                 
2245         /* Not a lot we can do if there's an error other than bail. */
2246         if (dup2(fd, 0) == -1)
2247                 exit(1);
2248         if (dup2(fd, 1) == -1)
2249                 exit(1);
2250   
2251         /* Should we close stderr and reopen /dev/null? */
2252   
2253         maxopen = sysconf(_SC_OPEN_MAX);
2254         for (i=3; i < maxopen; i++)
2255                 close(i);
2256   
2257 #ifdef TIOCNOTTY
2258         /* Detach from the controlling tty if we have one. Otherwise,
2259            SSH might do something stupid like trying to use it instead
2260            of running $SSH_ASKPASS. Doh. */
2261         fd = open("/dev/tty", O_RDONLY);
2262         if (fd != -1) {
2263                 ioctl(fd, TIOCNOTTY, NULL);
2264                 close(fd);
2265         }
2266 #endif /* TIOCNOTTY */
2267
2268         execl("/bin/sh", "/bin/sh", "-c", command, NULL);
2269   
2270         /* Eep. Shouldn't reach this */
2271         exit(1);
2272 }
2273
2274 static int subcommand_connect(const char *command,
2275                               const char *servername, uint16_t port)
2276 {
2277         /* SEB unsupported on Windows */
2278         int sockfds[2];
2279         pid_t childpid;
2280   
2281         if (socketpair(AF_UNIX, SOCK_STREAM, 0, sockfds))
2282                 return -1;
2283   
2284         childpid = fork();
2285         if (!childpid) {
2286                 do_exec_command(sockfds[1], command, servername, port);
2287         }
2288         else if (childpid == -1) {
2289                 close(sockfds[0]);
2290                 close(sockfds[1]);
2291                 return -1;
2292         }
2293   
2294         close(sockfds[1]);
2295   
2296         /* Reap child, leaving grandchild process to run */
2297         waitpid(childpid, NULL, 0);
2298   
2299         return sockfds[0];
2300 }
2301
2302 int socket_connect_cmd(mailimap * imap, const char * command,
2303                        const char * server, int port)
2304 {
2305         int fd;
2306         mailstream * s;
2307         int r;
2308         
2309         fd = subcommand_connect(command, server, port);
2310         if (fd < 0)
2311                 return MAILIMAP_ERROR_STREAM;
2312         
2313         s = mailstream_socket_open(fd);
2314         if (s == NULL) {
2315                 close(fd);
2316                 return MAILIMAP_ERROR_STREAM;
2317         }
2318         
2319         r = mailimap_connect(imap, s);
2320         if (r != MAILIMAP_NO_ERROR) {
2321                 mailstream_close(s);
2322                 return r;
2323         }
2324         
2325         return MAILIMAP_NO_ERROR;
2326 }
2327
2328 /* connect cmd */
2329
2330 struct connect_cmd_param {
2331         mailimap * imap;
2332         const char * command;
2333         const char * server;
2334         int port;
2335 };
2336
2337 struct connect_cmd_result {
2338         int error;
2339 };
2340
2341 static void connect_cmd_run(struct etpan_thread_op * op)
2342 {
2343         int r;
2344         struct connect_cmd_param * param;
2345         struct connect_cmd_result * result;
2346         
2347         param = op->param;
2348         result = op->result;
2349         
2350         r = socket_connect_cmd(param->imap, param->command,
2351                                param->server, param->port);
2352         
2353         result->error = r;
2354 }
2355
2356
2357 int imap_threaded_connect_cmd(Folder * folder, const char * command,
2358                               const char * server, int port)
2359 {
2360         struct connect_cmd_param param;
2361         struct connect_cmd_result result;
2362         chashdatum key;
2363         chashdatum value;
2364         mailimap * imap;
2365         
2366         imap = mailimap_new(0, NULL);
2367         
2368         key.data = &folder;
2369         key.len = sizeof(folder);
2370         value.data = imap;
2371         value.len = 0;
2372         chash_set(session_hash, &key, &value, NULL);
2373         
2374         param.imap = imap;
2375         param.command = command;
2376         param.server = server;
2377         param.port = port;
2378         
2379         threaded_run(folder, &param, &result, connect_cmd_run);
2380         
2381         debug_print("connect_cmd ok %i\n", result.error);
2382         
2383         return result.error;
2384 }
2385 #else
2386
2387 void imap_main_init(void)
2388 {
2389 }
2390 void imap_main_done(void)
2391 {
2392 }
2393 void imap_main_set_timeout(int sec)
2394 {
2395 }
2396
2397 #endif