2006-03-21 [wwp] 2.0.0cvs160
[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__) || 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(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 = NULL;
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         int uid;
2030 };
2031
2032 static void append_run(struct etpan_thread_op * op)
2033 {
2034         struct append_param * param;
2035         struct append_result * result;
2036         int r;
2037         char * data;
2038         size_t size;
2039         struct stat stat_buf;
2040         int fd, uid = 0;
2041         
2042         param = op->param;
2043         result = op->result;
2044         
2045         r = stat(param->filename, &stat_buf);
2046         if (r < 0) {
2047                 result->error = MAILIMAP_ERROR_APPEND;
2048                 return;
2049         }
2050         size = stat_buf.st_size;
2051         
2052         fd = open(param->filename, O_RDONLY);
2053         if (fd < 0) {
2054                 result->error = MAILIMAP_ERROR_APPEND;
2055                 return;
2056         }
2057         
2058         data = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0);
2059         if (data == (void *) MAP_FAILED) {
2060                 close(fd);
2061                 result->error = MAILIMAP_ERROR_APPEND;
2062                 return;
2063         }
2064         
2065         r = mailimap_append(param->imap, param->mailbox,
2066                             param->flag_list, NULL,
2067                             data, size/*, &uid */);
2068         
2069         munmap(data, size);
2070         close(fd);
2071         
2072         result->error = r;
2073         result->uid = uid;
2074         debug_print("imap append run - end %i uid %d\n", r, uid);
2075 }
2076
2077 int imap_threaded_append(Folder * folder, const char * mailbox,
2078                          const char * filename,
2079                          struct mailimap_flag_list * flag_list,
2080                          int *uid)
2081 {
2082         struct append_param param;
2083         struct append_result result;
2084         mailimap * imap;
2085         
2086         debug_print("imap append - begin\n");
2087         
2088         imap = get_imap(folder);
2089         param.imap = imap;
2090         param.mailbox = mailbox;
2091         param.filename = filename;
2092         param.flag_list = flag_list;
2093         
2094         threaded_run(folder, &param, &result, append_run);
2095         
2096         if (result.error != MAILIMAP_NO_ERROR)
2097                 return result.error;
2098         
2099         debug_print("imap append - end\n");
2100         if (uid != NULL)
2101                 *uid = result.uid;
2102
2103         return result.error;
2104 }
2105
2106
2107
2108
2109 struct expunge_param {
2110         mailimap * imap;
2111 };
2112
2113 struct expunge_result {
2114         int error;
2115 };
2116
2117 static void expunge_run(struct etpan_thread_op * op)
2118 {
2119         struct expunge_param * param;
2120         struct expunge_result * result;
2121         int r;
2122         
2123         param = op->param;
2124         r = mailimap_expunge(param->imap);
2125         
2126         result = op->result;
2127         result->error = r;
2128         debug_print("imap expunge run - end %i\n", r);
2129 }
2130
2131 int imap_threaded_expunge(Folder * folder)
2132 {
2133         struct expunge_param param;
2134         struct expunge_result result;
2135         
2136         debug_print("imap expunge - begin\n");
2137         
2138         param.imap = get_imap(folder);
2139         
2140         threaded_run(folder, &param, &result, expunge_run);
2141         
2142         debug_print("imap expunge - end\n");
2143         
2144         return result.error;
2145 }
2146
2147
2148 struct copy_param {
2149         mailimap * imap;
2150         struct mailimap_set * set;
2151         const char * mb;
2152 };
2153
2154 struct copy_result {
2155         int error;
2156 };
2157
2158 static void copy_run(struct etpan_thread_op * op)
2159 {
2160         struct copy_param * param;
2161         struct copy_result * result;
2162         int r;
2163         
2164         param = op->param;
2165         
2166         r = mailimap_uid_copy(param->imap, param->set, param->mb);
2167         
2168         result = op->result;
2169         result->error = r;
2170         
2171         debug_print("imap copy run - end %i\n", r);
2172 }
2173
2174 int imap_threaded_copy(Folder * folder, struct mailimap_set * set,
2175                        const char * mb)
2176 {
2177         struct copy_param param;
2178         struct copy_result result;
2179         mailimap * imap;
2180         
2181         debug_print("imap copy - begin\n");
2182         
2183         imap = get_imap(folder);
2184         param.imap = imap;
2185         param.set = set;
2186         param.mb = mb;
2187         
2188         threaded_run(folder, &param, &result, copy_run);
2189         
2190         if (result.error != MAILIMAP_NO_ERROR)
2191                 return result.error;
2192         
2193         debug_print("imap copy - end\n");
2194         
2195         return result.error;
2196 }
2197
2198
2199
2200 struct store_param {
2201         mailimap * imap;
2202         struct mailimap_set * set;
2203         struct mailimap_store_att_flags * store_att_flags;
2204 };
2205
2206 struct store_result {
2207         int error;
2208 };
2209
2210 static void store_run(struct etpan_thread_op * op)
2211 {
2212         struct store_param * param;
2213         struct store_result * result;
2214         int r;
2215         
2216         param = op->param;
2217         
2218         r = mailimap_uid_store(param->imap, param->set,
2219                                param->store_att_flags);
2220         
2221         result = op->result;
2222         result->error = r;
2223         
2224         debug_print("imap store run - end %i\n", r);
2225 }
2226
2227 int imap_threaded_store(Folder * folder, struct mailimap_set * set,
2228                         struct mailimap_store_att_flags * store_att_flags)
2229 {
2230         struct store_param param;
2231         struct store_result result;
2232         mailimap * imap;
2233         
2234         debug_print("imap store - begin\n");
2235         
2236         imap = get_imap(folder);
2237         param.imap = imap;
2238         param.set = set;
2239         param.store_att_flags = store_att_flags;
2240         
2241         threaded_run(folder, &param, &result, store_run);
2242         
2243         if (result.error != MAILIMAP_NO_ERROR)
2244                 return result.error;
2245         
2246         debug_print("imap store - end\n");
2247         
2248         return result.error;
2249 }
2250
2251
2252 #define ENV_BUFFER_SIZE 512
2253
2254 static void do_exec_command(int fd, const char * command,
2255                             const char * servername, uint16_t port)
2256 {
2257         int i, maxopen;
2258 #ifdef SOLARIS
2259         char env_buffer[ENV_BUFFER_SIZE];
2260 #endif
2261         
2262         if (fork() > 0) {
2263                 /* Fork again to become a child of init rather than
2264                    the etpan client. */
2265                 exit(0);
2266         }
2267   
2268 #ifdef SOLARIS
2269         if (servername)
2270                 snprintf(env_buffer, ENV_BUFFER_SIZE,
2271                          "ETPANSERVER=%s", servername);
2272         else
2273                 snprintf(env_buffer, ENV_BUFFER_SIZE, "ETPANSERVER=");
2274         putenv(env_buffer);
2275 #else
2276         if (servername)
2277                 setenv("ETPANSERVER", servername, 1);
2278         else
2279                 unsetenv("ETPANSERVER");
2280 #endif
2281   
2282 #ifdef SOLARIS
2283         if (port)
2284                 snprintf(env_buffer, ENV_BUFFER_SIZE, "ETPANPORT=%d", port);
2285         else
2286                 snprintf(env_buffer, ENV_BUFFER_SIZE, "ETPANPORT=");
2287         putenv(env_buffer);
2288 #else
2289         if (port) {
2290                 char porttext[20];
2291                 
2292                 snprintf(porttext, sizeof(porttext), "%d", port);
2293                 setenv("ETPANPORT", porttext, 1);
2294         }
2295         else {
2296                 unsetenv("ETPANPORT");
2297         }
2298 #endif
2299                 
2300         /* Not a lot we can do if there's an error other than bail. */
2301         if (dup2(fd, 0) == -1)
2302                 exit(1);
2303         if (dup2(fd, 1) == -1)
2304                 exit(1);
2305   
2306         /* Should we close stderr and reopen /dev/null? */
2307   
2308         maxopen = sysconf(_SC_OPEN_MAX);
2309         for (i=3; i < maxopen; i++)
2310                 close(i);
2311   
2312 #ifdef TIOCNOTTY
2313         /* Detach from the controlling tty if we have one. Otherwise,
2314            SSH might do something stupid like trying to use it instead
2315            of running $SSH_ASKPASS. Doh. */
2316         fd = open("/dev/tty", O_RDONLY);
2317         if (fd != -1) {
2318                 ioctl(fd, TIOCNOTTY, NULL);
2319                 close(fd);
2320         }
2321 #endif /* TIOCNOTTY */
2322
2323         execl("/bin/sh", "/bin/sh", "-c", command, NULL);
2324   
2325         /* Eep. Shouldn't reach this */
2326         exit(1);
2327 }
2328
2329 static int subcommand_connect(const char *command,
2330                               const char *servername, uint16_t port)
2331 {
2332         /* SEB unsupported on Windows */
2333         int sockfds[2];
2334         pid_t childpid;
2335   
2336         if (socketpair(AF_UNIX, SOCK_STREAM, 0, sockfds))
2337                 return -1;
2338   
2339         childpid = fork();
2340         if (!childpid) {
2341                 do_exec_command(sockfds[1], command, servername, port);
2342         }
2343         else if (childpid == -1) {
2344                 close(sockfds[0]);
2345                 close(sockfds[1]);
2346                 return -1;
2347         }
2348   
2349         close(sockfds[1]);
2350   
2351         /* Reap child, leaving grandchild process to run */
2352         waitpid(childpid, NULL, 0);
2353   
2354         return sockfds[0];
2355 }
2356
2357 int socket_connect_cmd(mailimap * imap, const char * command,
2358                        const char * server, int port)
2359 {
2360         int fd;
2361         mailstream * s;
2362         int r;
2363         
2364         fd = subcommand_connect(command, server, port);
2365         if (fd < 0)
2366                 return MAILIMAP_ERROR_STREAM;
2367         
2368         s = mailstream_socket_open(fd);
2369         if (s == NULL) {
2370                 close(fd);
2371                 return MAILIMAP_ERROR_STREAM;
2372         }
2373         
2374         r = mailimap_connect(imap, s);
2375         if (r != MAILIMAP_NO_ERROR) {
2376                 mailstream_close(s);
2377                 return r;
2378         }
2379         
2380         return MAILIMAP_NO_ERROR;
2381 }
2382
2383 /* connect cmd */
2384
2385 struct connect_cmd_param {
2386         mailimap * imap;
2387         const char * command;
2388         const char * server;
2389         int port;
2390 };
2391
2392 struct connect_cmd_result {
2393         int error;
2394 };
2395
2396 static void connect_cmd_run(struct etpan_thread_op * op)
2397 {
2398         int r;
2399         struct connect_cmd_param * param;
2400         struct connect_cmd_result * result;
2401         
2402         param = op->param;
2403         result = op->result;
2404         
2405         r = socket_connect_cmd(param->imap, param->command,
2406                                param->server, param->port);
2407         
2408         result->error = r;
2409 }
2410
2411
2412 int imap_threaded_connect_cmd(Folder * folder, const char * command,
2413                               const char * server, int port)
2414 {
2415         struct connect_cmd_param param;
2416         struct connect_cmd_result result;
2417         chashdatum key;
2418         chashdatum value;
2419         mailimap * imap;
2420         
2421         imap = mailimap_new(0, NULL);
2422         
2423         key.data = &folder;
2424         key.len = sizeof(folder);
2425         value.data = imap;
2426         value.len = 0;
2427         chash_set(session_hash, &key, &value, NULL);
2428         
2429         param.imap = imap;
2430         param.command = command;
2431         param.server = server;
2432         param.port = port;
2433         
2434         threaded_run(folder, &param, &result, connect_cmd_run);
2435         
2436         debug_print("connect_cmd ok %i\n", result.error);
2437         
2438         return result.error;
2439 }
2440 #else
2441
2442 void imap_main_init(void)
2443 {
2444 }
2445 void imap_main_done(void)
2446 {
2447 }
2448 void imap_main_set_timeout(int sec)
2449 {
2450 }
2451
2452 #endif