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