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