~ubuntu-branches/ubuntu/maverick/evolution-data-server/maverick-proposed

« back to all changes in this revision

Viewing changes to camel/providers/imapx/camel-imapx-server.c

  • Committer: Bazaar Package Importer
  • Author(s): Didier Roche
  • Date: 2010-05-17 17:02:06 UTC
  • mfrom: (1.1.79 upstream) (1.6.12 experimental)
  • Revision ID: james.westby@ubuntu.com-20100517170206-4ufr52vwrhh26yh0
Tags: 2.30.1-1ubuntu1
* Merge from debian experimental. Remaining change:
  (LP: #42199, #229669, #173703, #360344, #508494)
  + debian/control:
    - add Vcs-Bzr tag
    - don't use libgnome
    - Use Breaks instead of Conflicts against evolution 2.25 and earlier.
  + debian/evolution-data-server.install,
    debian/patches/45_libcamel_providers_version.patch:
    - use the upstream versioning, not a Debian-specific one 
  + debian/libedata-book1.2-dev.install, debian/libebackend-1.2-dev.install,
    debian/libcamel1.2-dev.install, debian/libedataserverui1.2-dev.install:
    - install html documentation
  + debian/rules:
    - don't build documentation it's shipped with the tarball

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
 
 
2
#ifdef HAVE_CONFIG_H
 
3
#include <config.h>
 
4
#endif
 
5
 
 
6
#include <errno.h>
 
7
#include <string.h>
 
8
#include <glib.h>
 
9
#include <glib/gstdio.h>
 
10
 
 
11
// fixme, use own type funcs
 
12
#include <ctype.h>
 
13
 
 
14
#ifdef HAVE_NSS
 
15
#include <nspr.h>
 
16
#include <prio.h>
 
17
#include <prerror.h>
 
18
#include <prerr.h>
 
19
#endif
 
20
 
 
21
#include <camel/camel-list-utils.h>
 
22
#include <camel/camel-msgport.h>
 
23
#include <camel/camel-object.h>
 
24
#include <camel/camel-url.h>
 
25
#include <camel/camel-session.h>
 
26
#include <camel/camel-stream-fs.h>
 
27
#include <camel/camel-stream-null.h>
 
28
#include <camel/camel-stream-mem.h>
 
29
#include <camel/camel-stream-filter.h>
 
30
#include <camel/camel-mime-filter-canon.h>
 
31
#include <camel/camel-mime-message.h>
 
32
#include "camel/camel-string-utils.h"
 
33
#include <camel/camel-net-utils.h>
 
34
#include "camel/camel-private.h"
 
35
#include <camel/camel-tcp-stream-ssl.h>
 
36
#include <camel/camel-tcp-stream-raw.h>
 
37
#include <camel/camel-db.h>
 
38
 
 
39
#include <camel/camel-sasl.h>
 
40
#include <camel/camel-i18n.h>
 
41
#include <camel/camel-utf8.h>
 
42
#include <camel/camel-file-utils.h>
 
43
 
 
44
#include "camel-imapx-utils.h"
 
45
#include "camel-imapx-exception.h"
 
46
#include "camel-imapx-stream.h"
 
47
#include "camel-imapx-server.h"
 
48
#include "camel-imapx-folder.h"
 
49
#include "camel-imapx-store.h"
 
50
#include "camel-imapx-summary.h"
 
51
 
 
52
#define c(x)
 
53
#define e(x) 
 
54
 
 
55
#define CFS_CLASS(x) ((CamelFolderSummaryClass *)((CamelObject *)x)->klass)
 
56
 
 
57
#define CIF(x) ((CamelIMAPXFolder *)x)
 
58
 
 
59
#define QUEUE_LOCK(x) (g_static_rec_mutex_lock(&(x)->queue_lock))
 
60
#define QUEUE_UNLOCK(x) (g_static_rec_mutex_unlock(&(x)->queue_lock))
 
61
 
 
62
/* All comms with server go here */
 
63
 
 
64
/* Try pipelining fetch requests, 'in bits' */
 
65
#define MULTI_SIZE (20480)
 
66
 
 
67
/* How many outstanding commands do we allow before we just queue them? */
 
68
#define MAX_COMMANDS (10)
 
69
 
 
70
/* How many message headers to fetch at a time update summary for new messages*/
 
71
#define BATCH_FETCH_COUNT 500
 
72
 
 
73
#define MAX_COMMAND_LEN 1000
 
74
 
 
75
extern gint camel_application_is_exiting;
 
76
 
 
77
struct _uidset_state {
 
78
        struct _CamelIMAPXEngine *ie;
 
79
        gint entries, uids;
 
80
        gint total, limit;
 
81
        guint32 start;
 
82
        guint32 last;
 
83
};
 
84
 
 
85
struct _CamelIMAPXCommand;
 
86
void imapx_uidset_init(struct _uidset_state *ss, gint total, gint limit);
 
87
gint imapx_uidset_done(struct _uidset_state *ss, struct _CamelIMAPXCommand *ic);
 
88
gint imapx_uidset_add(struct _uidset_state *ss, struct _CamelIMAPXCommand *ic, const gchar *uid);
 
89
static gboolean imapx_disconnect (CamelIMAPXServer *is);
 
90
static gint imapx_uid_cmp(gconstpointer ap, gconstpointer bp, gpointer data);
 
91
 
 
92
typedef struct _CamelIMAPXCommandPart CamelIMAPXCommandPart;
 
93
typedef struct _CamelIMAPXCommand CamelIMAPXCommand;
 
94
 
 
95
typedef enum {
 
96
        CAMEL_IMAPX_COMMAND_SIMPLE = 0,
 
97
        CAMEL_IMAPX_COMMAND_DATAWRAPPER,
 
98
        CAMEL_IMAPX_COMMAND_STREAM,
 
99
        CAMEL_IMAPX_COMMAND_AUTH,
 
100
        CAMEL_IMAPX_COMMAND_FILE,
 
101
        CAMEL_IMAPX_COMMAND_STRING,
 
102
        CAMEL_IMAPX_COMMAND_MASK = 0xff,
 
103
        CAMEL_IMAPX_COMMAND_CONTINUATION = 0x8000 /* does this command expect continuation? */
 
104
} camel_imapx_command_part_t;
 
105
 
 
106
struct _CamelIMAPXCommandPart {
 
107
        struct _CamelIMAPXCommandPart *next;
 
108
        struct _CamelIMAPXCommandPart *prev;
 
109
 
 
110
        struct _CamelIMAPXCommand *parent;
 
111
 
 
112
        gint data_size;
 
113
        gchar *data;
 
114
 
 
115
        camel_imapx_command_part_t type;
 
116
 
 
117
        gint ob_size;
 
118
        gpointer ob;
 
119
};
 
120
 
 
121
typedef gint (*CamelIMAPXEngineFunc)(struct _CamelIMAPXServer *engine, guint32 id, gpointer data);
 
122
typedef void (*CamelIMAPXCommandFunc)(struct _CamelIMAPXServer *engine, struct _CamelIMAPXCommand *);
 
123
 
 
124
struct _CamelIMAPXCommand {
 
125
        struct _CamelIMAPXCommand *next, *prev;
 
126
 
 
127
        gint pri;
 
128
 
 
129
        const gchar *name;      /* command name/type (e.g. FETCH) */
 
130
 
 
131
        gchar *select;          /* folder to select */
 
132
 
 
133
        struct _status_info *status; /* status for command, indicates it is complete if != NULL */
 
134
 
 
135
        /* If exception is set, it means we were not able to parse above status, it might be
 
136
           because user cancelled the operation or io error */
 
137
        CamelException *ex;
 
138
 
 
139
        guint32 tag;
 
140
 
 
141
        struct _CamelStreamMem *mem;    /* for building the part TOOD: just use a GString? */
 
142
        CamelDList parts;
 
143
        CamelIMAPXCommandPart *current;
 
144
 
 
145
        /* used for running some commands syncronously */
 
146
        EFlag *flag;
 
147
 
 
148
        /* responsible for free'ing the command */
 
149
        CamelIMAPXCommandFunc complete;
 
150
        struct _CamelIMAPXJob *job;
 
151
};
 
152
 
 
153
CamelIMAPXCommand *camel_imapx_command_new(const gchar *name, const gchar *select, const gchar *fmt, ...);
 
154
void camel_imapx_command_add(CamelIMAPXCommand *ic, const gchar *fmt, ...);
 
155
void camel_imapx_command_free(CamelIMAPXCommand *ic);
 
156
void camel_imapx_command_close(CamelIMAPXCommand *ic);
 
157
static gboolean imapx_is_command_queue_empty (CamelIMAPXServer *is);
 
158
 
 
159
/* states for the connection? */
 
160
enum {
 
161
        IMAPX_DISCONNECTED,
 
162
        IMAPX_CONNECTED,
 
163
        IMAPX_AUTHENTICATED,
 
164
        IMAPX_SELECTED
 
165
};
 
166
 
 
167
struct _refresh_info {
 
168
        gchar *uid;
 
169
        gboolean exists;
 
170
        guint32 server_flags;
 
171
        CamelFlag *server_user_flags;
 
172
};
 
173
 
 
174
enum {
 
175
        IMAPX_JOB_GET_MESSAGE = 1<<0,
 
176
        IMAPX_JOB_APPEND_MESSAGE = 1<<1,
 
177
        IMAPX_JOB_COPY_MESSAGE = 1<<2,
 
178
        IMAPX_JOB_FETCH_NEW_MESSAGES = 1<<3,
 
179
        IMAPX_JOB_REFRESH_INFO = 1<<4,
 
180
        IMAPX_JOB_SYNC_CHANGES = 1<<5,
 
181
        IMAPX_JOB_EXPUNGE = 1<<6,
 
182
        IMAPX_JOB_NOOP = 1<<7,
 
183
        IMAPX_JOB_IDLE = 1<<8,
 
184
        IMAPX_JOB_LIST = 1<<9,
 
185
        IMAPX_JOB_MANAGE_SUBSCRIPTION = 1<<10,
 
186
        IMAPX_JOB_CREATE_FOLDER = 1<<11,
 
187
        IMAPX_JOB_DELETE_FOLDER = 1<<12,
 
188
        IMAPX_JOB_RENAME_FOLDER = 1<<13,
 
189
};
 
190
 
 
191
/* Operations on the store (folder_tree) will have highest priority as we know for sure they are sync
 
192
   and user triggered. */
 
193
enum {
 
194
        IMAPX_PRIORITY_CREATE_FOLDER = 200,
 
195
        IMAPX_PRIORITY_DELETE_FOLDER = 200,
 
196
        IMAPX_PRIORITY_RENAME_FOLDER = 200,
 
197
        IMAPX_PRIORITY_MANAGE_SUBSCRIPTION = 200,
 
198
        IMAPX_PRIORITY_SYNC_CHANGES = 150,
 
199
        IMAPX_PRIORITY_EXPUNGE = 150,
 
200
        IMAPX_PRIORITY_GET_MESSAGE = 100,
 
201
        IMAPX_PRIORITY_REFRESH_INFO = 0,
 
202
        IMAPX_PRIORITY_NOOP = 0,
 
203
        IMAPX_PRIORITY_NEW_MESSAGES = 0,
 
204
        IMAPX_PRIORITY_APPEND_MESSAGE = -60,
 
205
        IMAPX_PRIIORITY_COPY_MESSAGE = -60,
 
206
        IMAPX_PRIORITY_LIST = -80,
 
207
        IMAPX_PRIORITY_IDLE = -100,
 
208
        IMAPX_PRIORITY_SYNC_MESSAGE = -120
 
209
};
 
210
 
 
211
struct _imapx_flag_change {
 
212
        GPtrArray *infos;
 
213
        gchar *name;
 
214
};
 
215
 
 
216
typedef struct _CamelIMAPXJob CamelIMAPXJob;
 
217
struct _CamelIMAPXJob {
 
218
        CamelMsg msg;
 
219
 
 
220
        CamelException *ex;
 
221
 
 
222
        void (*start)(CamelIMAPXServer *is, struct _CamelIMAPXJob *job);
 
223
 
 
224
        // ??
 
225
        //CamelOperation *op;
 
226
 
 
227
        gint noreply:1;         /* dont wait for reply */
 
228
        guint32 type;           /* operation type */
 
229
        gint pri;               /* the command priority */
 
230
        short commands;         /* counts how many commands are outstanding */
 
231
 
 
232
        CamelFolder *folder;
 
233
        CamelOperation *op;
 
234
 
 
235
        union {
 
236
                struct {
 
237
                        /* in: uid requested */
 
238
                        gchar *uid;
 
239
                        /* in/out: message content stream output */
 
240
                        CamelStream *stream;
 
241
                        /* working variables */
 
242
                        gsize body_offset;
 
243
                        gssize body_len;
 
244
                        gsize fetch_offset;
 
245
                        gsize size;
 
246
                        gboolean use_multi_fetch;
 
247
                } get_message;
 
248
                struct {
 
249
                        /* array of refresh info's */
 
250
                        GArray *infos;
 
251
                        /* used for biulding uidset stuff */
 
252
                        gint index;
 
253
                        gint last_index;
 
254
                        struct _uidset_state uidset;
 
255
                        /* changes during refresh */
 
256
                        CamelFolderChangeInfo *changes;
 
257
                } refresh_info;
 
258
                struct {
 
259
                        GPtrArray *changed_uids;
 
260
                        guint32 on_set;
 
261
                        guint32 off_set;
 
262
                        GArray *on_user; /* imapx_flag_change */
 
263
                        GArray *off_user;
 
264
                } sync_changes;
 
265
                struct {
 
266
                        gchar *path;
 
267
                        CamelMessageInfo *info;
 
268
                } append_message;
 
269
                struct {
 
270
                        CamelFolder *dest;
 
271
                        GPtrArray *uids;
 
272
                        gboolean delete_originals;
 
273
                        gint index;
 
274
                        gint last_index;
 
275
                        struct _uidset_state uidset;
 
276
                } copy_messages;
 
277
                struct {
 
278
                        gchar *pattern;
 
279
                        guint32 flags;
 
280
                        GHashTable *folders;
 
281
                } list;
 
282
 
 
283
                struct {
 
284
                        const gchar *folder_name;
 
285
                        gboolean subscribe;
 
286
                } manage_subscriptions;
 
287
 
 
288
                struct {
 
289
                        const gchar *ofolder_name;
 
290
                        const gchar *nfolder_name;
 
291
                } rename_folder;
 
292
                
 
293
                const gchar *folder_name;
 
294
        } u;
 
295
};
 
296
 
 
297
static void imapx_job_done (CamelIMAPXServer *is, CamelIMAPXJob *job);
 
298
static void imapx_run_job (CamelIMAPXServer *is, CamelIMAPXJob *job);
 
299
static void imapx_job_fetch_new_messages_start (CamelIMAPXServer *is, CamelIMAPXJob *job);
 
300
static void imapx_command_copy_messages_step_done (CamelIMAPXServer *is, CamelIMAPXCommand *ic);
 
301
static gint imapx_refresh_info_uid_cmp(gconstpointer ap, gconstpointer bp);
 
302
static gint imapx_uids_array_cmp (gconstpointer ap, gconstpointer bp);
 
303
static void imapx_server_sync_changes(CamelIMAPXServer *is, CamelFolder *folder, gint pri, CamelException *ex);
 
304
 
 
305
typedef struct _CamelIMAPXIdle CamelIMAPXIdle;
 
306
struct _CamelIMAPXIdle {
 
307
        GMutex *idle_lock;
 
308
        EFlag *idle_start_watch;
 
309
        GThread *idle_thread;
 
310
 
 
311
        gboolean idle_issue_done;
 
312
        gboolean in_idle;
 
313
        gboolean started;
 
314
        gboolean idle_exit;
 
315
};
 
316
 
 
317
static gboolean imapx_in_idle (CamelIMAPXServer *is);
 
318
static gboolean imapx_idle_supported (CamelIMAPXServer *is);
 
319
static void imapx_start_idle (CamelIMAPXServer *is);
 
320
static void imapx_exit_idle (CamelIMAPXServer *is);
 
321
static void imapx_init_idle (CamelIMAPXServer *is);
 
322
static void imapx_stop_idle (CamelIMAPXServer *is, CamelException *ex);
 
323
static void camel_imapx_server_idle (CamelIMAPXServer *is, CamelFolder *folder, CamelException *ex);
 
324
 
 
325
enum {
 
326
        USE_SSL_NEVER,
 
327
        USE_SSL_ALWAYS,
 
328
        USE_SSL_WHEN_POSSIBLE
 
329
};
 
330
 
 
331
#define SSL_PORT_FLAGS (CAMEL_TCP_STREAM_SSL_ENABLE_SSL2 | CAMEL_TCP_STREAM_SSL_ENABLE_SSL3)
 
332
#define STARTTLS_FLAGS (CAMEL_TCP_STREAM_SSL_ENABLE_TLS)
 
333
 
 
334
static void imapx_select(CamelIMAPXServer *is, CamelFolder *folder, gboolean force, CamelException *ex);
 
335
 
 
336
/*
 
337
  this creates a uid (or sequence number) set directly into a command,
 
338
  if total is set, then we break it up into total uids. (i.e. command time)
 
339
  if limit is set, then we break it up into limit entries (i.e. command length)
 
340
*/
 
341
void
 
342
imapx_uidset_init(struct _uidset_state *ss, gint total, gint limit)
 
343
{
 
344
        ss->uids = 0;
 
345
        ss->entries = 0;
 
346
        ss->start = 0;
 
347
        ss->last = 0;
 
348
        ss->total = total;
 
349
        ss->limit = limit;
 
350
}
 
351
 
 
352
gint
 
353
imapx_uidset_done(struct _uidset_state *ss, CamelIMAPXCommand *ic)
 
354
{
 
355
        gint ret = 0;
 
356
 
 
357
        if (ss->last != 0 && ss->last != ss->start) {
 
358
                camel_imapx_command_add(ic, ":%d", ss->last);
 
359
        }
 
360
 
 
361
        ret = ss->last != 0;
 
362
 
 
363
        ss->start = 0;
 
364
        ss->last = 0;
 
365
        ss->uids = 0;
 
366
        ss->entries = 0;
 
367
 
 
368
        return ret;
 
369
}
 
370
 
 
371
gint
 
372
imapx_uidset_add(struct _uidset_state *ss, CamelIMAPXCommand *ic, const gchar *uid)
 
373
{
 
374
        guint32 uidn;
 
375
 
 
376
        uidn = strtoul(uid, NULL, 10);
 
377
        if (uidn == 0)
 
378
                return -1;
 
379
 
 
380
        ss->uids++;
 
381
 
 
382
        e(printf("uidset add '%s'\n", uid));
 
383
 
 
384
        if (ss->last == 0) {
 
385
                e(printf(" start\n"));
 
386
                camel_imapx_command_add(ic, "%d", uidn);
 
387
                ss->entries++;
 
388
                ss->start = uidn;
 
389
        } else {
 
390
                if (ss->last != uidn-1) {
 
391
                        if (ss->last == ss->start) {
 
392
                                e(printf(" ,next\n"));
 
393
                                camel_imapx_command_add(ic, ",%d", uidn);
 
394
                                ss->entries++;
 
395
                        } else {
 
396
                                e(printf(" :range\n"));
 
397
                                camel_imapx_command_add(ic, ":%d,%d", ss->last, uidn);
 
398
                                ss->entries+=2;
 
399
                        }
 
400
                        ss->start = uidn;
 
401
                }
 
402
        }
 
403
 
 
404
        ss->last = uidn;
 
405
 
 
406
        if ((ss->limit && ss->entries >= ss->limit)
 
407
            || (ss->total && ss->uids >= ss->total)) {
 
408
                e(printf(" done, %d entries, %d uids\n", ss->entries, ss->uids));
 
409
                imapx_uidset_done(ss, ic);
 
410
                return 1;
 
411
        }
 
412
 
 
413
        return 0;
 
414
}
 
415
 
 
416
static void
 
417
imapx_command_add_part(CamelIMAPXCommand *ic, camel_imapx_command_part_t type, gpointer o)
 
418
{
 
419
        CamelIMAPXCommandPart *cp;
 
420
        CamelStreamNull *null;
 
421
        guint ob_size = 0;
 
422
 
 
423
        /* TODO: literal+? */
 
424
 
 
425
        switch (type & CAMEL_IMAPX_COMMAND_MASK) {
 
426
        case CAMEL_IMAPX_COMMAND_DATAWRAPPER:
 
427
        case CAMEL_IMAPX_COMMAND_STREAM: {
 
428
                CamelObject *ob = o;
 
429
 
 
430
                /* TODO: seekable streams we could just seek to the end and back */
 
431
                null = (CamelStreamNull *)camel_stream_null_new();
 
432
                if ( (type & CAMEL_IMAPX_COMMAND_MASK) == CAMEL_IMAPX_COMMAND_DATAWRAPPER) {
 
433
                        camel_data_wrapper_write_to_stream((CamelDataWrapper *)ob, (CamelStream *)null);
 
434
                } else {
 
435
                        camel_stream_reset((CamelStream *)ob);
 
436
                        camel_stream_write_to_stream((CamelStream *)ob, (CamelStream *)null);
 
437
                        camel_stream_reset((CamelStream *)ob);
 
438
                }
 
439
                type |= CAMEL_IMAPX_COMMAND_CONTINUATION;
 
440
                camel_object_ref(ob);
 
441
                ob_size = null->written;
 
442
                camel_object_unref((CamelObject *)null);
 
443
                camel_stream_printf((CamelStream *)ic->mem, "{%u}", ob_size);
 
444
                break;
 
445
        }
 
446
        case CAMEL_IMAPX_COMMAND_AUTH: {
 
447
                CamelObject *ob = o;
 
448
 
 
449
                /* we presume we'll need to get additional data only if we're not authenticated yet */
 
450
                camel_object_ref(ob);
 
451
                camel_stream_printf((CamelStream *)ic->mem, "%s", ((CamelSasl *)ob)->mech);
 
452
                if (!camel_sasl_authenticated((CamelSasl *)ob))
 
453
                        type |= CAMEL_IMAPX_COMMAND_CONTINUATION;
 
454
                break;
 
455
        }
 
456
        case CAMEL_IMAPX_COMMAND_FILE: {
 
457
                gchar *path = o;
 
458
                struct stat st;
 
459
 
 
460
                if (stat(path, &st) == 0) {
 
461
                        o = g_strdup(o);
 
462
                        ob_size = st.st_size;
 
463
                } else
 
464
                        o = NULL;
 
465
 
 
466
                camel_stream_printf((CamelStream *)ic->mem, "{%u}", ob_size);
 
467
                type |= CAMEL_IMAPX_COMMAND_CONTINUATION;
 
468
                break;
 
469
        }
 
470
        case CAMEL_IMAPX_COMMAND_STRING:
 
471
                o = g_strdup(o);
 
472
                ob_size = strlen(o);
 
473
                camel_stream_printf((CamelStream *)ic->mem, "{%u}", ob_size);
 
474
                type |= CAMEL_IMAPX_COMMAND_CONTINUATION;
 
475
                break;
 
476
        default:
 
477
                ob_size = 0;
 
478
        }
 
479
 
 
480
        cp = g_malloc0(sizeof(*cp));
 
481
        cp->type = type;
 
482
        cp->ob_size = ob_size;
 
483
        cp->ob = o;
 
484
        cp->data_size = ic->mem->buffer->len;
 
485
        cp->data = g_malloc(cp->data_size+1);
 
486
        memcpy(cp->data, ic->mem->buffer->data, cp->data_size);
 
487
        cp->data[cp->data_size] = 0;
 
488
 
 
489
        camel_stream_reset((CamelStream *)ic->mem);
 
490
        /* FIXME: hackish? */
 
491
        g_byte_array_set_size(ic->mem->buffer, 0);
 
492
 
 
493
        camel_dlist_addtail(&ic->parts, (CamelDListNode *)cp);
 
494
}
 
495
 
 
496
static void
 
497
imapx_command_addv(CamelIMAPXCommand *ic, const gchar *fmt, va_list ap)
 
498
{
 
499
        const gchar *p, *ps, *start;
 
500
        guchar c;
 
501
        guint width;
 
502
        gchar ch;
 
503
        gint llong;
 
504
        gint left;
 
505
        gint fill;
 
506
        gint zero;
 
507
        gchar *s;
 
508
        gchar *P;
 
509
        gint d;
 
510
        glong l;
 
511
        guint32 f;
 
512
        CamelFlag *F;
 
513
        CamelStream *S;
 
514
        CamelDataWrapper *D;
 
515
        CamelSasl *A;
 
516
        gchar buffer[16];
 
517
        CamelFolder *folder;
 
518
        gchar *fname = NULL, *encoded = NULL;
 
519
        CamelException ex = CAMEL_EXCEPTION_INITIALISER;
 
520
 
 
521
        c(printf("adding command, fmt = '%s'\n", fmt));
 
522
 
 
523
        p = fmt;
 
524
        ps = fmt;
 
525
        while (( c = *p++ )) {
 
526
                switch (c) {
 
527
                case '%':
 
528
                        if (*p == '%') {
 
529
                                camel_stream_write((CamelStream *)ic->mem, ps, p-ps);
 
530
                                p++;
 
531
                                ps = p;
 
532
                        } else {
 
533
                                camel_stream_write((CamelStream *)ic->mem, ps, p-ps-1);
 
534
                                start = p-1;
 
535
                                width = 0;
 
536
                                left = FALSE;
 
537
                                fill = FALSE;
 
538
                                zero = FALSE;
 
539
                                llong = FALSE;
 
540
 
 
541
                                do {
 
542
                                        c = *p++;
 
543
                                        if (c == '0')
 
544
                                                zero = TRUE;
 
545
                                        else if ( c== '-')
 
546
                                                left = TRUE;
 
547
                                        else
 
548
                                                break;
 
549
                                } while (c);
 
550
 
 
551
                                do {
 
552
                                        // FIXME: ascii isdigit
 
553
                                        if (isdigit(c))
 
554
                                                width = width * 10 + (c-'0');
 
555
                                        else
 
556
                                                break;
 
557
                                } while ((c = *p++));
 
558
 
 
559
                                if (c == 'l') {
 
560
                                        llong = TRUE;
 
561
                                        c = *p++;
 
562
                                }
 
563
 
 
564
                                switch (c) {
 
565
                                case 'A': /* auth object - sasl auth, treat as special kind of continuation */
 
566
                                        A = va_arg(ap, CamelSasl *);
 
567
                                        imapx_command_add_part(ic, CAMEL_IMAPX_COMMAND_AUTH, A);
 
568
                                        break;
 
569
                                case 'S': /* stream */
 
570
                                        S = va_arg(ap, CamelStream *);
 
571
                                        c(printf("got stream '%p'\n", S));
 
572
                                        imapx_command_add_part(ic, CAMEL_IMAPX_COMMAND_STREAM, S);
 
573
                                        break;
 
574
                                case 'D': /* datawrapper */
 
575
                                        D = va_arg(ap, CamelDataWrapper *);
 
576
                                        c(printf("got data wrapper '%p'\n", D));
 
577
                                        imapx_command_add_part(ic, CAMEL_IMAPX_COMMAND_DATAWRAPPER, D);
 
578
                                        break;
 
579
                                case 'P': /* filename path */
 
580
                                        P = va_arg(ap, gchar *);
 
581
                                        c(printf("got file path '%s'\n", P));
 
582
                                        imapx_command_add_part(ic, CAMEL_IMAPX_COMMAND_FILE, P);
 
583
                                        break;
 
584
                                case 't': /* token */
 
585
                                        s = va_arg(ap, gchar *);
 
586
                                        camel_stream_write((CamelStream *)ic->mem, s, strlen(s));
 
587
                                        break;
 
588
                                case 's': /* simple string */
 
589
                                        s = va_arg(ap, gchar *);
 
590
                                        c(printf("got string '%s'\n", s));
 
591
                                        if (*s) {
 
592
                                                guchar mask = imapx_is_mask(s);
 
593
 
 
594
                                                if (mask & IMAPX_TYPE_ATOM_CHAR)
 
595
                                                        camel_stream_write((CamelStream *)ic->mem, s, strlen(s));
 
596
                                                else if (mask & IMAPX_TYPE_TEXT_CHAR) {
 
597
                                                        camel_stream_write((CamelStream *)ic->mem, "\"", 1);
 
598
                                                        while (*s) {
 
599
                                                                gchar *start = s;
 
600
 
 
601
                                                                while (*s && imapx_is_quoted_char(*s))
 
602
                                                                        s++;
 
603
                                                                camel_stream_write((CamelStream *)ic->mem, start, s-start);
 
604
                                                                if (*s) {
 
605
                                                                        camel_stream_write((CamelStream *)ic->mem, "\\", 1);
 
606
                                                                        camel_stream_write((CamelStream *)ic->mem, s, 1);
 
607
                                                                        s++;
 
608
                                                                }
 
609
                                                        }
 
610
                                                        camel_stream_write((CamelStream *)ic->mem, "\"", 1);
 
611
                                                } else {
 
612
                                                        imapx_command_add_part(ic, CAMEL_IMAPX_COMMAND_STRING, s);
 
613
                                                }
 
614
                                        } else {
 
615
                                                camel_stream_write((CamelStream *)ic->mem, "\"\"", 2);
 
616
                                        }
 
617
                                        break;
 
618
                                case 'f': /* imap folder name */
 
619
                                        folder = va_arg(ap, CamelFolder *);
 
620
                                        c(printf("got folder '%s'\n", s));
 
621
                                        fname = camel_imapx_store_summary_full_from_path(((CamelIMAPXStore *) folder->parent_store)->summary, folder->full_name);
 
622
                                        if (fname) {
 
623
                                                encoded = camel_utf8_utf7(fname);
 
624
                                                g_free (fname);
 
625
                                        } else
 
626
                                                encoded = camel_utf8_utf7 (folder->full_name);
 
627
 
 
628
                                        camel_stream_printf((CamelStream *)ic->mem, "\"%s\"", encoded?encoded:"");
 
629
 
 
630
                                        g_free (encoded);
 
631
                                        break;
 
632
                                case 'F': /* IMAP flags set */
 
633
                                        f = va_arg(ap, guint32);
 
634
                                        F = va_arg(ap, CamelFlag *);
 
635
                                        imapx_write_flags((CamelStream *)ic->mem, f, F, &ex);
 
636
                                        break;
 
637
                                case 'c':
 
638
                                        d = va_arg(ap, gint);
 
639
                                        ch = d;
 
640
                                        camel_stream_write((CamelStream *)ic->mem, &ch, 1);
 
641
                                        break;
 
642
                                case 'd': /* int/unsigned */
 
643
                                case 'u':
 
644
                                        if (llong) {
 
645
                                                l = va_arg(ap, glong);
 
646
                                                c(printf("got glong '%d'\n", (gint)l));
 
647
                                                memcpy(buffer, start, p-start);
 
648
                                                buffer[p-start] = 0;
 
649
                                                camel_stream_printf((CamelStream *)ic->mem, buffer, l);
 
650
                                        } else {
 
651
                                                d = va_arg(ap, gint);
 
652
                                                c(printf("got gint '%d'\n", d));
 
653
                                                memcpy(buffer, start, p-start);
 
654
                                                buffer[p-start] = 0;
 
655
                                                camel_stream_printf((CamelStream *)ic->mem, buffer, d);
 
656
                                        }
 
657
                                        break;
 
658
                                }
 
659
 
 
660
                                ps = p;
 
661
                        }
 
662
                        break;
 
663
                case '\\':      /* only for \\ really, we dont support \n\r etc at all */
 
664
                        c = *p;
 
665
                        if (c) {
 
666
                                g_assert(c == '\\');
 
667
                                camel_stream_write((CamelStream *)ic->mem, ps, p-ps);
 
668
                                p++;
 
669
                                ps = p;
 
670
                        }
 
671
                }
 
672
        }
 
673
 
 
674
        camel_stream_write((CamelStream *)ic->mem, ps, p-ps-1);
 
675
}
 
676
 
 
677
CamelIMAPXCommand *
 
678
camel_imapx_command_new(const gchar *name, const gchar *select, const gchar *fmt, ...)
 
679
{
 
680
        CamelIMAPXCommand *ic;
 
681
        static gint tag = 0;
 
682
        va_list ap;
 
683
 
 
684
        ic = g_malloc0(sizeof(*ic));
 
685
        ic->tag = tag++;
 
686
        ic->name = name;
 
687
        ic->mem = (CamelStreamMem *)camel_stream_mem_new();
 
688
        ic->select = g_strdup(select);
 
689
        camel_dlist_init(&ic->parts);
 
690
        ic->ex = camel_exception_new ();
 
691
 
 
692
        if (fmt && fmt[0]) {
 
693
                va_start(ap, fmt);
 
694
                imapx_command_addv(ic, fmt, ap);
 
695
                va_end(ap);
 
696
        }
 
697
 
 
698
        return ic;
 
699
}
 
700
 
 
701
void
 
702
camel_imapx_command_add(CamelIMAPXCommand *ic, const gchar *fmt, ...)
 
703
{
 
704
        va_list ap;
 
705
 
 
706
        g_assert(ic->mem);      /* gets reset on queue */
 
707
 
 
708
        if (fmt && fmt[0]) {
 
709
                va_start(ap, fmt);
 
710
                imapx_command_addv(ic, fmt, ap);
 
711
                va_end(ap);
 
712
        }
 
713
}
 
714
 
 
715
void
 
716
camel_imapx_command_free(CamelIMAPXCommand *ic)
 
717
{
 
718
        CamelIMAPXCommandPart *cp;
 
719
 
 
720
        if (ic == NULL)
 
721
                return;
 
722
 
 
723
        if (ic->mem)
 
724
                camel_object_unref((CamelObject *)ic->mem);
 
725
        imapx_free_status(ic->status);
 
726
        g_free(ic->select);
 
727
 
 
728
        while ((cp = ((CamelIMAPXCommandPart *)camel_dlist_remhead(&ic->parts)))) {
 
729
                g_free(cp->data);
 
730
                if (cp->ob) {
 
731
                        switch (cp->type & CAMEL_IMAPX_COMMAND_MASK) {
 
732
                        case CAMEL_IMAPX_COMMAND_FILE:
 
733
                        case CAMEL_IMAPX_COMMAND_STRING:
 
734
                                g_free(cp->ob);
 
735
                                break;
 
736
                        default:
 
737
                                camel_object_unref(cp->ob);
 
738
                        }
 
739
                }
 
740
                g_free(cp);
 
741
        }
 
742
 
 
743
        camel_exception_free (ic->ex);
 
744
        g_free(ic);
 
745
}
 
746
 
 
747
void
 
748
camel_imapx_command_close(CamelIMAPXCommand *ic)
 
749
{
 
750
        if (ic->mem) {
 
751
                c(printf("completing command buffer is [%d] '%.*s'\n", ic->mem->buffer->len, (gint)ic->mem->buffer->len, ic->mem->buffer->data));
 
752
                if (ic->mem->buffer->len > 0)
 
753
                        imapx_command_add_part(ic, CAMEL_IMAPX_COMMAND_SIMPLE, NULL);
 
754
 
 
755
                camel_object_unref((CamelObject *)ic->mem);
 
756
                ic->mem = NULL;
 
757
        }
 
758
}
 
759
 
 
760
/* Must hold QUEUE_LOCK */
 
761
static gboolean
 
762
imapx_command_start (CamelIMAPXServer *imap, CamelIMAPXCommand *ic)
 
763
{
 
764
        CamelIMAPXCommandPart *cp;
 
765
 
 
766
        camel_imapx_command_close(ic);
 
767
        cp = (CamelIMAPXCommandPart *)ic->parts.head;
 
768
        g_assert(cp->next);
 
769
        ic->current = cp;
 
770
 
 
771
        /* TODO: If we support literal+ we should be able to write the whole command out
 
772
           at this point .... >here< */
 
773
 
 
774
        if (cp->type & CAMEL_IMAPX_COMMAND_CONTINUATION)
 
775
                imap->literal = ic;
 
776
 
 
777
        camel_dlist_addtail(&imap->active, (CamelDListNode *)ic);
 
778
 
 
779
        g_static_rec_mutex_lock (&imap->ostream_lock);
 
780
 
 
781
        c(printf("Staring command (active=%d,%s) %c%05u %s\r\n", camel_dlist_length(&imap->active), imap->literal?" literal":"", imap->tagprefix, ic->tag, cp->data));
 
782
        if (!imap->stream || camel_stream_printf((CamelStream *)imap->stream, "%c%05u %s\r\n", imap->tagprefix, ic->tag, cp->data) == -1) {
 
783
                g_static_rec_mutex_unlock (&imap->ostream_lock);
 
784
 
 
785
                camel_exception_set (ic->ex, 1, "Failed to issue the command");
 
786
                camel_dlist_remove ((CamelDListNode *)ic);
 
787
                if (ic && ic->complete)
 
788
                        ic->complete (imap, ic);
 
789
                return FALSE;
 
790
        }
 
791
 
 
792
        g_static_rec_mutex_unlock (&imap->ostream_lock);
 
793
 
 
794
        return TRUE;
 
795
}
 
796
 
 
797
/* See if we can start another task yet.
 
798
 
 
799
        If we're waiting for a literal, we cannot proceed.
 
800
 
 
801
        If we're about to change the folder we're
 
802
        looking at from user-direction, we dont proceed.
 
803
 
 
804
        If we have a folder selected, first see if any
 
805
        jobs are waiting on it, but only if they are
 
806
        at least as high priority as anything we
 
807
        have running.
 
808
 
 
809
        If we dont, select the first folder required,
 
810
        then queue all the outstanding jobs on it, that
 
811
        are at least as high priority as the first.
 
812
 
 
813
        must have QUEUE lock */
 
814
 
 
815
static void
 
816
imapx_command_start_next(CamelIMAPXServer *is, CamelException *ex)
 
817
{
 
818
        CamelIMAPXCommand *ic, *nc;
 
819
        gint count = 0;
 
820
        gint pri = -128;
 
821
 
 
822
        c(printf("** Starting next command\n"));
 
823
        if (is->literal != NULL || is->select_pending != NULL) {
 
824
                c(if (is->select_pending))
 
825
                        c(printf("* no, waiting for literal/pending select '%s'\n", is->select_pending->full_name));
 
826
 
 
827
                /* TODO prolly start the store operations which do not require any folder to be selected */
 
828
                return;
 
829
        }
 
830
 
 
831
        if (imapx_idle_supported (is) && is->state == IMAPX_SELECTED) {
 
832
                gboolean empty = imapx_is_command_queue_empty (is);
 
833
 
 
834
                if (imapx_in_idle (is) && !camel_dlist_empty (&is->queue)) {
 
835
                        imapx_stop_idle (is, ex);
 
836
                        c(printf ("waiting for idle to stop \n"));
 
837
                        return;
 
838
                } else if (empty && !imapx_in_idle (is)) {
 
839
                        imapx_start_idle (is);
 
840
                        c(printf ("starting idle \n"));
 
841
                        return;
 
842
                }
 
843
        }
 
844
 
 
845
        ic = (CamelIMAPXCommand *)is->queue.head;
 
846
        nc = ic->next;
 
847
        if (nc == NULL) {
 
848
                c(printf("* no, no jobs\n"));
 
849
                return;
 
850
        }
 
851
 
 
852
        /* See if any queued jobs on this select first */
 
853
        if (is->select) {
 
854
                c(printf("- we're selected on '%s', current jobs?\n", is->select));
 
855
                for (ic = (CamelIMAPXCommand *)is->active.head;ic->next;ic=ic->next) {
 
856
                        c(printf("-  %3d '%s'\n", (gint)ic->pri, ic->name));
 
857
                        if (ic->pri > pri)
 
858
                                pri = ic->pri;
 
859
                        count++;
 
860
                        if (count > MAX_COMMANDS) {
 
861
                                c(printf("** too many jobs busy, waiting for results for now\n"));
 
862
                                return;
 
863
                        }
 
864
                }
 
865
                
 
866
                c(printf("-- Checking job queue\n"));
 
867
                count = 0;
 
868
                ic = (CamelIMAPXCommand *)is->queue.head;
 
869
                nc = ic->next;
 
870
                while (nc && is->literal == NULL && count < MAX_COMMANDS && ic->pri >= pri) {
 
871
                        c(printf("-- %3d '%s'?\n", (gint)ic->pri, ic->name));
 
872
                        if (ic->select == NULL || strcmp(ic->select, is->select) == 0) {
 
873
                                c(printf("--> starting '%s'\n", ic->name));
 
874
                                pri = ic->pri;
 
875
                                camel_dlist_remove((CamelDListNode *)ic);
 
876
                                imapx_command_start(is, ic);
 
877
                                count++;
 
878
                        } else 
 
879
                                break;
 
880
                        ic = nc;
 
881
                        nc = nc->next;
 
882
                }
 
883
 
 
884
                if (count)
 
885
                        return;
 
886
 
 
887
                ic = (CamelIMAPXCommand *)is->queue.head;
 
888
        }
 
889
 
 
890
        /* If we need to select a folder for the first command, do it now, once
 
891
           it is complete it will re-call us if it succeeded */
 
892
        if (ic->job->folder) {
 
893
                imapx_select(is, ic->job->folder, FALSE, ex);
 
894
        } else {
 
895
                pri = ic->pri;
 
896
                nc = ic->next;
 
897
                count = 0;
 
898
                while (nc && is->literal == NULL && count < MAX_COMMANDS && ic->pri >= pri) {
 
899
                        if (ic->select == NULL || (is->select && strcmp(ic->select, is->select))) {
 
900
                                c(printf("* queueing job %3d '%s'\n", (gint)ic->pri, ic->name));
 
901
                                pri = ic->pri;
 
902
                                camel_dlist_remove((CamelDListNode *)ic);
 
903
                                if (!imapx_command_start(is, ic)) {
 
904
                                        QUEUE_UNLOCK (is);
 
905
                                        ic->complete (is, ic);
 
906
                                        QUEUE_LOCK (is);
 
907
                                }
 
908
                                count++;
 
909
                        }
 
910
                        ic = nc;
 
911
                        nc = nc->next;
 
912
                }
 
913
        }
 
914
}
 
915
 
 
916
static gboolean
 
917
imapx_is_command_queue_empty (CamelIMAPXServer *is)
 
918
{
 
919
        gboolean ret = FALSE;
 
920
 
 
921
        if (camel_dlist_empty (&is->queue) && camel_dlist_empty (&is->active))
 
922
                ret = TRUE;
 
923
 
 
924
        return ret;
 
925
}
 
926
 
 
927
static void
 
928
imapx_command_queue(CamelIMAPXServer *is, CamelIMAPXCommand *ic)
 
929
{
 
930
        CamelIMAPXCommand *scan;
 
931
 
 
932
        /* We enqueue in priority order, new messages have
 
933
           higher priority than older messages with the same priority */
 
934
 
 
935
        camel_imapx_command_close(ic);
 
936
 
 
937
        c(printf("enqueue job '%.*s'\n", ((CamelIMAPXCommandPart *)ic->parts.head)->data_size, ((CamelIMAPXCommandPart *)ic->parts.head)->data));
 
938
 
 
939
        QUEUE_LOCK(is);
 
940
 
 
941
        scan = (CamelIMAPXCommand *)is->queue.head;
 
942
        if (scan->next == NULL)
 
943
                camel_dlist_addtail(&is->queue, (CamelDListNode *)ic);
 
944
        else {
 
945
                while (scan->next) {
 
946
                        if (ic->pri >= scan->pri)
 
947
                                break;
 
948
                        scan = scan->next;
 
949
                }
 
950
 
 
951
                scan->prev->next = ic;
 
952
                ic->next = scan;
 
953
                ic->prev = scan->prev;
 
954
                scan->prev = ic;
 
955
        }
 
956
 
 
957
        imapx_command_start_next (is, NULL);
 
958
 
 
959
        QUEUE_UNLOCK(is);
 
960
 
 
961
        return;
 
962
}
 
963
 
 
964
/* Must have QUEUE lock */
 
965
static CamelIMAPXCommand *
 
966
imapx_find_command_tag(CamelIMAPXServer *imap, guint tag)
 
967
{
 
968
        CamelIMAPXCommand *ic = NULL;
 
969
 
 
970
        QUEUE_LOCK (imap);
 
971
 
 
972
        ic = imap->literal;
 
973
        if (ic && ic->tag == tag)
 
974
                goto found;
 
975
 
 
976
        for (ic = (CamelIMAPXCommand *)imap->active.head;ic->next;ic=ic->next)
 
977
                if (ic->tag == tag)
 
978
                        goto found;
 
979
found:
 
980
        QUEUE_UNLOCK (imap);
 
981
 
 
982
        return ic;
 
983
}
 
984
 
 
985
static gboolean
 
986
imapx_job_matches (const gchar *folder_name, CamelIMAPXJob *job, guint32 type, const gchar *uid)
 
987
{
 
988
        switch (job->type) {
 
989
                case IMAPX_JOB_GET_MESSAGE:
 
990
                        if (folder_name && strcmp(job->folder->full_name, folder_name) == 0
 
991
                                        && strcmp(job->u.get_message.uid, uid) == 0)
 
992
                                return TRUE;
 
993
                        break;
 
994
                case IMAPX_JOB_FETCH_NEW_MESSAGES:
 
995
                case IMAPX_JOB_REFRESH_INFO:
 
996
                case IMAPX_JOB_SYNC_CHANGES:
 
997
                case IMAPX_JOB_EXPUNGE:
 
998
                        if (folder_name
 
999
                                        && strcmp(job->folder->full_name, folder_name) == 0)
 
1000
                                return TRUE;
 
1001
                        break;
 
1002
                case IMAPX_JOB_LIST:
 
1003
                        return TRUE;
 
1004
        }
 
1005
 
 
1006
        return FALSE;
 
1007
}
 
1008
 
 
1009
/* Must not have QUEUE lock */
 
1010
static CamelIMAPXJob *
 
1011
imapx_match_active_job (CamelIMAPXServer *is, guint32 type, const gchar *uid)
 
1012
{
 
1013
        CamelIMAPXJob *job = NULL;
 
1014
        CamelIMAPXCommand *ic;
 
1015
 
 
1016
        QUEUE_LOCK(is);
 
1017
 
 
1018
        for (ic = (CamelIMAPXCommand *)is->active.head;ic->next;ic=ic->next) {
 
1019
                job = ic->job;
 
1020
                if (!job || !(job->type & type))
 
1021
                        continue;
 
1022
 
 
1023
                if (imapx_job_matches (is->select, job, type, uid))
 
1024
                        goto found;
 
1025
        }
 
1026
        job = NULL;
 
1027
found:
 
1028
        QUEUE_UNLOCK(is);
 
1029
        return job;
 
1030
}
 
1031
 
 
1032
static CamelIMAPXJob *
 
1033
imapx_is_job_in_queue (CamelIMAPXServer *is, const gchar *folder_name, guint32 type, const gchar *uid)
 
1034
{
 
1035
        CamelDListNode *node;
 
1036
        CamelIMAPXJob *job = NULL;
 
1037
        gboolean found = FALSE;
 
1038
 
 
1039
        QUEUE_LOCK(is);
 
1040
 
 
1041
        for (node = is->jobs.head;node->next;node = job->msg.ln.next) {
 
1042
                job = (CamelIMAPXJob *) node;
 
1043
 
 
1044
                if (!job || !(job->type & type))
 
1045
                        continue;
 
1046
 
 
1047
                if (imapx_job_matches (folder_name, job, type, uid)) {
 
1048
                        found = TRUE;
 
1049
                        break;
 
1050
                }
 
1051
        }
 
1052
 
 
1053
        QUEUE_UNLOCK (is);
 
1054
 
 
1055
        if (found)
 
1056
                return job;
 
1057
        else
 
1058
                return NULL;
 
1059
}
 
1060
 
 
1061
/* handle any untagged responses */
 
1062
static gint
 
1063
imapx_untagged(CamelIMAPXServer *imap, CamelException *ex)
 
1064
{
 
1065
        guint id, len;
 
1066
        guchar *token, *p, c;
 
1067
        gint tok;
 
1068
        gboolean lsub = FALSE;
 
1069
        struct _status_info *sinfo;
 
1070
 
 
1071
        e(printf("got untagged response\n"));
 
1072
        id = 0;
 
1073
        tok = camel_imapx_stream_token(imap->stream, &token, &len, ex);
 
1074
        if (camel_exception_is_set (ex))
 
1075
                return -1;
 
1076
 
 
1077
        if (tok == IMAPX_TOK_INT) {
 
1078
                id = strtoul((gchar *) token, NULL, 10);
 
1079
                tok = camel_imapx_stream_token(imap->stream, &token, &len, ex);
 
1080
        }
 
1081
 
 
1082
        if (tok == '\n') {
 
1083
                camel_exception_set (ex, 1, "truncated server response");
 
1084
                return -1;
 
1085
        }
 
1086
 
 
1087
        e(printf("Have token '%s' id %d\n", token, id));
 
1088
        p = token;
 
1089
        while ((c = *p))
 
1090
                *p++ = toupper((gchar) c);
 
1091
 
 
1092
        switch (imapx_tokenise ((const gchar *) token, len)) {
 
1093
        case IMAPX_CAPABILITY:
 
1094
                if (imap->cinfo)
 
1095
                        imapx_free_capability(imap->cinfo);
 
1096
                imap->cinfo = imapx_parse_capability(imap->stream, ex);
 
1097
                c(printf("got capability flags %08x\n", imap->cinfo->capa));
 
1098
                return 0;
 
1099
        case IMAPX_EXPUNGE: {
 
1100
                guint32 expunge = id;
 
1101
                CamelIMAPXJob *job = imapx_match_active_job (imap, IMAPX_JOB_EXPUNGE, NULL);
 
1102
 
 
1103
                /* If there is a job running, let it handle the deletion */
 
1104
                if (job)
 
1105
                        break;
 
1106
 
 
1107
                c(printf("expunged: %d\n", id));
 
1108
                if (imap->select_folder) {
 
1109
                        gchar *uid = NULL;
 
1110
                        CamelMessageInfo *mi;
 
1111
 
 
1112
                        uid = camel_folder_summary_uid_from_index (imap->select_folder->summary, expunge - 1);
 
1113
                        if (!uid)
 
1114
                                break;
 
1115
 
 
1116
                        if (imap->changes == NULL)
 
1117
                                imap->changes = camel_folder_change_info_new();
 
1118
 
 
1119
                        mi = camel_folder_summary_uid (imap->select_folder->summary, uid);
 
1120
                        if (mi) {
 
1121
                                imapx_update_summary_for_removed_message (mi, imap->select_folder);
 
1122
                                camel_message_info_free (mi);
 
1123
                        }
 
1124
 
 
1125
                        camel_folder_summary_remove_uid_fast (imap->select_folder->summary, uid);
 
1126
                        imap->expunged = g_slist_prepend (imap->expunged, uid);
 
1127
 
 
1128
                        camel_folder_change_info_remove_uid (imap->changes, uid);
 
1129
 
 
1130
                        if (imapx_idle_supported (imap) && imapx_in_idle (imap)) {
 
1131
                                camel_db_delete_uids (imap->store->cdb_w, imap->select_folder->full_name, imap->expunged, NULL);
 
1132
                                imapx_update_store_summary (imap->select_folder);
 
1133
                                camel_object_trigger_event(imap->select_folder, "folder_changed", imap->changes);
 
1134
 
 
1135
                                g_slist_foreach (imap->expunged, (GFunc) g_free, NULL);
 
1136
                                imap->expunged = NULL;
 
1137
                                camel_folder_change_info_clear (imap->changes);
 
1138
                        }
 
1139
                }
 
1140
 
 
1141
                break;
 
1142
        }
 
1143
        case IMAPX_NAMESPACE: {
 
1144
                CamelIMAPXNamespaceList *nsl = NULL;
 
1145
 
 
1146
                nsl = imapx_parse_namespace_list (imap->stream, ex);
 
1147
                if (nsl != NULL) {
 
1148
                        CamelIMAPXStore *imapx_store = (CamelIMAPXStore *) imap->store;
 
1149
                        CamelIMAPXStoreNamespace *ns;
 
1150
 
 
1151
                        imapx_store->summary->namespaces = nsl;
 
1152
                        camel_store_summary_touch ((CamelStoreSummary *) imapx_store->summary);
 
1153
 
 
1154
                        /* TODO Need to remove imapx_store->dir_sep to support multiple namespaces */
 
1155
                        ns = nsl->personal;
 
1156
                        if (ns)
 
1157
                                imapx_store->dir_sep = ns->sep;
 
1158
                }
 
1159
 
 
1160
                return 0;
 
1161
        }
 
1162
        case IMAPX_EXISTS:
 
1163
                c(printf("exists: %d\n", id));
 
1164
                imap->exists = id;
 
1165
 
 
1166
                if (imap->select_folder)
 
1167
                        ((CamelIMAPXFolder *) imap->select_folder)->exists_on_server = id;
 
1168
 
 
1169
                if (imapx_idle_supported (imap) && imapx_in_idle (imap)) {
 
1170
                        if (camel_folder_summary_count (imap->select_folder->summary) < id)
 
1171
                                imapx_stop_idle (imap, ex);
 
1172
                }
 
1173
 
 
1174
                break;
 
1175
        case IMAPX_FLAGS: {
 
1176
                guint32 flags;
 
1177
 
 
1178
                imapx_parse_flags(imap->stream, &flags, NULL, ex);
 
1179
 
 
1180
                c(printf("flags: %08x\n", flags));
 
1181
                break;
 
1182
        }
 
1183
        case IMAPX_FETCH: {
 
1184
                struct _fetch_info *finfo;
 
1185
 
 
1186
                finfo = imapx_parse_fetch(imap->stream, ex);
 
1187
                if (camel_exception_is_set (ex)) {
 
1188
                        imapx_free_fetch(finfo);
 
1189
                        return -1;
 
1190
                }
 
1191
 
 
1192
                if ((finfo->got & (FETCH_BODY|FETCH_UID)) == (FETCH_BODY|FETCH_UID)) {
 
1193
                        CamelIMAPXJob *job = imapx_match_active_job(imap, IMAPX_JOB_GET_MESSAGE, finfo->uid);
 
1194
 
 
1195
                        /* This must've been a get-message request, fill out the body stream,
 
1196
                           in the right spot */
 
1197
 
 
1198
                        if (job && !camel_exception_is_set (job->ex)) {
 
1199
                                if (job->u.get_message.use_multi_fetch) {
 
1200
                                        job->u.get_message.body_offset = finfo->offset;
 
1201
                                        camel_seekable_stream_seek((CamelSeekableStream *)job->u.get_message.stream, finfo->offset, CAMEL_STREAM_SET);
 
1202
                                }
 
1203
 
 
1204
                                job->u.get_message.body_len = camel_stream_write_to_stream(finfo->body, job->u.get_message.stream);
 
1205
                                if (job->u.get_message.body_len == -1)
 
1206
                                        camel_exception_setv(job->ex, 1, "error writing to cache stream: %s\n", g_strerror(errno));
 
1207
                        }
 
1208
                }
 
1209
 
 
1210
                if (finfo->got & FETCH_FLAGS && !(finfo->got & FETCH_UID)) {
 
1211
                        if (imap->select_folder) {
 
1212
                                CamelFolder *folder;
 
1213
                                CamelMessageInfo *mi = NULL;
 
1214
                                gboolean changed = FALSE;
 
1215
                                gchar *uid = NULL;
 
1216
 
 
1217
                                camel_object_ref(imap->select_folder);
 
1218
                                folder = imap->select_folder;
 
1219
 
 
1220
                                c(printf("flag changed: %d\n", id));
 
1221
 
 
1222
                                if ( (uid = camel_folder_summary_uid_from_index (folder->summary, id - 1)))
 
1223
                                {
 
1224
                                        mi = camel_folder_summary_uid (folder->summary, uid);
 
1225
                                        if (mi)
 
1226
                                                changed = imapx_update_message_info_flags (mi, finfo->flags, finfo->user_flags, folder);
 
1227
                                        finfo->user_flags = NULL;
 
1228
                                }
 
1229
 
 
1230
                                if (changed) {
 
1231
                                        if (imap->changes == NULL)
 
1232
                                                imap->changes = camel_folder_change_info_new();
 
1233
 
 
1234
                                        camel_folder_change_info_change_uid(imap->changes, uid);
 
1235
                                        g_free (uid);
 
1236
                                }
 
1237
 
 
1238
                                if (imapx_idle_supported (imap) && changed && imapx_in_idle (imap)) {
 
1239
                                        camel_folder_summary_save_to_db (imap->select_folder->summary, NULL);
 
1240
                                        imapx_update_store_summary (imap->select_folder);
 
1241
                                        camel_object_trigger_event(imap->select_folder, "folder_changed", imap->changes);
 
1242
                                        camel_folder_change_info_clear (imap->changes);
 
1243
                                }
 
1244
 
 
1245
                                if (mi)
 
1246
                                        camel_message_info_free (mi);
 
1247
                                camel_object_unref (folder);
 
1248
                        }
 
1249
                }
 
1250
 
 
1251
                if ((finfo->got & (FETCH_FLAGS|FETCH_UID)) == (FETCH_FLAGS|FETCH_UID) && !(finfo->got & FETCH_HEADER)) {
 
1252
                        CamelIMAPXJob *job = imapx_match_active_job (imap, IMAPX_JOB_FETCH_NEW_MESSAGES|IMAPX_JOB_REFRESH_INFO, NULL);
 
1253
 
 
1254
                        /* This is either a refresh_info job, check to see if it is and update
 
1255
                           if so, otherwise it must've been an unsolicited response, so update
 
1256
                           the summary to match */
 
1257
 
 
1258
                        if (job) {
 
1259
                                struct _refresh_info r;
 
1260
 
 
1261
                                r.uid = finfo->uid;
 
1262
                                finfo->uid = NULL;
 
1263
                                r.server_flags = finfo->flags;
 
1264
                                r.server_user_flags = finfo->user_flags;
 
1265
                                finfo->user_flags = NULL;
 
1266
                                r.exists = FALSE;
 
1267
                                g_array_append_val(job->u.refresh_info.infos, r);
 
1268
                        } else {
 
1269
                        }
 
1270
                }
 
1271
 
 
1272
                if ((finfo->got & (FETCH_HEADER|FETCH_UID)) == (FETCH_HEADER|FETCH_UID)) {
 
1273
                        CamelIMAPXJob *job = imapx_match_active_job (imap, IMAPX_JOB_FETCH_NEW_MESSAGES|IMAPX_JOB_REFRESH_INFO, NULL);
 
1274
 
 
1275
                        /* This must be a refresh info job as well, but it has asked for
 
1276
                           new messages to be added to the index */
 
1277
 
 
1278
                        if (job) {
 
1279
                                CamelMimeParser *mp;
 
1280
                                CamelMessageInfo *mi;
 
1281
 
 
1282
                                /* Do we want to save these headers for later too?  Do we care? */
 
1283
 
 
1284
                                mp = camel_mime_parser_new();
 
1285
                                camel_mime_parser_init_with_stream(mp, finfo->header);
 
1286
                                mi = camel_folder_summary_info_new_from_parser(job->folder->summary, mp);
 
1287
                                camel_object_unref(mp);
 
1288
 
 
1289
                                if (mi) {
 
1290
                                        guint32 server_flags;
 
1291
                                        CamelFlag *server_user_flags;
 
1292
                                        CamelMessageInfoBase *binfo;
 
1293
 
 
1294
                                        mi->uid = camel_pstring_strdup (finfo->uid);
 
1295
 
 
1296
                                        if (!(finfo->got & FETCH_FLAGS))
 
1297
                                        {
 
1298
                                                struct _refresh_info *r = NULL;
 
1299
                                                GArray *infos = job->u.refresh_info.infos;
 
1300
                                                gint min = job->u.refresh_info.last_index;
 
1301
                                                gint max = job->u.refresh_info.index, mid;
 
1302
                                                gboolean found = FALSE;
 
1303
 
 
1304
                                                /* array is sorted, so use a binary search */
 
1305
                                                do {
 
1306
                                                        gint cmp = 0;
 
1307
 
 
1308
                                                        mid = (min + max)/2;
 
1309
                                                        r = &g_array_index(infos, struct _refresh_info, mid);
 
1310
                                                        cmp = imapx_uid_cmp (finfo->uid, r->uid, NULL);
 
1311
 
 
1312
                                                        if (cmp > 0)
 
1313
                                                                min = mid + 1;
 
1314
                                                        else if (cmp < 0)
 
1315
                                                                max = mid - 1;
 
1316
                                                        else
 
1317
                                                                found = TRUE;
 
1318
 
 
1319
                                                } while (!found && min <= max);
 
1320
 
 
1321
                                                if (!found)
 
1322
                                                        g_assert_not_reached ();
 
1323
 
 
1324
                                                server_flags = r->server_flags;
 
1325
                                                server_user_flags = r->server_user_flags;
 
1326
                                        } else {
 
1327
                                                server_flags = finfo->flags;
 
1328
                                                server_user_flags = finfo->user_flags;
 
1329
                                                /* free user_flags ? */
 
1330
                                                finfo->user_flags = NULL;
 
1331
                                        }
 
1332
 
 
1333
                                        binfo = (CamelMessageInfoBase *) mi;
 
1334
                                        binfo->size = finfo->size;
 
1335
 
 
1336
                                        if (!camel_folder_summary_check_uid (job->folder->summary, mi->uid)) {
 
1337
                                                CamelIMAPXFolder *ifolder = (CamelIMAPXFolder *)job->folder;
 
1338
                                                
 
1339
                                                camel_folder_summary_add(job->folder->summary, mi);
 
1340
                                                imapx_set_message_info_flags_for_new_message (mi, server_flags, server_user_flags, job->folder);
 
1341
                                                camel_folder_change_info_add_uid (job->u.refresh_info.changes, mi->uid);
 
1342
 
 
1343
                                                if (!g_hash_table_lookup (ifolder->ignore_recent, mi->uid)) {
 
1344
                                                        camel_folder_change_info_recent_uid (job->u.refresh_info.changes, mi->uid);
 
1345
                                                        g_hash_table_remove (ifolder->ignore_recent, mi->uid);
 
1346
                                                }
 
1347
 
 
1348
                                                if (job->op)
 
1349
                                                        camel_operation_progress (job->op, (camel_folder_summary_count (job->folder->summary) * 100)/imap->exists);
 
1350
                                        }
 
1351
                                }
 
1352
                        }
 
1353
                }
 
1354
 
 
1355
                imapx_free_fetch(finfo);
 
1356
                break;
 
1357
        }
 
1358
        case IMAPX_LSUB:
 
1359
                lsub = TRUE;
 
1360
        case IMAPX_LIST: {
 
1361
                struct _list_info *linfo = imapx_parse_list(imap->stream, ex);
 
1362
                CamelIMAPXJob *job = imapx_match_active_job(imap, IMAPX_JOB_LIST, linfo->name);
 
1363
 
 
1364
                // TODO: we want to make sure the names match?
 
1365
 
 
1366
                if (job->u.list.flags & CAMEL_STORE_FOLDER_INFO_SUBSCRIBED) {
 
1367
                        c(printf("lsub: '%s' (%c)\n", linfo->name, linfo->separator));
 
1368
 
 
1369
                } else {
 
1370
                        c(printf("list: '%s' (%c)\n", linfo->name, linfo->separator));
 
1371
                }
 
1372
 
 
1373
                if (job && g_hash_table_lookup(job->u.list.folders, linfo->name) == NULL) {
 
1374
                        if (lsub)
 
1375
                                linfo->flags |= CAMEL_FOLDER_SUBSCRIBED;
 
1376
                        g_hash_table_insert(job->u.list.folders, linfo->name, linfo);
 
1377
                } else {
 
1378
                        g_warning("got list response but no current listing job happening?\n");
 
1379
                        imapx_free_list(linfo);
 
1380
                }
 
1381
                break;
 
1382
        }
 
1383
        case IMAPX_RECENT:
 
1384
                c(printf("recent: %d\n", id));
 
1385
                imap->recent = id;
 
1386
                break;
 
1387
        case IMAPX_STATUS: {
 
1388
                struct _state_info *sinfo = imapx_parse_status_info (imap->stream, ex);
 
1389
                if (sinfo) {
 
1390
                        /* this is what we use atm */
 
1391
                        imap->exists = sinfo->messages;
 
1392
                        imap->unread = sinfo->unseen;
 
1393
 
 
1394
                        g_free (sinfo);
 
1395
                }
 
1396
                break;
 
1397
        }
 
1398
        case IMAPX_BYE: case IMAPX_OK: case IMAPX_NO: case IMAPX_BAD: case IMAPX_PREAUTH:
 
1399
                /* TODO: validate which ones of these can happen as unsolicited responses */
 
1400
                /* TODO: handle bye/preauth differently */
 
1401
                camel_imapx_stream_ungettoken(imap->stream, tok, token, len);
 
1402
                sinfo = imapx_parse_status(imap->stream, ex);
 
1403
                camel_object_trigger_event(imap, "status", sinfo);
 
1404
                switch (sinfo->condition) {
 
1405
                case IMAPX_READ_WRITE:
 
1406
                        imap->mode = IMAPX_MODE_READ|IMAPX_MODE_WRITE;
 
1407
                        c(printf("folder is read-write\n"));
 
1408
                        break;
 
1409
                case IMAPX_READ_ONLY:
 
1410
                        imap->mode = IMAPX_MODE_READ;
 
1411
                        c(printf("folder is read-only\n"));
 
1412
                        break;
 
1413
                case IMAPX_UIDVALIDITY:
 
1414
                        imap->uidvalidity = sinfo->u.uidvalidity;
 
1415
                        break;
 
1416
                case IMAPX_UNSEEN:
 
1417
                        imap->unseen = sinfo->u.unseen;
 
1418
                        break;
 
1419
                case IMAPX_PERMANENTFLAGS:
 
1420
                        imap->permanentflags = sinfo->u.permanentflags;
 
1421
                        break;
 
1422
                case IMAPX_ALERT:
 
1423
                        c(printf("ALERT!: %s\n", sinfo->text));
 
1424
                        break;
 
1425
                case IMAPX_PARSE:
 
1426
                        c(printf("PARSE: %s\n", sinfo->text));
 
1427
                        break;
 
1428
                default:
 
1429
                        break;
 
1430
                }
 
1431
                imapx_free_status(sinfo);
 
1432
                return 0;
 
1433
        default:
 
1434
                /* unknown response, just ignore it */
 
1435
                c(printf("unknown token: %s\n", token));
 
1436
        }
 
1437
 
 
1438
        return camel_imapx_stream_skip(imap->stream, ex);
 
1439
}
 
1440
 
 
1441
/* handle any continuation requests
 
1442
   either data continuations, or auth continuation */
 
1443
static gint
 
1444
imapx_continuation(CamelIMAPXServer *imap, CamelException *ex)
 
1445
{
 
1446
        CamelIMAPXCommand *ic, *newliteral = NULL;
 
1447
        CamelIMAPXCommandPart *cp;
 
1448
 
 
1449
        c(printf("got continuation response\n"));
 
1450
 
 
1451
        /* The 'literal' pointer is like a write-lock, nothing else
 
1452
           can write while we have it ... so we dont need any
 
1453
           ohter lock here.  All other writes go through
 
1454
           queue-lock */
 
1455
        if (imapx_idle_supported (imap) && imapx_in_idle (imap)) {
 
1456
                camel_imapx_stream_skip (imap->stream, ex);
 
1457
 
 
1458
                c(printf("Got continuation response for IDLE \n"));
 
1459
                imap->idle->started = TRUE;
 
1460
 
 
1461
                QUEUE_LOCK(imap);
 
1462
                imap->literal = NULL;
 
1463
                imapx_command_start_next(imap, ex);
 
1464
                QUEUE_UNLOCK(imap);
 
1465
 
 
1466
                return 1;
 
1467
        }
 
1468
 
 
1469
        ic = imap->literal;
 
1470
        if (ic == NULL) {
 
1471
                camel_imapx_stream_skip(imap->stream, ex);
 
1472
                c(printf("got continuation response with no outstanding continuation requests?\n"));
 
1473
                return 1;
 
1474
        }
 
1475
 
 
1476
        c(printf("got continuation response for data\n"));
 
1477
        cp = ic->current;
 
1478
        switch (cp->type & CAMEL_IMAPX_COMMAND_MASK) {
 
1479
        case CAMEL_IMAPX_COMMAND_DATAWRAPPER:
 
1480
                c(printf("writing data wrapper to literal\n"));
 
1481
                camel_data_wrapper_write_to_stream((CamelDataWrapper *)cp->ob, (CamelStream *)imap->stream);
 
1482
                break;
 
1483
        case CAMEL_IMAPX_COMMAND_STREAM:
 
1484
                c(printf("writing stream to literal\n"));
 
1485
                camel_stream_write_to_stream((CamelStream *)cp->ob, (CamelStream *)imap->stream);
 
1486
                break;
 
1487
        case CAMEL_IMAPX_COMMAND_AUTH: {
 
1488
                gchar *resp;
 
1489
                guchar *token;
 
1490
                gint tok;
 
1491
                guint len;
 
1492
 
 
1493
                tok = camel_imapx_stream_token(imap->stream, &token, &len, ex);
 
1494
                resp = camel_sasl_challenge_base64((CamelSasl *)cp->ob, (const gchar *) token, ex);
 
1495
                if (camel_exception_is_set(ex))
 
1496
                        return -1;
 
1497
 
 
1498
                c(printf("got auth continuation, feeding token '%s' back to auth mech\n", resp));
 
1499
 
 
1500
                camel_stream_write((CamelStream *)imap->stream, resp, strlen(resp));
 
1501
 
 
1502
                /* we want to keep getting called until we get a status reponse from the server
 
1503
                   ignore what sasl tells us */
 
1504
                newliteral = ic;
 
1505
 
 
1506
                break; }
 
1507
        case CAMEL_IMAPX_COMMAND_FILE: {
 
1508
                CamelStream *file;
 
1509
 
 
1510
                c(printf("writing file '%s' to literal\n", (gchar *)cp->ob));
 
1511
 
 
1512
                // FIXME: errors
 
1513
                if (cp->ob && (file = camel_stream_fs_new_with_name(cp->ob, O_RDONLY, 0))) {
 
1514
                        camel_stream_write_to_stream(file, (CamelStream *)imap->stream);
 
1515
                        camel_object_unref(file);
 
1516
                } else if (cp->ob_size > 0) {
 
1517
                        // Server is expecting data ... ummm, send it zeros?  abort?
 
1518
                }
 
1519
                break; }
 
1520
        case CAMEL_IMAPX_COMMAND_STRING:
 
1521
                camel_stream_write((CamelStream *)imap->stream, cp->ob, cp->ob_size);
 
1522
                break;
 
1523
        default:
 
1524
                /* should we just ignore? */
 
1525
                imap->literal = NULL;
 
1526
                camel_exception_set (ex, 1, "continuation response for non-continuation request");
 
1527
                return -1;
 
1528
        }
 
1529
 
 
1530
        camel_imapx_stream_skip(imap->stream, ex);
 
1531
 
 
1532
        cp = cp->next;
 
1533
        if (cp->next) {
 
1534
                ic->current = cp;
 
1535
                c(printf("next part of command \"A%05u: %s\"\n", ic->tag, cp->data));
 
1536
                camel_stream_printf((CamelStream *)imap->stream, "%s\r\n", cp->data);
 
1537
                if (cp->type & CAMEL_IMAPX_COMMAND_CONTINUATION) {
 
1538
                        newliteral = ic;
 
1539
                } else {
 
1540
                        g_assert(cp->next->next == NULL);
 
1541
                }
 
1542
        } else {
 
1543
                c(printf("%p: queueing continuation\n", ic));
 
1544
                camel_stream_printf((CamelStream *)imap->stream, "\r\n");
 
1545
        }
 
1546
 
 
1547
        QUEUE_LOCK(imap);
 
1548
        imap->literal = newliteral;
 
1549
 
 
1550
        imapx_command_start_next(imap, ex);
 
1551
        QUEUE_UNLOCK(imap);
 
1552
 
 
1553
        return 1;
 
1554
}
 
1555
 
 
1556
/* handle a completion line */
 
1557
static gint
 
1558
imapx_completion(CamelIMAPXServer *imap, guchar *token, gint len, CamelException *ex)
 
1559
{
 
1560
        CamelIMAPXCommand *ic;
 
1561
        guint tag;
 
1562
 
 
1563
        if (token[0] != imap->tagprefix) {
 
1564
                camel_exception_setv (ex, 1, "Server sent unexpected response: %s", token);
 
1565
 
 
1566
                return -1;
 
1567
        }
 
1568
 
 
1569
        tag = strtoul( (const gchar *)token+1, NULL, 10);
 
1570
 
 
1571
        if ((ic = imapx_find_command_tag(imap, tag)) == NULL) {
 
1572
                camel_exception_setv (ex, 1, "got response tag unexpectedly: %s", token);
 
1573
 
 
1574
                return -1;
 
1575
        }
 
1576
 
 
1577
        c(printf("Got completion response for command %05u '%s'\n", ic->tag, ic->name));
 
1578
 
 
1579
        if (camel_folder_change_info_changed (imap->changes)) {
 
1580
                if (imap->changes->uid_changed->len)
 
1581
                        camel_folder_summary_save_to_db (imap->select_folder->summary, NULL);
 
1582
                else
 
1583
                        camel_db_delete_uids (imap->store->cdb_w, imap->select_folder->full_name, imap->expunged, NULL);
 
1584
 
 
1585
                if (imap->expunged) {
 
1586
                        g_slist_foreach (imap->expunged, (GFunc) g_free, NULL);
 
1587
                        imap->expunged = NULL;
 
1588
                }
 
1589
 
 
1590
                imapx_update_store_summary (imap->select_folder);
 
1591
                camel_object_trigger_event(imap->select_folder, "folder_changed", imap->changes);
 
1592
                camel_folder_change_info_clear (imap->changes);
 
1593
        }
 
1594
 
 
1595
        QUEUE_LOCK(imap);
 
1596
 
 
1597
        camel_dlist_remove((CamelDListNode *)ic);
 
1598
        camel_dlist_addtail(&imap->done, (CamelDListNode *)ic);
 
1599
        if (imap->literal == ic)
 
1600
                imap->literal = NULL;
 
1601
 
 
1602
        if (ic->current->next->next) {
 
1603
                QUEUE_UNLOCK(imap);
 
1604
                camel_exception_setv (ex, 1, "command still has unsent parts? %s", ic->name);
 
1605
 
 
1606
                return -1;
 
1607
        }
 
1608
 
 
1609
        /* A follow-on command might've already queued a new literal since were were done with ours? */
 
1610
//      if (imap->literal != NULL) {
 
1611
//              QUEUE_UNLOCK(imap);
 
1612
//              camel_exception_throw(1, "command still has outstanding continuation", imap->literal->name);
 
1613
//      }
 
1614
 
 
1615
        camel_dlist_remove ((CamelDListNode *) ic);
 
1616
        QUEUE_UNLOCK(imap);
 
1617
 
 
1618
        ic->status = imapx_parse_status(imap->stream, ex);
 
1619
 
 
1620
        if (ic->complete)
 
1621
                ic->complete (imap, ic);
 
1622
 
 
1623
        QUEUE_LOCK(imap);
 
1624
        imapx_command_start_next(imap, ex);
 
1625
        QUEUE_UNLOCK(imap);
 
1626
 
 
1627
        return 1;
 
1628
}
 
1629
 
 
1630
static void
 
1631
imapx_step(CamelIMAPXServer *is, CamelException *ex)
 
1632
{
 
1633
        guint len;
 
1634
        guchar *token;
 
1635
        gint tok;
 
1636
 
 
1637
        // poll ?  wait for other stuff? loop?
 
1638
        tok = camel_imapx_stream_token (is->stream, &token, &len, ex);
 
1639
        if (camel_exception_is_set (ex))
 
1640
                return;
 
1641
 
 
1642
        if (tok == '*')
 
1643
                imapx_untagged (is, ex);
 
1644
        else if (tok == IMAPX_TOK_TOKEN)
 
1645
                imapx_completion (is, token, len, ex);
 
1646
        else if (tok == '+')
 
1647
                imapx_continuation (is, ex);
 
1648
        else
 
1649
                camel_exception_set (ex, 1, "unexpected server response:");
 
1650
}
 
1651
 
 
1652
/* Used to run 1 command synchronously,
 
1653
   use for capa, login, and namespaces only. */
 
1654
static void
 
1655
imapx_command_run(CamelIMAPXServer *is, CamelIMAPXCommand *ic)
 
1656
/* throws IO,PARSE exception */
 
1657
{
 
1658
        camel_imapx_command_close(ic);
 
1659
 
 
1660
        QUEUE_LOCK(is);
 
1661
        imapx_command_start(is, ic);
 
1662
        QUEUE_UNLOCK(is);
 
1663
 
 
1664
        do {
 
1665
                imapx_step(is, ic->ex);
 
1666
        } while (ic->status == NULL && !camel_exception_is_set (ic->ex));
 
1667
 
 
1668
        QUEUE_LOCK(is);
 
1669
        camel_dlist_remove((CamelDListNode *)ic);
 
1670
        QUEUE_UNLOCK(is);
 
1671
}
 
1672
 
 
1673
static void
 
1674
imapx_command_complete (CamelIMAPXServer *is, CamelIMAPXCommand *ic)
 
1675
{
 
1676
        e_flag_set (ic->flag);
 
1677
}
 
1678
 
 
1679
/* change status to a job and remove command_run_sync */
 
1680
static void
 
1681
imapx_command_status_done (CamelIMAPXServer *is, CamelIMAPXCommand *ic)
 
1682
{
 
1683
        CamelIMAPXFolder *ifolder = (CamelIMAPXFolder *) ic->job->folder;
 
1684
 
 
1685
        ifolder->unread_on_server = is->unread;
 
1686
        e_flag_set (ic->flag);
 
1687
}
 
1688
 
 
1689
/* The caller should free the command as well */
 
1690
static void
 
1691
imapx_command_run_sync (CamelIMAPXServer *is, CamelIMAPXCommand *ic)
 
1692
{
 
1693
        CamelIMAPXCommandFunc complete = NULL;
 
1694
 
 
1695
        ic->flag = e_flag_new ();
 
1696
        complete = ic->complete;
 
1697
 
 
1698
        if (!ic->complete)
 
1699
                ic->complete = imapx_command_complete;
 
1700
 
 
1701
        imapx_command_queue (is, ic);
 
1702
        e_flag_wait (ic->flag);
 
1703
 
 
1704
        e_flag_free (ic->flag);
 
1705
        ic->flag = NULL;
 
1706
}
 
1707
 
 
1708
/* ********************************************************************** */
 
1709
/* Should be called when there are no more commands needed to complete the job */
 
1710
 
 
1711
static void
 
1712
imapx_job_done (CamelIMAPXServer *is, CamelIMAPXJob *job)
 
1713
{
 
1714
        QUEUE_LOCK (is);
 
1715
        camel_dlist_remove((CamelDListNode *)job);
 
1716
        QUEUE_UNLOCK (is);
 
1717
 
 
1718
        if (job->noreply) {
 
1719
                camel_exception_clear(job->ex);
 
1720
                g_free(job);
 
1721
        } else
 
1722
                camel_msgport_reply((CamelMsg *) job);
 
1723
}
 
1724
 
 
1725
static gboolean
 
1726
imapx_register_job (CamelIMAPXServer *is, CamelIMAPXJob *job)
 
1727
{
 
1728
        if (is->state >= IMAPX_AUTHENTICATED) {
 
1729
                QUEUE_LOCK (is);
 
1730
                camel_dlist_addhead (&is->jobs, (CamelDListNode *)job);
 
1731
                QUEUE_UNLOCK (is);
 
1732
 
 
1733
        } else {
 
1734
                e(printf ("NO connection yet, maybe user cancelled jobs earlier ?"));
 
1735
                camel_exception_set (job->ex, CAMEL_EXCEPTION_SERVICE_NOT_CONNECTED, "Not authenticated");
 
1736
                return FALSE;
 
1737
        }
 
1738
 
 
1739
        return TRUE;
 
1740
}
 
1741
 
 
1742
static void
 
1743
imapx_run_job (CamelIMAPXServer *is, CamelIMAPXJob *job)
 
1744
{
 
1745
        CamelMsgPort *reply = NULL;
 
1746
 
 
1747
        if (!job->noreply) {
 
1748
                reply = camel_msgport_new ();
 
1749
                job->msg.reply_port = reply;
 
1750
        }
 
1751
 
 
1752
        /* Any exceptions to the start should be reported async through our reply msgport */
 
1753
        job->start (is, job);
 
1754
 
 
1755
        if (!job->noreply) {
 
1756
                CamelMsg *completed;
 
1757
 
 
1758
                completed = camel_msgport_pop (reply);
 
1759
                camel_msgport_destroy (reply);
 
1760
 
 
1761
                g_assert(completed == (CamelMsg *)job);
 
1762
        }
 
1763
}
 
1764
 
 
1765
/* ********************************************************************** */
 
1766
// IDLE support
 
1767
 
 
1768
#define IDLE_LOCK(x) (g_mutex_lock((x)->idle_lock))
 
1769
#define IDLE_UNLOCK(x) (g_mutex_unlock((x)->idle_lock))
 
1770
 
 
1771
/*TODO handle negative cases sanely */
 
1772
static gboolean
 
1773
imapx_command_idle_stop (CamelIMAPXServer *is, CamelException *ex)
 
1774
{
 
1775
        if (!is->stream || camel_stream_printf((CamelStream *)is->stream, "%s", "DONE\r\n") == -1) {
 
1776
                camel_exception_set (ex, 1, "Unable to issue DONE");
 
1777
                return FALSE;
 
1778
        }
 
1779
 
 
1780
        return TRUE;
 
1781
}
 
1782
 
 
1783
static void
 
1784
imapx_command_idle_done (CamelIMAPXServer *is, CamelIMAPXCommand *ic)
 
1785
{
 
1786
        CamelIMAPXIdle *idle = is->idle;
 
1787
 
 
1788
        if (camel_exception_is_set (ic->ex) || ic->status->result != IMAPX_OK) {
 
1789
                if (!camel_exception_is_set (ic->ex))
 
1790
                        camel_exception_setv(ic->job->ex, 1, "Error performing IDLE: %s", ic->status->text);
 
1791
                else
 
1792
                        camel_exception_xfer (ic->job->ex, ic->ex);
 
1793
        }
 
1794
 
 
1795
        IDLE_LOCK (idle);
 
1796
        idle->in_idle = FALSE;
 
1797
        idle->idle_issue_done = FALSE;
 
1798
        idle->started = FALSE;
 
1799
        IDLE_UNLOCK (idle);
 
1800
 
 
1801
        imapx_job_done (is, ic->job);
 
1802
        camel_imapx_command_free (ic);
 
1803
}
 
1804
 
 
1805
static void
 
1806
imapx_job_idle_start(CamelIMAPXServer *is, CamelIMAPXJob *job)
 
1807
{
 
1808
        CamelIMAPXCommand *ic;
 
1809
        CamelIMAPXCommandPart *cp;
 
1810
 
 
1811
        ic = camel_imapx_command_new ("IDLE", job->folder->full_name, "IDLE");
 
1812
        ic->job = job;
 
1813
        ic->pri = job->pri;
 
1814
        ic->complete = imapx_command_idle_done;
 
1815
 
 
1816
        camel_imapx_command_close(ic);
 
1817
        cp = (CamelIMAPXCommandPart *)ic->parts.head;
 
1818
        cp->type |= CAMEL_IMAPX_COMMAND_CONTINUATION;
 
1819
 
 
1820
        QUEUE_LOCK (is);
 
1821
        imapx_command_start (is, ic);
 
1822
        QUEUE_UNLOCK (is);
 
1823
}
 
1824
 
 
1825
static void
 
1826
camel_imapx_server_idle (CamelIMAPXServer *is, CamelFolder *folder, CamelException *ex)
 
1827
{
 
1828
        CamelIMAPXJob *job;
 
1829
 
 
1830
        job = g_malloc0 (sizeof(*job));
 
1831
        job->type = IMAPX_JOB_IDLE;
 
1832
        job->start = imapx_job_idle_start;
 
1833
        job->folder = folder;
 
1834
        job->ex = ex;
 
1835
 
 
1836
        if (imapx_register_job (is, job))
 
1837
                imapx_run_job(is, job);
 
1838
        g_free(job);
 
1839
}
 
1840
 
 
1841
static void
 
1842
imapx_server_fetch_new_messages (CamelIMAPXServer *is, CamelFolder *folder, gboolean async, CamelException *ex)
 
1843
{
 
1844
        CamelIMAPXJob *job;
 
1845
 
 
1846
        job = g_malloc0(sizeof(*job));
 
1847
        job->type = IMAPX_JOB_FETCH_NEW_MESSAGES;
 
1848
        job->start = imapx_job_fetch_new_messages_start;
 
1849
        job->folder = folder;
 
1850
        job->noreply = async;
 
1851
        job->ex = ex;
 
1852
        job->u.refresh_info.changes = camel_folder_change_info_new();
 
1853
        job->op = camel_operation_registered ();
 
1854
 
 
1855
        if (imapx_register_job (is, job))
 
1856
                imapx_run_job (is, job);
 
1857
 
 
1858
        if (!async)
 
1859
                g_free (job);
 
1860
}
 
1861
 
 
1862
static gpointer
 
1863
imapx_idle_thread (gpointer data)
 
1864
{
 
1865
        CamelException *ex = camel_exception_new ();
 
1866
        CamelIMAPXServer *is = (CamelIMAPXServer *) data;
 
1867
 
 
1868
        while (TRUE) {
 
1869
                CamelIMAPXFolder *ifolder = (CamelIMAPXFolder *) is->select_folder;
 
1870
 
 
1871
                e_flag_clear (is->idle->idle_start_watch);
 
1872
                camel_imapx_server_idle (is, is->select_folder, ex);
 
1873
 
 
1874
                if (!camel_exception_is_set (ex) && ifolder->exists_on_server >
 
1875
                                camel_folder_summary_count (((CamelFolder *) ifolder)->summary) && imapx_is_command_queue_empty (is))
 
1876
                        imapx_server_fetch_new_messages (is, is->select_folder, TRUE, ex);
 
1877
 
 
1878
                if (camel_exception_is_set (ex)) {
 
1879
                        e(printf ("Caught exception in idle thread:  %s \n", ex->desc));
 
1880
                        /* No way to asyncronously notify UI ? */
 
1881
                        camel_exception_clear (ex);
 
1882
                }
 
1883
 
 
1884
                e_flag_wait (is->idle->idle_start_watch);
 
1885
 
 
1886
                if (is->idle->idle_exit)
 
1887
                        break;
 
1888
        }
 
1889
 
 
1890
        camel_exception_free (ex);
 
1891
        is->idle->idle_thread = NULL;
 
1892
        return NULL;
 
1893
}
 
1894
 
 
1895
static void
 
1896
imapx_stop_idle (CamelIMAPXServer *is, CamelException *ex)
 
1897
{
 
1898
        CamelIMAPXIdle *idle = is->idle;
 
1899
 
 
1900
        IDLE_LOCK (idle);
 
1901
 
 
1902
        if (!idle->idle_issue_done && idle->started) {
 
1903
                imapx_command_idle_stop (is, ex);
 
1904
                idle->idle_issue_done = TRUE;
 
1905
        }
 
1906
 
 
1907
        IDLE_UNLOCK (idle);
 
1908
}
 
1909
 
 
1910
static void
 
1911
imapx_init_idle (CamelIMAPXServer *is)
 
1912
{
 
1913
        is->idle = g_new0 (CamelIMAPXIdle, 1);
 
1914
        is->idle->idle_lock = g_mutex_new ();
 
1915
}
 
1916
 
 
1917
static void
 
1918
imapx_exit_idle (CamelIMAPXServer *is)
 
1919
{
 
1920
        CamelIMAPXIdle *idle = is->idle;
 
1921
 
 
1922
        if (!idle)
 
1923
                return;
 
1924
 
 
1925
        IDLE_LOCK (idle);
 
1926
 
 
1927
        if (idle->idle_thread) {
 
1928
                idle->idle_exit = TRUE;
 
1929
                e_flag_set (idle->idle_start_watch);
 
1930
 
 
1931
                if (idle->idle_thread)
 
1932
                        g_thread_join (idle->idle_thread);
 
1933
        }
 
1934
 
 
1935
        idle->idle_thread = NULL;
 
1936
        IDLE_UNLOCK (idle);
 
1937
 
 
1938
        g_mutex_free (idle->idle_lock);
 
1939
        if (idle->idle_start_watch)
 
1940
                e_flag_free (idle->idle_start_watch);
 
1941
 
 
1942
        g_free (is->idle);
 
1943
        is->idle = NULL;
 
1944
}
 
1945
 
 
1946
static void
 
1947
imapx_start_idle (CamelIMAPXServer *is)
 
1948
{
 
1949
        CamelIMAPXIdle *idle = is->idle;
 
1950
 
 
1951
        if (camel_application_is_exiting)
 
1952
                return;
 
1953
 
 
1954
        IDLE_LOCK (idle);
 
1955
 
 
1956
        if (!idle->idle_thread) {
 
1957
                idle->idle_start_watch = e_flag_new ();
 
1958
                idle->idle_thread = g_thread_create ((GThreadFunc) imapx_idle_thread, is, TRUE, NULL);
 
1959
        } else
 
1960
                e_flag_set (idle->idle_start_watch);
 
1961
 
 
1962
        idle->in_idle = TRUE;
 
1963
 
 
1964
        IDLE_UNLOCK (idle);
 
1965
}
 
1966
 
 
1967
static gboolean
 
1968
imapx_in_idle (CamelIMAPXServer *is)
 
1969
{
 
1970
        gboolean ret = FALSE;
 
1971
        CamelIMAPXIdle *idle = is->idle;
 
1972
 
 
1973
        IDLE_LOCK (idle);
 
1974
        ret = idle->in_idle;
 
1975
        IDLE_UNLOCK (idle);
 
1976
 
 
1977
        return ret;
 
1978
}
 
1979
 
 
1980
static gboolean
 
1981
imapx_idle_supported (CamelIMAPXServer *is)
 
1982
{
 
1983
        return (is->cinfo && is->cinfo->capa & IMAPX_CAPABILITY_IDLE && is->use_idle);
 
1984
}
 
1985
 
 
1986
// end IDLE
 
1987
/* ********************************************************************** */
 
1988
static void
 
1989
imapx_command_select_done (CamelIMAPXServer *is, CamelIMAPXCommand *ic)
 
1990
{
 
1991
 
 
1992
        if (camel_exception_is_set (ic->ex) || ic->status->result != IMAPX_OK) {
 
1993
                CamelDList failed;
 
1994
                CamelIMAPXCommand *cw, *cn;
 
1995
 
 
1996
                c(printf("Select failed\n"));
 
1997
                camel_dlist_init (&failed);
 
1998
 
 
1999
                QUEUE_LOCK(is);
 
2000
                cw = (CamelIMAPXCommand *)is->queue.head;
 
2001
                cn = cw->next;
 
2002
 
 
2003
                if (is->select_pending) {
 
2004
                        while (cn) {
 
2005
                                if (cw->select && strcmp(cw->select, is->select_pending->full_name) == 0) {
 
2006
                                        camel_dlist_remove((CamelDListNode *)cw);
 
2007
                                        camel_dlist_addtail(&failed, (CamelDListNode *)cw);
 
2008
                                }
 
2009
                                cw = cn;
 
2010
                                cn = cn->next;
 
2011
                        }
 
2012
                }
 
2013
 
 
2014
                QUEUE_UNLOCK(is);
 
2015
 
 
2016
                cw = (CamelIMAPXCommand *)failed.head;
 
2017
                if (cw) {
 
2018
                        cn = cw->next;
 
2019
                        while (cn) {
 
2020
                                if (ic->status)
 
2021
                                        cw->status = imapx_copy_status(ic->status);
 
2022
                                camel_exception_setv (cw->ex, 1, "select %s failed", cw->select);
 
2023
                                cw->complete(is, cw);
 
2024
                                cw = cn;
 
2025
                                cn = cn->next;
 
2026
                        }
 
2027
                }
 
2028
 
 
2029
                if (is->select_pending)
 
2030
                        camel_object_unref(is->select_pending);
 
2031
        } else {
 
2032
                CamelIMAPXFolder *ifolder = (CamelIMAPXFolder *) is->select_pending;
 
2033
                c(printf("Select ok!\n"));
 
2034
 
 
2035
                is->select_folder = is->select_pending;
 
2036
                is->select = g_strdup(is->select_folder->full_name);
 
2037
                is->state = IMAPX_SELECTED;
 
2038
                ifolder->exists_on_server = is->exists;
 
2039
#if 0
 
2040
                /* This must trigger a complete index rebuild! */
 
2041
                if (is->uidvalidity && is->uidvalidity != ((CamelIMAPXSummary *)is->select_folder->summary)->uidvalidity)
 
2042
                        g_warning("uidvalidity doesn't match!");
 
2043
 
 
2044
                /* This should trigger a new messages scan */
 
2045
                if (is->exists != is->select_folder->summary->root_view->total_count)
 
2046
                        g_warning("exists is %d our summary is %d and summary exists is %d\n", is->exists,
 
2047
                                  is->select_folder->summary->root_view->total_count,
 
2048
                                  ((CamelIMAPXSummary *)is->select_folder->summary)->exists);
 
2049
#endif
 
2050
        }
 
2051
 
 
2052
        is->select_pending = NULL;
 
2053
        camel_imapx_command_free (ic);
 
2054
}
 
2055
 
 
2056
/* Should have a queue lock. TODO Change the way select is written */
 
2057
static void
 
2058
imapx_select (CamelIMAPXServer *is, CamelFolder *folder, gboolean forced, CamelException *ex)
 
2059
{
 
2060
        CamelIMAPXCommand *ic;
 
2061
 
 
2062
        /* Select is complicated by the fact we may have commands
 
2063
           active on the server for a different selection.
 
2064
 
 
2065
           So this waits for any commands to complete, selects the
 
2066
           new folder, and halts the queuing of any new commands.
 
2067
           It is assumed whomever called is us about to issue
 
2068
           a high-priority command anyway */
 
2069
 
 
2070
        /* TODO check locking here, pending_select will do
 
2071
           most of the work for normal commands, but not
 
2072
           for another select */
 
2073
 
 
2074
        if (is->select_pending)
 
2075
                return;
 
2076
 
 
2077
        if (is->select && strcmp(is->select, folder->full_name) == 0 && !forced)
 
2078
                return;
 
2079
 
 
2080
        if (!camel_dlist_empty(&is->active))
 
2081
                return;
 
2082
 
 
2083
        is->select_pending = folder;
 
2084
        camel_object_ref(folder);
 
2085
        if (is->select_folder) {
 
2086
                g_free(is->select);
 
2087
                camel_object_unref(is->select_folder);
 
2088
                is->select = NULL;
 
2089
                is->select_folder = NULL;
 
2090
        }
 
2091
 
 
2092
        is->uidvalidity = 0;
 
2093
        is->unseen = 0;
 
2094
        is->permanentflags = 0;
 
2095
        is->exists = 0;
 
2096
        is->recent = 0;
 
2097
        is->mode = 0;
 
2098
 
 
2099
        /* Hrm, what about reconnecting? */
 
2100
        is->state = IMAPX_AUTHENTICATED;
 
2101
 
 
2102
        ic = camel_imapx_command_new("SELECT", NULL, "SELECT %f", folder);
 
2103
        ic->complete = imapx_command_select_done;
 
2104
        imapx_command_start (is, ic);
 
2105
}
 
2106
 
 
2107
gboolean
 
2108
imapx_connect_to_server (CamelIMAPXServer *is, CamelException *ex)
 
2109
{
 
2110
        CamelStream * tcp_stream = NULL;
 
2111
        CamelSockOptData sockopt;
 
2112
        gint ret, ssl_mode = 0;
 
2113
 
 
2114
#ifdef HAVE_SSL
 
2115
        const gchar *mode;
 
2116
#endif
 
2117
        guchar *buffer = NULL;
 
2118
        guint len;
 
2119
        const gchar *serv;
 
2120
        const gchar *port = NULL;
 
2121
        struct addrinfo *ai, hints = { 0 };
 
2122
        CamelIMAPXCommand *ic;
 
2123
 
 
2124
        if (is->url->port) {
 
2125
                serv = g_alloca(16);
 
2126
                sprintf((gchar *) serv, "%d", is->url->port);
 
2127
        } else {
 
2128
                serv = "imap";
 
2129
                port = "143";
 
2130
        }
 
2131
#ifdef HAVE_SSL
 
2132
        mode = camel_url_get_param(is->url, "use_ssl");
 
2133
        if (mode && strcmp(mode, "never") != 0) {
 
2134
                if (!strcmp(mode, "when-possible")) {
 
2135
                        tcp_stream = camel_tcp_stream_ssl_new_raw(is->session, is->url->host, STARTTLS_FLAGS);
 
2136
                        ssl_mode = 2;
 
2137
                } else {
 
2138
                        if (is->url->port == 0) {
 
2139
                                serv = "imaps";
 
2140
                                port = "993";
 
2141
                        }
 
2142
                        tcp_stream = camel_tcp_stream_ssl_new(is->session, is->url->host, SSL_PORT_FLAGS);
 
2143
                }
 
2144
                is->is_ssl_stream = TRUE;
 
2145
        } else {
 
2146
                tcp_stream = camel_tcp_stream_raw_new ();
 
2147
                is->is_ssl_stream = FALSE;
 
2148
        }
 
2149
#else
 
2150
        tcp_stream = camel_tcp_stream_raw_new ();
 
2151
        is->is_ssl_stream = FALSE;
 
2152
#endif /* HAVE_SSL */
 
2153
 
 
2154
        hints.ai_socktype = SOCK_STREAM;
 
2155
        ai = camel_getaddrinfo(is->url->host, serv, &hints, ex);
 
2156
        if (ex && ex->id && ex->id != CAMEL_EXCEPTION_USER_CANCEL && port != NULL) {
 
2157
                camel_exception_clear(ex);
 
2158
                ai = camel_getaddrinfo(is->url->host, port, &hints, ex);
 
2159
        }
 
2160
 
 
2161
        if (ex && ex->id) {
 
2162
                e(printf ("Unable to connect %d %s \n", ex->id, ex->desc));
 
2163
                camel_object_unref(tcp_stream);
 
2164
                return FALSE;
 
2165
        }
 
2166
 
 
2167
        ret = camel_tcp_stream_connect(CAMEL_TCP_STREAM(tcp_stream), ai);
 
2168
        camel_freeaddrinfo(ai);
 
2169
        if (ret == -1) {
 
2170
                if (errno == EINTR)
 
2171
                        camel_exception_set (ex, CAMEL_EXCEPTION_USER_CANCEL, _("Connection cancelled"));
 
2172
                else
 
2173
                        camel_exception_setv (ex, CAMEL_EXCEPTION_SERVICE_UNAVAILABLE,
 
2174
                                        _("Could not connect to %s (port %s): %s"),
 
2175
                                        is->url->host, serv, g_strerror(errno));
 
2176
                camel_object_unref(tcp_stream);
 
2177
                return FALSE;
 
2178
        }
 
2179
 
 
2180
        is->stream = (CamelIMAPXStream *) camel_imapx_stream_new(tcp_stream);
 
2181
        camel_object_unref(tcp_stream);
 
2182
 
 
2183
        /* Disable Nagle - we send a lot of small requests which nagle slows down */
 
2184
        sockopt.option = CAMEL_SOCKOPT_NODELAY;
 
2185
        sockopt.value.no_delay = TRUE;
 
2186
        camel_tcp_stream_setsockopt((CamelTcpStream *)tcp_stream, &sockopt);
 
2187
 
 
2188
        /* Set keepalive - needed for some hosts/router configurations, we're idle a lot */
 
2189
        sockopt.option = CAMEL_SOCKOPT_KEEPALIVE;
 
2190
        sockopt.value.keep_alive = TRUE;
 
2191
        camel_tcp_stream_setsockopt ((CamelTcpStream *)tcp_stream, &sockopt);
 
2192
 
 
2193
        camel_imapx_stream_gets (is->stream, &buffer, &len);
 
2194
        e(printf("Got greeting '%.*s'\n", len, buffer));
 
2195
 
 
2196
        ic = camel_imapx_command_new("CAPABILITY", NULL, "CAPABILITY");
 
2197
        imapx_command_run(is, ic);
 
2198
 
 
2199
        if (camel_exception_is_set (ic->ex) || ic->status->result != IMAPX_OK) {
 
2200
                if (!camel_exception_is_set (ic->ex))
 
2201
                        camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, "%s", ic->status->text);
 
2202
                else
 
2203
                        camel_exception_xfer (ex, ic->ex);
 
2204
 
 
2205
                camel_imapx_command_free(ic);
 
2206
                goto exit;
 
2207
        }
 
2208
        camel_imapx_command_free(ic);
 
2209
 
 
2210
#ifdef HAVE_SSL
 
2211
        if (ssl_mode == 2)
 
2212
        {
 
2213
 
 
2214
                if (!(is->cinfo->capa & IMAPX_CAPABILITY_STARTTLS)) {
 
2215
                        camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
 
2216
                                        _("Failed to connect to IMAP server %s in secure mode: %s"),
 
2217
                                        is->url->host, _("STARTTLS not supported"));
 
2218
                        goto exit;
 
2219
                }
 
2220
 
 
2221
                ic = camel_imapx_command_new ("STARTTLS", NULL, "STARTTLS");
 
2222
                imapx_command_run (is, ic);
 
2223
                
 
2224
                if (camel_exception_is_set (ic->ex) || ic->status->result != IMAPX_OK) {
 
2225
                        if (!camel_exception_is_set (ic->ex))
 
2226
                                camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, "%s", ic->status->text);
 
2227
                        else
 
2228
                                camel_exception_xfer (ex, ic->ex);
 
2229
 
 
2230
 
 
2231
                        camel_imapx_command_free(ic);
 
2232
                        goto exit;
 
2233
                }
 
