~ubuntu-branches/ubuntu/karmic/libtinymail/karmic

« back to all changes in this revision

Viewing changes to libtinymail-camel/camel-lite/camel/providers/imap/camel-imap-store.c

  • Committer: Bazaar Package Importer
  • Author(s): Steve Kowalik
  • Date: 2007-10-12 11:21:12 UTC
  • Revision ID: james.westby@ubuntu.com-20071012112112-fod9fs7yrooxjr7i
Tags: upstream-0.0.2
ImportĀ upstreamĀ versionĀ 0.0.2

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
 
2
/* camel-imap-store.c : class for an imap store */
 
3
 
 
4
/*
 
5
 *  Authors:
 
6
 *    Dan Winship <danw@ximian.com>
 
7
 *    Jeffrey Stedfast <fejj@ximian.com>
 
8
 *    Philip Van Hoof <pvanhoof@gnome.org>
 
9
 *
 
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.
 
12
 *
 
13
 *  Copyright 2000, 2003 Ximian, Inc.
 
14
 *
 
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.
 
18
 *
 
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.
 
23
 *
 
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.
 
28
 *
 
29
 */
 
30
 
 
31
#include <config.h>
 
32
 
 
33
#include <errno.h>
 
34
#include <stdio.h>
 
35
#include <stdlib.h>
 
36
#include <string.h>
 
37
#include <unistd.h>
 
38
 
 
39
#include <glib.h>
 
40
#include <glib/gi18n-lib.h>
 
41
#include <glib/gstdio.h>
 
42
#include <stdio.h>
 
43
#include <sys/types.h>
 
44
#include <dirent.h>
 
45
 
 
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"
 
67
 
 
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"
 
75
 
 
76
#if !GLIB_CHECK_VERSION (2, 8, 0)
 
77
#define g_access access
 
78
#endif
 
79
 
 
80
#define d(x) 
 
81
 
 
82
/* Specified in RFC 2060 */
 
83
#define IMAP_PORT "143"
 
84
#define IMAPS_PORT "993"
 
85
 
 
86
#ifdef G_OS_WIN32
 
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).
 
90
 */
 
91
#define strtok_r(s,sep,lasts) (*(lasts)=strtok((s),(sep)))
 
92
#endif
 
93
 
 
94
static CamelDiscoStoreClass *parent_class = NULL;
 
95
 
 
96
static char imap_tag_prefix = 'A';
 
97
 
 
98
static void construct (CamelService *service, CamelSession *session,
 
99
                       CamelProvider *provider, CamelURL *url,
 
100
                       CamelException *ex);
 
101
 
 
102
static int imap_setv (CamelObject *object, CamelException *ex, CamelArgV *args);
 
103
static int imap_getv (CamelObject *object, CamelException *ex, CamelArgGetV *args);
 
104
 
 
105
static char *imap_get_name (CamelService *service, gboolean brief);
 
106
 
 
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);
 
120
 
 
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,
 
125
                                                const char *top,
 
126
                                                guint32 flags,
 
127
                                                CamelException *ex);
 
128
static CamelFolderInfo *get_folder_info_offline (CamelStore *store,
 
129
                                                 const char *top,
 
130
                                                 guint32 flags,
 
131
                                                 CamelException *ex);
 
132
static gboolean folder_subscribed (CamelStore *store, const char *folder_name);
 
133
static void subscribe_folder (CamelStore *store, const char *folder_name,
 
134
                              CamelException *ex);
 
135
static void unsubscribe_folder (CamelStore *store, const char *folder_name,
 
136
                                CamelException *ex);
 
137
 
 
138
static void get_folders_sync(CamelImapStore *imap_store, const char *pattern, CamelException *ex);
 
139
 
 
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);
 
144
 
 
145
static void imap_get_folder_status (CamelStore *store, const char *folder_name, int *unseen, int *messages, int *uidnext);
 
146
 
 
147
void
 
148
camel_imap_recon (CamelImapStore *store, CamelException *mex)
 
149
{
 
150
        CamelService *service = CAMEL_SERVICE (store);
 
151
 
 
152
        service->reconnecting = TRUE;
 
153
        if (service->reconnecter)
 
154
                service->reconnecter (service, FALSE, service->data);
 
155
 
 
156
        camel_service_disconnect (service, FALSE, NULL);
 
157
        camel_service_connect (service, mex);
 
158
 
 
159
        if (mex && camel_exception_is_set (mex))
 
160
        {
 
161
                camel_exception_clear (mex);
 
162
                sleep (1);
 
163
                camel_service_connect (service, mex);
 
164
        }
 
165
        if (service->reconnection) {
 
166
                if (!camel_exception_is_set (mex))
 
167
                        service->reconnection (service, TRUE, service->data);
 
168
                else
 
169
                        service->reconnection (service, FALSE, service->data);
 
170
        }
 
171
 
 
172
        service->reconnecting = FALSE;
 
173
}
 
174
 
 
175
static void 
 
176
imap_delete_cache  (CamelStore *store)
 
177
{
 
178
        CamelImapStore *imap_store = (CamelImapStore *) store;
 
179
        gchar *folder_dir = imap_store->storage_path;
 
180
        camel_rm (folder_dir);
 
181
}
 
182
 
 
183
static void 
 
184
let_idle_die (CamelImapStore *imap_store, gboolean connect_buz)
 
185
{
 
186
        imap_store->idle_cont = FALSE;
 
187
 
 
188
        g_static_rec_mutex_lock (imap_store->idle_prefix_lock);
 
189
        g_static_rec_mutex_lock (imap_store->idle_lock);
 
190
 
 
191
        imap_store->idle_cont = FALSE;
 
192
 
 
193
        /* This one can get called from within the thread! This would deadlock
 
194
 
 
195
        if (imap_store->in_idle && imap_store->idle_thread) {
 
196
                g_thread_join (imap_store->idle_thread);
 
197
                imap_store->idle_thread = NULL;
 
198
        } */
 
199
 
 
200
        if (imap_store->idle_prefix)
 
201
        {
 
202
                gchar *resp = NULL;
 
203
                int nwritten = 0;
 
204
                CamelException ex = CAMEL_EXCEPTION_INITIALISER;
 
205
 
 
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");
 
210
                if (nwritten != -1) 
 
211
                {
 
212
                        resp = NULL;
 
213
                        while ((camel_imap_command_response_idle (imap_store, &resp, &ex)) == CAMEL_IMAP_RESPONSE_UNTAGGED) 
 
214
                        {
 
215
                                idle_debug ("(.., ..) <- %s | in idle_deal_with_stuff in let_idle_die\n", resp); 
 
216
                                g_free (resp); resp=NULL; 
 
217
                        }
 
218
                        if (resp)
 
219
                                g_free (resp);
 
220
                }
 
221
        }
 
222
 
 
223
        g_static_rec_mutex_unlock (imap_store->idle_lock);
 
224
        g_static_rec_mutex_unlock (imap_store->idle_prefix_lock);
 
225
 
 
226
        return;
 
227
}
 
228
 
 
229
void
 
230
camel_imap_store_stop_idle (CamelImapStore *store)
 
231
{
 
232
        if (store->current_folder && CAMEL_IS_IMAP_FOLDER (store->current_folder))
 
233
                camel_imap_folder_stop_idle (store->current_folder);
 
234
        else {
 
235
                store->idle_cont = FALSE;
 
236
 
 
237
                g_static_rec_mutex_lock (store->idle_prefix_lock);
 
238
                g_static_rec_mutex_lock (store->idle_lock);
 
239
 
 
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;
 
244
                }
 
245
 
 
246
                if (store->idle_prefix) 
 
247
                {
 
248
                        gchar *resp = NULL;
 
249
                        int nwritten = 0;
 
250
                        CamelException ex = CAMEL_EXCEPTION_INITIALISER;
 
251
 
 
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");
 
255
 
 
256
                        if (nwritten != -1) 
 
257
                        {
 
258
                                resp = NULL;
 
259
                                while ((camel_imap_command_response_idle (store, &resp, &ex)) == CAMEL_IMAP_RESPONSE_UNTAGGED) 
 
260
                                {
 
261
                                        idle_debug ("(.., ..) <- %s | in idle_deal_with_stuff (no current folder?)\n", resp); 
 
262
                                        g_free (resp); resp=NULL; 
 
263
                                }
 
264
                                if (resp)
 
265
                                        g_free (resp);
 
266
                        }
 
267
 
 
268
                        CAMEL_SERVICE_REC_UNLOCK (store, connect_lock);
 
269
                        g_free (store->idle_prefix);
 
270
                        store->idle_prefix = NULL;
 
271
                }
 
272
 
 
273
                g_static_rec_mutex_unlock (store->idle_lock);
 
274
                g_static_rec_mutex_unlock (store->idle_prefix_lock);
 
275
        }
 
276
}
 
277
 
 
278
 
 
279
void
 
280
camel_imap_store_start_idle (CamelImapStore *store)
 
281
{
 
282
        if (store->current_folder && CAMEL_IS_IMAP_FOLDER (store->current_folder))
 
283
                camel_imap_folder_start_idle (store->current_folder);
 
284
}
 
285
 
 
286
static void
 
287
camel_imap_store_class_init (CamelImapStoreClass *camel_imap_store_class)
 
288
{
 
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);
 
297
 
 
298
        parent_class = CAMEL_DISCO_STORE_CLASS (camel_type_get_global_classfuncs (camel_disco_store_get_type ()));
 
299
 
 
300
        /* virtual method overload */
 
301
        camel_object_class->setv = imap_setv;
 
302
        camel_object_class->getv = imap_getv;
 
303
        
 
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;
 
307
 
 
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;
 
322
 
 
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;
 
334
}
 
335
 
 
336
static gboolean
 
337
free_key (gpointer key, gpointer value, gpointer user_data)
 
338
{
 
339
        g_free (key);
 
340
        return TRUE;
 
341
}
 
342
 
 
343
static void
 
344
camel_imap_store_finalize (CamelObject *object)
 
345
{
 
346
        CamelImapStore *imap_store = CAMEL_IMAP_STORE (object);
 
347
        CamelDiscoStore *disco = CAMEL_DISCO_STORE (object);
 
348
        CamelException nex = CAMEL_EXCEPTION_INITIALISER;
 
349
 
 
350
        let_idle_die (imap_store, TRUE);
 
351
 
 
352
        /* This frees current_folder, folders, authtypes, streams, and namespace. */
 
353
        camel_service_disconnect((CamelService *)imap_store, TRUE, NULL);
 
354
 
 
355
        if (imap_store->addrinfo) {
 
356
                freeaddrinfo (imap_store->addrinfo);
 
357
                imap_store->addrinfo = NULL;
 
358
        }
 
359
 
 
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);
 
364
        }
 
365
        g_static_rec_mutex_unlock (imap_store->sum_lock);
 
366
 
 
367
 
 
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);
 
372
 
 
373
        if (disco->diary) {
 
374
                camel_object_unref (disco->diary);
 
375
                disco->diary = NULL;
 
376
        }
 
377
 
 
378
        g_static_rec_mutex_free (imap_store->idle_prefix_lock);
 
379
        imap_store->idle_prefix_lock = NULL;
 
380
 
 
381
        g_static_rec_mutex_free (imap_store->idle_lock);
 
382
        imap_store->idle_lock = NULL;
 
383
 
 
384
        g_static_rec_mutex_free (imap_store->sum_lock);
 
385
        imap_store->sum_lock = NULL;
 
386
 
 
387
}
 
388
 
 
389
static void
 
390
camel_imap_store_init (gpointer object, gpointer klass)
 
391
{
 
392
        CamelImapStore *imap_store = CAMEL_IMAP_STORE (object);
 
393
 
 
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);
 
399
 
 
400
        imap_store->idle_lock = g_new0 (GStaticRecMutex, 1);
 
401
        g_static_rec_mutex_init (imap_store->idle_lock);
 
402
 
 
403
        imap_store->sum_lock = g_new0 (GStaticRecMutex, 1);
 
404
        g_static_rec_mutex_init (imap_store->sum_lock);
 
405
 
 
406
        imap_store->dontdistridlehack = FALSE;
 
407
 
 
408
        imap_store->idle_sleep = 20; /* default of 20s */
 
409
        imap_store->getsrv_sleep = 100; /* default of 100s */
 
410
 
 
411
        imap_store->in_idle = FALSE;
 
412
        imap_store->idle_cont = FALSE;
 
413
        imap_store->idle_thread = NULL;
 
414
        imap_store->idle_prefix = NULL;
 
415
 
 
416
        imap_store->istream = NULL;
 
417
        imap_store->ostream = NULL;
 
418
        imap_store->has_login = FALSE;
 
419
 
 
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;
 
426
 
 
427
        imap_store->addrinfo = NULL;
 
428
        ((CamelStore *)imap_store)->flags |= CAMEL_STORE_SUBSCRIPTIONS;
 
429
 
 
430
        imap_store->tag_prefix = imap_tag_prefix++;
 
431
        if (imap_tag_prefix > 'Z')
 
432
                imap_tag_prefix = 'A';
 
433
}
 
434
 
 
435
CamelType
 
436
camel_imap_store_get_type (void)
 
437
{
 
438
        static CamelType camel_imap_store_type = CAMEL_INVALID_TYPE;
 
439
        
 
440
        if (camel_imap_store_type == CAMEL_INVALID_TYPE)        {
 
441
                camel_imap_store_type =
 
442
                        camel_type_register (CAMEL_DISCO_STORE_TYPE,
 
443
                                             "CamelImapStore",
 
444
                                             sizeof (CamelImapStore),
 
445
                                             sizeof (CamelImapStoreClass),
 
446
                                             (CamelObjectClassInitFunc) camel_imap_store_class_init,
 
447
                                             NULL,
 
448
                                             (CamelObjectInitFunc) camel_imap_store_init,
 
449
                                             (CamelObjectFinalizeFunc) camel_imap_store_finalize);
 
450
        }
 
451
        
 
452
        return camel_imap_store_type;
 
453
}
 
454
 
 
455
static void
 
456
construct (CamelService *service, CamelSession *session,
 
457
           CamelProvider *provider, CamelURL *url,
 
458
           CamelException *ex)
 
459
{
 
460
        CamelImapStore *imap_store = CAMEL_IMAP_STORE (service);
 
461
        CamelStore *store = CAMEL_STORE (service);
 
462
        CamelDiscoStore *disco_store = CAMEL_DISCO_STORE (service);
 
463
        char *tmp, *path;
 
464
        CamelURL *summary_url;
 
465
 
 
466
        CAMEL_SERVICE_CLASS (parent_class)->construct (service, session, provider, url, ex);
 
467
        if (camel_exception_is_set (ex))
 
468
                return;
 
469
 
 
470
        imap_store->storage_path = camel_session_get_storage_path (session, service, ex);
 
471
        if (!imap_store->storage_path)
 
472
                return;
 
473
 
 
474
        /* FIXME */
 
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));
 
478
 
 
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"));
 
486
        }
 
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;
 
492
        }
 
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;
 
497
 
 
498
        /* setup journal*/
 
499
        path = g_strdup_printf ("%s/journal", imap_store->storage_path);
 
500
        disco_store->diary = camel_disco_diary_new (disco_store, path, ex);
 
501
        g_free (path);
 
502
 
 
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);
 
506
 
 
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;
 
515
 
 
516
                if (is->namespace) {
 
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);
 
520
                        } else {
 
521
                                imap_store->namespace = g_strdup(is->namespace->full_name);
 
522
                                imap_store->dir_sep = is->namespace->sep;
 
523
                        }
 
524
                }
 
525
 
 
526
                imap_store->capabilities = is->capabilities;
 
527
                imap_set_server_level(imap_store);
 
528
        }
 
529
        g_static_rec_mutex_unlock (imap_store->sum_lock);
 
530
 
 
531
        return;
 
532
}
 
533
 
 
534
 
 
535
 
 
536
static int
 
537
imap_setv (CamelObject *object, CamelException *ex, CamelArgV *args)
 
538
{
 
539
        CamelImapStore *store = (CamelImapStore *) object;
 
540
        guint32 tag, flags;
 
541
        int i;
 
542
        
 
543
        for (i = 0; i < args->argc; i++) {
 
544
                tag = args->argv[i].tag;
 
545
                
 
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)
 
549
                        continue;
 
550
 
 
551
                switch (tag) {
 
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;*/
 
558
                        }
 
559
                        break;
 
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);
 
563
                        
 
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;*/
 
568
                        }
 
569
                        break;
 
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... */
 
575
                        break;
 
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... */
 
581
                        break;
 
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);
 
585
                        break;
 
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);
 
589
                        break;
 
590
                default:
 
591
                        /* error?? */
 
592
                        continue;
 
593
                }
 
594
                
 
595
                /* let our parent know that we've handled this arg */
 
596
                camel_argv_ignore (args, i);
 
597
        }
 
598
        
 
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. */
 
602
        
 
603
        return CAMEL_OBJECT_CLASS (parent_class)->setv (object, ex, args);
 
