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

« back to all changes in this revision

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

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

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
 
 
2
#include <ctype.h>
 
3
#include <errno.h>
 
4
#include <string.h>
 
5
 
 
6
#include <camel/camel-folder-summary.h>
 
7
#include <camel/camel-store.h>
 
8
#include <camel/camel-utf8.h>
 
9
#include <camel/camel-string-utils.h>
 
10
 
 
11
#include "camel-imapx-folder.h"
 
12
#include "camel-imapx-stream.h"
 
13
#include "camel-imapx-summary.h"
 
14
#include "camel-imapx-store-summary.h"
 
15
#include "camel-imapx-utils.h"
 
16
#include "camel-imapx-exception.h"
 
17
#include "libedataserver/e-memory.h"
 
18
 
 
19
/* high-level parser state */
 
20
#define p(x)
 
21
/* debug */
 
22
#define d(x)
 
23
 
 
24
#include "camel-imapx-tokenise.h"
 
25
#define SUBFOLDER_DIR_NAME     "subfolders"
 
26
 
 
27
#ifdef __GNUC__
 
28
__inline
 
29
#endif
 
30
camel_imapx_id_t
 
31
imapx_tokenise (register const gchar *str, register guint len)
 
32
{
 
33
        struct _imapx_keyword *k = imapx_tokenise_struct(str, len);
 
34
 
 
35
        if (k)
 
36
                return k->id;
 
37
        return 0;
 
38
}
 
39
 
 
40
static void imapx_namespace_clear (CamelIMAPXStoreNamespace **ns);
 
41
static const gchar * rename_label_flag (const gchar *flag, gint len, gboolean server_to_evo);
 
42
static GPtrArray *imapx_parse_uids (CamelIMAPXStream *is, CamelException *ex);
 
43
 
 
44
/* flag table */
 
45
static struct {
 
46
        const gchar *name;
 
47
        guint32 flag;
 
48
} flag_table[] = {
 
49
        { "\\ANSWERED", CAMEL_MESSAGE_ANSWERED },
 
50
        { "\\DELETED", CAMEL_MESSAGE_DELETED },
 
51
        { "\\DRAFT", CAMEL_MESSAGE_DRAFT },
 
52
        { "\\FLAGGED", CAMEL_MESSAGE_FLAGGED },
 
53
        { "\\SEEN", CAMEL_MESSAGE_SEEN },
 
54
        { "\\RECENT", CAMEL_IMAPX_MESSAGE_RECENT },
 
55
        { "\\*", CAMEL_MESSAGE_USER }
 
56
};
 
57
 
 
58
/* utility functions
 
59
   shoudl this be part of imapx-driver? */
 
60
/* mabye this should be a stream op? */
 
61
void
 
62
imapx_parse_flags(CamelIMAPXStream *stream, guint32 *flagsp, CamelFlag **user_flagsp, CamelException *ex)
 
63
/* throws IO,PARSE exception */
 
64
{
 
65
        gint tok, i;
 
66
        guint len;
 
67
        guchar *token, *p, c;
 
68
        guint32 flags = 0;
 
69
 
 
70
        *flagsp = flags;
 
71
 
 
72
        tok = camel_imapx_stream_token(stream, &token, &len, ex);
 
73
        if (tok == '(') {
 
74
                do {
 
75
                        tok = camel_imapx_stream_token(stream, &token, &len, ex);
 
76
                        if (tok == IMAPX_TOK_TOKEN) {
 
77
                                p = token;
 
78
                                // FIXME: ascii_toupper
 
79
                                while ((c=*p))
 
80
                                        *p++ = toupper(c);
 
81
                                for (i = 0; i < G_N_ELEMENTS (flag_table); i++)
 
82
                                        if (!strcmp((gchar *)token, flag_table[i].name)) {
 
83
                                                flags |= flag_table[i].flag;
 
84
                                                goto found;
 
85
                                        }
 
86
                                if (user_flagsp) {
 
87
                                        const gchar *flag_name = rename_label_flag ((gchar *) token, strlen ((gchar *) token), TRUE);
 
88
 
 
89
                                        camel_flag_set(user_flagsp, flag_name, TRUE);
 
90
 
 
91
                                }
 
92
                        found:
 
93
                                tok = tok; /* fixes stupid warning */
 
94
                        } else if (tok != ')') {
 
95
                                camel_exception_set (ex, 1, "expecting flag");
 
96
                                return;
 
97
                        }
 
98
                } while (tok != ')');
 
99
        } else {
 
100
                camel_exception_set (ex, 1, "expecting flag list");
 
101
                return;
 
102
        }
 
103
 
 
104
        *flagsp = flags;
 
105
}
 
106
 
 
107
/*
 
108
 * rename_flag
 
109
 * Converts label flag name on server to name used in Evolution or back.
 
110
 * if the flags does not match returns the original one as it is.
 
111
 * It will never return NULL, it will return empty string, instead.
 
112
 *
 
113
 * @flag: Flag to rename.
 
114
 * @len: Length of the flag name.
 
115
 * @server_to_evo: if TRUE, then converting server names to evo's names, if FALSE then opposite.
 
116
 */
 
117
static const gchar *
 
118
rename_label_flag (const gchar *flag, gint len, gboolean server_to_evo)
 
119
{
 
120
        gint i;
 
121
        const gchar *labels[] = {
 
122
                "$Label1", "$Labelimportant",
 
123
                "$Label2", "$Labelwork",
 
124
                "$Label3", "$Labelpersonal",
 
125
                "$Label4", "$Labeltodo",
 
126
                "$Label5", "$Labellater",
 
127
                NULL,      NULL };
 
128
 
 
129
        /* It really can pass zero-length flags inside, in that case it was able
 
130
           to always add first label, which is definitely wrong. */
 
131
        if (!len || !flag || !*flag)
 
132
                return "";
 
133
 
 
134
        for (i = 0 + (server_to_evo ? 0 : 1); labels[i]; i = i + 2) {
 
135
                if (!g_ascii_strncasecmp (flag, labels[i], len))
 
136
                        return labels [i + (server_to_evo ? 1 : -1)];
 
137
        }
 
138
 
 
139
        return flag;
 
140
}
 
141
 
 
142
void
 
143
imapx_write_flags(CamelStream *stream, guint32 flags, CamelFlag *user_flags, CamelException *ex)
 
144
/* throws IO exception */
 
145
{
 
146
        gint i;
 
147
 
 
148
        if (camel_stream_write(stream, "(", 1) == -1) {
 
149
                camel_exception_setv (ex, 1, "io error: %s", strerror(errno));
 
150
                return;
 
151
        }
 
152
 
 
153
        for (i=0;flags!=0 && i< G_N_ELEMENTS (flag_table);i++) {
 
154
                if (flag_table[i].flag & flags) {
 
155
                        if (flags & CAMEL_IMAPX_MESSAGE_RECENT)
 
156
                                continue;
 
157
 
 
158
                        if (camel_stream_write (stream, flag_table[i].name, strlen(flag_table[i].name)) == -1) {
 
159
                                camel_exception_setv (ex,1, "io error: %s", strerror(errno));
 
160
                                return;
 
161
                        }
 
162
 
 
163
                        flags &= ~flag_table[i].flag;
 
164
                        if (flags != 0 && user_flags == NULL)
 
165
                                if (camel_stream_write(stream, " ", 1) == -1) {
 
166
                                        camel_exception_setv (ex, 1, "io error: %s", strerror(errno));
 
167
                                        return;
 
168
                                }
 
169
                }
 
170
        }
 
171
 
 
172
        while (user_flags) {
 
173
                const gchar *flag_name = rename_label_flag (user_flags->name, strlen (user_flags->name), FALSE);
 
174
 
 
175
                if (camel_stream_write(stream, flag_name, strlen (flag_name)) == -1) {
 
176
                        camel_exception_setv (ex, 1, "io error: %s", strerror(errno));
 
177
                        return;
 
178
                }
 
179
 
 
180
                if (user_flags->next && camel_stream_write(stream, " ", 1) == -1) {
 
181
                        camel_exception_setv (ex, 1, "io error: %s", strerror(errno));
 
182
                        return;
 
183
                }
 
184
 
 
185
                user_flags = user_flags->next;
 
186
        }
 
187
 
 
188
        if (camel_stream_write(stream, ")", 1) == -1) {
 
189
                camel_exception_setv (ex, 1, "io error: %s", strerror(errno));
 
190
                return;
 
191
        }
 
192
}
 
193
 
 
194
static gboolean
 
195
imapx_update_user_flags (CamelMessageInfo *info, CamelFlag *server_user_flags)
 
196
{
 
197
        gboolean changed = FALSE;
 
198
        CamelMessageInfoBase *binfo = (CamelMessageInfoBase *) info;
 
199
        gboolean set_cal = FALSE;
 
200
 
 
201
        if (camel_flag_get (&binfo->user_flags, "$has_cal"))
 
202
                set_cal = TRUE;
 
203
 
 
204
        changed = camel_flag_list_copy(&binfo->user_flags, &server_user_flags);
 
205
 
 
206
        /* reset the calendar flag if it was set in messageinfo before */
 
207
        if (set_cal)
 
208
                camel_flag_set (&binfo->user_flags, "$has_cal", TRUE);
 
209
 
 
210
        return changed;
 
211
}
 
212
 
 
213
gboolean
 
214
imapx_update_message_info_flags (CamelMessageInfo *info, guint32 server_flags, CamelFlag *server_user_flags, CamelFolder *folder)
 
215
{
 
216
        gboolean changed = FALSE;
 
217
        CamelIMAPXMessageInfo *xinfo = (CamelIMAPXMessageInfo *) info;
 
218
 
 
219
        if (server_flags != xinfo->server_flags)
 
220
        {
 
221
                guint32 server_set, server_cleared;
 
222
                gint read=0, deleted=0, junk=0;
 
223
 
 
224
                server_set = server_flags & ~xinfo->server_flags;
 
225
                server_cleared = xinfo->server_flags & ~server_flags;
 
226
 
 
227
                if (server_set & CAMEL_MESSAGE_SEEN)
 
228
                        read = 1;
 
229
                else if (server_cleared & CAMEL_MESSAGE_SEEN)
 
230
                        read = -1;
 
231
 
 
232
                if (server_set & CAMEL_MESSAGE_DELETED)
 
233
                        deleted = 1;
 
234
                else if (server_cleared & CAMEL_MESSAGE_DELETED)
 
235
                        deleted = -1;
 
236
 
 
237
                if (server_set & CAMEL_MESSAGE_JUNK)
 
238
                        junk = 1;
 
239
                else if (server_cleared & CAMEL_MESSAGE_JUNK)
 
240
                        junk = -1;
 
241
 
 
242
                d(printf("%s %s %s %s\n", xinfo->info.uid, read == 1 ? "read" : ( read == -1 ? "unread" : ""),
 
243
                                        deleted == 1 ? "deleted" : ( deleted == -1 ? "undeleted" : ""),
 
244
                                        junk == 1 ? "junk" : ( junk == -1 ? "unjunked" : "")));
 
245
 
 
246
                if (read)
 
247
                        folder->summary->unread_count -= read;
 
248
                if (deleted)
 
249
                        folder->summary->deleted_count += deleted;
 
250
                if (junk)
 
251
                        folder->summary->junk_count += junk;
 
252
                if (junk && !deleted)
 
253
                        folder->summary->junk_not_deleted_count += junk;
 
254
                if (junk ||  deleted)
 
255
                        folder->summary->visible_count -= junk ? junk : deleted;
 
256
 
 
257
                xinfo->info.flags = (xinfo->info.flags | server_set) & ~server_cleared;
 
258
                xinfo->server_flags = server_flags;
 
259
                xinfo->info.dirty = TRUE;
 
260
                if (info->summary)
 
261
                        camel_folder_summary_touch (info->summary);
 
262
                changed = TRUE;
 
263
        }
 
264
 
 
265
        if ((folder->permanent_flags & CAMEL_MESSAGE_USER) != 0 && imapx_update_user_flags (info, server_user_flags))
 
266
                changed = TRUE;
 
267
 
 
268
        return changed;
 
269
}
 
