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