2234
                camel_imapx_command_free(ic);
 
2235
 
 
2236
                if (camel_tcp_stream_ssl_enable_ssl (CAMEL_TCP_STREAM_SSL (tcp_stream)) == -1) {
 
2237
                        camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
 
2238
                                        _("Failed to connect to IMAP server %s in secure mode: %s"),
 
2239
                                        is->url->host, _("SSL negotiations failed"));
 
2240
                        goto exit;
 
2241
                }
 
2242
        }
 
2243
#endif
 
2244
 
 
2245
exit:
 
2246
        if (camel_exception_is_set (ex)) {
 
2247
                e(printf("Unable to connect %d %s \n", ex->id, ex->desc));
 
2248
                camel_object_unref (is->stream);
 
2249
                is->stream = NULL;
 
2250
                
 
2251
                if (is->cinfo) {
 
2252
                        imapx_free_capability(is->cinfo);
 
2253
                        is->cinfo = NULL;
 
2254
                }
 
2255
 
 
2256
                return FALSE;
 
2257
        }
 
2258
 
 
2259
        return TRUE;
 
2260
}
 
2261
 
 
2262
static void
 
2263
imapx_reconnect (CamelIMAPXServer *is, CamelException *ex)
 
2264
{
 
2265
        CamelSasl *sasl;
 
2266
        CamelIMAPXCommand *ic;
 
2267
        gchar *errbuf = NULL;
 
2268
        CamelService *service = (CamelService *) is->store;
 
2269
        const gchar *auth_domain = NULL;
 
2270
        gboolean authenticated = FALSE;
 
2271
 
 
2272
        while (!authenticated) {
 
2273
                if (errbuf) {
 
2274
                        /* We need to un-cache the password before prompting again */
 
2275
                        camel_session_forget_password (is->session, service, auth_domain, "password", ex);
 
2276
                        g_free (service->url->passwd);
 
2277
                        service->url->passwd = NULL;
 
2278
                        camel_exception_clear (ex);
 
2279
                }
 
2280
 
 
2281
                imapx_connect_to_server (is, ex);
 
2282
                if (camel_exception_is_set (ex))
 
2283
                        goto exception;
 
2284
 
 
2285
                if (service->url->passwd == NULL) {
 
2286
                        gchar *base_prompt;
 
2287
                        gchar *full_prompt;
 
2288
 
 
2289
                        base_prompt = camel_session_build_password_prompt (
 
2290
                                        "IMAP", service->url->user, service->url->host);
 
2291
 
 
2292
                        if (errbuf != NULL)
 
2293
                                full_prompt = g_strconcat (errbuf, base_prompt, NULL);
 
2294
                        else
 
2295
                                full_prompt = g_strdup (base_prompt);
 
2296
 
 
2297
                        auth_domain = camel_url_get_param (service->url, "auth-domain");
 
2298
                        service->url->passwd = camel_session_get_password(is->session, (CamelService *)is->store,
 
2299
                                        auth_domain,
 
2300
                                        full_prompt, "password", CAMEL_SESSION_PASSWORD_SECRET, ex);
 
2301
 
 
2302
                        g_free (base_prompt);
 
2303
                        g_free (full_prompt);
 
2304
                        g_free (errbuf);
 
2305
                        errbuf = NULL;
 
2306
 
 
2307
                        if (!service->url->passwd) {
 
2308
                                camel_exception_set (ex, CAMEL_EXCEPTION_USER_CANCEL,
 
2309
                                                _("You did not enter a password."));
 
2310
                                goto exception;
 
2311
                        }
 
2312
                }
 
2313
 
 
2314
                if (service->url->authmech
 
2315
                                && (sasl = camel_sasl_new("imap", service->url->authmech, NULL))) {
 
2316
                        ic = camel_imapx_command_new("AUTHENTICATE", NULL, "AUTHENTICATE %A", sasl);
 
2317
                        camel_object_unref(sasl);
 
2318
                } else {
 
2319
                        ic = camel_imapx_command_new("LOGIN", NULL, "LOGIN %s %s", service->url->user, service->url->passwd);
 
2320
                }
 
2321
 
 
2322
                imapx_command_run (is, ic);
 
2323
 
 
2324
                if (!(camel_exception_is_set (ic->ex) || ic->status->result != IMAPX_OK))
 
2325
                        authenticated = TRUE;
 
2326
                else {
 
2327
                        /* If exception is set, it might be mostly due to cancellation and we would get an 
 
2328
                           io error, else re-prompt. If authentication fails for other reasons ic->status would be
 
2329
                            set with the error message */
 
2330
                        if (camel_exception_is_set (ic->ex)) {
 
2331
                                camel_exception_xfer (ex, ic->ex);
 
2332
                                camel_imapx_command_free(ic);
 
2333
                                goto exception;
 
2334
                        }
 
2335
 
 
2336
                        errbuf = g_markup_printf_escaped (
 
2337
                                        _("Unable to authenticate to IMAP server.\n%s\n\n"),
 
2338
                                         ic->status->text);
 
2339
                        camel_exception_clear (ex);
 
2340
 
 
2341
                }
 
2342
                
 
2343
                camel_imapx_command_free(ic);
 
2344
        }
 
2345
        
 
2346
        /* After login we re-capa */
 
2347
        if (is->cinfo) {
 
2348
                imapx_free_capability(is->cinfo);
 
2349
                is->cinfo = NULL;
 
2350
        }
 
2351
 
 
2352
        ic = camel_imapx_command_new("CAPABILITY", NULL, "CAPABILITY");
 
2353
        imapx_command_run (is, ic);
 
2354
        camel_exception_xfer (ex, ic->ex);
 
2355
        camel_imapx_command_free(ic);
 
2356
 
 
2357
        if (camel_exception_is_set (ex))
 
2358
                goto exception;
 
2359
 
 
2360
        is->state = IMAPX_AUTHENTICATED;
 
2361
 
 
2362
        if (((CamelIMAPXStore *)is->store)->rec_options & IMAPX_USE_IDLE)
 
2363
                is->use_idle = TRUE;
 
2364
        else
 
2365
                is->use_idle = FALSE;
 
2366
        
 
2367
        if (imapx_idle_supported (is))
 
2368
                imapx_init_idle (is);
 
2369
 
 
2370
        /* Fetch namespaces */
 
2371
        if (is->cinfo->capa & IMAPX_CAPABILITY_NAMESPACE) {
 
2372
                ic = camel_imapx_command_new ("NAMESPACE", NULL, "NAMESPACE");
 
2373
                imapx_command_run (is, ic);
 
2374
                camel_exception_xfer (ex, ic->ex);
 
2375
                camel_imapx_command_free (ic);
 
2376
 
 
2377
                if (camel_exception_is_set (ex))
 
2378
                        goto exception;
 
2379
        }
 
2380
 
 
2381
        if (((CamelIMAPXStore *) is->store)->summary->namespaces == NULL) {
 
2382
                CamelIMAPXNamespaceList *nsl = NULL;
 
2383
                CamelIMAPXStoreNamespace *ns = NULL;
 
2384
                CamelIMAPXStore *imapx_store = (CamelIMAPXStore *) is->store;
 
2385
 
 
2386
                /* set a default namespace */
 
2387
                nsl = g_malloc0(sizeof(CamelIMAPXNamespaceList));
 
2388
                ns = g_new0 (CamelIMAPXStoreNamespace, 1);
 
2389
                ns->next = NULL;
 
2390
                ns->path = g_strdup ("");
 
2391
                ns->full_name = g_strdup ("");
 
2392
                ns->sep = '/';
 
2393
                nsl->personal = ns;
 
2394
                imapx_store->summary->namespaces = nsl;
 
2395
                /* FIXME needs to be identified from list response */
 
2396
                imapx_store->dir_sep = ns->sep;
 
2397
        }
 
2398
 
 
2399
        if (!camel_exception_is_set (ex))
 
2400
                return;
 
2401
 
 
2402
exception:
 
2403
        imapx_disconnect (is);
 
2404
        
 
2405
        if (is->cinfo) {
 
2406
                imapx_free_capability(is->cinfo);
 
2407
                is->cinfo = NULL;
 
2408
        }
 
2409
}
 