604
}
 
605
 
 
606
static int
 
607
imap_getv (CamelObject *object, CamelException *ex, CamelArgGetV *args)
 
608
{
 
609
        CamelImapStore *store = (CamelImapStore *) object;
 
610
        guint32 tag;
 
611
        int i;
 
612
        
 
613
        for (i = 0; i < args->argc; i++) {
 
614
                tag = args->argv[i].tag;
 
615
                
 
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)
 
619
                        continue;
 
620
                
 
621
                switch (tag) {
 
622
                case CAMEL_IMAP_STORE_NAMESPACE:
 
623
                        *args->argv[i].ca_str = store->namespace;
 
624
                        break;
 
625
                case CAMEL_IMAP_STORE_OVERRIDE_NAMESPACE:
 
626
                        *args->argv[i].ca_int = store->parameters & IMAP_PARAM_OVERRIDE_NAMESPACE ? TRUE : FALSE;
 
627
                        break;
 
628
                case CAMEL_IMAP_STORE_CHECK_ALL:
 
629
                        *args->argv[i].ca_int = store->parameters & IMAP_PARAM_CHECK_ALL ? TRUE : FALSE;
 
630
                        break;
 
631
                case CAMEL_IMAP_STORE_FILTER_INBOX:
 
632
                        *args->argv[i].ca_int = store->parameters & IMAP_PARAM_FILTER_INBOX ? TRUE : FALSE;
 
633
                        break;
 
634
                case CAMEL_IMAP_STORE_FILTER_JUNK:
 
635
                        *args->argv[i].ca_int = store->parameters & IMAP_PARAM_FILTER_JUNK ? TRUE : FALSE;
 
636
                        break;
 
637
                case CAMEL_IMAP_STORE_FILTER_JUNK_INBOX:
 
638
                        *args->argv[i].ca_int = store->parameters & IMAP_PARAM_FILTER_JUNK_INBOX ? TRUE : FALSE;
 
639
                        break;
 
640
                default:
 
641
                        /* error? */
 
642
                        break;
 
643
                }
 
644
        }
 
645
        
 
646
        return CAMEL_OBJECT_CLASS (parent_class)->getv (object, ex, args);
 
647
}
 
648
 
 
649
static char *
 
650
imap_get_name (CamelService *service, gboolean brief)
 
651
{
 
652
        if (brief)
 
653
                return g_strdup_printf (_("IMAP server %s"), service->url->host);
 
654
        else
 
655
                return g_strdup_printf (_("IMAP service for %s on %s"),
 
656
                                        service->url->user, service->url->host);
 
657
}
 
658
 
 
659
static void
 
660
imap_set_server_level (CamelImapStore *store)
 
661
{
 
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;
 
667
        else
 
668
                store->server_level = IMAP_LEVEL_UNKNOWN;
 
669
}
 
670
 
 
671
static struct {
 
672
        const char *name;
 
673
        guint32 flag;
 
674
} capabilities[] = {
 
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 },
 
688
        { NULL, 0 }
 
689
};
 
690
 
 
691
static void
 
692
parse_capability(CamelImapStore *store, char *capa)
 
693
{
 
694
        char *lasts;
 
695
        int i;
 
696
 
 
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,
 
700
                                             g_strdup (capa + 5),
 
701
                                             GINT_TO_POINTER (1));
 
702
                        continue;
 
703
                }
 
704
                for (i = 0; capabilities[i].name; i++) {
 
705
                        if (g_ascii_strcasecmp (capa, capabilities[i].name) == 0) {
 
706
                                store->capabilities |= capabilities[i].flag;
 
707
                                break;
 
708
                        }
 
709
                }
 
710
        }
 
711
}
 
712
 
 
713
static gboolean
 
714
imap_get_capability (CamelService *service, CamelException *ex)
 
715
{
 
716
        CamelImapStore *store = CAMEL_IMAP_STORE (service);
 
717
        CamelImapResponse *response;
 
718
        char *result;
 
719
        
 
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");
 
725
        if (!response)
 
726
                return FALSE;
 
727
        result = camel_imap_response_extract (store, response, "CAPABILITY ", ex);
 
728
        if (!result)
 
729
                return FALSE;
 
730
        
 
731
        /* Skip over "* CAPABILITY ". */
 
732
        parse_capability(store, result+13);
 
733
        g_free (result);
 
734
 
 
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);
 
741
                        g_free (result);
 
742
                }
 
743
        }
 
744
        
 
745
        imap_set_server_level (store);
 
746
 
 
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);
 
752
        }
 
753
        g_static_rec_mutex_unlock (store->sum_lock);
 
754
 
 
755
        return TRUE;
 
756
}
 
757
 
 
758
enum {
 
759
        MODE_CLEAR,
 
760
        MODE_SSL,
 
761
        MODE_TLS,
 
762
};
 
763
 
 
764
#ifdef HAVE_SSL
 
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)
 
767
#endif
 
768
 
 
769
 
 
770
gboolean 
 
771
camel_imap_store_restore_stream_buffer (CamelImapStore *store)
 
772
{
 
773
        if (store->istream == NULL || !CAMEL_IS_STREAM_BUFFER (store->istream))
 
774
        {
 
775
                if (store->ostream && CAMEL_IS_STREAM (store->ostream))
 
776
                {
 
777
                        /* This is a recoverable situation. It's strange though */
 
778
                        store->istream = camel_stream_buffer_new (store->ostream, CAMEL_STREAM_BUFFER_READ);
 
779
                } else {
 
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", 
 
785
                                g_strerror (errno));
 
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));
 
790
                        return FALSE;
 
791
                }
 
792
        }
 
793
        return TRUE;
 
794
}
 
795
 
 
796
static gboolean
 
797
connect_to_server (CamelService *service, struct addrinfo *ai, int ssl_mode, int must_tls, CamelException *ex)
 
798
{
 
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;
 
805
        char *buf;
 
806
        gboolean not_ssl = TRUE;
 
807
 
 
808
        memset (&sockopt, 0, sizeof (CamelSockOptData));
 
809
 
 
810
        if (ssl_mode != MODE_CLEAR) 
 
811
        {
 
812
                not_ssl = FALSE;
 
813
 
 
814
#ifdef HAVE_SSL
 
815
                if (ssl_mode == MODE_TLS)
 
816
                        tcp_stream = camel_tcp_stream_ssl_new_raw (service, service->url->host, STARTTLS_FLAGS);
 
817
                else 
 
818
                        tcp_stream = camel_tcp_stream_ssl_new (service, service->url->host, SSL_PORT_FLAGS);
 
819
#else
 
820
 
 
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"));
 
824
 
 
825
                return FALSE;
 
826
 
 
827
#endif /* HAVE_SSL */
 
828
        } else
 
829
                tcp_stream = camel_tcp_stream_raw_new ();
 
830
 
 
831
        if (camel_tcp_stream_connect ((CamelTcpStream *) tcp_stream, ai) == -1) 
 
832
        {
 
833
                if (errno == EINTR)
 
834
                        camel_exception_set (ex, CAMEL_EXCEPTION_USER_CANCEL,
 
835
                                             _("Connection cancelled"));
 
836
                else
 
837
                        camel_exception_setv (ex, CAMEL_EXCEPTION_SERVICE_UNAVAILABLE,
 
838
                                              _("Could not connect to %s: %s"),
 
839
                                              service->url->host,
 
840
                                              g_strerror (errno));
 
841
                camel_object_unref (tcp_stream);
 
842
                return FALSE;
 
843
        }
 
844
 
 
845
        store->ostream = tcp_stream;
 
846
        store->istream = camel_stream_buffer_new (tcp_stream, CAMEL_STREAM_BUFFER_READ);
 
847
 
 
848
        store->connected = TRUE;
 
849
        store->preauthed = FALSE;
 
850
        store->command = 0;
 
851
 
 
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);
 
856
 
 
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);
 
861
 
 
862
        /* Read the greeting, if any, and deal with PREAUTH */
 
863
        if (camel_imap_store_readline (store, &buf, ex) < 0) 
 
864
        {
 
865
                camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
 
866
                        _("Failed to connect to IMAP server %s"),
 
867
                        service->url->host, _("Reading IMAP greeting failed"));
 
868
                goto exception;
 
869
        }
 
870
 
 
871
        if (!strncmp(buf, "* PREAUTH", 9))
 
872
                store->preauthed = TRUE;
 
873
 
 
874
        if (strstr (buf, "Courier-IMAP"))
 
875
                store->courier_crap = TRUE;
 
876
 
 
877
        if (store->courier_crap || getenv("CAMEL_IMAP_BRAINDAMAGED")) 
 
878
        {
 
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")) 
 
887
        {
 
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.
 
891
                 *
 
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
 
896
                 * well.
 
897
                 *
 
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
 
902
                 * for details.
 
903
                 **/
 
904
                force_imap4 = TRUE;
 
905
        }
 
906
 
 
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 */
 
910
 
 
911
        /* Tinymail hack: always use IMAP4, not IMAP4rev1 (sorry) */
 
912
        /* force_imap4 = TRUE; */
 
913
        store->braindamaged = TRUE;
 
914
        /* end of hack :) */
 
915
 
 
916
        g_free (buf);
 
917
 
 
918
        /* get the imap server capabilities */
 
919
        if (!imap_get_capability (service, ex)) 
 
920
        {
 
921
                camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
 
922
                        _("Failed to connect to IMAP server %s"),
 
923
                        service->url->host, _("Reading first CAPABILITY failed"));
 
924
                goto exception;
 
925
        }
 
926
 
 
927
        if (must_tls && !(store->capabilities & IMAP_CAPABILITY_STARTTLS))
 
928
        {
 
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"));
 
932
                goto exception;
 
933
        }
 
934
 
 
935
        if (force_imap4) 
 
936
        {
 
937
                store->capabilities &= ~IMAP_CAPABILITY_IMAP4REV1;
 
938
                store->server_level = IMAP_LEVEL_IMAP4;
 
939
        }
 
940
 
 
941
        if (!must_tls && (not_ssl || ssl_mode != MODE_TLS)) 
 
942
        {
 
943
                /* we're done */
 
944
                return TRUE;
 
945
        }
 
946
 
 
947
 
 
948
#ifdef HAVE_SSL
 
949
 
 
950
        if (!(store->capabilities & IMAP_CAPABILITY_STARTTLS)) 
 
951
        {
 
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"));
 
955
                goto exception;
 
956
        }
 
957
 
 
958
        /* as soon as we send a STARTTLS command, all hope is lost of a clean QUIT if problems arise */
 
959
        clean_quit = FALSE;
 
960
 
 
961
        response = camel_imap_command (store, NULL, ex, "STARTTLS");
 
962
        if (!response) 
 
963
        {
 
964
                camel_object_unref (store->istream);
 
965
                camel_object_unref (store->ostream);
 
966
                store->istream = NULL;
 
967
                store->ostream = NULL;
 
968
                clean_quit = FALSE;
 
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"));
 
972
                goto exception;
 
973
        }
 
974
 
 
975
        camel_imap_response_free_without_processing (store, response);
 
976
 
 
977
        /* Okay, now toggle SSL/TLS mode */
 
978
        if (camel_tcp_stream_ssl_enable_ssl (CAMEL_TCP_STREAM_SSL (tcp_stream)) == -1) 
 
979
        {
 
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));
 
983
                goto exception;
 
984
        }
 
985
 
 
986
#else
 
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"));
 
990
        goto exception;
 
991
#endif /* HAVE_SSL */
 
992
 
 
993
 
 
994
        /* rfc2595, section 4 states that after a successful STLS
 
995
           command, the client MUST discard prior CAPA responses */
 
996
 
 
997
        if (!imap_get_capability (service, ex)) 
 
998
        {
 
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"));
 
1002
                goto exception;
 
1003
        }
 
1004
 
 
1005
        if (store->capabilities & IMAP_CAPABILITY_LOGINDISABLED ) { 
 
1006
                clean_quit = TRUE;
 
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"));
 
1010
                goto exception;
 
1011
        }
 
1012
 
 
1013
        return TRUE;
 
1014
 
 
1015
exception:
 
1016
 
 
1017
        if (clean_quit && store->connected) 
 
1018
        {
 
1019
                /* try to disconnect cleanly */
 
1020
                response = camel_imap_command (store, NULL, ex, "LOGOUT");
 
1021
                if (response)
 
1022
                        camel_imap_response_free_without_processing (store, response);
 
1023
        }
 
1024
 
 
1025
        if (store->istream) {
 
1026
                camel_object_unref (store->istream);
 
1027
                store->istream = NULL;
 
1028
        }
 
1029
        if (store->ostream) {
 
1030
                camel_object_unref (store->ostream);
 
1031
                store->ostream = NULL;
 
1032
        }
 
1033
        store->connected = FALSE;
 
1034
 
 
1035
        return FALSE;
 
1036
}
 
1037
 
 
1038
#ifndef G_OS_WIN32
 
1039
 
 
1040
/* Using custom commands to connect to IMAP servers is not supported on Win32 */
 
1041
 
 
1042
static gboolean
 
1043
connect_to_server_process (CamelService *service, const char *cmd, CamelException *ex)
 
1044
{
 
1045
        CamelImapStore *store = (CamelImapStore *) service;
 
1046
        CamelStream *cmd_stream;
 
1047
        int ret, i = 0;
 
1048
        char *buf;
 
1049
        char *cmd_copy;
 
1050
        char *full_cmd;
 
1051
        char *child_env[7];
 
1052
 
 
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);
 
1057
        g_free(buf);
 
1058
 
 
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;
 
1069
 
 
1070
        /* Now do %h, %u, etc. substitution in cmd */
 
1071
        buf = cmd_copy = g_strdup(cmd);
 
1072
 
 
1073
        full_cmd = g_strdup("");
 
1074
 
 
1075
        for(;;) {
 
1076
                char *pc;
 
1077
                char *tmp;
 
1078
                char *var;
 
1079
                int len;
 
1080
 
 
1081
                pc = strchr(buf, '%');
 
1082
        ignore:
 
1083
                if (!pc) {
 
1084
                        tmp = g_strdup_printf("%s%s", full_cmd, buf);
 
1085
                        g_free(full_cmd);
 
1086
                        full_cmd = tmp;
 
1087
                        break;
 
1088
                }
 
1089
                
 
1090
                len = pc - buf;
 
1091
 
 
1092
                var = NULL;
 
1093
 
 
1094
                switch(pc[1]) {
 
1095
                case 'h':
 
1096
                        var = service->url->host;
 
1097
                        break;
 
1098
                case 'u':
 
1099
                        var = service->url->user;
 
1100
                        break;
 
1101
                }
 
1102
                if (!var) {
 
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, '%');
 
1106
                        goto ignore;
 
1107
                }
 
1108
                tmp = g_strdup_printf("%s%.*s%s", full_cmd, len, buf, var);
 
1109
                g_free(full_cmd);
 
1110
                full_cmd = tmp;
 
1111
                buf = pc + 2;
 
1112
        }
 
1113
                        
 
1114
        g_free(cmd_copy);
 
1115
 
 
1116
        cmd_stream = camel_stream_process_new ();
 
1117
        
 
1118
        ret = camel_stream_process_connect (CAMEL_STREAM_PROCESS(cmd_stream),
 
1119
                                            full_cmd, (const char **)child_env);
 
1120
 
 
1121
        while (i)
 
1122
                g_free(child_env[--i]);
 
1123
 
 
1124
        if (ret == -1) {
 
1125
                if (errno == EINTR)
 
1126
                        camel_exception_set (ex, CAMEL_EXCEPTION_USER_CANCEL,
 
1127
                                             _("Connection cancelled"));
 
1128
                else
 
1129
                        camel_exception_setv (ex, CAMEL_EXCEPTION_SERVICE_UNAVAILABLE,
 
1130
                                              _("Could not connect with command \"%s\": %s"),
 
1131
                                              full_cmd, g_strerror (errno));
 
1132
                
 
1133
                camel_object_unref (cmd_stream);
 
1134
                g_free (full_cmd);
 
1135
                return FALSE;
 
1136
        }
 
1137
        g_free (full_cmd);
 
1138
 
 
1139
        store->ostream = cmd_stream;
 
1140
        store->istream = camel_stream_buffer_new (cmd_stream, CAMEL_STREAM_BUFFER_READ);
 
1141
 
 
1142
        store->connected = TRUE;
 
1143
        store->preauthed = FALSE;
 
1144
        store->command = 0;
 
1145
 
 
1146
        /* Read the greeting, if any, and deal with PREAUTH */
 
1147
        if (camel_imap_store_readline (store, &buf, ex) < 0) 
 
1148
        {
 
1149
                if (store->istream) {
 
1150
                        camel_object_unref (store->istream);
 
1151
                        store->istream = NULL;
 
1152
                }
 
1153
                
 
1154
                if (store->ostream) {
 
1155
                        camel_object_unref (store->ostream);
 
1156
                        store->ostream = NULL;
 
1157
                }
 
1158
 
 
1159
                store->connected = FALSE;
 
1160
                return FALSE;
 
1161
        }
 
