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