1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /* camel-imap-store.c : class for a imap store */
5 * Authors: Michael Zucchi <notzed@ximian.com>
7 * Copyright (C) 2000-2002 Ximian, Inc. (www.ximian.com)
9 * This program is free software; you can redistribute it and/or
10 * modify it under the terms of version 2 of the GNU Lesser General Public
11 * License as published by the Free Software Foundation.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU Lesser General Public License for more details.
18 * You should have received a copy of the GNU Lesser General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
34 #include <netinet/in.h>
35 #include <sys/socket.h>
36 #include <sys/types.h>
38 #include <glib/gi18n-lib.h>
40 #include "camel/camel-data-cache.h"
41 #include "camel/camel-exception.h"
42 #include "camel/camel-net-utils.h"
43 #include "camel/camel-operation.h"
44 #include "camel/camel-sasl.h"
45 #include "camel/camel-session.h"
46 #include "camel/camel-stream-buffer.h"
47 #include "camel/camel-tcp-stream-raw.h"
48 #include "camel/camel-tcp-stream.h"
49 #include "camel/camel-url.h"
52 #include "camel/camel-tcp-stream-ssl.h"
55 #include "camel-imapp-driver.h"
56 #include "camel-imapp-engine.h"
57 #include "camel-imapp-exception.h"
58 #include "camel-imapp-folder.h"
59 #include "camel-imapp-store-summary.h"
60 #include "camel-imapp-store.h"
61 #include "camel-imapp-utils.h"
63 /* Specified in RFC 2060 section 2.1 */
66 static CamelStoreClass *parent_class = NULL;
68 static void finalize (CamelObject *object);
70 static void imap_construct(CamelService *service, CamelSession *session, CamelProvider *provider, CamelURL *url, CamelException *ex);
71 /* static char *imap_get_name(CamelService *service, gboolean brief);*/
72 static gboolean imap_connect (CamelService *service, CamelException *ex);
73 static gboolean imap_disconnect (CamelService *service, gboolean clean, CamelException *ex);
74 static GList *imap_query_auth_types (CamelService *service, CamelException *ex);
76 static CamelFolder *imap_get_trash (CamelStore *store, CamelException *ex);
78 static CamelFolder *imap_get_folder(CamelStore * store, const char *folder_name, guint32 flags, CamelException * ex);
79 static CamelFolder *imap_get_inbox (CamelStore *store, CamelException *ex);
80 static void imap_rename_folder(CamelStore *store, const char *old_name, const char *new_name, CamelException *ex);
81 static CamelFolderInfo *imap_get_folder_info (CamelStore *store, const char *top, guint32 flags, CamelException *ex);
82 static void imap_delete_folder(CamelStore *store, const char *folder_name, CamelException *ex);
83 static void imap_rename_folder(CamelStore *store, const char *old, const char *new, CamelException *ex);
84 static CamelFolderInfo *imap_create_folder(CamelStore *store, const char *parent_name, const char *folder_name, CamelException *ex);
86 /* yet to see if this should go global or not */
87 void camel_imapp_store_folder_selected(CamelIMAPPStore *store, CamelIMAPPFolder *folder, CamelIMAPPSelectResponse *select);
90 camel_imapp_store_class_init (CamelIMAPPStoreClass *camel_imapp_store_class)
92 CamelServiceClass *camel_service_class = CAMEL_SERVICE_CLASS(camel_imapp_store_class);
93 CamelStoreClass *camel_store_class = CAMEL_STORE_CLASS(camel_imapp_store_class);
95 parent_class = CAMEL_STORE_CLASS(camel_type_get_global_classfuncs(camel_store_get_type()));
97 /* virtual method overload */
98 camel_service_class->construct = imap_construct;
99 /*camel_service_class->get_name = imap_get_name;*/
100 camel_service_class->query_auth_types = imap_query_auth_types;
101 camel_service_class->connect = imap_connect;
102 camel_service_class->disconnect = imap_disconnect;
104 camel_store_class->get_trash = imap_get_trash;
105 camel_store_class->get_folder = imap_get_folder;
106 camel_store_class->get_inbox = imap_get_inbox;
108 camel_store_class->create_folder = imap_create_folder;
109 camel_store_class->rename_folder = imap_rename_folder;
110 camel_store_class->delete_folder = imap_delete_folder;
111 camel_store_class->get_folder_info = imap_get_folder_info;
115 camel_imapp_store_init (gpointer object, gpointer klass)
117 /*CamelIMAPPStore *istore = object;*/
121 camel_imapp_store_get_type (void)
123 static CamelType camel_imapp_store_type = CAMEL_INVALID_TYPE;
125 if (!camel_imapp_store_type) {
126 camel_imapp_store_type = camel_type_register(CAMEL_STORE_TYPE,
128 sizeof (CamelIMAPPStore),
129 sizeof (CamelIMAPPStoreClass),
130 (CamelObjectClassInitFunc) camel_imapp_store_class_init,
132 (CamelObjectInitFunc) camel_imapp_store_init,
136 return camel_imapp_store_type;
140 finalize (CamelObject *object)
142 CamelIMAPPStore *imap_store = CAMEL_IMAPP_STORE (object);
144 /* force disconnect so we dont have it run later, after we've cleaned up some stuff */
147 camel_service_disconnect((CamelService *)imap_store, TRUE, NULL);
149 if (imap_store->driver)
150 camel_object_unref(imap_store->driver);
151 if (imap_store->cache)
152 camel_object_unref(imap_store->cache);
155 static void imap_construct(CamelService *service, CamelSession *session, CamelProvider *provider, CamelURL *url, CamelException *ex)
157 char *root, *summary;
158 CamelIMAPPStore *store = (CamelIMAPPStore *)service;
160 CAMEL_SERVICE_CLASS (parent_class)->construct (service, session, provider, url, ex);
161 if (camel_exception_is_set(ex))
165 store->summary = camel_imapp_store_summary_new();
166 root = camel_session_get_storage_path(service->session, service, ex);
168 summary = g_build_filename(root, ".ev-store-summary", NULL);
169 camel_store_summary_set_filename((CamelStoreSummary *)store->summary, summary);
170 /* FIXME: need to remove params, passwords, etc */
171 camel_store_summary_set_uri_base((CamelStoreSummary *)store->summary, service->url);
172 camel_store_summary_load((CamelStoreSummary *)store->summary);
175 camel_exception_xfer(ex, e);
182 USE_SSL_WHEN_POSSIBLE
185 #define SSL_PORT_FLAGS (CAMEL_TCP_STREAM_SSL_ENABLE_SSL2 | CAMEL_TCP_STREAM_SSL_ENABLE_SSL3)
186 #define STARTTLS_FLAGS (CAMEL_TCP_STREAM_SSL_ENABLE_TLS)
189 connect_to_server (CamelService *service, int ssl_mode, int try_starttls)
190 /* throws IO exception */
192 CamelIMAPPStore *store = CAMEL_IMAPP_STORE (service);
193 CamelStream * volatile tcp_stream = NULL;
194 CamelIMAPPStream * volatile imap_stream = NULL;
198 ex = camel_exception_new();
201 const char *port = NULL;
202 struct addrinfo *ai, hints = { 0 };
204 /* parent class connect initialization */
205 CAMEL_SERVICE_CLASS (parent_class)->connect (service, ex);
207 camel_exception_throw_ex(ex);
209 if (service->url->port) {
211 sprintf(serv, "%d", service->url->port);
218 if (camel_url_get_param (service->url, "use_ssl")) {
220 tcp_stream = camel_tcp_stream_ssl_new_raw (service->session, service->url->host, STARTTLS_FLAGS);
222 if (service->url->port == 0) {
226 tcp_stream = camel_tcp_stream_ssl_new (service->session, service->url->host, SSL_PORT_FLAGS);
229 tcp_stream = camel_tcp_stream_raw_new ();
232 tcp_stream = camel_tcp_stream_raw_new ();
233 #endif /* HAVE_SSL */
235 hints.ai_socktype = SOCK_STREAM;
236 ai = camel_getaddrinfo(service->url->host, serv, &hints, ex);
237 if (ex->id && ex->id != CAMEL_EXCEPTION_USER_CANCEL && port != NULL) {
238 camel_exception_clear(ex);
239 ai = camel_getaddrinfo(service->url->host, port, &hints, ex);
243 camel_exception_throw_ex(ex);
245 ret = camel_tcp_stream_connect(CAMEL_TCP_STREAM(tcp_stream), ai);
246 camel_freeaddrinfo(ai);
249 camel_exception_throw(CAMEL_EXCEPTION_USER_CANCEL, _("Connection canceled"));
251 camel_exception_throw(CAMEL_EXCEPTION_SERVICE_UNAVAILABLE,
252 _("Could not connect to %s (port %s): %s"),
253 service->url->host, serv, strerror(errno));
256 imap_stream = (CamelIMAPPStream *)camel_imapp_stream_new(tcp_stream);
257 store->driver = camel_imapp_driver_new(imap_stream);
259 camel_object_unref(imap_stream);
260 camel_object_unref(tcp_stream);
263 camel_object_unref(tcp_stream);
265 camel_object_unref((CamelObject *)imap_stream);
266 camel_exception_throw_ex(e);
269 camel_exception_free(ex);
274 /* leave this stuff out for now */
281 { "", USE_SSL_ALWAYS },
282 { "always", USE_SSL_ALWAYS },
283 { "when-possible", USE_SSL_WHEN_POSSIBLE },
284 { "never", USE_SSL_NEVER },
285 { NULL, USE_SSL_NEVER },
289 connect_to_server_wrapper (CamelService *service, CamelException *ex)
295 use_ssl = camel_url_get_param (service->url, "use_ssl");
297 for (i = 0; ssl_options[i].value; i++)
298 if (!strcmp (ssl_options[i].value, use_ssl))
300 ssl_mode = ssl_options[i].mode;
302 ssl_mode = USE_SSL_NEVER;
304 if (ssl_mode == USE_SSL_ALWAYS) {
305 /* First try the ssl port */
306 if (!connect_to_server (service, ssl_mode, FALSE, ex)) {
307 if (camel_exception_get_id (ex) == CAMEL_EXCEPTION_SERVICE_UNAVAILABLE) {
308 /* The ssl port seems to be unavailable, lets try STARTTLS */
309 camel_exception_clear (ex);
310 return connect_to_server (service, ssl_mode, TRUE, ex);
317 } else if (ssl_mode == USE_SSL_WHEN_POSSIBLE) {
318 /* If the server supports STARTTLS, use it */
319 return connect_to_server (service, ssl_mode, TRUE, ex);
321 /* User doesn't care about SSL */
322 return connect_to_server (service, ssl_mode, FALSE, ex);
325 return connect_to_server (service, USE_SSL_NEVER, FALSE, ex);
330 extern CamelServiceAuthType camel_imapp_password_authtype;
331 extern CamelServiceAuthType camel_imapp_apop_authtype;
334 imap_query_auth_types (CamelService *service, CamelException *ex)
336 /*CamelIMAPPStore *store = CAMEL_IMAPP_STORE (service);*/
339 types = CAMEL_SERVICE_CLASS (parent_class)->query_auth_types (service, ex);
344 if (connect_to_server_wrapper (service, NULL)) {
345 types = g_list_concat(types, g_list_copy(store->engine->auth));
346 imap_disconnect (service, TRUE, NULL);
348 camel_exception_setv (ex, CAMEL_EXCEPTION_SERVICE_UNAVAILABLE,
349 _("Could not connect to POP server on %s"),
357 store_get_pass(CamelIMAPPStore *store)
359 if (((CamelService *)store)->url->passwd == NULL) {
363 camel_exception_init(&ex);
365 prompt = g_strdup_printf (_("%sPlease enter the IMAP password for %s@%s"),
366 store->login_error?store->login_error:"",
367 ((CamelService *)store)->url->user,
368 ((CamelService *)store)->url->host);
369 ((CamelService *)store)->url->passwd = camel_session_get_password(camel_service_get_session((CamelService *)store),
370 (CamelService *)store, NULL,
371 prompt, "password", CAMEL_SESSION_PASSWORD_SECRET, &ex);
373 if (camel_exception_is_set(&ex))
374 camel_exception_throw_ex(&ex);
378 static struct _CamelSasl *
379 store_get_sasl(struct _CamelIMAPPDriver *driver, CamelIMAPPStore *store)
381 store_get_pass(store);
383 if (((CamelService *)store)->url->authmech)
384 return camel_sasl_new("imap", ((CamelService *)store)->url->authmech, (CamelService *)store);
390 store_get_login(struct _CamelIMAPPDriver *driver, char **login, char **pass, CamelIMAPPStore *store)
392 store_get_pass(store);
394 *login = g_strdup(((CamelService *)store)->url->user);
395 *pass = g_strdup(((CamelService *)store)->url->passwd);
399 imap_connect (CamelService *service, CamelException *ex)
401 CamelIMAPPStore *store = (CamelIMAPPStore *)service;
402 volatile int ret = FALSE;
405 volatile int retry = TRUE;
407 if (store->cache == NULL) {
410 root = camel_session_get_storage_path(service->session, service, ex);
412 store->cache = camel_data_cache_new(root, 0, ex);
415 /* Default cache expiry - 1 week or not visited in a day */
416 camel_data_cache_set_expire_age(store->cache, 60*60*24*7);
417 camel_data_cache_set_expire_access(store->cache, 60*60*24);
420 if (camel_exception_is_set(ex))
421 camel_exception_throw_ex(ex);
424 connect_to_server(service, USE_SSL_NEVER, FALSE);
426 camel_imapp_driver_set_sasl_factory(store->driver, (CamelIMAPPSASLFunc)store_get_sasl, store);
427 camel_imapp_driver_set_login_query(store->driver, (CamelIMAPPLoginFunc)store_get_login, store);
428 store->login_error = NULL;
432 if (store->driver->engine->state != IMAP_ENGINE_AUTH)
433 camel_imapp_driver_login(store->driver);
437 g_free(store->login_error);
438 store->login_error = NULL;
440 case CAMEL_EXCEPTION_SERVICE_CANT_AUTHENTICATE:
441 store->login_error = g_strdup_printf("%s\n\n", e->desc);
442 camel_session_forget_password(service->session, service, NULL, "password", ex);
443 camel_url_set_passwd(service->url, NULL);
446 camel_exception_throw_ex(e);
452 camel_exception_xfer(ex, e);
453 camel_service_disconnect(service, TRUE, NULL);
457 g_free(store->login_error);
458 store->login_error = NULL;
464 imap_disconnect (CamelService *service, gboolean clean, CamelException *ex)
466 CamelIMAPPStore *store = CAMEL_IMAPP_STORE (service);
470 if (!CAMEL_SERVICE_CLASS (parent_class)->disconnect (service, clean, ex))
473 /* logout/disconnect */
475 camel_object_unref(store->driver);
476 store->driver = NULL;
483 imap_get_trash (CamelStore *store, CamelException *ex)
490 imap_get_folder (CamelStore *store, const char *folder_name, guint32 flags, CamelException *ex)
492 CamelIMAPPStore *istore = (CamelIMAPPStore *)store;
493 CamelIMAPPFolder * volatile folder = NULL;
497 /* 1. create the folder */
499 /* 3. update the folder */
502 folder = (CamelIMAPPFolder *)camel_imapp_folder_new(store, folder_name);
503 camel_imapp_driver_select(istore->driver, folder);
506 camel_object_unref(folder);
509 camel_exception_xfer(ex, e);
512 return (CamelFolder *)folder;
516 imap_get_inbox(CamelStore *store, CamelException *ex)
518 camel_exception_setv(ex, 1, "get_inbox::unimplemented");
523 /* 8 bit, string compare */
524 static int folders_build_cmp(const void *app, const void *bpp)
526 struct _list_info *a = *((struct _list_info **)app);
527 struct _list_info *b = *((struct _list_info **)bpp);
528 unsigned char *ap = (unsigned char *)(a->name);
529 unsigned char *bp = (unsigned char *)(b->name);
531 printf("qsort, cmp '%s' <> '%s'\n", ap, bp);
533 while (*ap && *ap == *bp) {
545 /* FIXME: this should go via storesummary? */
546 static CamelFolderInfo *
547 folders_build_info(CamelURL *base, struct _list_info *li)
549 char *path, *full_name, *name;
552 full_name = imapp_list_get_path(li);
553 name = strrchr(full_name, '/');
559 path = alloca(strlen(full_name)+2);
560 sprintf(path, "/%s", full_name);
561 camel_url_set_path(base, path);
563 fi = g_malloc0(sizeof(*fi));
564 fi->uri = camel_url_to_string(base, CAMEL_URL_HIDE_ALL);
565 fi->name = g_strdup(name);
566 fi->full_name = full_name;
569 fi->flags = li->flags;
571 if (!g_ascii_strcasecmp(fi->full_name, "inbox"))
572 fi->flags |= CAMEL_FOLDER_SYSTEM;
574 /* TODO: could look up count here ... */
576 /*folder = camel_object_bag_get(store->folders, "INBOX");*/
591 /* note, pname is the raw name, not the folderinfo name */
592 /* note also this free's as we go, since we never go 'backwards' */
593 static CamelFolderInfo *
594 folders_build_rec(CamelURL *base, GPtrArray *folders, int *ip, CamelFolderInfo *pfi, char *pname)
597 CamelFolderInfo *last = NULL, *first = NULL;
600 plen = strlen(pname);
602 for(;(*ip)<(int)folders->len;) {
604 struct _list_info *li;
606 li = folders->pdata[*ip];
607 printf("checking '%s' is child of '%s'\n", li->name, pname);
609 /* is this a child of the parent? */
611 && (strncmp(pname, li->name, strlen(pname)) != 0
612 || li->name[plen] != li->separator)) {
618 /* is this not an immediate child of the parent? */
622 && li->separator != 0
623 && (p = strchr(li->name + plen + 1, li->separator)) != NULL) {
625 struct _list_info tli;
627 tli.flags = CAMEL_FOLDER_NOSELECT|CAMEL_FOLDER_CHILDREN;
628 tli.separator = li->separator;
629 tli.name = g_strndup(li->name, p-li->name+1);
630 fi = folders_build_info(base, &tli);
632 if (pfi && pfi->child == NULL)
634 i = folders_build_rec(folders, i, fi, tli.name);
640 fi = folders_build_info(base, li);
649 fi->child = folders_build_rec(base, folders, ip, fi, li->name);
657 folder_info_dump(CamelFolderInfo *fi, int depth)
662 memset(s, ' ', depth);
665 printf("%s%s (%s)\n", s, fi->name, fi->uri);
667 folder_info_dump(fi->child, depth+2);
673 static CamelFolderInfo *
674 imap_get_folder_info(CamelStore *store, const char *top, guint32 flags, CamelException *ex)
676 CamelIMAPPStore *istore = (CamelIMAPPStore *)store;
677 CamelFolderInfo * fi= NULL;
680 /* FIXME: temporary, since this is not a disco store */
681 if (istore->driver == NULL
682 && !camel_service_connect((CamelService *)store, ex))
686 if (name == NULL || name[0] == 0) {
698 /* FIXME: subscriptions? lsub? */
699 folders = camel_imapp_driver_list(istore->driver, name, flags);
701 /* this greatly simplifies the tree algorithm ... but it might
702 be faster just to use a hashtable to find parents? */
703 qsort(folders->pdata, folders->len, sizeof(folders->pdata[0]), folders_build_cmp);
706 base = camel_url_copy(((CamelService *)store)->url);
707 fi = folders_build_rec(base, folders, &i, NULL, NULL);
708 camel_url_free(base);
709 g_ptr_array_free(folders, TRUE);
711 camel_exception_xfer(ex, e);
714 printf("built folder info:\n");
715 folder_info_dump(fi, 2);
720 if (top == NULL || !g_ascii_strcasecmp(top, "inbox")) {
721 CamelURL *uri = camel_url_copy(((CamelService *)store)->url);
723 camel_url_set_path(uri, "/INBOX");
724 fi = g_malloc0(sizeof(*fi));
725 fi->url = camel_url_to_string(uri, CAMEL_URL_HIDE_ALL);
727 fi->name = g_strdup("INBOX");
728 fi->full_name = g_strdup("INBOX");
729 fi->path = g_strdup("/INBOX");
730 fi->unread_message_count = -1;
733 folder = camel_object_bag_get(store->folders, "INBOX");
735 /*if (!cflags & FAST)*/
736 camel_imapp_driver_update(istore->driver, (CamelIMAPPFolder *)folder);
737 fi->unread_message_count = camel_folder_get_unread_message_count(folder);
738 camel_object_unref(folder);
741 camel_exception_setv(ex, 1, "not implemented");
747 istore->pending_list = g_ptr_array_new();
750 ic = camel_imapp_engine_command_new(istore->driver->engine, "LIST", NULL, "LIST \"\" %f", top);
751 camel_imapp_engine_command_queue(istore->driver->engine, ic);
752 while (camel_imapp_engine_iterate(istore->driver->engine, ic) > 0)
755 if (ic->status->result != IMAP_OK)
756 camel_exception_throw(1, "list failed: %s", ic->status->text);
758 camel_exception_xfer(ex, e);
761 camel_imapp_engine_command_free(istore->driver->engine, ic);
763 printf("got folder list:\n");
764 for (i=0;i<(int)istore->pending_list->len;i++) {
765 struct _list_info *linfo = istore->pending_list->pdata[i];
767 printf("%s (%c)\n", linfo->name, linfo->separator);
768 imap_free_list(linfo);
770 istore->pending_list = NULL;
777 imap_delete_folder(CamelStore *store, const char *folder_name, CamelException *ex)
779 camel_exception_setv(ex, 1, "delete_folder::unimplemented");
783 imap_rename_folder(CamelStore *store, const char *old, const char *new, CamelException *ex)
785 camel_exception_setv(ex, 1, "rename_folder::unimplemented");
788 static CamelFolderInfo *
789 imap_create_folder(CamelStore *store, const char *parent_name, const char *folder_name, CamelException *ex)
791 camel_exception_setv(ex, 1, "create_folder::unimplemented");
795 /* ********************************************************************** */
797 static int store_resp_fetch(CamelIMAPPEngine *ie, guint32 id, void *data)
799 struct _fetch_info *finfo;
800 CamelIMAPPStore *istore = data;
801 CamelMessageInfo *info;
802 struct _pending_fetch *pending;
804 finfo = imap_parse_fetch(ie->stream);
805 if (istore->selected) {
806 if ((finfo->got & FETCH_UID) == 0) {
807 printf("didn't get uid in fetch response?\n");
809 info = camel_folder_summary_index(((CamelFolder *)istore->selected)->summary, id-1);
810 /* exists, check/update */
812 if (strcmp(finfo->uid, camel_message_info_uid(info)) != 0) {
813 printf("summary at index %d has uid %s expected %s\n", id, camel_message_info_uid(info), finfo->uid);
814 /* uid mismatch??? try do it based on uid instead? try to reorder? i dont know? */
815 camel_message_info_free(info);
816 info = camel_folder_summary_uid(((CamelFolder *)istore->selected)->summary, finfo->uid);
821 if (finfo->got & (FETCH_FLAGS)) {
822 printf("updating flags for uid '%s'\n", finfo->uid);
823 info->flags = finfo->flags;
824 camel_folder_change_info_change_uid(istore->selected->changes, finfo->uid);
826 if (finfo->got & FETCH_MINFO) {
827 printf("got envelope unexpectedly?\n");
829 /* other things go here, like body fetches */
831 pending = g_hash_table_lookup(istore->pending_fetch_table, finfo->uid);
833 /* we need to create a new info, we only care about flags and minfo */
836 info = pending->info;
838 info = camel_folder_summary_info_new(((CamelFolder *)istore->selected)->summary);
839 camel_message_info_set_uid(info, g_strdup(finfo->uid));
842 if (finfo->got & FETCH_FLAGS)
843 info->flags = finfo->flags;
845 if (finfo->got & FETCH_MINFO) {
846 /* if we only use ENVELOPE? */
847 camel_message_info_set_subject(info, g_strdup(camel_message_info_subject(finfo->minfo)));
848 camel_message_info_set_from(info, g_strdup(camel_message_info_from(finfo->minfo)));
849 camel_message_info_set_to(info, g_strdup(camel_message_info_to(finfo->minfo)));
850 camel_message_info_set_cc(info, g_strdup(camel_message_info_cc(finfo->minfo)));
851 info->date_sent = finfo->minfo->date_sent;
852 camel_folder_summary_add(((CamelFolder *)istore->selected)->summary, info);
853 camel_folder_change_info_add_uid(istore->selected->changes, finfo->uid);
855 e_dlist_remove((EDListNode *)pending);
856 g_hash_table_remove(istore->pending_fetch_table, finfo->uid);
857 /*e_memchunk_free(istore->pending_fetch_chunks, pending);*/
859 } else if (finfo->got & FETCH_HEADER) {
860 /* if we only use HEADER? */
864 camel_message_info_free(info);
865 mp = camel_mime_parser_new();
866 camel_mime_parser_init_with_stream(mp, finfo->header);
867 info = camel_folder_summary_info_new_from_parser(((CamelFolder *)istore->selected)->summary, mp);
868 camel_object_unref(mp);
869 camel_message_info_set_uid(info, g_strdup(finfo->uid));
871 camel_folder_summary_add(((CamelFolder *)istore->selected)->summary, info);
872 camel_folder_change_info_add_uid(istore->selected->changes, finfo->uid);
874 /* FIXME: use a dlist */
875 e_dlist_remove((EDListNode *)pending);
876 g_hash_table_remove(istore->pending_fetch_table, camel_message_info_uid(pending->info));
877 camel_message_info_free(pending->info);
878 /*e_memchunk_free(istore->pending_fetch_chunks, pending);*/
880 } else if (finfo->got & FETCH_FLAGS) {
881 if (pending == NULL) {
882 pending = e_memchunk_alloc(istore->pending_fetch_chunks);
883 pending->info = info;
884 g_hash_table_insert(istore->pending_fetch_table, (char *)camel_message_info_uid(info), pending);
885 e_dlist_addtail(&istore->pending_fetch_list, (EDListNode *)pending);
889 camel_message_info_free(info);
890 printf("got unexpected fetch response?\n");
891 imap_dump_fetch(finfo);
896 printf("unexpected fetch response, no folder selected?\n");
898 /*imap_dump_fetch(finfo);*/
899 imap_free_fetch(finfo);
901 return camel_imapp_engine_skip(ie);
905 /* ********************************************************************** */
907 /* should be moved to imapp-utils?
908 stuff in imapp-utils should be moved to imapp-parse? */
910 /* ********************************************************************** */
914 camel_imapp_store_folder_selected(CamelIMAPPStore *store, CamelIMAPPFolder *folder, CamelIMAPPSelectResponse *select)
916 CamelIMAPPCommand * volatile ic = NULL;
917 CamelIMAPPStore *istore = (CamelIMAPPStore *)store;
919 struct _uidset_state ss;
921 CamelMessageInfo *info;
922 struct _pending_fetch *fw, *fn;
924 printf("imap folder selected\n");
926 if (select->uidvalidity == folder->uidvalidity
927 && select->exists == folder->exists
928 && select->recent == folder->recent
929 && select->unseen == folder->unseen) {
934 istore->pending_fetch_table = g_hash_table_new(g_str_hash, g_str_equal);
935 istore->pending_fetch_chunks = e_memchunk_new(256, sizeof(struct _pending_fetch));
937 /* perform an update - flags first (and see what we have) */
939 ic = camel_imapp_engine_command_new(istore->engine, "FETCH", NULL, "FETCH 1:%d (UID FLAGS)", select->exists);
940 camel_imapp_engine_command_queue(istore->engine, ic);
941 while (camel_imapp_engine_iterate(istore->engine, ic) > 0)
944 if (ic->status->result != IMAP_OK)
945 camel_exception_throw(1, "fetch failed: %s", ic->status->text);
947 /* pending_fetch_list now contains any new messages */
948 /* FIXME: how do we work out no-longer present messages? */
949 printf("now fetching info for messages?\n");
950 uidset_init(&ss, store->engine);
951 ic = camel_imapp_engine_command_new(istore->engine, "FETCH", NULL, "UID FETCH ");
952 fw = (struct _pending_fetch *)istore->pending_fetch_list.head;
956 /* if the uid set fills, then flush the command out */
957 if (uidset_add(&ss, ic, camel_message_info_uid(info))
958 || (fn->next == NULL && uidset_done(&ss, ic))) {
959 camel_imapp_engine_command_add(istore->engine, ic, " (FLAGS RFC822.HEADER)");
960 camel_imapp_engine_command_queue(istore->engine, ic);
961 while (camel_imapp_engine_iterate(istore->engine, ic) > 0)
963 if (ic->status->result != IMAP_OK)
964 camel_exception_throw(1, "fetch failed: %s", ic->status->text);
966 camel_imapp_engine_command_free(istore->engine, ic);
967 ic = camel_imapp_engine_command_new(istore->engine, "FETCH", NULL, "UID FETCH ");
973 printf("The pending list should now be empty: %s\n", e_dlist_empty(&istore->pending_fetch_list)?"TRUE":"FALSE");
975 info = camel_folder_summary_index(((CamelFolder *)istore->selected)->summary, i);
977 printf("message info [%d] =\n", i);
978 camel_message_info_dump(info);
979 camel_message_info_free(info);
984 camel_exception_throw_ex(e);
987 g_hash_table_destroy(istore->pending_fetch_table);
988 istore->pending_fetch_table = NULL;
989 e_memchunk_destroy(istore->pending_fetch_chunks);
991 camel_imapp_engine_command_free(istore->engine, ic);
996 /*char *uids[] = {"1", "2", "4", "5", "6", "7", "9", "11", "12", 0};*/
997 /*char *uids[] = {"1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", 0};*/
998 char *uids[] = {"1", "3", "5", "7", "9", "11", "12", "13", "14", "15", "20", "21", "24", "25", "26", 0};
1001 uidset_test(CamelIMAPPEngine *ie)
1003 struct _uidset_state ss;
1004 CamelIMAPPCommand *ic;
1007 /*ic = camel_imapp_engine_command_new(ie, 0, "FETCH", NULL, "FETCH ");*/
1008 uidset_init(&ss, 0, 0);
1009 for (i=0;uids[i];i++) {
1010 if (uidset_add(&ss, uids[i])) {
1011 printf("\n[%d] flushing uids\n", i);
1015 if (uidset_done(&ss)) {
1016 printf("\nflushing uids\n");