1162
 
 
1163
        if (!strncmp(buf, "* PREAUTH", 9))
 
1164
                store->preauthed = TRUE;
 
1165
        g_free (buf);
 
1166
 
 
1167
        /* get the imap server capabilities */
 
1168
        if (!imap_get_capability (service, ex)) 
 
1169
        {
 
1170
                if (store->istream) {
 
1171
                        camel_object_unref (store->istream);
 
1172
                        store->istream = NULL;
 
1173
                }
 
1174
                if (store->ostream) {
 
1175
                        camel_object_unref (store->ostream);
 
1176
                        store->ostream = NULL;
 
1177
                }
 
1178
 
 
1179
                store->connected = FALSE;
 
1180
                return FALSE;
 
1181
        }
 
1182
 
 
1183
        return TRUE;
 
1184
        
 
1185
}
 
1186
 
 
1187
#endif
 
1188
 
 
1189
static struct {
 
1190
        char *value;
 
1191
        char *serv;
 
1192
        char *port;
 
1193
        int mode;
 
1194
        int must_tls;
 
1195
} ssl_options[] = {
 
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 },
 
1202
};
 
1203
 
 
1204
 
 
1205
static gboolean
 
1206
connect_to_server_wrapper (CamelService *service, CamelException *ex)
 
1207
{
 
1208
        const char *ssl_mode, *idle_sleep, *getsrv_sleep;
 
1209
        struct addrinfo hints, *ai;
 
1210
        int mode = -1, ret, i, must_tls = 0;
 
1211
        char *serv;
 
1212
        const char *port;
 
1213
        CamelImapStore *store = (CamelImapStore *) service;
 
1214
 
 
1215
#ifndef G_OS_WIN32
 
1216
        const char *command;
 
1217
 
 
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);
 
1221
#endif
 
1222
 
 
1223
        if (ex)
 
1224
                camel_exception_clear (ex);
 
1225
 
 
1226
        if ((idle_sleep = camel_url_get_param (service->url, "idle_delay")))
 
1227
        {
 
1228
                int tmp = atoi (idle_sleep);
 
1229
                if (tmp != -1)
 
1230
                        CAMEL_IMAP_STORE (service)->idle_sleep = tmp;
 
1231
        } 
 
1232
 
 
1233
        if ((getsrv_sleep = camel_url_get_param (service->url, "getsrv_delay")))
 
1234
        {
 
1235
                int tmp = atoi (getsrv_sleep);
 
1236
                if (tmp != -1)
 
1237
                        CAMEL_IMAP_STORE (service)->getsrv_sleep = tmp;
 
1238
        } 
 
1239
 
 
1240
        if ((ssl_mode = camel_url_get_param (service->url, "use_ssl"))) 
 
1241
        {
 
1242
                for (i = 0; ssl_options[i].value; i++)
 
1243
                        if (!strcmp (ssl_options[i].value, ssl_mode))
 
1244
                                break;
 
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;
 
1249
        } else 
 
1250
        {
 
1251
                mode = MODE_CLEAR;
 
1252
                serv = "imap";
 
1253
                port = IMAP_PORT;
 
1254
                must_tls = 0;
 
1255
        }
 
1256
 
 
1257
        if (service->url->port) 
 
1258
        {
 
1259
                serv = g_alloca (16);
 
1260
                sprintf (serv, "%d", service->url->port);
 
1261
                port = NULL;
 
1262
        }
 
1263
 
 
1264
        memset (&hints, 0, sizeof (hints));
 
1265
        hints.ai_socktype = SOCK_STREAM;
 
1266
        hints.ai_family = PF_UNSPEC;
 
1267
 
 
1268
        if (store->addrinfo == NULL)
 
1269
                store->addrinfo = camel_getaddrinfo(service->url->host, serv, &hints, ex);
 
1270
 
 
1271
        if (store->addrinfo == NULL && port != NULL && 
 
1272
            camel_exception_get_id(ex) != CAMEL_EXCEPTION_USER_CANCEL) 
 
1273
        {
 
1274
                camel_exception_clear (ex);
 
1275
                store->addrinfo = camel_getaddrinfo(service->url->host, port, &hints, ex);
 
1276
        }
 
1277
 
 
1278
        if (store->addrinfo == NULL)
 
1279
                return FALSE;
 
1280
 
 
1281
        ret = connect_to_server (service, store->addrinfo, mode, must_tls, ex);
 
1282
 
 
1283
        return ret;
 
1284
}
 
1285
 
 
1286
extern CamelServiceAuthType camel_imap_password_authtype;
 
1287
 
 
1288
static GList *
 
1289
query_auth_types (CamelService *service, CamelException *ex)
 
1290
{
 
1291
        CamelImapStore *store = CAMEL_IMAP_STORE (service);
 
1292
        CamelServiceAuthType *authtype;
 
1293
        GList *sasl_types, *t, *next;
 
1294
        gboolean connected;
 
1295
        
 
1296
        if (!camel_disco_store_check_online (CAMEL_DISCO_STORE (store), ex))
 
1297
                return NULL;
 
1298
 
 
1299
        CAMEL_SERVICE_REC_LOCK (store, connect_lock);
 
1300
        connected = store->istream != NULL && store->connected;
 
1301
        if (!connected)
 
1302
                connected = connect_to_server_wrapper (service, ex);
 
1303
        CAMEL_SERVICE_REC_UNLOCK (store, connect_lock);
 
1304
 
 
1305
        if (!connected)
 
1306
                return NULL;
 
1307
        
 
1308
        sasl_types = camel_sasl_authtype_list (FALSE);
 
1309
        for (t = sasl_types; t; t = next) {
 
1310
                authtype = t->data;
 
1311
                next = t->next;
 
1312
                
 
1313
                if (!g_hash_table_lookup (store->authtypes, authtype->authproto)) {
 
1314
                        sasl_types = g_list_remove_link (sasl_types, t);
 
1315
                        g_list_free_1 (t);
 
1316
                }
 
1317
        }
 
1318
        
 
1319
        return g_list_prepend (sasl_types, &camel_imap_password_authtype);
 
1320
}
 
1321
 
 
1322
/* folder_name is path name */
 
1323
static CamelFolderInfo *
 
1324
imap_build_folder_info(CamelImapStore *imap_store, const char *folder_name)
 
1325
{
 
1326
        CamelURL *url;
 
1327
        const char *name;
 
1328
        CamelFolderInfo *fi;
 
1329
 
 
1330
        fi = camel_folder_info_new ();
 
1331
        fi->full_name = g_strdup(folder_name);
 
1332
 
 
1333
 
 
1334
        url = camel_url_new (imap_store->base_url, NULL);
 
1335
        g_free (url->path);
 
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, '/');
 
1340
        if (name == NULL)
 
1341
                name = fi->full_name;
 
1342
        else
 
1343
                name++;
 
1344
        if (!g_ascii_strcasecmp (fi->full_name, "INBOX"))
 
1345
                fi->name = g_strdup (_("Inbox"));
 
1346
        else
 
1347
                fi->name = g_strdup (name);
 
1348
        
 
1349
        return fi;
 
1350
}
 
1351
 
 
1352
static void
 
1353
imap_folder_effectively_unsubscribed(CamelImapStore *imap_store, 
 
1354
                                     const char *folder_name, CamelException *ex)
 
1355
{
 
1356
        CamelFolderInfo *fi;
 
1357
        CamelStoreInfo *si;
 
1358
 
 
1359
        g_static_rec_mutex_lock (imap_store->sum_lock);
 
1360
        si = camel_store_summary_path((CamelStoreSummary *)imap_store->summary, folder_name);
 
1361
        if (si) {
 
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);
 
1366
                }
 
1367
                camel_store_summary_info_free((CamelStoreSummary *)imap_store->summary, si);
 
1368
        }
 
1369
        g_static_rec_mutex_unlock (imap_store->sum_lock);
 
1370
 
 
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
 
1374
                   are done here... */
 
1375
                return;
 
1376
 
 
1377
        }
 
1378
 
 
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);
 
1382
}
 
1383
 
 
1384
static void
 
1385
imap_forget_folder (CamelImapStore *imap_store, const char *folder_name, CamelException *ex)
 
1386
{
 
1387
        CamelFolderSummary *summary;
 
1388
        CamelImapMessageCache *cache;
 
1389
        char *summary_file, *state_file;
 
1390
        char *journal_file;
 
1391
        char *folder_dir, *storage_path;
 
1392
        CamelFolderInfo *fi;
 
1393
        const char *name;
 
1394
 
 
1395
        name = strrchr (folder_name, imap_store->dir_sep);
 
1396
        if (name)
 
1397
                name++;
 
1398
        else
 
1399
                name = folder_name;
 
1400
        
 
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);
 
1406
                goto event;
 
1407
        }
 
1408
        
 
1409
        summary_file = g_strdup_printf ("%s/summary", folder_dir);
 
1410
        summary = camel_imap_summary_new (NULL, summary_file);
 
1411
        if (!summary) {
 
1412
                g_free (summary_file);
 
1413
                g_free (folder_dir);
 
1414
                goto event;
 
1415
        }
 
1416
        
 
1417
        cache = camel_imap_message_cache_new (folder_dir, summary, ex);
 
1418
        if (cache)
 
1419
                camel_imap_message_cache_clear (cache);
 
1420
 
 
1421
        camel_object_unref (cache);
 
1422
        camel_object_unref (summary);
 
1423
        
 
1424
        g_unlink (summary_file);
 
1425
        g_free (summary_file);
 
1426
        
 
1427
        journal_file = g_strdup_printf ("%s/journal", folder_dir);
 
1428
        g_unlink (journal_file);
 
1429
        g_free (journal_file);
 
1430
 
 
1431
        state_file = g_strdup_printf ("%s/cmeta", folder_dir);
 
1432
        g_unlink (state_file);
 
1433
        g_free (state_file);
 
1434
 
 
1435
        state_file = g_strdup_printf("%s/subfolders", folder_dir);
 
1436
        g_rmdir(state_file);
 
1437
        g_free(state_file);
 
1438
        
 
1439
        g_rmdir (folder_dir);
 
1440
        g_free (folder_dir);
 
1441
        
 
1442
 event:
 
1443
 
 
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);
 
1448
 
 
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);
 
1452
}
 
1453
 
 
1454
static gboolean
 
1455
imap_check_folder_still_extant (CamelImapStore *imap_store, const char *full_name, 
 
1456
                                CamelException *ex)
 
1457
{
 
1458
        CamelImapResponse *response;
 
1459
 
 
1460
        response = camel_imap_command (imap_store, NULL, ex, "LIST \"\" %F",
 
1461
                                       full_name);
 
1462
 
 
1463
        if (response) {
 
1464
                gboolean stillthere = response->untagged->len != 0;
 
1465
 
 
1466
                camel_imap_response_free_without_processing (imap_store, response);
 
1467
 
 
1468
                return stillthere;
 
1469
        }
 
1470
 
 
1471
        /* if the command was rejected, there must be some other error,
 
1472
           assume it worked so we dont blow away the folder unecessarily */
 
1473
        return TRUE;
 
1474
}
 
1475
 
 
1476
#if 0
 
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 */
 
1485
static void
 
1486
imap_store_refresh_folders (CamelImapStore *store, CamelException *ex)
 
1487
{
 
1488
        GPtrArray *folders;
 
1489
        int i;
 
1490
        
 
1491
        folders = camel_object_bag_list(CAMEL_STORE (store)->folders);
 
1492
        
 
1493
        for (i = 0; i <folders->len; i++) {
 
1494
                CamelFolder *folder = folders->pdata[i];
 
1495
 
 
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);
 
1499
                        continue;
 
1500
                }
 
1501
 
 
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);
 
1505
 
 
1506
                if (camel_exception_is_set (ex) &&
 
1507
                    imap_check_folder_still_extant (store, folder->full_name, ex) == FALSE) {
 
1508
                        gchar *namedup;
 
1509
                        
 
1510
                        /* the folder was deleted (may happen when we come back online
 
1511
                         * after being offline */
 
1512
                        
 
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);
 
1517
                        g_free (namedup);
 
1518
                } else
 
1519
                        camel_object_unref(folder);
 
1520
        }
 
1521
        
 
1522
        g_ptr_array_free (folders, TRUE);
 
1523
}
 
1524
#endif
 
1525
 
 
1526
static gboolean
 
1527
try_auth (CamelImapStore *store, const char *mech, CamelException *ex)
 
1528
{
 
1529
        CamelSasl *sasl;
 
1530
        CamelImapResponse *response;
 
1531
        char *resp;
 
1532
        char *sasl_resp;
 
1533
        
 
1534
        response = camel_imap_command (store, NULL, ex, "AUTHENTICATE %s", mech);
 
1535
        if (!response)
 
1536
                return FALSE;
 
1537
        
 
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);
 
1541
                if (!resp)
 
1542
                        goto lose;
 
1543
                
 
1544
                sasl_resp = camel_sasl_challenge_base64 (sasl, imap_next_word (resp), ex);
 
1545
                g_free (resp);
 
1546
                if (!sasl_resp || camel_exception_is_set (ex))
 
1547
                        goto break_and_lose;
 
1548
                
 
1549
                response = camel_imap_command_continuation (store, sasl_resp, strlen (sasl_resp), ex);
 
1550
                g_free (sasl_resp);
 
1551
                if (!response)
 
1552
                        goto lose;
 
1553
        }
 
1554
        
 
1555
        resp = camel_imap_response_extract_continuation (store, response, NULL);
 
1556
        if (resp) {
 
1557
                /* Oops. SASL claims we're done, but the IMAP server
 
1558
                 * doesn't think so...
 
1559
                 */
 
1560
                g_free (resp);
 
1561
                goto lose;
 
1562
        }
 
1563
        
 
1564
        camel_object_unref (sasl);
 
1565
        
 
1566
        return TRUE;
 
1567
        
 
1568
 break_and_lose:
 
1569
        /* Get the server out of "waiting for continuation data" mode. */
 
1570
        response = camel_imap_command_continuation (store, "*", 1, NULL);
 
1571
        if (response)
 
1572
                camel_imap_response_free (store, response);
 
1573
        
 
1574
 lose:
 
1575
        if (!camel_exception_is_set (ex)) {
 
1576
                camel_exception_set (ex, CAMEL_EXCEPTION_SERVICE_CANT_AUTHENTICATE,
 
1577
                                     _("Bad authentication response from server."));
 
1578
        }
 
1579
        
 
1580
        camel_object_unref (sasl);
 
1581
        
 
1582
        return FALSE;
 
1583
}
 
1584
 
 
1585
static gboolean
 
1586
imap_auth_loop (CamelService *service, CamelException *ex)
 
1587
{
 
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;
 
1595
 
 
1596
        /* Bugfix for #432234 */
 
1597
        if (store->capabilities & IMAP_CAPABILITY_LOGINDISABLED)
 
1598
                return TRUE;
 
1599
 
 
1600
        auth_domain = camel_url_get_param (service->url, "auth-domain");
 
1601
 
 
1602
        if (store->preauthed) 
 
1603
        {
 
1604
                if (camel_verbose_debug)
 
1605
                        fprintf(stderr, "Server %s has preauthenticated us.\n",
 
1606
                                service->url->host);
 
1607
                return TRUE;
 
1608
        }
 
1609
 
 
1610
        if (service->url->authmech) 
 
1611
        {
 
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"),
 
1616
                                        service->url->host,
 
1617
                                        service->url->authmech);
 
1618
                        return FALSE;
 
1619
                }
 
1620
 
 
1621
                authtype = camel_sasl_authtype (service->url->authmech);
 
1622
                if (!authtype) {
 
1623
                        camel_exception_setv (ex, CAMEL_EXCEPTION_SERVICE_CANT_AUTHENTICATE,
 
1624
                                              _("No support for authentication type %s"),
 
1625
                                              service->url->authmech);
 
1626
                        return FALSE;
 
1627
                }
 
1628
 
 
1629
                if (!authtype->need_password) 
 
1630
                {
 
1631
                        authenticated = try_auth (store, authtype->authproto, ex);
 
1632
                        if (!authenticated)
 
1633
                                return FALSE;
 
1634
                }
 
1635
        }
 
1636
 
 
1637
        while (!authenticated) 
 
