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