talons

Fork of Claws Mail https://www.claws-mail
Log | Files | Refs | README | LICENSE

imap-thread.c (75154B)


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