1638
        {
 
1639
                if (errbuf) 
 
1640
                {
 
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;
 
1645
                }
 
1646
 
 
1647
                if (!service->url->passwd) 
 
1648
                {
 
1649
                        char *prompt;
 
1650
                        
 
1651
                        prompt = g_strdup_printf (_("%sPlease enter the IMAP "
 
1652
                                                  "password for %s@%s"),
 
1653
                                                errbuf ? errbuf : "",
 
1654
                                                service->url->user,
 
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);
 
1659
                        g_free (prompt);
 
1660
                        g_free (errbuf);
 
1661
                        errbuf = NULL;
 
1662
 
 
1663
                        if (!service->url->passwd) {
 
1664
                                camel_exception_set (ex, CAMEL_EXCEPTION_USER_CANCEL,
 
1665
                                                _("You did not enter a password."));
 
1666
                                return FALSE;
 
1667
                        }
 
1668
                }
 
1669
                
 
1670
                if (!store->connected) 
 
1671
                {
 
1672
                        /* Some servers (eg, courier) will disconnect on
 
1673
                         * a bad password. So reconnect here. */
 
1674
                        if (!connect_to_server_wrapper (service, ex))
 
1675
                                return FALSE;
 
1676
                }
 
1677
 
 
1678
                if (authtype)
 
1679
                        authenticated = try_auth (store, authtype->authproto, ex);
 
1680
                else {
 
1681
 
 
1682
                        if (!service->url->passwd)
 
1683
                        {
 
1684
                                camel_exception_set (ex, CAMEL_EXCEPTION_USER_CANCEL,
 
1685
                                                     _("You did not enter a password."));
 
1686
                                return FALSE;
 
1687
                        }
 
1688
 
 
1689
                        response = camel_imap_command (store, NULL, ex,
 
1690
                                                       "LOGIN %S %S",
 
1691
                                                       service->url->user,
 
1692
                                                       service->url->passwd);
 
1693
                        if (response) {
 
1694
                                camel_imap_response_free (store, response);
 
1695
                                authenticated = TRUE;
 
1696
                        }
 
1697
                }
 
1698
                if (!authenticated) {
 
1699
                        if (camel_exception_get_id(ex) == CAMEL_EXCEPTION_USER_CANCEL)
 
1700
                                return FALSE;
 
1701
                        
 
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);
 
1706
 
 
1707
                        /* Disconnect */
 
1708
                        camel_service_disconnect (service, FALSE, NULL);
 
1709
 
 
1710
                } else
 
1711
                        if (!imap_get_capability (service, ex))
 
1712
                        {
 
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);
 
1717
                                return FALSE;
 
1718
                        }
 
1719
        }
 
1720
 
 
1721
        return TRUE;
 
1722
}
 
1723
 
 
1724
static gboolean
 
1725
can_work_offline (CamelDiscoStore *disco_store)
 
1726
{
 
1727
        CamelImapStore *store = CAMEL_IMAP_STORE (disco_store);
 
1728
 
 
1729
        return camel_store_summary_count((CamelStoreSummary *)store->summary) != 0;
 
1730
}
 
1731
 
 
1732
static gboolean
 
1733
imap_connect_online (CamelService *service, CamelException *ex)
 
1734
{
 
1735
        CamelImapStore *store = CAMEL_IMAP_STORE (service);
 
1736
        CamelImapResponse *response;
 
1737
        /*struct _namespaces *namespaces;*/
 
1738
        char *result, *name;
 
1739
        size_t len;
 
1740
        CamelImapStoreNamespace *ns;
 
1741
 
 
1742
        store->going_online = TRUE;
 
1743
        store->got_online = FALSE;
 
1744
 
 
1745
        camel_operation_uncancel (NULL);
 
1746
 
 
1747
 
 
1748
        imap_debug ("imap_connect_online\n");
 
1749
 
 
1750
        let_idle_die (store, TRUE);
 
1751
 
 
1752
        CAMEL_SERVICE_REC_LOCK (store, connect_lock);
 
1753
 
 
1754
        if (!connect_to_server_wrapper (service, ex) || !imap_auth_loop (service, ex)) 
 
1755
        {
 
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;
 
1760
                return FALSE;
 
1761
        }
 
1762
 
 
1763
        /* Get namespace and hierarchy separator */
 
1764
        if ((store->capabilities & IMAP_CAPABILITY_NAMESPACE) &&
 
1765
                !(store->parameters & IMAP_PARAM_OVERRIDE_NAMESPACE)) 
 
1766
        {
 
1767
                response = camel_imap_command (store, NULL, ex, "NAMESPACE");
 
1768
                if (!response)
 
1769
                        goto done;
 
1770
 
 
1771
                result = camel_imap_response_extract (store, response, "NAMESPACE", ex);
 
1772
                if (!result)
 
1773
                        goto done;
 
1774
 
 
1775
#if 0
 
1776
                /* new code... */
 
1777
                namespaces = imap_parse_namespace_response (result);
 
1778
                imap_namespaces_destroy (namespaces);
 
1779
                /* end new code */
 
1780
#endif
 
1781
 
 
1782
                name = camel_strstrcase (result, "NAMESPACE ((");
 
1783
                if (name) 
 
1784
                {
 
1785
                        char *sep;
 
1786
                        name += 12;
 
1787
                        store->namespace = imap_parse_string ((const char **) &name, &len);
 
1788
                        if (name && *name++ == ' ') {
 
1789
                                sep = imap_parse_string ((const char **) &name, &len);
 
1790
                                if (sep) {
 
1791
                                        store->dir_sep = *sep;
 
1792
                                        g_free (sep);
 
1793
                                }
 
1794
                        }
 
1795
                }
 
1796
                g_free (result);
 
1797
        }
 
1798
 
 
1799
        if (store->namespace && strlen (store->namespace) == 0) {
 
1800
                g_free (store->namespace);
 
1801
                store->namespace = NULL;
 
1802
        }
 
1803
 
 
1804
        if (store->namespace && !store->dir_sep) {
 
1805
                if (FALSE && store->server_level >= IMAP_LEVEL_IMAP4REV1) 
 
1806
                {
 
1807
                        /* This idiom means "tell me the hierarchy separator
 
1808
                         * for the given path, even if that path doesn't exist.
 
1809
                         */
 
1810
                        response = camel_imap_command (store, NULL, ex,
 
1811
                                                       "LIST %G \"\"",
 
1812
                                                       store->namespace);
 
1813
                } else {
 
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 "").
 
1817
                         */
 
1818
                        response = camel_imap_command (store, NULL, ex,
 
1819
                                                       "LIST \"\" %G",
 
1820
                                                       store->namespace);
 
1821
                }
 
1822
 
 
1823
                if (!response)
 
1824
                        goto done;
 
1825
 
 
1826
                result = camel_imap_response_extract (store, response, "LIST", NULL);
 
1827
                if (result) 
 
1828
                {
 
1829
                        imap_parse_list_response (store, result, NULL, &store->dir_sep, NULL);
 
1830
                        g_free (result);
 
1831
                }
 
1832
        }
 
1833
 
 
1834
        if (!store->dir_sep)
 
1835
                store->dir_sep = '/';   /* Guess */
 
1836
 
 
1837
        if (!store->namespace)
 
1838
                store->namespace = g_strdup ("");
 
1839
 
 
1840
        /* canonicalize the namespace to end with dir_sep */
 
1841
        len = strlen (store->namespace);
 
1842
        if (len && store->namespace[len - 1] != store->dir_sep) 
 
1843
        {
 
1844
                gchar *tmp = g_strdup_printf ("%s%c", store->namespace, store->dir_sep);
 
1845
                g_free (store->namespace);
 
1846
                store->namespace = tmp;
 
1847
        }
 
1848
 
 
1849
        g_static_rec_mutex_lock (store->sum_lock);
 
1850
 
 
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);
 
1853
 
 
1854
        if ((store->parameters & IMAP_PARAM_SUBSCRIPTIONS)
 
1855
            && camel_store_summary_count((CamelStoreSummary *)store->summary) == 0) 
 
1856
        {
 
1857
                CamelStoreInfo *si;
 
1858
                char *pattern;
 
1859
 
 
1860
                get_folders_sync(store, store->namespace, ex);
 
1861
                if (camel_exception_is_set(ex))
 
1862
                        goto done;
 
1863
                pattern = imap_concat(store, store->namespace, "*");
 
1864
                get_folders_sync(store, pattern, ex);
 
1865
                g_free (pattern);
 
1866
                if (camel_exception_is_set(ex))
 
1867
                        goto done;
 
1868
 
 
1869
                /* Make sure INBOX is present/subscribed */
 
1870
                si = camel_store_summary_path((CamelStoreSummary *)store->summary, "INBOX");
 
1871
 
 
1872
                if (si == NULL || (si->flags & CAMEL_FOLDER_SUBSCRIBED) == 0) 
 
1873
                {
 
1874
                        response = camel_imap_command (store, NULL, ex, "SUBSCRIBE INBOX");
 
1875
                        if (response != NULL) {
 
1876
                                camel_imap_response_free (store, response);
 
1877
                        }
 
1878
                        if (si)
 
1879
                                camel_store_summary_info_free((CamelStoreSummary *)store->summary, si);
 
1880
                        if (camel_exception_is_set(ex))
 
1881
                                goto done;
 
1882
                        get_folders_sync(store, "INBOX", ex);
 
1883
                }
 
1884
 
 
1885
                store->refresh_stamp = time(0);
 
1886
        }
 
1887
 
 
1888
 done:
 
1889
 
 
1890
        /* save any changes we had */
 
1891
        camel_store_summary_save((CamelStoreSummary *)store->summary, ex);
 
1892
 
 
1893
        g_static_rec_mutex_unlock (store->sum_lock);
 
1894
 
 
1895
        CAMEL_SERVICE_REC_UNLOCK (store, connect_lock);
 
1896
        if (camel_exception_is_set (ex))
 
1897
                camel_service_disconnect (service, TRUE, NULL);
 
1898
        else
 
1899
                store->has_login = TRUE;
 
1900
 
 
1901
        store->going_online = FALSE;
 
1902
        store->got_online = !camel_exception_is_set (ex);
 
1903
 
 
1904
        return store->got_online;
 
1905
}
 
1906
 
 
1907
static gboolean
 
1908
imap_connect_offline (CamelService *service, CamelException *ex)
 
1909
{
 
1910
        CamelImapStore *store = CAMEL_IMAP_STORE (service);
 
1911
        CamelDiscoStore *disco_store = CAMEL_DISCO_STORE (service);
 
1912
 
 
1913
        imap_debug ("imap_connect_offline\n");
 
1914
 
 
1915
        /* let_idle_die (store, TRUE); */
 
1916
 
 
1917
        if (!disco_store->diary)
 
1918
                return FALSE;
 
1919
 
 
1920
        store->connected = !camel_exception_is_set (ex);
 
1921
 
 
1922
        return store->connected;
 
1923
}
 
1924
 
 
1925
static gboolean
 
1926
imap_disconnect_offline (CamelService *service, gboolean clean, CamelException *ex)
 
1927
{
 
1928
        CamelImapStore *store = CAMEL_IMAP_STORE (service);
 
1929
 
 
1930
        imap_debug ("imap_disconnect_offline\n");
 
1931
 
 
1932
        let_idle_die (store, TRUE);
 
1933
 
 
1934
        if (store->istream) {
 
1935
                camel_stream_close(store->istream);
 
1936
                camel_object_unref(store->istream);
 
1937
                store->istream = NULL;
 
1938
        }
 
1939
 
 
1940
        if (store->ostream) {
 
1941
                camel_stream_close(store->ostream);
 
1942
                camel_object_unref(store->ostream);
 
1943
                store->ostream = NULL;
 
1944
        }
 
1945
 
 
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;
 
1950
 
 
1951
        if (store->authtypes) {
 
1952
                g_hash_table_foreach_remove (store->authtypes,
 
1953
                                             free_key, NULL);
 
1954
                g_hash_table_destroy (store->authtypes);
 
1955
                store->authtypes = NULL;
 
1956
        }
 
1957
 
 
1958
        if (store->namespace && !(store->parameters & IMAP_PARAM_OVERRIDE_NAMESPACE)) {
 
1959
                g_free (store->namespace);
 
1960
                store->namespace = NULL;
 
1961
        }
 
1962
 
 
1963
        return TRUE;
 
1964
}
 
1965
 
 
1966
static gboolean
 
1967
imap_disconnect_online (CamelService *service, gboolean clean, CamelException *ex)
 
1968
{
 
1969
        CamelImapStore *store = CAMEL_IMAP_STORE (service);
 
1970
        CamelImapResponse *response;
 
1971
 
 
1972
        imap_debug ("imap_disconnect_online\n");
 
1973
 
 
1974
        let_idle_die (store, TRUE);
 
1975
 
 
1976
        if (store->connected && clean) {
 
1977
                response = camel_imap_command (store, NULL, NULL, "LOGOUT");
 
1978
                camel_imap_response_free (store, response);
 
1979
        }
 
1980
 
 
1981
        imap_disconnect_offline (service, clean, ex);
 
1982
 
 
1983
        return TRUE;
 
1984
}
 
1985
 
 
1986
 
 
1987
static gboolean
 
1988
imap_summary_is_dirty (CamelFolderSummary *summary)
 
1989
{
 
1990
        CamelImapMessageInfo *info;
 
1991
        int max, i;
 
1992
        int found = FALSE;
 
1993
 
 
1994
        max = camel_folder_summary_count (summary);
 
1995
        for (i = 0; i < max && !found; i++) {
 
1996
                info = (CamelImapMessageInfo *)camel_folder_summary_index (summary, i);
 
1997
                if (info) {
 
1998
                        found = info->info.flags & CAMEL_MESSAGE_FOLDER_FLAGGED;
 
1999
                        camel_message_info_free(info);
 
2000
                }
 
2001
        }
 
2002
 
 
2003
        return FALSE;
 
2004
}
 
2005
 
 
2006
static void
 
2007
imap_noop (CamelStore *store, CamelException *ex)
 
2008
{
 
2009
        CamelImapStore *imap_store = (CamelImapStore *) store;
 
2010
        CamelImapResponse *response;
 
2011
        CamelFolder *current_folder;
 
2012
 
 
2013
        CAMEL_SERVICE_REC_LOCK (imap_store, connect_lock);
 
2014
 
 
2015
        if (!camel_disco_store_check_online((CamelDiscoStore *)store, ex))
 
2016
                goto done;
 
2017
 
 
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);
 
2022
        } else {
 
2023
                response = camel_imap_command (imap_store, NULL, ex, "NOOP");
 
2024
                if (response)
 
2025
                        camel_imap_response_free (imap_store, response);
 
2026
        }
 
2027
done:
 
2028
        CAMEL_SERVICE_REC_UNLOCK (imap_store, connect_lock);
 
2029
}
 
2030
 
 
2031
static CamelFolder *
 
2032
imap_get_trash(CamelStore *store, CamelException *ex)
 
2033
{
 
2034
        CamelFolder *folder = CAMEL_STORE_CLASS(parent_class)->get_trash(store, ex);
 
2035
 
 
2036
        if (folder) {
 
2037
                char *state = g_build_filename(((CamelImapStore *)store)->storage_path, "system", "Trash.cmeta", NULL);
 
2038
 
 
2039
                camel_object_set(folder, NULL, CAMEL_OBJECT_STATE_FILE, state, NULL);
 
2040
                g_free(state);
 
2041
                /* no defaults? */
 
2042
                camel_object_state_read(folder);
 
2043
        }
 
2044
 
 
2045
        return folder;
 
2046
}
 
2047
 
 
2048
static CamelFolder *
 
2049
imap_get_junk(CamelStore *store, CamelException *ex)
 
2050
{
 
2051
        CamelFolder *folder = CAMEL_STORE_CLASS(parent_class)->get_junk(store, ex);
 
2052
 
 
2053
        if (folder) {
 
2054
                char *state = g_build_filename(((CamelImapStore *)store)->storage_path, "system", "Junk.cmeta", NULL);
 
2055
 
 
2056
                camel_object_set(folder, NULL, CAMEL_OBJECT_STATE_FILE, state, NULL);
 
2057
                g_free(state);
 
2058
                /* no defaults? */
 
2059
                camel_object_state_read(folder);
 
2060
        }
 
2061
 
 
2062
        return folder;
 
2063
}
 
2064
 
 
2065
static guint
 
2066
hash_folder_name (gconstpointer key)
 
2067
{
 
2068
        if (g_ascii_strcasecmp (key, "INBOX") == 0)
 
2069
                return g_str_hash ("INBOX");
 
2070
        else
 
2071
                return g_str_hash (key);
 
2072
}
 
2073
 
 
2074
static gint
 
2075
compare_folder_name (gconstpointer a, gconstpointer b)
 
2076
{
 
2077
        gconstpointer aname = a, bname = b;
 
2078
 
 
2079
        if (g_ascii_strcasecmp (a, "INBOX") == 0)
 
2080
                aname = "INBOX";
 
2081
        if (g_ascii_strcasecmp (b, "INBOX") == 0)
 
2082
                bname = "INBOX";
 
2083
        return g_str_equal (aname, bname);
 
2084
}
 
2085
 
 
2086
struct imap_status_item {
 
2087
        struct imap_status_item *next;
 
2088
        char *name;
 
2089
        guint32 value;
 
2090
};
 
2091
 
 
2092
static void
 
2093
imap_status_item_free (struct imap_status_item *items)
 