270
 
 
271
void
 
272
imapx_set_message_info_flags_for_new_message (CamelMessageInfo *info, guint32 server_flags, CamelFlag *server_user_flags, CamelFolder *folder)
 
273
{
 
274
        CamelMessageInfoBase *binfo = (CamelMessageInfoBase *) info;
 
275
        CamelIMAPXMessageInfo *xinfo = (CamelIMAPXMessageInfo *) info;
 
276
        gint unread=0, deleted=0, junk=0;
 
277
        guint32 flags;
 
278
 
 
279
        binfo->flags |= server_flags;
 
280
        xinfo->server_flags = server_flags;
 
281
 
 
282
        if (folder->permanent_flags & CAMEL_MESSAGE_USER)
 
283
                imapx_update_user_flags (info, server_user_flags);
 
284
 
 
285
        /* update the summary count */
 
286
        flags = binfo->flags;
 
287
 
 
288
        if (!(flags & CAMEL_MESSAGE_SEEN))
 
289
                unread = 1;
 
290
 
 
291
        if (flags & CAMEL_MESSAGE_DELETED)
 
292
                deleted = 1;
 
293
 
 
294
        if (flags & CAMEL_MESSAGE_JUNK)
 
295
                junk = 1;
 
296
 
 
297
        if (folder->summary) {
 
298
 
 
299
                if (unread)
 
300
                        folder->summary->unread_count += unread;
 
301
                if (deleted)
 
302
                        folder->summary->deleted_count += deleted;
 
303
                if (junk)
 
304
                        folder->summary->junk_count += junk;
 
305
                if (junk && !deleted)
 
306
                        folder->summary->junk_not_deleted_count += junk;
 
307
                folder->summary->visible_count++;
 
308
                if (junk ||  deleted)
 
309
                        folder->summary->visible_count -= junk ? junk : deleted;
 
310
 
 
311
                folder->summary->saved_count++;
 
312
                camel_folder_summary_touch(folder->summary);
 
313
        }
 
314
 
 
315
        binfo->flags &= ~CAMEL_MESSAGE_FOLDER_FLAGGED;
 
316
}
 
317
 
 
318
void
 
319
imapx_update_summary_for_removed_message (CamelMessageInfo *info, CamelFolder *folder)
 
320
{
 
321
        CamelMessageInfoBase *dinfo = (CamelMessageInfoBase *) info;
 
322
        gint unread=0, deleted=0, junk=0;
 
323
        guint32 flags;
 
324
 
 
325
        flags = dinfo->flags;
 
326
        if (!(flags & CAMEL_MESSAGE_SEEN))
 
327
                unread = 1;
 
328
 
 
329
        if (flags & CAMEL_MESSAGE_DELETED)
 
330
                deleted = 1;
 
331
 
 
332
        if (flags & CAMEL_MESSAGE_JUNK)
 
333
                junk = 1;
 
334
 
 
335
        if (unread)
 
336
                folder->summary->unread_count--;
 
337
 
 
338
        if (deleted)
 
339
                folder->summary->deleted_count--;
 
340
        if (junk)
 
341
                folder->summary->junk_count--;
 
342
 
 
343
        if (junk && !deleted)
 
344
                folder->summary->junk_not_deleted_count--;
 
345
 
 
346
        if (!junk &&  !deleted)
 
347
                folder->summary->visible_count--;
 
348
 
 
349
        folder->summary->saved_count--;
 
350
}
 
351
 
 
352
void
 
353
imapx_update_store_summary (CamelFolder *folder)
 
354
{
 
355
        CamelStoreInfo *si;
 
356
 
 
357
        si = camel_store_summary_path ((CamelStoreSummary *) ((CamelIMAPXStore *) folder->parent_store)->summary, folder->full_name);
 
358
        if (si) {
 
359
                guint32 unread, total;
 
360
 
 
361
                camel_object_get(folder, NULL, CAMEL_FOLDER_TOTAL, &total, CAMEL_FOLDER_UNREAD, &unread, NULL);
 
362
                if (si->unread != unread || si->total != total) {
 
363
                        si->unread = unread;
 
364
                        si->total = total;
 
365
 
 
366
                        camel_store_summary_touch ((CamelStoreSummary *)((CamelIMAPXStore *) folder->parent_store)->summary);
 
367
                        camel_store_summary_save ((CamelStoreSummary *)((CamelIMAPXStore *) folder->parent_store)->summary);
 
368
                }
 
369
        }
 
370
}
 
371
 
 
372
/*
 
373
capability_data ::= "CAPABILITY" SPACE [1#capability SPACE] "IMAP4rev1"
 
374
                    [SPACE 1#capability]
 
375
                    ;; IMAP4rev1 servers which offer RFC 1730
 
376
                    ;; compatibility MUST list "IMAP4" as the first
 
377
                    ;; capability.
 
378
*/
 
379
 
 
380
struct {
 
381
        const gchar *name;
 
382
        guint32 flag;
 
383
} capa_table[] = {
 
384
        { "IMAP4", IMAPX_CAPABILITY_IMAP4 },
 
385
        { "IMAP4REV1", IMAPX_CAPABILITY_IMAP4REV1 },
 
386
        { "STATUS",  IMAPX_CAPABILITY_STATUS } ,
 
387
        { "NAMESPACE", IMAPX_CAPABILITY_NAMESPACE },
 
388
        { "UIDPLUS",  IMAPX_CAPABILITY_UIDPLUS },
 
389
        { "LITERAL+", IMAPX_CAPABILITY_LITERALPLUS },
 
390
        { "STARTTLS", IMAPX_CAPABILITY_STARTTLS },
 
391
        { "IDLE", IMAPX_CAPABILITY_IDLE },
 
392
};
 
393
 
 
394
struct _capability_info *
 
395
imapx_parse_capability(CamelIMAPXStream *stream, CamelException *ex)
 
396
{
 
397
        gint tok, i;
 
398
        guint len;
 
399
        guchar *token, *p, c;
 
400
        gboolean free_token = FALSE;
 
401
        struct _capability_info * cinfo;
 
402
 
 
403
        cinfo = g_malloc0(sizeof(*cinfo));
 
404
        cinfo->auth_types = g_hash_table_new_full (g_str_hash, g_str_equal, (GDestroyNotify) g_free, NULL);
 
405
 
 
406
        /* FIXME: handle auth types */
 
407
        while (!camel_exception_is_set (ex) && (tok = camel_imapx_stream_token(stream, &token, &len, ex)) != '\n') {
 
408
                switch (tok) {
 
409
                        case 43:
 
410
                                token = (guchar *) g_strconcat ((gchar *)token, "+", NULL);
 
411
                                free_token = TRUE;
 
412
                        case IMAPX_TOK_TOKEN:
 
413
                        case IMAPX_TOK_STRING:
 
414
                                p = token;
 
415
                                while ((c = *p))
 
416
                                        *p++ = toupper(c);
 
417
                                if (!strncmp ((gchar *) token, "AUTH=", 5)) {
 
418
                                        g_hash_table_insert (cinfo->auth_types,
 
419
                                                        g_strdup ((gchar *)token + 5),
 
420
                                                        GINT_TO_POINTER (1));
 
421
                                        break;
 
422
                                }
 
423
                        case IMAPX_TOK_INT:
 
424
                                d(printf(" cap: '%s'\n", token));
 
425
                                for (i = 0; i < G_N_ELEMENTS (capa_table); i++)
 
426
                                        if (!strcmp((gchar *) token, capa_table[i].name))
 
427
                                                cinfo->capa |= capa_table[i].flag;
 
428
                                if (free_token) {
 
429
                                        g_free (token);
 
430
                                        token = NULL;
 
431
                                }
 
432
                                free_token = FALSE;
 
433
                                break;
 
434
                        default:
 
435
                                camel_exception_set (ex, 1, "capability: expecting name");
 
436
                                break;
 
437
                }
 
438
        }
 
439
 
 
440
        if (camel_exception_is_set (ex)) {
 
441
                imapx_free_capability(cinfo);
 
442
                cinfo = NULL;
 
443
        }
 
444
 
 
445
        return cinfo;
 
446
}
 
447
 
 
448
void imapx_free_capability(struct _capability_info *cinfo)
 
449
{
 
450
        g_hash_table_destroy (cinfo->auth_types);
 
451
        g_free(cinfo);
 
452
}
 
453
 
 
454
struct _CamelIMAPXNamespaceList *
 
455
imapx_parse_namespace_list (CamelIMAPXStream *stream, CamelException *ex)
 
456
{
 
457
        CamelIMAPXStoreNamespace *namespaces[3], *node, *tail;
 
458
        CamelIMAPXNamespaceList *nsl = NULL;
 
459
        gint tok, i;
 
460
        guint len;
 
461
        guchar *token;
 
462
        gint n = 0;
 
463
 
 
464
        nsl = g_malloc0(sizeof(CamelIMAPXNamespaceList));
 
465
        nsl->personal = NULL;
 
466
        nsl->shared = NULL;
 
467
        nsl->other = NULL;
 
468
 
 
469
        tok = camel_imapx_stream_token (stream, &token, &len, ex);
 
470
        do {
 
471
                namespaces[n] = NULL;
 
472
                tail = (CamelIMAPXStoreNamespace *) &namespaces[n];
 
473
 
 
474
                if (tok == '(') {
 
475
                        tok = camel_imapx_stream_token (stream, &token, &len, ex);
 
476
 
 
477
                        while (tok == '(') {
 
478
                                tok = camel_imapx_stream_token (stream, &token, &len, ex);
 
479
                                if (tok != IMAPX_TOK_STRING) {
 
480
                                        camel_exception_set (ex, 1, "namespace: expected a string path name");
 
481
                                        goto exception;
 
482
                                }
 
483
 
 
484
                                node = g_new0 (CamelIMAPXStoreNamespace, 1);
 
485
                                node->next = NULL;
 
486
                                node->path = g_strdup ((gchar *) token);
 
487
 
 
488
                                tok = camel_imapx_stream_token (stream, &token, &len, ex);
 
489
 
 
490
                                if (tok == IMAPX_TOK_STRING) {
 
491
                                        if (strlen ((gchar *) token) == 1) {
 
492
                                                node->sep = *token;
 
493
                                        } else {
 
494
                                                if (*token)
 
495
                                                        node->sep = node->path [strlen (node->path) - 1];
 
496
                                                else
 
497
                                                        node->sep = '\0';
 
498
                                        }
 
499
                                } else if (tok == IMAPX_TOK_TOKEN) {
 
500
                                        /* will a NIL be possible here? */
 
501
                                        node->sep = '\0';
 
502
                                } else {
 
503
                                        camel_exception_set (ex, 1, "namespace: expected a string separator");
 
504
                                        g_free (node->path);
 
505
                                        g_free (node);
 
506
                                        goto exception;
 
507
                                }
 
508
 
 
509
                                tail->next = node;
 
510
                                tail = node;
 
511
 
 
512
                                if (*node->path && node->path [strlen (node->path) -1] == node->sep)
 
513
                                        node->path [strlen (node->path) - 1] = '\0';
 
514
 
 
515
                                if (!g_ascii_strncasecmp (node->path, "INBOX", 5) &&
 
516
                                                (node->path [6] == '\0' || node->path [6] == node->sep ))
 
517
                                        memcpy (node->path, "INBOX", 5);
 
518
                                
 
519
                                /* TODO remove full_name later. not required */
 
520
                                node->full_name = g_strdup (node->path);
 
521
 
 
522
                                tok = camel_imapx_stream_token (stream, &token, &len, ex);
 
523
                                if (tok != ')') {
 
524
                                        camel_exception_set (ex, 1, "namespace: expected a ')'");
 
525
                                        goto exception;
 
526
                                }
 
527
 
 
528
                                tok = camel_imapx_stream_token (stream, &token, &len, ex);
 
529
                        }
 
530
 
 
531
                        if (tok != ')') {
 
532
                                camel_exception_set (ex, 1, "namespace: expected a ')'");
 
533
                                goto exception;
 
534
                        }
 
535
 
 
536
                } else if (tok == IMAPX_TOK_TOKEN && !strcmp ((gchar *) token, "NIL")) {
 
537
                        namespaces [n] = NULL;
 
538
                } else {
 
539
                        camel_exception_set (ex, 1, "namespace: expected either a '(' or NIL");
 
540
                        goto exception;
 
541
                }
 
542
 
 
543
                tok = camel_imapx_stream_token (stream, &token, &len, ex);
 
544
                n++;
 
545
        } while (n < 3);
 
546
 
 
547
        nsl->personal = namespaces [0];
 
548
        nsl->shared = namespaces [1];
 
549
        nsl->other = namespaces [2];
 
550
 
 
551
        return nsl;
 
552
exception:
 
553
        g_free (nsl);
 
554
        for (i=0; i < 3; i++)
 
555
                imapx_namespace_clear (&namespaces [i]);
 
556
 
 
557
        return NULL;
 
558
}
 
