1
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2
/* camel-imap-store.c : class for an imap store */
6
* Dan Winship <danw@ximian.com>
7
* Jeffrey Stedfast <fejj@ximian.com>
8
* Philip Van Hoof <pvanhoof@gnome.org>
10
* This is camel-lite's version of CamelImapStore. Take a look at camel-imap-folder.c
11
* for more information on what differs from Camel's upstream version.
13
* Copyright 2000, 2003 Ximian, Inc.
15
* This program is free software; you can redistribute it and/or
16
* modify it under the terms of version 2 of the GNU Lesser General Public
17
* License as published by the Free Software Foundation.
19
* This program is distributed in the hope that it will be useful,
20
* but WITHOUT ANY WARRANTY; without even the implied warranty of
21
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
22
* General Public License for more details.
24
* You should have received a copy of the GNU Lesser General Public
25
* License along with this program; if not, write to the
26
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
27
* Boston, MA 02111-1307, USA.
40
#include <glib/gi18n-lib.h>
41
#include <glib/gstdio.h>
43
#include <sys/types.h>
46
#include "camel/camel-debug.h"
47
#include "camel/camel-disco-diary.h"
48
#include "camel/camel-exception.h"
49
#include "camel/camel-file-utils.h"
50
#include "camel/camel-folder.h"
51
#include "camel/camel-net-utils.h"
52
#include "camel/camel-string-utils.h"
53
#include "camel/camel-private.h"
54
#include "camel/camel-sasl.h"
55
#include "camel/camel-session.h"
56
#include "camel/camel-stream-buffer.h"
57
#include "camel/camel-stream-fs.h"
58
#include "camel/camel-stream-process.h"
59
#include "camel/camel-stream.h"
60
#include "camel/camel-stream-mem.h"
61
#include "camel/camel-mime-message.h"
62
#include "camel/camel-string-utils.h"
63
#include "camel/camel-tcp-stream-raw.h"
64
#include "camel/camel-tcp-stream-ssl.h"
65
#include "camel/camel-url.h"
66
#include "camel/camel-utf8.h"
68
#include "camel-imap-command.h"
69
#include "camel-imap-folder.h"
70
#include "camel-imap-message-cache.h"
71
#include "camel-imap-store-summary.h"
72
#include "camel-imap-store.h"
73
#include "camel-imap-summary.h"
74
#include "camel-imap-utils.h"
76
#if !GLIB_CHECK_VERSION (2, 8, 0)
77
#define g_access access
82
/* Specified in RFC 2060 */
83
#define IMAP_PORT "143"
84
#define IMAPS_PORT "993"
87
/* The strtok() in Microsoft's C library is MT-safe (but still uses
88
* only one buffer pointer per thread, but for the use of strtok_r()
89
* here that's enough).
91
#define strtok_r(s,sep,lasts) (*(lasts)=strtok((s),(sep)))
94
static CamelDiscoStoreClass *parent_class = NULL;
96
static char imap_tag_prefix = 'A';
98
static void construct (CamelService *service, CamelSession *session,
99
CamelProvider *provider, CamelURL *url,
102
static int imap_setv (CamelObject *object, CamelException *ex, CamelArgV *args);
103
static int imap_getv (CamelObject *object, CamelException *ex, CamelArgGetV *args);
105
static char *imap_get_name (CamelService *service, gboolean brief);
107
static gboolean can_work_offline (CamelDiscoStore *disco_store);
108
static gboolean imap_connect_online (CamelService *service, CamelException *ex);
109
static gboolean imap_connect_offline (CamelService *service, CamelException *ex);
110
static gboolean imap_disconnect_online (CamelService *service, gboolean clean, CamelException *ex);
111
static gboolean imap_disconnect_offline (CamelService *service, gboolean clean, CamelException *ex);
112
static void imap_noop (CamelStore *store, CamelException *ex);
113
static CamelFolder *imap_get_junk(CamelStore *store, CamelException *ex);
114
static CamelFolder *imap_get_trash(CamelStore *store, CamelException *ex);
115
static GList *query_auth_types (CamelService *service, CamelException *ex);
116
static guint hash_folder_name (gconstpointer key);
117
static gint compare_folder_name (gconstpointer a, gconstpointer b);
118
static CamelFolder *get_folder_online (CamelStore *store, const char *folder_name, guint32 flags, CamelException *ex);
119
static CamelFolder *get_folder_offline (CamelStore *store, const char *folder_name, guint32 flags, CamelException *ex);
121
static CamelFolderInfo *create_folder (CamelStore *store, const char *parent_name, const char *folder_name, CamelException *ex);
122
static void delete_folder (CamelStore *store, const char *folder_name, CamelException *ex);
123
static void rename_folder (CamelStore *store, const char *old_name, const char *new_name, CamelException *ex);
124
static CamelFolderInfo *get_folder_info_online (CamelStore *store,
128
static CamelFolderInfo *get_folder_info_offline (CamelStore *store,
132
static gboolean folder_subscribed (CamelStore *store, const char *folder_name);
133
static void subscribe_folder (CamelStore *store, const char *folder_name,
135
static void unsubscribe_folder (CamelStore *store, const char *folder_name,
138
static void get_folders_sync(CamelImapStore *imap_store, const char *pattern, CamelException *ex);
140
static void imap_folder_effectively_unsubscribed(CamelImapStore *imap_store, const char *folder_name, CamelException *ex);
141
static gboolean imap_check_folder_still_extant (CamelImapStore *imap_store, const char *full_name, CamelException *ex);
142
static void imap_forget_folder(CamelImapStore *imap_store, const char *folder_name, CamelException *ex);
143
static void imap_set_server_level (CamelImapStore *store);
145
static void imap_get_folder_status (CamelStore *store, const char *folder_name, int *unseen, int *messages, int *uidnext);
148
camel_imap_recon (CamelImapStore *store, CamelException *mex)
150
CamelService *service = CAMEL_SERVICE (store);
152
service->reconnecting = TRUE;
153
if (service->reconnecter)
154
service->reconnecter (service, FALSE, service->data);
156
camel_service_disconnect (service, FALSE, NULL);
157
camel_service_connect (service, mex);
159
if (mex && camel_exception_is_set (mex))
161
camel_exception_clear (mex);
163
camel_service_connect (service, mex);
165
if (service->reconnection) {
166
if (!camel_exception_is_set (mex))
167
service->reconnection (service, TRUE, service->data);
169
service->reconnection (service, FALSE, service->data);
172
service->reconnecting = FALSE;
176
imap_delete_cache (CamelStore *store)
178
CamelImapStore *imap_store = (CamelImapStore *) store;
179
gchar *folder_dir = imap_store->storage_path;
180
camel_rm (folder_dir);
184
let_idle_die (CamelImapStore *imap_store, gboolean connect_buz)
186
imap_store->idle_cont = FALSE;
188
g_static_rec_mutex_lock (imap_store->idle_prefix_lock);
189
g_static_rec_mutex_lock (imap_store->idle_lock);
191
imap_store->idle_cont = FALSE;
193
/* This one can get called from within the thread! This would deadlock
195
if (imap_store->in_idle && imap_store->idle_thread) {
196
g_thread_join (imap_store->idle_thread);
197
imap_store->idle_thread = NULL;
200
if (imap_store->idle_prefix)
204
CamelException ex = CAMEL_EXCEPTION_INITIALISER;
206
g_free (imap_store->idle_prefix);
207
imap_store->idle_prefix=NULL;
208
idle_debug ("Sending DONE in let_idle_die\n");
209
nwritten = camel_stream_printf (imap_store->ostream, "DONE\r\n");
213
while ((camel_imap_command_response_idle (imap_store, &resp, &ex)) == CAMEL_IMAP_RESPONSE_UNTAGGED)
215
idle_debug ("(.., ..) <- %s | in idle_deal_with_stuff in let_idle_die\n", resp);
216
g_free (resp); resp=NULL;
223
g_static_rec_mutex_unlock (imap_store->idle_lock);
224
g_static_rec_mutex_unlock (imap_store->idle_prefix_lock);
230
camel_imap_store_stop_idle (CamelImapStore *store)
232
if (store->current_folder && CAMEL_IS_IMAP_FOLDER (store->current_folder))
233
camel_imap_folder_stop_idle (store->current_folder);
235
store->idle_cont = FALSE;
237
g_static_rec_mutex_lock (store->idle_prefix_lock);
238
g_static_rec_mutex_lock (store->idle_lock);
240
store->idle_cont = FALSE;
241
if (store->in_idle && store->idle_thread) {
242
g_thread_join (store->idle_thread);
243
store->idle_thread = NULL;
246
if (store->idle_prefix)
250
CamelException ex = CAMEL_EXCEPTION_INITIALISER;
252
idle_debug ("Sending DONE in camel_imap_store_stop_idle (no current folder?)\n");
253
CAMEL_SERVICE_REC_LOCK (store, connect_lock);
254
nwritten = camel_stream_printf (store->ostream, "DONE\r\n");
259
while ((camel_imap_command_response_idle (store, &resp, &ex)) == CAMEL_IMAP_RESPONSE_UNTAGGED)
261
idle_debug ("(.., ..) <- %s | in idle_deal_with_stuff (no current folder?)\n", resp);
262
g_free (resp); resp=NULL;
268
CAMEL_SERVICE_REC_UNLOCK (store, connect_lock);
269
g_free (store->idle_prefix);
270
store->idle_prefix = NULL;
273
g_static_rec_mutex_unlock (store->idle_lock);
274
g_static_rec_mutex_unlock (store->idle_prefix_lock);
280
camel_imap_store_start_idle (CamelImapStore *store)
282
if (store->current_folder && CAMEL_IS_IMAP_FOLDER (store->current_folder))
283
camel_imap_folder_start_idle (store->current_folder);
287
camel_imap_store_class_init (CamelImapStoreClass *camel_imap_store_class)
289
CamelObjectClass *camel_object_class =
290
CAMEL_OBJECT_CLASS (camel_imap_store_class);
291
CamelServiceClass *camel_service_class =
292
CAMEL_SERVICE_CLASS (camel_imap_store_class);
293
CamelStoreClass *camel_store_class =
294
CAMEL_STORE_CLASS (camel_imap_store_class);
295
CamelDiscoStoreClass *camel_disco_store_class =
296
CAMEL_DISCO_STORE_CLASS (camel_imap_store_class);
298
parent_class = CAMEL_DISCO_STORE_CLASS (camel_type_get_global_classfuncs (camel_disco_store_get_type ()));
300
/* virtual method overload */
301
camel_object_class->setv = imap_setv;
302
camel_object_class->getv = imap_getv;
304
camel_service_class->construct = construct;
305
camel_service_class->query_auth_types = query_auth_types;
306
camel_service_class->get_name = imap_get_name;
308
camel_store_class->delete_cache = imap_delete_cache;
309
camel_store_class->hash_folder_name = hash_folder_name;
310
camel_store_class->compare_folder_name = compare_folder_name;
311
camel_store_class->create_folder = create_folder;
312
camel_store_class->delete_folder = delete_folder;
313
camel_store_class->rename_folder = rename_folder;
314
camel_store_class->free_folder_info = camel_store_free_folder_info_full;
315
camel_store_class->folder_subscribed = folder_subscribed;
316
camel_store_class->subscribe_folder = subscribe_folder;
317
camel_store_class->unsubscribe_folder = unsubscribe_folder;
318
camel_store_class->noop = imap_noop;
319
camel_store_class->get_trash = imap_get_trash;
320
camel_store_class->get_junk = imap_get_junk;
321
camel_store_class->get_folder_status = imap_get_folder_status;
323
camel_disco_store_class->can_work_offline = can_work_offline;
324
camel_disco_store_class->connect_online = imap_connect_online;
325
camel_disco_store_class->connect_offline = imap_connect_offline;
326
camel_disco_store_class->disconnect_online = imap_disconnect_online;
327
camel_disco_store_class->disconnect_offline = imap_disconnect_offline;
328
camel_disco_store_class->get_folder_online = get_folder_online;
329
camel_disco_store_class->get_folder_offline = get_folder_offline;
330
camel_disco_store_class->get_folder_resyncing = get_folder_online;
331
camel_disco_store_class->get_folder_info_online = get_folder_info_online;
332
camel_disco_store_class->get_folder_info_offline = get_folder_info_offline;
333
camel_disco_store_class->get_folder_info_resyncing = get_folder_info_online;
337
free_key (gpointer key, gpointer value, gpointer user_data)
344
camel_imap_store_finalize (CamelObject *object)
346
CamelImapStore *imap_store = CAMEL_IMAP_STORE (object);
347
CamelDiscoStore *disco = CAMEL_DISCO_STORE (object);
348
CamelException nex = CAMEL_EXCEPTION_INITIALISER;
350
let_idle_die (imap_store, TRUE);
352
/* This frees current_folder, folders, authtypes, streams, and namespace. */
353
camel_service_disconnect((CamelService *)imap_store, TRUE, NULL);
355
if (imap_store->addrinfo) {
356
freeaddrinfo (imap_store->addrinfo);
357
imap_store->addrinfo = NULL;
360
g_static_rec_mutex_lock (imap_store->sum_lock);
361
if (imap_store->summary) {
362
camel_store_summary_save((CamelStoreSummary *)imap_store->summary, &nex);
363
camel_object_unref(imap_store->summary);
365
g_static_rec_mutex_unlock (imap_store->sum_lock);
368
if (imap_store->base_url)
369
g_free (imap_store->base_url);
370
if (imap_store->storage_path)
371
g_free (imap_store->storage_path);
374
camel_object_unref (disco->diary);
378
g_static_rec_mutex_free (imap_store->idle_prefix_lock);
379
imap_store->idle_prefix_lock = NULL;
381
g_static_rec_mutex_free (imap_store->idle_lock);
382
imap_store->idle_lock = NULL;
384
g_static_rec_mutex_free (imap_store->sum_lock);
385
imap_store->sum_lock = NULL;
390
camel_imap_store_init (gpointer object, gpointer klass)
392
CamelImapStore *imap_store = CAMEL_IMAP_STORE (object);
394
imap_store->got_online = FALSE;
395
imap_store->going_online = FALSE;
396
imap_store->courier_crap = FALSE;
397
imap_store->idle_prefix_lock = g_new0 (GStaticRecMutex, 1);
398
g_static_rec_mutex_init (imap_store->idle_prefix_lock);
400
imap_store->idle_lock = g_new0 (GStaticRecMutex, 1);
401
g_static_rec_mutex_init (imap_store->idle_lock);
403
imap_store->sum_lock = g_new0 (GStaticRecMutex, 1);
404
g_static_rec_mutex_init (imap_store->sum_lock);
406
imap_store->dontdistridlehack = FALSE;
408
imap_store->idle_sleep = 20; /* default of 20s */
409
imap_store->getsrv_sleep = 100; /* default of 100s */
411
imap_store->in_idle = FALSE;
412
imap_store->idle_cont = FALSE;
413
imap_store->idle_thread = NULL;
414
imap_store->idle_prefix = NULL;
416
imap_store->istream = NULL;
417
imap_store->ostream = NULL;
418
imap_store->has_login = FALSE;
420
imap_store->dir_sep = '\0';
421
imap_store->current_folder = NULL;
422
imap_store->last_folder = NULL;
423
imap_store->connected = FALSE;
424
imap_store->preauthed = FALSE;
425
imap_store->clean_exit = TRUE;
427
imap_store->addrinfo = NULL;
428
((CamelStore *)imap_store)->flags |= CAMEL_STORE_SUBSCRIPTIONS;
430
imap_store->tag_prefix = imap_tag_prefix++;
431
if (imap_tag_prefix > 'Z')
432
imap_tag_prefix = 'A';
436
camel_imap_store_get_type (void)
438
static CamelType camel_imap_store_type = CAMEL_INVALID_TYPE;
440
if (camel_imap_store_type == CAMEL_INVALID_TYPE) {
441
camel_imap_store_type =
442
camel_type_register (CAMEL_DISCO_STORE_TYPE,
444
sizeof (CamelImapStore),
445
sizeof (CamelImapStoreClass),
446
(CamelObjectClassInitFunc) camel_imap_store_class_init,
448
(CamelObjectInitFunc) camel_imap_store_init,
449
(CamelObjectFinalizeFunc) camel_imap_store_finalize);
452
return camel_imap_store_type;
456
construct (CamelService *service, CamelSession *session,
457
CamelProvider *provider, CamelURL *url,
460
CamelImapStore *imap_store = CAMEL_IMAP_STORE (service);
461
CamelStore *store = CAMEL_STORE (service);
462
CamelDiscoStore *disco_store = CAMEL_DISCO_STORE (service);
464
CamelURL *summary_url;
466
CAMEL_SERVICE_CLASS (parent_class)->construct (service, session, provider, url, ex);
467
if (camel_exception_is_set (ex))
470
imap_store->storage_path = camel_session_get_storage_path (session, service, ex);
471
if (!imap_store->storage_path)
475
imap_store->base_url = camel_url_to_string (service->url, (CAMEL_URL_HIDE_PASSWORD |
476
CAMEL_URL_HIDE_PARAMS |
477
CAMEL_URL_HIDE_AUTH));
479
imap_store->parameters = 0;
480
if (camel_url_get_param (url, "use_lsub"))
481
imap_store->parameters |= IMAP_PARAM_SUBSCRIPTIONS;
482
if (camel_url_get_param (url, "override_namespace") && camel_url_get_param (url, "namespace")) {
483
imap_store->parameters |= IMAP_PARAM_OVERRIDE_NAMESPACE;
484
g_free(imap_store->namespace);
485
imap_store->namespace = g_strdup (camel_url_get_param (url, "namespace"));
487
if (camel_url_get_param (url, "check_all"))
488
imap_store->parameters |= IMAP_PARAM_CHECK_ALL;
489
if (camel_url_get_param (url, "filter")) {
490
imap_store->parameters |= IMAP_PARAM_FILTER_INBOX;
491
store->flags |= CAMEL_STORE_FILTER_INBOX;
493
if (camel_url_get_param (url, "filter_junk"))
494
imap_store->parameters |= IMAP_PARAM_FILTER_JUNK;
495
if (camel_url_get_param (url, "filter_junk_inbox"))
496
imap_store->parameters |= IMAP_PARAM_FILTER_JUNK_INBOX;
499
path = g_strdup_printf ("%s/journal", imap_store->storage_path);
500
disco_store->diary = camel_disco_diary_new (disco_store, path, ex);
503
/* setup/load the store summary */
504
tmp = alloca(strlen(imap_store->storage_path)+32);
505
sprintf(tmp, "%s/.ev-store-summary", imap_store->storage_path);
507
g_static_rec_mutex_lock (imap_store->sum_lock);
508
imap_store->summary = camel_imap_store_summary_new();
509
camel_store_summary_set_filename((CamelStoreSummary *)imap_store->summary, tmp);
510
summary_url = camel_url_new(imap_store->base_url, NULL);
511
camel_store_summary_set_uri_base((CamelStoreSummary *)imap_store->summary, summary_url);
512
camel_url_free(summary_url);
513
if (camel_store_summary_load((CamelStoreSummary *)imap_store->summary) == 0) {
514
CamelImapStoreSummary *is = imap_store->summary;
517
/* if namespace has changed, clear folder list */
518
if (imap_store->namespace && strcmp(imap_store->namespace, is->namespace->full_name) != 0) {
519
camel_store_summary_clear((CamelStoreSummary *)is);
521
imap_store->namespace = g_strdup(is->namespace->full_name);
522
imap_store->dir_sep = is->namespace->sep;
526
imap_store->capabilities = is->capabilities;
527
imap_set_server_level(imap_store);
529
g_static_rec_mutex_unlock (imap_store->sum_lock);
537
imap_setv (CamelObject *object, CamelException *ex, CamelArgV *args)
539
CamelImapStore *store = (CamelImapStore *) object;
543
for (i = 0; i < args->argc; i++) {
544
tag = args->argv[i].tag;
546
/* make sure this is an arg we're supposed to handle */
547
if ((tag & CAMEL_ARG_TAG) <= CAMEL_IMAP_STORE_ARG_FIRST ||
548
(tag & CAMEL_ARG_TAG) >= CAMEL_IMAP_STORE_ARG_FIRST + 100)
552
case CAMEL_IMAP_STORE_NAMESPACE:
553
if (strcmp (store->namespace, args->argv[i].ca_str) != 0) {
554
g_free (store->namespace);
555
store->namespace = g_strdup (args->argv[i].ca_str);
556
/* the current imap code will need to do a reconnect for this to take effect */
557
/*reconnect = TRUE;*/
560
case CAMEL_IMAP_STORE_OVERRIDE_NAMESPACE:
561
flags = args->argv[i].ca_int ? IMAP_PARAM_OVERRIDE_NAMESPACE : 0;
562
flags |= (store->parameters & ~IMAP_PARAM_OVERRIDE_NAMESPACE);
564
if (store->parameters != flags) {
565
store->parameters = flags;
566
/* the current imap code will need to do a reconnect for this to take effect */
567
/*reconnect = TRUE;*/
570
case CAMEL_IMAP_STORE_CHECK_ALL:
571
flags = args->argv[i].ca_int ? IMAP_PARAM_CHECK_ALL : 0;
572
flags |= (store->parameters & ~IMAP_PARAM_CHECK_ALL);
573
store->parameters = flags;
574
/* no need to reconnect for this option to take effect... */
576
case CAMEL_IMAP_STORE_FILTER_INBOX:
577
flags = args->argv[i].ca_int ? IMAP_PARAM_FILTER_INBOX : 0;
578
flags |= (store->parameters & ~IMAP_PARAM_FILTER_INBOX);
579
store->parameters = flags;
580
/* no need to reconnect for this option to take effect... */
582
case CAMEL_IMAP_STORE_FILTER_JUNK:
583
flags = args->argv[i].ca_int ? IMAP_PARAM_FILTER_JUNK : 0;
584
store->parameters = flags | (store->parameters & ~IMAP_PARAM_FILTER_JUNK);
586
case CAMEL_IMAP_STORE_FILTER_JUNK_INBOX:
587
flags = args->argv[i].ca_int ? IMAP_PARAM_FILTER_JUNK_INBOX : 0;
588
store->parameters = flags | (store->parameters & ~IMAP_PARAM_FILTER_JUNK_INBOX);
595
/* let our parent know that we've handled this arg */
596
camel_argv_ignore (args, i);
599
/* FIXME: if we need to reconnect for a change to take affect,
600
we need to do it here... or, better yet, somehow chain it
601
up to CamelService's setv implementation. */
603
return CAMEL_OBJECT_CLASS (parent_class)->setv (object, ex, args);
607
imap_getv (CamelObject *object, CamelException *ex, CamelArgGetV *args)
609
CamelImapStore *store = (CamelImapStore *) object;
613
for (i = 0; i < args->argc; i++) {
614
tag = args->argv[i].tag;
616
/* make sure this is an arg we're supposed to handle */
617
if ((tag & CAMEL_ARG_TAG) <= CAMEL_IMAP_STORE_ARG_FIRST ||
618
(tag & CAMEL_ARG_TAG) >= CAMEL_IMAP_STORE_ARG_FIRST + 100)
622
case CAMEL_IMAP_STORE_NAMESPACE:
623
*args->argv[i].ca_str = store->namespace;
625
case CAMEL_IMAP_STORE_OVERRIDE_NAMESPACE:
626
*args->argv[i].ca_int = store->parameters & IMAP_PARAM_OVERRIDE_NAMESPACE ? TRUE : FALSE;
628
case CAMEL_IMAP_STORE_CHECK_ALL:
629
*args->argv[i].ca_int = store->parameters & IMAP_PARAM_CHECK_ALL ? TRUE : FALSE;
631
case CAMEL_IMAP_STORE_FILTER_INBOX:
632
*args->argv[i].ca_int = store->parameters & IMAP_PARAM_FILTER_INBOX ? TRUE : FALSE;
634
case CAMEL_IMAP_STORE_FILTER_JUNK:
635
*args->argv[i].ca_int = store->parameters & IMAP_PARAM_FILTER_JUNK ? TRUE : FALSE;
637
case CAMEL_IMAP_STORE_FILTER_JUNK_INBOX:
638
*args->argv[i].ca_int = store->parameters & IMAP_PARAM_FILTER_JUNK_INBOX ? TRUE : FALSE;
646
return CAMEL_OBJECT_CLASS (parent_class)->getv (object, ex, args);
650
imap_get_name (CamelService *service, gboolean brief)
653
return g_strdup_printf (_("IMAP server %s"), service->url->host);
655
return g_strdup_printf (_("IMAP service for %s on %s"),
656
service->url->user, service->url->host);
660
imap_set_server_level (CamelImapStore *store)
662
if (store->capabilities & IMAP_CAPABILITY_IMAP4REV1) {
663
store->server_level = IMAP_LEVEL_IMAP4REV1;
664
store->capabilities |= IMAP_CAPABILITY_STATUS;
665
} else if (store->capabilities & IMAP_CAPABILITY_IMAP4)
666
store->server_level = IMAP_LEVEL_IMAP4;
668
store->server_level = IMAP_LEVEL_UNKNOWN;
675
{ "IMAP4", IMAP_CAPABILITY_IMAP4 },
676
{ "IMAP4REV1", IMAP_CAPABILITY_IMAP4REV1 },
677
{ "STATUS", IMAP_CAPABILITY_STATUS },
678
{ "NAMESPACE", IMAP_CAPABILITY_NAMESPACE },
679
{ "UIDPLUS", IMAP_CAPABILITY_UIDPLUS },
680
{ "LITERAL+", IMAP_CAPABILITY_LITERALPLUS },
681
{ "STARTTLS", IMAP_CAPABILITY_STARTTLS },
682
{ "XGWEXTENSIONS", IMAP_CAPABILITY_XGWEXTENSIONS },
683
{ "XGWMOVE", IMAP_CAPABILITY_XGWMOVE },
684
{ "LOGINDISABLED", IMAP_CAPABILITY_LOGINDISABLED },
685
{ "CONDSTORE", IMAP_CAPABILITY_CONDSTORE },
686
{ "IDLE", IMAP_CAPABILITY_IDLE },
687
{ "BINARY", IMAP_CAPABILITY_BINARY },
692
parse_capability(CamelImapStore *store, char *capa)
697
for (capa = strtok_r (capa, " ", &lasts); capa; capa = strtok_r (NULL, " ", &lasts)) {
698
if (!strncmp (capa, "AUTH=", 5)) {
699
g_hash_table_insert (store->authtypes,
701
GINT_TO_POINTER (1));
704
for (i = 0; capabilities[i].name; i++) {
705
if (g_ascii_strcasecmp (capa, capabilities[i].name) == 0) {
706
store->capabilities |= capabilities[i].flag;
714
imap_get_capability (CamelService *service, CamelException *ex)
716
CamelImapStore *store = CAMEL_IMAP_STORE (service);
717
CamelImapResponse *response;
720
/* Find out the IMAP capabilities */
721
/* We assume we have utf8 capable search until a failed search tells us otherwise */
722
store->capabilities = IMAP_CAPABILITY_utf8_search;
723
store->authtypes = g_hash_table_new (g_str_hash, g_str_equal);
724
response = camel_imap_command (store, NULL, ex, "CAPABILITY");
727
result = camel_imap_response_extract (store, response, "CAPABILITY ", ex);
731
/* Skip over "* CAPABILITY ". */
732
parse_capability(store, result+13);
735
/* dunno why the groupwise guys didn't just list this in capabilities */
736
if (store->capabilities & IMAP_CAPABILITY_XGWEXTENSIONS) {
737
/* not critical if this fails */
738
response = camel_imap_command (store, NULL, NULL, "XGWEXTENSIONS");
739
if (response && (result = camel_imap_response_extract (store, response, "XGWEXTENSIONS ", NULL))) {
740
parse_capability(store, result+16);
745
imap_set_server_level (store);
747
g_static_rec_mutex_lock (store->sum_lock);
748
if (store->summary->capabilities != store->capabilities) {
749
store->summary->capabilities = store->capabilities;
750
camel_store_summary_touch((CamelStoreSummary *)store->summary);
751
camel_store_summary_save((CamelStoreSummary *)store->summary, ex);
753
g_static_rec_mutex_unlock (store->sum_lock);
765
#define SSL_PORT_FLAGS (CAMEL_TCP_STREAM_SSL_ENABLE_SSL2 | CAMEL_TCP_STREAM_SSL_ENABLE_SSL3)
766
#define STARTTLS_FLAGS (CAMEL_TCP_STREAM_SSL_ENABLE_TLS)
771
camel_imap_store_restore_stream_buffer (CamelImapStore *store)
773
if (store->istream == NULL || !CAMEL_IS_STREAM_BUFFER (store->istream))
775
if (store->ostream && CAMEL_IS_STREAM (store->ostream))
777
/* This is a recoverable situation. It's strange though */
778
store->istream = camel_stream_buffer_new (store->ostream, CAMEL_STREAM_BUFFER_READ);
780
CamelException ex = CAMEL_EXCEPTION_INITIALISER;
781
camel_operation_uncancel (NULL);
782
camel_service_disconnect (CAMEL_SERVICE (store), FALSE, &ex);
783
camel_exception_clear (&ex);
784
g_warning ("Something terrible happened with your connection.\nTrying to recover. (%s)\n",
786
camel_service_connect (CAMEL_SERVICE (store), &ex);
787
if (camel_exception_is_set (&ex))
788
g_warning ("Connection recovery failed: %s",
789
camel_exception_get_description (&ex));
797
connect_to_server (CamelService *service, struct addrinfo *ai, int ssl_mode, int must_tls, CamelException *ex)
799
CamelImapStore *store = (CamelImapStore *) service;
800
CamelImapResponse *response;
801
CamelStream *tcp_stream;
802
CamelSockOptData sockopt;
803
gboolean force_imap4 = FALSE;
804
gboolean clean_quit = TRUE;
806
gboolean not_ssl = TRUE;
808
memset (&sockopt, 0, sizeof (CamelSockOptData));
810
if (ssl_mode != MODE_CLEAR)
815
if (ssl_mode == MODE_TLS)
816
tcp_stream = camel_tcp_stream_ssl_new_raw (service, service->url->host, STARTTLS_FLAGS);
818
tcp_stream = camel_tcp_stream_ssl_new (service, service->url->host, SSL_PORT_FLAGS);
821
camel_exception_setv (ex, CAMEL_EXCEPTION_SERVICE_UNAVAILABLE,
822
_("Could not connect to %s: %s"),
823
service->url->host, _("SSL unavailable in this build"));
827
#endif /* HAVE_SSL */
829
tcp_stream = camel_tcp_stream_raw_new ();
831
if (camel_tcp_stream_connect ((CamelTcpStream *) tcp_stream, ai) == -1)
834
camel_exception_set (ex, CAMEL_EXCEPTION_USER_CANCEL,
835
_("Connection cancelled"));
837
camel_exception_setv (ex, CAMEL_EXCEPTION_SERVICE_UNAVAILABLE,
838
_("Could not connect to %s: %s"),
841
camel_object_unref (tcp_stream);
845
store->ostream = tcp_stream;
846
store->istream = camel_stream_buffer_new (tcp_stream, CAMEL_STREAM_BUFFER_READ);
848
store->connected = TRUE;
849
store->preauthed = FALSE;
852
/* Disable Nagle - we send a lot of small requests which nagle slows down */
853
sockopt.option = CAMEL_SOCKOPT_NODELAY;
854
sockopt.value.no_delay = TRUE;
855
camel_tcp_stream_setsockopt((CamelTcpStream *)tcp_stream, &sockopt);
857
/* Set keepalive - needed for some hosts/router configurations, we're idle a lot */
858
sockopt.option = CAMEL_SOCKOPT_KEEPALIVE;
859
sockopt.value.keep_alive = TRUE;
860
camel_tcp_stream_setsockopt((CamelTcpStream *)tcp_stream, &sockopt);
862
/* Read the greeting, if any, and deal with PREAUTH */
863
if (camel_imap_store_readline (store, &buf, ex) < 0)
865
camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
866
_("Failed to connect to IMAP server %s"),
867
service->url->host, _("Reading IMAP greeting failed"));
871
if (!strncmp(buf, "* PREAUTH", 9))
872
store->preauthed = TRUE;
874
if (strstr (buf, "Courier-IMAP"))
875
store->courier_crap = TRUE;
877
if (store->courier_crap || getenv("CAMEL_IMAP_BRAINDAMAGED"))
879
/* Courier-IMAP is braindamaged. So far this flag only
880
* works around the fact that Courier-IMAP is known to
881
* give invalid BODY responses seemingly because its
882
* MIME parser sucks. In any event, we can't rely on
883
* them so we always have to request the full messages
884
* rather than getting individual parts. */
885
store->braindamaged = TRUE;
886
} else if (strstr (buf, "WEB.DE") || strstr (buf, "Mail2World"))
888
/* This is a workaround for servers which advertise
889
* IMAP4rev1 but which can sometimes subtly break in
890
* various ways if we try to use IMAP4rev1 queries.
892
* WEB.DE: when querying for HEADER.FIELDS.NOT, it
893
* returns an empty literal for the headers. Many
894
* complaints about empty message-list fields on the
895
* mailing lists and probably a few bugzilla bugs as
898
* Mail2World (aka NamePlanet): When requesting
899
* message info's, it ignores the fact that we
900
* requested BODY.PEEK[HEADER.FIELDS.NOT (RECEIVED)]
901
* and so the responses are incomplete. See bug #58766
907
/* Kolumbus requires part numbers in BINARY.PEEK, which is their mis -
908
* interpretation of BINARY imo, so we just ignore its BINARY support.
909
* The server also claims to support UIDPLUS, but doesn't */
911
/* Tinymail hack: always use IMAP4, not IMAP4rev1 (sorry) */
912
/* force_imap4 = TRUE; */
913
store->braindamaged = TRUE;
918
/* get the imap server capabilities */
919
if (!imap_get_capability (service, ex))
921
camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
922
_("Failed to connect to IMAP server %s"),
923
service->url->host, _("Reading first CAPABILITY failed"));
927
if (must_tls && !(store->capabilities & IMAP_CAPABILITY_STARTTLS))
929
camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
930
_("Failed to connect to IMAP server %s"),
931
service->url->host, _("STARTTLS not announced as supported on this server"));
937
store->capabilities &= ~IMAP_CAPABILITY_IMAP4REV1;
938
store->server_level = IMAP_LEVEL_IMAP4;
941
if (!must_tls && (not_ssl || ssl_mode != MODE_TLS))
950
if (!(store->capabilities & IMAP_CAPABILITY_STARTTLS))
952
camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
953
_("Failed to connect to IMAP server %s in secure mode: %s"),
954
service->url->host, _("STARTTLS not supported"));
958
/* as soon as we send a STARTTLS command, all hope is lost of a clean QUIT if problems arise */
961
response = camel_imap_command (store, NULL, ex, "STARTTLS");
964
camel_object_unref (store->istream);
965
camel_object_unref (store->ostream);
966
store->istream = NULL;
967
store->ostream = NULL;
969
camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
970
_("Failed to connect to IMAP server %s in secure mode: %s"),
971
service->url->host, _("STARTTLS not supported"));
975
camel_imap_response_free_without_processing (store, response);
977
/* Okay, now toggle SSL/TLS mode */
978
if (camel_tcp_stream_ssl_enable_ssl (CAMEL_TCP_STREAM_SSL (tcp_stream)) == -1)
980
camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
981
_("Failed to connect to IMAP server %s in secure mode: %s (%s)"),
982
service->url->host, _("SSL negotiations failed"), strerror (errno));
987
camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
988
_("Failed to connect to IMAP server %s in secure mode: %s"),
989
service->url->host, _("SSL is not available in this build"));
991
#endif /* HAVE_SSL */
994
/* rfc2595, section 4 states that after a successful STLS
995
command, the client MUST discard prior CAPA responses */
997
if (!imap_get_capability (service, ex))
999
camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
1000
_("Failed to connect to IMAP server %s (%s)"),
1001
service->url->host, _("Reading second CAPABILITY failed"));
1005
if (store->capabilities & IMAP_CAPABILITY_LOGINDISABLED ) {
1007
camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
1008
_("Failed to connect to IMAP server %s in secure mode: %s"),
1009
service->url->host, _("Unknown error"));
1017
if (clean_quit && store->connected)
1019
/* try to disconnect cleanly */
1020
response = camel_imap_command (store, NULL, ex, "LOGOUT");
1022
camel_imap_response_free_without_processing (store, response);
1025
if (store->istream) {
1026
camel_object_unref (store->istream);
1027
store->istream = NULL;
1029
if (store->ostream) {
1030
camel_object_unref (store->ostream);
1031
store->ostream = NULL;
1033
store->connected = FALSE;
1040
/* Using custom commands to connect to IMAP servers is not supported on Win32 */
1043
connect_to_server_process (CamelService *service, const char *cmd, CamelException *ex)
1045
CamelImapStore *store = (CamelImapStore *) service;
1046
CamelStream *cmd_stream;
1053
/* Put full details in the environment, in case the connection
1054
program needs them */
1055
buf = camel_url_to_string(service->url, 0);
1056
child_env[i++] = g_strdup_printf("URL=%s", buf);
1059
child_env[i++] = g_strdup_printf("URLHOST=%s", service->url->host);
1060
if (service->url->port)
1061
child_env[i++] = g_strdup_printf("URLPORT=%d", service->url->port);
1062
if (service->url->user)
1063
child_env[i++] = g_strdup_printf("URLUSER=%s", service->url->user);
1064
if (service->url->passwd)
1065
child_env[i++] = g_strdup_printf("URLPASSWD=%s", service->url->passwd);
1066
if (service->url->path)
1067
child_env[i++] = g_strdup_printf("URLPATH=%s", service->url->path);
1068
child_env[i] = NULL;
1070
/* Now do %h, %u, etc. substitution in cmd */
1071
buf = cmd_copy = g_strdup(cmd);
1073
full_cmd = g_strdup("");
1081
pc = strchr(buf, '%');
1084
tmp = g_strdup_printf("%s%s", full_cmd, buf);
1096
var = service->url->host;
1099
var = service->url->user;
1103
/* If there wasn't a valid %-code, with an actual
1104
variable to insert, pretend we didn't see the % */
1105
pc = strchr(pc + 1, '%');
1108
tmp = g_strdup_printf("%s%.*s%s", full_cmd, len, buf, var);
1116
cmd_stream = camel_stream_process_new ();
1118
ret = camel_stream_process_connect (CAMEL_STREAM_PROCESS(cmd_stream),
1119
full_cmd, (const char **)child_env);
1122
g_free(child_env[--i]);
1126
camel_exception_set (ex, CAMEL_EXCEPTION_USER_CANCEL,
1127
_("Connection cancelled"));
1129
camel_exception_setv (ex, CAMEL_EXCEPTION_SERVICE_UNAVAILABLE,
1130
_("Could not connect with command \"%s\": %s"),
1131
full_cmd, g_strerror (errno));
1133
camel_object_unref (cmd_stream);
1139
store->ostream = cmd_stream;
1140
store->istream = camel_stream_buffer_new (cmd_stream, CAMEL_STREAM_BUFFER_READ);
1142
store->connected = TRUE;
1143
store->preauthed = FALSE;
1146
/* Read the greeting, if any, and deal with PREAUTH */
1147
if (camel_imap_store_readline (store, &buf, ex) < 0)
1149
if (store->istream) {
1150
camel_object_unref (store->istream);
1151
store->istream = NULL;
1154
if (store->ostream) {
1155
camel_object_unref (store->ostream);
1156
store->ostream = NULL;
1159
store->connected = FALSE;
1163
if (!strncmp(buf, "* PREAUTH", 9))
1164
store->preauthed = TRUE;
1167
/* get the imap server capabilities */
1168
if (!imap_get_capability (service, ex))
1170
if (store->istream) {
1171
camel_object_unref (store->istream);
1172
store->istream = NULL;
1174
if (store->ostream) {
1175
camel_object_unref (store->ostream);
1176
store->ostream = NULL;
1179
store->connected = FALSE;
1196
{ "", "imaps", IMAPS_PORT, MODE_SSL, 0 }, /* really old (1.x) */
1197
{ "wrapped", "imaps", IMAPS_PORT, MODE_SSL, 0 },
1198
{ "tls", "imap", IMAPS_PORT, MODE_TLS, 1 },
1199
{ "when-possible", "imap", IMAP_PORT, MODE_TLS, 0 },
1200
{ "never", "imap", IMAP_PORT, MODE_CLEAR, 0 },
1201
{ NULL, "imap", IMAP_PORT, MODE_CLEAR, 0 },
1206
connect_to_server_wrapper (CamelService *service, CamelException *ex)
1208
const char *ssl_mode, *idle_sleep, *getsrv_sleep;
1209
struct addrinfo hints, *ai;
1210
int mode = -1, ret, i, must_tls = 0;
1213
CamelImapStore *store = (CamelImapStore *) service;
1216
const char *command;
1218
if (camel_url_get_param(service->url, "use_command")
1219
&& (command = camel_url_get_param(service->url, "command")))
1220
return connect_to_server_process(service, command, ex);
1224
camel_exception_clear (ex);
1226
if ((idle_sleep = camel_url_get_param (service->url, "idle_delay")))
1228
int tmp = atoi (idle_sleep);
1230
CAMEL_IMAP_STORE (service)->idle_sleep = tmp;
1233
if ((getsrv_sleep = camel_url_get_param (service->url, "getsrv_delay")))
1235
int tmp = atoi (getsrv_sleep);
1237
CAMEL_IMAP_STORE (service)->getsrv_sleep = tmp;
1240
if ((ssl_mode = camel_url_get_param (service->url, "use_ssl")))
1242
for (i = 0; ssl_options[i].value; i++)
1243
if (!strcmp (ssl_options[i].value, ssl_mode))
1245
mode = ssl_options[i].mode;
1246
serv = ssl_options[i].serv;
1247
port = ssl_options[i].port;
1248
must_tls = ssl_options[i].must_tls;
1257
if (service->url->port)
1259
serv = g_alloca (16);
1260
sprintf (serv, "%d", service->url->port);
1264
memset (&hints, 0, sizeof (hints));
1265
hints.ai_socktype = SOCK_STREAM;
1266
hints.ai_family = PF_UNSPEC;
1268
if (store->addrinfo == NULL)
1269
store->addrinfo = camel_getaddrinfo(service->url->host, serv, &hints, ex);
1271
if (store->addrinfo == NULL && port != NULL &&
1272
camel_exception_get_id(ex) != CAMEL_EXCEPTION_USER_CANCEL)
1274
camel_exception_clear (ex);
1275
store->addrinfo = camel_getaddrinfo(service->url->host, port, &hints, ex);
1278
if (store->addrinfo == NULL)
1281
ret = connect_to_server (service, store->addrinfo, mode, must_tls, ex);
1286
extern CamelServiceAuthType camel_imap_password_authtype;
1289
query_auth_types (CamelService *service, CamelException *ex)
1291
CamelImapStore *store = CAMEL_IMAP_STORE (service);
1292
CamelServiceAuthType *authtype;
1293
GList *sasl_types, *t, *next;
1296
if (!camel_disco_store_check_online (CAMEL_DISCO_STORE (store), ex))
1299
CAMEL_SERVICE_REC_LOCK (store, connect_lock);
1300
connected = store->istream != NULL && store->connected;
1302
connected = connect_to_server_wrapper (service, ex);
1303
CAMEL_SERVICE_REC_UNLOCK (store, connect_lock);
1308
sasl_types = camel_sasl_authtype_list (FALSE);
1309
for (t = sasl_types; t; t = next) {
1313
if (!g_hash_table_lookup (store->authtypes, authtype->authproto)) {
1314
sasl_types = g_list_remove_link (sasl_types, t);
1319
return g_list_prepend (sasl_types, &camel_imap_password_authtype);
1322
/* folder_name is path name */
1323
static CamelFolderInfo *
1324
imap_build_folder_info(CamelImapStore *imap_store, const char *folder_name)
1328
CamelFolderInfo *fi;
1330
fi = camel_folder_info_new ();
1331
fi->full_name = g_strdup(folder_name);
1334
url = camel_url_new (imap_store->base_url, NULL);
1336
url->path = g_strdup_printf ("/%s", folder_name);
1337
fi->uri = camel_url_to_string (url, CAMEL_URL_HIDE_ALL);
1338
camel_url_free(url);
1339
name = strrchr (fi->full_name, '/');
1341
name = fi->full_name;
1344
if (!g_ascii_strcasecmp (fi->full_name, "INBOX"))
1345
fi->name = g_strdup (_("Inbox"));
1347
fi->name = g_strdup (name);
1353
imap_folder_effectively_unsubscribed(CamelImapStore *imap_store,
1354
const char *folder_name, CamelException *ex)
1356
CamelFolderInfo *fi;
1359
g_static_rec_mutex_lock (imap_store->sum_lock);
1360
si = camel_store_summary_path((CamelStoreSummary *)imap_store->summary, folder_name);
1362
if (si->flags & CAMEL_STORE_INFO_FOLDER_SUBSCRIBED) {
1363
si->flags &= ~CAMEL_STORE_INFO_FOLDER_SUBSCRIBED;
1364
camel_store_summary_touch((CamelStoreSummary *)imap_store->summary);
1365
camel_store_summary_save((CamelStoreSummary *)imap_store->summary, ex);
1367
camel_store_summary_info_free((CamelStoreSummary *)imap_store->summary, si);
1369
g_static_rec_mutex_unlock (imap_store->sum_lock);
1371
if (imap_store->renaming) {
1372
/* we don't need to emit a "folder_unsubscribed" signal
1373
if we are in the process of renaming folders, so we
1379
fi = imap_build_folder_info(imap_store, folder_name);
1380
camel_object_trigger_event (CAMEL_OBJECT (imap_store), "folder_unsubscribed", fi);
1381
camel_folder_info_free (fi);
1385
imap_forget_folder (CamelImapStore *imap_store, const char *folder_name, CamelException *ex)
1387
CamelFolderSummary *summary;
1388
CamelImapMessageCache *cache;
1389
char *summary_file, *state_file;
1391
char *folder_dir, *storage_path;
1392
CamelFolderInfo *fi;
1395
name = strrchr (folder_name, imap_store->dir_sep);
1401
storage_path = g_strdup_printf ("%s/folders", imap_store->storage_path);
1402
folder_dir = imap_path_to_physical (storage_path, folder_name);
1403
g_free (storage_path);
1404
if (g_access (folder_dir, F_OK) != 0) {
1405
g_free (folder_dir);
1409
summary_file = g_strdup_printf ("%s/summary", folder_dir);
1410
summary = camel_imap_summary_new (NULL, summary_file);
1412
g_free (summary_file);
1413
g_free (folder_dir);
1417
cache = camel_imap_message_cache_new (folder_dir, summary, ex);
1419
camel_imap_message_cache_clear (cache);
1421
camel_object_unref (cache);
1422
camel_object_unref (summary);
1424
g_unlink (summary_file);
1425
g_free (summary_file);
1427
journal_file = g_strdup_printf ("%s/journal", folder_dir);
1428
g_unlink (journal_file);
1429
g_free (journal_file);
1431
state_file = g_strdup_printf ("%s/cmeta", folder_dir);
1432
g_unlink (state_file);
1433
g_free (state_file);
1435
state_file = g_strdup_printf("%s/subfolders", folder_dir);
1436
g_rmdir(state_file);
1439
g_rmdir (folder_dir);
1440
g_free (folder_dir);
1444
g_static_rec_mutex_lock (imap_store->sum_lock);
1445
camel_store_summary_remove_path((CamelStoreSummary *)imap_store->summary, folder_name);
1446
camel_store_summary_save((CamelStoreSummary *)imap_store->summary, ex);
1447
g_static_rec_mutex_unlock (imap_store->sum_lock);
1449
fi = imap_build_folder_info(imap_store, folder_name);
1450
camel_object_trigger_event (CAMEL_OBJECT (imap_store), "folder_deleted", fi);
1451
camel_folder_info_free (fi);
1455
imap_check_folder_still_extant (CamelImapStore *imap_store, const char *full_name,
1458
CamelImapResponse *response;
1460
response = camel_imap_command (imap_store, NULL, ex, "LIST \"\" %F",
1464
gboolean stillthere = response->untagged->len != 0;
1466
camel_imap_response_free_without_processing (imap_store, response);
1471
/* if the command was rejected, there must be some other error,
1472
assume it worked so we dont blow away the folder unecessarily */
1477
/* This is a little 'hack' to avoid the deadlock conditions that would otherwise
1478
ensue when calling camel_folder_refresh_info from inside a lock */
1479
/* NB: on second thougts this is probably not entirely safe, but it'll do for now */
1480
/* No, its definetly not safe. So its been changed to copy the folders first */
1481
/* the alternative is to:
1482
make the camel folder->lock recursive (which should probably be done)
1483
or remove it from camel_folder_refresh_info, and use another locking mechanism */
1484
/* also see get_folder_info_online() for the same hack repeated */
1486
imap_store_refresh_folders (CamelImapStore *store, CamelException *ex)
1491
folders = camel_object_bag_list(CAMEL_STORE (store)->folders);
1493
for (i = 0; i <folders->len; i++) {
1494
CamelFolder *folder = folders->pdata[i];
1496
/* NB: we can have vtrash folders also in our store ... bit hacky */
1497
if (!CAMEL_IS_IMAP_FOLDER(folder)) {
1498
camel_object_unref(folder);
1502
CAMEL_IMAP_FOLDER (folder)->need_rescan = TRUE;
1503
if (!camel_exception_is_set(ex))
1504
CAMEL_FOLDER_CLASS (CAMEL_OBJECT_GET_CLASS(folder))->refresh_info(folder, ex);
1506
if (camel_exception_is_set (ex) &&
1507
imap_check_folder_still_extant (store, folder->full_name, ex) == FALSE) {
1510
/* the folder was deleted (may happen when we come back online
1511
* after being offline */
1513
namedup = g_strdup (folder->full_name);
1514
camel_object_unref(folder);
1515
imap_folder_effectively_unsubscribed (store, namedup, ex);
1516
imap_forget_folder (store, namedup, ex);
1519
camel_object_unref(folder);
1522
g_ptr_array_free (folders, TRUE);
1527
try_auth (CamelImapStore *store, const char *mech, CamelException *ex)
1530
CamelImapResponse *response;
1534
response = camel_imap_command (store, NULL, ex, "AUTHENTICATE %s", mech);
1538
sasl = camel_sasl_new ("imap", mech, CAMEL_SERVICE (store));
1539
while (!camel_sasl_authenticated (sasl)) {
1540
resp = camel_imap_response_extract_continuation (store, response, ex);
1544
sasl_resp = camel_sasl_challenge_base64 (sasl, imap_next_word (resp), ex);
1546
if (!sasl_resp || camel_exception_is_set (ex))
1547
goto break_and_lose;
1549
response = camel_imap_command_continuation (store, sasl_resp, strlen (sasl_resp), ex);
1555
resp = camel_imap_response_extract_continuation (store, response, NULL);
1557
/* Oops. SASL claims we're done, but the IMAP server
1558
* doesn't think so...
1564
camel_object_unref (sasl);
1569
/* Get the server out of "waiting for continuation data" mode. */
1570
response = camel_imap_command_continuation (store, "*", 1, NULL);
1572
camel_imap_response_free (store, response);
1575
if (!camel_exception_is_set (ex)) {
1576
camel_exception_set (ex, CAMEL_EXCEPTION_SERVICE_CANT_AUTHENTICATE,
1577
_("Bad authentication response from server."));
1580
camel_object_unref (sasl);
1586
imap_auth_loop (CamelService *service, CamelException *ex)
1588
CamelImapStore *store = CAMEL_IMAP_STORE (service);
1589
CamelSession *session = camel_service_get_session (service);
1590
CamelServiceAuthType *authtype = NULL;
1591
CamelImapResponse *response;
1592
char *errbuf = NULL;
1593
gboolean authenticated = FALSE;
1594
const char *auth_domain;
1596
/* Bugfix for #432234 */
1597
if (store->capabilities & IMAP_CAPABILITY_LOGINDISABLED)
1600
auth_domain = camel_url_get_param (service->url, "auth-domain");
1602
if (store->preauthed)
1604
if (camel_verbose_debug)
1605
fprintf(stderr, "Server %s has preauthenticated us.\n",
1606
service->url->host);
1610
if (service->url->authmech)
1612
if (!g_hash_table_lookup (store->authtypes, service->url->authmech)) {
1613
camel_exception_setv (ex, CAMEL_EXCEPTION_SERVICE_CANT_AUTHENTICATE,
1614
_("IMAP server %s does not support requested "
1615
"authentication type %s"),
1617
service->url->authmech);
1621
authtype = camel_sasl_authtype (service->url->authmech);
1623
camel_exception_setv (ex, CAMEL_EXCEPTION_SERVICE_CANT_AUTHENTICATE,
1624
_("No support for authentication type %s"),
1625
service->url->authmech);
1629
if (!authtype->need_password)
1631
authenticated = try_auth (store, authtype->authproto, ex);
1637
while (!authenticated)
1641
/* We need to un-cache the password before prompting again */
1642
camel_session_forget_password (session, service, auth_domain, "password", ex);
1643
g_free (service->url->passwd);
1644
service->url->passwd = NULL;
1647
if (!service->url->passwd)
1651
prompt = g_strdup_printf (_("%sPlease enter the IMAP "
1652
"password for %s@%s"),
1653
errbuf ? errbuf : "",
1655
service->url->host);
1656
service->url->passwd =
1657
camel_session_get_password (session, service, auth_domain,
1658
prompt, "password", CAMEL_SESSION_PASSWORD_SECRET, ex);
1663
if (!service->url->passwd) {
1664
camel_exception_set (ex, CAMEL_EXCEPTION_USER_CANCEL,
1665
_("You did not enter a password."));
1670
if (!store->connected)
1672
/* Some servers (eg, courier) will disconnect on
1673
* a bad password. So reconnect here. */
1674
if (!connect_to_server_wrapper (service, ex))
1679
authenticated = try_auth (store, authtype->authproto, ex);
1682
if (!service->url->passwd)
1684
camel_exception_set (ex, CAMEL_EXCEPTION_USER_CANCEL,
1685
_("You did not enter a password."));
1689
response = camel_imap_command (store, NULL, ex,
1692
service->url->passwd);
1694
camel_imap_response_free (store, response);
1695
authenticated = TRUE;
1698
if (!authenticated) {
1699
if (camel_exception_get_id(ex) == CAMEL_EXCEPTION_USER_CANCEL)
1702
errbuf = g_strdup_printf (_("Unable to authenticate "
1703
"to IMAP server.\n%s\n\n"),
1704
camel_exception_get_description (ex));
1705
camel_exception_clear (ex);
1708
camel_service_disconnect (service, FALSE, NULL);
1711
if (!imap_get_capability (service, ex))
1713
errbuf = g_strdup_printf (_("Unable to authenticate "
1714
"to IMAP server.\n%s\n\n"),
1715
camel_exception_get_description (ex));
1716
camel_exception_clear (ex);
1725
can_work_offline (CamelDiscoStore *disco_store)
1727
CamelImapStore *store = CAMEL_IMAP_STORE (disco_store);
1729
return camel_store_summary_count((CamelStoreSummary *)store->summary) != 0;
1733
imap_connect_online (CamelService *service, CamelException *ex)
1735
CamelImapStore *store = CAMEL_IMAP_STORE (service);
1736
CamelImapResponse *response;
1737
/*struct _namespaces *namespaces;*/
1738
char *result, *name;
1740
CamelImapStoreNamespace *ns;
1742
store->going_online = TRUE;
1743
store->got_online = FALSE;
1745
camel_operation_uncancel (NULL);
1748
imap_debug ("imap_connect_online\n");
1750
let_idle_die (store, TRUE);
1752
CAMEL_SERVICE_REC_LOCK (store, connect_lock);
1754
if (!connect_to_server_wrapper (service, ex) || !imap_auth_loop (service, ex))
1756
/* CAMEL_DISCO_STORE (store)->status = CAMEL_DISCO_STORE_OFFLINE; */
1757
CAMEL_SERVICE_REC_UNLOCK (store, connect_lock);
1758
/* camel_service_disconnect (service, TRUE, NULL); */
1759
store->going_online = FALSE;
1763
/* Get namespace and hierarchy separator */
1764
if ((store->capabilities & IMAP_CAPABILITY_NAMESPACE) &&
1765
!(store->parameters & IMAP_PARAM_OVERRIDE_NAMESPACE))
1767
response = camel_imap_command (store, NULL, ex, "NAMESPACE");
1771
result = camel_imap_response_extract (store, response, "NAMESPACE", ex);
1777
namespaces = imap_parse_namespace_response (result);
1778
imap_namespaces_destroy (namespaces);
1782
name = camel_strstrcase (result, "NAMESPACE ((");
1787
store->namespace = imap_parse_string ((const char **) &name, &len);
1788
if (name && *name++ == ' ') {
1789
sep = imap_parse_string ((const char **) &name, &len);
1791
store->dir_sep = *sep;
1799
if (store->namespace && strlen (store->namespace) == 0) {
1800
g_free (store->namespace);
1801
store->namespace = NULL;
1804
if (store->namespace && !store->dir_sep) {
1805
if (FALSE && store->server_level >= IMAP_LEVEL_IMAP4REV1)
1807
/* This idiom means "tell me the hierarchy separator
1808
* for the given path, even if that path doesn't exist.
1810
response = camel_imap_command (store, NULL, ex,
1814
/* Plain IMAP4 doesn't have that idiom, so we fall back
1815
* to "tell me about this folder", which will fail if
1816
* the folder doesn't exist (eg, if namespace is "").
1818
response = camel_imap_command (store, NULL, ex,
1826
result = camel_imap_response_extract (store, response, "LIST", NULL);
1829
imap_parse_list_response (store, result, NULL, &store->dir_sep, NULL);
1834
if (!store->dir_sep)
1835
store->dir_sep = '/'; /* Guess */
1837
if (!store->namespace)
1838
store->namespace = g_strdup ("");
1840
/* canonicalize the namespace to end with dir_sep */
1841
len = strlen (store->namespace);
1842
if (len && store->namespace[len - 1] != store->dir_sep)
1844
gchar *tmp = g_strdup_printf ("%s%c", store->namespace, store->dir_sep);
1845
g_free (store->namespace);
1846
store->namespace = tmp;
1849
g_static_rec_mutex_lock (store->sum_lock);
1851
ns = camel_imap_store_summary_namespace_new(store->summary, store->namespace, store->dir_sep);
1852
camel_imap_store_summary_namespace_set(store->summary, ns);
1854
if ((store->parameters & IMAP_PARAM_SUBSCRIPTIONS)
1855
&& camel_store_summary_count((CamelStoreSummary *)store->summary) == 0)
1860
get_folders_sync(store, store->namespace, ex);
1861
if (camel_exception_is_set(ex))
1863
pattern = imap_concat(store, store->namespace, "*");
1864
get_folders_sync(store, pattern, ex);
1866
if (camel_exception_is_set(ex))
1869
/* Make sure INBOX is present/subscribed */
1870
si = camel_store_summary_path((CamelStoreSummary *)store->summary, "INBOX");
1872
if (si == NULL || (si->flags & CAMEL_FOLDER_SUBSCRIBED) == 0)
1874
response = camel_imap_command (store, NULL, ex, "SUBSCRIBE INBOX");
1875
if (response != NULL) {
1876
camel_imap_response_free (store, response);
1879
camel_store_summary_info_free((CamelStoreSummary *)store->summary, si);
1880
if (camel_exception_is_set(ex))
1882
get_folders_sync(store, "INBOX", ex);
1885
store->refresh_stamp = time(0);
1890
/* save any changes we had */
1891
camel_store_summary_save((CamelStoreSummary *)store->summary, ex);
1893
g_static_rec_mutex_unlock (store->sum_lock);
1895
CAMEL_SERVICE_REC_UNLOCK (store, connect_lock);
1896
if (camel_exception_is_set (ex))
1897
camel_service_disconnect (service, TRUE, NULL);
1899
store->has_login = TRUE;
1901
store->going_online = FALSE;
1902
store->got_online = !camel_exception_is_set (ex);
1904
return store->got_online;
1908
imap_connect_offline (CamelService *service, CamelException *ex)
1910
CamelImapStore *store = CAMEL_IMAP_STORE (service);
1911
CamelDiscoStore *disco_store = CAMEL_DISCO_STORE (service);
1913
imap_debug ("imap_connect_offline\n");
1915
/* let_idle_die (store, TRUE); */
1917
if (!disco_store->diary)
1920
store->connected = !camel_exception_is_set (ex);
1922
return store->connected;
1926
imap_disconnect_offline (CamelService *service, gboolean clean, CamelException *ex)
1928
CamelImapStore *store = CAMEL_IMAP_STORE (service);
1930
imap_debug ("imap_disconnect_offline\n");
1932
let_idle_die (store, TRUE);
1934
if (store->istream) {
1935
camel_stream_close(store->istream);
1936
camel_object_unref(store->istream);
1937
store->istream = NULL;
1940
if (store->ostream) {
1941
camel_stream_close(store->ostream);
1942
camel_object_unref(store->ostream);
1943
store->ostream = NULL;
1946
store->connected = FALSE;
1947
/* if (store->current_folder && CAMEL_IS_OBJECT (store->current_folder))
1948
camel_object_unref (store->current_folder); */
1949
store->current_folder = NULL;
1951
if (store->authtypes) {
1952
g_hash_table_foreach_remove (store->authtypes,
1954
g_hash_table_destroy (store->authtypes);
1955
store->authtypes = NULL;
1958
if (store->namespace && !(store->parameters & IMAP_PARAM_OVERRIDE_NAMESPACE)) {
1959
g_free (store->namespace);
1960
store->namespace = NULL;
1967
imap_disconnect_online (CamelService *service, gboolean clean, CamelException *ex)
1969
CamelImapStore *store = CAMEL_IMAP_STORE (service);
1970
CamelImapResponse *response;
1972
imap_debug ("imap_disconnect_online\n");
1974
let_idle_die (store, TRUE);
1976
if (store->connected && clean) {
1977
response = camel_imap_command (store, NULL, NULL, "LOGOUT");
1978
camel_imap_response_free (store, response);
1981
imap_disconnect_offline (service, clean, ex);
1988
imap_summary_is_dirty (CamelFolderSummary *summary)
1990
CamelImapMessageInfo *info;
1994
max = camel_folder_summary_count (summary);
1995
for (i = 0; i < max && !found; i++) {
1996
info = (CamelImapMessageInfo *)camel_folder_summary_index (summary, i);
1998
found = info->info.flags & CAMEL_MESSAGE_FOLDER_FLAGGED;
1999
camel_message_info_free(info);
2007
imap_noop (CamelStore *store, CamelException *ex)
2009
CamelImapStore *imap_store = (CamelImapStore *) store;
2010
CamelImapResponse *response;
2011
CamelFolder *current_folder;
2013
CAMEL_SERVICE_REC_LOCK (imap_store, connect_lock);
2015
if (!camel_disco_store_check_online((CamelDiscoStore *)store, ex))
2018
current_folder = imap_store->current_folder;
2019
if (current_folder && CAMEL_IS_IMAP_FOLDER (current_folder) && imap_summary_is_dirty (current_folder->summary)) {
2020
/* let's sync the flags instead. NB: must avoid folder lock */
2021
((CamelFolderClass *)((CamelObject *)current_folder)->klass)->sync(current_folder, FALSE, ex);
2023
response = camel_imap_command (imap_store, NULL, ex, "NOOP");
2025
camel_imap_response_free (imap_store, response);
2028
CAMEL_SERVICE_REC_UNLOCK (imap_store, connect_lock);
2031
static CamelFolder *
2032
imap_get_trash(CamelStore *store, CamelException *ex)
2034
CamelFolder *folder = CAMEL_STORE_CLASS(parent_class)->get_trash(store, ex);
2037
char *state = g_build_filename(((CamelImapStore *)store)->storage_path, "system", "Trash.cmeta", NULL);
2039
camel_object_set(folder, NULL, CAMEL_OBJECT_STATE_FILE, state, NULL);
2042
camel_object_state_read(folder);
2048
static CamelFolder *
2049
imap_get_junk(CamelStore *store, CamelException *ex)
2051
CamelFolder *folder = CAMEL_STORE_CLASS(parent_class)->get_junk(store, ex);
2054
char *state = g_build_filename(((CamelImapStore *)store)->storage_path, "system", "Junk.cmeta", NULL);
2056
camel_object_set(folder, NULL, CAMEL_OBJECT_STATE_FILE, state, NULL);
2059
camel_object_state_read(folder);
2066
hash_folder_name (gconstpointer key)
2068
if (g_ascii_strcasecmp (key, "INBOX") == 0)
2069
return g_str_hash ("INBOX");
2071
return g_str_hash (key);
2075
compare_folder_name (gconstpointer a, gconstpointer b)
2077
gconstpointer aname = a, bname = b;
2079
if (g_ascii_strcasecmp (a, "INBOX") == 0)
2081
if (g_ascii_strcasecmp (b, "INBOX") == 0)
2083
return g_str_equal (aname, bname);
2086
struct imap_status_item {
2087
struct imap_status_item *next;
2093
imap_status_item_free (struct imap_status_item *items)
2095
struct imap_status_item *next;
2097
while (items != NULL) {
2099
g_free (items->name);
2105
static struct imap_status_item *
2106
get_folder_status (CamelImapStore *imap_store, const char *folder_name, const char *type, gboolean err_handle_on_fail)
2108
struct imap_status_item *items, *item, *tail;
2109
CamelImapResponse *response;
2110
char *status, *name, *p;
2111
CamelException ex = CAMEL_EXCEPTION_INITIALISER;
2113
if (!(imap_store->capabilities & IMAP_CAPABILITY_STATUS))
2116
response = camel_imap_command (imap_store, NULL, &ex,
2122
if (err_handle_on_fail) {
2125
camel_exception_init (&ex);
2126
if (imap_check_folder_still_extant (imap_store, folder_name, &ex) == FALSE) {
2127
imap_folder_effectively_unsubscribed (imap_store, folder_name, &ex);
2128
imap_forget_folder (imap_store, folder_name, &ex);
2130
camel_exception_clear (&ex);
2135
if (!(status = camel_imap_response_extract (imap_store, response, "STATUS", NULL)))
2138
p = status + strlen ("* STATUS ");
2142
/* skip past the mailbox string */
2145
while (*p != '\0') {
2146
if (*p == '"' && p[-1] != '\\') {
2175
tail = (struct imap_status_item *) &items;
2182
item = g_malloc (sizeof (struct imap_status_item));
2184
item->name = g_strndup (name, p - name);
2185
item->value = strtoul (p, &p, 10);
2192
} while (*p != ')');
2196
camel_imap_store_start_idle (imap_store);
2202
camel_imap_store_set_status_for (CamelImapStore *imap_store, const char *folder_name, guint32 messages, guint32 unseen, guint32 uidnext)
2204
char *storage_path, *folder_dir, *filename;
2207
storage_path = g_strdup_printf("%s/folders", imap_store->storage_path);
2208
folder_dir = imap_path_to_physical (storage_path, folder_name);
2209
g_free(storage_path);
2210
filename = g_strdup_printf ("%s/status", folder_dir);
2211
g_free (folder_dir);
2213
file = fopen (filename, "w");
2218
fprintf (file, "%d %d %d", messages, unseen, uidnext);
2226
camel_imap_store_get_status_for (CamelImapStore *imap_store, const char *folder_name, guint32 *messages, guint32 *unseen, guint32 *uidnext)
2228
char *storage_path, *folder_dir, *filename;
2230
int nmessages, nunseen, nuidnext;
2232
storage_path = g_strdup_printf("%s/folders", imap_store->storage_path);
2233
folder_dir = imap_path_to_physical (storage_path, folder_name);
2234
g_free(storage_path);
2235
filename = g_strdup_printf ("%s/status", folder_dir);
2236
g_free (folder_dir);
2238
file = fopen (filename, "r");
2243
fscanf (file, "%i %i %i", &nmessages, &nunseen, &nuidnext);
2244
*messages = nmessages;
2246
*uidnext = nuidnext;
2253
static CamelMessageInfo*
2254
parse_message_header (char *response)
2256
CamelMessageInfoBase *mi = NULL;
2257
char *uid = NULL, *idate = NULL;
2258
size_t body_len = 0;
2259
guint32 flags = 0, size = 0;
2261
if (*response != '(') {
2264
if (*response != '*' || *(response + 1) != ' ')
2266
seq = strtol (response + 2, &response, 10);
2269
if (g_ascii_strncasecmp (response, " FETCH (", 8) != 0)
2276
if (!g_ascii_strncasecmp (response, "FLAGS ", 6)) {
2278
flags = imap_parse_flag_list (&response);
2279
} else if (!g_ascii_strncasecmp (response, "RFC822.SIZE ", 12)) {
2281
size = strtoul (response, &response, 10);
2282
} else if (!g_ascii_strncasecmp (response, "BODY[", 5) ||
2283
!g_ascii_strncasecmp (response, "RFC822 ", 7))
2286
if (*response == 'B') {
2288
p = strchr (response, ']');
2289
if (!p || *(p + 1) != ' ')
2295
body = imap_parse_nstring ((const char **) &response, &body_len);
2300
CamelMimeMessage *msg = camel_mime_message_new ();
2301
CamelStream *stream = camel_stream_mem_new_with_buffer (body, body_len);
2304
if (camel_data_wrapper_construct_from_stream (CAMEL_DATA_WRAPPER (msg), stream) == -1) {
2305
camel_object_unref (CAMEL_OBJECT (msg));
2309
mi = (CamelMessageInfoBase *) camel_folder_summary_info_new_from_message (NULL, msg);
2310
camel_object_unref (CAMEL_OBJECT (msg));
2312
} else if (!g_ascii_strncasecmp (response, "UID ", 4)) {
2313
int len = strcspn (response + 4, " )");
2314
uid = g_strndup (response + 4, len);
2315
response += 4 + len;
2316
} else if (!g_ascii_strncasecmp (response, "INTERNALDATE ", 13)) {
2317
int len; response += 13;
2318
if (*response == '"') {
2320
len = strcspn (response, "\"");
2321
idate = g_strndup (response, len);
2322
response += len + 1;
2325
g_warning ("Unexpected FETCH response from server: (%s", response);
2328
} while (response && *response != ')');
2339
mi->date_received = decode_internaldate ((const unsigned char *) idate);
2345
return (CamelMessageInfo *) mi;
2349
_camel_imap_store_get_recent_messages (CamelImapStore *imap_store, const char *folder_name, int *unseen, int *messages, gboolean withthem)
2352
struct imap_status_item *items, *item;
2353
guint ounseen, omessages, ouidnext;
2354
GPtrArray *retval = NULL;
2355
CamelException ex = CAMEL_EXCEPTION_INITIALISER;
2356
CamelImapResponse *response;
2357
CamelException tex = CAMEL_EXCEPTION_INITIALISER;
2359
if (!camel_disco_store_check_online (CAMEL_DISCO_STORE (imap_store), &ex))
2363
Example: C: A042 STATUS blurdybloop (UIDNEXT MESSAGES)
2364
S: * STATUS blurdybloop (MESSAGES 231 UIDNEXT 44292)
2365
S: A042 OK STATUS completed
2368
camel_operation_uncancel (NULL);
2371
/* On for example courier, the selected's STATUS is cached (kill that cache) */
2374
response = camel_imap_command (imap_store, NULL, &tex,
2375
"SELECT"/*, folder_name*/);
2377
printf ("SELECT %s\n", folder_name);
2380
camel_imap_response_free (imap_store, response);
2383
item = items = get_folder_status (imap_store, folder_name, "MESSAGES UNSEEN UIDNEXT", FALSE);
2384
while (item != NULL) {
2385
if (!g_ascii_strcasecmp (item->name, "MESSAGES"))
2386
*messages = item->value;
2387
if (!g_ascii_strcasecmp (item->name, "UNSEEN"))
2388
*unseen = item->value;
2389
if (!g_ascii_strcasecmp (item->name, "UIDNEXT"))
2390
uidnext = item->value;
2393
imap_status_item_free (items);
2394
printf ("%d %d %d\n", *messages, *unseen, uidnext);
2398
camel_imap_store_get_status_for (imap_store, folder_name, &omessages, &ounseen, &ouidnext);
2400
if (ouidnext != uidnext)
2402
CamelImapResponseType type;
2405
camel_exception_clear (&tex);
2407
if (!camel_imap_command_start (imap_store, NULL, &tex,
2408
"UID FETCH %d:* (FLAGS RFC822.SIZE INTERNALDATE BODY.PEEK[HEADER])", ouidnext-1))
2411
while ((type = camel_imap_command_response (imap_store, &resp, &tex))
2412
== CAMEL_IMAP_RESPONSE_UNTAGGED)
2416
CamelMessageInfo *mi = parse_message_header (resp);
2422
retval = g_ptr_array_new ();
2423
g_ptr_array_add (retval, mi);
2428
/* Restore the original folder selection */
2429
if (imap_store->current_folder != NULL && imap_store->current_folder->full_name != NULL)
2431
response = camel_imap_command (imap_store, NULL, &tex,
2432
"SELECT %F", imap_store->current_folder->full_name);
2434
camel_imap_response_free (imap_store, response);
2441
camel_imap_store_set_status_for (imap_store, folder_name, *messages, *unseen, uidnext);
2447
imap_get_folder_status (CamelStore *store, const char *folder_name, int *unseen, int *messages, int *uidnext)
2449
CamelImapStore *imap_store = CAMEL_IMAP_STORE (store);
2450
struct imap_status_item *items, *item;
2451
CamelException ex = CAMEL_EXCEPTION_INITIALISER;
2452
char *storage_path = g_strdup_printf("%s/folders", imap_store->storage_path);
2453
char *folder_dir = imap_path_to_physical (storage_path, folder_name);
2454
gchar *spath = g_strdup_printf ("%s/summary.mmap", folder_dir);
2455
guint32 mversion=-1; guint32 mflags=-1;
2456
guint32 mnextuid=-1; time_t mtime=-1; guint32 msaved_count=-1;
2457
guint32 munread_count=-1; guint32 mdeleted_count=-1;
2458
guint32 mjunk_count=-1;
2460
camel_file_util_read_counts_2 (spath, &mversion, &mflags, &mnextuid,
2461
&mtime, &msaved_count, &munread_count, &mdeleted_count,
2464
if (munread_count != -1)
2465
*unseen = munread_count;
2466
if (msaved_count != -1)
2467
*messages = msaved_count;
2469
*uidnext = mnextuid;
2472
g_free (storage_path);
2473
g_free (folder_dir);
2475
if (!camel_disco_store_check_online (CAMEL_DISCO_STORE (imap_store), &ex))
2478
CAMEL_SERVICE_REC_LOCK (imap_store, connect_lock);
2481
* Example: C: A042 STATUS blurdybloop (UIDNEXT MESSAGES)
2482
* S: * STATUS blurdybloop (MESSAGES 231 UIDNEXT 44292)
2483
* S: A042 OK STATUS completed */
2485
item = items = get_folder_status (imap_store, folder_name, "MESSAGES UNSEEN UIDNEXT", FALSE);
2487
if (item == NULL) /* Fallback situation for bizare servers */ {
2488
item = items = get_folder_status (imap_store, folder_name, "MESSAGES", FALSE);
2489
if (munread_count == -1)
2495
while (item != NULL) {
2496
if (!g_ascii_strcasecmp (item->name, "MESSAGES"))
2497
*messages = item->value;
2498
if (!g_ascii_strcasecmp (item->name, "UNSEEN")) {
2499
if (munread_count == -1)
2500
*unseen = item->value;
2502
if (!g_ascii_strcasecmp (item->name, "UIDNEXT"))
2503
*uidnext = item->value;
2506
imap_status_item_free (items);
2508
CAMEL_SERVICE_REC_UNLOCK (imap_store, connect_lock);
2513
static CamelFolder *
2514
get_folder_online (CamelStore *store, const char *folder_name, guint32 flags, CamelException *ex)
2516
CamelImapStore *imap_store = CAMEL_IMAP_STORE (store);
2517
CamelImapResponse *response;
2518
CamelFolder *new_folder;
2519
char *folder_dir, *storage_path;
2521
/* Try to get it locally first, if it is, then the client will
2522
force a select when necessary */
2523
new_folder = get_folder_offline(store, folder_name, flags, ex);
2526
camel_exception_clear(ex);
2528
CAMEL_SERVICE_REC_LOCK(imap_store, connect_lock);
2530
if (!camel_disco_store_check_online((CamelDiscoStore *)imap_store, ex)) {
2531
CAMEL_SERVICE_REC_UNLOCK (imap_store, connect_lock);
2535
if (!g_ascii_strcasecmp (folder_name, "INBOX"))
2536
folder_name = "INBOX";
2538
if (imap_store->current_folder) {
2539
/* camel_object_unref (imap_store->current_folder); */
2540
imap_store->current_folder = NULL;
2543
response = camel_imap_command (imap_store, NULL, ex, "SELECT %F", folder_name);
2545
char *folder_real, *parent_name, *parent_real;
2548
if (camel_exception_get_id(ex) == CAMEL_EXCEPTION_USER_CANCEL) {
2549
CAMEL_SERVICE_REC_UNLOCK (imap_store, connect_lock);
2553
camel_exception_clear (ex);
2555
if (!(flags & CAMEL_STORE_FOLDER_CREATE)) {
2556
CAMEL_SERVICE_REC_UNLOCK (imap_store, connect_lock);
2557
camel_exception_setv (ex, CAMEL_EXCEPTION_STORE_NO_FOLDER,
2558
_("No such folder %s"), folder_name);
2562
parent_name = strrchr(folder_name, '/');
2563
c = parent_name ? parent_name+1 : folder_name;
2564
while (*c && *c != imap_store->dir_sep && !strchr ("#%*", *c))
2568
CAMEL_SERVICE_REC_UNLOCK (imap_store, connect_lock);
2569
camel_exception_setv (ex, CAMEL_EXCEPTION_FOLDER_INVALID_PATH,
2570
_("The folder name \"%s\" is invalid because it contains the character \"%c\""),
2576
parent_name = g_strndup (folder_name, parent_name - folder_name);
2577
parent_real = camel_imap_store_summary_path_to_full (imap_store->summary, parent_name, imap_store->dir_sep);
2582
if (parent_real != NULL) {
2583
gboolean need_convert = FALSE;
2584
char *resp, *thisone;
2588
if (!(response = camel_imap_command (imap_store, NULL, ex, "LIST \"\" %G", parent_real))) {
2589
CAMEL_SERVICE_REC_UNLOCK (imap_store, connect_lock);
2590
g_free (parent_name);
2591
g_free (parent_real);
2595
/* FIXME: does not handle unexpected circumstances very well */
2596
for (i = 0; i < response->untagged->len; i++) {
2597
resp = response->untagged->pdata[i];
2599
if (!imap_parse_list_response (imap_store, resp, (int *) &flags, NULL, &thisone))
2602
if (!strcmp (parent_name, thisone)) {
2603
if (flags & CAMEL_FOLDER_NOINFERIORS)
2604
need_convert = TRUE;
2610
camel_imap_response_free (imap_store, response);
2612
/* if not, check if we can delete it and recreate it */
2614
struct imap_status_item *items, *item;
2615
guint32 messages = 0;
2619
item = items = get_folder_status (imap_store, parent_name, "MESSAGES", TRUE);
2620
while (item != NULL) {
2621
if (!g_ascii_strcasecmp (item->name, "MESSAGES")) {
2622
messages = item->value;
2629
imap_status_item_free (items);
2632
camel_exception_set (ex, CAMEL_EXCEPTION_FOLDER_INVALID_STATE,
2633
_("The parent folder is not allowed to contain subfolders"));
2634
CAMEL_SERVICE_REC_UNLOCK (imap_store, connect_lock);
2635
g_free (parent_name);
2636
g_free (parent_real);
2640
/* delete the old parent and recreate it */
2641
camel_exception_init (&lex);
2642
delete_folder (store, parent_name, &lex);
2643
if (camel_exception_is_set (&lex)) {
2644
CAMEL_SERVICE_REC_UNLOCK (imap_store, connect_lock);
2645
camel_exception_xfer (ex, &lex);
2646
g_free (parent_name);
2647
g_free (parent_real);
2651
/* add the dirsep to the end of parent_name */
2652
name = g_strdup_printf ("%s%c", parent_real, imap_store->dir_sep);
2653
response = camel_imap_command (imap_store, NULL, ex, "CREATE %G",
2658
CAMEL_SERVICE_REC_UNLOCK (imap_store, connect_lock);
2659
g_free (parent_name);
2660
g_free (parent_real);
2663
camel_imap_response_free (imap_store, response);
2666
g_free (parent_real);
2669
g_free (parent_name);
2671
folder_real = camel_imap_store_summary_path_to_full(imap_store->summary, folder_name, imap_store->dir_sep);
2672
response = camel_imap_command (imap_store, NULL, ex, "CREATE %G", folder_real);
2674
camel_imap_store_summary_add_from_full(imap_store->summary, folder_real, imap_store->dir_sep);
2676
camel_imap_response_free (imap_store, response);
2678
response = camel_imap_command (imap_store, NULL, NULL, "SELECT %F", folder_name);
2680
g_free(folder_real);
2682
CAMEL_SERVICE_REC_UNLOCK (imap_store, connect_lock);
2685
} else if (flags & CAMEL_STORE_FOLDER_EXCL) {
2686
camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
2687
_("Cannot create folder `%s': folder exists."),
2690
camel_imap_response_free_without_processing (imap_store, response);
2692
CAMEL_SERVICE_REC_UNLOCK (imap_store, connect_lock);
2697
storage_path = g_strdup_printf("%s/folders", imap_store->storage_path);
2698
folder_dir = imap_path_to_physical (storage_path, folder_name);
2699
g_free(storage_path);
2700
new_folder = camel_imap_folder_new (store, folder_name, folder_dir, ex);
2701
g_free (folder_dir);
2703
CamelException local_ex;
2705
imap_store->current_folder = new_folder;
2706
/* camel_object_ref (new_folder); */
2707
camel_exception_init (&local_ex);
2708
camel_imap_folder_selected (new_folder, response, &local_ex, TRUE);
2710
if (camel_exception_is_set (&local_ex)) {
2711
camel_exception_xfer (ex, &local_ex);
2712
/* camel_object_unref (imap_store->current_folder); */
2713
imap_store->current_folder = NULL;
2714
camel_object_unref (new_folder);
2718
camel_imap_response_free_without_processing (imap_store, response);
2720
CAMEL_SERVICE_REC_UNLOCK (imap_store, connect_lock);
2725
static CamelFolder *
2726
get_folder_offline (CamelStore *store, const char *folder_name,
2727
guint32 flags, CamelException *ex)
2729
CamelImapStore *imap_store = CAMEL_IMAP_STORE (store);
2730
CamelFolder *new_folder = NULL;
2733
if (!g_ascii_strcasecmp (folder_name, "INBOX"))
2734
folder_name = "INBOX";
2736
si = camel_store_summary_path((CamelStoreSummary *)imap_store->summary, folder_name);
2738
char *folder_dir, *storage_path;
2740
storage_path = g_strdup_printf("%s/folders", imap_store->storage_path);
2741
folder_dir = imap_path_to_physical (storage_path, folder_name);
2742
g_free(storage_path);
2743
new_folder = camel_imap_folder_new (store, folder_name, folder_dir, ex);
2746
camel_store_summary_info_free((CamelStoreSummary *)imap_store->summary, si);
2748
camel_exception_setv (ex, CAMEL_EXCEPTION_STORE_NO_FOLDER,
2749
_("No such folder %s"), folder_name);
2756
delete_folder (CamelStore *store, const char *folder_name, CamelException *ex)
2758
CamelImapStore *imap_store = CAMEL_IMAP_STORE (store);
2759
CamelImapResponse *response;
2761
CAMEL_SERVICE_REC_LOCK (imap_store, connect_lock);
2763
if (!camel_disco_store_check_online((CamelDiscoStore *)imap_store, ex))
2766
camel_imap_store_stop_idle (imap_store);
2767
/* make sure this folder isn't currently SELECTed */
2768
response = camel_imap_command (imap_store, NULL, ex, "SELECT INBOX");
2772
camel_imap_response_free_without_processing (imap_store, response);
2773
/*if (imap_store->current_folder)
2774
camel_object_unref (imap_store->current_folder);*/
2776
/* no need to actually create a CamelFolder for INBOX */
2777
imap_store->current_folder = NULL;
2779
response = camel_imap_command(imap_store, NULL, ex, "DELETE %F", folder_name);
2781
camel_imap_response_free (imap_store, response);
2782
imap_forget_folder (imap_store, folder_name, ex);
2785
CAMEL_SERVICE_REC_UNLOCK(imap_store, connect_lock);
2789
manage_subscriptions (CamelStore *store, const char *old_name, gboolean subscribe)
2791
CamelImapStore *imap_store = CAMEL_IMAP_STORE (store);
2793
int olen = strlen(old_name);
2797
count = camel_store_summary_count((CamelStoreSummary *)imap_store->summary);
2798
for (i=0;i<count;i++) {
2799
si = camel_store_summary_index((CamelStoreSummary *)imap_store->summary, i);
2801
path = camel_store_info_path(imap_store->summary, si);
2802
if (strncmp(path, old_name, olen) == 0) {
2804
subscribe_folder(store, path, NULL);
2806
unsubscribe_folder(store, path, NULL);
2808
camel_store_summary_info_free((CamelStoreSummary *)imap_store->summary, si);
2814
rename_folder_info (CamelImapStore *imap_store, const char *old_name, const char *new_name)
2818
int olen = strlen(old_name);
2820
char *npath, *nfull;
2822
count = camel_store_summary_count((CamelStoreSummary *)imap_store->summary);
2823
for (i=0;i<count;i++) {
2824
si = camel_store_summary_index((CamelStoreSummary *)imap_store->summary, i);
2827
path = camel_store_info_path(imap_store->summary, si);
2828
if (strncmp(path, old_name, olen) == 0) {
2829
if (strlen(path) > olen)
2830
npath = g_strdup_printf("%s/%s", new_name, path+olen+1);
2832
npath = g_strdup(new_name);
2833
nfull = camel_imap_store_summary_path_to_full(imap_store->summary, npath, imap_store->dir_sep);
2835
/* workaround for broken server (courier uses '.') that doesn't rename
2836
subordinate folders as required by rfc 2060 */
2837
if (imap_store->dir_sep == '.' && imap_store->courier_crap) {
2838
CamelImapResponse *response;
2840
/* TNY: I'm not sure about this one! */
2842
response = camel_imap_command (imap_store, NULL, NULL, "RENAME %F %G", path, nfull);
2844
camel_imap_response_free (imap_store, response);
2847
camel_store_info_set_string((CamelStoreSummary *)imap_store->summary, si, CAMEL_STORE_INFO_PATH, npath);
2848
camel_store_info_set_string((CamelStoreSummary *)imap_store->summary, si, CAMEL_IMAP_STORE_INFO_FULL_NAME, nfull);
2850
camel_store_summary_touch((CamelStoreSummary *)imap_store->summary);
2854
camel_store_summary_info_free((CamelStoreSummary *)imap_store->summary, si);
2859
make_path (char *path, int nmode, int parent_mode)
2865
if (stat (path, &sb) == 0)
2867
if (S_ISDIR (sb.st_mode) == 0)
2869
if (chmod (path, nmode))
2875
npath = g_strdup (path); /* So we can write to it. */
2877
/* Check whether or not we need to do anything with intermediate dirs. */
2879
/* Skip leading slashes. */
2884
while ((p = strchr (p, '/')))
2887
if (stat (npath, &sb) != 0)
2889
if (mkdir (npath, parent_mode))
2895
else if (S_ISDIR (sb.st_mode) == 0)
2901
*p++ = '/'; /* restore slash */
2906
/* Create the final directory component. */
2907
if (stat (npath, &sb) && mkdir (npath, nmode))
2919
rename_folder (CamelStore *store, const char *old_name, const char *new_name_in, CamelException *ex)
2921
CamelImapStore *imap_store = CAMEL_IMAP_STORE (store);
2922
CamelImapResponse *response;
2923
char *oldpath, *newpath, *storage_path;
2924
char *tpath, *lslash;
2926
CAMEL_SERVICE_REC_LOCK (imap_store, connect_lock);
2928
camel_operation_start (NULL, "Renaming folder");
2930
if (!camel_disco_store_check_online((CamelDiscoStore *)imap_store, ex))
2933
/* Undefined progress */
2934
camel_operation_progress(NULL, 0, 0);
2936
/* make sure this folder isn't currently SELECTed - it's
2937
actually possible to rename INBOX but if you do another
2938
INBOX will immediately be created by the server */
2939
response = camel_imap_command (imap_store, NULL, ex, "SELECT INBOX");
2943
/* Undefined progress */
2944
camel_operation_progress(NULL, 0, 0);
2946
camel_imap_response_free_without_processing (imap_store, response);
2947
/*if (imap_store->current_folder)
2948
camel_object_unref (imap_store->current_folder); */
2949
/* no need to actually create a CamelFolder for INBOX */
2950
imap_store->current_folder = NULL;
2952
/* Undefined progress */
2953
camel_operation_progress(NULL, 0, 0);
2955
imap_store->renaming = TRUE;
2956
if (imap_store->parameters & IMAP_PARAM_SUBSCRIPTIONS)
2957
manage_subscriptions(store, old_name, FALSE);
2959
response = camel_imap_command (imap_store, NULL, ex, "RENAME %F %F", old_name, new_name_in);
2961
if (imap_store->parameters & IMAP_PARAM_SUBSCRIPTIONS)
2962
manage_subscriptions(store, old_name, TRUE);
2966
/* Undefined progress */
2967
camel_operation_progress(NULL, 0, 0);
2969
camel_imap_response_free (imap_store, response);
2971
/* rename summary, and handle broken server */
2972
rename_folder_info(imap_store, old_name, new_name_in);
2974
if (imap_store->parameters & IMAP_PARAM_SUBSCRIPTIONS)
2975
manage_subscriptions(store, new_name_in, TRUE);
2977
/* Undefined progress */
2978
camel_operation_progress(NULL, 0, 0);
2980
storage_path = g_strdup_printf("%s/folders", imap_store->storage_path);
2981
oldpath = imap_path_to_physical (storage_path, old_name);
2982
newpath = imap_path_to_physical (storage_path, new_name_in);
2983
g_free(storage_path);
2985
/* So do we care if this didn't work? Its just a cache? */
2986
tpath = g_strdup (newpath);
2987
/* TNY TODO: Win32 portage needed */
2988
lslash = strrchr (tpath, '/');
2991
make_path (tpath, S_IRWXU, S_IRWXU);
2995
if (g_rename (oldpath, newpath) == -1) {
2996
g_warning ("Could not rename message cache '%s' to '%s': %s: cache reset",
2997
oldpath, newpath, strerror (errno));
3003
imap_store->renaming = FALSE;
3004
CAMEL_SERVICE_REC_UNLOCK(imap_store, connect_lock);
3005
camel_operation_end (NULL);
3008
static CamelFolderInfo *
3009
create_folder (CamelStore *store, const char *parent_name,
3010
const char *folder_name, CamelException *ex)
3012
CamelImapStore *imap_store = CAMEL_IMAP_STORE (store);
3013
char *full_name, *resp, *thisone, *parent_real, *real_name;
3014
CamelImapResponse *response;
3015
CamelException internal_ex;
3016
CamelFolderInfo *root = NULL;
3017
gboolean need_convert;
3021
if (!camel_disco_store_check_online (CAMEL_DISCO_STORE (store), ex))
3028
while (*c && *c != imap_store->dir_sep && !strchr ("#%*", *c))
3032
camel_exception_setv (ex, CAMEL_EXCEPTION_FOLDER_INVALID_PATH,
3033
_("The folder name \"%s\" is invalid because it contains the character \"%c\""),
3038
/* check if the parent allows inferiors */
3040
/* FIXME: use storesummary directly */
3041
parent_real = camel_imap_store_summary_full_from_path(imap_store->summary, parent_name);
3042
if (parent_real == NULL) {
3043
camel_exception_setv(ex, CAMEL_EXCEPTION_FOLDER_INVALID_STATE,
3044
_("Unknown parent folder: %s"), parent_name);
3048
need_convert = FALSE;
3049
response = camel_imap_command (imap_store, NULL, ex, "LIST \"\" %G",
3051
if (!response) /* whoa, this is bad */ {
3052
g_free(parent_real);
3056
/* FIXME: does not handle unexpected circumstances very well */
3057
for (i = 0; i < response->untagged->len && !need_convert; i++) {
3058
resp = response->untagged->pdata[i];
3060
if (!imap_parse_list_response (imap_store, resp, &flags, NULL, &thisone))
3063
if (strcmp (thisone, parent_name) == 0) {
3064
if (flags & CAMEL_FOLDER_NOINFERIORS)
3065
need_convert = TRUE;
3071
camel_imap_response_free (imap_store, response);
3073
camel_exception_init (&internal_ex);
3075
/* if not, check if we can delete it and recreate it */
3077
struct imap_status_item *items, *item;
3078
guint32 messages = 0;
3081
item = items = get_folder_status (imap_store, parent_name, "MESSAGES", TRUE);
3082
while (item != NULL) {
3083
if (!g_ascii_strcasecmp (item->name, "MESSAGES")) {
3084
messages = item->value;
3091
imap_status_item_free (items);
3094
camel_exception_set (ex, CAMEL_EXCEPTION_FOLDER_INVALID_STATE,
3095
_("The parent folder is not allowed to contain subfolders"));
3096
g_free(parent_real);
3100
/* delete the old parent and recreate it */
3101
delete_folder (store, parent_name, &internal_ex);
3102
if (camel_exception_is_set (&internal_ex)) {
3103
camel_exception_xfer (ex, &internal_ex);
3107
/* add the dirsep to the end of parent_name */
3108
name = g_strdup_printf ("%s%c", parent_real, imap_store->dir_sep);
3109
response = camel_imap_command (imap_store, NULL, ex, "CREATE %G",
3114
g_free(parent_real);
3117
camel_imap_response_free (imap_store, response);
3119
root = imap_build_folder_info(imap_store, parent_name);
3122
/* ok now we can create the folder */
3123
real_name = camel_imap_store_summary_path_to_full(imap_store->summary, folder_name, imap_store->dir_sep);
3124
full_name = imap_concat (imap_store, parent_real, real_name);
3127
camel_imap_store_stop_idle (imap_store);
3128
response = camel_imap_command (imap_store, NULL, ex, "CREATE %G", full_name);
3131
CamelImapStoreInfo *si;
3132
CamelFolderInfo *fi;
3134
camel_imap_response_free (imap_store, response);
3136
si = camel_imap_store_summary_add_from_full(imap_store->summary, full_name, imap_store->dir_sep);
3137
camel_store_summary_save((CamelStoreSummary *)imap_store->summary, ex);
3138
fi = imap_build_folder_info(imap_store, camel_store_info_path(imap_store->summary, si));
3139
fi->flags |= CAMEL_FOLDER_NOCHILDREN;
3146
camel_object_trigger_event (CAMEL_OBJECT (store), "folder_created", root);
3148
/* need to re-recreate the folder we just deleted */
3149
camel_object_trigger_event (CAMEL_OBJECT (store), "folder_created", root);
3150
camel_folder_info_free(root);
3155
g_free(parent_real);
3160
static CamelFolderInfo *
3161
parse_list_response_as_folder_info (CamelImapStore *imap_store,
3162
const char *response)
3164
CamelFolderInfo *fi;
3166
char sep, *dir, *path;
3168
CamelImapStoreInfo *si;
3171
if (!imap_parse_list_response (imap_store, response, &flags, &sep, &dir))
3174
/* FIXME: should use imap_build_folder_info, note the differences with param setting tho */
3176
si = camel_imap_store_summary_add_from_full(imap_store->summary, dir, sep?sep:'/');
3181
newflags = (si->info.flags & CAMEL_STORE_INFO_FOLDER_SUBSCRIBED) | (flags & ~CAMEL_STORE_INFO_FOLDER_SUBSCRIBED);
3182
if (si->info.flags != newflags) {
3183
si->info.flags = newflags;
3184
camel_store_summary_touch((CamelStoreSummary *)imap_store->summary);
3187
flags = (flags & ~CAMEL_FOLDER_SUBSCRIBED) | (si->info.flags & CAMEL_STORE_FOLDER_INFO_SUBSCRIBED);
3189
fi = camel_folder_info_new ();
3191
fi->full_name = g_strdup(camel_store_info_path(imap_store->summary, si));
3192
if (!g_ascii_strcasecmp(fi->full_name, "inbox")) {
3193
flags |= CAMEL_FOLDER_SYSTEM|CAMEL_FOLDER_TYPE_INBOX;
3194
fi->name = g_strdup (_("Inbox"));
3196
fi->name = g_strdup(camel_store_info_name(imap_store->summary, si));
3198
/* HACK: some servers report noinferiors for all folders (uw-imapd)
3199
We just translate this into nochildren, and let the imap layer enforce
3200
it. See create folder */
3201
if (flags & CAMEL_FOLDER_NOINFERIORS)
3202
flags = (flags & ~CAMEL_FOLDER_NOINFERIORS) | CAMEL_FOLDER_NOCHILDREN;
3205
url = camel_url_new (imap_store->base_url, NULL);
3206
path = alloca(strlen(fi->full_name)+2);
3207
sprintf(path, "/%s", fi->full_name);
3208
camel_url_set_path(url, path);
3210
if (flags & CAMEL_FOLDER_NOSELECT || fi->name[0] == 0)
3211
camel_url_set_param (url, "noselect", "yes");
3212
fi->uri = camel_url_to_string (url, 0);
3213
camel_url_free (url);
3218
static int imap_match_pattern(char dir_sep, const char *pattern, const char *name)
3228
} else if (p == '%') {
3234
} else if (p == '*') {
3240
return n == 0 && (p == '%' || p == 0);
3243
/* imap needs to treat inbox case insensitive */
3244
/* we'll assume the names are normalised already */
3245
static guint folder_hash(const void *ap)
3249
if (g_ascii_strcasecmp(a, "INBOX") == 0)
3252
return g_str_hash(a);
3255
static int folder_eq(const void *ap, const void *bp)
3260
if (g_ascii_strcasecmp(a, "INBOX") == 0)
3262
if (g_ascii_strcasecmp(b, "INBOX") == 0)
3265
return g_str_equal(a, b);
3269
get_folders_free(void *k, void *v, void *d)
3271
camel_folder_info_free(v);
3275
get_folders_sync(CamelImapStore *imap_store, const char *pattern, CamelException *ex)
3277
CamelImapResponse *response;
3278
CamelFolderInfo *fi, *hfi;
3281
GHashTable *present;
3284
/* We do a LIST followed by LSUB, and merge the results. LSUB may not be a strict
3285
subset of LIST for some servers, so we can't use either or separately */
3287
/* TNY TODO! It used to loop until 2, but I think it's just wrong to
3288
merge with LSUB?! (It doesn't make any sense) */
3290
present = g_hash_table_new(folder_hash, folder_eq);
3294
camel_imap_store_stop_idle (imap_store);
3295
response = camel_imap_command (imap_store, NULL, ex,
3296
"%s \"\" %G", j==1 ? "LSUB" : "LIST",
3301
for (i = 0; i < response->untagged->len; i++)
3303
list = response->untagged->pdata[i];
3304
fi = parse_list_response_as_folder_info (imap_store, list);
3308
if (FALSE && j == 0)
3310
struct imap_status_item *item, *items;
3311
item = items = get_folder_status (imap_store, fi->full_name, "MESSAGES UNSEEN", TRUE);
3312
while (item != NULL)
3314
if (!g_ascii_strcasecmp (item->name, "MESSAGES"))
3315
fi->total = item->value;
3316
if (!g_ascii_strcasecmp (item->name, "UNSEEN"))
3317
fi->unread = item->value;
3320
imap_status_item_free (items);
3323
hfi = g_hash_table_lookup(present, fi->full_name);
3329
/* It's in LSUB but not in LIST? */
3331
fi->flags |= CAMEL_STORE_INFO_FOLDER_SUBSCRIBED;
3332
if ((fi->flags & (CAMEL_IMAP_FOLDER_MARKED | CAMEL_IMAP_FOLDER_UNMARKED)))
3333
imap_store->capabilities |= IMAP_CAPABILITY_useful_lsub;
3336
if (j == 0) /* From the LSUB we don't add folders */
3337
g_hash_table_insert(present, fi->full_name, fi);
3339
camel_folder_info_free(fi);
3342
hfi->flags |= CAMEL_STORE_INFO_FOLDER_SUBSCRIBED;
3346
hfi->unread = fi->unread;
3347
hfi->total = fi->total;
3349
camel_folder_info_free(fi);
3353
camel_imap_response_free (imap_store, response);
3356
/* Sync summary to match */
3358
/* FIXME: we need to emit folder_create/subscribed/etc events for any new folders */
3359
count = camel_store_summary_count((CamelStoreSummary *)imap_store->summary);
3361
for (i=0;i<count;i++)
3363
si = camel_store_summary_index((CamelStoreSummary *)imap_store->summary, i);
3367
if (imap_match_pattern(imap_store->dir_sep, pattern, camel_imap_store_info_full_name(imap_store->summary, si)))
3369
if ((fi = g_hash_table_lookup(present, camel_store_info_path(imap_store->summary, si))) != NULL)
3371
if (((fi->flags ^ si->flags) & CAMEL_STORE_INFO_FOLDER_SUBSCRIBED))
3372
si->flags = (si->flags & ~CAMEL_FOLDER_SUBSCRIBED) | (fi->flags & CAMEL_FOLDER_SUBSCRIBED);
3373
si->unread = fi->unread;
3374
si->total = fi->total;
3375
camel_store_summary_touch((CamelStoreSummary *)imap_store->summary);
3376
camel_store_summary_save((CamelStoreSummary *)imap_store->summary, ex);
3378
camel_store_summary_remove((CamelStoreSummary *)imap_store->summary, si);
3379
camel_store_summary_save((CamelStoreSummary *)imap_store->summary, ex);
3384
camel_store_summary_info_free((CamelStoreSummary *)imap_store->summary, si);
3387
g_hash_table_foreach(present, get_folders_free, NULL);
3388
g_hash_table_destroy(present);
3393
dumpfi(CamelFolderInfo *fi)
3396
CamelFolderInfo *n = fi;
3408
printf("%-40s %-30s %*s\n", fi->path, fi->full_name, depth*2+strlen(fi->url), fi->url);
3420
fill_fi(CamelStore *store, CamelFolderInfo *fi, guint32 flags)
3422
CamelFolder *folder;
3423
CamelImapStore *imap_store = (CamelImapStore *) store;
3425
char *storage_path = g_strdup_printf("%s/folders", imap_store->storage_path);
3426
char *folder_dir = imap_path_to_physical (storage_path, fi->full_name);
3428
g_free(storage_path);
3429
camel_du (folder_dir, &msize);
3431
folder = camel_object_bag_peek(store->folders, fi->full_name);
3435
fi->unread = camel_folder_get_unread_message_count(folder);
3436
fi->total = camel_folder_get_message_count(folder);
3437
camel_object_unref(folder);
3439
} else if ((fi->unread == -1) || (fi->total == -1)) {
3440
gchar *spath = g_strdup_printf ("%s/summary.mmap", folder_dir);
3441
camel_file_util_read_counts (spath, fi);
3445
fi->local_size = msize;
3446
g_free (folder_dir);
3449
struct _refresh_msg {
3450
CamelSessionThreadMsg msg;
3457
refresh_refresh(CamelSession *session, CamelSessionThreadMsg *msg)
3459
struct _refresh_msg *m = (struct _refresh_msg *)msg;
3460
CamelImapStore *store = (CamelImapStore *)m->store;
3461
CamelException nex = CAMEL_EXCEPTION_INITIALISER;
3463
CAMEL_SERVICE_REC_LOCK(m->store, connect_lock);
3465
if (!camel_disco_store_check_online((CamelDiscoStore *)m->store, &m->ex))
3468
if (store->namespace && store->namespace[0]) {
3471
get_folders_sync(store, "INBOX", &m->ex);
3472
if (camel_exception_is_set(&m->ex))
3474
get_folders_sync(store, store->namespace, &m->ex);
3475
if (camel_exception_is_set(&m->ex))
3477
pattern = imap_concat(store, store->namespace, "*");
3478
get_folders_sync(store, pattern, &m->ex);
3481
get_folders_sync((CamelImapStore *)m->store, "*", &m->ex);
3483
camel_store_summary_touch((CamelStoreSummary *)((CamelImapStore *)m->store)->summary);
3484
camel_store_summary_save((CamelStoreSummary *)((CamelImapStore *)m->store)->summary, &nex);
3486
CAMEL_SERVICE_REC_UNLOCK(m->store, connect_lock);
3490
refresh_free(CamelSession *session, CamelSessionThreadMsg *msg)
3492
struct _refresh_msg *m = (struct _refresh_msg *)msg;
3494
camel_object_unref(m->store);
3495
camel_exception_clear(&m->ex);
3498
static CamelSessionThreadOps refresh_ops = {
3503
static CamelFolderInfo *
3504
get_folder_info_online (CamelStore *store, const char *top, guint32 flags, CamelException *ex)
3506
CamelImapStore *imap_store = CAMEL_IMAP_STORE (store);
3507
CamelFolderInfo *tree = NULL;
3509
g_static_rec_mutex_lock (imap_store->sum_lock);
3511
/* If we have a list of folders already, use that, but if we haven't
3512
updated for a while, then trigger an asynchronous rescan. Otherwise
3513
we update the list first, and then build it from that */
3518
if (imap_store->going_online || !imap_store->got_online)
3521
if (camel_debug("imap:folder_info"))
3522
printf("get folder info online\n");
3524
if ((flags & CAMEL_STORE_FOLDER_INFO_SUBSCRIBED)
3525
&& camel_store_summary_count((CamelStoreSummary *)imap_store->summary) > 0) {
3530
ref = now > imap_store->refresh_stamp+60*60*1;
3532
CAMEL_SERVICE_REC_LOCK(store, connect_lock);
3533
ref = now > imap_store->refresh_stamp+60*60*1;
3535
struct _refresh_msg *m;
3537
imap_store->refresh_stamp = now;
3539
m = camel_session_thread_msg_new(((CamelService *)store)->session, &refresh_ops, sizeof(*m));
3541
camel_object_ref(store);
3542
camel_exception_init(&m->ex);
3543
camel_session_thread_queue(((CamelService *)store)->session, &m->msg, 0);
3545
CAMEL_SERVICE_REC_UNLOCK(store, connect_lock);
3551
CAMEL_SERVICE_REC_LOCK(store, connect_lock);
3553
if (!camel_disco_store_check_online((CamelDiscoStore *)imap_store, ex))
3557
if (imap_store->namespace && imap_store->namespace[0]) {
3558
get_folders_sync(imap_store, "INBOX", ex);
3559
if (camel_exception_is_set(ex))
3562
i = strlen(imap_store->namespace)-1;
3563
pattern = g_alloca(i+5);
3564
strcpy(pattern, imap_store->namespace);
3565
while (i>0 && pattern[i] == imap_store->dir_sep)
3569
pattern = g_alloca(2);
3577
name = camel_imap_store_summary_full_from_path(imap_store->summary, top);
3579
name = camel_imap_store_summary_path_to_full(imap_store->summary, top, imap_store->dir_sep);
3582
pattern = g_alloca(i+5);
3583
strcpy(pattern, name);
3587
get_folders_sync(imap_store, pattern, ex);
3588
if (camel_exception_is_set(ex))
3590
if (pattern[0] != '*' && imap_store->dir_sep) {
3591
pattern[i] = imap_store->dir_sep;
3592
pattern[i+1] = (flags & CAMEL_STORE_FOLDER_INFO_RECURSIVE)?'*':'%';
3594
get_folders_sync(imap_store, pattern, ex);
3596
camel_store_summary_touch((CamelStoreSummary *)imap_store->summary);
3597
camel_store_summary_save((CamelStoreSummary *)imap_store->summary, ex);
3599
CAMEL_SERVICE_REC_UNLOCK(store, connect_lock);
3602
tree = get_folder_info_offline(store, top, flags, ex);
3604
g_static_rec_mutex_unlock (imap_store->sum_lock);
3608
CAMEL_SERVICE_REC_UNLOCK(store, connect_lock);
3610
tree = get_folder_info_offline (store, top, flags, ex);
3612
g_static_rec_mutex_unlock (imap_store->sum_lock);
3616
static CamelFolderInfo *
3617
get_folder_info_offline (CamelStore *store, const char *top,
3618
guint32 flags, CamelException *ex)
3620
CamelImapStore *imap_store = CAMEL_IMAP_STORE (store);
3621
gboolean include_inbox = FALSE;
3622
CamelFolderInfo *fi;
3624
char *pattern, *name;
3627
g_static_rec_mutex_lock (imap_store->sum_lock);
3629
if (camel_debug("imap:folder_info"))
3630
printf("get folder info offline\n");
3632
/* FIXME: obey other flags */
3634
folders = g_ptr_array_new ();
3636
if (top == NULL || top[0] == '\0') {
3637
include_inbox = TRUE;
3641
/* get starting point */
3643
if (imap_store->namespace && imap_store->namespace[0]) {
3644
name = g_strdup(imap_store->summary->namespace->full_name);
3645
top = imap_store->summary->namespace->path;
3647
name = g_strdup("");
3649
name = camel_imap_store_summary_full_from_path(imap_store->summary, top);
3651
name = camel_imap_store_summary_path_to_full(imap_store->summary, top, imap_store->dir_sep);
3654
pattern = imap_concat(imap_store, name, "*");
3656
/* folder_info_build will insert parent nodes as necessary and mark
3657
* them as noselect, which is information we actually don't have at
3658
* the moment. So let it do the right thing by bailing out if it's
3659
* not a folder we're explicitly interested in. */
3661
for (i=0;i<camel_store_summary_count((CamelStoreSummary *)imap_store->summary);i++) {
3662
CamelStoreInfo *si = camel_store_summary_index((CamelStoreSummary *)imap_store->summary, i);
3667
if ((!strcmp(name, camel_imap_store_info_full_name(imap_store->summary, si))
3668
|| imap_match_pattern(imap_store->dir_sep, pattern, camel_imap_store_info_full_name(imap_store->summary, si))
3669
|| (include_inbox && !g_ascii_strcasecmp (camel_imap_store_info_full_name(imap_store->summary, si), "INBOX")))
3670
&& ((imap_store->parameters & IMAP_PARAM_SUBSCRIPTIONS) == 0
3671
|| (flags & CAMEL_STORE_FOLDER_INFO_SUBSCRIBED) == 0
3672
|| (si->flags & CAMEL_STORE_INFO_FOLDER_SUBSCRIBED)))
3675
fi = imap_build_folder_info(imap_store, camel_store_info_path((CamelStoreSummary *)imap_store->summary, si));
3676
fi->flags = si->flags;
3677
fi->unread = si->unread;
3678
fi->total = si->total;
3680
if (fi->unread == 0 || fi->total == 0)
3686
/* HACK: some servers report noinferiors for all folders (uw-imapd)
3687
We just translate this into nochildren, and let the imap layer enforce
3688
it. See create folder */
3689
if (fi->flags & CAMEL_FOLDER_NOINFERIORS)
3690
fi->flags = (fi->flags & ~CAMEL_FOLDER_NOINFERIORS) | CAMEL_FOLDER_NOCHILDREN;
3692
/* blah, this gets lost somewhere, i can't be bothered finding out why */
3693
if (!g_ascii_strcasecmp(fi->full_name, "inbox"))
3694
fi->flags = (fi->flags & ~CAMEL_FOLDER_TYPE_MASK) | CAMEL_FOLDER_TYPE_INBOX;
3696
if (si->flags & CAMEL_FOLDER_NOSELECT) {
3697
CamelURL *url = camel_url_new(fi->uri, NULL);
3699
camel_url_set_param (url, "noselect", "yes");
3701
fi->uri = camel_url_to_string (url, 0);
3702
camel_url_free (url);
3707
fill_fi((CamelStore *)imap_store, fi, 0);
3708
if (fi->unread == -1)
3711
if (fi->total == -1)
3714
g_ptr_array_add (folders, fi);
3716
camel_store_summary_info_free((CamelStoreSummary *)imap_store->summary, si);
3720
fi = camel_folder_info_build (folders, top, '/', TRUE);
3721
g_ptr_array_free (folders, TRUE);
3724
g_static_rec_mutex_unlock (imap_store->sum_lock);
3730
folder_subscribed (CamelStore *store, const char *folder_name)
3732
CamelImapStore *imap_store = CAMEL_IMAP_STORE (store);
3736
g_static_rec_mutex_lock (imap_store->sum_lock);
3738
si = camel_store_summary_path((CamelStoreSummary *)imap_store->summary, folder_name);
3740
truth = (si->flags & CAMEL_STORE_INFO_FOLDER_SUBSCRIBED) != 0;
3741
camel_store_summary_info_free((CamelStoreSummary *)imap_store->summary, si);
3744
g_static_rec_mutex_unlock (imap_store->sum_lock);
3749
/* Note: folder_name must match a folder as listed with get_folder_info() -> full_name */
3751
subscribe_folder (CamelStore *store, const char *folder_name,
3754
CamelImapStore *imap_store = CAMEL_IMAP_STORE (store);
3755
CamelImapResponse *response;
3756
CamelFolderInfo *fi;
3759
CAMEL_SERVICE_REC_LOCK(store, connect_lock);
3761
if (!camel_disco_store_check_online((CamelDiscoStore *)imap_store, ex))
3764
camel_imap_store_stop_idle (imap_store);
3765
response = camel_imap_command (imap_store, NULL, ex,
3766
"SUBSCRIBE %F", folder_name);
3769
camel_imap_response_free (imap_store, response);
3771
g_static_rec_mutex_lock (imap_store->sum_lock);
3773
si = camel_store_summary_path((CamelStoreSummary *)imap_store->summary, folder_name);
3775
if ((si->flags & CAMEL_STORE_INFO_FOLDER_SUBSCRIBED) == 0) {
3776
si->flags |= CAMEL_STORE_INFO_FOLDER_SUBSCRIBED;
3777
camel_store_summary_touch((CamelStoreSummary *)imap_store->summary);
3778
camel_store_summary_save((CamelStoreSummary *)imap_store->summary, ex);
3780
camel_store_summary_info_free((CamelStoreSummary *)imap_store->summary, si);
3783
g_static_rec_mutex_unlock (imap_store->sum_lock);
3785
if (imap_store->renaming) {
3786
/* we don't need to emit a "folder_subscribed" signal
3787
if we are in the process of renaming folders, so we
3792
fi = imap_build_folder_info(imap_store, folder_name);
3793
fi->flags |= CAMEL_FOLDER_NOCHILDREN;
3795
camel_object_trigger_event (CAMEL_OBJECT (store), "folder_subscribed", fi);
3796
camel_folder_info_free (fi);
3798
CAMEL_SERVICE_REC_UNLOCK(store, connect_lock);
3802
unsubscribe_folder (CamelStore *store, const char *folder_name,
3805
CamelImapStore *imap_store = CAMEL_IMAP_STORE (store);
3806
CamelImapResponse *response;
3808
CAMEL_SERVICE_REC_LOCK(store, connect_lock);
3810
if (!camel_disco_store_check_online((CamelDiscoStore *)imap_store, ex))
3813
camel_imap_store_stop_idle (imap_store);
3814
response = camel_imap_command (imap_store, NULL, ex,
3815
"UNSUBSCRIBE %F", folder_name);
3818
camel_imap_response_free (imap_store, response);
3820
imap_folder_effectively_unsubscribed (imap_store, folder_name, ex);
3822
CAMEL_SERVICE_REC_UNLOCK(store, connect_lock);
3827
folder_flags_have_changed (CamelFolder *folder)
3829
CamelMessageInfo *info;
3832
max = camel_folder_summary_count (folder->summary);
3833
for (i = 0; i < max; i++) {
3834
info = camel_folder_summary_index (folder->summary, i);
3837
if (info->flags & CAMEL_MESSAGE_FOLDER_FLAGGED) {
3846
/* Use this whenever you need to ensure you're both connected and
3849
camel_imap_store_connected (CamelImapStore *store, CamelException *ex)
3851
/* This looks stupid ... because it is.
3853
camel-service-connect will return OK if we connect in 'offline mode',
3854
which isn't what we want at all. So we have to recheck we actually
3855
did connect anyway ... */
3857
if (store->istream != NULL
3858
|| (camel_disco_store_check_online((CamelDiscoStore *)store, ex)
3859
&& camel_service_connect((CamelService *)store, ex)
3860
&& store->istream != NULL))
3863
if (!camel_exception_is_set(ex))
3864
camel_exception_set (ex, CAMEL_EXCEPTION_SERVICE_UNAVAILABLE,
3865
_("You must be working online to complete this operation"));
3871
/* FIXME: please god, when will the hurting stop? Thus function is so
3872
fucking broken it's not even funny. */
3874
camel_imap_store_readline (CamelImapStore *store, char **dest, CamelException *ex)
3876
CamelStreamBuffer *stream;
3877
char linebuf[1024] = {0};
3881
g_return_val_if_fail (CAMEL_IS_IMAP_STORE (store), -1);
3882
g_return_val_if_fail (dest, -1);
3886
/* Check for connectedness. Failed (or cancelled) operations will
3887
* close the connection. We can't expect a read to have any
3888
* meaning if we reconnect, so always set an exception.
3891
if (!camel_disco_store_check_online((CamelDiscoStore *)store, ex))
3894
camel_imap_store_restore_stream_buffer (store);
3895
stream = CAMEL_STREAM_BUFFER (store->istream);
3897
ba = g_byte_array_new ();
3898
while ((nread = camel_stream_buffer_gets (stream, linebuf, sizeof (linebuf))) > 0) {
3899
g_byte_array_append (ba, (const guchar*) linebuf, nread);
3900
if (linebuf[nread - 1] == '\n')
3907
CamelException mex = CAMEL_EXCEPTION_INITIALISER;
3908
camel_exception_set (ex, CAMEL_EXCEPTION_USER_CANCEL, _("Operation cancelled"));
3909
camel_imap_recon (store, &mex);
3910
imap_debug ("Recon: %s\n", camel_exception_get_description (&mex));
3912
camel_exception_setv (ex, CAMEL_EXCEPTION_SERVICE_UNAVAILABLE,
3913
_("Server unexpectedly disconnected: %s"),
3914
g_strerror (errno));
3915
camel_service_disconnect (CAMEL_SERVICE (store), FALSE, NULL);
3918
g_byte_array_free (ba, TRUE);
3922
if (camel_verbose_debug) {
3923
fprintf (stderr, "received: ");
3924
fwrite (ba->data, 1, ba->len, stderr);
3927
/* camel-imap-command.c:imap_read_untagged expects the CRLFs
3928
to be stripped off and be nul-terminated *sigh* */
3929
nread = ba->len - 1;
3930
ba->data[nread] = '\0';
3931
if (ba->data[nread - 1] == '\r') {
3932
ba->data[nread - 1] = '\0';
3936
*dest = (char *) ba->data;
3937
g_byte_array_free (ba, FALSE);
3944
/* FIXME: please god, when will the hurting stop? Thus function is so
3945
fucking broken it's not even funny. */
3947
camel_imap_store_readline_idle (CamelImapStore *store, char **dest, CamelException *ex)
3949
CamelStreamBuffer *stream;
3950
char linebuf[1024] = {0};
3954
g_return_val_if_fail (CAMEL_IS_IMAP_STORE (store), -1);
3955
g_return_val_if_fail (dest, -1);
3959
/* Check for connectedness. Failed (or cancelled) operations will
3960
* close the connection. We can't expect a read to have any
3961
* meaning if we reconnect, so always set an exception.
3964
if (!camel_disco_store_check_online((CamelDiscoStore *)store, ex))
3967
camel_imap_store_restore_stream_buffer (store);
3968
stream = CAMEL_STREAM_BUFFER (store->istream);
3970
ba = g_byte_array_new ();
3971
while ((nread = camel_stream_buffer_gets_idle (stream, linebuf, sizeof (linebuf))) > 0) {
3972
g_byte_array_append (ba, (const guchar*) linebuf, nread);
3973
if (linebuf[nread - 1] == '\n')
3980
CamelException mex = CAMEL_EXCEPTION_INITIALISER;
3981
camel_exception_set (ex, CAMEL_EXCEPTION_USER_CANCEL, _("Operation cancelled"));
3982
camel_imap_recon (store, &mex);
3983
imap_debug ("Recon in idle: %s\n", camel_exception_get_description (&mex));
3985
camel_exception_setv (ex, CAMEL_EXCEPTION_SERVICE_UNAVAILABLE,
3986
_("Server unexpectedly disconnected: %s"),
3987
g_strerror (errno));
3988
camel_service_disconnect (CAMEL_SERVICE (store), FALSE, NULL);
3991
g_byte_array_free (ba, TRUE);
3995
if (camel_verbose_debug) {
3996
fprintf (stderr, "received: ");
3997
fwrite (ba->data, 1, ba->len, stderr);
4000
/* camel-imap-command.c:imap_read_untagged expects the CRLFs
4001
to be stripped off and be nul-terminated *sigh* */
4002
nread = ba->len - 1;
4003
ba->data[nread] = '\0';
4004
if (ba->data[nread - 1] == '\r') {
4005
ba->data[nread - 1] = '\0';
4009
*dest = (char *) ba->data;
4010
g_byte_array_free (ba, FALSE);
4016
camel_imap_store_readline_nl (CamelImapStore *store, char **dest, CamelException *ex)
4018
CamelStreamBuffer *stream;
4019
char linebuf[1024] = {0};
4023
g_return_val_if_fail (CAMEL_IS_IMAP_STORE (store), -1);
4024
g_return_val_if_fail (dest, -1);
4028
/* Check for connectedness. Failed (or cancelled) operations will
4029
* close the connection. We can't expect a read to have any
4030
* meaning if we reconnect, so always set an exception.
4033
if (!camel_disco_store_check_online((CamelDiscoStore *)store, ex))
4036
camel_imap_store_restore_stream_buffer (store);
4038
if (!store->istream)
4041
stream = CAMEL_STREAM_BUFFER (store->istream);
4043
ba = g_byte_array_new ();
4044
while ((nread = camel_stream_buffer_gets (stream, linebuf, sizeof (linebuf))) > 0) {
4045
g_byte_array_append (ba, (const guchar*) linebuf, nread);
4046
if (linebuf[nread - 1] == '\n')
4053
CamelException mex = CAMEL_EXCEPTION_INITIALISER;
4054
camel_exception_set (ex, CAMEL_EXCEPTION_USER_CANCEL, _("Operation cancelled"));
4055
camel_imap_recon (store, &mex);
4056
imap_debug ("Recon in nl: %s\n", camel_exception_get_description (&mex));
4058
camel_exception_setv (ex, CAMEL_EXCEPTION_SERVICE_UNAVAILABLE,
4059
_("Server unexpectedly disconnected: %s"),
4060
g_strerror (errno));
4061
camel_service_disconnect (CAMEL_SERVICE (store), FALSE, NULL);
4064
g_byte_array_free (ba, TRUE);
4068
if (camel_verbose_debug) {
4069
fprintf (stderr, "received: ");
4070
fwrite (ba->data, 1, ba->len, stderr);
4073
/* camel-imap-command.c:imap_read_untagged expects the CRLFs
4074
to be stripped off and be nul-terminated *sigh* */
4075
nread = ba->len - 1;
4076
ba->data[nread] = '\0';
4077
if (ba->data[nread - 1] == '\r') {
4078
ba->data[nread - 1] = '\0';
4082
*dest = (char *) ba->data;
4083
g_byte_array_free (ba, FALSE);
4089
camel_imap_store_readline_nb (CamelImapStore *store, char **dest, CamelException *ex)
4091
CamelStreamBuffer *stream;
4092
char linebuf[1024] = {0};
4096
g_return_val_if_fail (CAMEL_IS_IMAP_STORE (store), -1);
4097
g_return_val_if_fail (dest, -1);
4101
if (store->istream == NULL || ((CamelObject *)store->istream)->ref_count <= 0)
4104
stream = CAMEL_STREAM_BUFFER (store->istream);
4105
ba = g_byte_array_new ();
4106
while ((nread = camel_tcp_stream_buffer_gets_nb (stream, linebuf, sizeof (linebuf))) > 0)
4108
g_byte_array_append (ba, (const guchar*) linebuf, nread);
4109
if (linebuf[nread - 1] == '\n')
4114
g_byte_array_free (ba, TRUE);
4118
nread = ba->len - 1;
4119
ba->data[nread] = '\0';
4120
if (ba->data[nread - 1] == '\r') {
4121
ba->data[nread - 1] = '\0';
4125
*dest = (char *) ba->data;
4126
g_byte_array_free (ba, FALSE);