2094
{
 
2095
        struct imap_status_item *next;
 
2096
        
 
2097
        while (items != NULL) {
 
2098
                next = items->next;
 
2099
                g_free (items->name);
 
2100
                g_free (items);
 
2101
                items = next;
 
2102
        }
 
2103
}
 
2104
 
 
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)
 
2107
{
 
2108
        struct imap_status_item *items, *item, *tail;
 
2109
        CamelImapResponse *response;
 
2110
        char *status, *name, *p;
 
2111
        CamelException ex = CAMEL_EXCEPTION_INITIALISER;
 
2112
 
 
2113
        if (!(imap_store->capabilities & IMAP_CAPABILITY_STATUS))
 
2114
                return NULL;
 
2115
 
 
2116
        response = camel_imap_command (imap_store, NULL, &ex,
 
2117
                                       "STATUS %F (%s)",
 
2118
                                       folder_name,
 
2119
                                       type);
 
2120
 
 
2121
        if (!response) {
 
2122
                if (err_handle_on_fail) {
 
2123
                        CamelException ex;
 
2124
 
 
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);
 
2129
                        }
 
2130
                        camel_exception_clear (&ex);
 
2131
                }
 
2132
                return NULL;
 
2133
        }
 
2134
        
 
2135
        if (!(status = camel_imap_response_extract (imap_store, response, "STATUS", NULL)))
 
2136
                return NULL;
 
2137
        
 
2138
        p = status + strlen ("* STATUS ");
 
2139
        while (*p == ' ')
 
2140
                p++;
 
2141
        
 
2142
        /* skip past the mailbox string */
 
2143
        if (*p == '"') {
 
2144
                p++;
 
2145
                while (*p != '\0') {
 
2146
                        if (*p == '"' && p[-1] != '\\') {
 
2147
                                p++;
 
2148
                                break;
 
2149
                        }
 
2150
                        
 
2151
                        p++;
 
2152
                }
 
2153
        } else {
 
2154
                while (*p != ' ')
 
2155
                        p++;
 
2156
        }
 
2157
        
 
2158
        while (*p == ' ')
 
2159
                p++;
 
2160
        
 
2161
        if (*p++ != '(') {
 
2162
                g_free (status);
 
2163
                return NULL;
 
2164
        }
 
2165
        
 
2166
        while (*p == ' ')
 
2167
                p++;
 
2168
        
 
2169
        if (*p == ')') {
 
2170
                g_free (status);
 
2171
                return NULL;
 
2172
        }
 
2173
        
 
2174
        items = NULL;
 
2175
        tail = (struct imap_status_item *) &items;
 
2176
        
 
2177
        do {
 
2178
                name = p;
 
2179
                while (*p != ' ')
 
2180
                        p++;
 
2181
                
 
2182
                item = g_malloc (sizeof (struct imap_status_item));
 
2183
                item->next = NULL;
 
2184
                item->name = g_strndup (name, p - name);
 
2185
                item->value = strtoul (p, &p, 10);
 
2186
                
 
2187
                tail->next = item;
 
2188
                tail = item;
 
2189
                
 
2190
                while (*p == ' ')
 
2191
                        p++;
 
2192
        } while (*p != ')');
 
2193
        
 
2194
        g_free (status);
 
2195
 
 
2196
        camel_imap_store_start_idle (imap_store);
 
2197
 
 
2198
        return items;
 
2199
}
 
2200
 
 
2201
static void
 
2202
camel_imap_store_set_status_for (CamelImapStore *imap_store, const char *folder_name, guint32 messages, guint32 unseen, guint32 uidnext)
 
2203
{
 
2204
        char *storage_path, *folder_dir, *filename;
 
2205
        FILE *file = NULL;
 
2206
 
 
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);
 
2212
 
 
2213
        file = fopen (filename, "w");
 
2214
        g_free (filename);
 
2215
 
 
2216
        if (file != NULL)
 
2217
        {
 
2218
                fprintf (file, "%d %d %d", messages, unseen, uidnext);
 
2219
                fclose (file);
 
2220
        }
 
2221
 
 
2222
        return;
 
2223
}
 
2224
 
 
2225
static void 
 
2226
camel_imap_store_get_status_for (CamelImapStore *imap_store, const char *folder_name, guint32 *messages, guint32 *unseen, guint32 *uidnext)
 
2227
{
 
2228
        char *storage_path, *folder_dir, *filename;
 
2229
        FILE *file = NULL;
 
2230
        int nmessages, nunseen, nuidnext;
 
2231
 
 
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);
 
2237
 
 
2238
        file = fopen (filename, "r");
 
2239
        g_free (filename);
 
2240
 
 
2241
        if (file != NULL)
 
2242
        {
 
2243
                fscanf (file, "%i %i %i", &nmessages, &nunseen, &nuidnext);
 
2244
                *messages = nmessages; 
 
2245
                *unseen = nunseen; 
 
2246
                *uidnext = nuidnext;
 
2247
                fclose (file);
 
2248
        }
 
2249
 
 
2250
        return;
 
2251
}
 
2252
 
 
2253
static CamelMessageInfo*
 
2254
parse_message_header (char *response)
 
2255
{
 
2256
        CamelMessageInfoBase *mi = NULL;
 
2257
        char *uid = NULL, *idate = NULL;
 
2258
        size_t body_len = 0;
 
2259
        guint32 flags = 0, size = 0;
 
2260
 
 
2261
        if (*response != '(') {
 
2262
                long seq;
 
2263
                
 
2264
                if (*response != '*' || *(response + 1) != ' ')
 
2265
                        return NULL;
 
2266
                seq = strtol (response + 2, &response, 10);
 
2267
                if (seq == 0)
 
2268
                        return NULL;
 
2269
                if (g_ascii_strncasecmp (response, " FETCH (", 8) != 0)
 
2270
                        return NULL;
 
2271
                response += 7;
 
2272
        }
 
2273
        
 
2274
        do {
 
2275
                response++;
 
2276
                if (!g_ascii_strncasecmp (response, "FLAGS ", 6)) {
 
2277
                        response += 6;
 
2278
                        flags = imap_parse_flag_list (&response);
 
2279
                } else if (!g_ascii_strncasecmp (response, "RFC822.SIZE ", 12)) {
 
2280
                        response += 12;
 
2281
                        size = strtoul (response, &response, 10);
 
2282
                } else if (!g_ascii_strncasecmp (response, "BODY[", 5) ||
 
2283
                           !g_ascii_strncasecmp (response, "RFC822 ", 7)) 
 
2284
                {
 
2285
                        char *p, *body;
 
2286
                        if (*response == 'B') {
 
2287
                                response += 5;
 
2288
                                p = strchr (response, ']');
 
2289
                                if (!p || *(p + 1) != ' ')
 
2290
                                        break;
 
2291
                                response = p + 2;
 
2292
                        } else
 
2293
                                response += 7;
 
2294
 
 
2295
                        body = imap_parse_nstring ((const char **) &response, &body_len);
 
2296
                        if (!response)
 
2297
                                break;
 
2298
                        if (body)
 
2299
                        {
 
2300
                                CamelMimeMessage *msg = camel_mime_message_new ();
 
2301
                                CamelStream *stream = camel_stream_mem_new_with_buffer (body, body_len);
 
2302
                                g_free (body);
 
2303
 
 
2304
                                if (camel_data_wrapper_construct_from_stream (CAMEL_DATA_WRAPPER (msg), stream) == -1) {
 
2305
                                        camel_object_unref (CAMEL_OBJECT (msg));
 
2306
                                        break;
 
2307
                                }
 
2308
 
 
2309
                                mi = (CamelMessageInfoBase *) camel_folder_summary_info_new_from_message (NULL, msg);
 
2310
                                camel_object_unref (CAMEL_OBJECT (msg));
 
2311
                        }
 
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 == '"') {
 
2319
                                response++;
 
2320
                                len = strcspn (response, "\"");
 
2321
                                idate = g_strndup (response, len);
 
2322
                                response += len + 1;
 
2323
                        }
 
2324
                } else {
 
2325
                        g_warning ("Unexpected FETCH response from server: (%s", response);
 
2326
                        break;
 
2327
                }
 
2328
        } while (response && *response != ')');
 
2329
 
 
2330
        if (mi)
 
2331
        {
 
2332
                mi->flags |= flags;
 
2333
                if (uid) {
 
2334
                        if (mi->uid)
 
2335
                                g_free (mi->uid);
 
2336
                        mi->uid = uid;
 
2337
                }
 
2338
                if (idate) {
 
2339
                        mi->date_received = decode_internaldate ((const unsigned char *) idate);
 
2340
                        g_free (idate);
 
2341
                }
 
2342
                mi->size = size;
 
2343
        }
 
2344
 
 
2345
        return (CamelMessageInfo *) mi;
 
2346
}
 
2347
 
 
2348
GPtrArray*
 
2349
_camel_imap_store_get_recent_messages (CamelImapStore *imap_store, const char *folder_name, int *unseen, int *messages, gboolean withthem)
 
2350
{
 
2351
        guint32 uidnext=-1;
 
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;
 
2358
 
 
2359
        if (!camel_disco_store_check_online (CAMEL_DISCO_STORE (imap_store), &ex))
 
2360
                return NULL;
 
2361
 
 
2362
/*
 
2363
      Example:    C: A042 STATUS blurdybloop (UIDNEXT MESSAGES)
 
2364
                  S: * STATUS blurdybloop (MESSAGES 231 UIDNEXT 44292)
 
2365
                  S: A042 OK STATUS completed
 
2366
*/
 
2367
 
 
2368
        camel_operation_uncancel (NULL);
 
2369
 
 
2370
 
 
2371
        /* On for example courier, the selected's STATUS is cached (kill that cache) */
 
2372
        if (withthem)
 
2373
        {
 
2374
            response = camel_imap_command (imap_store, NULL, &tex,
 
2375
                            "SELECT"/*, folder_name*/);
 
2376
 
 
2377
printf ("SELECT %s\n", folder_name);
 
2378
 
 
2379
            if (response)
 
2380
                    camel_imap_response_free (imap_store, response);
 
2381
        }
 
2382
 
 
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;
 
2391
                item = item->next;
 
2392
        }
 
2393
        imap_status_item_free (items);
 
2394
printf ("%d %d %d\n", *messages, *unseen, uidnext);
 
2395
 
 
2396
        if (withthem)
 
2397
        {
 
2398
                camel_imap_store_get_status_for (imap_store, folder_name, &omessages, &ounseen, &ouidnext);
 
2399
 
 
2400
                if (ouidnext != uidnext)
 
2401
                {
 
2402
                        CamelImapResponseType type;
 
2403
                        char *resp; 
 
2404
 
 
2405
                        camel_exception_clear (&tex);
 
2406
 
 
2407
                        if (!camel_imap_command_start (imap_store, NULL, &tex,
 
2408
                                "UID FETCH %d:* (FLAGS RFC822.SIZE INTERNALDATE BODY.PEEK[HEADER])", ouidnext-1))
 
2409
                                goto done;
 
2410
 
 
2411
                        while ((type = camel_imap_command_response (imap_store, &resp, &tex))
 
2412
                                == CAMEL_IMAP_RESPONSE_UNTAGGED) 
 
2413
                        {
 
2414
                           if (resp)
 
2415
                           {
 
2416
                                CamelMessageInfo *mi = parse_message_header (resp);
 
2417
                                g_free (resp);
 
2418
 
 
2419
                                if (mi)
 
2420
                                {
 
2421
                                        if (retval == NULL)
 
2422
                                                retval = g_ptr_array_new ();
 
2423
                                        g_ptr_array_add (retval, mi);
 
2424
                                }
 
2425
                           }
 
2426
                        }
 
2427
 
 
2428
                        /* Restore the original folder selection */
 
2429
                        if (imap_store->current_folder != NULL && imap_store->current_folder->full_name != NULL)
 
2430
                        {
 
2431
                                response = camel_imap_command (imap_store, NULL, &tex,
 
2432
                                                "SELECT %F", imap_store->current_folder->full_name);
 
2433
                                if (response)
 
2434
                                        camel_imap_response_free (imap_store, response);
 
2435
                        }
 
2436
                }
 
2437
        }
 
2438
 
 
2439
done:
 
2440
 
 
2441
        camel_imap_store_set_status_for (imap_store, folder_name, *messages, *unseen, uidnext);
 
2442
 
 
2443
        return retval;
 
2444
}
 
2445
 
 
2446
static void
 
2447
imap_get_folder_status (CamelStore *store, const char *folder_name, int *unseen, int *messages, int *uidnext)
 
2448
{
 
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;
 
2459
 
 
2460
        camel_file_util_read_counts_2 (spath, &mversion, &mflags, &mnextuid,
 
2461
                &mtime, &msaved_count, &munread_count, &mdeleted_count,
 
2462
                &mjunk_count);
 
2463
 
 
2464
        if (munread_count != -1)
 
2465
                *unseen = munread_count;
 
2466
        if (msaved_count != -1)
 
2467
                *messages = msaved_count;
 
2468
        if (mnextuid != -1)
 
2469
                *uidnext = mnextuid;
 
2470
 
 
2471
        g_free (spath);
 
2472
        g_free (storage_path);
 
2473
        g_free (folder_dir);
 
2474
 
 
2475
        if (!camel_disco_store_check_online (CAMEL_DISCO_STORE (imap_store), &ex))
 
2476
                return;
 
2477
 
 
2478
        CAMEL_SERVICE_REC_LOCK (imap_store, connect_lock);
 
2479
 
 
2480
        /*
 
2481
         *  Example: C: A042 STATUS blurdybloop (UIDNEXT MESSAGES)
 
2482
         *           S: * STATUS blurdybloop (MESSAGES 231 UIDNEXT 44292)
 
2483
         *           S: A042 OK STATUS completed */
 
2484
 
 
2485
        item = items = get_folder_status (imap_store, folder_name, "MESSAGES UNSEEN UIDNEXT", FALSE);
 
2486
 
 
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)
 
2490
                        *unseen = 0; 
 
2491
                if (mnextuid == -1)
 
2492
                        *uidnext = 0;
 
2493
        }
 
2494
 
 
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;
 
2501
                }
 
2502
                if (!g_ascii_strcasecmp (item->name, "UIDNEXT"))
 
2503
                        *uidnext = item->value;
 
2504
                item = item->next;
 
2505
        }
 
2506
        imap_status_item_free (items);
 
2507
 
 
2508
        CAMEL_SERVICE_REC_UNLOCK (imap_store, connect_lock);
 
2509
 
 
2510
        return;
 
2511
}
 
2512
 
 
2513
static CamelFolder *
 
2514
get_folder_online (CamelStore *store, const char *folder_name, guint32 flags, CamelException *ex)
 
2515
{
 
2516
        CamelImapStore *imap_store = CAMEL_IMAP_STORE (store);
 
2517
        CamelImapResponse *response;
 
2518
        CamelFolder *new_folder;
 
2519
        char *folder_dir, *storage_path;
 
2520
 
 
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);
 
2524
        if (new_folder)
 
2525
                return new_folder;
 
2526
        camel_exception_clear(ex);
 
2527
 
 
2528
        CAMEL_SERVICE_REC_LOCK(imap_store, connect_lock);
 
2529
 
 
2530
        if (!camel_disco_store_check_online((CamelDiscoStore *)imap_store, ex)) {
 
2531
                CAMEL_SERVICE_REC_UNLOCK (imap_store, connect_lock);
 
2532
                return NULL;
 
2533
        }
 
2534
        
 
2535
        if (!g_ascii_strcasecmp (folder_name, "INBOX"))
 
2536
                folder_name = "INBOX";
 
2537
 
 
2538
        if (imap_store->current_folder) {
 
2539
                /* camel_object_unref (imap_store->current_folder); */
 
2540
                imap_store->current_folder = NULL;
 
2541
        }
 
2542
 
 
2543
        response = camel_imap_command (imap_store, NULL, ex, "SELECT %F", folder_name);
 
2544
        if (!response) {
 
2545
                char *folder_real, *parent_name, *parent_real;
 
2546
                const char *c;
 
2547
                
 
2548
                if (camel_exception_get_id(ex) == CAMEL_EXCEPTION_USER_CANCEL) {
 
2549
                        CAMEL_SERVICE_REC_UNLOCK (imap_store, connect_lock);
 
2550
                        return NULL;
 
2551
                }
 
2552
                
 
2553
                camel_exception_clear (ex);
 
2554
                
 
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);
 
2559
                        return NULL;
 
2560
                }
 
2561
                
 
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))
 
2565
                        c++;
 
2566
                
 
2567
                if (*c != '\0') {
 
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\""),
 
2571
                                              folder_name, *c);
 
2572
                        return NULL;
 
2573
                }
 
2574
 
 
2575
                if (parent_name) {
 
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);
 
2578
                } else {
 
2579
                        parent_real = NULL;
 
2580
                }
 
2581
                
 
2582
                if (parent_real != NULL) {
 
2583
                        gboolean need_convert = FALSE;
 
2584
                        char *resp, *thisone;
 
2585
                        guint32 flags;
 
2586
                        int i;
 
2587
                        
 
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);
 