559
 
 
560
/*
 
561
body            ::= "(" body_type_1part / body_type_mpart ")"
 
562
 
 
563
body_extension  ::= nstring / number / "(" 1#body_extension ")"
 
564
                    ;; Future expansion.  Client implementations
 
565
                    ;; MUST accept body_extension fields.  Server
 
566
                    ;; implementations MUST NOT generate
 
567
                    ;; body_extension fields except as defined by
 
568
                    ;; future standard or standards-track
 
569
                    ;; revisions of this specification.
 
570
 
 
571
body_ext_1part  ::= body_fld_md5 [SPACE body_fld_dsp
 
572
                    [SPACE body_fld_lang
 
573
                    [SPACE 1#body_extension]]]
 
574
                    ;; MUST NOT be returned on non-extensible
 
575
                    ;; "BODY" fetch
 
576
 
 
577
body_ext_mpart  ::= body_fld_param
 
578
                    [SPACE body_fld_dsp SPACE body_fld_lang
 
579
                    [SPACE 1#body_extension]]
 
580
                    ;; MUST NOT be returned on non-extensible
 
581
                    ;; "BODY" fetch
 
582
 
 
583
body_fields     ::= body_fld_param SPACE body_fld_id SPACE
 
584
                    body_fld_desc SPACE body_fld_enc SPACE
 
585
                    body_fld_octets
 
586
 
 
587
body_fld_desc   ::= nstring
 
588
 
 
589
body_fld_dsp    ::= "(" string SPACE body_fld_param ")" / nil
 
590
 
 
591
body_fld_enc    ::= (<"> ("7BIT" / "8BIT" / "BINARY" / "BASE64"/
 
592
                    "QUOTED-PRINTABLE") <">) / string
 
593
 
 
594
body_fld_id     ::= nstring
 
595
 
 
596
body_fld_lang   ::= nstring / "(" 1#string ")"
 
597
 
 
598
body_fld_lines  ::= number
 
599
 
 
600
body_fld_md5    ::= nstring
 
601
 
 
602
body_fld_octets ::= number
 
603
 
 
604
body_fld_param  ::= "(" 1#(string SPACE string) ")" / nil
 
605
 
 
606
body_type_1part ::= (body_type_basic / body_type_msg / body_type_text)
 
607
                    [SPACE body_ext_1part]
 
608
 
 
609
body_type_basic ::= media_basic SPACE body_fields
 
610
                    ;; MESSAGE subtype MUST NOT be "RFC822"
 
611
 
 
612
body_type_mpart ::= 1*body SPACE media_subtype
 
613
                    [SPACE body_ext_mpart]
 
614
 
 
615
body_type_msg   ::= media_message SPACE body_fields SPACE envelope
 
616
                    SPACE body SPACE body_fld_lines
 
617
 
 
618
body_type_text  ::= media_text SPACE body_fields SPACE body_fld_lines
 
619
 
 
620
envelope        ::= "(" env_date SPACE env_subject SPACE env_from
 
621
                    SPACE env_sender SPACE env_reply_to SPACE env_to
 
622
                    SPACE env_cc SPACE env_bcc SPACE env_in_reply_to
 
623
                    SPACE env_message_id ")"
 
624
 
 
625
env_bcc         ::= "(" 1*address ")" / nil
 
626
 
 
627
env_cc          ::= "(" 1*address ")" / nil
 
628
 
 
629
env_date        ::= nstring
 
630
 
 
631
env_from        ::= "(" 1*address ")" / nil
 
632
 
 
633
env_in_reply_to ::= nstring
 
634
 
 
635
env_message_id  ::= nstring
 
636
 
 
637
env_reply_to    ::= "(" 1*address ")" / nil
 
638
 
 
639
env_sender      ::= "(" 1*address ")" / nil
 
640
 
 
641
env_subject     ::= nstring
 
642
 
 
643
env_to          ::= "(" 1*address ")" / nil
 
644
 
 
645
media_basic     ::= (<"> ("APPLICATION" / "AUDIO" / "IMAGE" /
 
646
                    "MESSAGE" / "VIDEO") <">) / string)
 
647
                    SPACE media_subtype
 
648
                    ;; Defined in [MIME-IMT]
 
649
 
 
650
media_message   ::= <"> "MESSAGE" <"> SPACE <"> "RFC822" <">
 
651
                    ;; Defined in [MIME-IMT]
 
652
 
 
653
media_subtype   ::= string
 
654
                    ;; Defined in [MIME-IMT]
 
655
 
 
656
media_text      ::= <"> "TEXT" <"> SPACE media_subtype
 
657
                    ;; Defined in [MIME-IMT]
 
658
 
 
659
 ( "type" "subtype"  body_fields [envelope body body_fld_lines]
 
660
                                 [body_fld_lines]
 
661
 
 
662
 (("TEXT" "PLAIN" ("CHARSET"
 
663
                     "US-ASCII") NIL NIL "7BIT" 1152 23)("TEXT" "PLAIN"
 
664
                     ("CHARSET" "US-ASCII" "NAME" "cc.diff")
 
665
                     "<960723163407.20117h@cac.washington.edu>"
 
666
                     "Compiler diff" "BASE64" 4554 73) "MIXED"))
 
667
 
 
668
*/
 
669
 
 
670
/*
 
671
struct _body_fields {
 
672
        CamelContentType *ct;
 
673
        gchar *msgid, *desc;
 
674
        CamelTransferEncoding encoding;
 
675
        guint32 size;
 
676
        };*/
 
677
 
 
678
void
 
679
imapx_free_body(struct _CamelMessageContentInfo *cinfo)
 
680
{
 
681
        struct _CamelMessageContentInfo *list, *next;
 
682
 
 
683
        list = cinfo->childs;
 
684
        while (list) {
 
685
                next = list->next;
 
686
                imapx_free_body(list);
 
687
                list = next;
 
688
        }
 
689
 
 
690
        if (cinfo->type)
 
691
                camel_content_type_unref(cinfo->type);
 
692
        g_free(cinfo->id);
 
693
        g_free(cinfo->description);
 
694
        g_free(cinfo->encoding);
 
695
        g_free(cinfo);
 
696
}
 
697
 
 
698
void
 
699
imapx_parse_param_list(CamelIMAPXStream *is, struct _camel_header_param **plist, CamelException *ex)
 
700
{
 
701
        gint tok;
 
702
        guint len;
 
703
        guchar *token;
 
704
        gchar *param;
 
705
 
 
706
        p(printf("body_fld_param\n"));
 
707
 
 
708
        /* body_fld_param  ::= "(" 1#(string SPACE string) ")" / nil */
 
709
        tok = camel_imapx_stream_token(is, &token, &len, ex);
 
710
        if (tok == '(') {
 
711
                while (1) {
 
712
                        tok = camel_imapx_stream_token(is, &token, &len, ex);
 
713
                        if (tok == ')')
 
714
                                break;
 
715
                        camel_imapx_stream_ungettoken(is, tok, token, len);
 
716
 
 
717
                        camel_imapx_stream_astring(is, &token, ex);
 
718
                        param = alloca (strlen ((gchar *) token)+1);
 
719
                        strcpy(param, (gchar *) token);
 
720
                        camel_imapx_stream_astring(is, &token, ex);
 
721
                        camel_header_set_param(plist, param, (gchar *) token);
 
722
                }
 
723
        } /* else check nil?  no need */
 
724
}
 
725
 
 
726
struct _CamelContentDisposition *
 
727
imapx_parse_ext_optional(CamelIMAPXStream *is, CamelException *ex)
 
