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