2592
                                return NULL;
 
2593
                        }
 
2594
                        
 
2595
                        /* FIXME: does not handle unexpected circumstances very well */
 
2596
                        for (i = 0; i < response->untagged->len; i++) {
 
2597
                                resp = response->untagged->pdata[i];
 
2598
                                
 
2599
                                if (!imap_parse_list_response (imap_store, resp, (int *) &flags, NULL, &thisone))
 
2600
                                        continue;
 
2601
                                
 
2602
                                if (!strcmp (parent_name, thisone)) {
 
2603
                                        if (flags & CAMEL_FOLDER_NOINFERIORS)
 
2604
                                                need_convert = TRUE;
 
2605
                                }
 
2606
                                
 
2607
                                g_free (thisone);
 
2608
                        }
 
2609
                        
 
2610
                        camel_imap_response_free (imap_store, response);
 
2611
                        
 
2612
                        /* if not, check if we can delete it and recreate it */
 
2613
                        if (need_convert) {
 
2614
                                struct imap_status_item *items, *item;
 
2615
                                guint32 messages = 0;
 
2616
                                CamelException lex;
 
2617
                                char *name;
 
2618
                                
 
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;
 
2623
                                                break;
 
2624
                                        }
 
2625
                                        
 
2626
                                        item = item->next;
 
2627
                                }
 
2628
                                
 
2629
                                imap_status_item_free (items);
 
2630
                                
 
2631
                                if (messages > 0) {
 
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);
 
2637
                                        return NULL;
 
2638
                                }
 
2639
                                
 
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);
 
2648
                                        return NULL;
 
2649
                                }
 
2650
                                
 
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",
 
2654
                                                               name);
 
2655
                                g_free (name);
 
2656
                                
 
2657
                                if (!response) {
 
2658
                                        CAMEL_SERVICE_REC_UNLOCK (imap_store, connect_lock);
 
2659
                                        g_free (parent_name);
 
2660
                                        g_free (parent_real);
 
2661
                                        return NULL;
 
2662
                                } else
 
2663
                                        camel_imap_response_free (imap_store, response);
 
2664
                        }
 
2665
                        
 
2666
                        g_free (parent_real);
 
2667
                }
 
2668
                
 
2669
                g_free (parent_name);
 
2670
 
 
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);
 
2673
                if (response) {
 
2674
                        camel_imap_store_summary_add_from_full(imap_store->summary, folder_real, imap_store->dir_sep);
 
2675
 
 
2676
                        camel_imap_response_free (imap_store, response);
 
2677
                        
 
2678
                        response = camel_imap_command (imap_store, NULL, NULL, "SELECT %F", folder_name);
 
2679
                }
 
2680
                g_free(folder_real);
 
2681
                if (!response) {
 
2682
                        CAMEL_SERVICE_REC_UNLOCK (imap_store, connect_lock);
 
2683
                        return NULL;
 
2684
                }
 
2685
        } else if (flags & CAMEL_STORE_FOLDER_EXCL) {
 
2686
                camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
 
2687
                                      _("Cannot create folder `%s': folder exists."),
 
2688
                                      folder_name);
 
2689
                
 
2690
                camel_imap_response_free_without_processing (imap_store, response);
 
2691
                
 
2692
                CAMEL_SERVICE_REC_UNLOCK (imap_store, connect_lock);
 
2693
                
 
2694
                return NULL;
 
2695
        }
 
2696
 
 
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);
 
2702
        if (new_folder) {
 
2703
                CamelException local_ex;
 
2704
 
 
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);
 
2709
 
 
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);
 
2715
                        new_folder = NULL;
 
2716
                }
 
2717
        }
 
2718
        camel_imap_response_free_without_processing (imap_store, response);
 
2719
        
 
2720
        CAMEL_SERVICE_REC_UNLOCK (imap_store, connect_lock);
 
2721
        
 
2722
        return new_folder;
 
2723
}
 
2724
 
 
2725
static CamelFolder *
 
2726
get_folder_offline (CamelStore *store, const char *folder_name,
 
2727
                    guint32 flags, CamelException *ex)
 
2728
{
 
2729
        CamelImapStore *imap_store = CAMEL_IMAP_STORE (store);
 
2730
        CamelFolder *new_folder = NULL;
 
2731
        CamelStoreInfo *si;
 
2732
 
 
2733
        if (!g_ascii_strcasecmp (folder_name, "INBOX"))
 
2734
                folder_name = "INBOX";
 
2735
 
 
2736
        si = camel_store_summary_path((CamelStoreSummary *)imap_store->summary, folder_name);
 
2737
        if (si) {
 
2738
                char *folder_dir, *storage_path;
 
2739
 
 
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);
 
2744
                g_free(folder_dir);
 
2745
 
 
2746
                camel_store_summary_info_free((CamelStoreSummary *)imap_store->summary, si);
 
2747
        } else {
 
2748
                camel_exception_setv (ex, CAMEL_EXCEPTION_STORE_NO_FOLDER,
 
2749
                                      _("No such folder %s"), folder_name);
 
2750
        }
 
2751
        
 
2752
        return new_folder;
 
2753
}
 
2754
 
 
2755
static void
 
2756
delete_folder (CamelStore *store, const char *folder_name, CamelException *ex)
 
2757
{
 
2758
        CamelImapStore *imap_store = CAMEL_IMAP_STORE (store);
 
2759
        CamelImapResponse *response;
 
2760
 
 
2761
        CAMEL_SERVICE_REC_LOCK (imap_store, connect_lock);
 
2762
 
 
2763
        if (!camel_disco_store_check_online((CamelDiscoStore *)imap_store, ex))
 
2764
                goto fail;
 
2765
 
 
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");
 
2769
        if (!response)
 
2770
                goto fail;
 
2771
 
 
2772
        camel_imap_response_free_without_processing (imap_store, response);
 
2773
        /*if (imap_store->current_folder)
 
2774
                camel_object_unref (imap_store->current_folder);*/
 
2775
 
 
2776
        /* no need to actually create a CamelFolder for INBOX */
 
2777
        imap_store->current_folder = NULL;
 
2778
 
 
2779
        response = camel_imap_command(imap_store, NULL, ex, "DELETE %F", folder_name);
 
2780
        if (response) {
 
2781
                camel_imap_response_free (imap_store, response);
 
2782
                imap_forget_folder (imap_store, folder_name, ex);
 
2783
        }
 
2784
fail:
 
2785
        CAMEL_SERVICE_REC_UNLOCK(imap_store, connect_lock);
 
2786
}
 
2787
 
 
2788
static void
 
2789
manage_subscriptions (CamelStore *store, const char *old_name, gboolean subscribe)
 
2790
{
 
2791
        CamelImapStore *imap_store = CAMEL_IMAP_STORE (store);
 
2792
        CamelStoreInfo *si;
 
2793
        int olen = strlen(old_name);
 
2794
        const char *path;
 
2795
        int i, count;
 
2796
 
 
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);
 
2800
                if (si) {
 
2801
                        path = camel_store_info_path(imap_store->summary, si);
 
2802
                        if (strncmp(path, old_name, olen) == 0) {
 
2803
                                if (subscribe)
 
2804
                                        subscribe_folder(store, path, NULL);
 
2805
                                else
 
2806
                                        unsubscribe_folder(store, path, NULL);
 
2807
                        }
 
2808
                        camel_store_summary_info_free((CamelStoreSummary *)imap_store->summary, si);
 
2809
                }
 
2810
        }
 
2811
}
 
2812
 
 
2813
static void
 
2814
rename_folder_info (CamelImapStore *imap_store, const char *old_name, const char *new_name)
 
2815
{
 
2816
        int i, count;
 
2817
        CamelStoreInfo *si;
 
2818
        int olen = strlen(old_name);
 
2819
        const char *path;
 
2820
        char *npath, *nfull;
 
2821
 
 
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);
 
2825
                if (si == NULL)
 
2826
                        continue;
 
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);
 
2831
                        else
 
2832
                                npath = g_strdup(new_name);
 
2833
                        nfull = camel_imap_store_summary_path_to_full(imap_store->summary, npath, imap_store->dir_sep);
 
2834
                        
 
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;
 
2839
 
 
2840
                                /* TNY: I'm not sure about this one! */
 
2841
 
 
2842
                                response = camel_imap_command (imap_store, NULL, NULL, "RENAME %F %G", path, nfull);
 
2843
                                if (response)
 
2844
                                        camel_imap_response_free (imap_store, response);
 
2845
                        }
 
2846
 
 
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);
 
2849
 
 
2850
                        camel_store_summary_touch((CamelStoreSummary *)imap_store->summary);
 
2851
                        g_free(nfull);
 
2852
                        g_free(npath);
 
2853
                }
 
2854
                camel_store_summary_info_free((CamelStoreSummary *)imap_store->summary, si);
 
2855
        }
 
2856
}
 
2857
 
 
2858
static int
 
2859
make_path (char *path, int nmode, int parent_mode)
 
2860
{
 
2861
  int oumask;
 
2862
  struct stat sb;
 
2863
  char *p, *npath;
 
2864
 
 
2865
  if (stat (path, &sb) == 0)
 
2866
    {
 
2867
      if (S_ISDIR (sb.st_mode) == 0)
 
2868
          return 1;
 
2869
      if (chmod (path, nmode))
 
2870
          return 1;
 
2871
      return 0;
 
2872
    }
 
2873
 
 
2874
  oumask = umask (0);
 
2875
  npath = g_strdup (path);      /* So we can write to it. */
 
2876
    
 
2877
  /* Check whether or not we need to do anything with intermediate dirs. */
 
2878
 
 
2879
  /* Skip leading slashes. */
 
2880
  p = npath;
 
2881
  while (*p == '/')
 
2882
    p++;
 
2883
 
 
2884
  while ((p = strchr (p, '/')))
 
2885
    {
 
2886
      *p = '\0';
 
2887
      if (stat (npath, &sb) != 0)
 
2888
        {
 
2889
          if (mkdir (npath, parent_mode))
 
2890
            {
 
2891
              free (npath);
 
2892
              return 1;
 
2893
            }
 
2894
        }
 
2895
      else if (S_ISDIR (sb.st_mode) == 0)
 
2896
        {
 
2897
          free (npath);
 
2898
          return 1;
 
2899
        }
 
2900
 
 
2901
      *p++ = '/';       /* restore slash */
 
2902
      while (*p == '/')
 
2903
        p++;
 
2904
    }
 
2905
 
 
2906
  /* Create the final directory component. */
 
2907
  if (stat (npath, &sb) && mkdir (npath, nmode))
 
2908
    {
 
2909
      free (npath);
 
2910
      return 1;
 
2911
    }
 
2912
 
 
2913
  free (npath);
 
2914
  return 0;
 
2915
}
 
2916
 
 
2917
 
 
2918
static void
 
2919
rename_folder (CamelStore *store, const char *old_name, const char *new_name_in, CamelException *ex)
 
2920
{
 
2921
        CamelImapStore *imap_store = CAMEL_IMAP_STORE (store);
 
2922
        CamelImapResponse *response;
 
2923
        char *oldpath, *newpath, *storage_path;
 
2924
        char *tpath, *lslash;
 
2925
 
 
2926
        CAMEL_SERVICE_REC_LOCK (imap_store, connect_lock);
 
2927
 
 
2928
        camel_operation_start (NULL, "Renaming folder");
 
2929
 
 
2930
        if (!camel_disco_store_check_online((CamelDiscoStore *)imap_store, ex))
 
2931
                goto fail;
 
2932
        
 
2933
        /* Undefined progress */
 
2934
        camel_operation_progress(NULL, 0, 0);
 
2935
 
 
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");
 
2940
        if (!response)
 
2941
                goto fail;
 
2942
 
 
2943
        /* Undefined progress */
 
2944
        camel_operation_progress(NULL, 0, 0);
 
2945
 
 
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;
 
2951
 
 
2952
        /* Undefined progress */
 
2953
        camel_operation_progress(NULL, 0, 0);
 
2954
 
 
2955
        imap_store->renaming = TRUE;
 
2956
        if (imap_store->parameters & IMAP_PARAM_SUBSCRIPTIONS)
 
2957
                manage_subscriptions(store, old_name, FALSE);
 
2958
 
 
2959
        response = camel_imap_command (imap_store, NULL, ex, "RENAME %F %F", old_name, new_name_in);
 
2960
        if (!response) {
 
2961
                if (imap_store->parameters & IMAP_PARAM_SUBSCRIPTIONS)
 
2962
                        manage_subscriptions(store, old_name, TRUE);
 
2963
                goto fail;
 
2964
        }
 
2965
        
 
2966
        /* Undefined progress */
 
2967
        camel_operation_progress(NULL, 0, 0);
 
2968
 
 
2969
        camel_imap_response_free (imap_store, response);
 
2970
 
 
2971
        /* rename summary, and handle broken server */
 
2972
        rename_folder_info(imap_store, old_name, new_name_in);
 
2973
 
 
2974
        if (imap_store->parameters & IMAP_PARAM_SUBSCRIPTIONS)
 
2975
                manage_subscriptions(store, new_name_in, TRUE);
 
2976
 
 
2977
        /* Undefined progress */
 
2978
        camel_operation_progress(NULL, 0, 0);
 
2979
 
 
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);
 
2984
 
 
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, '/');
 
2989
        if (lslash) {
 
2990
                lslash = '\0';
 
2991
                make_path (tpath, S_IRWXU, S_IRWXU);
 
2992
        }
 
2993
        g_free (tpath);
 
2994
 
 
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));
 
2998
        }
 
2999
        
 
3000
        g_free (oldpath);
 
3001
        g_free (newpath);
 
3002
fail:
 
3003
        imap_store->renaming = FALSE;
 
3004
        CAMEL_SERVICE_REC_UNLOCK(imap_store, connect_lock);
 
3005
        camel_operation_end (NULL);
 
3006
}
 
3007
 
 
3008
static CamelFolderInfo *
 
3009
create_folder (CamelStore *store, const char *parent_name,
 
3010
               const char *folder_name, CamelException *ex)
 
3011
{
 
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;
 
3018
        int i = 0, flags;
 
3019
        const char *c;
 
3020
        
 
3021
        if (!camel_disco_store_check_online (CAMEL_DISCO_STORE (store), ex))
 
3022
                return NULL;
 
3023
 
 
3024
        if (!parent_name)
 
3025
                parent_name = "";
 
3026
        
 
3027
        c = folder_name;
 
3028
        while (*c && *c != imap_store->dir_sep && !strchr ("#%*", *c))
 
3029
                c++;
 
3030
        
 
3031
        if (*c != '\0') {
 
3032
                camel_exception_setv (ex, CAMEL_EXCEPTION_FOLDER_INVALID_PATH,
 
3033
                                      _("The folder name \"%s\" is invalid because it contains the character \"%c\""),
 
3034
                                      folder_name, *c);
 
3035
                return NULL;
 
3036
        }
 
3037
        
 
3038
        /* check if the parent allows inferiors */
 
3039
 
 
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);
 
3045
                return NULL;
 
3046
        }
 
3047
 
 
3048
        need_convert = FALSE;
 
3049
        response = camel_imap_command (imap_store, NULL, ex, "LIST \"\" %G",
 
3050
                                       parent_real);
 
3051
        if (!response) /* whoa, this is bad */ {
 
3052
                g_free(parent_real);
 
3053
                return NULL;
 
3054
        }
 
3055
        
 
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];
 
3059
                
 
3060
                if (!imap_parse_list_response (imap_store, resp, &flags, NULL, &thisone))
 
3061
                        continue;
 
3062
                
 
3063
                if (strcmp (thisone, parent_name) == 0) {
 
3064
                        if (flags & CAMEL_FOLDER_NOINFERIORS)
 
3065
                                need_convert = TRUE;
 
3066
                }
 
3067
 
 
3068
                g_free(thisone);
 
3069
        }
 
3070
        
 
3071
        camel_imap_response_free (imap_store, response);
 
3072
        
 
3073
        camel_exception_init (&internal_ex);
 
3074
        
 
3075
        /* if not, check if we can delete it and recreate it */
 
3076
        if (need_convert) {
 
3077
                struct imap_status_item *items, *item;
 
3078
                guint32 messages = 0;
 
3079
                char *name;
 
3080
                
 
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;
 
3085
                                break;
 
3086
                        }
 
3087
                        
 
3088
                        item = item->next;
 
3089
                }
 
3090
                
 
3091
                imap_status_item_free (items);
 
3092
                
 
3093
                if (messages > 0) {
 
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);
 
3097
                        return NULL;
 
3098
                }
 
3099
                
 
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);
 
3104
                        return NULL;
 
3105
                }
 
3106
                
 
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",
 
3110
                                               name);
 
3111
                g_free (name);
 
3112
                
 
3113
                if (!response) {
 
3114
                        g_free(parent_real);
 
3115
                        return NULL;
 
3116
                } else
 
3117
                        camel_imap_response_free (imap_store, response);
 
