9
#include <glib/gstdio.h>
11
// fixme, use own type funcs
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>
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>
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"
55
#define CFS_CLASS(x) ((CamelFolderSummaryClass *)((CamelObject *)x)->klass)
57
#define CIF(x) ((CamelIMAPXFolder *)x)
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))
62
/* All comms with server go here */
64
/* Try pipelining fetch requests, 'in bits' */
65
#define MULTI_SIZE (20480)
67
/* How many outstanding commands do we allow before we just queue them? */
68
#define MAX_COMMANDS (10)
70
/* How many message headers to fetch at a time update summary for new messages*/
71
#define BATCH_FETCH_COUNT 500
73
#define MAX_COMMAND_LEN 1000
75
extern gint camel_application_is_exiting;
77
struct _uidset_state {
78
struct _CamelIMAPXEngine *ie;
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);
92
typedef struct _CamelIMAPXCommandPart CamelIMAPXCommandPart;
93
typedef struct _CamelIMAPXCommand CamelIMAPXCommand;
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;
106
struct _CamelIMAPXCommandPart {
107
struct _CamelIMAPXCommandPart *next;
108
struct _CamelIMAPXCommandPart *prev;
110
struct _CamelIMAPXCommand *parent;
115
camel_imapx_command_part_t type;
121
typedef gint (*CamelIMAPXEngineFunc)(struct _CamelIMAPXServer *engine, guint32 id, gpointer data);
122
typedef void (*CamelIMAPXCommandFunc)(struct _CamelIMAPXServer *engine, struct _CamelIMAPXCommand *);
124
struct _CamelIMAPXCommand {
125
struct _CamelIMAPXCommand *next, *prev;
129
const gchar *name; /* command name/type (e.g. FETCH) */
131
gchar *select; /* folder to select */
133
struct _status_info *status; /* status for command, indicates it is complete if != NULL */
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 */
141
struct _CamelStreamMem *mem; /* for building the part TOOD: just use a GString? */
143
CamelIMAPXCommandPart *current;
145
/* used for running some commands syncronously */
148
/* responsible for free'ing the command */
149
CamelIMAPXCommandFunc complete;
150
struct _CamelIMAPXJob *job;
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);
159
/* states for the connection? */
167
struct _refresh_info {
170
guint32 server_flags;
171
CamelFlag *server_user_flags;
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,
191
/* Operations on the store (folder_tree) will have highest priority as we know for sure they are sync
192
and user triggered. */
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
211
struct _imapx_flag_change {
216
typedef struct _CamelIMAPXJob CamelIMAPXJob;
217
struct _CamelIMAPXJob {
222
void (*start)(CamelIMAPXServer *is, struct _CamelIMAPXJob *job);
225
//CamelOperation *op;
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 */
237
/* in: uid requested */
239
/* in/out: message content stream output */
241
/* working variables */
246
gboolean use_multi_fetch;
249
/* array of refresh info's */
251
/* used for biulding uidset stuff */
254
struct _uidset_state uidset;
255
/* changes during refresh */
256
CamelFolderChangeInfo *changes;
259
GPtrArray *changed_uids;
262
GArray *on_user; /* imapx_flag_change */
267
CamelMessageInfo *info;
272
gboolean delete_originals;
275
struct _uidset_state uidset;
284
const gchar *folder_name;
286
} manage_subscriptions;
289
const gchar *ofolder_name;
290
const gchar *nfolder_name;
293
const gchar *folder_name;
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);
305
typedef struct _CamelIMAPXIdle CamelIMAPXIdle;
306
struct _CamelIMAPXIdle {
308
EFlag *idle_start_watch;
309
GThread *idle_thread;
311
gboolean idle_issue_done;
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);
328
USE_SSL_WHEN_POSSIBLE
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)
334
static void imapx_select(CamelIMAPXServer *is, CamelFolder *folder, gboolean force, CamelException *ex);
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)
342
imapx_uidset_init(struct _uidset_state *ss, gint total, gint limit)
353
imapx_uidset_done(struct _uidset_state *ss, CamelIMAPXCommand *ic)
357
if (ss->last != 0 && ss->last != ss->start) {
358
camel_imapx_command_add(ic, ":%d", ss->last);
372
imapx_uidset_add(struct _uidset_state *ss, CamelIMAPXCommand *ic, const gchar *uid)
376
uidn = strtoul(uid, NULL, 10);
382
e(printf("uidset add '%s'\n", uid));
385
e(printf(" start\n"));
386
camel_imapx_command_add(ic, "%d", uidn);
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);
396
e(printf(" :range\n"));
397
camel_imapx_command_add(ic, ":%d,%d", ss->last, uidn);
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);
417
imapx_command_add_part(CamelIMAPXCommand *ic, camel_imapx_command_part_t type, gpointer o)
419
CamelIMAPXCommandPart *cp;
420
CamelStreamNull *null;
423
/* TODO: literal+? */
425
switch (type & CAMEL_IMAPX_COMMAND_MASK) {
426
case CAMEL_IMAPX_COMMAND_DATAWRAPPER:
427
case CAMEL_IMAPX_COMMAND_STREAM: {
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);
435
camel_stream_reset((CamelStream *)ob);
436
camel_stream_write_to_stream((CamelStream *)ob, (CamelStream *)null);
437
camel_stream_reset((CamelStream *)ob);
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);
446
case CAMEL_IMAPX_COMMAND_AUTH: {
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;
456
case CAMEL_IMAPX_COMMAND_FILE: {
460
if (stat(path, &st) == 0) {
462
ob_size = st.st_size;
466
camel_stream_printf((CamelStream *)ic->mem, "{%u}", ob_size);
467
type |= CAMEL_IMAPX_COMMAND_CONTINUATION;
470
case CAMEL_IMAPX_COMMAND_STRING:
473
camel_stream_printf((CamelStream *)ic->mem, "{%u}", ob_size);
474
type |= CAMEL_IMAPX_COMMAND_CONTINUATION;
480
cp = g_malloc0(sizeof(*cp));
482
cp->ob_size = ob_size;
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;
489
camel_stream_reset((CamelStream *)ic->mem);
490
/* FIXME: hackish? */
491
g_byte_array_set_size(ic->mem->buffer, 0);
493
camel_dlist_addtail(&ic->parts, (CamelDListNode *)cp);
497
imapx_command_addv(CamelIMAPXCommand *ic, const gchar *fmt, va_list ap)
499
const gchar *p, *ps, *start;
518
gchar *fname = NULL, *encoded = NULL;
519
CamelException ex = CAMEL_EXCEPTION_INITIALISER;
521
c(printf("adding command, fmt = '%s'\n", fmt));
525
while (( c = *p++ )) {
529
camel_stream_write((CamelStream *)ic->mem, ps, p-ps);
533
camel_stream_write((CamelStream *)ic->mem, ps, p-ps-1);
552
// FIXME: ascii isdigit
554
width = width * 10 + (c-'0');
557
} while ((c = *p++));
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);
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);
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);
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);
584
case 't': /* token */
585
s = va_arg(ap, gchar *);
586
camel_stream_write((CamelStream *)ic->mem, s, strlen(s));
588
case 's': /* simple string */
589
s = va_arg(ap, gchar *);
590
c(printf("got string '%s'\n", s));
592
guchar mask = imapx_is_mask(s);
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);
601
while (*s && imapx_is_quoted_char(*s))
603
camel_stream_write((CamelStream *)ic->mem, start, s-start);
605
camel_stream_write((CamelStream *)ic->mem, "\\", 1);
606
camel_stream_write((CamelStream *)ic->mem, s, 1);
610
camel_stream_write((CamelStream *)ic->mem, "\"", 1);
612
imapx_command_add_part(ic, CAMEL_IMAPX_COMMAND_STRING, s);
615
camel_stream_write((CamelStream *)ic->mem, "\"\"", 2);
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);
623
encoded = camel_utf8_utf7(fname);
626
encoded = camel_utf8_utf7 (folder->full_name);
628
camel_stream_printf((CamelStream *)ic->mem, "\"%s\"", encoded?encoded:"");
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);
638
d = va_arg(ap, gint);
640
camel_stream_write((CamelStream *)ic->mem, &ch, 1);
642
case 'd': /* int/unsigned */
645
l = va_arg(ap, glong);
646
c(printf("got glong '%d'\n", (gint)l));
647
memcpy(buffer, start, p-start);
649
camel_stream_printf((CamelStream *)ic->mem, buffer, l);
651
d = va_arg(ap, gint);
652
c(printf("got gint '%d'\n", d));
653
memcpy(buffer, start, p-start);
655
camel_stream_printf((CamelStream *)ic->mem, buffer, d);
663
case '\\': /* only for \\ really, we dont support \n\r etc at all */
667
camel_stream_write((CamelStream *)ic->mem, ps, p-ps);
674
camel_stream_write((CamelStream *)ic->mem, ps, p-ps-1);
678
camel_imapx_command_new(const gchar *name, const gchar *select, const gchar *fmt, ...)
680
CamelIMAPXCommand *ic;
684
ic = g_malloc0(sizeof(*ic));
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 ();
694
imapx_command_addv(ic, fmt, ap);
702
camel_imapx_command_add(CamelIMAPXCommand *ic, const gchar *fmt, ...)
706
g_assert(ic->mem); /* gets reset on queue */
710
imapx_command_addv(ic, fmt, ap);
716
camel_imapx_command_free(CamelIMAPXCommand *ic)
718
CamelIMAPXCommandPart *cp;
724
camel_object_unref((CamelObject *)ic->mem);
725
imapx_free_status(ic->status);
728
while ((cp = ((CamelIMAPXCommandPart *)camel_dlist_remhead(&ic->parts)))) {
731
switch (cp->type & CAMEL_IMAPX_COMMAND_MASK) {
732
case CAMEL_IMAPX_COMMAND_FILE:
733
case CAMEL_IMAPX_COMMAND_STRING:
737
camel_object_unref(cp->ob);
743
camel_exception_free (ic->ex);
748
camel_imapx_command_close(CamelIMAPXCommand *ic)
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);
755
camel_object_unref((CamelObject *)ic->mem);
760
/* Must hold QUEUE_LOCK */
762
imapx_command_start (CamelIMAPXServer *imap, CamelIMAPXCommand *ic)
764
CamelIMAPXCommandPart *cp;
766
camel_imapx_command_close(ic);
767
cp = (CamelIMAPXCommandPart *)ic->parts.head;
771
/* TODO: If we support literal+ we should be able to write the whole command out
772
at this point .... >here< */
774
if (cp->type & CAMEL_IMAPX_COMMAND_CONTINUATION)
777
camel_dlist_addtail(&imap->active, (CamelDListNode *)ic);
779
g_static_rec_mutex_lock (&imap->ostream_lock);
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);
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);
792
g_static_rec_mutex_unlock (&imap->ostream_lock);
797
/* See if we can start another task yet.
799
If we're waiting for a literal, we cannot proceed.
801
If we're about to change the folder we're
802
looking at from user-direction, we dont proceed.
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
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.
813
must have QUEUE lock */
816
imapx_command_start_next(CamelIMAPXServer *is, CamelException *ex)
818
CamelIMAPXCommand *ic, *nc;
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));
827
/* TODO prolly start the store operations which do not require any folder to be selected */
831
if (imapx_idle_supported (is) && is->state == IMAPX_SELECTED) {
832
gboolean empty = imapx_is_command_queue_empty (is);
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"));
838
} else if (empty && !imapx_in_idle (is)) {
839
imapx_start_idle (is);
840
c(printf ("starting idle \n"));
845
ic = (CamelIMAPXCommand *)is->queue.head;
848
c(printf("* no, no jobs\n"));
852
/* See if any queued jobs on this select first */
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));
860
if (count > MAX_COMMANDS) {
861
c(printf("** too many jobs busy, waiting for results for now\n"));
866
c(printf("-- Checking job queue\n"));
868
ic = (CamelIMAPXCommand *)is->queue.head;
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));
875
camel_dlist_remove((CamelDListNode *)ic);
876
imapx_command_start(is, ic);
887
ic = (CamelIMAPXCommand *)is->queue.head;
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);
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));
902
camel_dlist_remove((CamelDListNode *)ic);
903
if (!imapx_command_start(is, ic)) {
905
ic->complete (is, ic);
917
imapx_is_command_queue_empty (CamelIMAPXServer *is)
919
gboolean ret = FALSE;
921
if (camel_dlist_empty (&is->queue) && camel_dlist_empty (&is->active))
928
imapx_command_queue(CamelIMAPXServer *is, CamelIMAPXCommand *ic)
930
CamelIMAPXCommand *scan;
932
/* We enqueue in priority order, new messages have
933
higher priority than older messages with the same priority */
935
camel_imapx_command_close(ic);
937
c(printf("enqueue job '%.*s'\n", ((CamelIMAPXCommandPart *)ic->parts.head)->data_size, ((CamelIMAPXCommandPart *)ic->parts.head)->data));
941
scan = (CamelIMAPXCommand *)is->queue.head;
942
if (scan->next == NULL)
943
camel_dlist_addtail(&is->queue, (CamelDListNode *)ic);
946
if (ic->pri >= scan->pri)
951
scan->prev->next = ic;
953
ic->prev = scan->prev;
957
imapx_command_start_next (is, NULL);
964
/* Must have QUEUE lock */
965
static CamelIMAPXCommand *
966
imapx_find_command_tag(CamelIMAPXServer *imap, guint tag)
968
CamelIMAPXCommand *ic = NULL;
973
if (ic && ic->tag == tag)
976
for (ic = (CamelIMAPXCommand *)imap->active.head;ic->next;ic=ic->next)
986
imapx_job_matches (const gchar *folder_name, CamelIMAPXJob *job, guint32 type, const gchar *uid)
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)
994
case IMAPX_JOB_FETCH_NEW_MESSAGES:
995
case IMAPX_JOB_REFRESH_INFO:
996
case IMAPX_JOB_SYNC_CHANGES:
997
case IMAPX_JOB_EXPUNGE:
999
&& strcmp(job->folder->full_name, folder_name) == 0)
1002
case IMAPX_JOB_LIST:
1009
/* Must not have QUEUE lock */
1010
static CamelIMAPXJob *
1011
imapx_match_active_job (CamelIMAPXServer *is, guint32 type, const gchar *uid)
1013
CamelIMAPXJob *job = NULL;
1014
CamelIMAPXCommand *ic;
1018
for (ic = (CamelIMAPXCommand *)is->active.head;ic->next;ic=ic->next) {
1020
if (!job || !(job->type & type))
1023
if (imapx_job_matches (is->select, job, type, uid))
1032
static CamelIMAPXJob *
1033
imapx_is_job_in_queue (CamelIMAPXServer *is, const gchar *folder_name, guint32 type, const gchar *uid)
1035
CamelDListNode *node;
1036
CamelIMAPXJob *job = NULL;
1037
gboolean found = FALSE;
1041
for (node = is->jobs.head;node->next;node = job->msg.ln.next) {
1042
job = (CamelIMAPXJob *) node;
1044
if (!job || !(job->type & type))
1047
if (imapx_job_matches (folder_name, job, type, uid)) {
1061
/* handle any untagged responses */
1063
imapx_untagged(CamelIMAPXServer *imap, CamelException *ex)
1066
guchar *token, *p, c;
1068
gboolean lsub = FALSE;
1069
struct _status_info *sinfo;
1071
e(printf("got untagged response\n"));
1073
tok = camel_imapx_stream_token(imap->stream, &token, &len, ex);
1074
if (camel_exception_is_set (ex))
1077
if (tok == IMAPX_TOK_INT) {
1078
id = strtoul((gchar *) token, NULL, 10);
1079
tok = camel_imapx_stream_token(imap->stream, &token, &len, ex);
1083
camel_exception_set (ex, 1, "truncated server response");
1087
e(printf("Have token '%s' id %d\n", token, id));
1090
*p++ = toupper((gchar) c);
1092
switch (imapx_tokenise ((const gchar *) token, len)) {
1093
case IMAPX_CAPABILITY:
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));
1099
case IMAPX_EXPUNGE: {
1100
guint32 expunge = id;
1101
CamelIMAPXJob *job = imapx_match_active_job (imap, IMAPX_JOB_EXPUNGE, NULL);
1103
/* If there is a job running, let it handle the deletion */
1107
c(printf("expunged: %d\n", id));
1108
if (imap->select_folder) {
1110
CamelMessageInfo *mi;
1112
uid = camel_folder_summary_uid_from_index (imap->select_folder->summary, expunge - 1);
1116
if (imap->changes == NULL)
1117
imap->changes = camel_folder_change_info_new();
1119
mi = camel_folder_summary_uid (imap->select_folder->summary, uid);
1121
imapx_update_summary_for_removed_message (mi, imap->select_folder);
1122
camel_message_info_free (mi);
1125
camel_folder_summary_remove_uid_fast (imap->select_folder->summary, uid);
1126
imap->expunged = g_slist_prepend (imap->expunged, uid);
1128
camel_folder_change_info_remove_uid (imap->changes, uid);
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);
1135
g_slist_foreach (imap->expunged, (GFunc) g_free, NULL);
1136
imap->expunged = NULL;
1137
camel_folder_change_info_clear (imap->changes);
1143
case IMAPX_NAMESPACE: {
1144
CamelIMAPXNamespaceList *nsl = NULL;
1146
nsl = imapx_parse_namespace_list (imap->stream, ex);
1148
CamelIMAPXStore *imapx_store = (CamelIMAPXStore *) imap->store;
1149
CamelIMAPXStoreNamespace *ns;
1151
imapx_store->summary->namespaces = nsl;
1152
camel_store_summary_touch ((CamelStoreSummary *) imapx_store->summary);
1154
/* TODO Need to remove imapx_store->dir_sep to support multiple namespaces */
1157
imapx_store->dir_sep = ns->sep;
1163
c(printf("exists: %d\n", id));
1166
if (imap->select_folder)
1167
((CamelIMAPXFolder *) imap->select_folder)->exists_on_server = id;
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);
1178
imapx_parse_flags(imap->stream, &flags, NULL, ex);
1180
c(printf("flags: %08x\n", flags));
1184
struct _fetch_info *finfo;
1186
finfo = imapx_parse_fetch(imap->stream, ex);
1187
if (camel_exception_is_set (ex)) {
1188
imapx_free_fetch(finfo);
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);
1195
/* This must've been a get-message request, fill out the body stream,
1196
in the right spot */
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);
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));
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;
1217
camel_object_ref(imap->select_folder);
1218
folder = imap->select_folder;
1220
c(printf("flag changed: %d\n", id));
1222
if ( (uid = camel_folder_summary_uid_from_index (folder->summary, id - 1)))
1224
mi = camel_folder_summary_uid (folder->summary, uid);
1226
changed = imapx_update_message_info_flags (mi, finfo->flags, finfo->user_flags, folder);
1227
finfo->user_flags = NULL;
1231
if (imap->changes == NULL)
1232
imap->changes = camel_folder_change_info_new();
1234
camel_folder_change_info_change_uid(imap->changes, uid);
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);
1246
camel_message_info_free (mi);
1247
camel_object_unref (folder);
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);
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 */
1259
struct _refresh_info r;
1263
r.server_flags = finfo->flags;
1264
r.server_user_flags = finfo->user_flags;
1265
finfo->user_flags = NULL;
1267
g_array_append_val(job->u.refresh_info.infos, r);
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);
1275
/* This must be a refresh info job as well, but it has asked for
1276
new messages to be added to the index */
1279
CamelMimeParser *mp;
1280
CamelMessageInfo *mi;
1282
/* Do we want to save these headers for later too? Do we care? */
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);
1290
guint32 server_flags;
1291
CamelFlag *server_user_flags;
1292
CamelMessageInfoBase *binfo;
1294
mi->uid = camel_pstring_strdup (finfo->uid);
1296
if (!(finfo->got & FETCH_FLAGS))
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;
1304
/* array is sorted, so use a binary search */
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);
1319
} while (!found && min <= max);
1322
g_assert_not_reached ();
1324
server_flags = r->server_flags;
1325
server_user_flags = r->server_user_flags;
1327
server_flags = finfo->flags;
1328
server_user_flags = finfo->user_flags;
1329
/* free user_flags ? */
1330
finfo->user_flags = NULL;
1333
binfo = (CamelMessageInfoBase *) mi;
1334
binfo->size = finfo->size;
1336
if (!camel_folder_summary_check_uid (job->folder->summary, mi->uid)) {
1337
CamelIMAPXFolder *ifolder = (CamelIMAPXFolder *)job->folder;
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);
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);
1349
camel_operation_progress (job->op, (camel_folder_summary_count (job->folder->summary) * 100)/imap->exists);
1355
imapx_free_fetch(finfo);
1361
struct _list_info *linfo = imapx_parse_list(imap->stream, ex);
1362
CamelIMAPXJob *job = imapx_match_active_job(imap, IMAPX_JOB_LIST, linfo->name);
1364
// TODO: we want to make sure the names match?
1366
if (job->u.list.flags & CAMEL_STORE_FOLDER_INFO_SUBSCRIBED) {
1367
c(printf("lsub: '%s' (%c)\n", linfo->name, linfo->separator));
1370
c(printf("list: '%s' (%c)\n", linfo->name, linfo->separator));
1373
if (job && g_hash_table_lookup(job->u.list.folders, linfo->name) == NULL) {
1375
linfo->flags |= CAMEL_FOLDER_SUBSCRIBED;
1376
g_hash_table_insert(job->u.list.folders, linfo->name, linfo);
1378
g_warning("got list response but no current listing job happening?\n");
1379
imapx_free_list(linfo);
1384
c(printf("recent: %d\n", id));
1387
case IMAPX_STATUS: {
1388
struct _state_info *sinfo = imapx_parse_status_info (imap->stream, ex);
1390
/* this is what we use atm */
1391
imap->exists = sinfo->messages;
1392
imap->unread = sinfo->unseen;
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"));
1409
case IMAPX_READ_ONLY:
1410
imap->mode = IMAPX_MODE_READ;
1411
c(printf("folder is read-only\n"));
1413
case IMAPX_UIDVALIDITY:
1414
imap->uidvalidity = sinfo->u.uidvalidity;
1417
imap->unseen = sinfo->u.unseen;
1419
case IMAPX_PERMANENTFLAGS:
1420
imap->permanentflags = sinfo->u.permanentflags;
1423
c(printf("ALERT!: %s\n", sinfo->text));
1426
c(printf("PARSE: %s\n", sinfo->text));
1431
imapx_free_status(sinfo);
1434
/* unknown response, just ignore it */
1435
c(printf("unknown token: %s\n", token));
1438
return camel_imapx_stream_skip(imap->stream, ex);
1441
/* handle any continuation requests
1442
either data continuations, or auth continuation */
1444
imapx_continuation(CamelIMAPXServer *imap, CamelException *ex)
1446
CamelIMAPXCommand *ic, *newliteral = NULL;
1447
CamelIMAPXCommandPart *cp;
1449
c(printf("got continuation response\n"));
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
1455
if (imapx_idle_supported (imap) && imapx_in_idle (imap)) {
1456
camel_imapx_stream_skip (imap->stream, ex);
1458
c(printf("Got continuation response for IDLE \n"));
1459
imap->idle->started = TRUE;
1462
imap->literal = NULL;
1463
imapx_command_start_next(imap, ex);
1471
camel_imapx_stream_skip(imap->stream, ex);
1472
c(printf("got continuation response with no outstanding continuation requests?\n"));
1476
c(printf("got continuation response for data\n"));
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);
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);
1487
case CAMEL_IMAPX_COMMAND_AUTH: {
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))
1498
c(printf("got auth continuation, feeding token '%s' back to auth mech\n", resp));
1500
camel_stream_write((CamelStream *)imap->stream, resp, strlen(resp));
1502
/* we want to keep getting called until we get a status reponse from the server
1503
ignore what sasl tells us */
1507
case CAMEL_IMAPX_COMMAND_FILE: {
1510
c(printf("writing file '%s' to literal\n", (gchar *)cp->ob));
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?
1520
case CAMEL_IMAPX_COMMAND_STRING:
1521
camel_stream_write((CamelStream *)imap->stream, cp->ob, cp->ob_size);
1524
/* should we just ignore? */
1525
imap->literal = NULL;
1526
camel_exception_set (ex, 1, "continuation response for non-continuation request");
1530
camel_imapx_stream_skip(imap->stream, ex);
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) {
1540
g_assert(cp->next->next == NULL);
1543
c(printf("%p: queueing continuation\n", ic));
1544
camel_stream_printf((CamelStream *)imap->stream, "\r\n");
1548
imap->literal = newliteral;
1550
imapx_command_start_next(imap, ex);
1556
/* handle a completion line */
1558
imapx_completion(CamelIMAPXServer *imap, guchar *token, gint len, CamelException *ex)
1560
CamelIMAPXCommand *ic;
1563
if (token[0] != imap->tagprefix) {
1564
camel_exception_setv (ex, 1, "Server sent unexpected response: %s", token);
1569
tag = strtoul( (const gchar *)token+1, NULL, 10);
1571
if ((ic = imapx_find_command_tag(imap, tag)) == NULL) {
1572
camel_exception_setv (ex, 1, "got response tag unexpectedly: %s", token);
1577
c(printf("Got completion response for command %05u '%s'\n", ic->tag, ic->name));
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);
1583
camel_db_delete_uids (imap->store->cdb_w, imap->select_folder->full_name, imap->expunged, NULL);
1585
if (imap->expunged) {
1586
g_slist_foreach (imap->expunged, (GFunc) g_free, NULL);
1587
imap->expunged = NULL;
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);
1597
camel_dlist_remove((CamelDListNode *)ic);
1598
camel_dlist_addtail(&imap->done, (CamelDListNode *)ic);
1599
if (imap->literal == ic)
1600
imap->literal = NULL;
1602
if (ic->current->next->next) {
1604
camel_exception_setv (ex, 1, "command still has unsent parts? %s", ic->name);
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);
1615
camel_dlist_remove ((CamelDListNode *) ic);
1618
ic->status = imapx_parse_status(imap->stream, ex);
1621
ic->complete (imap, ic);
1624
imapx_command_start_next(imap, ex);
1631
imapx_step(CamelIMAPXServer *is, CamelException *ex)
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))
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);
1649
camel_exception_set (ex, 1, "unexpected server response:");
1652
/* Used to run 1 command synchronously,
1653
use for capa, login, and namespaces only. */
1655
imapx_command_run(CamelIMAPXServer *is, CamelIMAPXCommand *ic)
1656
/* throws IO,PARSE exception */
1658
camel_imapx_command_close(ic);
1661
imapx_command_start(is, ic);
1665
imapx_step(is, ic->ex);
1666
} while (ic->status == NULL && !camel_exception_is_set (ic->ex));
1669
camel_dlist_remove((CamelDListNode *)ic);
1674
imapx_command_complete (CamelIMAPXServer *is, CamelIMAPXCommand *ic)
1676
e_flag_set (ic->flag);
1679
/* change status to a job and remove command_run_sync */
1681
imapx_command_status_done (CamelIMAPXServer *is, CamelIMAPXCommand *ic)
1683
CamelIMAPXFolder *ifolder = (CamelIMAPXFolder *) ic->job->folder;
1685
ifolder->unread_on_server = is->unread;
1686
e_flag_set (ic->flag);
1689
/* The caller should free the command as well */
1691
imapx_command_run_sync (CamelIMAPXServer *is, CamelIMAPXCommand *ic)
1693
CamelIMAPXCommandFunc complete = NULL;
1695
ic->flag = e_flag_new ();
1696
complete = ic->complete;
1699
ic->complete = imapx_command_complete;
1701
imapx_command_queue (is, ic);
1702
e_flag_wait (ic->flag);
1704
e_flag_free (ic->flag);
1708
/* ********************************************************************** */
1709
/* Should be called when there are no more commands needed to complete the job */
1712
imapx_job_done (CamelIMAPXServer *is, CamelIMAPXJob *job)
1715
camel_dlist_remove((CamelDListNode *)job);
1719
camel_exception_clear(job->ex);
1722
camel_msgport_reply((CamelMsg *) job);
1726
imapx_register_job (CamelIMAPXServer *is, CamelIMAPXJob *job)
1728
if (is->state >= IMAPX_AUTHENTICATED) {
1730
camel_dlist_addhead (&is->jobs, (CamelDListNode *)job);
1734
e(printf ("NO connection yet, maybe user cancelled jobs earlier ?"));
1735
camel_exception_set (job->ex, CAMEL_EXCEPTION_SERVICE_NOT_CONNECTED, "Not authenticated");
1743
imapx_run_job (CamelIMAPXServer *is, CamelIMAPXJob *job)
1745
CamelMsgPort *reply = NULL;
1747
if (!job->noreply) {
1748
reply = camel_msgport_new ();
1749
job->msg.reply_port = reply;
1752
/* Any exceptions to the start should be reported async through our reply msgport */
1753
job->start (is, job);
1755
if (!job->noreply) {
1756
CamelMsg *completed;
1758
completed = camel_msgport_pop (reply);
1759
camel_msgport_destroy (reply);
1761
g_assert(completed == (CamelMsg *)job);
1765
/* ********************************************************************** */
1768
#define IDLE_LOCK(x) (g_mutex_lock((x)->idle_lock))
1769
#define IDLE_UNLOCK(x) (g_mutex_unlock((x)->idle_lock))
1771
/*TODO handle negative cases sanely */
1773
imapx_command_idle_stop (CamelIMAPXServer *is, CamelException *ex)
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");
1784
imapx_command_idle_done (CamelIMAPXServer *is, CamelIMAPXCommand *ic)
1786
CamelIMAPXIdle *idle = is->idle;
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);
1792
camel_exception_xfer (ic->job->ex, ic->ex);
1796
idle->in_idle = FALSE;
1797
idle->idle_issue_done = FALSE;
1798
idle->started = FALSE;
1801
imapx_job_done (is, ic->job);
1802
camel_imapx_command_free (ic);
1806
imapx_job_idle_start(CamelIMAPXServer *is, CamelIMAPXJob *job)
1808
CamelIMAPXCommand *ic;
1809
CamelIMAPXCommandPart *cp;
1811
ic = camel_imapx_command_new ("IDLE", job->folder->full_name, "IDLE");
1814
ic->complete = imapx_command_idle_done;
1816
camel_imapx_command_close(ic);
1817
cp = (CamelIMAPXCommandPart *)ic->parts.head;
1818
cp->type |= CAMEL_IMAPX_COMMAND_CONTINUATION;
1821
imapx_command_start (is, ic);
1826
camel_imapx_server_idle (CamelIMAPXServer *is, CamelFolder *folder, CamelException *ex)
1830
job = g_malloc0 (sizeof(*job));
1831
job->type = IMAPX_JOB_IDLE;
1832
job->start = imapx_job_idle_start;
1833
job->folder = folder;
1836
if (imapx_register_job (is, job))
1837
imapx_run_job(is, job);
1842
imapx_server_fetch_new_messages (CamelIMAPXServer *is, CamelFolder *folder, gboolean async, CamelException *ex)
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;
1852
job->u.refresh_info.changes = camel_folder_change_info_new();
1853
job->op = camel_operation_registered ();
1855
if (imapx_register_job (is, job))
1856
imapx_run_job (is, job);
1863
imapx_idle_thread (gpointer data)
1865
CamelException *ex = camel_exception_new ();
1866
CamelIMAPXServer *is = (CamelIMAPXServer *) data;
1869
CamelIMAPXFolder *ifolder = (CamelIMAPXFolder *) is->select_folder;
1871
e_flag_clear (is->idle->idle_start_watch);
1872
camel_imapx_server_idle (is, is->select_folder, ex);
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);
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);
1884
e_flag_wait (is->idle->idle_start_watch);
1886
if (is->idle->idle_exit)
1890
camel_exception_free (ex);
1891
is->idle->idle_thread = NULL;
1896
imapx_stop_idle (CamelIMAPXServer *is, CamelException *ex)
1898
CamelIMAPXIdle *idle = is->idle;
1902
if (!idle->idle_issue_done && idle->started) {
1903
imapx_command_idle_stop (is, ex);
1904
idle->idle_issue_done = TRUE;
1911
imapx_init_idle (CamelIMAPXServer *is)
1913
is->idle = g_new0 (CamelIMAPXIdle, 1);
1914
is->idle->idle_lock = g_mutex_new ();
1918
imapx_exit_idle (CamelIMAPXServer *is)
1920
CamelIMAPXIdle *idle = is->idle;
1927
if (idle->idle_thread) {
1928
idle->idle_exit = TRUE;
1929
e_flag_set (idle->idle_start_watch);
1931
if (idle->idle_thread)
1932
g_thread_join (idle->idle_thread);
1935
idle->idle_thread = NULL;
1938
g_mutex_free (idle->idle_lock);
1939
if (idle->idle_start_watch)
1940
e_flag_free (idle->idle_start_watch);
1947
imapx_start_idle (CamelIMAPXServer *is)
1949
CamelIMAPXIdle *idle = is->idle;
1951
if (camel_application_is_exiting)
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);
1960
e_flag_set (idle->idle_start_watch);
1962
idle->in_idle = TRUE;
1968
imapx_in_idle (CamelIMAPXServer *is)
1970
gboolean ret = FALSE;
1971
CamelIMAPXIdle *idle = is->idle;
1974
ret = idle->in_idle;
1981
imapx_idle_supported (CamelIMAPXServer *is)
1983
return (is->cinfo && is->cinfo->capa & IMAPX_CAPABILITY_IDLE && is->use_idle);
1987
/* ********************************************************************** */
1989
imapx_command_select_done (CamelIMAPXServer *is, CamelIMAPXCommand *ic)
1992
if (camel_exception_is_set (ic->ex) || ic->status->result != IMAPX_OK) {
1994
CamelIMAPXCommand *cw, *cn;
1996
c(printf("Select failed\n"));
1997
camel_dlist_init (&failed);
2000
cw = (CamelIMAPXCommand *)is->queue.head;
2003
if (is->select_pending) {
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);
2016
cw = (CamelIMAPXCommand *)failed.head;
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);
2029
if (is->select_pending)
2030
camel_object_unref(is->select_pending);
2032
CamelIMAPXFolder *ifolder = (CamelIMAPXFolder *) is->select_pending;
2033
c(printf("Select ok!\n"));
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;
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!");
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);
2052
is->select_pending = NULL;
2053
camel_imapx_command_free (ic);
2056
/* Should have a queue lock. TODO Change the way select is written */
2058
imapx_select (CamelIMAPXServer *is, CamelFolder *folder, gboolean forced, CamelException *ex)
2060
CamelIMAPXCommand *ic;
2062
/* Select is complicated by the fact we may have commands
2063
active on the server for a different selection.
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 */
2070
/* TODO check locking here, pending_select will do
2071
most of the work for normal commands, but not
2072
for another select */
2074
if (is->select_pending)
2077
if (is->select && strcmp(is->select, folder->full_name) == 0 && !forced)
2080
if (!camel_dlist_empty(&is->active))
2083
is->select_pending = folder;
2084
camel_object_ref(folder);
2085
if (is->select_folder) {
2087
camel_object_unref(is->select_folder);
2089
is->select_folder = NULL;
2092
is->uidvalidity = 0;
2094
is->permanentflags = 0;
2099
/* Hrm, what about reconnecting? */
2100
is->state = IMAPX_AUTHENTICATED;
2102
ic = camel_imapx_command_new("SELECT", NULL, "SELECT %f", folder);
2103
ic->complete = imapx_command_select_done;
2104
imapx_command_start (is, ic);
2108
imapx_connect_to_server (CamelIMAPXServer *is, CamelException *ex)
2110
CamelStream * tcp_stream = NULL;
2111
CamelSockOptData sockopt;
2112
gint ret, ssl_mode = 0;
2117
guchar *buffer = NULL;
2120
const gchar *port = NULL;
2121
struct addrinfo *ai, hints = { 0 };
2122
CamelIMAPXCommand *ic;
2124
if (is->url->port) {
2125
serv = g_alloca(16);
2126
sprintf((gchar *) serv, "%d", is->url->port);
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);
2138
if (is->url->port == 0) {
2142
tcp_stream = camel_tcp_stream_ssl_new(is->session, is->url->host, SSL_PORT_FLAGS);
2144
is->is_ssl_stream = TRUE;
2146
tcp_stream = camel_tcp_stream_raw_new ();
2147
is->is_ssl_stream = FALSE;
2150
tcp_stream = camel_tcp_stream_raw_new ();
2151
is->is_ssl_stream = FALSE;
2152
#endif /* HAVE_SSL */
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);
2162
e(printf ("Unable to connect %d %s \n", ex->id, ex->desc));
2163
camel_object_unref(tcp_stream);
2167
ret = camel_tcp_stream_connect(CAMEL_TCP_STREAM(tcp_stream), ai);
2168
camel_freeaddrinfo(ai);
2171
camel_exception_set (ex, CAMEL_EXCEPTION_USER_CANCEL, _("Connection cancelled"));
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);
2180
is->stream = (CamelIMAPXStream *) camel_imapx_stream_new(tcp_stream);
2181
camel_object_unref(tcp_stream);
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);
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);
2193
camel_imapx_stream_gets (is->stream, &buffer, &len);
2194
e(printf("Got greeting '%.*s'\n", len, buffer));
2196
ic = camel_imapx_command_new("CAPABILITY", NULL, "CAPABILITY");
2197
imapx_command_run(is, ic);
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);
2203
camel_exception_xfer (ex, ic->ex);
2205
camel_imapx_command_free(ic);
2208
camel_imapx_command_free(ic);
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"));
2221
ic = camel_imapx_command_new ("STARTTLS", NULL, "STARTTLS");
2222
imapx_command_run (is, ic);
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);
2228
camel_exception_xfer (ex, ic->ex);
2231
camel_imapx_command_free(ic);
2234
camel_imapx_command_free(ic);
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"));
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);
2252
imapx_free_capability(is->cinfo);
2263
imapx_reconnect (CamelIMAPXServer *is, CamelException *ex)
2266
CamelIMAPXCommand *ic;
2267
gchar *errbuf = NULL;
2268
CamelService *service = (CamelService *) is->store;
2269
const gchar *auth_domain = NULL;
2270
gboolean authenticated = FALSE;
2272
while (!authenticated) {
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);
2281
imapx_connect_to_server (is, ex);
2282
if (camel_exception_is_set (ex))
2285
if (service->url->passwd == NULL) {
2289
base_prompt = camel_session_build_password_prompt (
2290
"IMAP", service->url->user, service->url->host);
2293
full_prompt = g_strconcat (errbuf, base_prompt, NULL);
2295
full_prompt = g_strdup (base_prompt);
2297
auth_domain = camel_url_get_param (service->url, "auth-domain");
2298
service->url->passwd = camel_session_get_password(is->session, (CamelService *)is->store,
2300
full_prompt, "password", CAMEL_SESSION_PASSWORD_SECRET, ex);
2302
g_free (base_prompt);
2303
g_free (full_prompt);
2307
if (!service->url->passwd) {
2308
camel_exception_set (ex, CAMEL_EXCEPTION_USER_CANCEL,
2309
_("You did not enter a password."));
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);
2319
ic = camel_imapx_command_new("LOGIN", NULL, "LOGIN %s %s", service->url->user, service->url->passwd);
2322
imapx_command_run (is, ic);
2324
if (!(camel_exception_is_set (ic->ex) || ic->status->result != IMAPX_OK))
2325
authenticated = TRUE;
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);
2336
errbuf = g_markup_printf_escaped (
2337
_("Unable to authenticate to IMAP server.\n%s\n\n"),
2339
camel_exception_clear (ex);
2343
camel_imapx_command_free(ic);
2346
/* After login we re-capa */
2348
imapx_free_capability(is->cinfo);
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);
2357
if (camel_exception_is_set (ex))
2360
is->state = IMAPX_AUTHENTICATED;
2362
if (((CamelIMAPXStore *)is->store)->rec_options & IMAPX_USE_IDLE)
2363
is->use_idle = TRUE;
2365
is->use_idle = FALSE;
2367
if (imapx_idle_supported (is))
2368
imapx_init_idle (is);
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);
2377
if (camel_exception_is_set (ex))
2381
if (((CamelIMAPXStore *) is->store)->summary->namespaces == NULL) {
2382
CamelIMAPXNamespaceList *nsl = NULL;
2383
CamelIMAPXStoreNamespace *ns = NULL;
2384
CamelIMAPXStore *imapx_store = (CamelIMAPXStore *) is->store;
2386
/* set a default namespace */
2387
nsl = g_malloc0(sizeof(CamelIMAPXNamespaceList));
2388
ns = g_new0 (CamelIMAPXStoreNamespace, 1);
2390
ns->path = g_strdup ("");
2391
ns->full_name = g_strdup ("");
2394
imapx_store->summary->namespaces = nsl;
2395
/* FIXME needs to be identified from list response */
2396
imapx_store->dir_sep = ns->sep;
2399
if (!camel_exception_is_set (ex))
2403
imapx_disconnect (is);
2406
imapx_free_capability(is->cinfo);
2411
/* ********************************************************************** */
2414
imapx_command_fetch_message_done(CamelIMAPXServer *is, CamelIMAPXCommand *ic)
2416
CamelIMAPXJob *job = ic->job;
2417
gboolean failed = FALSE;
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 */
2425
if (camel_exception_is_set (ic->ex) || ic->status->result != IMAPX_OK) {
2427
job->u.get_message.body_len = -1;
2428
} else if (job->u.get_message.use_multi_fetch) {
2430
if (!failed && job->u.get_message.fetch_offset <= job->u.get_message.size) {
2431
camel_imapx_command_free (ic);
2433
camel_operation_progress (job->op, (job->u.get_message.fetch_offset *100)/job->u.get_message.size);
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;
2441
ic->pri = job->pri - 1;
2442
job->u.get_message.fetch_offset += MULTI_SIZE;
2444
imapx_command_queue(is, ic);
2449
if (job->commands == 0) {
2450
CamelIMAPXFolder *ifolder = (CamelIMAPXFolder *) job->folder;
2451
CamelStream *stream = job->u.get_message.stream;
2453
/* return the exception from last command */
2455
if (!camel_exception_is_set (ic->ex))
2456
camel_exception_setv(job->ex, 1, "Error fetching message: %s", ic->status->text);
2458
camel_exception_xfer (job->ex, ic->ex);
2459
camel_object_unref (stream);
2460
job->u.get_message.stream = NULL;
2462
CamelIMAPXFolder *ifolder = (CamelIMAPXFolder *) job->folder;
2465
gchar *tmp = camel_data_cache_get_filename (ifolder->cache, "tmp", job->u.get_message.uid, NULL);
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;
2471
dir = g_strndup (cache_file, temp - cache_file);
2472
g_mkdir_with_parents (dir, 0700);
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);
2479
camel_exception_setv(job->ex, 1, "closing tmp stream failed: %s", g_strerror(errno));
2482
job->u.get_message.stream = camel_data_cache_get (ifolder->cache, "cur", job->u.get_message.uid, NULL);
2486
camel_data_cache_remove (ifolder->cache, "tmp", job->u.get_message.uid, NULL);
2487
imapx_job_done (is, job);
2490
camel_imapx_command_free (ic);
2494
imapx_job_get_message_start (CamelIMAPXServer *is, CamelIMAPXJob *job)
2496
CamelIMAPXCommand *ic;
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;
2508
job->u.get_message.fetch_offset += MULTI_SIZE;
2510
imapx_command_queue(is, ic);
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;
2519
imapx_command_queue(is, ic);
2523
/* ********************************************************************** */
2526
imapx_command_copy_messages_step_start (CamelIMAPXServer *is, CamelIMAPXJob *job, gint index)
2528
CamelIMAPXCommand *ic;
2529
GPtrArray *uids = job->u.copy_messages.uids;
2532
ic = camel_imapx_command_new ("COPY", job->folder->full_name, "UID COPY ");
2533
ic->complete = imapx_command_copy_messages_step_done;
2536
job->u.copy_messages.last_index = i;
2538
for (;i < uids->len; i++) {
2540
const gchar *uid = (gchar *) g_ptr_array_index (uids, i);
2542
res = imapx_uidset_add (&job->u.copy_messages.uidset, ic, uid);
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);
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);
2560
imapx_command_copy_messages_step_done (CamelIMAPXServer *is, CamelIMAPXCommand *ic)
2562
CamelIMAPXJob *job = ic->job;
2563
gint i = job->u.copy_messages.index;
2564
GPtrArray *uids = job->u.copy_messages.uids;
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");
2570
camel_exception_xfer (job->ex, ic->ex);
2575
if (job->u.copy_messages.delete_originals) {
2578
for (j = job->u.copy_messages.last_index; j < i; j++)
2579
camel_folder_delete_message (job->folder, uids->pdata [j]);
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) {
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;
2591
g_hash_table_insert (ifolder->ignore_recent, str, GINT_TO_POINTER (1));
2596
if (i < uids->len) {
2597
camel_imapx_command_free (ic);
2598
imapx_command_copy_messages_step_start (is, job, i);
2602
camel_object_unref (job->u.copy_messages.dest);
2603
camel_object_unref (job->folder);
2605
imapx_job_done (is, job);
2606
camel_imapx_command_free (ic);
2610
imapx_job_copy_messages_start (CamelIMAPXServer *is, CamelIMAPXJob *job)
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);
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);
2621
/* ********************************************************************** */
2624
imapx_command_append_message_done (CamelIMAPXServer *is, CamelIMAPXCommand *ic)
2626
CamelIMAPXJob *job = ic->job;
2627
CamelIMAPXFolder *ifolder = (CamelIMAPXFolder *) job->folder;
2628
CamelMessageInfo *mi;
2629
gchar *cur, *old_uid;
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. */
2636
mi = camel_message_info_clone (job->u.append_message.info);
2637
old_uid = g_strdup (job->u.append_message.info->uid);
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;
2646
uid = g_strdup_printf("%u", (guint)ic->status->u.appenduid.uid);
2647
mi->uid = camel_pstring_add (uid, TRUE);
2649
cur = camel_data_cache_get_filename (ifolder->cache, "cur", mi->uid, NULL);
2650
g_rename (job->u.append_message.path, cur);
2652
/* should we update the message count ? */
2653
camel_folder_summary_add (job->folder->summary, mi);
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",
2659
camel_folder_change_info_free (changes);
2663
g_message ("but uidvalidity changed \n");
2667
if (!camel_exception_is_set (ic->ex))
2668
camel_exception_setv(job->ex, 1, "Error appending message: %s", ic->status->text);
2670
camel_exception_xfer (job->ex, ic->ex);
2673
camel_data_cache_remove (ifolder->cache, "new", old_uid, NULL);
2675
camel_message_info_free(job->u.append_message.info);
2676
g_free(job->u.append_message.path);
2677
camel_object_unref(job->folder);
2679
imapx_job_done (is, job);
2680
camel_imapx_command_free (ic);
2684
imapx_job_append_message_start(CamelIMAPXServer *is, CamelIMAPXJob *job)
2686
CamelIMAPXCommand *ic;
2688
/* TODO: we could supply the original append date from the file timestamp */
2689
ic = camel_imapx_command_new("APPEND", NULL,
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;
2699
imapx_command_queue(is, ic);
2702
/* ********************************************************************** */
2705
imapx_refresh_info_uid_cmp (gconstpointer ap, gconstpointer bp)
2709
av = g_ascii_strtoull ((const gchar *)ap, NULL, 10);
2710
bv = g_ascii_strtoull ((const gchar *)bp, NULL, 10);
2721
imapx_uids_array_cmp (gconstpointer ap, gconstpointer bp)
2723
const gchar **a = (const gchar **) ap;
2724
const gchar **b = (const gchar **) bp;
2726
return imapx_refresh_info_uid_cmp (*a, *b);
2730
imapx_refresh_info_cmp (gconstpointer ap, gconstpointer bp)
2732
const struct _refresh_info *a = ap;
2733
const struct _refresh_info *b = bp;
2735
return imapx_refresh_info_uid_cmp (a->uid, b->uid);
2738
/* skips over non-server uids (pending appends) */
2740
imapx_index_next (GPtrArray *uids, CamelFolderSummary *s, guint index)
2743
while (index < uids->len) {
2744
CamelMessageInfo *info;
2747
if (index >= uids->len)
2750
info = camel_folder_summary_uid (s, g_ptr_array_index (uids, index));
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)));
2758
camel_message_info_free (info);
2767
imapx_command_step_fetch_done(CamelIMAPXServer *is, CamelIMAPXCommand *ic)
2769
CamelIMAPXJob *job = ic->job;
2770
gint i = job->u.refresh_info.index;
2771
GArray *infos = job->u.refresh_info.infos;
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");
2777
camel_exception_xfer (job->ex, ic->ex);
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);
2788
camel_folder_change_info_clear(job->u.refresh_info.changes);
2791
camel_imapx_command_free (ic);
2793
ic = camel_imapx_command_new("FETCH", job->folder->full_name, "UID FETCH ");
2794
ic->complete = imapx_command_step_fetch_done;
2796
ic->pri = job->pri - 1;
2797
job->u.refresh_info.last_index = i;
2799
for (;i<infos->len;i++) {
2801
struct _refresh_info *r = &g_array_index(infos, struct _refresh_info, i);
2804
res = imapx_uidset_add(&job->u.refresh_info.uidset, ic, r->uid);
2806
camel_imapx_command_add(ic, " (RFC822.SIZE RFC822.HEADER)");
2807
job->u.refresh_info.index = i;
2808
imapx_command_queue(is, ic);
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);
2823
for (i=0;i<infos->len;i++) {
2824
struct _refresh_info *r = &g_array_index(infos, struct _refresh_info, i);
2828
g_array_free(job->u.refresh_info.infos, TRUE);
2830
imapx_job_done (is, job);
2831
camel_imapx_command_free (ic);
2835
imapx_uid_cmp(gconstpointer ap, gconstpointer bp, gpointer data)
2837
const gchar *a = ap, *b = bp;
2839
unsigned long av, bv;
2841
av = strtoul(a, &ae, 10);
2842
bv = strtoul(b, &be, 10);
2854
return strcmp(ae, be);
2858
imapx_job_scan_changes_done(CamelIMAPXServer *is, CamelIMAPXCommand *ic)
2860
CamelIMAPXJob *job = ic->job;
2862
GArray *infos = job->u.refresh_info.infos;
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;
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
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 */
2884
/* obtain a copy to be thread safe */
2885
uids = camel_folder_summary_array (s);
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);
2891
s_minfo = camel_folder_summary_uid (s, g_ptr_array_index (uids, 0));
2893
for (i=0; i<infos->len; i++) {
2894
struct _refresh_info *r = &g_array_index(infos, struct _refresh_info, i);
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);
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);
2904
j = imapx_index_next (uids, s, j);
2906
s_minfo = camel_folder_summary_uid (s, g_ptr_array_index (uids, j));
2910
if (s_minfo && uid_cmp(s_minfo->uid, r->uid, s) == 0) {
2911
info = (CamelIMAPXMessageInfo *)s_minfo;
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));
2920
camel_message_info_free (s_minfo);
2927
j = imapx_index_next (uids, s, j);
2929
s_minfo = camel_folder_summary_uid (s, g_ptr_array_index (uids, j));
2933
camel_message_info_free (s_minfo);
2935
while (j < uids->len) {
2936
s_minfo = camel_folder_summary_uid (s, g_ptr_array_index (uids, j));
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);
2949
for (l = removed; l != NULL; l = g_slist_next (l)) {
2950
gchar *uid = (gchar *) l->data;
2951
CamelMessageInfo *mi;
2953
mi = camel_folder_summary_uid (is->select_folder->summary, uid);
2955
imapx_update_summary_for_removed_message (mi, is->select_folder);
2956
camel_message_info_free (mi);
2959
camel_folder_change_info_remove_uid (job->u.refresh_info.changes, uid);
2960
camel_folder_summary_remove_uid_fast (s, uid);
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);
2969
imapx_update_store_summary (job->folder);
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);
2975
camel_folder_free_uids (job->folder, uids);
2977
/* If we have any new messages, download their headers, but only a few (100?) at a time */
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);
2986
if (!camel_exception_is_set (ic->ex))
2987
camel_exception_setv(job->ex, 1, "Error retriving message: %s", ic->status->text);
2989
camel_exception_xfer (job->ex, ic->ex);
2992
for (i=0;i<infos->len;i++) {
2993
struct _refresh_info *r = &g_array_index(infos, struct _refresh_info, i);
2998
g_array_free(job->u.refresh_info.infos, TRUE);
2999
imapx_job_done (is, job);
3000
camel_imapx_command_free (ic);
3004
imapx_job_scan_changes_start(CamelIMAPXServer *is, CamelIMAPXJob *job)
3006
CamelIMAPXCommand *ic;
3008
camel_operation_start (job->op, _("Scanning for changed messages in %s"), job->folder->name);
3010
ic = camel_imapx_command_new ("FETCH", job->folder->full_name,
3011
"FETCH 1:* (UID FLAGS)");
3013
ic->complete = imapx_job_scan_changes_done;
3015
job->u.refresh_info.infos = g_array_new (0, 0, sizeof(struct _refresh_info));
3016
imapx_command_queue (is, ic);
3020
imapx_command_fetch_new_messages_done (CamelIMAPXServer *is, CamelIMAPXCommand *ic)
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);
3026
camel_exception_xfer (ic->job->ex, ic->ex);
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);
3038
if (ic->job->noreply)
3039
camel_folder_change_info_free(ic->job->u.refresh_info.changes);
3042
camel_operation_unref (ic->job->op);
3044
imapx_job_done (is, ic->job);
3045
camel_imapx_command_free (ic);
3049
imapx_job_fetch_new_messages_start (CamelIMAPXServer *is, CamelIMAPXJob *job)
3051
CamelIMAPXCommand *ic;
3052
CamelFolder *folder = job->folder;
3053
CamelIMAPXFolder *ifolder = (CamelIMAPXFolder *) folder;
3054
guint32 total, diff;
3057
total = camel_folder_summary_count (folder->summary);
3058
diff = ifolder->exists_on_server - total;
3061
uid = camel_folder_summary_uid_from_index (folder->summary, total - 1);
3063
uid = g_strdup ("1");
3065
camel_operation_start (job->op, _("Fetching summary information for new messages in %s"), folder->name);
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));
3073
ic->complete = imapx_command_step_fetch_done;
3075
ic = camel_imapx_command_new ("FETCH", job->folder->full_name,
3076
"UID FETCH %s:* (RFC822.SIZE RFC822.HEADER FLAGS)", uid);
3078
ic->complete = imapx_command_fetch_new_messages_done;
3083
imapx_command_queue (is, ic);
3087
imapx_job_refresh_info_start (CamelIMAPXServer *is, CamelIMAPXJob *job)
3090
CamelIMAPXFolder *ifolder = (CamelIMAPXFolder *) job->folder;
3091
CamelFolder *folder = job->folder;
3092
CamelException *ex = job->ex;
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);
3100
if (camel_exception_is_set (ex))
3104
/* Fetch the new messages */
3105
if (ifolder->exists_on_server > total)
3107
imapx_server_fetch_new_messages (is, folder, FALSE, job->ex);
3108
if (camel_exception_is_set (job->ex))
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))
3117
/* Check if a rescan is needed */
3118
total = camel_folder_summary_count (folder->summary);
3119
if (ifolder->exists_on_server == total) {
3121
CamelIMAPXCommand *ic;
3123
ic = camel_imapx_command_new ("STATUS", folder->full_name, "STATUS %f (MESSAGES UNSEEN)", folder);
3126
ic->complete = imapx_command_status_done;
3127
imapx_command_run_sync (is, ic);
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);
3133
camel_exception_xfer (job->ex, ic->ex);
3135
camel_imapx_command_free (ic);
3138
camel_imapx_command_free (ic);
3140
camel_object_get (folder, NULL, CAMEL_FOLDER_UNREAD, &unread, NULL);
3141
if (ifolder->exists_on_server == total && unread == ifolder->unread_on_server)
3145
imapx_job_scan_changes_start (is, job);
3149
imapx_job_done (is, job);
3152
/* ********************************************************************** */
3155
imapx_command_expunge_done (CamelIMAPXServer *is, CamelIMAPXCommand *ic)
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);
3161
camel_exception_xfer (ic->job->ex, ic->ex);
3164
CamelFolder *folder = ic->job->folder;
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);
3169
if (uids && uids->len) {
3170
CamelFolderChangeInfo *changes;
3171
GSList *removed = NULL;
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);
3180
imapx_update_summary_for_removed_message (mi, folder);
3181
camel_message_info_free (mi);
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]);
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);
3194
g_slist_free (removed);
3195
g_ptr_array_foreach (uids, (GFunc) camel_pstring_free, NULL);
3196
g_ptr_array_free (uids, TRUE);
3200
imapx_job_done (is, ic->job);
3201
camel_imapx_command_free (ic);
3205
imapx_job_expunge_start(CamelIMAPXServer *is, CamelIMAPXJob *job)
3207
CamelIMAPXCommand *ic;
3209
imapx_server_sync_changes (is, job->folder, job->pri, job->ex);
3211
/* TODO handle UIDPLUS capability */
3212
ic = camel_imapx_command_new("EXPUNGE", job->folder->full_name, "EXPUNGE");
3215
ic->complete = imapx_command_expunge_done;
3216
imapx_command_queue(is, ic);
3219
/* ********************************************************************** */
3222
imapx_command_list_done (CamelIMAPXServer *is, CamelIMAPXCommand *ic)
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);
3228
camel_exception_xfer (ic->job->ex, ic->ex);
3231
e(printf ("==== list or lsub completed ==== \n"));
3232
imapx_job_done (is, ic->job);
3233
camel_imapx_command_free (ic);
3237
imapx_job_list_start(CamelIMAPXServer *is, CamelIMAPXJob *job)
3239
CamelIMAPXCommand *ic;
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);
3246
ic->complete = imapx_command_list_done;
3247
imapx_command_queue(is, ic);
3249
/* ********************************************************************** */
3253
imapx_encode_folder_name (CamelIMAPXStore *istore, const gchar *folder_name)
3255
gchar *fname, *encoded;
3257
fname = camel_imapx_store_summary_full_from_path(istore->summary, folder_name);
3259
encoded = camel_utf8_utf7(fname);
3262
encoded = camel_utf8_utf7 (folder_name);
3268
imapx_command_subscription_done (CamelIMAPXServer *is, CamelIMAPXCommand *ic)
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);
3274
camel_exception_xfer (ic->job->ex, ic->ex);
3277
imapx_job_done (is, ic->job);
3278
camel_imapx_command_free (ic);
3282
imapx_job_manage_subscription_start (CamelIMAPXServer *is, CamelIMAPXJob *job)
3284
CamelIMAPXCommand *ic;
3285
const gchar *str = NULL;
3286
gchar *encoded_fname = NULL;
3289
if (job->u.manage_subscriptions.subscribe)
3292
str = "UNSUBSCRIBE";
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);
3299
ic->complete = imapx_command_subscription_done;
3300
imapx_command_queue(is, ic);
3302
g_free (encoded_fname);
3305
/* ********************************************************************** */
3308
imapx_command_create_folder_done (CamelIMAPXServer *is, CamelIMAPXCommand *ic)
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);
3314
camel_exception_xfer (ic->job->ex, ic->ex);
3317
imapx_job_done (is, ic->job);
3318
camel_imapx_command_free (ic);
3322
imapx_job_create_folder_start (CamelIMAPXServer *is, CamelIMAPXJob *job)
3324
CamelIMAPXCommand *ic;
3325
gchar *encoded_fname = NULL;
3327
encoded_fname = camel_utf8_utf7 (job->u.folder_name);
3328
ic = camel_imapx_command_new ("CREATE", NULL, "CREATE %s", encoded_fname);
3331
ic->complete = imapx_command_create_folder_done;
3332
imapx_command_queue(is, ic);
3334
g_free (encoded_fname);
3337
/* ********************************************************************** */
3340
imapx_command_delete_folder_done (CamelIMAPXServer *is, CamelIMAPXCommand *ic)
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);
3346
camel_exception_xfer (ic->job->ex, ic->ex);
3349
imapx_job_done (is, ic->job);
3350
camel_imapx_command_free (ic);
3354
imapx_job_delete_folder_start (CamelIMAPXServer *is, CamelIMAPXJob *job)
3356
CamelIMAPXCommand *ic;
3357
gchar *encoded_fname = NULL;
3359
encoded_fname = imapx_encode_folder_name ((CamelIMAPXStore *) is->store, job->u.folder_name);
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);
3365
ic->complete = imapx_command_delete_folder_done;
3366
imapx_command_queue(is, ic);
3368
g_free (encoded_fname);
3371
/* ********************************************************************** */
3374
imapx_command_rename_folder_done (CamelIMAPXServer *is, CamelIMAPXCommand *ic)
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);
3380
camel_exception_xfer (ic->job->ex, ic->ex);
3383
imapx_job_done (is, ic->job);
3384
camel_imapx_command_free (ic);
3388
imapx_job_rename_folder_start (CamelIMAPXServer *is, CamelIMAPXJob *job)
3390
CamelIMAPXCommand *ic;
3391
gchar *en_ofname = NULL, *en_nfname = NULL;
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);
3396
ic = camel_imapx_command_new ("RENAME", "INBOX", "RENAME %s %s", en_ofname, en_nfname);
3399
ic->complete = imapx_command_rename_folder_done;
3400
imapx_command_queue(is, ic);
3406
/* ********************************************************************** */
3409
imapx_command_noop_done (CamelIMAPXServer *is, CamelIMAPXCommand *ic)
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);
3415
camel_exception_xfer (ic->job->ex, ic->ex);
3418
imapx_job_done (is, ic->job);
3419
camel_imapx_command_free (ic);
3423
imapx_job_noop_start(CamelIMAPXServer *is, CamelIMAPXJob *job)
3425
CamelIMAPXCommand *ic;
3428
ic = camel_imapx_command_new ("NOOP", job->folder->full_name, "NOOP");
3430
ic = camel_imapx_command_new ("NOOP", NULL, "NOOP");
3433
ic->complete = imapx_command_noop_done;
3435
ic->pri = IMAPX_PRIORITY_REFRESH_INFO;
3437
ic->pri = IMAPX_PRIORITY_NOOP;
3438
imapx_command_queue(is, ic);
3441
/* ********************************************************************** */
3443
/* FIXME: this is basically a copy of the same in camel-imapx-utils.c */
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 }
3467
imapx_command_sync_changes_done (CamelIMAPXServer *is, CamelIMAPXCommand *ic)
3469
CamelIMAPXJob *job = ic->job;
3470
gboolean failed = FALSE;
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.
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 */
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);
3487
camel_exception_xfer (job->ex, ic->ex);
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]);
3503
info->server_flags = ((CamelMessageInfoBase *)info)->flags & CAMEL_IMAPX_SERVER_FLAGS;
3504
info->info.flags &= ~CAMEL_MESSAGE_FOLDER_FLAGGED;
3505
info->info.dirty = TRUE;
3507
camel_folder_summary_touch (job->folder->summary);
3508
camel_message_info_free (info);
3510
/* FIXME: move over user flags too */
3514
if (job->commands == 0) {
3515
if (job->folder->summary && (job->folder->summary->flags & CAMEL_SUMMARY_DIRTY) != 0) {
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);
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);
3527
camel_store_summary_info_free ((CamelStoreSummary *)((CamelIMAPXStore *) job->folder->parent_store)->summary, si);
3531
camel_folder_summary_save_to_db (job->folder->summary, job->ex);
3532
camel_store_summary_save((CamelStoreSummary *)((CamelIMAPXStore *) job->folder->parent_store)->summary);
3534
imapx_job_done (is, job);
3536
camel_imapx_command_free (ic);
3540
imapx_job_sync_changes_start(CamelIMAPXServer *is, CamelIMAPXJob *job)
3543
struct _uidset_state ss;
3544
GPtrArray *uids = job->u.sync_changes.changed_uids;
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;
3551
for (j = 0; j < G_N_ELEMENTS (flags_table); j++) {
3552
guint32 flag = flags_table[j].flag;
3553
CamelIMAPXCommand *ic = NULL;
3555
if ((orset & flag) == 0)
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]);
3570
flags = ((CamelMessageInfoBase *)info)->flags & CAMEL_IMAPX_SERVER_FLAGS;
3571
sflags = info->server_flags & CAMEL_IMAPX_SERVER_FLAGS;
3574
if ( (on && (((flags ^ sflags) & flags) & flag))
3575
|| (!on && (((flags ^ sflags) & ~flags) & flag))) {
3577
ic = camel_imapx_command_new("STORE", job->folder->full_name, "UID STORE ");
3578
ic->complete = imapx_command_sync_changes_done;
3582
send = imapx_uidset_add(&ss, ic, camel_message_info_uid(info));
3584
if (send || (i == uids->len-1 && imapx_uidset_done(&ss, ic))) {
3586
camel_imapx_command_add(ic, " %tFLAGS.SILENT (%t)", on?"+":"-", flags_table[j].name);
3587
imapx_command_queue(is, ic);
3590
camel_message_info_free (info);
3595
CamelIMAPXCommand *ic = NULL;
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);
3600
imapx_uidset_init(&ss, 0, 100);
3601
for (i=0; i < c->infos->len; i++) {
3602
CamelIMAPXMessageInfo *info = c->infos->pdata[i];
3605
ic = camel_imapx_command_new("STORE", job->folder->full_name, "UID STORE ");
3606
ic->complete = imapx_command_sync_changes_done;
3611
if (imapx_uidset_add(&ss, ic, camel_message_info_uid (info))
3612
|| (i == c->infos->len-1 && imapx_uidset_done (&ss, ic))) {
3614
camel_imapx_command_add(ic, " %tFLAGS.SILENT (%t)", on?"+":"-", c->name);
3615
imapx_command_queue(is, ic);
3623
/* Since this may start in another thread ... we need to
3624
lock the commands count, ho hum */
3626
if (job->commands == 0) {
3627
imapx_job_done (is, job);
3631
/* we cancel all the commands and their jobs, so associated jobs will be notified */
3633
cancel_all_jobs (CamelIMAPXServer *is, CamelException *ex)
3635
CamelIMAPXCommand *cw, *cn;
3641
cw = (CamelIMAPXCommand *) is->queue.head;
3643
cw = (CamelIMAPXCommand *) is->active.head;
3650
camel_dlist_remove ((CamelDListNode *)cw);
3653
camel_exception_set (cw->ex, ex->id, ex->desc);
3655
cw->complete (is, cw);
3667
/* ********************************************************************** */
3670
parse_contents (CamelIMAPXServer *is, CamelException *ex)
3677
buffered = camel_imapx_stream_buffered (is->stream);
3679
} while (buffered && !camel_exception_is_set (ex));
3683
The main processing (reading) loop.
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
3690
imapx_parser_thread (gpointer d)
3692
CamelIMAPXServer *is = d;
3693
CamelException ex = CAMEL_EXCEPTION_INITIALISER;
3696
op = camel_operation_new (NULL, NULL);
3697
op = camel_operation_register (op);
3700
while (!camel_exception_is_set (&ex) && is->stream) {
3701
camel_operation_uncancel (op);
3703
if (is->is_ssl_stream) {
3704
PRPollDesc pollfds[2] = { };
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;
3714
res = PR_Poll(pollfds, 2, PR_MillisecondsToInterval (30 * 1000));
3716
g_usleep(1) /* ?? */ ;
3717
else if (res == 0) {
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)
3726
if (!is->is_ssl_stream) {
3727
GPollFD fds[2] = { {0, 0, 0}, {0, 0, 0} };
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;
3735
res = g_poll(fds, 2, 1000*30);
3737
g_usleep(1) /* ?? */ ;
3740
else if (fds[0].revents & G_IO_IN) {
3741
parse_contents (is, &ex);
3742
} else if (fds[1].revents & G_IO_IN)
3746
if (camel_application_is_exiting || is->parser_quit) {
3747
camel_exception_setv (&ex, CAMEL_EXCEPTION_USER_CANCEL, "Operation Cancelled: %s", g_strerror(errno));
3751
if (camel_operation_cancel_check (op)) {
3755
is_empty = camel_dlist_empty (&is->active);
3758
if ((is_empty || (imapx_idle_supported (is) && imapx_in_idle (is))))
3759
camel_operation_uncancel (op);
3761
camel_exception_setv (&ex, CAMEL_EXCEPTION_USER_CANCEL, "Operation Cancelled: %s", g_strerror(errno));
3765
imapx_disconnect (is);
3766
cancel_all_jobs (is, &ex);
3768
if (imapx_idle_supported (is))
3769
imapx_exit_idle (is);
3771
camel_exception_clear (&ex);
3774
camel_operation_unregister (op);
3775
camel_operation_unref (op);
3779
is->parser_thread = NULL;
3780
is->parser_quit = FALSE;
3785
imapx_server_class_init(CamelIMAPXServerClass *ieclass)
3787
ieclass->tagprefix = 'A';
3789
// camel_object_class_add_event((CamelObjectClass *)ieclass, "status", NULL);
3793
imapx_server_init(CamelIMAPXServer *is, CamelIMAPXServerClass *isclass)
3795
camel_dlist_init(&is->queue);
3796
camel_dlist_init(&is->active);
3797
camel_dlist_init(&is->done);
3798
camel_dlist_init(&is->jobs);
3800
/* not used at the moment. Use it in future */
3801
is->job_timeout = 29 * 60 * 1000 * 1000;
3803
g_static_rec_mutex_init (&is->queue_lock);
3804
g_static_rec_mutex_init (&is->ostream_lock);
3806
is->tagprefix = isclass->tagprefix;
3807
isclass->tagprefix++;
3808
if (isclass->tagprefix > 'Z')
3809
isclass->tagprefix = 'A';
3810
is->tagprefix = 'A';
3812
is->state = IMAPX_DISCONNECTED;
3814
is->expunged = NULL;
3815
is->changes = camel_folder_change_info_new ();
3816
is->parser_quit = FALSE;
3818
is->uid_eflags = g_hash_table_new_full (g_str_hash, g_str_equal, (GDestroyNotify)g_free, (GDestroyNotify) e_flag_free);
3822
imapx_server_finalise(CamelIMAPXServer *is, CamelIMAPXServerClass *isclass)
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);
3828
camel_folder_change_info_free (is->changes);
3832
camel_imapx_server_get_type (void)
3834
static CamelType type = CAMEL_INVALID_TYPE;
3836
if (type == CAMEL_INVALID_TYPE) {
3837
type = camel_type_register (
3838
camel_object_get_type (),
3840
sizeof (CamelIMAPXServer),
3841
sizeof (CamelIMAPXServerClass),
3842
(CamelObjectClassInitFunc) imapx_server_class_init,
3844
(CamelObjectInitFunc) imapx_server_init,
3845
(CamelObjectFinalizeFunc) imapx_server_finalise);
3852
camel_imapx_server_new(CamelStore *store, CamelURL *url)
3854
CamelIMAPXServer *is = (CamelIMAPXServer *)camel_object_new(camel_imapx_server_get_type());
3856
is->session = ((CamelService *)store)->session;
3857
camel_object_ref(is->session);
3859
is->url = camel_url_copy(url);
3865
imapx_disconnect (CamelIMAPXServer *is)
3867
gboolean ret = TRUE;
3869
g_static_rec_mutex_lock (&is->ostream_lock);
3872
if (camel_stream_close (is->stream->source) == -1)
3875
camel_object_unref (CAMEL_OBJECT (is->stream));
3879
/* TODO need a select lock */
3880
if (is->select_folder) {
3881
camel_object_unref(is->select_folder);
3882
is->select_folder = NULL;
3890
if (is->select_pending) {
3891
camel_object_unref(is->select_pending);
3892
is->select_pending = NULL;
3896
camel_imapx_command_free (is->literal);
3901
imapx_free_capability(is->cinfo);
3905
is->state = IMAPX_DISCONNECTED;
3907
g_static_rec_mutex_unlock (&is->ostream_lock);
3912
/* Client commands */
3914
camel_imapx_server_connect (CamelIMAPXServer *is, gboolean connect, CamelException *ex)
3916
gboolean ret = FALSE;
3918
CAMEL_SERVICE_REC_LOCK (is->store, connect_lock);
3920
if (is->state == IMAPX_AUTHENTICATED || is->state == IMAPX_SELECTED) {
3925
g_static_rec_mutex_lock (&is->ostream_lock);
3926
imapx_reconnect (is, ex);
3927
g_static_rec_mutex_unlock (&is->ostream_lock);
3929
if (camel_exception_is_set (ex)) {
3934
is->parser_thread = g_thread_create((GThreadFunc) imapx_parser_thread, is, TRUE, NULL);
3937
is->parser_quit = TRUE;
3938
camel_operation_cancel (is->op);
3939
if (is->parser_thread)
3940
g_thread_join (is->parser_thread);
3945
CAMEL_SERVICE_REC_UNLOCK (is->store, connect_lock);
3949
static CamelStream *
3950
imapx_server_get_message (CamelIMAPXServer *is, CamelFolder *folder, CamelOperation *op, const gchar *uid, gint pri, CamelException *ex)
3952
CamelStream *stream = NULL, *tmp_stream;
3953
CamelIMAPXFolder *ifolder = (CamelIMAPXFolder *) folder;
3955
gchar *cache_file = NULL;
3956
CamelMessageInfo *mi;
3957
gboolean registered;
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);
3965
g_free (cache_file);
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);
3979
stream = camel_data_cache_get (ifolder->cache, "cur", uid, NULL);
3981
camel_exception_set (ex, 1, "Could not retrieve the message");
3985
mi = camel_folder_summary_uid (folder->summary, uid);
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."));
3995
tmp_stream = camel_data_cache_add (ifolder->cache, "tmp", uid, NULL);
3997
job = g_malloc0(sizeof(*job));
3999
job->type = IMAPX_JOB_GET_MESSAGE;
4000
job->start = imapx_job_get_message_start;
4001
job->folder = folder;
4003
job->u.get_message.uid = (gchar *)uid;
4004
job->u.get_message.stream = tmp_stream;
4007
if (((CamelMessageInfoBase *) mi)->size > MULTI_SIZE)
4008
job->u.get_message.use_multi_fetch = TRUE;
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);
4019
imapx_run_job(is, job);
4022
if (!camel_exception_is_set (job->ex))
4023
stream = job->u.get_message.stream;
4027
/* HACK FIXME just sleep for sometime so that the other waiting locks gets released by that time. Think of a
4030
g_hash_table_remove (is->uid_eflags, uid);
4036
camel_imapx_server_get_message(CamelIMAPXServer *is, CamelFolder *folder, const gchar *uid, CamelException *ex)
4038
CamelStream *stream;
4039
CamelOperation *op = camel_operation_registered ();
4041
stream = imapx_server_get_message(is, folder, op, uid, IMAPX_PRIORITY_GET_MESSAGE, ex);
4043
camel_operation_unref (op);
4049
camel_imapx_server_sync_message (CamelIMAPXServer *is, CamelFolder *folder, const gchar *uid, CamelException *ex)
4051
gchar *cache_file = NULL;
4052
CamelIMAPXFolder *ifolder = (CamelIMAPXFolder *) folder;
4053
CamelStream *stream;
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);
4061
stream = imapx_server_get_message (is, folder, NULL, uid, IMAPX_PRIORITY_SYNC_MESSAGE, ex);
4063
camel_object_unref(stream);
4067
camel_imapx_server_copy_message (CamelIMAPXServer *is, CamelFolder *source, CamelFolder *dest, GPtrArray *uids, gboolean delete_originals, CamelException *ex)
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;
4081
camel_object_ref(source);
4082
camel_object_ref (dest);
4084
if (imapx_register_job (is, job))
4085
imapx_run_job (is, job);
4089
camel_imapx_server_append_message(CamelIMAPXServer *is, CamelFolder *folder, CamelMimeMessage *message, const CamelMessageInfo *mi, CamelException *ex)
4091
gchar *uid = NULL, *tmp = NULL;
4092
CamelStream *stream, *filter;
4093
CamelIMAPXFolder *ifolder = (CamelIMAPXFolder *) folder;
4094
CamelMimeFilter *canon;
4096
CamelMessageInfo *info;
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 */
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));
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);
4121
camel_exception_setv(ex, 2, "Cannot create spool file: %s", g_strerror(errno));
4122
camel_data_cache_remove (ifolder->cache, "new", uid, NULL);
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);
4129
((CamelMessageInfoBase *) info)->flags = ((CamelMessageInfoBase *) mi)->flags;
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 */
4137
job = g_malloc0(sizeof(*job));
4138
job->pri = IMAPX_PRIORITY_APPEND_MESSAGE;
4139
job->type = IMAPX_JOB_APPEND_MESSAGE;
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;
4147
if (imapx_register_job (is, job))
4148
imapx_run_job(is, job);
4153
camel_imapx_server_noop (CamelIMAPXServer *is, CamelFolder *folder, CamelException *ex)
4157
job = g_malloc0(sizeof(*job));
4158
job->type = IMAPX_JOB_NOOP;
4159
job->start = imapx_job_noop_start;
4160
job->folder = folder;
4162
job->pri = IMAPX_PRIORITY_NOOP;
4164
if (imapx_register_job (is, job))
4165
imapx_run_job(is, job);
4171
camel_imapx_server_refresh_info (CamelIMAPXServer *is, CamelFolder *folder, CamelException *ex)
4174
gboolean registered = TRUE;
4178
if (imapx_is_job_in_queue (is, folder->full_name, IMAPX_JOB_REFRESH_INFO, NULL)) {
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;
4188
job->op = camel_operation_registered ();
4189
job->u.refresh_info.changes = camel_folder_change_info_new();
4190
job->pri = IMAPX_PRIORITY_REFRESH_INFO;
4192
if (g_ascii_strcasecmp(folder->full_name, "INBOX") == 0)
4195
registered = imapx_register_job (is, job);
4200
imapx_run_job (is, job);
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);
4206
camel_folder_change_info_free(job->u.refresh_info.changes);
4209
camel_operation_unref (job->op);
4214
imapx_sync_free_user(GArray *user_set)
4218
if (user_set == NULL)
4221
for (i=0;i<user_set->len;i++) {
4222
GPtrArray *infos = g_array_index (user_set, struct _imapx_flag_change, i).infos;
4225
for (j = 0; j < infos->len; j++) {
4226
CamelMessageInfo *info = g_ptr_array_index (infos, j);
4227
camel_message_info_free (info);
4230
g_ptr_array_free(infos, TRUE);
4232
g_array_free(user_set, TRUE);
4236
imapx_server_sync_changes(CamelIMAPXServer *is, CamelFolder *folder, gint pri, CamelException *ex)
4238
guint i, on_orset, off_orset;
4240
GArray *on_user = NULL, *off_user = NULL;
4241
CamelIMAPXMessageInfo *info;
4243
gboolean registered;
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.
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.
4255
uids = camel_folder_summary_get_changed (folder->summary);
4257
if (uids->len == 0) {
4258
g_ptr_array_free (uids, TRUE);
4262
off_orset = on_orset = 0;
4263
for (i=0; i < uids->len; i++) {
4264
guint32 flags, sflags;
4265
CamelFlag *uflags, *suflags;
4268
info = (CamelIMAPXMessageInfo *) camel_folder_summary_uid (folder->summary, uids->pdata[i]);
4273
if (!(info->info.flags & CAMEL_MESSAGE_FOLDER_FLAGGED)) {
4274
camel_message_info_free (info);
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;
4285
uflags = ((CamelMessageInfoBase *)info)->user_flags;
4286
suflags = info->server_user_flags;
4287
while (uflags || suflags) {
4292
res = strcmp(uflags->name, suflags->name);
4293
else if (uflags->name && *uflags->name)
4296
uflags = uflags->next;
4304
uflags = uflags->next;
4305
suflags = suflags->next;
4308
CamelFlag *user_flag;
4309
struct _imapx_flag_change *change = NULL, add;
4312
if (on_user == NULL)
4313
on_user = g_array_new(FALSE, FALSE, sizeof(struct _imapx_flag_change));
4316
uflags = uflags->next;
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;
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)
4331
add.name = g_strdup(user_flag->name);
4332
add.infos = g_ptr_array_new();
4333
g_array_append_val(user_set, add);
4336
camel_message_info_ref (info);
4337
g_ptr_array_add(change->infos, info);
4340
camel_message_info_free (info);
4343
if ((on_orset|off_orset) == 0 && on_user == NULL && off_user == NULL)
4346
/* TODO above code should go into changes_start */
4350
if ((job = imapx_is_job_in_queue (is, folder->full_name, IMAPX_JOB_SYNC_CHANGES, NULL))) {
4358
job = g_malloc0(sizeof(*job));
4359
job->type = IMAPX_JOB_SYNC_CHANGES;
4360
job->start = imapx_job_sync_changes_start;
4362
job->folder = folder;
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;
4370
registered = imapx_register_job (is, job);
4375
imapx_run_job(is, job);
4380
imapx_sync_free_user(on_user);
4381
imapx_sync_free_user(off_user);
4383
camel_folder_free_uids (folder, uids);
4387
camel_imapx_server_sync_changes(CamelIMAPXServer *is, CamelFolder *folder, CamelException *ex)
4389
imapx_server_sync_changes (is, folder, IMAPX_PRIORITY_SYNC_CHANGES, ex);
4394
camel_imapx_server_expunge(CamelIMAPXServer *is, CamelFolder *folder, CamelException *ex)
4397
gboolean registered;
4399
/* Do we really care to wait for this one to finish? */
4402
if (imapx_is_job_in_queue (is, folder->full_name, IMAPX_JOB_EXPUNGE, NULL)) {
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;
4414
registered = imapx_register_job (is, job);
4419
imapx_run_job(is, job);
4425
imapx_name_hash(gconstpointer key)
4427
if (g_ascii_strcasecmp(key, "INBOX") == 0)
4428
return g_str_hash("INBOX");
4430
return g_str_hash(key);
4434
imapx_name_equal(gconstpointer a, gconstpointer b)
4436
gconstpointer aname = a, bname = b;
4438
if (g_ascii_strcasecmp(a, "INBOX") == 0)
4440
if (g_ascii_strcasecmp(b, "INBOX") == 0)
4442
return g_str_equal(aname, bname);
4446
imapx_list_flatten(gpointer k, gpointer v, gpointer d)
4448
GPtrArray *folders = d;
4450
g_ptr_array_add(folders, v);
4454
imapx_list_cmp(gconstpointer ap, gconstpointer bp)
4456
struct _list_info *a = ((struct _list_info **)ap)[0];
4457
struct _list_info *b = ((struct _list_info **)bp)[0];
4459
return strcmp(a->name, b->name);
4463
camel_imapx_server_list(CamelIMAPXServer *is, const gchar *top, guint32 flags, CamelException *ex)
4466
GPtrArray *folders = NULL;
4467
gchar *encoded_name;
4469
encoded_name = camel_utf8_utf7 (top);
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;
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);
4482
sprintf(job->u.list.pattern, "%s", encoded_name);
4484
/* sync operation which is triggered by user */
4485
if (flags & CAMEL_STORE_FOLDER_INFO_SUBSCRIPTION_LIST)
4488
if (imapx_register_job (is, job)) {
4489
imapx_run_job (is, job);
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);
4496
g_hash_table_destroy(job->u.list.folders);
4497
g_free (encoded_name);
4504
camel_imapx_server_manage_subscription (CamelIMAPXServer *is, const gchar *folder_name, gboolean subscribe, CamelException *ex)
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;
4513
job->u.manage_subscriptions.subscribe = subscribe;
4514
job->u.manage_subscriptions.folder_name = folder_name;
4516
if (imapx_register_job (is, job))
4517
imapx_run_job (is, job);
4523
camel_imapx_server_create_folder (CamelIMAPXServer *is, const gchar *folder_name, CamelException *ex)
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;
4532
job->u.folder_name = folder_name;
4534
if (imapx_register_job (is, job))
4535
imapx_run_job (is, job);
4541
camel_imapx_server_delete_folder (CamelIMAPXServer *is, const gchar *folder_name, CamelException *ex)
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;
4550
job->u.folder_name = folder_name;
4552
if (imapx_register_job (is, job))
4553
imapx_run_job (is, job);
4559
camel_imapx_server_rename_folder (CamelIMAPXServer *is, const gchar *old_name, const gchar *new_name, CamelException *ex)
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;
4568
job->u.rename_folder.ofolder_name = old_name;
4569
job->u.rename_folder.nfolder_name = new_name;
4571
if (imapx_register_job (is, job))
4572
imapx_run_job (is, job);