728
{
 
729
        gint tok;
 
730
        guint len;
 
731
        guchar *token;
 
732
        struct _CamelContentDisposition *dinfo = NULL;
 
733
 
 
734
        /* this parses both extension types, from the body_fld_dsp onwards */
 
735
        /* although the grammars are different, they can be parsed the same way */
 
736
 
 
737
        /* body_ext_1part  ::= body_fld_md5 [SPACE body_fld_dsp
 
738
           [SPACE body_fld_lang
 
739
           [SPACE 1#body_extension]]]
 
740
           ;; MUST NOT be returned on non-extensible
 
741
           ;; "BODY" fetch */
 
742
 
 
743
        /* body_ext_mpart  ::= body_fld_param
 
744
           [SPACE body_fld_dsp SPACE body_fld_lang
 
745
           [SPACE 1#body_extension]]
 
746
           ;; MUST NOT be returned on non-extensible
 
747
           ;; "BODY" fetch */
 
748
 
 
749
        /* body_fld_dsp    ::= "(" string SPACE body_fld_param ")" / nil */
 
750
 
 
751
        tok = camel_imapx_stream_token(is, &token, &len, ex);
 
752
        switch (tok) {
 
753
                case '(':
 
754
                        dinfo = g_malloc0(sizeof(*dinfo));
 
755
                        dinfo->refcount = 1;
 
756
                        /* should be string */
 
757
                        camel_imapx_stream_astring(is, &token, ex);
 
758
 
 
759
                        dinfo->disposition = g_strdup((gchar *) token);
 
760
                        imapx_parse_param_list(is, &dinfo->params, ex);
 
761
                case IMAPX_TOK_TOKEN:
 
762
                        d(printf("body_fld_dsp: NIL\n"));
 
763
                        break;
 
764
                default:
 
765
                        camel_exception_set (ex, 1, "body_fld_disp: expecting nil or list");
 
766
                        return NULL;
 
767
        }
 
768
 
 
769
        p(printf("body_fld_lang\n"));
 
770
 
 
771
        /* body_fld_lang   ::= nstring / "(" 1#string ")" */
 
772
 
 
773
        /* we just drop the lang string/list, save it somewhere? */
 
774
 
 
775
        tok = camel_imapx_stream_token(is, &token, &len, ex);
 
776
        switch (tok) {
 
777
                case '(':
 
778
                        while (1) {
 
779
                                tok = camel_imapx_stream_token(is, &token, &len, ex);
 
780
                                if (tok == ')') {
 
781
                                        break;
 
782
                                } else if (tok != IMAPX_TOK_STRING) {
 
783
                                        camel_exception_set (ex, 1, "expecting string");
 
784
                                        break;
 
785
                                }
 
786
                        }
 
787
                        break;
 
788
                case IMAPX_TOK_TOKEN:
 
789
                        d(printf("body_fld_lang = nil\n"));
 
790
                        /* treat as 'nil' */
 
791
                        break;
 
792
                case IMAPX_TOK_STRING:
 
793
                        /* we have a string */
 
794
                        break;
 
795
                case IMAPX_TOK_LITERAL:
 
796
                        /* we have a literal string */
 
797
                        camel_imapx_stream_set_literal(is, len);
 
798
                        while ((tok = camel_imapx_stream_getl(is, &token, &len)) > 0) {
 
799
                                d(printf("Skip literal data '%.*s'\n", (gint)len, token));
 
800
                        }
 
801
                        break;
 
802
 
 
803
        }
 
804
 
 
805
        if camel_exception_is_set (ex) {
 
806
                if (dinfo)
 
807
                        camel_content_disposition_unref(dinfo);
 
808
        }
 
809
 
 
810
        return dinfo;
 
811
}
 
812
 
 
813
struct _CamelMessageContentInfo *
 
814
imapx_parse_body_fields(CamelIMAPXStream *is, CamelException *ex)
 
815
{
 
816
        guchar *token;
 
817
        gchar  *type;
 
818
        struct _CamelMessageContentInfo *cinfo;
 
819
 
 
820
        /* body_fields     ::= body_fld_param SPACE body_fld_id SPACE
 
821
           body_fld_desc SPACE body_fld_enc SPACE
 
822
           body_fld_octets */
 
823
 
 
824
        p(printf("body_fields\n"));
 
825
 
 
826
        cinfo = g_malloc0(sizeof(*cinfo));
 
827
 
 
828
        /* this should be string not astring */
 
829
        camel_imapx_stream_astring(is, &token, ex);
 
830
        if (camel_exception_is_set (ex))
 
831
                goto error;
 
832
        type = alloca(strlen( (gchar *) token)+1);
 
833
        strcpy(type, (gchar *) token);
 
834
        camel_imapx_stream_astring(is, &token, ex);
 
835
        if (camel_exception_is_set (ex))
 
836
                goto error;
 
837
        cinfo->type = camel_content_type_new(type, (gchar *) token);
 
838
        imapx_parse_param_list(is, &cinfo->type->params, ex);
 
839
        if (camel_exception_is_set (ex))
 
840
                goto error;
 
841
 
 
842
        /* body_fld_id     ::= nstring */
 
843
        camel_imapx_stream_nstring(is, &token, ex);
 
844
        if (camel_exception_is_set (ex))
 
845
                goto error;
 
846
        cinfo->id = g_strdup((gchar *) token);
 
847
 
 
848
        /* body_fld_desc   ::= nstring */
 
849
        camel_imapx_stream_nstring(is, &token, ex);
 
850
        if (camel_exception_is_set (ex))
 
851
                goto error;
 
852
        cinfo->description = g_strdup((gchar *) token);
 
853
 
 
854
        /* body_fld_enc    ::= (<"> ("7BIT" / "8BIT" / "BINARY" / "BASE64"/
 
855
           "QUOTED-PRINTABLE") <">) / string */
 
856
        camel_imapx_stream_astring(is, &token, ex);
 
857
        if (camel_exception_is_set (ex))
 
858
                goto error;
 
859
        cinfo->encoding = g_strdup((gchar *) token);
 
860
 
 
861
        /* body_fld_octets ::= number */
 
862
        cinfo->size = camel_imapx_stream_number(is, ex);
 
863
        if (camel_exception_is_set (ex))
 
864
                goto error;
 
865
 
 
866
        return cinfo;
 
867
error:
 
868
        imapx_free_body(cinfo);
 
869
        return cinfo;
 
870
}
 
871
 
 
872
struct _camel_header_address *
 
873
imapx_parse_address_list(CamelIMAPXStream *is, CamelException *ex)
 
874
/* throws PARSE,IO exception */
 
875
{
 
876
        gint tok;
 
877
        guint len;
 
878
        guchar *token, *host;
 
879
        gchar *mbox;
 
880
        struct _camel_header_address *list = NULL;
 
881
 
 
882
        /* "(" 1*address ")" / nil */
 
883
 
 
884
        tok = camel_imapx_stream_token(is, &token, &len, ex);
 
885
        if (tok == '(') {
 
886
                while (1) {
 
887
                        struct _camel_header_address *addr, *group = NULL;
 
888
 
 
889
                        /* address         ::= "(" addr_name SPACE addr_adl SPACE addr_mailbox
 
890
                           SPACE addr_host ")" */
 
891
                        tok = camel_imapx_stream_token(is, &token, &len, ex);
 
892
                        if (tok == ')')
 
893
                                break;
 
894
                        if (tok != '(') {
 
895
                                camel_header_address_list_clear(&list);
 
896
                                camel_exception_set (ex, 1, "missing '(' for address");
 
897
                                return NULL;
 
898
                        }
 
899
 
 
900
                        addr = camel_header_address_new();
 
901
                        addr->type = CAMEL_HEADER_ADDRESS_NAME;
 
902
                        tok = camel_imapx_stream_nstring(is, &token, ex);
 
903
                        addr->name = g_strdup((gchar *) token);
 
904
                        /* we ignore the route, nobody uses it in the real world */
 
905
                        tok = camel_imapx_stream_nstring(is, &token, ex);
 
906
 
 
907
                        /* [RFC-822] group syntax is indicated by a special
 
908
                           form of address structure in which the host name
 
909
                           field is NIL.  If the mailbox name field is also
 
910
                           NIL, this is an end of group marker (semi-colon in
 
911
                           RFC 822 syntax).  If the mailbox name field is
 
912
                           non-NIL, this is a start of group marker, and the
 
913
                           mailbox name field holds the group name phrase. */
 
914
 
 
915
                        tok = camel_imapx_stream_nstring(is,(guchar **) &mbox, ex);
 
916
                        mbox = g_strdup(mbox);
 
917
                        tok = camel_imapx_stream_nstring(is, &host, ex);
 
918
                        if (host == NULL) {
 
919
                                if (mbox == NULL) {
 
920
                                        group = NULL;
 
921
                                } else {
 
922
                                        d(printf("adding group '%s'\n", mbox));
 
923
                                        g_free(addr->name);
 
924
                                        addr->name = mbox;
 
925
                                        addr->type = CAMEL_HEADER_ADDRESS_GROUP;
 
926
                                        camel_header_address_list_append(&list, addr);
 
927
                                        group = addr;
 
928
                                }
 
929
                        } else {
 
930
                                addr->v.addr = g_strdup_printf("%s%s%s", mbox? mbox:"", host?"@":"", host?(gchar *)host:"");
 
931
                                g_free(mbox);
 
932
                                d(printf("adding address '%s'\n", addr->v.addr));
 
933
                                if (group != NULL)
 
934
                                        camel_header_address_add_member(group, addr);
 
935
                                else
 
936
                                        camel_header_address_list_append(&list, addr);
 
937
                        }
 
938
                        do {
 
939
                                tok = camel_imapx_stream_token(is, &token, &len, ex);
 
940
                        } while (tok != ')');
 
941
                }
 
942
        } else {
 
943
                d(printf("empty, nil '%s'\n", token));
 
944
        }
 
945
 
 
946
        /* CHEN TODO handle exception at required places */
 
947
 
 
948
        return list;
 
949
}
 
950
 
 
951
struct _CamelMessageInfo *
 
952
imapx_parse_envelope(CamelIMAPXStream *is, CamelException *ex)
 
953
{
 
954
        gint tok;
 
955
        guint len;
 
956
        guchar *token;
 
957
        struct _camel_header_address *addr, *addr_from;
 
958
        gchar *addrstr;
 
959
        struct _CamelMessageInfoBase *minfo;
 
960
 
 
961
        /* envelope        ::= "(" env_date SPACE env_subject SPACE env_from
 
962
           SPACE env_sender SPACE env_reply_to SPACE env_to
 
963
           SPACE env_cc SPACE env_bcc SPACE env_in_reply_to
 
964
           SPACE env_message_id ")" */
 
965
 
 
966
        p(printf("envelope\n"));
 
967
 
 
968
        minfo = (CamelMessageInfoBase *)camel_message_info_new(NULL);
 
969
 
 
970
        tok = camel_imapx_stream_token(is, &token, &len, ex);
 
971
        if (tok != '(') {
 
972
                camel_message_info_free(minfo);
 
973
                camel_exception_set (ex, 1, "envelope: expecting '('");
 
974
                return NULL;
 
975
        }
 
976
 
 
977
        /* env_date        ::= nstring */
 
978
        camel_imapx_stream_nstring(is, &token, ex);
 
979
        minfo->date_sent = camel_header_decode_date((gchar *) token, NULL);
 
980
 
 
981
        /* env_subject     ::= nstring */
 
982
        tok = camel_imapx_stream_nstring(is, &token, ex);
 
983
        minfo->subject = camel_pstring_strdup((gchar *) token);
 
984
 
 
985
        /* we merge from/sender into from, append should probably merge more smartly? */
 
986
 
 
987
        /* env_from        ::= "(" 1*address ")" / nil */
 
988
        addr_from = imapx_parse_address_list(is, ex);
 
989
 
 
990
        /* env_sender      ::= "(" 1*address ")" / nil */
 
991
        addr = imapx_parse_address_list(is, ex);
 
992
        if (addr_from) {
 
993
                camel_header_address_list_clear(&addr);
 
994
#if 0
 
995
                if (addr)
 
996
                        camel_header_address_list_append_list(&addr_from, &addr);
 
997
#endif
 
998
        } else {
 
999
                if (addr)
 
1000
                        addr_from = addr;
 
1001
        }
 
1002
 
 
1003
        if (addr_from) {
 
1004
                addrstr = camel_header_address_list_format(addr_from);
 
1005
                minfo->from = camel_pstring_strdup(addrstr);
 
1006
                g_free(addrstr);
 
1007
                camel_header_address_list_clear(&addr_from);
 
1008
        }
 
1009
 
 
1010
        /* we dont keep reply_to */
 
1011
 
 
1012
        /* env_reply_to    ::= "(" 1*address ")" / nil */
 
1013
        addr = imapx_parse_address_list(is, ex);
 
1014
        camel_header_address_list_clear(&addr);
 
1015
 
 
1016
        /* env_to          ::= "(" 1*address ")" / nil */
 
1017
        addr = imapx_parse_address_list(is, ex);
 
1018
        if (addr) {
 
1019
                addrstr = camel_header_address_list_format(addr);
 
1020
                minfo->to = camel_pstring_strdup(addrstr);
 
1021
                g_free(addrstr);
 
1022
                camel_header_address_list_clear(&addr);
 
1023
        }
 
1024
 
 
1025
        /* env_cc          ::= "(" 1*address ")" / nil */
 
1026
        addr = imapx_parse_address_list(is, ex);
 
1027
        if (addr) {
 
1028
                addrstr = camel_header_address_list_format(addr);
 
1029
                minfo->cc = camel_pstring_strdup(addrstr);
 
1030
                g_free(addrstr);
 
1031
                camel_header_address_list_clear(&addr);
 
1032
        }
 
1033
 
 
1034
        /* we dont keep bcc either */
 
1035
 
 
1036
        /* env_bcc         ::= "(" 1*address ")" / nil */
 
1037
        addr = imapx_parse_address_list(is, ex);
 
1038
        camel_header_address_list_clear(&addr);
 
1039
 
 
1040
        /* FIXME: need to put in-reply-to into references hash list */
 
1041
 
 
1042
        /* env_in_reply_to ::= nstring */
 
1043
        tok = camel_imapx_stream_nstring(is, &token, ex);
 
1044
 
 
1045
        /* FIXME: need to put message-id into message-id hash */
 
1046
 
 
1047
        /* env_message_id  ::= nstring */
 
1048
        tok = camel_imapx_stream_nstring(is, &token, ex);
 
1049
 
 
1050
        tok = camel_imapx_stream_token(is, &token, &len, ex);
 
1051
        if (tok != ')') {
 
1052
                camel_message_info_free(minfo);
 
1053
                camel_exception_set (ex, 1, "expecting ')'");
 
1054
                return NULL;
 
1055
        }
 
1056
 
 
1057
        /* CHEN TODO handle exceptions better */
 
1058
 
 
1059
        return (CamelMessageInfo *)minfo;
 
1060
}
 