3118
 
 
3119
                root = imap_build_folder_info(imap_store, parent_name);
 
3120
        }
 
3121
        
 
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);
 
3125
        g_free(real_name);
 
3126
 
 
3127
        camel_imap_store_stop_idle (imap_store);
 
3128
        response = camel_imap_command (imap_store, NULL, ex, "CREATE %G", full_name);
 
3129
        
 
3130
        if (response) {
 
3131
                CamelImapStoreInfo *si;
 
3132
                CamelFolderInfo *fi;
 
3133
 
 
3134
                camel_imap_response_free (imap_store, response);
 
3135
 
 
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;
 
3140
                if (root) {
 
3141
                        root->child = fi;
 
3142
                        fi->parent = root;
 
3143
                } else {
 
3144
                        root = fi;
 
3145
                }
 
3146
                camel_object_trigger_event (CAMEL_OBJECT (store), "folder_created", root);
 
3147
        } else if (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);
 
3151
                root = NULL;
 
3152
        }
 
3153
 
 
3154
        g_free (full_name);
 
3155
        g_free(parent_real);
 
3156
        
 
3157
        return root;
 
3158
}
 
3159
 
 
3160
static CamelFolderInfo *
 
3161
parse_list_response_as_folder_info (CamelImapStore *imap_store,
 
3162
                                    const char *response)
 
3163
{
 
3164
        CamelFolderInfo *fi;
 
3165
        int flags;
 
3166
        char sep, *dir, *path;
 
3167
        CamelURL *url;
 
3168
        CamelImapStoreInfo *si;
 
3169
        guint32 newflags;
 
3170
 
 
3171
        if (!imap_parse_list_response (imap_store, response, &flags, &sep, &dir))
 
3172
                return NULL;
 
3173
 
 
3174
        /* FIXME: should use imap_build_folder_info, note the differences with param setting tho */
 
3175
 
 
3176
        si = camel_imap_store_summary_add_from_full(imap_store->summary, dir, sep?sep:'/');
 
3177
        g_free(dir);
 
3178
        if (si == NULL)
 
3179
                return NULL;
 
3180
 
 
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);
 
3185
        }
 
3186
 
 
3187
        flags = (flags & ~CAMEL_FOLDER_SUBSCRIBED) | (si->info.flags & CAMEL_STORE_FOLDER_INFO_SUBSCRIBED);
 
3188
 
 
3189
        fi = camel_folder_info_new ();
 
3190
        
 
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"));
 
3195
        } else 
 
3196
                fi->name = g_strdup(camel_store_info_name(imap_store->summary, si));
 
3197
        
 
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;
 
3203
        fi->flags = flags;
 
3204
        
 
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);
 
3209
 
 
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);
 
3214
 
 
3215
        return fi;
 
3216
}
 
3217
 
 
3218
static int imap_match_pattern(char dir_sep, const char *pattern, const char *name)
 
3219
{
 
3220
        char p, n;
 
3221
 
 
3222
        p = *pattern++;
 
3223
        n = *name++;
 
3224
        while (n && p) {
 
3225
                if (n == p) {
 
3226
                        p = *pattern++;
 
3227
                        n = *name++;
 
3228
                } else if (p == '%') {
 
3229
                        if (n != dir_sep) {
 
3230
                                n = *name++;
 
3231
                        } else {
 
3232
                                p = *pattern++;
 
3233
                        }
 
3234
                } else if (p == '*') {
 
3235
                        return TRUE;
 
3236
                } else
 
3237
                        return FALSE;
 
3238
        }
 
3239
 
 
3240
        return n == 0 && (p == '%' || p == 0);
 
3241
}
 
3242
 
 
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)
 
3246
{
 
3247
        const char *a = ap;
 
3248
 
 
3249
        if (g_ascii_strcasecmp(a, "INBOX") == 0)
 
3250
                a = "INBOX";
 
3251
 
 
3252
        return g_str_hash(a);
 
3253
}
 
3254
 
 
3255
static int folder_eq(const void *ap, const void *bp)
 
3256
{
 
3257
        const char *a = ap;
 
3258
        const char *b = bp;
 
3259
 
 
3260
        if (g_ascii_strcasecmp(a, "INBOX") == 0)
 
3261
                a = "INBOX";
 
3262
        if (g_ascii_strcasecmp(b, "INBOX") == 0)
 
3263
                b = "INBOX";
 
3264
 
 
3265
        return g_str_equal(a, b);
 
3266
}
 
3267
 
 
3268
static void
 
3269
get_folders_free(void *k, void *v, void *d)
 
3270
{
 
3271
        camel_folder_info_free(v);
 
3272
}
 
3273
 
 
3274
static void
 
3275
get_folders_sync(CamelImapStore *imap_store, const char *pattern, CamelException *ex)
 
3276
{
 
3277
        CamelImapResponse *response;
 
3278
        CamelFolderInfo *fi, *hfi;
 
3279
        char *list;
 
3280
        int i, count, j;
 
3281
        GHashTable *present;
 
3282
        CamelStoreInfo *si;
 
3283
 
 
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 */
 
3286
 
 
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) */
 
3289
 
 
3290
        present = g_hash_table_new(folder_hash, folder_eq);
 
3291
 
 
3292
        for (j=0;j<2;j++) 
 
3293
        {
 
3294
                camel_imap_store_stop_idle (imap_store);
 
3295
                response = camel_imap_command (imap_store, NULL, ex,
 
3296
                                               "%s \"\" %G", j==1 ? "LSUB" : "LIST",
 
3297
                                               pattern);
 
3298
                if (!response)
 
3299
                        goto fail;
 
3300
 
 
3301
                for (i = 0; i < response->untagged->len; i++) 
 
3302
                {
 
3303
                        list = response->untagged->pdata[i];
 
3304
                        fi = parse_list_response_as_folder_info (imap_store, list);
 
3305
 
 
3306
                        if (fi) 
 
3307
                        {
 
3308
                                if (FALSE && j == 0)
 
3309
                                {
 
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) 
 
3313
                                        {
 
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;
 
3318
                                                item = item->next;
 
3319
                                        }
 
3320
                                        imap_status_item_free (items);
 
3321
                                }
 
3322
 
 
3323
                                hfi = g_hash_table_lookup(present, fi->full_name);
 
3324
 
 
3325
                                if (hfi == NULL) 
 
3326
                                {
 
3327
                                        if (j == 1) 
 
3328
                                        {
 
3329
                                                /* It's in LSUB but not in LIST? */
 
3330
 
 
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;
 
3334
                                        }
 
3335
 
 
3336
                                        if (j == 0) /* From the LSUB we don't add folders */
 
3337
                                                g_hash_table_insert(present, fi->full_name, fi);
 
3338
                                        else 
 
3339
                                                camel_folder_info_free(fi);
 
3340
                                } else {
 
3341
                                        if (j == 1)
 
3342
                                                hfi->flags |= CAMEL_STORE_INFO_FOLDER_SUBSCRIBED;
 
3343
 
 
3344
                                        if (j == 0) 
 
3345
                                        {
 
3346
                                                hfi->unread = fi->unread;
 
3347
                                                hfi->total = fi->total;
 
3348
                                        }
 
3349
                                        camel_folder_info_free(fi);
 
3350
                                }
 
3351
                        }
 
3352
                }
 
3353
                camel_imap_response_free (imap_store, response);
 
3354
        }
 
3355
 
 
3356
        /* Sync summary to match */
 
3357
 
 
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);
 
3360
 
 
3361
        for (i=0;i<count;i++) 
 
3362
        {
 
3363
                si = camel_store_summary_index((CamelStoreSummary *)imap_store->summary, i);
 
3364
                if (si == NULL)
 
3365
                        continue;
 
3366
 
 
3367
                if (imap_match_pattern(imap_store->dir_sep, pattern, camel_imap_store_info_full_name(imap_store->summary, si))) 
 
3368
                {
 
3369
                        if ((fi = g_hash_table_lookup(present, camel_store_info_path(imap_store->summary, si))) != NULL) 
 
3370
                        {
 
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);
 
3377
                        } else {
 
3378
                                camel_store_summary_remove((CamelStoreSummary *)imap_store->summary, si);
 
3379
                                camel_store_summary_save((CamelStoreSummary *)imap_store->summary, ex);
 
3380
                                count--;
 
3381
                                i--;
 
3382
                        }
 
3383
                }
 
3384
                camel_store_summary_info_free((CamelStoreSummary *)imap_store->summary, si);
 
3385
        }
 
3386
fail:
 
3387
        g_hash_table_foreach(present, get_folders_free, NULL);
 
3388
        g_hash_table_destroy(present);
 
3389
}
 
3390
 
 
3391
#if 0
 
3392
static void
 
3393
dumpfi(CamelFolderInfo *fi)
 
3394
{
 
3395
        int depth;
 
3396
        CamelFolderInfo *n = fi;
 
3397
 
 
3398
        if (fi == NULL)
 
3399
                return;
 
3400
 
 
3401
        depth = 0;
 
3402
        while (n->parent) {
 
3403
                depth++;
 
3404
                n = n->parent;
 
3405
        }
 
3406
 
 
3407
        while (fi) {
 
3408
                printf("%-40s %-30s %*s\n", fi->path, fi->full_name, depth*2+strlen(fi->url), fi->url);
 
3409
                if (fi->child)
 
3410
                        dumpfi(fi->child);
 
3411
                fi = fi->sibling;
 
3412
        }
 
3413
}
 
3414
#endif
 
3415
 
 
3416
 
 
3417
 
 
3418
 
 
3419
static void
 
3420
fill_fi(CamelStore *store, CamelFolderInfo *fi, guint32 flags)
 
3421
{
 
3422
        CamelFolder *folder;
 
3423
        CamelImapStore *imap_store = (CamelImapStore *) store;
 
3424
        gint msize = 0;
 
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);
 
3427
 
 
3428
        g_free(storage_path);
 
3429
        camel_du (folder_dir, &msize);
 
3430
 
 
3431
        folder = camel_object_bag_peek(store->folders, fi->full_name);
 
3432
 
 
3433
        if (folder) 
 
3434
        {
 
3435
                fi->unread = camel_folder_get_unread_message_count(folder);
 
3436
                fi->total = camel_folder_get_message_count(folder);
 
3437
                camel_object_unref(folder);
 
3438
 
 
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);
 
3442
                g_free (spath);
 
3443
        }
 
3444
 
 
3445
        fi->local_size = msize;
 
3446
        g_free (folder_dir);
 
3447
}
 
3448
 
 
3449
struct _refresh_msg {
 
3450
        CamelSessionThreadMsg msg;
 
3451
 
 
3452
        CamelStore *store;
 
3453
        CamelException ex;
 
3454
};
 
3455
 
 
3456
static void
 
3457
refresh_refresh(CamelSession *session, CamelSessionThreadMsg *msg)
 
3458
{
 
3459
        struct _refresh_msg *m = (struct _refresh_msg *)msg;
 
3460
        CamelImapStore *store = (CamelImapStore *)m->store;
 
3461
        CamelException nex = CAMEL_EXCEPTION_INITIALISER;
 
3462
 
 
3463
        CAMEL_SERVICE_REC_LOCK(m->store, connect_lock);
 
3464
 
 
3465
        if (!camel_disco_store_check_online((CamelDiscoStore *)m->store, &m->ex))
 
3466
                goto done;
 
3467
 
 
3468
        if (store->namespace && store->namespace[0]) {
 
3469
                char *pattern;
 
3470
                
 
3471
                get_folders_sync(store, "INBOX", &m->ex);
 
3472
                if (camel_exception_is_set(&m->ex))
 
3473
                        goto done;
 
3474
                get_folders_sync(store, store->namespace, &m->ex);
 
3475
                if (camel_exception_is_set(&m->ex))
 
3476
                        goto done;
 
3477
                pattern = imap_concat(store, store->namespace, "*");
 
3478
                get_folders_sync(store, pattern, &m->ex);
 
3479
                g_free(pattern);
 
3480
        } else {
 
3481
                get_folders_sync((CamelImapStore *)m->store, "*", &m->ex);
 
3482
        }
 
3483
        camel_store_summary_touch((CamelStoreSummary *)((CamelImapStore *)m->store)->summary);
 
3484
        camel_store_summary_save((CamelStoreSummary *)((CamelImapStore *)m->store)->summary, &nex);
 
3485
done:
 
3486
        CAMEL_SERVICE_REC_UNLOCK(m->store, connect_lock);
 
3487
}
 
3488
 
 
3489
static void
 
3490
refresh_free(CamelSession *session, CamelSessionThreadMsg *msg)
 
3491
{
 
3492
        struct _refresh_msg *m = (struct _refresh_msg *)msg;
 
3493
 
 
3494
        camel_object_unref(m->store);
 
3495
        camel_exception_clear(&m->ex);
 
3496
}
 
3497
 
 
3498
static CamelSessionThreadOps refresh_ops = {
 
3499
        refresh_refresh,
 
3500
        refresh_free,
 
3501
};
 
3502
 
 
3503
static CamelFolderInfo *
 
3504
get_folder_info_online (CamelStore *store, const char *top, guint32 flags, CamelException *ex)
 
3505
{
 
3506
        CamelImapStore *imap_store = CAMEL_IMAP_STORE (store);
 
3507
        CamelFolderInfo *tree = NULL;
 
3508
 
 
3509
        g_static_rec_mutex_lock (imap_store->sum_lock);
 
3510
 
 
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 */
 
3514
 
 
3515
        if (top == NULL)
 
3516
                top = "";
 
3517
 
 
3518
        if (imap_store->going_online || !imap_store->got_online)
 
3519
                goto fail;
 
3520
 
 
3521
        if (camel_debug("imap:folder_info"))
 
3522
                printf("get folder info online\n");
 
3523
 
 
3524
        if ((flags & CAMEL_STORE_FOLDER_INFO_SUBSCRIBED)
 
3525
            && camel_store_summary_count((CamelStoreSummary *)imap_store->summary) > 0) {
 
3526
                time_t now;
 
3527
                int ref;
 
3528
 
 
3529
                now = time(0);
 
3530
                ref = now > imap_store->refresh_stamp+60*60*1;
 
3531
                if (ref) {
 
3532
                        CAMEL_SERVICE_REC_LOCK(store, connect_lock);
 
3533
                        ref = now > imap_store->refresh_stamp+60*60*1;
 
3534
                        if (ref) {
 
3535
                                struct _refresh_msg *m;
 
3536
 
 
3537
                                imap_store->refresh_stamp = now;
 
3538
 
 
3539
                                m = camel_session_thread_msg_new(((CamelService *)store)->session, &refresh_ops, sizeof(*m));
 
3540
                                m->store = store;
 
3541
                                camel_object_ref(store);
 
3542
                                camel_exception_init(&m->ex);
 
3543
                                camel_session_thread_queue(((CamelService *)store)->session, &m->msg, 0);
 
3544
                        }
 
3545
                        CAMEL_SERVICE_REC_UNLOCK(store, connect_lock);
 
3546
                }
 
3547
        } else {
 
3548
                char *pattern;
 
3549
                int i;
 
3550
 
 
3551
                CAMEL_SERVICE_REC_LOCK(store, connect_lock);
 
3552
 
 
3553
                if (!camel_disco_store_check_online((CamelDiscoStore *)imap_store, ex))
 
3554
                        goto fail;
 
3555
 
 
3556
                if (top[0] == 0) {
 
3557
                        if (imap_store->namespace && imap_store->namespace[0]) {
 
3558
                                get_folders_sync(imap_store, "INBOX", ex);
 
3559
                                if (camel_exception_is_set(ex))
 
3560
                                        goto fail;
 
3561
                                
 
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)
 
3566
                                        pattern[i--] = 0;
 
3567
                                i++;
 
3568
                        } else {
 
3569
                                pattern = g_alloca(2);
 
3570
                                pattern[0] = '*';
 
3571
                                pattern[1] = 0;
 
3572
                                i=0;
 
3573
                        }
 
3574
                } else {
 
3575
                        char *name;
 
3576
                        
 
3577
                        name = camel_imap_store_summary_full_from_path(imap_store->summary, top);
 
3578
                        if (name == NULL)
 
3579
                                name = camel_imap_store_summary_path_to_full(imap_store->summary, top, imap_store->dir_sep);
 
3580
 
 
3581
                        i = strlen(name);
 
3582
                        pattern = g_alloca(i+5);
 
3583
                        strcpy(pattern, name);
 
3584
                        g_free(name);
 
3585
                }
 
3586
 
 
3587
                get_folders_sync(imap_store, pattern, ex);
 
3588
                if (camel_exception_is_set(ex))
 
3589
                        goto fail;
 
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)?'*':'%';
 
3593
                        pattern[i+2] = 0;
 
3594
                        get_folders_sync(imap_store, pattern, ex);
 
3595
                }
 
3596
                camel_store_summary_touch((CamelStoreSummary *)imap_store->summary);
 
3597
                camel_store_summary_save((CamelStoreSummary *)imap_store->summary, ex);
 
