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