2410
 
 
2411
/* ********************************************************************** */
 
2412
 
 
2413
static void
 
2414
imapx_command_fetch_message_done(CamelIMAPXServer *is, CamelIMAPXCommand *ic)
 
2415
{
 
2416
        CamelIMAPXJob *job = ic->job;
 
2417
        gboolean failed = FALSE;
 
2418
 
 
2419
        /* We either have more to fetch (partial mode?), we are complete,
 
2420
           or we failed.  Failure is handled in the fetch code, so
 
2421
           we just return the job, or keep it alive with more requests */
 
2422
 
 
2423
        job->commands--;
 
2424
 
 
2425
        if (camel_exception_is_set (ic->ex) || ic->status->result != IMAPX_OK) {
 
2426
                failed = TRUE;
 
2427
                job->u.get_message.body_len = -1;
 
2428
        } else  if (job->u.get_message.use_multi_fetch) {
 
2429
 
 
2430
                if (!failed && job->u.get_message.fetch_offset <= job->u.get_message.size) {
 
2431
                        camel_imapx_command_free (ic);
 
2432
                        if (job->op)
 
2433
                                camel_operation_progress (job->op, (job->u.get_message.fetch_offset *100)/job->u.get_message.size);
 
2434
 
 
2435
                        ic = camel_imapx_command_new("FETCH", job->folder->full_name,
 
2436
                                        "UID FETCH %t (BODY.PEEK[]", job->u.get_message.uid);
 
2437
                        camel_imapx_command_add(ic, "<%u.%u>", job->u.get_message.fetch_offset, MULTI_SIZE);
 
2438
                        camel_imapx_command_add(ic, ")");
 
2439
                        ic->complete = imapx_command_fetch_message_done;
 
2440
                        ic->job = job;
 
2441
                        ic->pri = job->pri - 1;
 
2442
                        job->u.get_message.fetch_offset += MULTI_SIZE;
 
2443
                        job->commands++;
 
2444
                        imapx_command_queue(is, ic);
 
2445
                        return;
 
2446
                }
 
2447
        }
 
2448
 
 
2449
        if (job->commands == 0) {
 
2450
                CamelIMAPXFolder *ifolder = (CamelIMAPXFolder *) job->folder;
 
2451
                CamelStream *stream = job->u.get_message.stream;
 
2452
 
 
2453
                /* return the exception from last command */
 
2454
                if (failed) {
 
2455
                        if (!camel_exception_is_set (ic->ex))
 
2456
                                camel_exception_setv(job->ex, 1, "Error fetching message: %s", ic->status->text);
 
2457
                        else
 
2458
                                camel_exception_xfer (job->ex, ic->ex);
 
2459
                        camel_object_unref (stream);
 
2460
                        job->u.get_message.stream = NULL;
 
2461
                } else {
 
2462
                        CamelIMAPXFolder *ifolder = (CamelIMAPXFolder *) job->folder;
 
2463
 
 
2464
                        if (stream) {
 
2465
                                gchar *tmp = camel_data_cache_get_filename (ifolder->cache, "tmp", job->u.get_message.uid, NULL);
 
2466
 
 
2467
                                if (camel_stream_flush (stream) == 0 && camel_stream_close (stream) == 0) {
 
2468
                                        gchar *cache_file = camel_data_cache_get_filename  (ifolder->cache, "cur", job->u.get_message.uid, NULL);
 
2469
                                        gchar *temp = g_strrstr (cache_file, "/"), *dir;
 
2470
 
 
2471
                                        dir = g_strndup (cache_file, temp - cache_file);
 
2472
                                        g_mkdir_with_parents (dir, 0700);
 
2473
                                        g_free (dir);
 
2474
 
 
2475
                                        if (g_rename (tmp, cache_file) != 0)
 
2476
                                                camel_exception_set (job->ex, 1, "failed to copy the tmp file");
 
2477
                                        g_free (cache_file);
 
2478
                                } else
 
2479
                                        camel_exception_setv(job->ex, 1, "closing tmp stream failed: %s", g_strerror(errno));
 
2480
 
 
2481
                                g_free (tmp);
 
2482
                                job->u.get_message.stream = camel_data_cache_get (ifolder->cache, "cur", job->u.get_message.uid, NULL);
 
2483
                        }
 
2484
                }
 
2485
 
 
2486
                camel_data_cache_remove (ifolder->cache, "tmp", job->u.get_message.uid, NULL);
 
2487
                imapx_job_done (is, job);
 
2488
        }
 
2489
 
 
2490
        camel_imapx_command_free (ic);
 
2491
}
 
