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