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