1061
 
 
1062
struct _CamelMessageContentInfo *
 
1063
imapx_parse_body(CamelIMAPXStream *is, CamelException *ex)
 
1064
{
 
1065
        gint tok;
 
1066
        guint len;
 
1067
        guchar *token;
 
1068
        struct _CamelMessageContentInfo * cinfo = NULL;
 
1069
        struct _CamelMessageContentInfo *subinfo, *last;
 
1070
        struct _CamelContentDisposition * dinfo = NULL;
 
1071
        struct _CamelMessageInfo * minfo = NULL;
 
1072
 
 
1073
        /* body            ::= "(" body_type_1part / body_type_mpart ")" */
 
1074
 
 
1075
        p(printf("body\n"));
 
1076
 
 
1077
        tok = camel_imapx_stream_token(is, &token, &len, ex);
 
1078
        if (tok != '(') {
 
1079
                camel_exception_set (ex, 1, "body: expecting '('");
 
1080
                return NULL;
 
1081
        }
 
1082
 
 
1083
        /* 1*body (optional for multiparts) */
 
1084
        tok = camel_imapx_stream_token(is, &token, &len, ex);
 
1085
        camel_imapx_stream_ungettoken(is, tok, token, len);
 
1086
        if (tok == '(') {
 
1087
                /* body_type_mpart ::= 1*body SPACE media_subtype
 
1088
                   [SPACE body_ext_mpart] */
 
1089
 
 
1090
                cinfo = g_malloc0(sizeof(*cinfo));
 
1091
                last = (struct _CamelMessageContentInfo *)&cinfo->childs;
 
1092
                do {
 
1093
                        subinfo = imapx_parse_body(is, ex);
 
1094
                        last->next = subinfo;
 
1095
                        last = subinfo;
 
1096
                        subinfo->parent = cinfo;
 
1097
                        tok = camel_imapx_stream_token(is, &token, &len, ex);
 
1098
                        camel_imapx_stream_ungettoken(is, tok, token, len);
 
1099
                } while (tok == '(');
 
1100
 
 
1101
                d(printf("media_subtype\n"));
 
1102
 
 
1103
                camel_imapx_stream_astring(is, &token, ex);
 
1104
                cinfo->type = camel_content_type_new("multipart", (gchar *) token);
 
1105
 
 
1106
                /* body_ext_mpart  ::= body_fld_param
 
1107
                   [SPACE body_fld_dsp SPACE body_fld_lang
 
1108
                   [SPACE 1#body_extension]]
 
1109
                   ;; MUST NOT be returned on non-extensible
 
1110
                   ;; "BODY" fetch */
 
1111
 
 
1112
                d(printf("body_ext_mpart\n"));
 
1113
 
 
1114
                tok = camel_imapx_stream_token(is, &token, &len, ex);
 
1115
                camel_imapx_stream_ungettoken(is, tok, token, len);
 
1116
                if (tok == '(') {
 
1117
                        imapx_parse_param_list(is, &cinfo->type->params, ex);
 
1118
 
 
1119
                        /* body_fld_dsp    ::= "(" string SPACE body_fld_param ")" / nil */
 
1120
 
 
1121
                        tok = camel_imapx_stream_token(is, &token, &len, ex);
 
1122
                        camel_imapx_stream_ungettoken(is, tok, token, len);
 
1123
                        if (tok == '(' || tok == IMAPX_TOK_TOKEN) {
 
1124
                                dinfo = imapx_parse_ext_optional(is, ex);
 
1125
                                /* other extension fields?, soaked up below */
 
1126
                        } else {
 
1127
                                camel_imapx_stream_ungettoken(is, tok, token, len);
 
1128
                        }
 
1129
                }
 
1130
        } else {
 
1131
                /* body_type_1part ::= (body_type_basic / body_type_msg / body_type_text)
 
1132
                   [SPACE body_ext_1part]
 
1133
 
 
1134
                   body_type_basic ::= media_basic SPACE body_fields
 
1135
                   body_type_text  ::= media_text SPACE body_fields SPACE body_fld_lines
 
1136
                   body_type_msg   ::= media_message SPACE body_fields SPACE envelope
 
1137
                   SPACE body SPACE body_fld_lines */
 
1138
 
 
1139
                d(printf("Single part body\n"));
 
1140
 
 
1141
                cinfo = imapx_parse_body_fields(is, ex);
 
1142
 
 
1143
                d(printf("envelope?\n"));
 
1144
 
 
1145
                /* do we have an envelope following */
 
1146
                tok = camel_imapx_stream_token(is, &token, &len, ex);
 
1147
                camel_imapx_stream_ungettoken(is, tok, token, len);
 
1148
                if (tok == '(') {
 
1149
                        /* what do we do with the envelope?? */
 
1150
                        minfo = imapx_parse_envelope(is, ex);
 
1151
                        /* what do we do with the message content info?? */
 
1152
                        //((CamelMessageInfoBase *)minfo)->content = imapx_parse_body(is);
 
1153
                        camel_message_info_free(minfo);
 
1154
                        minfo = NULL;
 
1155
                        d(printf("Scanned envelope - what do i do with it?\n"));
 
1156
                }
 
1157
 
 
1158
                d(printf("fld_lines?\n"));
 
1159
 
 
1160
                /* do we have fld_lines following? */
 
1161
                tok = camel_imapx_stream_token(is, &token, &len, ex);
 
1162
                if (tok == IMAPX_TOK_INT) {
 
1163
                        d(printf("field lines: %s\n", token));
 
1164
                        tok = camel_imapx_stream_token(is, &token, &len, ex);
 
1165
                }
 
1166
                camel_imapx_stream_ungettoken(is, tok, token, len);
 
1167
 
 
1168
                /* body_ext_1part  ::= body_fld_md5 [SPACE body_fld_dsp
 
1169
                   [SPACE body_fld_lang
 
1170
                   [SPACE 1#body_extension]]]
 
1171
                   ;; MUST NOT be returned on non-extensible
 
1172
                   ;; "BODY" fetch */
 
1173
 
 
1174
                d(printf("extension data?\n"));
 
1175
 
 
1176
                if (tok != ')') {
 
1177
                        camel_imapx_stream_nstring(is, &token, ex);
 
1178
 
 
1179
                        d(printf("md5: %s\n", token?(gchar *)token:"NIL"));
 
1180
 
 
1181
                        /* body_fld_dsp    ::= "(" string SPACE body_fld_param ")" / nil */
 
1182
 
 
1183
                        tok = camel_imapx_stream_token(is, &token, &len, ex);
 
1184
                        camel_imapx_stream_ungettoken(is, tok, token, len);
 
1185
                        if (tok == '(' || tok == IMAPX_TOK_TOKEN) {
 
1186
                                dinfo = imapx_parse_ext_optional(is, ex);
 
1187
                                /* then other extension fields, soaked up below */
 
1188
                        }
 
1189
                }
 
1190
        }
 
1191
 
 
1192
        /* soak up any other extension fields that may be present */
 
1193
        /* there should only be simple tokens, no lists */
 
1194
        do {
 
1195
                tok = camel_imapx_stream_token(is, &token, &len, ex);
 
1196
                if (tok != ')') {
 
1197
                        d(printf("Dropping extension data '%s'\n", token));
 
1198
                }
 
1199
        } while (tok != ')');
 
1200
 
 
1201
        /* CHEN TODO handle exceptions better */
 
1202
        if (camel_exception_is_set (ex)) {
 
1203
                if (cinfo)
 
1204
                        imapx_free_body(cinfo);
 
1205
                if (dinfo)
 
1206
                        camel_content_disposition_unref(dinfo);
 
1207
                if (minfo)
 
1208
                        camel_message_info_free(minfo);
 
1209
                return NULL;
 
1210
        }
 
1211
 
 
1212
        /* FIXME: do something with the disposition, currently we have no way to pass it out? */
 
1213
        if (dinfo)
 
1214
                camel_content_disposition_unref(dinfo);
 
1215
 
 
1216
        return cinfo;
 
1217
}
 
1218
 
 
1219
gchar *
 
1220
imapx_parse_section(CamelIMAPXStream *is, CamelException *ex)
 
1221
{
 
1222
        gint tok;
 
1223
        guint len;
 
1224
        guchar *token;
 
1225
        gchar * section = NULL;
 
1226
 
 
1227
        /* currently we only return the part within the [section] specifier
 
1228
           any header fields are parsed, but dropped */
 
1229
 
 
1230
        /*
 
1231
          section         ::= "[" [section_text /
 
1232
          (nz_number *["." nz_number] ["." (section_text / "MIME")])] "]"
 
1233
 
 
1234
          section_text    ::= "HEADER" / "HEADER.FIELDS" [".NOT"]
 
1235
          SPACE header_list / "TEXT"
 
1236
        */
 
1237
 
 
1238
        tok = camel_imapx_stream_token(is, &token, &len, ex);
 
1239
        if (tok != '[') {
 
1240
                camel_exception_set (ex, 1, "section: expecting '['");
 
1241
                return NULL;
 
1242
        }
 
1243
 
 
1244
        tok = camel_imapx_stream_token(is, &token, &len, ex);
 
1245
        if (tok == IMAPX_TOK_INT || tok == IMAPX_TOK_TOKEN)
 
1246
                section = g_strdup((gchar *) token);
 
1247
        else if (tok == ']') {
 
1248
                section = g_strdup("");
 
1249
                camel_imapx_stream_ungettoken(is, tok, token, len);
 
1250
        } else {
 
1251
                camel_exception_set (ex, 1, "section: expecting token");
 
1252
                return NULL;
 
1253
        }
 
1254
 
 
1255
        /* header_list     ::= "(" 1#header_fld_name ")"
 
1256
           header_fld_name ::= astring */
 
1257
 
 
1258
        /* we dont need the header specifiers */
 
1259
        tok = camel_imapx_stream_token(is, &token, &len, ex);
 
1260
        if (tok == '(') {
 
1261
                do {
 
1262
                        tok = camel_imapx_stream_token(is, &token, &len, ex);
 
1263
                        if (tok == IMAPX_TOK_STRING || tok == IMAPX_TOK_TOKEN || tok == IMAPX_TOK_INT) {
 
1264
                                /* ?do something? */
 
1265
                        } else if (tok != ')') {
 
1266
                                camel_exception_set (ex, 1, "section: header fields: expecting string");
 
1267
                                g_free (section);
 
1268
                                return NULL;
 
1269
                        }
 
1270
                } while (tok != ')');
 
1271
                tok = camel_imapx_stream_token(is, &token, &len, ex);
 
1272
        }
 
1273
 
 
1274
        if (tok != ']') {
 
1275
                camel_exception_set (ex, 1, "section: expecting ']'");
 
1276
                g_free(section);
 
1277
                return NULL;
 
1278
        }
 
1279
 
 
1280
        return section;
 
1281
}
 