2492
 
 
2493
static void
 
2494
imapx_job_get_message_start (CamelIMAPXServer *is, CamelIMAPXJob *job)
 
2495
{
 
2496
        CamelIMAPXCommand *ic;
 
2497
        gint i;
 
2498
 
 
2499
        if (job->u.get_message.use_multi_fetch) {
 
2500
                for (i=0; i < 3 && job->u.get_message.fetch_offset < job->u.get_message.size;i++) {
 
2501
                        ic = camel_imapx_command_new("FETCH", job->folder->full_name,
 
2502
                                        "UID FETCH %t (BODY.PEEK[]", job->u.get_message.uid);
 
2503
                        camel_imapx_command_add(ic, "<%u.%u>", job->u.get_message.fetch_offset, MULTI_SIZE);
 
2504
                        camel_imapx_command_add(ic, ")");
 
2505
                        ic->complete = imapx_command_fetch_message_done;
 
2506
                        ic->job = job;
 
2507
                        ic->pri = job->pri;
 
2508
                        job->u.get_message.fetch_offset += MULTI_SIZE;
 
2509
                        job->commands++;
 
2510
                        imapx_command_queue(is, ic);
 
2511
                }
 
2512
        } else {
 
2513
                ic = camel_imapx_command_new("FETCH", job->folder->full_name,
 
2514
                                "UID FETCH %t (BODY.PEEK[])", job->u.get_message.uid);
 
2515
                ic->complete = imapx_command_fetch_message_done;
 
2516
                ic->job = job;
 
2517
                ic->pri = job->pri;
 
2518
                job->commands++;
 
2519
                imapx_command_queue(is, ic);
 
2520
        }
 
2521
}
 
