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