1282
 
 
1283
void
 
1284
imapx_free_fetch(struct _fetch_info *finfo)
 
1285
{
 
1286
        if (finfo == NULL)
 
1287
                return;
 
1288
 
 
1289
        if (finfo->body)
 
1290
                camel_object_unref((CamelObject *)finfo->body);
 
1291
        if (finfo->text)
 
1292
                camel_object_unref((CamelObject *)finfo->text);
 
1293
        if (finfo->header)
 
1294
                camel_object_unref((CamelObject *)finfo->header);
 
1295
        if (finfo->minfo)
 
1296
                camel_message_info_free(finfo->minfo);
 
1297
        if (finfo->cinfo)
 
1298
                imapx_free_body(finfo->cinfo);
 
1299
        camel_flag_list_free(&finfo->user_flags);
 
1300
        g_free(finfo->date);
 
1301
        g_free(finfo->section);
 
1302
        g_free(finfo->uid);
 
1303
        g_free(finfo);
 
1304
}
 
1305
 
 
1306
#include <camel/camel-stream-fs.h>
 
1307
 
 
1308
/* debug, dump one out */
 
1309
void
 
1310
imapx_dump_fetch(struct _fetch_info *finfo)
 
1311
{
 
1312
        CamelStream *sout;
 
1313
        gint fd;
 
1314
 
 
1315
        d(printf("Fetch info:\n"));
 
1316
        if (finfo == NULL) {
 
1317
                d(printf("Empty\n"));
 
1318
                return;
 
1319
        }
 
1320
 
 
1321
        fd = dup(1);
 
1322
        sout = camel_stream_fs_new_with_fd(fd);
 
1323
        if (finfo->body) {
 
1324
                camel_stream_printf(sout, "Body content:\n");
 
1325
                camel_stream_write_to_stream(finfo->body, sout);
 
1326
                camel_stream_reset(finfo->body);
 
1327
        }
 
1328
        if (finfo->text) {
 
1329
                camel_stream_printf(sout, "Text content:\n");
 
1330
                camel_stream_write_to_stream(finfo->text, sout);
 
1331
                camel_stream_reset(finfo->text);
 
1332
        }
 
1333
        if (finfo->header) {
 
1334
                camel_stream_printf(sout, "Header content:\n");
 
1335
                camel_stream_write_to_stream(finfo->header, sout);
 
1336
                camel_stream_reset(finfo->header);
 
1337
        }
 
1338
        if (finfo->minfo) {
 
1339
                camel_stream_printf(sout, "Message Info:\n");
 
1340
                camel_message_info_dump(finfo->minfo);
 
1341
        }
 
1342
        if (finfo->cinfo) {
 
1343
                camel_stream_printf(sout, "Content Info:\n");
 
1344
                //camel_content_info_dump(finfo->cinfo, 0);
 
1345
        }
 
1346
        if (finfo->got & FETCH_SIZE)
 
1347
                camel_stream_printf(sout, "Size: %d\n", (gint)finfo->size);
 
1348
        if (finfo->got & FETCH_BODY)
 
1349
                camel_stream_printf(sout, "Offset: %d\n", (gint)finfo->offset);
 
1350
        if (finfo->got & FETCH_FLAGS)
 
1351
                camel_stream_printf(sout, "Flags: %08x\n", (gint)finfo->flags);
 
1352
        if (finfo->date)
 
1353
                camel_stream_printf(sout, "Date: '%s'\n", finfo->date);
 
1354
        if (finfo->section)
 
1355
                camel_stream_printf(sout, "Section: '%s'\n", finfo->section);
 
1356
        if (finfo->date)
 
1357
                camel_stream_printf(sout, "UID: '%s'\n", finfo->uid);
 
1358
        camel_object_unref((CamelObject *)sout);
 
1359
}
 
1360
 
 
1361
struct _fetch_info *
 
1362
imapx_parse_fetch(CamelIMAPXStream *is, CamelException *ex)
 
1363
{
 
1364
        gint tok;
 
1365
        guint len;
 
1366
        guchar *token, *p, c;
 
1367
        struct _fetch_info *finfo;
 
1368
 
 
1369
        finfo = g_malloc0(sizeof(*finfo));
 
1370
 
 
1371
        tok = camel_imapx_stream_token(is, &token, &len, ex);
 
1372
        if (tok != '(') {
 
1373
                camel_exception_set (ex, 1, "fetch: expecting '('");
 
1374
                g_free (finfo);
 
1375
                return NULL;
 
1376
        }
 
1377
 
 
1378
        while ((tok = camel_imapx_stream_token(is, &token, &len, ex)) == IMAPX_TOK_TOKEN) {
 
1379
 
 
1380
                p = token;
 
1381
                while ((c=*p))
 
1382
                        *p++ = toupper(c);
 
1383
 
 
1384
                switch (imapx_tokenise((gchar *) token, len)) {
 
1385
                        case IMAPX_ENVELOPE:
 
1386
                                finfo->minfo = imapx_parse_envelope(is, ex);
 
1387
                                finfo->got |= FETCH_MINFO;
 
1388
                                break;
 
1389
                        case IMAPX_FLAGS:
 
1390
                                imapx_parse_flags(is, &finfo->flags, &finfo->user_flags, ex);
 
1391
                                finfo->got |= FETCH_FLAGS;
 
1392
                                break;
 
1393
                        case IMAPX_INTERNALDATE:
 
1394
                                camel_imapx_stream_nstring(is, &token, ex);
 
1395
                                /* TODO: convert to camel format? */
 
1396
                                finfo->date = g_strdup((gchar *) token);
 
1397
                                finfo->got |= FETCH_DATE;
 
1398
                                break;
 
1399
                        case IMAPX_RFC822_HEADER:
 
1400
                                camel_imapx_stream_nstring_stream(is, &finfo->header, ex);
 
1401
                                finfo->got |= FETCH_HEADER;
 
1402
                                break;
 
1403
                        case IMAPX_RFC822_TEXT:
 
1404
                                camel_imapx_stream_nstring_stream(is, &finfo->text, ex);
 
1405
                                finfo->got |= FETCH_TEXT;
 
1406
                                break;
 
1407
                        case IMAPX_RFC822_SIZE:
 
1408
                                finfo->size = camel_imapx_stream_number(is, ex);
 
1409
                                finfo->got |= FETCH_SIZE;
 
1410
                                break;
 
1411
                        case IMAPX_BODYSTRUCTURE:
 
1412
                                finfo->cinfo = imapx_parse_body(is, ex);
 
1413
                                finfo->got |= FETCH_CINFO;
 
1414
                                break;
 
1415
                        case IMAPX_BODY:
 
1416
                                tok = camel_imapx_stream_token(is, &token, &len, ex);
 
1417
                                camel_imapx_stream_ungettoken(is, tok, token, len);
 
1418
                                if (tok == '(') {
 
1419
                                        finfo->cinfo = imapx_parse_body(is, ex);
 
1420
                                        finfo->got |= FETCH_CINFO;
 
1421
                                } else if (tok == '[') {
 
1422
                                        finfo->section = imapx_parse_section(is, ex);
 
1423
                                        finfo->got |= FETCH_SECTION;
 
1424
                                        tok = camel_imapx_stream_token(is, &token, &len, ex);
 
1425
                                        if (token[0] == '<') {
 
1426
                                                finfo->offset = strtoul((gchar *) token+1, NULL, 10);
 
1427
                                        } else {
 
1428
                                                camel_imapx_stream_ungettoken(is, tok, token, len);
 
1429
                                        }
 
1430
                                        camel_imapx_stream_nstring_stream(is, &finfo->body, ex);
 
1431
                                        finfo->got |= FETCH_BODY;
 
1432
                                } else {
 
1433
                                        camel_exception_set (ex, 1, "unknown body response");
 
1434
                                        imapx_free_fetch(finfo);
 
1435
                                        return NULL;
 
1436
                                }
 
1437
                                break;
 
1438
                        case IMAPX_UID:
 
1439
                                tok = camel_imapx_stream_token(is, &token, &len, ex);
 
1440
                                if (tok != IMAPX_TOK_INT) {
 
1441
                                        camel_exception_set (ex, 1, "uid not integer");
 
1442
                                }
 
1443
 
 
1444
                                finfo->uid = g_strdup((gchar *) token);
 
1445
                                finfo->got |= FETCH_UID;
 
1446
                                break;
 
1447
                        default:
 
1448
                                imapx_free_fetch(finfo);
 
1449
                                camel_exception_set (ex, 1, "unknown body response");
 
1450
                                return NULL;
 
1451
                }
 
1452
        }
 
1453
 
 
1454
        if (tok != ')') {
 
1455
                camel_exception_set (ex, 1, "missing closing ')' on fetch response");
 
1456
                imapx_free_fetch (finfo);
 
1457
                return NULL;
 
1458
        }
 
1459
 
 
1460
        return finfo;
 
1461
}
 
1462
 
 
1463
struct _state_info *
 
1464
imapx_parse_status_info (struct _CamelIMAPXStream *is, CamelException *ex)
 