2522
 
 
2523
/* ********************************************************************** */
 
2524
 
 
2525
static void
 
2526
imapx_command_copy_messages_step_start (CamelIMAPXServer *is, CamelIMAPXJob *job, gint index)
 
2527
{
 
2528
        CamelIMAPXCommand *ic;
 
2529
        GPtrArray *uids = job->u.copy_messages.uids;
 
2530
        gint i = index;
 
2531
 
 
2532
        ic = camel_imapx_command_new ("COPY", job->folder->full_name, "UID COPY ");
 
2533
        ic->complete = imapx_command_copy_messages_step_done;
 
2534
        ic->job = job;
 
2535
        ic->pri = job->pri;
 
2536
        job->u.copy_messages.last_index = i;
 
2537
 
 
2538
        for (;i < uids->len; i++) {
 
2539
                gint res;
 
2540
                const gchar *uid = (gchar *) g_ptr_array_index (uids, i);
 
2541
 
 
2542
                res = imapx_uidset_add (&job->u.copy_messages.uidset, ic, uid);
 
2543
                if (res == 1) {
 
2544
                        camel_imapx_command_add (ic, " %f", job->u.copy_messages.dest);
 
2545
                        job->u.copy_messages.index = i;
 
2546
                        imapx_command_queue (is, ic);
 
2547
                        return;
 
2548
                }
 
2549
        }
 
2550
 
 
2551
        job->u.copy_messages.index = i;
 
2552
        if (imapx_uidset_done (&job->u.copy_messages.uidset, ic)) {
 
2553
                camel_imapx_command_add (ic, " %f", job->u.copy_messages.dest);
 
2554
                imapx_command_queue (is, ic);
 
2555
                return;
 
2556
        }
 
2557
}
 
2558
 
 
2559
static void
 
2560
imapx_command_copy_messages_step_done (CamelIMAPXServer *is, CamelIMAPXCommand *ic)
 
2561
{
 
2562
        CamelIMAPXJob *job = ic->job;
 
2563
        gint i = job->u.copy_messages.index;
 
2564
        GPtrArray *uids = job->u.copy_messages.uids;
 
2565
 
 
2566
        if (camel_exception_is_set (ic->ex) || ic->status->result != IMAPX_OK) {
 
2567
                if (!camel_exception_is_set (ic->ex))
 
2568
                        camel_exception_set (job->ex, 1, "Error copying messages");
 
2569
                else
 
2570
                        camel_exception_xfer (job->ex, ic->ex);
 
2571
 
 
2572
                goto cleanup;
 
2573
        }
 
2574
 
 
2575
        if (job->u.copy_messages.delete_originals) {
 
2576
                gint j;
 
2577
 
 
2578
                for (j = job->u.copy_messages.last_index; j < i; j++)
 
2579
                        camel_folder_delete_message (job->folder, uids->pdata [j]);
 
2580
        }
 
2581
 
 
2582
        /* TODO copy the summary and cached messages to the new folder. We might need a sorted insert to avoid refreshing the dest folder */
 
2583
        if (ic->status->condition == IMAPX_COPYUID) {
 
2584
                gint i;
 
2585
                
 
2586
                for (i = 0; i < ic->status->u.copyuid.copied_uids->len; i++) {
 
2587
                        guint32 uid = GPOINTER_TO_UINT(g_ptr_array_index (ic->status->u.copyuid.copied_uids, i));
 
2588
                        gchar *str = g_strdup_printf ("%d",uid);
 
2589
                        CamelIMAPXFolder *ifolder = (CamelIMAPXFolder *) job->u.copy_messages.dest;
 
2590
                        
 
2591
                        g_hash_table_insert (ifolder->ignore_recent, str, GINT_TO_POINTER (1));
 
2592
                }
 
2593
                                
 
2594
        }
 
2595
 
 
2596
        if (i < uids->len) {
 
2597
                camel_imapx_command_free (ic);
 
2598
                imapx_command_copy_messages_step_start (is, job, i);
 
2599
        }
 
2600
 
 
2601
cleanup:
 
2602
        camel_object_unref (job->u.copy_messages.dest);
 
2603
        camel_object_unref (job->folder);
 
2604
 
 
2605
        imapx_job_done (is, job);
 
2606
        camel_imapx_command_free (ic);
 
2607
}
 
2608
 
 
2609
static void
 
2610
imapx_job_copy_messages_start (CamelIMAPXServer *is, CamelIMAPXJob *job)
 
2611
{
 
2612
        imapx_server_sync_changes (is, job->folder, job->pri, job->ex);
 
2613
        if (camel_exception_is_set (job->ex))
 
2614
                imapx_job_done (is, job);
 
2615
 
 
2616
        g_ptr_array_sort (job->u.copy_messages.uids, (GCompareFunc) imapx_uids_array_cmp);
 
2617
        imapx_uidset_init(&job->u.copy_messages.uidset, 0, MAX_COMMAND_LEN);
 
2618
        imapx_command_copy_messages_step_start (is, job, 0);
 
2619
}
 
2620
 
 
2621
/* ********************************************************************** */
 
2622
 
 
2623
static void
 
2624
imapx_command_append_message_done (CamelIMAPXServer *is, CamelIMAPXCommand *ic)
 
2625
{
 
2626
        CamelIMAPXJob *job = ic->job;
 
2627
        CamelIMAPXFolder *ifolder = (CamelIMAPXFolder *) job->folder;
 
2628
        CamelMessageInfo *mi;
 
2629
        gchar *cur, *old_uid;
 
2630
 
 
2631
        /* Append done.  If we the server supports UIDPLUS we will get an APPENDUID response
 
2632
           with the new uid.  This lets us move the message we have directly to the cache
 
2633
           and also create a correctly numbered MessageInfo, without losing any information.
 
2634
           Otherwise we have to wait for the server to less us know it was appended. */
 
2635
 
 
2636
        mi = camel_message_info_clone (job->u.append_message.info);
 
2637
        old_uid = g_strdup (job->u.append_message.info->uid);
 
2638
 
 
2639
        if (!camel_exception_is_set (ic->ex) && ic->status->result == IMAPX_OK) {
 
2640
                if (ic->status->condition == IMAPX_APPENDUID) {
 
2641
                        c(printf("Got appenduid %d %d\n", (gint)ic->status->u.appenduid.uidvalidity, (gint)ic->status->u.appenduid.uid));
 
2642
                        if (ic->status->u.appenduid.uidvalidity == is->uidvalidity) {
 
2643
                                CamelFolderChangeInfo *changes;
 
2644
                                gchar *uid;
 
2645
 
 
2646
                                uid = g_strdup_printf("%u", (guint)ic->status->u.appenduid.uid);
 
2647
                                mi->uid = camel_pstring_add (uid, TRUE);
 
2648
 
 
2649
                                cur = camel_data_cache_get_filename  (ifolder->cache, "cur", mi->uid, NULL);
 
2650
                                g_rename (job->u.append_message.path, cur);
 
2651
 
 
2652
                                /* should we update the message count ? */
 
2653
                                camel_folder_summary_add (job->folder->summary, mi);
 
2654
 
 
2655
                                changes = camel_folder_change_info_new ();
 
2656
                                camel_folder_change_info_add_uid (changes, mi->uid);
 
2657
                                camel_object_trigger_event (CAMEL_OBJECT (job->folder), "folder_changed",
 
2658
                                                changes);
 
2659
                                camel_folder_change_info_free (changes);
 
2660
 
 
2661
                                g_free(cur);
 
2662
                        } else {
 
2663
                                g_message ("but uidvalidity changed \n");
 
2664
                        }
 
2665
                }
 
2666
        } else {
 
2667
                if (!camel_exception_is_set (ic->ex))
 
2668
                        camel_exception_setv(job->ex, 1, "Error appending message: %s", ic->status->text);
 
2669
                else
 
2670
                        camel_exception_xfer (job->ex, ic->ex);
 
2671
        }
 
2672
 
 
2673
        camel_data_cache_remove (ifolder->cache, "new", old_uid, NULL);
 
2674
        g_free (old_uid);
 
2675
        camel_message_info_free(job->u.append_message.info);
 
2676
        g_free(job->u.append_message.path);
 
2677
        camel_object_unref(job->folder);
 
2678
 
 
2679
        imapx_job_done (is, job);
 
2680
        camel_imapx_command_free (ic);
 
2681
}
 
2682
 
 
2683
static void
 
2684
imapx_job_append_message_start(CamelIMAPXServer *is, CamelIMAPXJob *job)
 
2685
{
 
2686
        CamelIMAPXCommand *ic;
 
2687
 
 
2688
        /* TODO: we could supply the original append date from the file timestamp */
 
2689
        ic = camel_imapx_command_new("APPEND", NULL,
 
2690
                                     "APPEND %f %F %P",
 
2691
                                     job->folder,
 
2692
                                     ((CamelMessageInfoBase *)job->u.append_message.info)->flags,
 
2693
                                     ((CamelMessageInfoBase *)job->u.append_message.info)->user_flags,
 
2694
                                     job->u.append_message.path);
 
2695
        ic->complete = imapx_command_append_message_done;
 
2696
        ic->job = job;
 
2697
        ic->pri = job->pri;
 
2698
        job->commands++;
 
2699
        imapx_command_queue(is, ic);
 
2700
}
 
2701
 
 
2702
/* ********************************************************************** */
 
2703
 
 
2704
static gint
 
2705
imapx_refresh_info_uid_cmp (gconstpointer ap, gconstpointer bp)
 
2706
{
 
2707
        guint av, bv;
 
2708
 
 
2709
        av = g_ascii_strtoull ((const gchar *)ap, NULL, 10);
 
2710
        bv = g_ascii_strtoull ((const gchar *)bp, NULL, 10);
 
2711
 
 
2712
        if (av<bv)
 
2713
                return -1;
 
2714
        else if (av>bv)
 
2715
                return 1;
 
2716
        else
 
2717
                return 0;
 
2718
}
 
2719
 
 
2720
static gint
 
2721
imapx_uids_array_cmp (gconstpointer ap, gconstpointer bp)
 
2722
{
 
2723
        const gchar **a = (const gchar **) ap;
 
2724
        const gchar **b = (const gchar **) bp;
 
2725
 
 
2726
        return imapx_refresh_info_uid_cmp (*a, *b);
 
2727
}
 
2728
 
 
2729
static gint
 
2730
imapx_refresh_info_cmp (gconstpointer ap, gconstpointer bp)
 
2731
{
 
2732
        const struct _refresh_info *a = ap;
 
2733
        const struct _refresh_info *b = bp;
 
2734
 
 
2735
        return imapx_refresh_info_uid_cmp (a->uid, b->uid);
 
2736
}
 
2737
 
 
2738
/* skips over non-server uids (pending appends) */
 
2739
static guint
 
2740
imapx_index_next (GPtrArray *uids, CamelFolderSummary *s, guint index)
 
2741
{
 
2742
 
 
2743
        while (index < uids->len) {
 
2744
                CamelMessageInfo *info;
 
2745
 
 
2746
                index++;
 
2747
                if (index >= uids->len)
 
2748
                        break;
 
2749
 
 
2750
                info = camel_folder_summary_uid (s, g_ptr_array_index (uids, index));
 
2751
                if (!info)
 
2752
                        continue;
 
2753
 
 
2754
                if (info && (strchr(camel_message_info_uid(info), '-') != NULL)) {
 
2755
                        camel_message_info_free (info);
 
2756
                        e(printf("Ignoring offline uid '%s'\n", camel_message_info_uid(info)));
 
2757
                } else {
 
2758
                        camel_message_info_free (info);
 
2759
                        break;
 
2760
                }
 
2761
        }
 
2762
 
 
2763
        return index;
 
2764
}
 
2765
 
 
2766
static void
 
2767
imapx_command_step_fetch_done(CamelIMAPXServer *is, CamelIMAPXCommand *ic)
 
2768
{
 
2769
        CamelIMAPXJob *job = ic->job;
 
2770
        gint i = job->u.refresh_info.index;
 
2771
        GArray *infos = job->u.refresh_info.infos;
 
2772
 
 
2773
        if (camel_exception_is_set (ic->ex) || ic->status->result != IMAPX_OK) {
 
2774
                if (!camel_exception_is_set (ic->ex))
 
2775
                        camel_exception_set (job->ex, 1, "Error fetching message headers");
 
2776
                else
 
2777
                        camel_exception_xfer (job->ex, ic->ex);
 
2778
 
 
2779
                goto cleanup;
 
2780
        }
 
2781
 
 
2782
        if (camel_folder_change_info_changed(job->u.refresh_info.changes)) {
 
2783
                imapx_update_store_summary (job->folder);
 
2784
                camel_folder_summary_save_to_db (job->folder->summary, NULL);
 
2785
                camel_object_trigger_event(job->folder, "folder_changed", job->u.refresh_info.changes);
 
2786
        }
 
2787
 
 
2788
        camel_folder_change_info_clear(job->u.refresh_info.changes);
 
2789
 
 
2790
        if (i<infos->len) {
 
2791
                camel_imapx_command_free (ic);
 
2792
 
 
2793
                ic = camel_imapx_command_new("FETCH", job->folder->full_name, "UID FETCH ");
 
2794
                ic->complete = imapx_command_step_fetch_done;
 
2795
                ic->job = job;
 
2796
                ic->pri = job->pri - 1;
 
2797
                job->u.refresh_info.last_index = i;
 
2798
 
 
2799
                for (;i<infos->len;i++) {
 
2800
                        gint res;
 
2801
                        struct _refresh_info *r = &g_array_index(infos, struct _refresh_info, i);
 
2802
 
 
2803
                        if (!r->exists) {
 
2804
                                res = imapx_uidset_add(&job->u.refresh_info.uidset, ic, r->uid);
 
2805
                                if (res == 1) {
 
2806
                                        camel_imapx_command_add(ic, " (RFC822.SIZE RFC822.HEADER)");
 
2807
                                        job->u.refresh_info.index = i;
 
2808
                                        imapx_command_queue(is, ic);
 
2809
                                        return;
 
2810
                                }
 
2811
                        }
 
2812
                }
 
2813
 
 
2814
                job->u.refresh_info.index = i;
 
2815
                if (imapx_uidset_done(&job->u.refresh_info.uidset, ic)) {
 
2816
                        camel_imapx_command_add(ic, " (RFC822.SIZE RFC822.HEADER)");
 
2817
                        imapx_command_queue(is, ic);
 
2818
                        return;
 
2819
                }
 
2820
        }
 
2821
 
 
2822
cleanup:
 
2823
        for (i=0;i<infos->len;i++) {
 
2824
                struct _refresh_info *r = &g_array_index(infos, struct _refresh_info, i);
 
2825
 
 
2826
                g_free(r->uid);
 
2827
        }
 
2828
        g_array_free(job->u.refresh_info.infos, TRUE);
 
2829
 
 
2830
        imapx_job_done (is, job);
 
2831
        camel_imapx_command_free (ic);
 
2832
}
 
2833
 
 
2834
static gint
 
2835
imapx_uid_cmp(gconstpointer ap, gconstpointer bp, gpointer data)
 
2836
{
 
2837
        const gchar *a = ap, *b = bp;
 
2838
        gchar *ae, *be;
 
2839
        unsigned long av, bv;
 
2840
 
 
2841
        av = strtoul(a, &ae, 10);
 
2842
        bv = strtoul(b, &be, 10);
 
2843
 
 
2844
        if (av < bv)
 
2845
                return -1;
 
2846
        else if (av > bv)
 
2847
                return 1;
 
2848
 
 
2849
        if (*ae == '-')
 
2850
                ae++;
 
2851
        if (*be == '-')
 
2852
                be++;
 
2853
 
 
2854
        return strcmp(ae, be);
 
2855
}
 
2856
 
 
2857
static void
 
2858
imapx_job_scan_changes_done(CamelIMAPXServer *is, CamelIMAPXCommand *ic)
 
