2007-10-05 [colin] 3.0.2cvs15
[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[... - %d 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, (const unsigned char **)&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 < content_size) {
2274                         goto fclose;
2275                 }
2276                 
2277                 r = fclose(f);
2278                 if (r == EOF) {
2279                         goto unlink;
2280                 }
2281                 goto free;
2282                 
2283         fclose:
2284                 fclose(f);
2285                 goto unlink;
2286         close:
2287                 close(fd);
2288         unlink:
2289                 g_unlink(param->filename);
2290         
2291         free:
2292                 if (mmap_string_unref(content) != 0)
2293                         free(content);
2294         }
2295         
2296         debug_print("imap fetch_content run - end %i\n", r);
2297 }
2298
2299 int imap_threaded_fetch_content(Folder * folder, uint32_t msg_index,
2300                                 int with_body,
2301                                 const char * filename)
2302 {
2303         struct fetch_content_param param;
2304         struct fetch_content_result result;
2305         mailimap * imap;
2306         
2307         debug_print("imap fetch_content - begin\n");
2308         
2309         imap = get_imap(folder);
2310         param.imap = imap;
2311         param.msg_index = msg_index;
2312         param.filename = filename;
2313         param.with_body = with_body;
2314         
2315         threaded_run(folder, &param, &result, fetch_content_run);
2316         
2317         if (result.error != MAILIMAP_NO_ERROR)
2318                 return result.error;
2319         
2320         debug_print("imap fetch_content - end\n");
2321         
2322         return result.error;
2323 }
2324
2325
2326
2327 static int imap_flags_to_flags(struct mailimap_msg_att_dynamic * att_dyn)
2328 {
2329         int flags;
2330         clist * flag_list;
2331         clistiter * cur;
2332         
2333         flags = MSG_UNREAD;
2334         
2335         flag_list = att_dyn->att_list;
2336         if (flag_list == NULL)
2337                 return flags;
2338         
2339         for(cur = clist_begin(flag_list) ; cur != NULL ;
2340             cur = clist_next(cur)) {
2341                 struct mailimap_flag_fetch * flag_fetch;
2342                         
2343                 flag_fetch = clist_content(cur);
2344                 if (flag_fetch->fl_type == MAILIMAP_FLAG_FETCH_RECENT)
2345                         flags |= MSG_NEW;
2346                 else {
2347                         switch (flag_fetch->fl_flag->fl_type) {
2348                         case MAILIMAP_FLAG_ANSWERED:
2349                                 flags |= MSG_REPLIED;
2350                                 break;
2351                         case MAILIMAP_FLAG_FLAGGED:
2352                                 flags |= MSG_MARKED;
2353                                 break;
2354                         case MAILIMAP_FLAG_DELETED:
2355                                 flags |= MSG_DELETED;
2356                                 break;
2357                         case MAILIMAP_FLAG_SEEN:
2358                                 flags &= ~MSG_UNREAD;
2359                                 flags &= ~MSG_NEW;
2360                                 break;
2361                         }
2362                 }
2363         }
2364         
2365         return flags;
2366 }
2367
2368 static int imap_get_msg_att_info(struct mailimap_msg_att * msg_att,
2369                                  uint32_t * puid,
2370                                  char ** pheaders,
2371                                  size_t * pref_size,
2372                                  struct mailimap_msg_att_dynamic ** patt_dyn)
2373 {
2374   clistiter * item_cur;
2375   uint32_t uid;
2376   char * headers;
2377   size_t ref_size;
2378   struct mailimap_msg_att_dynamic * att_dyn;
2379
2380   uid = 0;
2381   headers = NULL;
2382   ref_size = 0;
2383   att_dyn = NULL;
2384
2385   for(item_cur = clist_begin(msg_att->att_list) ; item_cur != NULL ;
2386       item_cur = clist_next(item_cur)) {
2387     struct mailimap_msg_att_item * item;
2388
2389     item = clist_content(item_cur);
2390       
2391     switch (item->att_type) {
2392     case MAILIMAP_MSG_ATT_ITEM_STATIC:
2393       switch (item->att_data.att_static->att_type) {
2394       case MAILIMAP_MSG_ATT_UID:
2395         uid = item->att_data.att_static->att_data.att_uid;
2396         break;
2397
2398       case MAILIMAP_MSG_ATT_BODY_SECTION:
2399         if (headers == NULL) {
2400           headers = item->att_data.att_static->att_data.att_body_section->sec_body_part;
2401         }
2402         break;
2403       case MAILIMAP_MSG_ATT_RFC822_SIZE:
2404               ref_size = item->att_data.att_static->att_data.att_rfc822_size;
2405               break;
2406       }
2407       break;
2408       
2409     case MAILIMAP_MSG_ATT_ITEM_DYNAMIC:
2410       if (att_dyn == NULL) {
2411         att_dyn = item->att_data.att_dyn;
2412       }
2413       break;
2414     }
2415   }
2416
2417   if (puid != NULL)
2418     * puid = uid;
2419   if (pheaders != NULL)
2420     * pheaders = headers;
2421   if (pref_size != NULL)
2422     * pref_size = ref_size;
2423   if (patt_dyn != NULL)
2424     * patt_dyn = att_dyn;
2425
2426   return MAIL_NO_ERROR;
2427 }
2428
2429 static struct imap_fetch_env_info *
2430 fetch_to_env_info(struct mailimap_msg_att * msg_att)
2431 {
2432         struct imap_fetch_env_info * info;
2433         uint32_t uid;
2434         char * headers;
2435         size_t size;
2436         struct mailimap_msg_att_dynamic * att_dyn;
2437         
2438         imap_get_msg_att_info(msg_att, &uid, &headers, &size,
2439                               &att_dyn);
2440         
2441         if (!headers)
2442                 return NULL;
2443         info = malloc(sizeof(* info));
2444         info->uid = uid;
2445         info->headers = strdup(headers);
2446         info->size = size;
2447         info->flags = imap_flags_to_flags(att_dyn);
2448         
2449         return info;
2450 }
2451
2452 static int
2453 imap_fetch_result_to_envelop_list(clist * fetch_result,
2454                                   carray ** p_env_list)
2455 {
2456         clistiter * cur;
2457         unsigned int i;
2458         carray * env_list;
2459         i = 0;
2460   
2461         env_list = carray_new(16);
2462   
2463         for(cur = clist_begin(fetch_result) ; cur != NULL ;
2464             cur = clist_next(cur)) {
2465                 struct mailimap_msg_att * msg_att;
2466                 struct imap_fetch_env_info * env_info;
2467     
2468                 msg_att = clist_content(cur);
2469
2470                 env_info = fetch_to_env_info(msg_att);
2471                 if (!env_info)
2472                         return MAILIMAP_ERROR_MEMORY;
2473                 carray_add(env_list, env_info, NULL);
2474         }
2475   
2476         * p_env_list = env_list;
2477   
2478         return MAIL_NO_ERROR;
2479 }
2480
2481 static int imap_add_envelope_fetch_att(struct mailimap_fetch_type * fetch_type)
2482 {
2483         struct mailimap_fetch_att * fetch_att;
2484         int r;
2485         char * header;
2486         clist * hdrlist;
2487         struct mailimap_header_list * imap_hdrlist;
2488         struct mailimap_section * section;
2489
2490         hdrlist = clist_new();
2491   
2492         header = strdup("Date");
2493         r = clist_append(hdrlist, header);
2494         header = strdup("From");
2495         r = clist_append(hdrlist, header);
2496         header = strdup("To");
2497         r = clist_append(hdrlist, header);
2498         header = strdup("Cc");
2499         r = clist_append(hdrlist, header);
2500         header = strdup("Subject");
2501         r = clist_append(hdrlist, header);
2502         header = strdup("Message-ID");
2503         r = clist_append(hdrlist, header);
2504         header = strdup("References");
2505         r = clist_append(hdrlist, header);
2506         header = strdup("In-Reply-To");
2507         r = clist_append(hdrlist, header);
2508   
2509         imap_hdrlist = mailimap_header_list_new(hdrlist);
2510         section = mailimap_section_new_header_fields(imap_hdrlist);
2511         fetch_att = mailimap_fetch_att_new_body_peek_section(section);
2512         mailimap_fetch_type_new_fetch_att_list_add(fetch_type, fetch_att);
2513   
2514         return MAIL_NO_ERROR;
2515 }
2516
2517 static int imap_add_header_fetch_att(struct mailimap_fetch_type * fetch_type)
2518 {
2519         struct mailimap_fetch_att * fetch_att;
2520         struct mailimap_section * section;
2521         
2522         section = mailimap_section_new_header();
2523         fetch_att = mailimap_fetch_att_new_body_peek_section(section);
2524         mailimap_fetch_type_new_fetch_att_list_add(fetch_type, fetch_att);
2525         
2526         return MAIL_NO_ERROR;
2527 }
2528
2529 static int
2530 imap_get_envelopes_list(mailimap * imap, struct mailimap_set * set,
2531                         carray ** p_env_list)
2532 {
2533         struct mailimap_fetch_att * fetch_att;
2534         struct mailimap_fetch_type * fetch_type;
2535         int res;
2536         clist * fetch_result;
2537         int r;
2538         carray * env_list = NULL;
2539         chashdatum key;
2540         chashdatum value;
2541         
2542         fetch_type = mailimap_fetch_type_new_fetch_att_list_empty();
2543   
2544         /* uid */
2545         fetch_att = mailimap_fetch_att_new_uid();
2546         r = mailimap_fetch_type_new_fetch_att_list_add(fetch_type, fetch_att);
2547   
2548         /* flags */
2549         fetch_att = mailimap_fetch_att_new_flags();
2550         r = mailimap_fetch_type_new_fetch_att_list_add(fetch_type, fetch_att);
2551   
2552         /* rfc822 size */
2553         fetch_att = mailimap_fetch_att_new_rfc822_size();
2554         r = mailimap_fetch_type_new_fetch_att_list_add(fetch_type, fetch_att);
2555   
2556         /* headers */
2557         key.data = &imap;
2558         key.len = sizeof(imap);
2559         r = chash_get(courier_workaround_hash, &key, &value);
2560         if (r < 0)
2561                 r = imap_add_envelope_fetch_att(fetch_type);
2562         else
2563                 r = imap_add_header_fetch_att(fetch_type);
2564         
2565         mailstream_logger = imap_logger_fetch;
2566         
2567         r = mailimap_uid_fetch(imap, set, fetch_type, &fetch_result);
2568         
2569         mailstream_logger = imap_logger_cmd;
2570         switch (r) {
2571         case MAILIMAP_NO_ERROR:
2572                 break;
2573         default:
2574                 mailimap_fetch_type_free(fetch_type);
2575                 debug_print("uid_fetch: %d\n", r);
2576                 return r;
2577         }
2578         
2579         if (clist_begin(fetch_result) == NULL) {
2580                 res = MAILIMAP_ERROR_FETCH;
2581                 debug_print("clist_begin = NULL\n");
2582                 goto err;
2583         }
2584         
2585         r = imap_fetch_result_to_envelop_list(fetch_result, &env_list);
2586         mailimap_fetch_list_free(fetch_result);
2587         
2588         if (r != MAILIMAP_NO_ERROR) {
2589                 mailimap_fetch_type_free(fetch_type);
2590                 res = MAILIMAP_ERROR_MEMORY;
2591                 debug_print("fetch_result_to_envelop_list: %d\n", res);
2592                 goto err;
2593         }
2594         
2595         mailimap_fetch_type_free(fetch_type);
2596         
2597         * p_env_list = env_list;
2598         
2599         return MAILIMAP_NO_ERROR;
2600   
2601  err:
2602         return res;
2603 }
2604
2605 struct fetch_env_param {
2606         mailimap * imap;
2607         struct mailimap_set * set;
2608 };
2609
2610 struct fetch_env_result {
2611         carray * fetch_env_result;
2612         int error;
2613 };
2614
2615 static void fetch_env_run(struct etpan_thread_op * op)
2616 {
2617         struct fetch_env_param * param;
2618         struct fetch_env_result * result;
2619         carray * env_list;
2620         int r;
2621         
2622         param = op->param;
2623         result = op->result;
2624
2625         CHECK_IMAP();
2626
2627         env_list = NULL;
2628         r = imap_get_envelopes_list(param->imap, param->set,
2629                                     &env_list);
2630         
2631         result->error = r;
2632         result->fetch_env_result = env_list;
2633         
2634         debug_print("imap fetch_env run - end %i\n", r);
2635 }
2636
2637 int imap_threaded_fetch_env(Folder * folder, struct mailimap_set * set,
2638                             carray ** p_env_list)
2639 {
2640         struct fetch_env_param param;
2641         struct fetch_env_result result;
2642         mailimap * imap;
2643         
2644         debug_print("imap fetch_env - begin\n");
2645         
2646         imap = get_imap(folder);
2647         param.imap = imap;
2648         param.set = set;
2649         
2650         threaded_run(folder, &param, &result, fetch_env_run);
2651         
2652         if (result.error != MAILIMAP_NO_ERROR) {
2653                 chashdatum key;
2654                 chashdatum value;
2655                 int r;
2656                 
2657                 key.data = &imap;
2658                 key.len = sizeof(imap);
2659                 r = chash_get(courier_workaround_hash, &key, &value);
2660                 if (r < 0) {
2661                         value.data = NULL;
2662                         value.len = 0;
2663                         chash_set(courier_workaround_hash, &key, &value, NULL);
2664                         
2665                         threaded_run(folder, &param, &result, fetch_env_run);
2666                 }
2667         }
2668         
2669         if (result.error != MAILIMAP_NO_ERROR)
2670                 return result.error;
2671         
2672         debug_print("imap fetch_env - end\n");
2673         
2674         * p_env_list = result.fetch_env_result;
2675         
2676         return result.error;
2677 }
2678
2679 void imap_fetch_env_free(carray * env_list)
2680 {
2681         unsigned int i;
2682         
2683         for(i = 0 ; i < carray_count(env_list) ; i ++) {
2684                 struct imap_fetch_env_info * env_info;
2685                 
2686                 env_info = carray_get(env_list, i);
2687                 free(env_info->headers);
2688                 free(env_info);
2689         }
2690         carray_free(env_list);
2691 }
2692
2693
2694
2695
2696
2697 struct append_param {
2698         mailimap * imap;
2699         const char * mailbox;
2700         const char * filename;
2701         struct mailimap_flag_list * flag_list;
2702 };
2703
2704 struct append_result {
2705         int error;
2706         int uid;
2707 };
2708
2709 static void append_run(struct etpan_thread_op * op)
2710 {
2711         struct append_param * param;
2712         struct append_result * result;
2713         int r;
2714         char * data;
2715         size_t size;
2716         struct stat stat_buf;
2717         int fd;
2718         guint32 uid = 0, val = 0;
2719         
2720         param = op->param;
2721         result = op->result;
2722         
2723         CHECK_IMAP();
2724
2725         r = stat(param->filename, &stat_buf);
2726         if (r < 0) {
2727                 result->error = MAILIMAP_ERROR_APPEND;
2728                 return;
2729         }
2730         size = stat_buf.st_size;
2731         
2732         fd = open(param->filename, O_RDONLY);
2733         if (fd < 0) {
2734                 result->error = MAILIMAP_ERROR_APPEND;
2735                 return;
2736         }
2737         
2738         data = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0);
2739         if (data == (void *) MAP_FAILED) {
2740                 close(fd);
2741                 result->error = MAILIMAP_ERROR_APPEND;
2742                 return;
2743         }
2744         
2745         mailstream_logger = imap_logger_append;
2746         
2747         r = mailimap_uidplus_append(param->imap, param->mailbox,
2748                             param->flag_list, NULL,
2749                             data, size, &val, &uid);
2750
2751         mailstream_logger = imap_logger_cmd;
2752         
2753         munmap(data, size);
2754         close(fd);
2755         
2756         result->error = r;
2757         result->uid = uid;
2758         debug_print("imap append run - end %i uid %d\n", r, uid);
2759 }
2760
2761 int imap_threaded_append(Folder * folder, const char * mailbox,
2762                          const char * filename,
2763                          struct mailimap_flag_list * flag_list,
2764                          int *uid)
2765 {
2766         struct append_param param;
2767         struct append_result result;
2768         mailimap * imap;
2769         
2770         debug_print("imap append - begin\n");
2771         
2772         imap = get_imap(folder);
2773         param.imap = imap;
2774         param.mailbox = mailbox;
2775         param.filename = filename;
2776         param.flag_list = flag_list;
2777         
2778         threaded_run(folder, &param, &result, append_run);
2779         
2780         if (result.error != MAILIMAP_NO_ERROR)
2781                 return result.error;
2782         
2783         debug_print("imap append - end\n");
2784         if (uid != NULL)
2785                 *uid = result.uid;
2786
2787         return result.error;
2788 }
2789
2790
2791
2792
2793 struct expunge_param {
2794         mailimap * imap;
2795 };
2796
2797 struct expunge_result {
2798         int error;
2799 };
2800
2801 static void expunge_run(struct etpan_thread_op * op)
2802 {
2803         struct expunge_param * param;
2804         struct expunge_result * result;
2805         int r;
2806         
2807         param = op->param;
2808         result = op->result;
2809
2810         CHECK_IMAP();
2811
2812         r = mailimap_expunge(param->imap);
2813         
2814         result->error = r;
2815         debug_print("imap expunge run - end %i\n", r);
2816 }
2817
2818 int imap_threaded_expunge(Folder * folder)
2819 {
2820         struct expunge_param param;
2821         struct expunge_result result;
2822         
2823         debug_print("imap expunge - begin\n");
2824         
2825         param.imap = get_imap(folder);
2826         
2827         threaded_run(folder, &param, &result, expunge_run);
2828         
2829         debug_print("imap expunge - end\n");
2830         
2831         return result.error;
2832 }
2833
2834
2835 struct copy_param {
2836         mailimap * imap;
2837         struct mailimap_set * set;
2838         const char * mb;
2839 };
2840
2841 struct copy_result {
2842         int error;
2843         struct mailimap_set *source;
2844         struct mailimap_set *dest;
2845 };
2846
2847 static void copy_run(struct etpan_thread_op * op)
2848 {
2849         struct copy_param * param;
2850         struct copy_result * result;
2851         int r;
2852         guint32 val;
2853         struct mailimap_set *source = NULL, *dest = NULL;
2854
2855         param = op->param;
2856         result = op->result;
2857
2858         CHECK_IMAP();
2859
2860         r = mailimap_uidplus_uid_copy(param->imap, param->set, param->mb,
2861                 &val, &source, &dest);
2862         
2863         result->error = r;
2864         if (r == 0) {
2865                 result->source = source;
2866                 result->dest = dest;
2867         } else {
2868                 result->source = NULL;
2869                 result->dest = NULL;
2870         }
2871         debug_print("imap copy run - end %i\n", r);
2872 }
2873
2874 int imap_threaded_copy(Folder * folder, struct mailimap_set * set,
2875                        const char * mb, struct mailimap_set **source,
2876                        struct mailimap_set **dest)
2877 {
2878         struct copy_param param;
2879         struct copy_result result;
2880         mailimap * imap;
2881         
2882         debug_print("imap copy - begin\n");
2883         
2884         imap = get_imap(folder);
2885         param.imap = imap;
2886         param.set = set;
2887         param.mb = mb;
2888         
2889         threaded_run(folder, &param, &result, copy_run);
2890         *source = NULL;
2891         *dest = NULL;
2892         
2893         if (result.error != MAILIMAP_NO_ERROR)
2894                 return result.error;
2895         
2896         *source = result.source;
2897         *dest = result.dest;
2898
2899         debug_print("imap copy - end\n");
2900         
2901         return result.error;
2902 }
2903
2904
2905
2906 struct store_param {
2907         mailimap * imap;
2908         struct mailimap_set * set;
2909         struct mailimap_store_att_flags * store_att_flags;
2910 };
2911
2912 struct store_result {
2913         int error;
2914 };
2915
2916 static void store_run(struct etpan_thread_op * op)
2917 {
2918         struct store_param * param;
2919         struct store_result * result;
2920         int r;
2921         
2922         param = op->param;
2923         result = op->result;
2924
2925         CHECK_IMAP();
2926
2927         r = mailimap_uid_store(param->imap, param->set,
2928                                param->store_att_flags);
2929         
2930         result->error = r;
2931         
2932         debug_print("imap store run - end %i\n", r);
2933 }
2934
2935 int imap_threaded_store(Folder * folder, struct mailimap_set * set,
2936                         struct mailimap_store_att_flags * store_att_flags)
2937 {
2938         struct store_param param;
2939         struct store_result result;
2940         mailimap * imap;
2941         
2942         debug_print("imap store - begin\n");
2943         
2944         imap = get_imap(folder);
2945         param.imap = imap;
2946         param.set = set;
2947         param.store_att_flags = store_att_flags;
2948         
2949         threaded_run(folder, &param, &result, store_run);
2950         
2951         if (result.error != MAILIMAP_NO_ERROR)
2952                 return result.error;
2953         
2954         debug_print("imap store - end\n");
2955         
2956         return result.error;
2957 }
2958
2959
2960 #define ENV_BUFFER_SIZE 512
2961
2962 static void do_exec_command(int fd, const char * command,
2963                             const char * servername, uint16_t port)
2964 {
2965         int i, maxopen;
2966 #ifdef SOLARIS
2967         char env_buffer[ENV_BUFFER_SIZE];
2968 #endif
2969         
2970         if (fork() > 0) {
2971                 /* Fork again to become a child of init rather than
2972                    the etpan client. */
2973                 exit(0);
2974         }
2975   
2976 #ifdef SOLARIS
2977         if (servername)
2978                 snprintf(env_buffer, ENV_BUFFER_SIZE,
2979                          "ETPANSERVER=%s", servername);
2980         else
2981                 snprintf(env_buffer, ENV_BUFFER_SIZE, "ETPANSERVER=");
2982         putenv(env_buffer);
2983 #else
2984         if (servername)
2985                 setenv("ETPANSERVER", servername, 1);
2986         else
2987                 unsetenv("ETPANSERVER");
2988 #endif
2989   
2990 #ifdef SOLARIS
2991         if (port)
2992                 snprintf(env_buffer, ENV_BUFFER_SIZE, "ETPANPORT=%d", port);
2993         else
2994                 snprintf(env_buffer, ENV_BUFFER_SIZE, "ETPANPORT=");
2995         putenv(env_buffer);
2996 #else
2997         if (port) {
2998                 char porttext[20];
2999                 
3000                 snprintf(porttext, sizeof(porttext), "%d", port);
3001                 setenv("ETPANPORT", porttext, 1);
3002         }
3003         else {
3004                 unsetenv("ETPANPORT");
3005         }
3006 #endif
3007                 
3008         /* Not a lot we can do if there's an error other than bail. */
3009         if (dup2(fd, 0) == -1)
3010                 exit(1);
3011         if (dup2(fd, 1) == -1)
3012                 exit(1);
3013   
3014         /* Should we close stderr and reopen /dev/null? */
3015   
3016         maxopen = sysconf(_SC_OPEN_MAX);
3017         for (i=3; i < maxopen; i++)
3018                 close(i);
3019   
3020 #ifdef TIOCNOTTY
3021         /* Detach from the controlling tty if we have one. Otherwise,
3022            SSH might do something stupid like trying to use it instead
3023            of running $SSH_ASKPASS. Doh. */
3024         fd = open("/dev/tty", O_RDONLY);
3025         if (fd != -1) {
3026                 ioctl(fd, TIOCNOTTY, NULL);
3027                 close(fd);
3028         }
3029 #endif /* TIOCNOTTY */
3030
3031         execl("/bin/sh", "/bin/sh", "-c", command, NULL);
3032   
3033         /* Eep. Shouldn't reach this */
3034         exit(1);
3035 }
3036
3037 static int subcommand_connect(const char *command,
3038                               const char *servername, uint16_t port)
3039 {
3040         /* SEB unsupported on Windows */
3041         int sockfds[2];
3042         pid_t childpid;
3043   
3044         if (socketpair(AF_UNIX, SOCK_STREAM, 0, sockfds))
3045                 return -1;
3046   
3047         childpid = fork();
3048         if (!childpid) {
3049                 do_exec_command(sockfds[1], command, servername, port);
3050         }
3051         else if (childpid == -1) {
3052                 close(sockfds[0]);
3053                 close(sockfds[1]);
3054                 return -1;
3055         }
3056   
3057         close(sockfds[1]);
3058   
3059         /* Reap child, leaving grandchild process to run */
3060         waitpid(childpid, NULL, 0);
3061   
3062         return sockfds[0];
3063 }
3064
3065 static int socket_connect_cmd(mailimap * imap, const char * command,
3066                        const char * server, int port)
3067 {
3068         int fd;
3069         mailstream * s;
3070         int r;
3071         
3072         fd = subcommand_connect(command, server, port);
3073         if (fd < 0)
3074                 return MAILIMAP_ERROR_STREAM;
3075         
3076         s = mailstream_socket_open(fd);
3077         if (s == NULL) {
3078                 close(fd);
3079                 return MAILIMAP_ERROR_STREAM;
3080         }
3081         
3082         r = mailimap_connect(imap, s);
3083         if (r != MAILIMAP_NO_ERROR_AUTHENTICATED
3084         &&  r != MAILIMAP_NO_ERROR_NON_AUTHENTICATED) {
3085                 mailstream_close(s);
3086                 if (imap)
3087                         imap->imap_stream = NULL;
3088                 return r;
3089         }
3090         
3091         return r;
3092 }
3093
3094 /* connect cmd */
3095
3096 struct connect_cmd_param {
3097         mailimap * imap;
3098         const char * command;
3099         const char * server;
3100         int port;
3101 };
3102
3103 struct connect_cmd_result {
3104         int error;
3105 };
3106
3107 static void connect_cmd_run(struct etpan_thread_op * op)
3108 {
3109         int r;
3110         struct connect_cmd_param * param;
3111         struct connect_cmd_result * result;
3112         
3113         param = op->param;
3114         result = op->result;
3115         
3116         CHECK_IMAP();
3117
3118         r = socket_connect_cmd(param->imap, param->command,
3119                                param->server, param->port);
3120         
3121         result->error = r;
3122 }
3123
3124
3125 int imap_threaded_connect_cmd(Folder * folder, const char * command,
3126                               const char * server, int port)
3127 {
3128         struct connect_cmd_param param;
3129         struct connect_cmd_result result;
3130         chashdatum key;
3131         chashdatum value;
3132         mailimap * imap, * oldimap;
3133         
3134         oldimap = get_imap(folder);
3135
3136         imap = mailimap_new(0, NULL);
3137         
3138         if (oldimap) {
3139                 debug_print("deleting old imap %p\n", oldimap);
3140                 delete_imap(folder, oldimap);
3141         }
3142
3143         key.data = &folder;
3144         key.len = sizeof(folder);
3145         value.data = imap;
3146         value.len = 0;
3147         chash_set(session_hash, &key, &value, NULL);
3148         
3149         param.imap = imap;
3150         param.command = command;
3151         param.server = server;
3152         param.port = port;
3153         
3154         threaded_run(folder, &param, &result, connect_cmd_run);
3155         
3156         debug_print("connect_cmd ok %i with imap %p\n", result.error, imap);
3157         
3158         return result.error;
3159 }
3160
3161 void imap_threaded_cancel(Folder * folder)
3162 {
3163         mailimap * imap;
3164         
3165         imap = get_imap(folder);
3166         if (imap->imap_stream != NULL)
3167                 mailstream_cancel(imap->imap_stream);
3168 }
3169
3170 #else
3171
3172 void imap_main_init(void)
3173 {
3174 }
3175 void imap_main_done(void)
3176 {
3177 }
3178 void imap_main_set_timeout(int sec)
3179 {
3180 }
3181
3182 void imap_threaded_cancel(Folder * folder);
3183 {
3184 }
3185
3186 #endif