1465
{
 
1466
        struct _state_info *sinfo;
 
1467
        gint tok;
 
1468
        guint len;
 
1469
        guchar *token;
 
1470
 
 
1471
        sinfo = g_malloc0 (sizeof(*sinfo));
 
1472
 
 
1473
        /* skip the folder name */
 
1474
        camel_imapx_stream_token (is, &token, &len, ex);
 
1475
 
 
1476
        tok = camel_imapx_stream_token(is, &token, &len, ex);
 
1477
        if (tok != '(') {
 
1478
                camel_exception_set (ex, 1, "parse status info: expecting '('");
 
1479
                g_free (sinfo);
 
1480
                return NULL;
 
1481
        }
 
1482
 
 
1483
        while ((tok = camel_imapx_stream_token(is, &token, &len, ex)) == IMAPX_TOK_TOKEN) {
 
1484
                switch (imapx_tokenise((gchar *) token, len)) {
 
1485
                        case IMAPX_MESSAGES:
 
1486
                                sinfo->messages = camel_imapx_stream_number (is, ex);
 
1487
                                break;
 
1488
                        case IMAPX_RECENT:
 
1489
                                sinfo->recent = camel_imapx_stream_number (is, ex);
 
1490
                                break;
 
1491
                        case IMAPX_UIDNEXT:
 
1492
                                sinfo->uidnext = camel_imapx_stream_number (is, ex);
 
1493
                                break;
 
1494
                        case IMAPX_UIDVALIDITY:
 
1495
                                sinfo->uidvalidity = camel_imapx_stream_number (is, ex);
 
1496
                                break;
 
1497
                        case IMAPX_UNSEEN:
 
1498
                                sinfo->unseen = camel_imapx_stream_number (is, ex);
 
1499
                                break;
 
1500
                        default:
 
1501
                                g_free (sinfo);
 
1502
                                camel_exception_set (ex, 1, "unknown status response");
 
1503
                                return NULL;
 
1504
                }
 
1505
        }
 
1506
 
 
1507
        if (tok != ')') {
 
1508
                camel_exception_set (ex, 1, "missing closing ')' on status response");
 
1509
                g_free (sinfo);
 
1510
                return NULL;
 
1511
        }
 
1512
 
 
1513
        return sinfo;
 
1514
}
 
1515
 
 
1516
static void
 
1517
generate_uids_from_sequence (GPtrArray *uids, guint32 begin_uid, guint32 end_uid)
 
1518
{
 
1519
        guint32 i;
 
1520
 
 
1521
        for (i = begin_uid; i <= end_uid; i++)
 
1522
                g_ptr_array_add (uids, GUINT_TO_POINTER (i));
 
1523
}
 
1524
 
 
1525
static GPtrArray *
 
1526
imapx_parse_uids (CamelIMAPXStream *is, CamelException *ex)
 
1527
{
 
1528
        GPtrArray *uids = g_ptr_array_new ();
 
1529
        guchar *token;
 
1530
        gchar **splits;
 
1531
        guint len, str_len;
 
1532
        gint tok, i;
 
1533
 
 
1534
        tok = camel_imapx_stream_token (is, &token, &len, ex);
 
1535
        splits = g_strsplit ((gchar *) token, ",", -1);
 
1536
        str_len = g_strv_length (splits);
 
1537
 
 
1538
        for (i = 0; i < str_len; i++)   {
 
1539
                if (g_strstr_len (splits [i], -1, ":")) {
 
1540
                        gchar **seq = g_strsplit (splits [i], ":", -1);
 
1541
                        guint32 uid1 = strtoul ((gchar *) seq [0], NULL, 10);
 
1542
                        guint32 uid2 = strtoul ((gchar *) seq [1], NULL, 10);
 
1543
 
 
1544
                        generate_uids_from_sequence (uids, uid1, uid2);
 
1545
                        g_strfreev (seq);
 
1546
                } else {
 
1547
                        guint32 uid = strtoul ((gchar *) token, NULL, 10);
 
1548
                        g_ptr_array_add (uids, GUINT_TO_POINTER (uid));
 
1549
                }
 
1550
        }
 
1551
 
 
1552
        g_strfreev (splits);
 
1553
 
 
1554
        return uids;
 
1555
}
 
1556
 
 
1557
/* rfc 2060 section 7.1 Status Responses */
 
1558
/* shoudl this start after [ or before the [? token_unget anyone? */
 
1559
struct _status_info *
 
1560
imapx_parse_status(CamelIMAPXStream *is, CamelException *ex)
 
1561
{
 
1562
        gint tok;
 
1563
        guint len;
 
1564
        guchar *token;
 
1565
        struct _status_info *sinfo;
 
1566
 
 
1567
        sinfo = g_malloc0(sizeof(*sinfo));
 
1568
 
 
1569
        camel_imapx_stream_atom(is, &token, &len, ex);
 
1570
 
 
1571
        /*
 
1572
           resp_cond_auth  ::= ("OK" / "PREAUTH") SPACE resp_text
 
1573
           ;; Authentication condition
 
1574
 
 
1575
           resp_cond_bye   ::= "BYE" SPACE resp_text
 
1576
 
 
1577
           resp_cond_state ::= ("OK" / "NO" / "BAD") SPACE resp_text
 
1578
           ;; Status condition
 
1579
         */
 
1580
 
 
1581
        sinfo->result = imapx_tokenise((gchar *) token, len);
 
1582
        switch (sinfo->result) {
 
1583
                case IMAPX_OK:
 
1584
                case IMAPX_NO:
 
1585
                case IMAPX_BAD:
 
1586
                case IMAPX_PREAUTH:
 
1587
                case IMAPX_BYE:
 
1588
                        break;
 
1589
                default:
 
1590
                        camel_exception_set (ex, 1, "expecting OK/NO/BAD");
 
1591
                        g_free (sinfo);
 
1592
                        return NULL;
 
1593
        }
 
1594
 
 
1595
        tok = camel_imapx_stream_token(is, &token, &len, ex);
 
1596
        if (tok == '[') {
 
1597
                camel_imapx_stream_atom(is, &token, &len, ex);
 
1598
                sinfo->condition = imapx_tokenise((gchar *) token, len);
 
1599
 
 
1600
                /* parse any details */
 
1601
                switch (sinfo->condition) {
 
1602
                        case IMAPX_READ_ONLY:
 
1603
                        case IMAPX_READ_WRITE:
 
1604
                        case IMAPX_ALERT:
 
1605
                        case IMAPX_PARSE:
 
1606
                        case IMAPX_TRYCREATE:
 
1607
                                break;
 
1608
                        case IMAPX_APPENDUID:
 
1609
                                sinfo->u.appenduid.uidvalidity = camel_imapx_stream_number(is, ex);
 
1610
                                sinfo->u.appenduid.uid = camel_imapx_stream_number(is, ex);
 
1611
                                break;
 
1612
                        case IMAPX_COPYUID:
 
1613
                                sinfo->u.copyuid.uidvalidity = camel_imapx_stream_number(is, ex);
 
1614
                                sinfo->u.copyuid.uids = imapx_parse_uids (is, ex);
 
1615
                                sinfo->u.copyuid.copied_uids = imapx_parse_uids (is, ex);
 
1616
                                break;
 
1617
                        case IMAPX_NEWNAME:
 
1618
                                /* the rfc doesn't specify the bnf for this */
 
1619
                                camel_imapx_stream_astring(is, &token, ex);
 
1620
                                sinfo->u.newname.oldname = g_strdup((gchar *) token);
 
1621
                                camel_imapx_stream_astring(is, &token, ex);
 
1622
                                sinfo->u.newname.newname = g_strdup((gchar *) token);
 
1623
                                break;
 
1624
                        case IMAPX_PERMANENTFLAGS:
 
1625
                                /* we only care about \* for permanent flags, not user flags */
 
1626
                                imapx_parse_flags(is, &sinfo->u.permanentflags, NULL, ex);
 
1627
                                break;
 
1628
                        case IMAPX_UIDVALIDITY:
 
1629
                                sinfo->u.uidvalidity = camel_imapx_stream_number(is, ex);
 
1630
                                break;
 
1631
                        case IMAPX_UIDNEXT:
 
1632
                                sinfo->u.uidnext = camel_imapx_stream_number (is, ex);
 
1633
                                break;
 
1634
                        case IMAPX_UNSEEN:
 
1635
                                sinfo->u.unseen = camel_imapx_stream_number(is, ex);
 
1636
                                break;
 
1637
                        default:
 
1638
                                sinfo->condition = IMAPX_UNKNOWN;
 
1639
                                d(printf("Got unknown response code: %s: ignored\n", token));
 
1640
                }
 
1641
 
 
1642
                /* ignore anything we dont know about */
 
1643
                do {
 
1644
                        tok = camel_imapx_stream_token(is, &token, &len, ex);
 
1645
                        if (tok == '\n') {
 
1646
                                camel_exception_set (ex, 1, "server response truncated");
 
1647
                                imapx_free_status(sinfo);
 
1648
                                return NULL;
 
1649
                        }
 
1650
                } while (tok != ']');
 
1651
        } else {
 
1652
                camel_imapx_stream_ungettoken(is, tok, token, len);
 
1653
        }
 
1654
 
 
1655
        /* and take the human readable response */
 
1656
        camel_imapx_stream_text(is, (guchar **)&sinfo->text, ex);
 
1657
 
 
1658
        return sinfo;
 
1659
}
 
1660
 
 
1661
struct _status_info *
 
1662
imapx_copy_status(struct _status_info *sinfo)
 
1663
{
 
1664
        struct _status_info *out;
 
1665
 
 
1666
        out = g_malloc(sizeof(*out));
 
1667
        memcpy(out, sinfo, sizeof(*out));
 
1668
        out->text = g_strdup(out->text);
 
1669
        if (out->condition == IMAPX_NEWNAME) {
 
1670
                out->u.newname.oldname = g_strdup(out->u.newname.oldname);
 
1671
                out->u.newname.newname = g_strdup(out->u.newname.newname);
 
1672
        }
 
1673
 
 
1674
        return out;
 
1675
}
 
1676
 
 
1677
void
 
1678
imapx_free_status(struct _status_info *sinfo)
 
1679
{
 
1680
        if (sinfo == NULL)
 
1681
                return;
 
1682
 
 
1683
        switch (sinfo->condition) {
 
1684
        case IMAPX_NEWNAME:
 
1685
                g_free(sinfo->u.newname.oldname);
 
1686
                g_free(sinfo->u.newname.newname);
 
1687
                break;
 
1688
        case IMAPX_COPYUID:
 
1689
                g_ptr_array_free (sinfo->u.copyuid.uids, FALSE);
 
1690
                g_ptr_array_free (sinfo->u.copyuid.copied_uids, FALSE);
 
1691
                break;
 
1692
        default:
 
1693
                break;
 
1694
        }
 
1695
 
 
1696
        g_free(sinfo->text);
 
1697
        g_free(sinfo);
 
1698
}
 
1699
 
 
1700
/* FIXME: use tokeniser? */
 
1701
/* FIXME: real flags */
 
1702
static struct {
 
1703
        const gchar *name;
 
1704
        guint32 flag;
 
1705
} list_flag_table[] = {
 
1706
        { "\\NOINFERIORS", CAMEL_FOLDER_NOINFERIORS },
 
1707
        { "\\NOSELECT", CAMEL_FOLDER_NOSELECT },
 
1708
        { "\\MARKED", 1<< 16},
 
1709
        { "\\UNMARKED", 1<< 17},
 
1710
};
 
1711
 
 
1712
struct _list_info *
 
1713
imapx_parse_list(CamelIMAPXStream *is, CamelException *ex)
 
1714
/* throws io, parse */
 