2859
{
 
2860
        CamelIMAPXJob *job = ic->job;
 
2861
        gint i;
 
2862
        GArray *infos = job->u.refresh_info.infos;
 
2863
 
 
2864
        if (!camel_exception_is_set (ic->ex) && ic->status->result == IMAPX_OK) {
 
2865
                GCompareDataFunc uid_cmp = imapx_uid_cmp;
 
2866
                CamelMessageInfo *s_minfo = NULL;
 
2867
                CamelIMAPXMessageInfo *info;
 
2868
                CamelFolderSummary *s = job->folder->summary;
 
2869
                GSList *removed = NULL, *l;
 
2870
                gboolean fetch_new = FALSE;
 
2871
                gint i;
 
2872
                guint j = 0;
 
2873
                GPtrArray *uids;
 
2874
 
 
2875
                /* Here we do the typical sort/iterate/merge loop.
 
2876
                   If the server flags dont match what we had, we modify our
 
2877
                   flags to pick up what the server now has - but we merge
 
2878
                   not overwrite */
 
2879
 
 
2880
                /* FIXME: We also have to check the offline directory for
 
2881
                   anything missing in our summary, and also queue up jobs
 
2882
                   for all outstanding messages to be uploaded */
 
2883
 
 
2884
                /* obtain a copy to be thread safe */
 
2885
                uids = camel_folder_summary_array (s);
 
2886
 
 
2887
                qsort(infos->data, infos->len, sizeof(struct _refresh_info), imapx_refresh_info_cmp);
 
2888
                g_ptr_array_sort(uids, (GCompareFunc) imapx_uids_array_cmp);
 
2889
 
 
2890
                if (uids->len)
 
2891
                        s_minfo = camel_folder_summary_uid (s, g_ptr_array_index (uids, 0));
 
2892
 
 
2893
                for (i=0; i<infos->len; i++) {
 
2894
                        struct _refresh_info *r = &g_array_index(infos, struct _refresh_info, i);
 
2895
 
 
2896
                        while (s_minfo && uid_cmp (camel_message_info_uid(s_minfo), r->uid, s) < 0) {
 
2897
                                const gchar *uid = camel_message_info_uid (s_minfo);
 
2898
 
 
2899
                                camel_folder_change_info_remove_uid (job->u.refresh_info.changes, uid);
 
2900
                                removed = g_slist_prepend (removed, (gpointer ) g_strdup (uid));
 
2901
                                camel_message_info_free (s_minfo);
 
2902
                                s_minfo = NULL;
 
2903
 
 
2904
                                j = imapx_index_next (uids, s, j);
 
2905
                                if (j < uids->len)
 
2906
                                        s_minfo = camel_folder_summary_uid (s, g_ptr_array_index (uids, j));
 
2907
                        }
 
2908
 
 
2909
                        info = NULL;
 
2910
                        if (s_minfo && uid_cmp(s_minfo->uid, r->uid, s) == 0) {
 
2911
                                info = (CamelIMAPXMessageInfo *)s_minfo;
 
2912
 
 
2913
                                if (imapx_update_message_info_flags ((CamelMessageInfo *) info, r->server_flags, r->server_user_flags, job->folder))
 
2914
                                        camel_folder_change_info_change_uid (job->u.refresh_info.changes, camel_message_info_uid (s_minfo));
 
2915
                                r->exists = TRUE;
 
2916
                        } else
 
2917
                                fetch_new = TRUE;
 
2918
 
 
2919
                        if (s_minfo) {
 
2920
                                camel_message_info_free (s_minfo);
 
2921
                                s_minfo = NULL;
 
2922
                        }
 
2923
 
 
2924
                        if (j >= uids->len)
 
2925
                                break;
 
2926
 
 
2927
                        j = imapx_index_next (uids, s, j);
 
2928
                        if (j < uids->len)
 
2929
                                s_minfo = camel_folder_summary_uid (s, g_ptr_array_index (uids, j));
 
2930
                }
 
2931
 
 
2932
                if (s_minfo)
 
2933
                        camel_message_info_free (s_minfo);
 
2934
 
 
2935
                while (j < uids->len) {
 
2936
                        s_minfo = camel_folder_summary_uid (s, g_ptr_array_index (uids, j));
 
2937
 
 
2938
                        if (!s_minfo) {
 
2939
                                j++;
 
2940
                                continue;
 
2941
                        }
 
2942
 
 
2943
                        e(printf("Message %s vanished\n", s_minfo->uid));
 
2944
                        removed = g_slist_prepend (removed, (gpointer) g_strdup (s_minfo->uid));
 
2945
                        camel_message_info_free (s_minfo);
 
2946
                        j++;
 
2947
                }
 
2948
 
 
2949
                for (l = removed; l != NULL; l = g_slist_next (l)) {
 
2950
                        gchar *uid = (gchar *) l->data;
 
2951
                        CamelMessageInfo *mi;
 
2952
 
 
2953
                        mi = camel_folder_summary_uid (is->select_folder->summary, uid);
 
2954
                        if (mi) {
 
2955
                                imapx_update_summary_for_removed_message (mi, is->select_folder);
 
2956
                                camel_message_info_free (mi);
 
2957
                        }
 
2958
 
 
2959
                        camel_folder_change_info_remove_uid (job->u.refresh_info.changes, uid);
 
2960
                        camel_folder_summary_remove_uid_fast (s, uid);
 
2961
                }
 
2962
 
 
2963
                if (removed) {
 
2964
                        camel_db_delete_uids (is->store->cdb_w, s->folder->full_name, removed, NULL);
 
2965
                        g_slist_foreach (removed, (GFunc) g_free, NULL);
 
2966
                        g_slist_free (removed);
 
2967
                }
 
2968
 
 
2969
                imapx_update_store_summary (job->folder);
 
2970
 
 
2971
                if (camel_folder_change_info_changed(job->u.refresh_info.changes))
 
2972
                        camel_object_trigger_event(job->folder, "folder_changed", job->u.refresh_info.changes);
 
2973
                camel_folder_change_info_clear(job->u.refresh_info.changes);
 
2974
 
 
2975
                camel_folder_free_uids (job->folder, uids);
 
2976
 
 
2977
                /* If we have any new messages, download their headers, but only a few (100?) at a time */
 
2978
                if (fetch_new) {
 
2979
                        camel_operation_start (job->op, _("Fetching summary information for new messages in %s"), job->folder->name);
 
2980
                        imapx_uidset_init(&job->u.refresh_info.uidset, BATCH_FETCH_COUNT, 0);
 
2981
                        /* command will be free'ed in step_fetch_done */
 
2982
                        imapx_command_step_fetch_done(is, ic);
 
2983
                        return;
 
2984
                }
 
2985
        } else {
 
2986
                if (!camel_exception_is_set (ic->ex))
 
2987
                        camel_exception_setv(job->ex, 1, "Error retriving message: %s", ic->status->text);
 
2988
                else
 
2989
                        camel_exception_xfer (job->ex, ic->ex);
 
2990
        }
 
2991
 
 
2992
        for (i=0;i<infos->len;i++) {
 
2993
                struct _refresh_info *r = &g_array_index(infos, struct _refresh_info, i);
 
2994
 
 
2995
                g_free(r->uid);
 
2996
        }
 
2997
 
 
2998
        g_array_free(job->u.refresh_info.infos, TRUE);
 
2999
        imapx_job_done (is, job);
 
3000
        camel_imapx_command_free (ic);
 
3001
}
 
3002
 
 
3003
static void
 
3004
imapx_job_scan_changes_start(CamelIMAPXServer *is, CamelIMAPXJob *job)
 
3005
{
 
3006
        CamelIMAPXCommand *ic;
 
3007
 
 
3008
        camel_operation_start (job->op, _("Scanning for changed messages in %s"), job->folder->name);
 
3009
 
 
3010
        ic = camel_imapx_command_new ("FETCH", job->folder->full_name,
 
3011
                                     "FETCH 1:* (UID FLAGS)");
 
3012
        ic->job = job;
 
3013
        ic->complete = imapx_job_scan_changes_done;
 
3014
        ic->pri = job->pri;
 
3015
        job->u.refresh_info.infos = g_array_new (0, 0, sizeof(struct _refresh_info));
 
3016
        imapx_command_queue (is, ic);
 
3017
}
 
3018
 
 
3019
static void
 
3020
imapx_command_fetch_new_messages_done (CamelIMAPXServer *is, CamelIMAPXCommand *ic)
 
3021
{
 
3022
        if (camel_exception_is_set (ic->ex) || ic->status->result != IMAPX_OK) {
 
3023
                if (!camel_exception_is_set (ic->ex))
 
3024
                        camel_exception_setv(ic->job->ex, 1, "Error fetching new messages : %s", ic->status->text);
 
3025
                else
 
3026
                        camel_exception_xfer (ic->job->ex, ic->ex);
 
3027
                goto exception;
 
3028
        }
 
3029
 
 
3030
        if (camel_folder_change_info_changed(ic->job->u.refresh_info.changes)) {
 
3031
                imapx_update_store_summary (ic->job->folder);
 
3032
                camel_folder_summary_save_to_db (ic->job->folder->summary, NULL);
 
3033
                camel_object_trigger_event(ic->job->folder, "folder_changed", ic->job->u.refresh_info.changes);
 
3034
                camel_folder_change_info_clear(ic->job->u.refresh_info.changes);
 
3035
        }
 
3036
 
 
3037
exception:
 
3038
        if (ic->job->noreply)
 
3039
                camel_folder_change_info_free(ic->job->u.refresh_info.changes);
 
3040
 
 
3041
        if (ic->job->op)
 
3042
                camel_operation_unref (ic->job->op);
 
3043
 
 
3044
        imapx_job_done (is, ic->job);
 
3045
        camel_imapx_command_free (ic);
 
3046
}
 
3047
 
 
3048
static void
 
3049
imapx_job_fetch_new_messages_start (CamelIMAPXServer *is, CamelIMAPXJob *job)
 
3050
{
 
3051
        CamelIMAPXCommand *ic;
 
3052
        CamelFolder *folder = job->folder;
 
3053
        CamelIMAPXFolder *ifolder = (CamelIMAPXFolder *) folder;
 
3054
        guint32 total, diff;
 
3055
        gchar *uid = NULL;
 
3056
 
 
3057
        total = camel_folder_summary_count (folder->summary);
 
3058
        diff = ifolder->exists_on_server - total;
 
3059
 
 
3060
        if (total > 0)
 
3061
                uid = camel_folder_summary_uid_from_index (folder->summary, total - 1);
 
3062
        else
 
3063
                uid = g_strdup ("1");
 
3064
 
 
3065
        camel_operation_start (job->op, _("Fetching summary information for new messages in %s"), folder->name);
 
3066
 
 
3067
        if (diff > BATCH_FETCH_COUNT) {
 
3068
                ic = camel_imapx_command_new ("FETCH", job->folder->full_name,
 
3069
                                     "FETCH %s:* (UID FLAGS)", uid);
 
3070
                imapx_uidset_init(&job->u.refresh_info.uidset, BATCH_FETCH_COUNT, 0);
 
3071
                job->u.refresh_info.infos = g_array_new (0, 0, sizeof(struct _refresh_info));
 
3072
                ic->pri = job->pri;
 
3073
                ic->complete = imapx_command_step_fetch_done;
 
3074
        } else {
 
3075
                ic = camel_imapx_command_new ("FETCH", job->folder->full_name,
 
3076
                                        "UID FETCH %s:* (RFC822.SIZE RFC822.HEADER FLAGS)", uid);
 
3077
                ic->pri = job->pri;
 
3078
                ic->complete = imapx_command_fetch_new_messages_done;
 
3079
        }
 
3080
 
 
3081
        g_free (uid);
 
3082
        ic->job = job;
 
3083
        imapx_command_queue (is, ic);
 
3084
}
 
3085
 
 
3086
static void
 
3087
imapx_job_refresh_info_start (CamelIMAPXServer *is, CamelIMAPXJob *job)
 
3088
{
 
3089
        guint32 total;
 
3090
        CamelIMAPXFolder *ifolder = (CamelIMAPXFolder *) job->folder;
 
3091
        CamelFolder *folder = job->folder;
 
3092
        CamelException *ex = job->ex;
 
3093
 
 
3094
        total = camel_folder_summary_count (folder->summary);
 
3095
        /* Check if there are any new messages. The old imap doc says one needs to reselect in case of inbox to fetch
 
3096
           new messages. Need to check if its still true. Just use noop now */
 
3097
        if (ifolder->exists_on_server == total) {
 
3098
                camel_imapx_server_noop (is, folder, ex);
 
3099
 
 
3100
                if (camel_exception_is_set (ex))
 
3101
                        goto exception;
 
3102
        }
 
3103
 
 
3104
        /* Fetch the new messages */
 
3105
        if (ifolder->exists_on_server > total)
 
3106
        {
 
3107
                imapx_server_fetch_new_messages (is, folder, FALSE, job->ex);
 
3108
                if (camel_exception_is_set (job->ex))
 
3109
                        goto exception;
 
3110
        }
 
3111
 
 
3112
        /* Sync changes before fetching status, else unread count will not match. need to think about better ways for this */
 
3113
        imapx_server_sync_changes (is, folder, job->pri, ex);
 
3114
        if (camel_exception_is_set (job->ex))
 
3115
                goto exception;
 
3116
 
 
3117
        /* Check if a rescan is needed */
 
3118
        total = camel_folder_summary_count (folder->summary);
 
3119
        if (ifolder->exists_on_server == total) {
 
3120
                guint32 unread;
 
3121
                CamelIMAPXCommand *ic;
 
3122
 
 
3123
                ic = camel_imapx_command_new ("STATUS", folder->full_name, "STATUS %f (MESSAGES UNSEEN)", folder);
 
3124
                ic->job = job;
 
3125
                ic->pri = job->pri;
 
3126
                ic->complete = imapx_command_status_done;
 
3127
                imapx_command_run_sync (is, ic);
 
3128
 
 
3129
                if (camel_exception_is_set (ic->ex) || ic->status->result != IMAPX_OK) {
 
3130
                        if (!camel_exception_is_set (ic->ex))
 
3131
                                camel_exception_setv(job->ex, 1, "Error refreshing folder: %s", ic->status->text);
 
3132
                        else
 
3133
                                camel_exception_xfer (job->ex, ic->ex);
 
3134
 
 
3135
                        camel_imapx_command_free (ic);
 
3136
                        goto exception;
 
3137
                }
 
3138
                camel_imapx_command_free (ic);
 
3139
 
 
3140
                camel_object_get (folder, NULL, CAMEL_FOLDER_UNREAD, &unread, NULL);
 
3141
                if (ifolder->exists_on_server == total && unread == ifolder->unread_on_server)
 
3142
                        goto exception;
 
3143
        }
 
3144
 
 
3145
        imapx_job_scan_changes_start (is, job);
 
3146
        return;
 
3147
 
 
3148
exception:
 
3149
        imapx_job_done (is, job);
 
3150
}
 
3151
 
 
3152
/* ********************************************************************** */
 
3153
 
 
3154
static void
 
3155
imapx_command_expunge_done (CamelIMAPXServer *is, CamelIMAPXCommand *ic)
 
3156
{
 
3157
        if (camel_exception_is_set (ic->ex) || ic->status->result != IMAPX_OK) {
 
3158
                if (!camel_exception_is_set (ic->ex))
 
3159
                        camel_exception_setv(ic->job->ex, 1, "Error expunging message : %s", ic->status->text);
 
3160
                else
 
3161
                        camel_exception_xfer (ic->job->ex, ic->ex);
 
3162
        } else {
 
3163
                GPtrArray *uids;
 
3164
                CamelFolder *folder = ic->job->folder;
 
3165
 
 
3166
                camel_folder_summary_save_to_db (folder->summary, ic->job->ex);
 
3167
                uids = camel_db_get_folder_deleted_uids (folder->parent_store->cdb_r, folder->full_name, ic->job->ex);
 
3168
 
 
3169
                if (uids && uids->len)  {
 
3170
                        CamelFolderChangeInfo *changes;
 
3171
                        GSList *removed = NULL;
 
3172
                        gint i;
 
3173
 
 
3174
                        changes = camel_folder_change_info_new ();
 
3175
                        for (i = 0; i < uids->len; i++) {
 
3176
                                gchar *uid = uids->pdata [i];
 
3177
                                CamelMessageInfo *mi = camel_folder_summary_uid (folder->summary, uid);
 
3178
 
 
3179
                                if (mi) {
 
3180
                                        imapx_update_summary_for_removed_message (mi, folder);
 
3181
                                        camel_message_info_free (mi);
 
3182
                                }
 
3183
 
 
3184
                                camel_folder_summary_remove_uid_fast (folder->summary, uid);
 
3185
                                camel_folder_change_info_remove_uid (changes, uids->pdata[i]);
 
3186
                                removed = g_slist_prepend (removed, (gpointer) uids->pdata[i]);
 
3187
                        }
 
3188
 
 
3189
                        camel_db_delete_uids (folder->parent_store->cdb_w, folder->full_name, removed, ic->job->ex);
 
3190
                        camel_folder_summary_save_to_db (folder->summary, ic->job->ex);
 
3191
                        camel_object_trigger_event (CAMEL_OBJECT (folder), "folder_changed", changes);
 
3192
                        camel_folder_change_info_free (changes);
 
3193
 
 
3194
                        g_slist_free (removed);
 
3195
                        g_ptr_array_foreach (uids, (GFunc) camel_pstring_free, NULL);
 
3196
                        g_ptr_array_free (uids, TRUE);
 
3197
                }
 
3198
        }
 
3199
 
 
3200
        imapx_job_done (is, ic->job);
 
3201
        camel_imapx_command_free (ic);
 
3202
}
 
3203
 
 
3204
static void
 
3205
imapx_job_expunge_start(CamelIMAPXServer *is, CamelIMAPXJob *job)
 
3206
{
 
3207
        CamelIMAPXCommand *ic;
 
3208
 
 
3209
        imapx_server_sync_changes (is, job->folder, job->pri, job->ex);
 
3210
 
 
3211
        /* TODO handle UIDPLUS capability */
 
3212
        ic = camel_imapx_command_new("EXPUNGE", job->folder->full_name, "EXPUNGE");
 
3213
        ic->job = job;
 
3214
        ic->pri = job->pri;
 
3215
        ic->complete = imapx_command_expunge_done;
 
3216
        imapx_command_queue(is, ic);
 
3217
}
 
3218
 
 
3219
/* ********************************************************************** */
 
3220
 
 
3221
static void
 
3222
imapx_command_list_done (CamelIMAPXServer *is, CamelIMAPXCommand *ic)
 
3223
{
 
3224
        if (camel_exception_is_set (ic->ex) || ic->status->result != IMAPX_OK) {
 
3225
                if (!camel_exception_is_set (ic->ex))
 
3226
                        camel_exception_setv(ic->job->ex, 1, "Error fetching folders : %s", ic->status->text);
 
3227
                else
 
3228
                        camel_exception_xfer (ic->job->ex, ic->ex);
 
3229
        }
 
3230
 
 
3231
        e(printf ("==== list or lsub completed ==== \n"));
 
3232
        imapx_job_done (is, ic->job);
 
3233
        camel_imapx_command_free (ic);
 
3234
}
 
3235
 
 
3236
static void
 
3237
imapx_job_list_start(CamelIMAPXServer *is, CamelIMAPXJob *job)
 
3238
{
 
3239
        CamelIMAPXCommand *ic;
 
3240
 
 
3241
        ic = camel_imapx_command_new("LIST", NULL, "%s \"\" %s",
 
3242
                                     (job->u.list.flags & CAMEL_STORE_FOLDER_INFO_SUBSCRIBED)?"LSUB":"LIST",
 
3243
                                     job->u.list.pattern);
 
3244
        ic->pri = job->pri;
 
3245
        ic->job = job;
 
3246
        ic->complete = imapx_command_list_done;
 
3247
        imapx_command_queue(is, ic);
 
3248
}
 
3249
/* ********************************************************************** */
 
3250
 
 
3251
 
 
3252
static gchar *
 
3253
imapx_encode_folder_name (CamelIMAPXStore *istore, const gchar *folder_name)
 
3254
{
 
3255
        gchar *fname, *encoded;
 
3256
 
 
3257
        fname = camel_imapx_store_summary_full_from_path(istore->summary, folder_name);
 
3258
        if (fname) {
 
3259
                encoded = camel_utf8_utf7(fname);
 
3260
                g_free (fname);
 
3261
        } else
 
3262
                encoded = camel_utf8_utf7 (folder_name);
 
3263
        
 
3264
        return encoded;
 
3265
}
 
3266
 
 
3267
static void
 
3268
imapx_command_subscription_done (CamelIMAPXServer *is, CamelIMAPXCommand *ic)
 
3269
{
 
3270
        if (camel_exception_is_set (ic->ex) || ic->status->result != IMAPX_OK) {
 
3271
                if (!camel_exception_is_set (ic->ex))
 
3272
                        camel_exception_setv(ic->job->ex, 1, "Error subscribing to folder : %s", ic->status->text);
 
3273
                else
 
3274
                        camel_exception_xfer (ic->job->ex, ic->ex);
 
3275
        }
 
3276
 
 
3277
        imapx_job_done (is, ic->job);
 
3278
        camel_imapx_command_free (ic);
 
3279
}
 
3280
 
 
3281
static void
 
3282
imapx_job_manage_subscription_start (CamelIMAPXServer *is, CamelIMAPXJob *job)
 
3283
{
 
3284
        CamelIMAPXCommand *ic;
 
3285
        const gchar *str = NULL;
 
3286
        gchar *encoded_fname = NULL;
 
3287
        
 
3288
        
 
3289
        if (job->u.manage_subscriptions.subscribe)
 
3290
                str = "SUBSCRIBE";
 
3291
        else
 
3292
                str = "UNSUBSCRIBE";
 
3293
 
 
3294
        encoded_fname = imapx_encode_folder_name ((CamelIMAPXStore *) is->store, job->u.manage_subscriptions.folder_name);
 
3295
        ic = camel_imapx_command_new (str, NULL, "%s %s", str, encoded_fname);
 
3296
        
 
3297
        ic->pri = job->pri;
 
3298
        ic->job = job;
 
3299
        ic->complete = imapx_command_subscription_done;
 
3300
        imapx_command_queue(is, ic);
 
3301
 
 
3302
        g_free (encoded_fname);
 
3303
}
 
3304
 
 
3305
/* ********************************************************************** */
 
3306
 
 
3307
static void
 
3308
imapx_command_create_folder_done (CamelIMAPXServer *is, CamelIMAPXCommand *ic)
 
3309
{
 
3310
        if (camel_exception_is_set (ic->ex) || ic->status->result != IMAPX_OK) {
 
3311
                if (!camel_exception_is_set (ic->ex))
 
3312
                        camel_exception_setv(ic->job->ex, 1, "Error creating to folder : %s", ic->status->text);
 
3313
                else
 
3314
                        camel_exception_xfer (ic->job->ex, ic->ex);
 
3315
        }
 
3316
 
 
3317
        imapx_job_done (is, ic->job);
 
3318
        camel_imapx_command_free (ic);
 
3319
}
 
3320
 
 
3321
static void
 
3322
imapx_job_create_folder_start (CamelIMAPXServer *is, CamelIMAPXJob *job)
 
3323
{
 
3324
        CamelIMAPXCommand *ic;
 
3325
        gchar *encoded_fname = NULL;
 
3326
 
 
3327
        encoded_fname = camel_utf8_utf7 (job->u.folder_name);
 
3328
        ic = camel_imapx_command_new ("CREATE", NULL, "CREATE %s", encoded_fname);
 
3329
        ic->pri = job->pri;
 
3330
        ic->job = job;
 
3331
        ic->complete = imapx_command_create_folder_done;
 
3332
        imapx_command_queue(is, ic);
 
3333
 
 
3334
        g_free (encoded_fname);
 
3335
}
 
3336
 
 
3337
/* ********************************************************************** */
 
3338
 
 
3339
static void
 
3340
imapx_command_delete_folder_done (CamelIMAPXServer *is, CamelIMAPXCommand *ic)
 
3341
{
 
3342
        if (camel_exception_is_set (ic->ex) || ic->status->result != IMAPX_OK) {
 
3343
                if (!camel_exception_is_set (ic->ex))
 
3344
                        camel_exception_setv(ic->job->ex, 1, "Error deleting to folder : %s", ic->status->text);
 
3345
                else
 
3346
                        camel_exception_xfer (ic->job->ex, ic->ex);
 
3347
        }
 
3348
 
 
3349
        imapx_job_done (is, ic->job);
 
3350
        camel_imapx_command_free (ic);
 
3351
}
 
3352
 
 
3353
static void
 
3354
imapx_job_delete_folder_start (CamelIMAPXServer *is, CamelIMAPXJob *job)
 
3355
{
 
3356
        CamelIMAPXCommand *ic;
 
3357
        gchar *encoded_fname = NULL;
 
3358
 
 
3359
        encoded_fname = imapx_encode_folder_name ((CamelIMAPXStore *) is->store, job->u.folder_name);
 
3360
 
 
3361
        /* make sure to-be-deleted folder is not selected by selecting INBOX for this operation */
 
3362
        ic = camel_imapx_command_new ("DELETE", "INBOX", "DELETE %s", encoded_fname);
 
3363
        ic->pri = job->pri;
 
3364
        ic->job = job;
 
3365
        ic->complete = imapx_command_delete_folder_done;
 
3366
        imapx_command_queue(is, ic);
 
3367
 
 
3368
        g_free (encoded_fname);
 
3369
}
 
3370
 
 
3371
/* ********************************************************************** */
 
3372
 
 
3373
        static void
 
3374
imapx_command_rename_folder_done (CamelIMAPXServer *is, CamelIMAPXCommand *ic)
 
3375
{
 
3376
        if (camel_exception_is_set (ic->ex) || ic->status->result != IMAPX_OK) {
 
3377
                if (!camel_exception_is_set (ic->ex))
 
3378
                        camel_exception_setv(ic->job->ex, 1, "Error renaming to folder : %s", ic->status->text);
 
3379
                else
 
3380
                        camel_exception_xfer (ic->job->ex, ic->ex);
 
3381
        }
 
3382
 
 
3383
        imapx_job_done (is, ic->job);
 
3384
        camel_imapx_command_free (ic);
 
3385
}
 
3386
 
 
3387
static void
 
3388
imapx_job_rename_folder_start (CamelIMAPXServer *is, CamelIMAPXJob *job)
 
3389
{
 
3390
        CamelIMAPXCommand *ic;
 
3391
        gchar *en_ofname = NULL, *en_nfname = NULL;
 
3392
 
 
3393
        en_ofname = imapx_encode_folder_name ((CamelIMAPXStore *) is->store, job->u.rename_folder.ofolder_name);
 
3394
        en_nfname = imapx_encode_folder_name ((CamelIMAPXStore *) is->store, job->u.rename_folder.nfolder_name);
 
3395
        
 
3396
        ic = camel_imapx_command_new ("RENAME", "INBOX", "RENAME %s %s", en_ofname, en_nfname);
 
3397
        ic->pri = job->pri;
 
3398
        ic->job = job;
 
3399
        ic->complete = imapx_command_rename_folder_done;
 
3400
        imapx_command_queue(is, ic);
 
3401
 
 
3402
        g_free (en_ofname);
 
3403
        g_free (en_nfname);
 
3404
}
 
3405
 
 
3406
/* ********************************************************************** */
 
3407
 
 
3408
static void
 
3409
imapx_command_noop_done (CamelIMAPXServer *is, CamelIMAPXCommand *ic)
 
