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