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