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>
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"
19
/* high-level parser state */
24
#include "camel-imapx-tokenise.h"
25
#define SUBFOLDER_DIR_NAME "subfolders"
31
imapx_tokenise (register const gchar *str, register guint len)
33
struct _imapx_keyword *k = imapx_tokenise_struct(str, len);
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);
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 }
59
shoudl this be part of imapx-driver? */
60
/* mabye this should be a stream op? */
62
imapx_parse_flags(CamelIMAPXStream *stream, guint32 *flagsp, CamelFlag **user_flagsp, CamelException *ex)
63
/* throws IO,PARSE exception */
72
tok = camel_imapx_stream_token(stream, &token, &len, ex);
75
tok = camel_imapx_stream_token(stream, &token, &len, ex);
76
if (tok == IMAPX_TOK_TOKEN) {
78
// FIXME: ascii_toupper
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;
87
const gchar *flag_name = rename_label_flag ((gchar *) token, strlen ((gchar *) token), TRUE);
89
camel_flag_set(user_flagsp, flag_name, TRUE);
93
tok = tok; /* fixes stupid warning */
94
} else if (tok != ')') {
95
camel_exception_set (ex, 1, "expecting flag");
100
camel_exception_set (ex, 1, "expecting flag list");
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.
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.
118
rename_label_flag (const gchar *flag, gint len, gboolean server_to_evo)
121
const gchar *labels[] = {
122
"$Label1", "$Labelimportant",
123
"$Label2", "$Labelwork",
124
"$Label3", "$Labelpersonal",
125
"$Label4", "$Labeltodo",
126
"$Label5", "$Labellater",
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)
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)];
143
imapx_write_flags(CamelStream *stream, guint32 flags, CamelFlag *user_flags, CamelException *ex)
144
/* throws IO exception */
148
if (camel_stream_write(stream, "(", 1) == -1) {
149
camel_exception_setv (ex, 1, "io error: %s", strerror(errno));
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)
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));
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));
173
const gchar *flag_name = rename_label_flag (user_flags->name, strlen (user_flags->name), FALSE);
175
if (camel_stream_write(stream, flag_name, strlen (flag_name)) == -1) {
176
camel_exception_setv (ex, 1, "io error: %s", strerror(errno));
180
if (user_flags->next && camel_stream_write(stream, " ", 1) == -1) {
181
camel_exception_setv (ex, 1, "io error: %s", strerror(errno));
185
user_flags = user_flags->next;
188
if (camel_stream_write(stream, ")", 1) == -1) {
189
camel_exception_setv (ex, 1, "io error: %s", strerror(errno));
195
imapx_update_user_flags (CamelMessageInfo *info, CamelFlag *server_user_flags)
197
gboolean changed = FALSE;
198
CamelMessageInfoBase *binfo = (CamelMessageInfoBase *) info;
199
gboolean set_cal = FALSE;
201
if (camel_flag_get (&binfo->user_flags, "$has_cal"))
204
changed = camel_flag_list_copy(&binfo->user_flags, &server_user_flags);
206
/* reset the calendar flag if it was set in messageinfo before */
208
camel_flag_set (&binfo->user_flags, "$has_cal", TRUE);
214
imapx_update_message_info_flags (CamelMessageInfo *info, guint32 server_flags, CamelFlag *server_user_flags, CamelFolder *folder)
216
gboolean changed = FALSE;
217
CamelIMAPXMessageInfo *xinfo = (CamelIMAPXMessageInfo *) info;
219
if (server_flags != xinfo->server_flags)
221
guint32 server_set, server_cleared;
222
gint read=0, deleted=0, junk=0;
224
server_set = server_flags & ~xinfo->server_flags;
225
server_cleared = xinfo->server_flags & ~server_flags;
227
if (server_set & CAMEL_MESSAGE_SEEN)
229
else if (server_cleared & CAMEL_MESSAGE_SEEN)
232
if (server_set & CAMEL_MESSAGE_DELETED)
234
else if (server_cleared & CAMEL_MESSAGE_DELETED)
237
if (server_set & CAMEL_MESSAGE_JUNK)
239
else if (server_cleared & CAMEL_MESSAGE_JUNK)
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" : "")));
247
folder->summary->unread_count -= read;
249
folder->summary->deleted_count += deleted;
251
folder->summary->junk_count += junk;
252
if (junk && !deleted)
253
folder->summary->junk_not_deleted_count += junk;
255
folder->summary->visible_count -= junk ? junk : deleted;
257
xinfo->info.flags = (xinfo->info.flags | server_set) & ~server_cleared;
258
xinfo->server_flags = server_flags;
259
xinfo->info.dirty = TRUE;
261
camel_folder_summary_touch (info->summary);
265
if ((folder->permanent_flags & CAMEL_MESSAGE_USER) != 0 && imapx_update_user_flags (info, server_user_flags))
272
imapx_set_message_info_flags_for_new_message (CamelMessageInfo *info, guint32 server_flags, CamelFlag *server_user_flags, CamelFolder *folder)
274
CamelMessageInfoBase *binfo = (CamelMessageInfoBase *) info;
275
CamelIMAPXMessageInfo *xinfo = (CamelIMAPXMessageInfo *) info;
276
gint unread=0, deleted=0, junk=0;
279
binfo->flags |= server_flags;
280
xinfo->server_flags = server_flags;
282
if (folder->permanent_flags & CAMEL_MESSAGE_USER)
283
imapx_update_user_flags (info, server_user_flags);
285
/* update the summary count */
286
flags = binfo->flags;
288
if (!(flags & CAMEL_MESSAGE_SEEN))
291
if (flags & CAMEL_MESSAGE_DELETED)
294
if (flags & CAMEL_MESSAGE_JUNK)
297
if (folder->summary) {
300
folder->summary->unread_count += unread;
302
folder->summary->deleted_count += deleted;
304
folder->summary->junk_count += junk;
305
if (junk && !deleted)
306
folder->summary->junk_not_deleted_count += junk;
307
folder->summary->visible_count++;
309
folder->summary->visible_count -= junk ? junk : deleted;
311
folder->summary->saved_count++;
312
camel_folder_summary_touch(folder->summary);
315
binfo->flags &= ~CAMEL_MESSAGE_FOLDER_FLAGGED;
319
imapx_update_summary_for_removed_message (CamelMessageInfo *info, CamelFolder *folder)
321
CamelMessageInfoBase *dinfo = (CamelMessageInfoBase *) info;
322
gint unread=0, deleted=0, junk=0;
325
flags = dinfo->flags;
326
if (!(flags & CAMEL_MESSAGE_SEEN))
329
if (flags & CAMEL_MESSAGE_DELETED)
332
if (flags & CAMEL_MESSAGE_JUNK)
336
folder->summary->unread_count--;
339
folder->summary->deleted_count--;
341
folder->summary->junk_count--;
343
if (junk && !deleted)
344
folder->summary->junk_not_deleted_count--;
346
if (!junk && !deleted)
347
folder->summary->visible_count--;
349
folder->summary->saved_count--;
353
imapx_update_store_summary (CamelFolder *folder)
357
si = camel_store_summary_path ((CamelStoreSummary *) ((CamelIMAPXStore *) folder->parent_store)->summary, folder->full_name);
359
guint32 unread, total;
361
camel_object_get(folder, NULL, CAMEL_FOLDER_TOTAL, &total, CAMEL_FOLDER_UNREAD, &unread, NULL);
362
if (si->unread != unread || si->total != total) {
366
camel_store_summary_touch ((CamelStoreSummary *)((CamelIMAPXStore *) folder->parent_store)->summary);
367
camel_store_summary_save ((CamelStoreSummary *)((CamelIMAPXStore *) folder->parent_store)->summary);
373
capability_data ::= "CAPABILITY" SPACE [1#capability SPACE] "IMAP4rev1"
375
;; IMAP4rev1 servers which offer RFC 1730
376
;; compatibility MUST list "IMAP4" as the first
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 },
394
struct _capability_info *
395
imapx_parse_capability(CamelIMAPXStream *stream, CamelException *ex)
399
guchar *token, *p, c;
400
gboolean free_token = FALSE;
401
struct _capability_info * cinfo;
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);
406
/* FIXME: handle auth types */
407
while (!camel_exception_is_set (ex) && (tok = camel_imapx_stream_token(stream, &token, &len, ex)) != '\n') {
410
token = (guchar *) g_strconcat ((gchar *)token, "+", NULL);
412
case IMAPX_TOK_TOKEN:
413
case IMAPX_TOK_STRING:
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));
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;
435
camel_exception_set (ex, 1, "capability: expecting name");
440
if (camel_exception_is_set (ex)) {
441
imapx_free_capability(cinfo);
448
void imapx_free_capability(struct _capability_info *cinfo)
450
g_hash_table_destroy (cinfo->auth_types);
454
struct _CamelIMAPXNamespaceList *
455
imapx_parse_namespace_list (CamelIMAPXStream *stream, CamelException *ex)
457
CamelIMAPXStoreNamespace *namespaces[3], *node, *tail;
458
CamelIMAPXNamespaceList *nsl = NULL;
464
nsl = g_malloc0(sizeof(CamelIMAPXNamespaceList));
465
nsl->personal = NULL;
469
tok = camel_imapx_stream_token (stream, &token, &len, ex);
471
namespaces[n] = NULL;
472
tail = (CamelIMAPXStoreNamespace *) &namespaces[n];
475
tok = camel_imapx_stream_token (stream, &token, &len, ex);
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");
484
node = g_new0 (CamelIMAPXStoreNamespace, 1);
486
node->path = g_strdup ((gchar *) token);
488
tok = camel_imapx_stream_token (stream, &token, &len, ex);
490
if (tok == IMAPX_TOK_STRING) {
491
if (strlen ((gchar *) token) == 1) {
495
node->sep = node->path [strlen (node->path) - 1];
499
} else if (tok == IMAPX_TOK_TOKEN) {
500
/* will a NIL be possible here? */
503
camel_exception_set (ex, 1, "namespace: expected a string separator");
512
if (*node->path && node->path [strlen (node->path) -1] == node->sep)
513
node->path [strlen (node->path) - 1] = '\0';
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);
519
/* TODO remove full_name later. not required */
520
node->full_name = g_strdup (node->path);
522
tok = camel_imapx_stream_token (stream, &token, &len, ex);
524
camel_exception_set (ex, 1, "namespace: expected a ')'");
528
tok = camel_imapx_stream_token (stream, &token, &len, ex);
532
camel_exception_set (ex, 1, "namespace: expected a ')'");
536
} else if (tok == IMAPX_TOK_TOKEN && !strcmp ((gchar *) token, "NIL")) {
537
namespaces [n] = NULL;
539
camel_exception_set (ex, 1, "namespace: expected either a '(' or NIL");
543
tok = camel_imapx_stream_token (stream, &token, &len, ex);
547
nsl->personal = namespaces [0];
548
nsl->shared = namespaces [1];
549
nsl->other = namespaces [2];
554
for (i=0; i < 3; i++)
555
imapx_namespace_clear (&namespaces [i]);
561
body ::= "(" body_type_1part / body_type_mpart ")"
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.
571
body_ext_1part ::= body_fld_md5 [SPACE body_fld_dsp
573
[SPACE 1#body_extension]]]
574
;; MUST NOT be returned on non-extensible
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
583
body_fields ::= body_fld_param SPACE body_fld_id SPACE
584
body_fld_desc SPACE body_fld_enc SPACE
587
body_fld_desc ::= nstring
589
body_fld_dsp ::= "(" string SPACE body_fld_param ")" / nil
591
body_fld_enc ::= (<"> ("7BIT" / "8BIT" / "BINARY" / "BASE64"/
592
"QUOTED-PRINTABLE") <">) / string
594
body_fld_id ::= nstring
596
body_fld_lang ::= nstring / "(" 1#string ")"
598
body_fld_lines ::= number
600
body_fld_md5 ::= nstring
602
body_fld_octets ::= number
604
body_fld_param ::= "(" 1#(string SPACE string) ")" / nil
606
body_type_1part ::= (body_type_basic / body_type_msg / body_type_text)
607
[SPACE body_ext_1part]
609
body_type_basic ::= media_basic SPACE body_fields
610
;; MESSAGE subtype MUST NOT be "RFC822"
612
body_type_mpart ::= 1*body SPACE media_subtype
613
[SPACE body_ext_mpart]
615
body_type_msg ::= media_message SPACE body_fields SPACE envelope
616
SPACE body SPACE body_fld_lines
618
body_type_text ::= media_text SPACE body_fields SPACE body_fld_lines
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 ")"
625
env_bcc ::= "(" 1*address ")" / nil
627
env_cc ::= "(" 1*address ")" / nil
631
env_from ::= "(" 1*address ")" / nil
633
env_in_reply_to ::= nstring
635
env_message_id ::= nstring
637
env_reply_to ::= "(" 1*address ")" / nil
639
env_sender ::= "(" 1*address ")" / nil
641
env_subject ::= nstring
643
env_to ::= "(" 1*address ")" / nil
645
media_basic ::= (<"> ("APPLICATION" / "AUDIO" / "IMAGE" /
646
"MESSAGE" / "VIDEO") <">) / string)
648
;; Defined in [MIME-IMT]
650
media_message ::= <"> "MESSAGE" <"> SPACE <"> "RFC822" <">
651
;; Defined in [MIME-IMT]
653
media_subtype ::= string
654
;; Defined in [MIME-IMT]
656
media_text ::= <"> "TEXT" <"> SPACE media_subtype
657
;; Defined in [MIME-IMT]
659
( "type" "subtype" body_fields [envelope body body_fld_lines]
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"))
671
struct _body_fields {
672
CamelContentType *ct;
674
CamelTransferEncoding encoding;
679
imapx_free_body(struct _CamelMessageContentInfo *cinfo)
681
struct _CamelMessageContentInfo *list, *next;
683
list = cinfo->childs;
686
imapx_free_body(list);
691
camel_content_type_unref(cinfo->type);
693
g_free(cinfo->description);
694
g_free(cinfo->encoding);
699
imapx_parse_param_list(CamelIMAPXStream *is, struct _camel_header_param **plist, CamelException *ex)
706
p(printf("body_fld_param\n"));
708
/* body_fld_param ::= "(" 1#(string SPACE string) ")" / nil */
709
tok = camel_imapx_stream_token(is, &token, &len, ex);
712
tok = camel_imapx_stream_token(is, &token, &len, ex);
715
camel_imapx_stream_ungettoken(is, tok, token, len);
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);
723
} /* else check nil? no need */
726
struct _CamelContentDisposition *
727
imapx_parse_ext_optional(CamelIMAPXStream *is, CamelException *ex)
732
struct _CamelContentDisposition *dinfo = NULL;
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 */
737
/* body_ext_1part ::= body_fld_md5 [SPACE body_fld_dsp
739
[SPACE 1#body_extension]]]
740
;; MUST NOT be returned on non-extensible
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
749
/* body_fld_dsp ::= "(" string SPACE body_fld_param ")" / nil */
751
tok = camel_imapx_stream_token(is, &token, &len, ex);
754
dinfo = g_malloc0(sizeof(*dinfo));
756
/* should be string */
757
camel_imapx_stream_astring(is, &token, ex);
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"));
765
camel_exception_set (ex, 1, "body_fld_disp: expecting nil or list");
769
p(printf("body_fld_lang\n"));
771
/* body_fld_lang ::= nstring / "(" 1#string ")" */
773
/* we just drop the lang string/list, save it somewhere? */
775
tok = camel_imapx_stream_token(is, &token, &len, ex);
779
tok = camel_imapx_stream_token(is, &token, &len, ex);
782
} else if (tok != IMAPX_TOK_STRING) {
783
camel_exception_set (ex, 1, "expecting string");
788
case IMAPX_TOK_TOKEN:
789
d(printf("body_fld_lang = nil\n"));
792
case IMAPX_TOK_STRING:
793
/* we have a string */
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));
805
if camel_exception_is_set (ex) {
807
camel_content_disposition_unref(dinfo);
813
struct _CamelMessageContentInfo *
814
imapx_parse_body_fields(CamelIMAPXStream *is, CamelException *ex)
818
struct _CamelMessageContentInfo *cinfo;
820
/* body_fields ::= body_fld_param SPACE body_fld_id SPACE
821
body_fld_desc SPACE body_fld_enc SPACE
824
p(printf("body_fields\n"));
826
cinfo = g_malloc0(sizeof(*cinfo));
828
/* this should be string not astring */
829
camel_imapx_stream_astring(is, &token, ex);
830
if (camel_exception_is_set (ex))
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))
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))
842
/* body_fld_id ::= nstring */
843
camel_imapx_stream_nstring(is, &token, ex);
844
if (camel_exception_is_set (ex))
846
cinfo->id = g_strdup((gchar *) token);
848
/* body_fld_desc ::= nstring */
849
camel_imapx_stream_nstring(is, &token, ex);
850
if (camel_exception_is_set (ex))
852
cinfo->description = g_strdup((gchar *) token);
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))
859
cinfo->encoding = g_strdup((gchar *) token);
861
/* body_fld_octets ::= number */
862
cinfo->size = camel_imapx_stream_number(is, ex);
863
if (camel_exception_is_set (ex))
868
imapx_free_body(cinfo);
872
struct _camel_header_address *
873
imapx_parse_address_list(CamelIMAPXStream *is, CamelException *ex)
874
/* throws PARSE,IO exception */
878
guchar *token, *host;
880
struct _camel_header_address *list = NULL;
882
/* "(" 1*address ")" / nil */
884
tok = camel_imapx_stream_token(is, &token, &len, ex);
887
struct _camel_header_address *addr, *group = NULL;
889
/* address ::= "(" addr_name SPACE addr_adl SPACE addr_mailbox
890
SPACE addr_host ")" */
891
tok = camel_imapx_stream_token(is, &token, &len, ex);
895
camel_header_address_list_clear(&list);
896
camel_exception_set (ex, 1, "missing '(' for address");
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);
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. */
915
tok = camel_imapx_stream_nstring(is,(guchar **) &mbox, ex);
916
mbox = g_strdup(mbox);
917
tok = camel_imapx_stream_nstring(is, &host, ex);
922
d(printf("adding group '%s'\n", mbox));
925
addr->type = CAMEL_HEADER_ADDRESS_GROUP;
926
camel_header_address_list_append(&list, addr);
930
addr->v.addr = g_strdup_printf("%s%s%s", mbox? mbox:"", host?"@":"", host?(gchar *)host:"");
932
d(printf("adding address '%s'\n", addr->v.addr));
934
camel_header_address_add_member(group, addr);
936
camel_header_address_list_append(&list, addr);
939
tok = camel_imapx_stream_token(is, &token, &len, ex);
940
} while (tok != ')');
943
d(printf("empty, nil '%s'\n", token));
946
/* CHEN TODO handle exception at required places */
951
struct _CamelMessageInfo *
952
imapx_parse_envelope(CamelIMAPXStream *is, CamelException *ex)
957
struct _camel_header_address *addr, *addr_from;
959
struct _CamelMessageInfoBase *minfo;
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 ")" */
966
p(printf("envelope\n"));
968
minfo = (CamelMessageInfoBase *)camel_message_info_new(NULL);
970
tok = camel_imapx_stream_token(is, &token, &len, ex);
972
camel_message_info_free(minfo);
973
camel_exception_set (ex, 1, "envelope: expecting '('");
977
/* env_date ::= nstring */
978
camel_imapx_stream_nstring(is, &token, ex);
979
minfo->date_sent = camel_header_decode_date((gchar *) token, NULL);
981
/* env_subject ::= nstring */
982
tok = camel_imapx_stream_nstring(is, &token, ex);
983
minfo->subject = camel_pstring_strdup((gchar *) token);
985
/* we merge from/sender into from, append should probably merge more smartly? */
987
/* env_from ::= "(" 1*address ")" / nil */
988
addr_from = imapx_parse_address_list(is, ex);
990
/* env_sender ::= "(" 1*address ")" / nil */
991
addr = imapx_parse_address_list(is, ex);
993
camel_header_address_list_clear(&addr);
996
camel_header_address_list_append_list(&addr_from, &addr);
1004
addrstr = camel_header_address_list_format(addr_from);
1005
minfo->from = camel_pstring_strdup(addrstr);
1007
camel_header_address_list_clear(&addr_from);
1010
/* we dont keep reply_to */
1012
/* env_reply_to ::= "(" 1*address ")" / nil */
1013
addr = imapx_parse_address_list(is, ex);
1014
camel_header_address_list_clear(&addr);
1016
/* env_to ::= "(" 1*address ")" / nil */
1017
addr = imapx_parse_address_list(is, ex);
1019
addrstr = camel_header_address_list_format(addr);
1020
minfo->to = camel_pstring_strdup(addrstr);
1022
camel_header_address_list_clear(&addr);
1025
/* env_cc ::= "(" 1*address ")" / nil */
1026
addr = imapx_parse_address_list(is, ex);
1028
addrstr = camel_header_address_list_format(addr);
1029
minfo->cc = camel_pstring_strdup(addrstr);
1031
camel_header_address_list_clear(&addr);
1034
/* we dont keep bcc either */
1036
/* env_bcc ::= "(" 1*address ")" / nil */
1037
addr = imapx_parse_address_list(is, ex);
1038
camel_header_address_list_clear(&addr);
1040
/* FIXME: need to put in-reply-to into references hash list */
1042
/* env_in_reply_to ::= nstring */
1043
tok = camel_imapx_stream_nstring(is, &token, ex);
1045
/* FIXME: need to put message-id into message-id hash */
1047
/* env_message_id ::= nstring */
1048
tok = camel_imapx_stream_nstring(is, &token, ex);
1050
tok = camel_imapx_stream_token(is, &token, &len, ex);
1052
camel_message_info_free(minfo);
1053
camel_exception_set (ex, 1, "expecting ')'");
1057
/* CHEN TODO handle exceptions better */
1059
return (CamelMessageInfo *)minfo;
1062
struct _CamelMessageContentInfo *
1063
imapx_parse_body(CamelIMAPXStream *is, CamelException *ex)
1068
struct _CamelMessageContentInfo * cinfo = NULL;
1069
struct _CamelMessageContentInfo *subinfo, *last;
1070
struct _CamelContentDisposition * dinfo = NULL;
1071
struct _CamelMessageInfo * minfo = NULL;
1073
/* body ::= "(" body_type_1part / body_type_mpart ")" */
1075
p(printf("body\n"));
1077
tok = camel_imapx_stream_token(is, &token, &len, ex);
1079
camel_exception_set (ex, 1, "body: expecting '('");
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);
1087
/* body_type_mpart ::= 1*body SPACE media_subtype
1088
[SPACE body_ext_mpart] */
1090
cinfo = g_malloc0(sizeof(*cinfo));
1091
last = (struct _CamelMessageContentInfo *)&cinfo->childs;
1093
subinfo = imapx_parse_body(is, ex);
1094
last->next = 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 == '(');
1101
d(printf("media_subtype\n"));
1103
camel_imapx_stream_astring(is, &token, ex);
1104
cinfo->type = camel_content_type_new("multipart", (gchar *) token);
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
1112
d(printf("body_ext_mpart\n"));
1114
tok = camel_imapx_stream_token(is, &token, &len, ex);
1115
camel_imapx_stream_ungettoken(is, tok, token, len);
1117
imapx_parse_param_list(is, &cinfo->type->params, ex);
1119
/* body_fld_dsp ::= "(" string SPACE body_fld_param ")" / nil */
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 */
1127
camel_imapx_stream_ungettoken(is, tok, token, len);
1131
/* body_type_1part ::= (body_type_basic / body_type_msg / body_type_text)
1132
[SPACE body_ext_1part]
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 */
1139
d(printf("Single part body\n"));
1141
cinfo = imapx_parse_body_fields(is, ex);
1143
d(printf("envelope?\n"));
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);
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);
1155
d(printf("Scanned envelope - what do i do with it?\n"));
1158
d(printf("fld_lines?\n"));
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);
1166
camel_imapx_stream_ungettoken(is, tok, token, len);
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
1174
d(printf("extension data?\n"));
1177
camel_imapx_stream_nstring(is, &token, ex);
1179
d(printf("md5: %s\n", token?(gchar *)token:"NIL"));
1181
/* body_fld_dsp ::= "(" string SPACE body_fld_param ")" / nil */
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 */
1192
/* soak up any other extension fields that may be present */
1193
/* there should only be simple tokens, no lists */
1195
tok = camel_imapx_stream_token(is, &token, &len, ex);
1197
d(printf("Dropping extension data '%s'\n", token));
1199
} while (tok != ')');
1201
/* CHEN TODO handle exceptions better */
1202
if (camel_exception_is_set (ex)) {
1204
imapx_free_body(cinfo);
1206
camel_content_disposition_unref(dinfo);
1208
camel_message_info_free(minfo);
1212
/* FIXME: do something with the disposition, currently we have no way to pass it out? */
1214
camel_content_disposition_unref(dinfo);
1220
imapx_parse_section(CamelIMAPXStream *is, CamelException *ex)
1225
gchar * section = NULL;
1227
/* currently we only return the part within the [section] specifier
1228
any header fields are parsed, but dropped */
1231
section ::= "[" [section_text /
1232
(nz_number *["." nz_number] ["." (section_text / "MIME")])] "]"
1234
section_text ::= "HEADER" / "HEADER.FIELDS" [".NOT"]
1235
SPACE header_list / "TEXT"
1238
tok = camel_imapx_stream_token(is, &token, &len, ex);
1240
camel_exception_set (ex, 1, "section: expecting '['");
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);
1251
camel_exception_set (ex, 1, "section: expecting token");
1255
/* header_list ::= "(" 1#header_fld_name ")"
1256
header_fld_name ::= astring */
1258
/* we dont need the header specifiers */
1259
tok = camel_imapx_stream_token(is, &token, &len, ex);
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");
1270
} while (tok != ')');
1271
tok = camel_imapx_stream_token(is, &token, &len, ex);
1275
camel_exception_set (ex, 1, "section: expecting ']'");
1284
imapx_free_fetch(struct _fetch_info *finfo)
1290
camel_object_unref((CamelObject *)finfo->body);
1292
camel_object_unref((CamelObject *)finfo->text);
1294
camel_object_unref((CamelObject *)finfo->header);
1296
camel_message_info_free(finfo->minfo);
1298
imapx_free_body(finfo->cinfo);
1299
camel_flag_list_free(&finfo->user_flags);
1300
g_free(finfo->date);
1301
g_free(finfo->section);
1306
#include <camel/camel-stream-fs.h>
1308
/* debug, dump one out */
1310
imapx_dump_fetch(struct _fetch_info *finfo)
1315
d(printf("Fetch info:\n"));
1316
if (finfo == NULL) {
1317
d(printf("Empty\n"));
1322
sout = camel_stream_fs_new_with_fd(fd);
1324
camel_stream_printf(sout, "Body content:\n");
1325
camel_stream_write_to_stream(finfo->body, sout);
1326
camel_stream_reset(finfo->body);
1329
camel_stream_printf(sout, "Text content:\n");
1330
camel_stream_write_to_stream(finfo->text, sout);
1331
camel_stream_reset(finfo->text);
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);
1339
camel_stream_printf(sout, "Message Info:\n");
1340
camel_message_info_dump(finfo->minfo);
1343
camel_stream_printf(sout, "Content Info:\n");
1344
//camel_content_info_dump(finfo->cinfo, 0);
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);
1353
camel_stream_printf(sout, "Date: '%s'\n", finfo->date);
1355
camel_stream_printf(sout, "Section: '%s'\n", finfo->section);
1357
camel_stream_printf(sout, "UID: '%s'\n", finfo->uid);
1358
camel_object_unref((CamelObject *)sout);
1361
struct _fetch_info *
1362
imapx_parse_fetch(CamelIMAPXStream *is, CamelException *ex)
1366
guchar *token, *p, c;
1367
struct _fetch_info *finfo;
1369
finfo = g_malloc0(sizeof(*finfo));
1371
tok = camel_imapx_stream_token(is, &token, &len, ex);
1373
camel_exception_set (ex, 1, "fetch: expecting '('");
1378
while ((tok = camel_imapx_stream_token(is, &token, &len, ex)) == IMAPX_TOK_TOKEN) {
1384
switch (imapx_tokenise((gchar *) token, len)) {
1385
case IMAPX_ENVELOPE:
1386
finfo->minfo = imapx_parse_envelope(is, ex);
1387
finfo->got |= FETCH_MINFO;
1390
imapx_parse_flags(is, &finfo->flags, &finfo->user_flags, ex);
1391
finfo->got |= FETCH_FLAGS;
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;
1399
case IMAPX_RFC822_HEADER:
1400
camel_imapx_stream_nstring_stream(is, &finfo->header, ex);
1401
finfo->got |= FETCH_HEADER;
1403
case IMAPX_RFC822_TEXT:
1404
camel_imapx_stream_nstring_stream(is, &finfo->text, ex);
1405
finfo->got |= FETCH_TEXT;
1407
case IMAPX_RFC822_SIZE:
1408
finfo->size = camel_imapx_stream_number(is, ex);
1409
finfo->got |= FETCH_SIZE;
1411
case IMAPX_BODYSTRUCTURE:
1412
finfo->cinfo = imapx_parse_body(is, ex);
1413
finfo->got |= FETCH_CINFO;
1416
tok = camel_imapx_stream_token(is, &token, &len, ex);
1417
camel_imapx_stream_ungettoken(is, tok, token, len);
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);
1428
camel_imapx_stream_ungettoken(is, tok, token, len);
1430
camel_imapx_stream_nstring_stream(is, &finfo->body, ex);
1431
finfo->got |= FETCH_BODY;
1433
camel_exception_set (ex, 1, "unknown body response");
1434
imapx_free_fetch(finfo);
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");
1444
finfo->uid = g_strdup((gchar *) token);
1445
finfo->got |= FETCH_UID;
1448
imapx_free_fetch(finfo);
1449
camel_exception_set (ex, 1, "unknown body response");
1455
camel_exception_set (ex, 1, "missing closing ')' on fetch response");
1456
imapx_free_fetch (finfo);
1463
struct _state_info *
1464
imapx_parse_status_info (struct _CamelIMAPXStream *is, CamelException *ex)
1466
struct _state_info *sinfo;
1471
sinfo = g_malloc0 (sizeof(*sinfo));
1473
/* skip the folder name */
1474
camel_imapx_stream_token (is, &token, &len, ex);
1476
tok = camel_imapx_stream_token(is, &token, &len, ex);
1478
camel_exception_set (ex, 1, "parse status info: expecting '('");
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);
1489
sinfo->recent = camel_imapx_stream_number (is, ex);
1492
sinfo->uidnext = camel_imapx_stream_number (is, ex);
1494
case IMAPX_UIDVALIDITY:
1495
sinfo->uidvalidity = camel_imapx_stream_number (is, ex);
1498
sinfo->unseen = camel_imapx_stream_number (is, ex);
1502
camel_exception_set (ex, 1, "unknown status response");
1508
camel_exception_set (ex, 1, "missing closing ')' on status response");
1517
generate_uids_from_sequence (GPtrArray *uids, guint32 begin_uid, guint32 end_uid)
1521
for (i = begin_uid; i <= end_uid; i++)
1522
g_ptr_array_add (uids, GUINT_TO_POINTER (i));
1526
imapx_parse_uids (CamelIMAPXStream *is, CamelException *ex)
1528
GPtrArray *uids = g_ptr_array_new ();
1534
tok = camel_imapx_stream_token (is, &token, &len, ex);
1535
splits = g_strsplit ((gchar *) token, ",", -1);
1536
str_len = g_strv_length (splits);
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);
1544
generate_uids_from_sequence (uids, uid1, uid2);
1547
guint32 uid = strtoul ((gchar *) token, NULL, 10);
1548
g_ptr_array_add (uids, GUINT_TO_POINTER (uid));
1552
g_strfreev (splits);
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)
1565
struct _status_info *sinfo;
1567
sinfo = g_malloc0(sizeof(*sinfo));
1569
camel_imapx_stream_atom(is, &token, &len, ex);
1572
resp_cond_auth ::= ("OK" / "PREAUTH") SPACE resp_text
1573
;; Authentication condition
1575
resp_cond_bye ::= "BYE" SPACE resp_text
1577
resp_cond_state ::= ("OK" / "NO" / "BAD") SPACE resp_text
1581
sinfo->result = imapx_tokenise((gchar *) token, len);
1582
switch (sinfo->result) {
1590
camel_exception_set (ex, 1, "expecting OK/NO/BAD");
1595
tok = camel_imapx_stream_token(is, &token, &len, ex);
1597
camel_imapx_stream_atom(is, &token, &len, ex);
1598
sinfo->condition = imapx_tokenise((gchar *) token, len);
1600
/* parse any details */
1601
switch (sinfo->condition) {
1602
case IMAPX_READ_ONLY:
1603
case IMAPX_READ_WRITE:
1606
case IMAPX_TRYCREATE:
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);
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);
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);
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);
1628
case IMAPX_UIDVALIDITY:
1629
sinfo->u.uidvalidity = camel_imapx_stream_number(is, ex);
1632
sinfo->u.uidnext = camel_imapx_stream_number (is, ex);
1635
sinfo->u.unseen = camel_imapx_stream_number(is, ex);
1638
sinfo->condition = IMAPX_UNKNOWN;
1639
d(printf("Got unknown response code: %s: ignored\n", token));
1642
/* ignore anything we dont know about */
1644
tok = camel_imapx_stream_token(is, &token, &len, ex);
1646
camel_exception_set (ex, 1, "server response truncated");
1647
imapx_free_status(sinfo);
1650
} while (tok != ']');
1652
camel_imapx_stream_ungettoken(is, tok, token, len);
1655
/* and take the human readable response */
1656
camel_imapx_stream_text(is, (guchar **)&sinfo->text, ex);
1661
struct _status_info *
1662
imapx_copy_status(struct _status_info *sinfo)
1664
struct _status_info *out;
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);
1678
imapx_free_status(struct _status_info *sinfo)
1683
switch (sinfo->condition) {
1685
g_free(sinfo->u.newname.oldname);
1686
g_free(sinfo->u.newname.newname);
1689
g_ptr_array_free (sinfo->u.copyuid.uids, FALSE);
1690
g_ptr_array_free (sinfo->u.copyuid.copied_uids, FALSE);
1696
g_free(sinfo->text);
1700
/* FIXME: use tokeniser? */
1701
/* FIXME: real flags */
1705
} list_flag_table[] = {
1706
{ "\\NOINFERIORS", CAMEL_FOLDER_NOINFERIORS },
1707
{ "\\NOSELECT", CAMEL_FOLDER_NOSELECT },
1708
{ "\\MARKED", 1<< 16},
1709
{ "\\UNMARKED", 1<< 17},
1713
imapx_parse_list(CamelIMAPXStream *is, CamelException *ex)
1714
/* throws io, parse */
1718
guchar *token, *p, c;
1719
struct _list_info * linfo;
1721
linfo = g_malloc0(sizeof(*linfo));
1723
/* mailbox_list ::= "(" #("\Marked" / "\Noinferiors" /
1724
"\Noselect" / "\Unmarked" / flag_extension) ")"
1725
SPACE (<"> QUOTED_CHAR <"> / nil) SPACE mailbox */
1727
tok = camel_imapx_stream_token(is, &token, &len, ex);
1729
camel_exception_set (ex, 1, "list: expecting '('");
1734
while ((tok = camel_imapx_stream_token(is, &token, &len, ex)) != ')') {
1735
if (tok == IMAPX_TOK_STRING || tok == IMAPX_TOK_TOKEN) {
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;
1743
imapx_free_list(linfo);
1744
camel_exception_set (ex, 1, "list: expecting flag or ')'");
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);
1758
imapx_list_get_path(struct _list_info *li)
1764
if (li->separator != 0 && li->separator != '/') {
1765
p = path = alloca(strlen(li->name)*3+1);
1767
while ((c = *f++ & 0xff)) {
1768
if (c == li->separator)
1770
else if (c == '/' || c == '%')
1771
p += sprintf(p, "%%%02X", c);
1779
return camel_utf7_utf8(path);
1783
imapx_free_list(struct _list_info *linfo)
1786
g_free(linfo->name);
1791
/* ********************************************************************** */
1796
ATOM_CHAR ::= <any CHAR except atom_specials>
1798
atom_specials ::= "(" / ")" / "{" / SPACE / CTL / list_wildcards /
1801
CHAR ::= <any 7-bit US-ASCII character except NUL,
1804
CTL ::= <any ASCII control character and DEL,
1807
SPACE ::= <ASCII SP, space, 0x20>
1809
list_wildcards ::= "%" / "*"
1811
quoted_specials ::= <"> / "\"
1813
string ::= quoted / literal
1815
literal ::= "{" number "}" CRLF *CHAR8
1816
;; Number represents the number of CHAR8 octets
1818
quoted ::= <"> *QUOTED_CHAR <">
1820
QUOTED_CHAR ::= <any TEXT_CHAR except quoted_specials> /
1823
TEXT_CHAR ::= <any CHAR except CR and LF>
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,
1855
#define list_wildcards "*%"
1856
#define quoted_specials "\\\""
1857
#define atom_specials "(){" list_wildcards quoted_specials /* + CTL */
1859
/* special types for the tokeniser, come out as raw tokens */
1860
#define token_specials "\n*()[]+"
1861
#define notid_specials "\x20\r\n()[]+"
1863
void imapx_utils_init(void)
1868
for (i=0;i<128;i++) {
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;
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;
1885
imapx_specials[i] = v;
1889
guchar imapx_is_mask(const gchar *p)
1894
v &= imapx_specials[((guchar)*p) & 0xff];
1902
imapx_path_to_physical (const gchar *prefix, const gchar *vpath)
1904
GString *out = g_string_new(prefix);
1905
const gchar *p = vpath;
1908
g_string_append_c(out, '/');
1910
while ((c = *p++)) {
1912
g_string_append(out, "/" SUBFOLDER_DIR_NAME "/");
1916
g_string_append_c(out, c);
1920
g_string_free(out, FALSE);
1926
imapx_concat (CamelIMAPXStore *imapx_store, const gchar *prefix, const gchar *suffix)
1930
len = strlen (prefix);
1931
if (len == 0 || prefix[len - 1] == imapx_store->dir_sep)
1932
return g_strdup_printf ("%s%s", prefix, suffix);
1934
return g_strdup_printf ("%s%c%s", prefix, imapx_store->dir_sep, suffix);
1938
imapx_namespace_clear (CamelIMAPXStoreNamespace **ns)
1940
CamelIMAPXStoreNamespace *node, *next;
1943
while (node != NULL) {
1945
g_free (node->path);
1954
camel_imapx_namespace_list_clear (struct _CamelIMAPXNamespaceList *nsl)
1959
imapx_namespace_clear (&nsl->personal);
1960
imapx_namespace_clear (&nsl->shared);
1961
imapx_namespace_clear (&nsl->other);
1967
static CamelIMAPXStoreNamespace *
1968
imapx_namespace_copy (const CamelIMAPXStoreNamespace *ns)
1970
CamelIMAPXStoreNamespace *list, *node, *tail;
1973
tail = (CamelIMAPXStoreNamespace *) &list;
1975
while (ns != NULL) {
1976
tail->next = node = g_malloc (sizeof (CamelIMAPXStoreNamespace));
1977
node->path = g_strdup (ns->path);
1978
node->sep = ns->sep;
1988
struct _CamelIMAPXNamespaceList *
1989
camel_imapx_namespace_list_copy (const struct _CamelIMAPXNamespaceList *nsl)
1991
CamelIMAPXNamespaceList *new;
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);
2002
imapx_get_temp_uid (void)
2006
static gint counter = 0;
2007
G_LOCK_DEFINE_STATIC (lock);
2010
res = g_strdup_printf ("tempuid-%lx-%d",
2011
(gulong) time (NULL),