1715
{
 
1716
        gint tok, i;
 
1717
        guint len;
 
1718
        guchar *token, *p, c;
 
1719
        struct _list_info * linfo;
 
1720
 
 
1721
        linfo = g_malloc0(sizeof(*linfo));
 
1722
 
 
1723
        /* mailbox_list    ::= "(" #("\Marked" / "\Noinferiors" /
 
1724
           "\Noselect" / "\Unmarked" / flag_extension) ")"
 
1725
           SPACE (<"> QUOTED_CHAR <"> / nil) SPACE mailbox */
 
1726
 
 
1727
        tok = camel_imapx_stream_token(is, &token, &len, ex);
 
1728
        if (tok != '(') {
 
1729
                camel_exception_set (ex, 1, "list: expecting '('");
 
1730
                g_free (linfo);
 
1731
                return NULL;
 
1732
        }
 
1733
 
 
1734
        while ((tok = camel_imapx_stream_token(is, &token, &len, ex)) != ')') {
 
1735
                if (tok == IMAPX_TOK_STRING || tok == IMAPX_TOK_TOKEN) {
 
1736
                        p = token;
 
1737
                        while ((c=*p))
 
1738
                                *p++ = toupper(c);
 
1739
                        for (i = 0; i < G_N_ELEMENTS (list_flag_table); i++)
 
1740
                                if (!strcmp((gchar *) token, list_flag_table[i].name))
 
1741
                                        linfo->flags |= list_flag_table[i].flag;
 
1742
                } else {
 
1743
                        imapx_free_list(linfo);
 
1744
                        camel_exception_set (ex, 1, "list: expecting flag or ')'");
 
1745
                        return NULL;
 
1746
                }
 
1747
        }
 
1748
 
 
1749
        camel_imapx_stream_nstring(is, &token, ex);
 
1750
        linfo->separator = token?*token:0;
 
1751
        camel_imapx_stream_astring(is, &token, ex);
 
1752
        linfo->name = camel_utf7_utf8 ((gchar *) token);
 
1753
 
 
1754
        return linfo;
 
1755
}
 
1756
 
 
1757
gchar *
 
1758
imapx_list_get_path(struct _list_info *li)
 
1759
{
 
1760
        gchar *path, *p;
 
1761
        gint c;
 
1762
        const gchar *f;
 
1763
 
 
1764
        if (li->separator != 0 && li->separator != '/') {
 
1765
                p = path = alloca(strlen(li->name)*3+1);
 
1766
                f = li->name;
 
1767
                while ((c = *f++ & 0xff)) {
 
1768
                        if (c == li->separator)
 
1769
                                *p++ = '/';
 
1770
                        else if (c == '/' || c == '%')
 
1771
                                p += sprintf(p, "%%%02X", c);
 
1772
                        else
 
1773
                                *p++ = c;
 
1774
                }
 
1775
                *p = 0;
 
1776
        } else
 
1777
                path = li->name;
 
1778
 
 
1779
        return camel_utf7_utf8(path);
 
1780
}
 
1781
 
 
1782
void
 
1783
imapx_free_list(struct _list_info *linfo)
 
1784
{
 
1785
        if (linfo) {
 
1786
                g_free(linfo->name);
 
1787
                g_free(linfo);
 
1788
        }
 
1789
}
 
1790
 
 
1791
/* ********************************************************************** */
 
1792
 
 
1793
/*
 
1794
 From rfc2060
 
1795
 
 
1796
ATOM_CHAR       ::= <any CHAR except atom_specials>
 
1797
 
 
1798
atom_specials   ::= "(" / ")" / "{" / SPACE / CTL / list_wildcards /
 
1799
                    quoted_specials
 
1800
 
 
1801
CHAR            ::= <any 7-bit US-ASCII character except NUL,
 
1802
                     0x01 - 0x7f>
 
1803
 
 
1804
CTL             ::= <any ASCII control character and DEL,
 
1805
                        0x00 - 0x1f, 0x7f>
 
1806
 
 
1807
SPACE           ::= <ASCII SP, space, 0x20>
 
1808
 
 
1809
list_wildcards  ::= "%" / "*"
 
1810
 
 
1811
quoted_specials ::= <"> / "\"
 
1812
 
 
1813
string          ::= quoted / literal
 
1814
 
 
1815
literal         ::= "{" number "}" CRLF *CHAR8
 
1816
                    ;; Number represents the number of CHAR8 octets
 
1817
 
 
1818
quoted          ::= <"> *QUOTED_CHAR <">
 
1819
 
 
1820
QUOTED_CHAR     ::= <any TEXT_CHAR except quoted_specials> /
 
1821
                    "\" quoted_specials
 
1822
 
 
1823
TEXT_CHAR       ::= <any CHAR except CR and LF>
 
1824
 
 
1825
*/
 
1826
 
 
1827
/*
 
1828
ATOM = 1
 
1829
SIMPLE? = 2
 
1830
NOTID? = 4
 
1831
 
 
1832
QSPECIAL = 8
 
1833
 
 
1834
*/
 
1835
 
 
1836
guchar imapx_specials[256] = {
 
1837
/* 00 */0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 4, 0, 0,
 
1838
/* 10 */0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
 
1839
/* 20 */4, 1, 0, 1, 1, 0, 1, 1, 0, 0, 2, 7, 1, 1, 1, 1,
 
1840
/* 30 */1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
 
1841
/* 40 */7, 7, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
 
1842
/* 50 */1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 7, 0, 7, 1, 1,
 
1843
/* 60 */1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
 
1844
/* 70 */1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0,
 
1845
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
 
1846
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
 
1847
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
 
1848
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
 
1849
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
 
1850
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
 
1851
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
 
1852
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
 
1853
};
 
1854
 
 
1855
#define list_wildcards "*%"
 
1856
#define quoted_specials "\\\""
 
1857
#define atom_specials "(){" list_wildcards quoted_specials /* + CTL */
 
1858
 
 
1859
/* special types for the tokeniser, come out as raw tokens */
 
1860
#define token_specials "\n*()[]+"
 
1861
#define notid_specials "\x20\r\n()[]+"
 
1862
 
 
1863
void imapx_utils_init(void)
 
1864
{
 
1865
        gint i;
 
1866
        guchar v;
 
1867
 
 
1868
        for (i=0;i<128;i++) {
 
1869
                v = 0;
 
1870
                if (i>=1 && i<=0x7f) {
 
1871
                        v |= IMAPX_TYPE_CHAR;
 
1872
                        if (i != 0x0a && i != 0x0d) {
 
1873
                                v |= IMAPX_TYPE_TEXT_CHAR;
 
1874
                                if (i != '"' && i != '\\')
 
1875
                                        v |= IMAPX_TYPE_QUOTED_CHAR;
 
1876
                        }
 
1877
                        if (i> 0x20 && i <0x7f && strchr(atom_specials, i) == NULL)
 
1878
                                v |= IMAPX_TYPE_ATOM_CHAR;
 
1879
                        if (strchr(token_specials, i) != NULL)
 
1880
                                v |= IMAPX_TYPE_TOKEN_CHAR;
 
1881
                        if (strchr(notid_specials, i) != NULL)
 
1882
                                v |= IMAPX_TYPE_NOTID_CHAR;
 
1883
                }
 
1884
 
 
1885
                imapx_specials[i] = v;
 
1886
        }
 
1887
}
 
1888
 
 
1889
guchar imapx_is_mask(const gchar *p)
 
1890
{
 
1891
        guchar v = 0xff;
 
1892
 
 
1893
        while (*p) {
 
1894
                v &= imapx_specials[((guchar)*p) & 0xff];
 
1895
                p++;
 
1896
        }
 
1897
 
 
1898
        return v;
 
1899
}
 
1900
 
 
1901
gchar *
 
1902
imapx_path_to_physical (const gchar *prefix, const gchar *vpath)
 
1903
{
 
1904
        GString *out = g_string_new(prefix);
 
1905
        const gchar *p = vpath;
 
1906
        gchar c, *res;
 
1907
 
 
1908
        g_string_append_c(out, '/');
 
1909
        p = vpath;
 
1910
        while ((c = *p++)) {
 
1911
                if (c == '/') {
 
1912
                        g_string_append(out, "/" SUBFOLDER_DIR_NAME "/");
 
1913
                        while (*p == '/')
 
1914
                                p++;
 
1915
                } else
 
1916
                        g_string_append_c(out, c);
 
1917
        }
 
1918
 
 
1919
        res = out->str;
 
1920
        g_string_free(out, FALSE);
 
1921
 
 
1922
        return res;
 
1923
}
 
1924
 
 
1925
gchar *
 
1926
imapx_concat (CamelIMAPXStore *imapx_store, const gchar *prefix, const gchar *suffix)
 
1927
{
 
1928
        gsize len;
 
1929
 
 
1930
        len = strlen (prefix);
 
1931
        if (len == 0 || prefix[len - 1] == imapx_store->dir_sep)
 
1932
                return g_strdup_printf ("%s%s", prefix, suffix);
 
1933
        else
 
1934
                return g_strdup_printf ("%s%c%s", prefix, imapx_store->dir_sep, suffix);
 
1935
}
 
1936
 
 
1937
static void
 
1938
imapx_namespace_clear (CamelIMAPXStoreNamespace **ns)
 
1939
{
 
1940
        CamelIMAPXStoreNamespace *node, *next;
 
1941
 
 
1942
        node = *ns;
 
1943
        while (node != NULL) {
 
1944
                next = node->next;
 
1945
                g_free (node->path);
 
1946
                g_free (node);
 
1947
                node = next;
 
1948
        }
 
1949
 
 
1950
        *ns = NULL;
 
1951
}
 
1952
 
 
1953
void
 
1954
camel_imapx_namespace_list_clear (struct _CamelIMAPXNamespaceList *nsl)
 
1955
{
 
1956
        if (!nsl)
 
1957
                return;
 
1958
 
 
1959
        imapx_namespace_clear (&nsl->personal);
 
1960
        imapx_namespace_clear (&nsl->shared);
 
1961
        imapx_namespace_clear (&nsl->other);
 
1962
 
 
1963
        g_free (nsl);
 
1964
        nsl = NULL;
 
1965
}
 
1966
 
 
1967
static CamelIMAPXStoreNamespace *
 
1968
imapx_namespace_copy (const CamelIMAPXStoreNamespace *ns)
 
1969
{
 
1970
        CamelIMAPXStoreNamespace *list, *node, *tail;
 
1971
 
 
1972
        list = NULL;
 
1973
        tail = (CamelIMAPXStoreNamespace *) &list;
 
1974
 
 
1975
        while (ns != NULL) {
 
1976
                tail->next = node = g_malloc (sizeof (CamelIMAPXStoreNamespace));
 
1977
                node->path = g_strdup (ns->path);
 
1978
                node->sep = ns->sep;
 
1979
                ns = ns->next;
 
1980
                tail = node;
 
1981
        }
 
1982
 
 
1983
        tail->next = NULL;
 
1984
 
 
1985
        return list;
 
1986
}
 
1987
 
 
1988
struct _CamelIMAPXNamespaceList *
 
1989
camel_imapx_namespace_list_copy (const struct _CamelIMAPXNamespaceList *nsl)
 
1990
{
 
1991
        CamelIMAPXNamespaceList *new;
 
1992
 
 
1993
        new = g_malloc (sizeof (CamelIMAPXNamespaceList));
 
1994
        new->personal = imapx_namespace_copy (nsl->personal);
 
1995
        new->other = imapx_namespace_copy (nsl->other);
 
1996
        new->shared = imapx_namespace_copy (nsl->shared);
 
1997
 
 
1998
        return new;
 
1999
}
 
2000
 
 
2001
gchar *
 
2002
imapx_get_temp_uid (void)
 
2003
{
 
2004
        gchar *res;
 
2005
 
 
2006
        static gint counter = 0;
 
2007
        G_LOCK_DEFINE_STATIC (lock);
 
2008
 
 
2009
        G_LOCK (lock);
 
2010
        res = g_strdup_printf ("tempuid-%lx-%d",
 
2011
                               (gulong) time (NULL),
 
2012
                               counter++);
 
2013
        G_UNLOCK (lock);
 
2014
 
 
2015
        return res;
 
2016
}