3410
{
 
3411
        if (camel_exception_is_set (ic->ex) || ic->status->result != IMAPX_OK) {
 
3412
                if (!camel_exception_is_set (ic->ex))
 
3413
                        camel_exception_setv(ic->job->ex, 1, "Error performing NOOP: %s", ic->status->text);
 
3414
                else
 
3415
                        camel_exception_xfer (ic->job->ex, ic->ex);
 
3416
        }
 
3417
 
 
3418
        imapx_job_done (is, ic->job);
 
3419
        camel_imapx_command_free (ic);
 
3420
}
 
3421
 
 
3422
static void
 
3423
imapx_job_noop_start(CamelIMAPXServer *is, CamelIMAPXJob *job)
 
3424
{
 
3425
        CamelIMAPXCommand *ic;
 
3426
 
 
3427
        if (job->folder)
 
3428
                ic = camel_imapx_command_new ("NOOP", job->folder->full_name, "NOOP");
 
3429
        else
 
3430
                ic = camel_imapx_command_new ("NOOP", NULL, "NOOP");
 
3431
 
 
3432
        ic->job = job;
 
3433
        ic->complete = imapx_command_noop_done;
 
3434
        if (job->folder)
 
3435
                ic->pri = IMAPX_PRIORITY_REFRESH_INFO;
 
3436
        else
 
3437
                ic->pri = IMAPX_PRIORITY_NOOP;
 
3438
        imapx_command_queue(is, ic);
 
3439
}
 
3440
 
 
3441
/* ********************************************************************** */
 
3442
 
 
3443
/* FIXME: this is basically a copy of the same in camel-imapx-utils.c */
 
3444
static struct {
 
3445
        const gchar *name;
 
3446
        guint32 flag;
 
3447
} flags_table[] = {
 
3448
        { "\\ANSWERED", CAMEL_MESSAGE_ANSWERED },
 
3449
        { "\\DELETED", CAMEL_MESSAGE_DELETED },
 
3450
        { "\\DRAFT", CAMEL_MESSAGE_DRAFT },
 
3451
        { "\\FLAGGED", CAMEL_MESSAGE_FLAGGED },
 
3452
        { "\\SEEN", CAMEL_MESSAGE_SEEN },
 
3453
        { "\\RECENT", CAMEL_IMAPX_MESSAGE_RECENT }
 
3454
};
 
3455
 
 
3456
/*
 
3457
  flags 00101000
 
3458
 sflags 01001000
 
3459
 ^      01100000
 
3460
~flags  11010111
 
3461
&       01000000
 
3462
 
 
3463
&flags  00100000
 
3464
*/
 
3465
 
 
3466
static void
 
3467
imapx_command_sync_changes_done (CamelIMAPXServer *is, CamelIMAPXCommand *ic)
 
3468
{
 
3469
        CamelIMAPXJob *job = ic->job;
 
3470
        gboolean failed = FALSE;
 
3471
 
 
3472
        job->commands--;
 
3473
 
 
3474
        /* If this worked, we should really just update the changes that we sucessfully
 
3475
           stored, so we dont have to worry about sending them again ...
 
3476
           But then we'd have to track which uid's we actually updated, so its easier
 
3477
           just to refresh all of the ones we got.
 
3478
 
 
3479
           Not that ... given all the asynchronicity going on, we're guaranteed
 
3480
           that what we just set is actually what is on the server now .. but
 
3481
           if it isn't, i guess we'll fix up next refresh */
 
3482
 
 
3483
        if (camel_exception_is_set (ic->ex) || ic->status->result != IMAPX_OK) {
 
3484
                if (!camel_exception_is_set (ic->ex))
 
3485
                        camel_exception_setv(job->ex, 1, "Error syncing changes: %s", ic->status->text);
 
3486
                else
 
3487
                        camel_exception_xfer (job->ex, ic->ex);
 
3488
                failed = TRUE;
 
3489
        }
 
3490
 
 
3491
        /* lock cache ? */
 
3492
        if (!failed)
 
3493
        {
 
3494
                gint i;
 
3495
 
 
3496
                for (i=0;i<job->u.sync_changes.changed_uids->len;i++) {
 
3497
                        CamelIMAPXMessageInfo *info = (CamelIMAPXMessageInfo *) camel_folder_summary_uid (job->folder->summary,
 
3498
                                        job->u.sync_changes.changed_uids->pdata[i]);
 
3499
 
 
3500
                        if (!info)
 
3501
                                continue;
 
3502
 
 
3503
                        info->server_flags = ((CamelMessageInfoBase *)info)->flags & CAMEL_IMAPX_SERVER_FLAGS;
 
3504
                        info->info.flags &= ~CAMEL_MESSAGE_FOLDER_FLAGGED;
 
3505
                        info->info.dirty = TRUE;
 
3506
 
 
3507
                        camel_folder_summary_touch (job->folder->summary);
 
3508
                        camel_message_info_free (info);
 
3509
 
 
3510
                        /* FIXME: move over user flags too */
 
3511
                }
 
3512
        }
 
3513
 
 
3514
        if (job->commands == 0) {
 
3515
                if (job->folder->summary && (job->folder->summary->flags & CAMEL_SUMMARY_DIRTY) != 0) {
 
3516
                        CamelStoreInfo *si;
 
3517
 
 
3518
                        /* ... and store's summary when folder's summary is dirty */
 
3519
                        si = camel_store_summary_path ((CamelStoreSummary *)((CamelIMAPXStore *) job->folder->parent_store)->summary, job->folder->full_name);
 
3520
                        if (si) {
 
3521
                                if (si->total != job->folder->summary->saved_count || si->unread != job->folder->summary->unread_count) {
 
3522
                                        si->total = job->folder->summary->saved_count;
 
3523
                                        si->unread = job->folder->summary->unread_count;
 
3524
                                        camel_store_summary_touch ((CamelStoreSummary *)((CamelIMAPXStore *) job->folder->parent_store)->summary);
 
3525
                                }
 
3526
 
 
3527
                                camel_store_summary_info_free ((CamelStoreSummary *)((CamelIMAPXStore *) job->folder->parent_store)->summary, si);
 
3528
                        }
 
3529
                }
 
3530
 
 
3531
                camel_folder_summary_save_to_db (job->folder->summary, job->ex);
 
3532
                camel_store_summary_save((CamelStoreSummary *)((CamelIMAPXStore *) job->folder->parent_store)->summary);
 
3533
 
 
3534
                imapx_job_done (is, job);
 
3535
        }
 
3536
        camel_imapx_command_free (ic);
 
3537
}
 
3538
 
 
3539
static void
 
3540
imapx_job_sync_changes_start(CamelIMAPXServer *is, CamelIMAPXJob *job)
 
3541
{
 
3542
        guint32 i, j;
 
3543
        struct _uidset_state ss;
 
3544
        GPtrArray *uids = job->u.sync_changes.changed_uids;
 
3545
        gint on;
 
3546
 
 
3547
        for (on=0;on<2;on++) {
 
3548
                guint32 orset = on?job->u.sync_changes.on_set:job->u.sync_changes.off_set;
 
3549
                GArray *user_set = on?job->u.sync_changes.on_user:job->u.sync_changes.off_user;
 
3550
 
 
3551
                for (j = 0; j < G_N_ELEMENTS (flags_table); j++) {
 
3552
                        guint32 flag = flags_table[j].flag;
 
3553
                        CamelIMAPXCommand *ic = NULL;
 
3554
 
 
3555
                        if ((orset & flag) == 0)
 
3556
                                continue;
 
3557
 
 
3558
                        c(printf("checking/storing %s flags '%s'\n", on?"on":"off", flags_table[j].name));
 
3559
                        imapx_uidset_init(&ss, 0, 100);
 
3560
                        for (i = 0; i < uids->len; i++) {
 
3561
                                CamelIMAPXMessageInfo *info = (CamelIMAPXMessageInfo *)camel_folder_summary_uid
 
3562
                                                                                (job->folder->summary, uids->pdata[i]);
 
3563
                                guint32 flags;
 
3564
                                guint32 sflags;
 
3565
                                gint send;
 
3566
 
 
3567
                                if (!info)
 
3568
                                        continue;
 
3569
 
 
3570
                                flags = ((CamelMessageInfoBase *)info)->flags & CAMEL_IMAPX_SERVER_FLAGS;
 
3571
                                sflags = info->server_flags & CAMEL_IMAPX_SERVER_FLAGS;
 
3572
                                send = 0;
 
3573
 
 
3574
                                if ( (on && (((flags ^ sflags) & flags) & flag))
 
3575
                                     || (!on && (((flags ^ sflags) & ~flags) & flag))) {
 
3576
                                        if (ic == NULL) {
 
3577
                                                ic = camel_imapx_command_new("STORE", job->folder->full_name, "UID STORE ");
 
3578
                                                ic->complete = imapx_command_sync_changes_done;
 
3579
                                                ic->job = job;
 
3580
                                                ic->pri = job->pri;
 
3581
                                        }
 
3582
                                        send = imapx_uidset_add(&ss, ic, camel_message_info_uid(info));
 
3583
                                }
 
3584
                                if (send || (i == uids->len-1 && imapx_uidset_done(&ss, ic))) {
 
3585
                                        job->commands++;
 
3586
                                        camel_imapx_command_add(ic, " %tFLAGS.SILENT (%t)", on?"+":"-", flags_table[j].name);
 
3587
                                        imapx_command_queue(is, ic);
 
3588
                                        ic = NULL;
 
3589
                                }
 
3590
                                camel_message_info_free (info);
 
3591
                        }
 
3592
                }
 
3593
 
 
3594
                if (user_set) {
 
3595
                        CamelIMAPXCommand *ic = NULL;
 
3596
 
 
3597
                        for (j=0; j<user_set->len; j++) {
 
3598
                                struct _imapx_flag_change *c = &g_array_index(user_set, struct _imapx_flag_change, j);
 
3599
 
 
3600
                                imapx_uidset_init(&ss, 0, 100);
 
3601
                                for (i=0; i < c->infos->len; i++) {
 
3602
                                        CamelIMAPXMessageInfo *info = c->infos->pdata[i];
 
3603
 
 
3604
                                        if (ic == NULL) {
 
3605
                                                ic = camel_imapx_command_new("STORE", job->folder->full_name, "UID STORE ");
 
3606
                                                ic->complete = imapx_command_sync_changes_done;
 
3607
                                                ic->job = job;
 
3608
                                                ic->pri = job->pri;
 
3609
                                        }
 
3610
 
 
3611
                                        if (imapx_uidset_add(&ss, ic, camel_message_info_uid (info))
 
3612
                                            || (i == c->infos->len-1 && imapx_uidset_done (&ss, ic))) {
 
3613
                                                job->commands++;
 
3614
                                                camel_imapx_command_add(ic, " %tFLAGS.SILENT (%t)", on?"+":"-", c->name);
 
3615
                                                imapx_command_queue(is, ic);
 
3616
                                                ic = NULL;
 
3617
                                        }
 
3618
                                }
 
3619
                        }
 
3620
                }
 
3621
        }
 
3622
 
 
3623
        /* Since this may start in another thread ... we need to
 
3624
           lock the commands count, ho hum */
 
3625
 
 
3626
        if (job->commands == 0) {
 
3627
                imapx_job_done (is, job);
 
3628
        }
 
3629
}
 
3630
 
 
3631
/* we cancel all the commands and their jobs, so associated jobs will be notified */
 
3632
static void
 
3633
cancel_all_jobs (CamelIMAPXServer *is, CamelException *ex)
 
3634
{
 
3635
        CamelIMAPXCommand *cw, *cn;
 
3636
        gint i = 0;
 
3637
 
 
3638
        while (i < 2) {
 
3639
                QUEUE_LOCK(is);
 
3640
                if (i == 1)
 
3641
                        cw = (CamelIMAPXCommand *) is->queue.head;
 
3642
                else
 
3643
                        cw = (CamelIMAPXCommand *) is->active.head;
 
3644
 
 
3645
                cn = cw->next;
 
3646
                QUEUE_UNLOCK(is);
 
3647
 
 
3648
                while (cn) {
 
3649
                        QUEUE_LOCK(is);
 
3650
                        camel_dlist_remove ((CamelDListNode *)cw);
 
3651
                        QUEUE_UNLOCK(is);
 
3652
 
 
3653
                        camel_exception_set (cw->ex, ex->id, ex->desc);
 
3654
 
 
3655
                        cw->complete (is, cw);
 
3656
                        cw = cn;
 
3657
 
 
3658
                        QUEUE_LOCK(is);
 
3659
                        cn = cn->next;
 
3660
                        QUEUE_UNLOCK(is);
 
3661
                }
 
3662
 
 
3663
                i++;
 
3664
        }
 
3665
}
 
3666
 
 
3667
/* ********************************************************************** */
 
3668
 
 
3669
static void
 
3670
parse_contents (CamelIMAPXServer *is, CamelException *ex)
 
3671
{
 
3672
        gint buffered = 0;
 
3673
 
 
3674
        do {
 
3675
                imapx_step(is, ex);
 
3676
 
 
3677
                buffered = camel_imapx_stream_buffered (is->stream);
 
3678
 
 
3679
        } while (buffered && !camel_exception_is_set (ex));
 
3680
}
 
3681
 
 
3682
/*
 
3683
   The main processing (reading) loop.
 
3684
 
 
3685
   Main area of locking required is command_queue
 
3686
   and command_start_next, the 'literal' command,
 
3687
   the jobs queue, the active queue, the queue
 
3688
   queue. */
 
3689
static gpointer
 
3690
imapx_parser_thread (gpointer d)
 
3691
{
 
3692
        CamelIMAPXServer *is = d;
 
3693
        CamelException ex = CAMEL_EXCEPTION_INITIALISER;
 
3694
        CamelOperation *op;
 
3695
 
 
3696
        op = camel_operation_new (NULL, NULL);
 
3697
        op = camel_operation_register (op);
 
3698
        is->op = op;
 
3699
 
 
3700
        while (!camel_exception_is_set (&ex) && is->stream) {
 
3701
                camel_operation_uncancel (op);
 
3702
#ifdef HAVE_SSL
 
3703
                if (is->is_ssl_stream)  {
 
3704
                        PRPollDesc pollfds[2] = { };
 
3705
                        gint res;
 
3706
 
 
3707
                        pollfds[0].fd = camel_tcp_stream_ssl_sockfd ((CamelTcpStreamSSL *)is->stream->source);
 
3708
                        pollfds[0].in_flags = PR_POLL_READ;
 
3709
                        pollfds[1].fd = camel_operation_cancel_prfd (op);
 
3710
                        pollfds[1].in_flags = PR_POLL_READ;
 
3711
 
 
3712
#include <prio.h>
 
3713
 
 
3714
                        res = PR_Poll(pollfds, 2, PR_MillisecondsToInterval (30 * 1000));
 
3715
                        if (res == -1)
 
3716
                                g_usleep(1) /* ?? */ ;
 
3717
                        else if (res == 0) {
 
3718
                                /* timed out */
 
3719
                        } else if ((pollfds[0].out_flags & PR_POLL_READ)) {
 
3720
                                parse_contents (is, &ex);
 
3721
                        } else if (pollfds[1].out_flags & PR_POLL_READ)
 
3722
                                errno = EINTR;
 
3723
                }
 
3724
#endif
 
3725
 
 
3726
                if (!is->is_ssl_stream) {
 
3727
                        GPollFD fds[2] = { {0, 0, 0}, {0, 0, 0} };
 
3728
                        gint res;
 
3729
 
 
3730
                        fds[0].fd = ((CamelTcpStreamRaw *)is->stream->source)->sockfd;
 
3731
                        fds[0].events = G_IO_IN;
 
3732
                        fds[1].fd = camel_operation_cancel_fd (op);
 
3733
                        fds[1].events = G_IO_IN;
 
3734
 
 
3735
                        res = g_poll(fds, 2, 1000*30);
 
3736
                        if (res == -1)
 
3737
                                g_usleep(1) /* ?? */ ;
 
3738
                        else if (res == 0)
 
3739
                                /* timed out */;
 
3740
                        else if (fds[0].revents & G_IO_IN) {
 
3741
                                parse_contents (is, &ex);
 
3742
                        } else if (fds[1].revents & G_IO_IN)
 
3743
                                errno = EINTR;
 
3744
                }
 
3745
 
 
3746
                if (camel_application_is_exiting || is->parser_quit) {
 
3747
                        camel_exception_setv (&ex, CAMEL_EXCEPTION_USER_CANCEL, "Operation Cancelled: %s", g_strerror(errno));
 
3748
                        break;
 
3749
                }
 
3750
 
 
3751
                if (camel_operation_cancel_check (op)) {
 
3752
                        gint is_empty;
 
3753
 
 
3754
                        QUEUE_LOCK (is);
 
3755
                        is_empty = camel_dlist_empty (&is->active);
 
3756
                        QUEUE_UNLOCK (is);
 
3757
 
 
3758
                        if ((is_empty || (imapx_idle_supported (is) && imapx_in_idle (is))))
 
3759
                                camel_operation_uncancel (op);
 
3760
                        else
 
3761
                                camel_exception_setv (&ex, CAMEL_EXCEPTION_USER_CANCEL, "Operation Cancelled: %s", g_strerror(errno));
 
3762
                }
 
3763
        }
 
3764
 
 
3765
        imapx_disconnect (is);
 
3766
        cancel_all_jobs (is, &ex);
 
3767
 
 
3768
        if (imapx_idle_supported (is))
 
3769
                imapx_exit_idle (is);
 
3770
 
 
3771
        camel_exception_clear (&ex);
 
3772
 
 
3773
        if (op) {
 
3774
                camel_operation_unregister (op);
 
3775
                camel_operation_unref (op);
 
3776
        }
 
3777
        is->op = NULL;
 
3778
 
 
3779
        is->parser_thread = NULL;
 
3780
        is->parser_quit = FALSE;
 
3781
        return NULL;
 
3782
}
 
3783
 
 
3784
static void
 
3785
imapx_server_class_init(CamelIMAPXServerClass *ieclass)
 
3786
{
 
3787
        ieclass->tagprefix = 'A';
 
3788
 
 
3789
//      camel_object_class_add_event((CamelObjectClass *)ieclass, "status", NULL);
 
3790
}
 
3791
 
 
3792
static void
 
3793
imapx_server_init(CamelIMAPXServer *is, CamelIMAPXServerClass *isclass)
 
3794
{
 
3795
        camel_dlist_init(&is->queue);
 
3796
        camel_dlist_init(&is->active);
 
3797
        camel_dlist_init(&is->done);
 
3798
        camel_dlist_init(&is->jobs);
 
3799
 
 
3800
        /* not used at the moment. Use it in future */
 
3801
        is->job_timeout = 29 * 60 * 1000 * 1000;
 
3802
 
 
3803
        g_static_rec_mutex_init (&is->queue_lock);
 
3804
        g_static_rec_mutex_init (&is->ostream_lock);
 
3805
 
 
3806
        is->tagprefix = isclass->tagprefix;
 
3807
        isclass->tagprefix++;
 
3808
        if (isclass->tagprefix > 'Z')
 
3809
                isclass->tagprefix = 'A';
 
3810
        is->tagprefix = 'A';
 
3811
 
 
3812
        is->state = IMAPX_DISCONNECTED;
 
3813
 
 
3814
        is->expunged = NULL;
 
3815
        is->changes = camel_folder_change_info_new ();
 
3816
        is->parser_quit = FALSE;
 
3817
 
 
3818
        is->uid_eflags = g_hash_table_new_full (g_str_hash, g_str_equal, (GDestroyNotify)g_free, (GDestroyNotify) e_flag_free);
 
3819
}
 
3820
 
 
3821
static void
 
3822
imapx_server_finalise(CamelIMAPXServer *is, CamelIMAPXServerClass *isclass)
 
3823
{
 
3824
        g_static_rec_mutex_free(&is->queue_lock);
 
3825
        g_static_rec_mutex_free (&is->ostream_lock);
 
3826
        g_hash_table_destroy (is->uid_eflags);
 
3827
 
 
3828
        camel_folder_change_info_free (is->changes);
 
3829
}
 
3830
 
 
3831
CamelType
 
3832
camel_imapx_server_get_type (void)
 
3833
{
 
3834
        static CamelType type = CAMEL_INVALID_TYPE;
 
3835
 
 
3836
        if (type == CAMEL_INVALID_TYPE) {
 
3837
                type = camel_type_register (
 
3838
                        camel_object_get_type (),
 
3839
                        "CamelIMAPXServer",
 
3840
                        sizeof (CamelIMAPXServer),
 
3841
                        sizeof (CamelIMAPXServerClass),
 
3842
                        (CamelObjectClassInitFunc) imapx_server_class_init,
 
3843
                        NULL,
 
3844
                        (CamelObjectInitFunc) imapx_server_init,
 
3845
                        (CamelObjectFinalizeFunc) imapx_server_finalise);
 
3846
        }
 
3847
 
 
3848
        return type;
 
3849
}
 
3850
 
 
3851
CamelIMAPXServer *
 
3852
camel_imapx_server_new(CamelStore *store, CamelURL *url)
 
3853
{
 
3854
        CamelIMAPXServer *is = (CamelIMAPXServer *)camel_object_new(camel_imapx_server_get_type());
 
3855
 
 
3856
        is->session = ((CamelService *)store)->session;
 
3857
        camel_object_ref(is->session);
 
3858
        is->store = store;
 
3859
        is->url = camel_url_copy(url);
 
3860
 
 
3861
        return is;
 
3862
}
 
3863
 
 
3864
static gboolean
 
3865
imapx_disconnect (CamelIMAPXServer *is)
 
3866
{
 
3867
        gboolean ret = TRUE;
 
3868
 
 
3869
        g_static_rec_mutex_lock (&is->ostream_lock);
 
3870
 
 
3871
        if (is->stream) {
 
3872
                if (camel_stream_close (is->stream->source) == -1)
 
3873
                        ret = FALSE;
 
3874
 
 
3875
                camel_object_unref (CAMEL_OBJECT (is->stream));
 
3876
                is->stream = NULL;
 
3877
        }
 
3878
 
 
3879
        /* TODO need a select lock */
 
3880
        if (is->select_folder) {
 
3881
                camel_object_unref(is->select_folder);
 
3882
                is->select_folder = NULL;
 
3883
        }
 
3884
 
 
3885
        if (is->select) {
 
3886
                g_free(is->select);
 
3887
                is->select = NULL;
 
3888
        }
 
3889
 
 
3890
        if (is->select_pending) {
 
3891
                camel_object_unref(is->select_pending);
 
3892
                is->select_pending = NULL;
 
3893
        }
 
3894
 
 
3895
        if (is->literal) {
 
3896
                camel_imapx_command_free (is->literal);
 
3897
                is->literal = NULL;
 
3898
        }
 
3899
 
 
3900
        if (is->cinfo) {
 
3901
                imapx_free_capability(is->cinfo);
 
3902
                is->cinfo = NULL;       
 
3903
        }
 
3904
 
 
3905
        is->state = IMAPX_DISCONNECTED;
 
3906
 
 
3907
        g_static_rec_mutex_unlock (&is->ostream_lock);
 
3908
 
 
3909
        return ret;
 
3910
}
 
3911
 
 
3912
/* Client commands */
 
3913
gboolean
 
3914
camel_imapx_server_connect (CamelIMAPXServer *is, gboolean connect, CamelException *ex)
 
3915
{
 
3916
        gboolean ret = FALSE;
 
3917
 
 
3918
        CAMEL_SERVICE_REC_LOCK (is->store, connect_lock);
 
3919
        if (connect) {
 
3920
                if (is->state == IMAPX_AUTHENTICATED || is->state == IMAPX_SELECTED) {
 
3921
                        ret = TRUE;
 
3922
                        goto exit;
 
3923
                }
 
3924
 
 
3925
                g_static_rec_mutex_lock (&is->ostream_lock);
 
3926
                imapx_reconnect (is, ex);
 
3927
                g_static_rec_mutex_unlock (&is->ostream_lock);
 
3928
 
 
3929
                if (camel_exception_is_set (ex)) {
 
3930
                        ret = FALSE;
 
3931
                        goto exit;
 
3932
                }
 
3933
 
 
3934
                is->parser_thread = g_thread_create((GThreadFunc) imapx_parser_thread, is, TRUE, NULL);
 
3935
                ret = TRUE;
 
3936
        } else {
 
3937
                is->parser_quit = TRUE;
 
3938
                camel_operation_cancel (is->op);
 
3939
                if (is->parser_thread)
 
3940
                        g_thread_join (is->parser_thread);
 
3941
                ret = TRUE;
 
3942
        }
 
3943
 
 
3944
exit:
 
3945
        CAMEL_SERVICE_REC_UNLOCK (is->store, connect_lock);
 
3946
        return ret;
 
3947
}
 
3948
 
 
3949
static CamelStream *
 
3950
imapx_server_get_message (CamelIMAPXServer *is, CamelFolder *folder, CamelOperation *op, const gchar *uid, gint pri, CamelException *ex)
 
3951
{
 
3952
        CamelStream *stream = NULL, *tmp_stream;
 
3953
        CamelIMAPXFolder *ifolder = (CamelIMAPXFolder *) folder;
 
3954
        CamelIMAPXJob *job;
 
3955
        gchar *cache_file = NULL;
 
3956
        CamelMessageInfo *mi;
 
3957
        gboolean registered;
 
3958
        EFlag *flag = NULL;
 
3959
 
 
3960
        cache_file = camel_data_cache_get_filename  (ifolder->cache, "cur", uid, NULL);
 
3961
        if (g_file_test (cache_file, G_FILE_TEST_EXISTS)) {
 
3962
                g_free (cache_file);
 
3963
                return NULL;
 
3964
        }
 
3965
        g_free (cache_file);
 
3966
 
 
3967
        QUEUE_LOCK (is);
 
3968
 
 
3969
        if ((job = imapx_is_job_in_queue (is, folder->full_name, IMAPX_JOB_GET_MESSAGE, uid))) {
 
3970
                flag = g_hash_table_lookup (is->uid_eflags, uid);
 
3971
        
 
3972
                if (pri > job->pri)
 
3973
                        job->pri = pri;
 
3974
        
 
3975
                QUEUE_UNLOCK (is);
 
3976
 
 
3977
                e_flag_wait (flag);
 
3978
                
 
3979
                stream = camel_data_cache_get (ifolder->cache, "cur", uid, NULL);
 
3980
                if (!stream)
 
3981
                        camel_exception_set (ex, 1, "Could not retrieve the message");
 
3982
                return stream;
 
3983
        }
 
3984
 
 
3985
        mi = camel_folder_summary_uid (folder->summary, uid);
 
3986
        if (!mi) {
 
3987
                camel_exception_setv (
 
3988
                                ex, CAMEL_EXCEPTION_FOLDER_INVALID_UID,
 
3989
                                _("Cannot get message with message ID %s: %s"),
 
3990
                                uid, _("No such message available."));
 
3991
                QUEUE_UNLOCK (is);
 
3992
                return NULL;
 
3993
        }
 
3994
 
 
3995
        tmp_stream = camel_data_cache_add (ifolder->cache, "tmp", uid, NULL);
 
3996
 
 
3997
        job = g_malloc0(sizeof(*job));
 
3998
        job->pri = pri;
 
3999
        job->type = IMAPX_JOB_GET_MESSAGE;
 
4000
        job->start = imapx_job_get_message_start;
 
4001
        job->folder = folder;
 
4002
        job->op = op;
 
4003
        job->u.get_message.uid = (gchar *)uid;
 
4004
        job->u.get_message.stream = tmp_stream;
 
4005
        job->ex = ex;
 
4006
 
 
4007
        if (((CamelMessageInfoBase *) mi)->size > MULTI_SIZE)
 
4008
                job->u.get_message.use_multi_fetch = TRUE;
 
4009
 
 
4010
        job->u.get_message.size = ((CamelMessageInfoBase *) mi)->size;
 
4011
        camel_message_info_free (mi);
 
4012
        registered = imapx_register_job (is, job);
 
4013
        flag = e_flag_new ();
 
4014
        g_hash_table_insert (is->uid_eflags, g_strdup (uid), flag);
 
4015
 
 
4016
        QUEUE_UNLOCK (is);
 
4017
 
 
4018
        if (registered)
 
4019
                imapx_run_job(is, job);
 
4020
        
 
4021
        e_flag_set (flag);
 
4022
        if (!camel_exception_is_set (job->ex))
 
4023
                stream = job->u.get_message.stream;
 
4024
        
 
4025
        g_free(job);
 
4026
 
 
4027
        /* HACK FIXME just sleep for sometime so that the other waiting locks gets released by that time. Think of a
 
4028
         better way..*/
 
4029
        g_usleep (1000);
 
4030
        g_hash_table_remove (is->uid_eflags, uid);
 
4031
 
 
4032
        return stream;
 
4033
}
 