3598
 
 
3599
                CAMEL_SERVICE_REC_UNLOCK(store, connect_lock);
 
3600
        }
 
3601
 
 
3602
        tree = get_folder_info_offline(store, top, flags, ex);
 
3603
 
 
3604
        g_static_rec_mutex_unlock (imap_store->sum_lock);
 
3605
        return tree;
 
3606
 
 
3607
fail:
 
3608
        CAMEL_SERVICE_REC_UNLOCK(store, connect_lock);
 
3609
 
 
3610
        tree = get_folder_info_offline (store, top, flags, ex);
 
3611
 
 
3612
        g_static_rec_mutex_unlock (imap_store->sum_lock);
 
3613
        return tree;
 
3614
}
 
3615
 
 
3616
static CamelFolderInfo *
 
3617
get_folder_info_offline (CamelStore *store, const char *top,
 
3618
                         guint32 flags, CamelException *ex)
 
3619
{
 
3620
        CamelImapStore *imap_store = CAMEL_IMAP_STORE (store);
 
3621
        gboolean include_inbox = FALSE;
 
3622
        CamelFolderInfo *fi;
 
3623
        GPtrArray *folders;
 
3624
        char *pattern, *name;
 
3625
        int i;
 
3626
 
 
3627
        g_static_rec_mutex_lock (imap_store->sum_lock);
 
3628
 
 
3629
        if (camel_debug("imap:folder_info"))
 
3630
                printf("get folder info offline\n");
 
3631
 
 
3632
        /* FIXME: obey other flags */
 
3633
 
 
3634
        folders = g_ptr_array_new ();
 
3635
 
 
3636
        if (top == NULL || top[0] == '\0') {
 
3637
                include_inbox = TRUE;
 
3638
                top = "";
 
3639
        }
 
3640
 
 
3641
        /* get starting point */
 
3642
        if (top[0] == 0) {
 
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;
 
3646
                } else
 
3647
                        name = g_strdup("");
 
3648
        } else {
 
3649
                name = camel_imap_store_summary_full_from_path(imap_store->summary, top);
 
3650
                if (name == NULL)
 
3651
                        name = camel_imap_store_summary_path_to_full(imap_store->summary, top, imap_store->dir_sep);
 
3652
        }
 
3653
 
 
3654
        pattern = imap_concat(imap_store, name, "*");
 
3655
 
 
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. */
 
3660
 
 
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);
 
3663
 
 
3664
                if (si == NULL)
 
3665
                        continue;
 
3666
                
 
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))) 
 
3673
                {
 
3674
 
 
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;
 
3679
 
 
3680
                        if (fi->unread == 0 || fi->total == 0) 
 
3681
                        { 
 
3682
                                fi->unread = -1;
 
3683
                                fi->total = -1;
 
3684
                        }
 
3685
 
 
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;
 
3691
 
 
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;
 
3695
                        
 
3696
                        if (si->flags & CAMEL_FOLDER_NOSELECT) {
 
3697
                                CamelURL *url = camel_url_new(fi->uri, NULL);
 
3698
 
 
3699
                                camel_url_set_param (url, "noselect", "yes");
 
3700
                                g_free(fi->uri);
 
3701
                                fi->uri = camel_url_to_string (url, 0);
 
3702
                                camel_url_free (url);
 
3703
 
 
3704
                                fi->unread = 0;
 
3705
                                fi->total = 0;
 
3706
                        } else {
 
3707
                                fill_fi((CamelStore *)imap_store, fi, 0);
 
3708
                                if (fi->unread == -1)
 
3709
                                        fi->unread = 0;
 
3710
 
 
3711
                                if (fi->total == -1) 
 
3712
                                        fi->total = 0;
 
3713
                        }
 
3714
                        g_ptr_array_add (folders, fi);
 
3715
                }
 
3716
                camel_store_summary_info_free((CamelStoreSummary *)imap_store->summary, si);
 
3717
        }
 
3718
        g_free(pattern);
 
3719
 
 
3720
        fi = camel_folder_info_build (folders, top, '/', TRUE);
 
3721
        g_ptr_array_free (folders, TRUE);
 
3722
        g_free(name);
 
3723
 
 
3724
        g_static_rec_mutex_unlock (imap_store->sum_lock);
 
3725
 
 
3726
        return fi;
 
3727
}
 
3728
 
 
3729
static gboolean
 
3730
folder_subscribed (CamelStore *store, const char *folder_name)
 
3731
{
 
3732
        CamelImapStore *imap_store = CAMEL_IMAP_STORE (store);
 
3733
        CamelStoreInfo *si;
 
3734
        int truth = FALSE;
 
3735
 
 
3736
        g_static_rec_mutex_lock (imap_store->sum_lock);
 
3737
 
 
3738
        si = camel_store_summary_path((CamelStoreSummary *)imap_store->summary, folder_name);
 
3739
        if (si) {
 
3740
                truth = (si->flags & CAMEL_STORE_INFO_FOLDER_SUBSCRIBED) != 0;
 
3741
                camel_store_summary_info_free((CamelStoreSummary *)imap_store->summary, si);
 
3742
        }
 
3743
 
 
3744
        g_static_rec_mutex_unlock (imap_store->sum_lock);
 
3745
 
 
3746
        return truth;
 
3747
}
 
3748
 
 
3749
/* Note: folder_name must match a folder as listed with get_folder_info() -> full_name */
 
3750
static void
 
3751
subscribe_folder (CamelStore *store, const char *folder_name,
 
3752
                  CamelException *ex)
 
3753
{
 
3754
        CamelImapStore *imap_store = CAMEL_IMAP_STORE (store);
 
3755
        CamelImapResponse *response;
 
3756
        CamelFolderInfo *fi;
 
3757
        CamelStoreInfo *si;
 
3758
 
 
3759
        CAMEL_SERVICE_REC_LOCK(store, connect_lock);
 
3760
 
 
3761
        if (!camel_disco_store_check_online((CamelDiscoStore *)imap_store, ex))
 
3762
                goto done;
 
3763
 
 
3764
        camel_imap_store_stop_idle (imap_store);
 
3765
        response = camel_imap_command (imap_store, NULL, ex,
 
3766
                                       "SUBSCRIBE %F", folder_name);
 
3767
        if (!response)
 
3768
                goto done;
 
3769
        camel_imap_response_free (imap_store, response);
 
3770
 
 
3771
        g_static_rec_mutex_lock (imap_store->sum_lock);
 
3772
 
 
3773
        si = camel_store_summary_path((CamelStoreSummary *)imap_store->summary, folder_name);
 
3774
        if (si) {
 
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);
 
3779
                }
 
3780
                camel_store_summary_info_free((CamelStoreSummary *)imap_store->summary, si);
 
3781
        }
 
3782
 
 
3783
        g_static_rec_mutex_unlock (imap_store->sum_lock);
 
3784
 
 
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
 
3788
                   are done here... */
 
3789
                goto done;
 
3790
        }
 
3791
 
 
3792
        fi = imap_build_folder_info(imap_store, folder_name);
 
3793
        fi->flags |= CAMEL_FOLDER_NOCHILDREN;
 
3794
        
 
3795
        camel_object_trigger_event (CAMEL_OBJECT (store), "folder_subscribed", fi);
 
3796
        camel_folder_info_free (fi);
 
3797
done:
 
3798
        CAMEL_SERVICE_REC_UNLOCK(store, connect_lock);
 
3799
}
 
3800
 
 
3801
static void
 
3802
unsubscribe_folder (CamelStore *store, const char *folder_name,
 
3803
                    CamelException *ex)
 
3804
{
 
3805
        CamelImapStore *imap_store = CAMEL_IMAP_STORE (store);
 
3806
        CamelImapResponse *response;
 
3807
 
 
3808
        CAMEL_SERVICE_REC_LOCK(store, connect_lock);
 
3809
        
 
3810
        if (!camel_disco_store_check_online((CamelDiscoStore *)imap_store, ex))
 
3811
                goto done;
 
3812
 
 
3813
        camel_imap_store_stop_idle (imap_store);
 
3814
        response = camel_imap_command (imap_store, NULL, ex,
 
3815
                                       "UNSUBSCRIBE %F", folder_name);
 
3816
        if (!response)
 
3817
                goto done;
 
3818
        camel_imap_response_free (imap_store, response);
 
3819
 
 
3820
        imap_folder_effectively_unsubscribed (imap_store, folder_name, ex);
 
3821
done:
 
3822
        CAMEL_SERVICE_REC_UNLOCK(store, connect_lock);
 
3823
}
 
3824
 
 
3825
#if 0
 
3826
static gboolean
 
3827
folder_flags_have_changed (CamelFolder *folder)
 
3828
{
 
3829
        CamelMessageInfo *info;
 
3830
        int i, max;
 
3831
        
 
3832
        max = camel_folder_summary_count (folder->summary);
 
3833
        for (i = 0; i < max; i++) {
 
3834
                info = camel_folder_summary_index (folder->summary, i);
 
3835
                if (!info)
 
3836
                        continue;
 
3837
                if (info->flags & CAMEL_MESSAGE_FOLDER_FLAGGED) {
 
3838
                        return TRUE;
 
3839
                }
 
3840
        }
 
3841
        
 
3842
        return FALSE;
 
3843
}
 
3844
#endif
 
3845
 
 
3846
/* Use this whenever you need to ensure you're both connected and
 
3847
   online. */
 
3848
gboolean
 
3849
camel_imap_store_connected (CamelImapStore *store, CamelException *ex)
 
3850
{
 
3851
        /* This looks stupid ... because it is.
 
3852
 
 
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 ... */
 
3856
 
 
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))
 
3861
                return TRUE;
 
3862
 
 
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"));
 
3866
 
 
3867
        return FALSE;
 
3868
}
 
3869
 
 
3870
 
 
3871
/* FIXME: please god, when will the hurting stop? Thus function is so
 
3872
   fucking broken it's not even funny. */
 
3873
ssize_t
 
3874
camel_imap_store_readline (CamelImapStore *store, char **dest, CamelException *ex)
 
3875
{
 
3876
        CamelStreamBuffer *stream;
 
3877
        char linebuf[1024] = {0};
 
3878
        GByteArray *ba;
 
3879
        ssize_t nread;
 
3880
        
 
3881
        g_return_val_if_fail (CAMEL_IS_IMAP_STORE (store), -1);
 
3882
        g_return_val_if_fail (dest, -1);
 
3883
        
 
3884
        *dest = NULL;
 
3885
        
 
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.
 
3889
         */
 
3890
 
 
3891
        if (!camel_disco_store_check_online((CamelDiscoStore *)store, ex))
 
3892
                return -1;
 
3893
 
 
3894
        camel_imap_store_restore_stream_buffer (store);
 
3895
        stream = CAMEL_STREAM_BUFFER (store->istream);
 
3896
 
 
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')
 
3901
                        break;
 
3902
        }
 
3903
 
 
3904
        if (nread <= 0) {
 
3905
                if (errno == EINTR)
 
3906
                {
 
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));
 
3911
                } else {
 
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);
 
3916
                }
 
3917
 
 
3918
                g_byte_array_free (ba, TRUE);
 
3919
                return -1;
 
3920
        }
 
3921
        
 
3922
        if (camel_verbose_debug) {
 
3923
                fprintf (stderr, "received: ");
 
3924
                fwrite (ba->data, 1, ba->len, stderr);
 
3925
        }
 
3926
        
 
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';
 
3933
                nread--;
 
3934
        }
 
3935
        
 
3936
        *dest = (char *) ba->data;
 
3937
        g_byte_array_free (ba, FALSE);
 
3938
        
 
3939
        return nread;
 
3940
}
 
3941
 
 
3942
 
 
3943
 
 
3944
/* FIXME: please god, when will the hurting stop? Thus function is so
 
3945
   fucking broken it's not even funny. */
 
3946
ssize_t
 
3947
camel_imap_store_readline_idle (CamelImapStore *store, char **dest, CamelException *ex)
 
3948
{
 
3949
        CamelStreamBuffer *stream;
 
3950
        char linebuf[1024] = {0};
 
3951
        GByteArray *ba;
 
3952
        ssize_t nread;
 
3953
        
 
3954
        g_return_val_if_fail (CAMEL_IS_IMAP_STORE (store), -1);
 
3955
        g_return_val_if_fail (dest, -1);
 
3956
        
 
3957
        *dest = NULL;
 
3958
        
 
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.
 
3962
         */
 
3963
 
 
3964
        if (!camel_disco_store_check_online((CamelDiscoStore *)store, ex))
 
3965
                return -1;
 
3966
 
 
3967
        camel_imap_store_restore_stream_buffer (store);
 
3968
        stream = CAMEL_STREAM_BUFFER (store->istream);
 
3969
 
 
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')
 
3974
                        break;
 
3975
        }
 
3976
 
 
3977
        if (nread <= 0) {
 
3978
                if (errno == EINTR)
 
3979
                {
 
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));
 
3984
                } else {
 
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);
 
3989
                }
 
3990
 
 
3991
                g_byte_array_free (ba, TRUE);
 
3992
                return -1;
 
3993
        }
 
3994
        
 
3995
        if (camel_verbose_debug) {
 
3996
                fprintf (stderr, "received: ");
 
3997
                fwrite (ba->data, 1, ba->len, stderr);
 
3998
        }
 
3999
        
 
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';
 
4006
                nread--;
 
4007
        }
 
4008
        
 
4009
        *dest = (char *) ba->data;
 
4010
        g_byte_array_free (ba, FALSE);
 
4011
        
 
4012
        return nread;
 
4013
}
 
4014
 
 
4015
ssize_t
 
4016
camel_imap_store_readline_nl (CamelImapStore *store, char **dest, CamelException *ex)
 
4017
{
 
4018
        CamelStreamBuffer *stream;
 
4019
        char linebuf[1024] = {0};
 
4020
        GByteArray *ba;
 
4021
        ssize_t nread;
 
4022
        
 
4023
        g_return_val_if_fail (CAMEL_IS_IMAP_STORE (store), -1);
 
4024
        g_return_val_if_fail (dest, -1);
 
4025
        
 
4026
        *dest = NULL;
 
4027
        
 
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.
 
4031
         */
 
4032
 
 
4033
        if (!camel_disco_store_check_online((CamelDiscoStore *)store, ex))
 
4034
                return -1;
 
4035
 
 
4036
        camel_imap_store_restore_stream_buffer (store);
 
4037
 
 
4038
        if (!store->istream)
 
4039
                return -1;
 
4040
 
 
4041
        stream = CAMEL_STREAM_BUFFER (store->istream);
 
4042
 
 
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')
 
4047
                        break;
 
4048
        }
 
4049
 
 
4050
        if (nread <= 0) {
 
4051
                if (errno == EINTR)
 
4052
                {
 
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));
 
4057
                } else {
 
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);
 
4062
                }
 
4063
 
 
4064
                g_byte_array_free (ba, TRUE);
 
4065
                return -1;
 
4066
        }
 
4067
        
 
4068
        if (camel_verbose_debug) {
 
4069
                fprintf (stderr, "received: ");
 
4070
                fwrite (ba->data, 1, ba->len, stderr);
 
4071
        }
 
4072
        
 
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';
 
4079
                nread--;
 
4080
        }
 
4081
        
 
4082
        *dest = (char *) ba->data;
 
4083
        g_byte_array_free (ba, FALSE);
 
4084
        
 
4085
        return nread;
 
4086
}
 
4087
 
 
4088
ssize_t
 
4089
camel_imap_store_readline_nb (CamelImapStore *store, char **dest, CamelException *ex)
 
4090
{
 
4091
        CamelStreamBuffer *stream;
 
4092
        char linebuf[1024] = {0};
 
4093
        GByteArray *ba;
 
4094
        ssize_t nread;
 
4095
        
 
4096
        g_return_val_if_fail (CAMEL_IS_IMAP_STORE (store), -1);
 
4097
        g_return_val_if_fail (dest, -1);
 
4098
 
 
4099
        *dest = NULL;
 
4100
 
 
4101
        if (store->istream == NULL || ((CamelObject *)store->istream)->ref_count <= 0)
 
4102
                return -1;
 
4103
 
 
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) 
 
4107
        {
 
4108
                g_byte_array_append (ba, (const guchar*) linebuf, nread);
 
4109
                if (linebuf[nread - 1] == '\n')
 
4110
                        break;
 
4111
        }
 
4112
 
 
4113
        if (nread <= 0) {
 
4114
                g_byte_array_free (ba, TRUE);
 
4115
                return -1;
 
4116
        }
 
4117
 
 
4118
        nread = ba->len - 1;
 
4119
        ba->data[nread] = '\0';
 
4120
        if (ba->data[nread - 1] == '\r') {
 
4121
                ba->data[nread - 1] = '\0';
 
4122
                nread--;
 
4123
        }
 
4124
        
 
4125
        *dest = (char *) ba->data;
 
4126
        g_byte_array_free (ba, FALSE);
 
4127
        
 
4128
        return nread;
 
4129
}