4034
 
 
4035
CamelStream *
 
4036
camel_imapx_server_get_message(CamelIMAPXServer *is, CamelFolder *folder, const gchar *uid, CamelException *ex)
 
4037
{
 
4038
        CamelStream *stream;
 
4039
        CamelOperation *op = camel_operation_registered ();
 
4040
 
 
4041
        stream = imapx_server_get_message(is, folder, op, uid, IMAPX_PRIORITY_GET_MESSAGE, ex);
 
4042
        if (op)
 
4043
                camel_operation_unref (op);
 
4044
 
 
4045
        return stream;
 
4046
}
 
4047
 
 
4048
void
 
4049
camel_imapx_server_sync_message (CamelIMAPXServer *is, CamelFolder *folder, const gchar *uid, CamelException *ex)
 
4050
{
 
4051
        gchar *cache_file = NULL;
 
4052
        CamelIMAPXFolder *ifolder = (CamelIMAPXFolder *) folder;
 
4053
        CamelStream *stream;
 
4054
 
 
4055
        cache_file = camel_data_cache_get_filename  (ifolder->cache, "cur", uid, NULL);
 
4056
        if (g_file_test (cache_file, G_FILE_TEST_EXISTS)) {
 
4057
                g_free (cache_file);
 
4058
                return;
 
4059
        }
 
4060
 
 
4061
        stream = imapx_server_get_message (is, folder, NULL, uid, IMAPX_PRIORITY_SYNC_MESSAGE, ex);
 
4062
        if (stream)
 
4063
                camel_object_unref(stream);
 
4064
}
 
4065
 
 
4066
void
 
4067
camel_imapx_server_copy_message (CamelIMAPXServer *is, CamelFolder *source, CamelFolder *dest, GPtrArray *uids, gboolean delete_originals, CamelException *ex)
 
4068
{
 
4069
        CamelIMAPXJob *job;
 
4070
 
 
4071
        job = g_malloc0(sizeof(*job));
 
4072
        job->pri = IMAPX_PRIORITY_APPEND_MESSAGE;
 
4073
        job->type = IMAPX_JOB_COPY_MESSAGE;
 
4074
        job->start = imapx_job_copy_messages_start;
 
4075
        job->folder = source;
 
4076
        job->u.copy_messages.dest = dest;
 
4077
        job->u.copy_messages.uids = uids;
 
4078
        job->u.copy_messages.delete_originals = delete_originals;
 
4079
        job->ex = ex;
 
4080
 
 
4081
        camel_object_ref(source);
 
4082
        camel_object_ref (dest);
 
4083
 
 
4084
        if (imapx_register_job (is, job))
 
4085
                imapx_run_job (is, job);
 
4086
}
 
4087
 
 
4088
void
 
4089
camel_imapx_server_append_message(CamelIMAPXServer *is, CamelFolder *folder, CamelMimeMessage *message, const CamelMessageInfo *mi, CamelException *ex)
 
4090
{
 
4091
        gchar *uid = NULL, *tmp = NULL;
 
4092
        CamelStream *stream, *filter;
 
4093
        CamelIMAPXFolder *ifolder = (CamelIMAPXFolder *) folder;
 
4094
        CamelMimeFilter *canon;
 
4095
        CamelIMAPXJob *job;
 
4096
        CamelMessageInfo *info;
 
4097
        gint res;
 
4098
 
 
4099
        /* Append just assumes we have no/a dodgy connection.  We dump stuff into the 'new'
 
4100
           directory, and let the summary know it's there.  Then we fire off a no-reply
 
4101
           job which will asynchronously upload the message at some point in the future,
 
4102
           and fix up the summary to match */
 
4103
 
 
4104
        /* chen cleanup this later */
 
4105
        uid = imapx_get_temp_uid ();
 
4106
        stream = camel_data_cache_add (ifolder->cache, "new", uid, NULL);
 
4107
        if (stream == NULL) {
 
4108
                camel_exception_setv(ex, 2, "Cannot create spool file: %s", g_strerror((gint) errno));
 
4109
                return;
 
4110
        }
 
4111
 
 
4112
        filter = (CamelStream *)camel_stream_filter_new_with_stream(stream);
 
4113
        camel_object_unref(stream);
 
4114
        canon = camel_mime_filter_canon_new(CAMEL_MIME_FILTER_CANON_CRLF);
 
4115
        camel_stream_filter_add((CamelStreamFilter *)filter, canon);
 
4116
        res = camel_data_wrapper_write_to_stream((CamelDataWrapper *)message, filter);
 
4117
        camel_object_unref(canon);
 
4118
        camel_object_unref(filter);
 
4119
 
 
4120
        if (res == -1) {
 
4121
                camel_exception_setv(ex, 2, "Cannot create spool file: %s", g_strerror(errno));
 
4122
                camel_data_cache_remove (ifolder->cache, "new", uid, NULL);
 
4123
                return;
 
4124
        }
 
4125
 
 
4126
        tmp = camel_data_cache_get_filename (ifolder->cache, "new", uid, NULL);
 
4127
        info = camel_folder_summary_info_new_from_message((CamelFolderSummary *)folder->summary, message, NULL);
 
4128
        info->uid = uid;
 
4129
        ((CamelMessageInfoBase *) info)->flags = ((CamelMessageInfoBase *) mi)->flags;
 
4130
        uid = NULL;
 
4131
 
 
4132
        /* So, we actually just want to let the server loop that
 
4133
           messages need appending, i think.  This is so the same
 
4134
           mechanism is used for normal uploading as well as
 
4135
           offline re-syncing when we go back online */
 
4136
 
 
4137
        job = g_malloc0(sizeof(*job));
 
4138
        job->pri = IMAPX_PRIORITY_APPEND_MESSAGE;
 
4139
        job->type = IMAPX_JOB_APPEND_MESSAGE;
 
4140
        job->noreply = 1;
 
4141
        job->start = imapx_job_append_message_start;
 
4142
        job->folder = folder;
 
4143
        camel_object_ref(folder);
 
4144
        job->u.append_message.info = info;
 
4145
        job->u.append_message.path = tmp;
 
4146
 
 
4147
        if (imapx_register_job (is, job))
 
4148
                imapx_run_job(is, job);
 
4149
        return;
 
4150
}
 
4151
 
 
4152
void
 
4153
camel_imapx_server_noop (CamelIMAPXServer *is, CamelFolder *folder, CamelException *ex)
 
4154
{
 
4155
        CamelIMAPXJob *job;
 
4156
 
 
4157
        job = g_malloc0(sizeof(*job));
 
4158
        job->type = IMAPX_JOB_NOOP;
 
4159
        job->start = imapx_job_noop_start;
 
4160
        job->folder = folder;
 
4161
        job->ex = ex;
 
4162
        job->pri = IMAPX_PRIORITY_NOOP;
 
4163
 
 
4164
        if (imapx_register_job (is, job))
 
4165
                imapx_run_job(is, job);
 
4166
 
 
4167
        g_free(job);
 
4168
}
 
4169
 
 
4170
void
 
4171
camel_imapx_server_refresh_info (CamelIMAPXServer *is, CamelFolder *folder, CamelException *ex)
 
4172
{
 
4173
        CamelIMAPXJob *job;
 
4174
        gboolean registered = TRUE;
 
4175
 
 
4176
        QUEUE_LOCK (is);
 
4177
 
 
4178
        if (imapx_is_job_in_queue (is, folder->full_name, IMAPX_JOB_REFRESH_INFO, NULL)) {
 
4179
                QUEUE_UNLOCK (is);
 
4180
                return;
 
4181
        }
 
4182
 
 
4183
        job = g_malloc0(sizeof(*job));
 
4184
        job->type = IMAPX_JOB_REFRESH_INFO;
 
4185
        job->start = imapx_job_refresh_info_start;
 
4186
        job->folder = folder;
 
4187
        job->ex = ex;
 
4188
        job->op = camel_operation_registered ();
 
4189
        job->u.refresh_info.changes = camel_folder_change_info_new();
 
4190
        job->pri = IMAPX_PRIORITY_REFRESH_INFO;
 
4191
 
 
4192
        if (g_ascii_strcasecmp(folder->full_name, "INBOX") == 0)
 
4193
                job->pri += 10;
 
4194
 
 
4195
        registered = imapx_register_job (is, job);
 
4196
 
 
4197
        QUEUE_UNLOCK (is);
 
4198
 
 
4199
        if (registered) {
 
4200
                imapx_run_job (is, job);
 
4201
 
 
4202
                if (camel_folder_change_info_changed(job->u.refresh_info.changes))
 
4203
                        camel_object_trigger_event(folder, "folder_changed", job->u.refresh_info.changes);
 
4204
        }
 
4205
 
 
4206
        camel_folder_change_info_free(job->u.refresh_info.changes);
 
4207
 
 
4208
        if (job->op)
 
4209
                camel_operation_unref (job->op);
 
4210
        g_free(job);
 
4211
}
 
4212
 
 
4213
static void
 
4214
imapx_sync_free_user(GArray *user_set)
 
4215
{
 
4216
        gint i;
 
4217
 
 
4218
        if (user_set == NULL)
 
4219
                return;
 
4220
 
 
4221
        for (i=0;i<user_set->len;i++) {
 
4222
                GPtrArray *infos = g_array_index (user_set, struct _imapx_flag_change, i).infos;
 
4223
                gint j;
 
4224
 
 
4225
                for (j = 0; j < infos->len; j++) {
 
4226
                        CamelMessageInfo *info = g_ptr_array_index (infos, j);
 
4227
                        camel_message_info_free (info);
 
4228
                }
 
4229
 
 
4230
                g_ptr_array_free(infos, TRUE);
 
4231
        }
 
4232
        g_array_free(user_set, TRUE);
 
4233
}
 
4234
 
 
4235
static void
 
4236
imapx_server_sync_changes(CamelIMAPXServer *is, CamelFolder *folder, gint pri, CamelException *ex)
 
4237
{
 
4238
        guint i, on_orset, off_orset;
 
4239
        GPtrArray *uids;
 
4240
        GArray *on_user = NULL, *off_user = NULL;
 
4241
        CamelIMAPXMessageInfo *info;
 
4242
        CamelIMAPXJob *job;
 
4243
        gboolean registered;
 
4244
 
 
4245
        /* We calculate two masks, a mask of all flags which have been
 
4246
           turned off and a mask of all flags which have been turned
 
4247
           on. If either of these aren't 0, then we have work to do,
 
4248
           and we fire off a job to do it.
 
4249
 
 
4250
           User flags are a bit more tricky, we rely on the user
 
4251
           flags being sorted, and then we create a bunch of lists;
 
4252
           one for each flag being turned off, including each
 
4253
           info being turned off, and one for each flag being turned on.
 
4254
        */
 
4255
        uids = camel_folder_summary_get_changed (folder->summary);
 
4256
 
 
4257
        if (uids->len == 0) {
 
4258
                g_ptr_array_free (uids, TRUE);
 
4259
                return;
 
4260
        }
 
4261
 
 
4262
        off_orset = on_orset = 0;
 
4263
        for (i=0; i < uids->len; i++) {
 
4264
                guint32 flags, sflags;
 
4265
                CamelFlag *uflags, *suflags;
 
4266
                guint j = 0;
 
4267
 
 
4268
                info = (CamelIMAPXMessageInfo *) camel_folder_summary_uid (folder->summary, uids->pdata[i]);
 
4269
 
 
4270
                if (!info)
 
4271
                        continue;
 
4272
 
 
4273
                if (!(info->info.flags & CAMEL_MESSAGE_FOLDER_FLAGGED)) {
 
4274
                        camel_message_info_free (info);
 
4275
                        continue;
 
4276
                }
 
4277
 
 
4278
                flags = ((CamelMessageInfoBase *)info)->flags & CAMEL_IMAPX_SERVER_FLAGS;
 
4279
                sflags = info->server_flags & CAMEL_IMAPX_SERVER_FLAGS;
 
4280
                if (flags != sflags) {
 
4281
                        off_orset |= ( flags ^ sflags ) & ~flags;
 
4282
                        on_orset |= (flags ^ sflags) & flags;
 
4283
                }
 
4284
 
 
4285
                uflags = ((CamelMessageInfoBase *)info)->user_flags;
 
4286
                suflags = info->server_user_flags;
 
4287
                while (uflags || suflags) {
 
4288
                        gint res;
 
4289
 
 
4290
                        if (uflags) {
 
4291
                                if (suflags)
 
4292
                                        res = strcmp(uflags->name, suflags->name);
 
4293
                                else if (uflags->name && *uflags->name)
 
4294
                                        res = -1;
 
4295
                                else {
 
4296
                                        uflags = uflags->next;
 
4297
                                        continue;
 
4298
                                }
 
4299
                        } else {
 
4300
                                res = 1;
 
4301
                        }
 
4302
 
 
4303
                        if (res == 0) {
 
4304
                                uflags = uflags->next;
 
4305
                                suflags = suflags->next;
 
4306
                        } else {
 
4307
                                GArray *user_set;
 
4308
                                CamelFlag *user_flag;
 
4309
                                struct _imapx_flag_change *change = NULL, add;
 
4310
 
 
4311
                                if (res < 0) {
 
4312
                                        if (on_user == NULL)
 
4313
                                                on_user = g_array_new(FALSE, FALSE, sizeof(struct _imapx_flag_change));
 
4314
                                        user_set = on_user;
 
4315
                                        user_flag = uflags;
 
4316
                                        uflags = uflags->next;
 
4317
                                } else {
 
4318
                                        if (off_user == NULL)
 
4319
                                                off_user = g_array_new(FALSE, FALSE, sizeof(struct _imapx_flag_change));
 
4320
                                        user_set = off_user;
 
4321
                                        user_flag = suflags;
 
4322
                                        suflags = suflags->next;
 
4323
                                }
 
4324
 
 
4325
                                /* Could sort this and binary search */
 
4326
                                for (j = 0; j < user_set->len; j++) {
 
4327
                                        change = &g_array_index(user_set, struct _imapx_flag_change, j);
 
4328
                                        if (strcmp(change->name, user_flag->name) == 0)
 
4329
                                                goto found;
 
4330
                                }
 
4331
                                add.name = g_strdup(user_flag->name);
 
4332
                                add.infos = g_ptr_array_new();
 
4333
                                g_array_append_val(user_set, add);
 
4334
                                change = &add;
 
4335
                        found:
 
4336
                                camel_message_info_ref (info);
 
4337
                                g_ptr_array_add(change->infos, info);
 
4338
                        }
 
4339
                }
 
4340
                camel_message_info_free (info);
 
4341
        }
 
4342
 
 
4343
        if ((on_orset|off_orset) == 0 && on_user == NULL && off_user == NULL)
 
4344
                return;
 
4345
 
 
4346
        /* TODO above code should go into changes_start */
 
4347
 
 
4348
        QUEUE_LOCK (is);
 
4349
 
 
4350
        if ((job = imapx_is_job_in_queue (is, folder->full_name, IMAPX_JOB_SYNC_CHANGES, NULL))) {
 
4351
                if (pri > job->pri)
 
4352
                        job->pri = pri;
 
4353
 
 
4354
                QUEUE_UNLOCK (is);
 
4355
                goto done;
 
4356
        }
 
4357
 
 
4358
        job = g_malloc0(sizeof(*job));
 
4359
        job->type = IMAPX_JOB_SYNC_CHANGES;
 
4360
        job->start = imapx_job_sync_changes_start;
 
4361
        job->pri = pri;
 
4362
        job->folder = folder;
 
4363
        job->ex = ex;
 
4364
        job->u.sync_changes.changed_uids = uids;
 
4365
        job->u.sync_changes.on_set = on_orset;
 
4366
        job->u.sync_changes.off_set = off_orset;
 
4367
        job->u.sync_changes.on_user = on_user;
 
4368
        job->u.sync_changes.off_user = off_user;
 
4369
 
 
4370
        registered = imapx_register_job (is, job);
 
4371
 
 
4372
        QUEUE_UNLOCK (is);
 
4373
 
 
4374
        if (registered)
 
4375
                imapx_run_job(is, job);
 
4376
 
 
4377
        g_free(job);
 
4378
 
 
4379
done:
 
4380
        imapx_sync_free_user(on_user);
 
4381
        imapx_sync_free_user(off_user);
 
4382
 
 
4383
        camel_folder_free_uids (folder, uids);
 
4384
}
 
4385
 
 
4386
void
 
4387
camel_imapx_server_sync_changes(CamelIMAPXServer *is, CamelFolder *folder, CamelException *ex)
 
4388
{
 
4389
        imapx_server_sync_changes (is, folder, IMAPX_PRIORITY_SYNC_CHANGES, ex);
 
4390
}
 
4391
 
 
4392
/* expunge-uids? */
 
4393
void
 
4394
camel_imapx_server_expunge(CamelIMAPXServer *is, CamelFolder *folder, CamelException *ex)
 
4395
{
 
4396
        CamelIMAPXJob *job;
 
4397
        gboolean registered;
 
4398
 
 
4399
        /* Do we really care to wait for this one to finish? */
 
4400
        QUEUE_LOCK (is);
 
4401
 
 
4402
        if (imapx_is_job_in_queue (is, folder->full_name, IMAPX_JOB_EXPUNGE, NULL)) {
 
4403
                QUEUE_UNLOCK (is);
 
4404
                return;
 
4405
        }
 
4406
 
 
4407
        job = g_malloc0(sizeof(*job));
 
4408
        job->type = IMAPX_JOB_EXPUNGE;
 
4409
        job->start = imapx_job_expunge_start;
 
4410
        job->pri = IMAPX_PRIORITY_EXPUNGE;
 
4411
        job->folder = folder;
 
4412
        job->ex = ex;
 
4413
 
 
4414
        registered = imapx_register_job (is, job);
 
4415
 
 
4416
        QUEUE_UNLOCK (is);
 
4417
 
 
4418
        if (registered)
 
4419
                imapx_run_job(is, job);
 
4420
 
 
4421
        g_free(job);
 
4422
}
 
4423
 
 
4424
static guint
 
4425
imapx_name_hash(gconstpointer key)
 
4426
{
 
4427
        if (g_ascii_strcasecmp(key, "INBOX") == 0)
 
4428
                return g_str_hash("INBOX");
 
4429
        else
 
4430
                return g_str_hash(key);
 
4431
}
 
4432
 
 
4433
static gint
 
4434
imapx_name_equal(gconstpointer a, gconstpointer b)
 
4435
{
 
4436
        gconstpointer aname = a, bname = b;
 
4437
 
 
4438
        if (g_ascii_strcasecmp(a, "INBOX") == 0)
 
4439
                aname = "INBOX";
 
4440
        if (g_ascii_strcasecmp(b, "INBOX") == 0)
 
4441
                bname = "INBOX";
 
4442
        return g_str_equal(aname, bname);
 
4443
}
 
4444
 
 
4445
static void
 
4446
imapx_list_flatten(gpointer k, gpointer v, gpointer d)
 
4447
{
 
4448
        GPtrArray *folders = d;
 
4449
 
 
4450
        g_ptr_array_add(folders, v);
 
4451
}
 
4452
 
 
4453
static gint
 
4454
imapx_list_cmp(gconstpointer ap, gconstpointer bp)
 
4455
{
 
4456
        struct _list_info *a = ((struct _list_info **)ap)[0];
 
4457
        struct _list_info *b = ((struct _list_info **)bp)[0];
 
4458
 
 
4459
        return strcmp(a->name, b->name);
 
4460
}
 
4461
 
 
4462
GPtrArray *
 
4463
camel_imapx_server_list(CamelIMAPXServer *is, const gchar *top, guint32 flags, CamelException *ex)
 
4464
{
 
4465
        CamelIMAPXJob *job;
 
4466
        GPtrArray *folders = NULL;
 
4467
        gchar *encoded_name;
 
4468
 
 
4469
        encoded_name = camel_utf8_utf7 (top);
 
4470
 
 
4471
        job = g_malloc0(sizeof(*job));
 
4472
        job->type = IMAPX_JOB_LIST;
 
4473
        job->start = imapx_job_list_start;
 
4474
        job->pri = IMAPX_PRIORITY_LIST;
 
4475
        job->ex = ex;
 
4476
        job->u.list.flags = flags;
 
4477
        job->u.list.folders = g_hash_table_new(imapx_name_hash, imapx_name_equal);
 
4478
        job->u.list.pattern = g_alloca(strlen(encoded_name)+5);
 
4479
        if (flags & CAMEL_STORE_FOLDER_INFO_RECURSIVE)
 
4480
                sprintf(job->u.list.pattern, "%s*", encoded_name);
 
4481
        else
 
4482
                sprintf(job->u.list.pattern, "%s", encoded_name);
 
4483
 
 
4484
        /* sync operation which is triggered by user */
 
4485
        if (flags & CAMEL_STORE_FOLDER_INFO_SUBSCRIPTION_LIST)
 
4486
                job->pri += 300;
 
4487
 
 
4488
        if (imapx_register_job (is, job)) {
 
4489
                imapx_run_job (is, job);
 
4490
 
 
4491
                folders = g_ptr_array_new();
 
4492
                g_hash_table_foreach(job->u.list.folders, imapx_list_flatten, folders);
 
4493
                qsort(folders->pdata, folders->len, sizeof(folders->pdata[0]), imapx_list_cmp);
 
4494
        }
 
4495
 
 
4496
        g_hash_table_destroy(job->u.list.folders);
 
4497
        g_free (encoded_name);
 
4498
        g_free(job);
 
4499
 
 
4500
        return folders;
 
4501
}
 
4502
 
 
4503
void
 
4504
camel_imapx_server_manage_subscription (CamelIMAPXServer *is, const gchar *folder_name, gboolean subscribe, CamelException *ex)
 
4505
{
 
4506
        CamelIMAPXJob *job;
 
4507
        
 
4508
        job = g_malloc0(sizeof(*job));
 
4509
        job->type = IMAPX_JOB_MANAGE_SUBSCRIPTION;
 
4510
        job->start = imapx_job_manage_subscription_start;
 
4511
        job->pri = IMAPX_PRIORITY_MANAGE_SUBSCRIPTION;
 
4512
        job->ex = ex;
 
4513
        job->u.manage_subscriptions.subscribe = subscribe;
 
4514
        job->u.manage_subscriptions.folder_name = folder_name;
 
4515
        
 
4516
        if (imapx_register_job (is, job))
 
4517
                imapx_run_job (is, job);
 
4518
 
 
4519
        g_free (job);
 
4520
}
 
4521
 
 
4522
void
 
4523
camel_imapx_server_create_folder (CamelIMAPXServer *is, const gchar *folder_name, CamelException *ex)
 
4524
{
 
4525
        CamelIMAPXJob *job;
 
4526
        
 
4527
        job = g_malloc0(sizeof(*job));
 
4528
        job->type = IMAPX_JOB_CREATE_FOLDER;
 
4529
        job->start = imapx_job_create_folder_start;
 
4530
        job->pri = IMAPX_PRIORITY_CREATE_FOLDER;
 
4531
        job->ex = ex;
 
4532
        job->u.folder_name = folder_name;
 
4533
 
 
4534
        if (imapx_register_job (is, job))
 
4535
                imapx_run_job (is, job);
 
4536
 
 
4537
        g_free (job);
 
4538
}
 
4539
 
 
4540
void
 
4541
camel_imapx_server_delete_folder (CamelIMAPXServer *is, const gchar *folder_name, CamelException *ex)
 
4542
{
 
4543
        CamelIMAPXJob *job;
 
4544
        
 
4545
        job = g_malloc0(sizeof(*job));
 
4546
        job->type = IMAPX_JOB_DELETE_FOLDER;
 
4547
        job->start = imapx_job_delete_folder_start;
 
4548
        job->pri = IMAPX_PRIORITY_DELETE_FOLDER;
 
4549
        job->ex = ex;
 
4550
        job->u.folder_name = folder_name;
 
4551
 
 
4552
        if (imapx_register_job (is, job))
 
4553
                imapx_run_job (is, job);
 
4554
 
 
4555
        g_free (job);
 
4556
}
 
4557
 
 
4558
void
 
4559
camel_imapx_server_rename_folder (CamelIMAPXServer *is, const gchar *old_name, const gchar *new_name, CamelException *ex)
 
4560
{
 
4561
        CamelIMAPXJob *job;
 
4562
        
 
4563
        job = g_malloc0(sizeof(*job));
 
4564
        job->type = IMAPX_JOB_RENAME_FOLDER;
 
4565
        job->start = imapx_job_rename_folder_start;
 
4566
        job->pri = IMAPX_PRIORITY_RENAME_FOLDER;
 
4567
        job->ex = ex;
 
4568
        job->u.rename_folder.ofolder_name = old_name;
 
4569
        job->u.rename_folder.nfolder_name = new_name;
 
4570
 
 
4571
        if (imapx_register_job (is, job))
 
4572
                imapx_run_job (is, job);
 
4573
 
 
4574
        g_free (job);
 
4575
 
 
4576
}