6
#include "camel-folder-summary.h"
7
#include "camel-store.h"
8
#include "camel-string-utils.h"
9
#include "camel-utf8.h"
11
#include "camel-imapp-engine.h"
12
#include "camel-imapp-exception.h"
13
#include "camel-imapp-folder.h"
14
#include "camel-imapp-stream.h"
15
#include "camel-imapp-utils.h"
17
/* high-level parser state */
22
/* ANSI-C code produced by gperf version 2.7 */
23
/* Command-line: gperf -H imap_hash -N imap_tokenise -L ANSI-C -o -t -k1,$ imap-tokens.txt */
24
struct _imap_keyword { gchar *name; camel_imapp_id_t id; };
27
best hash generated using: gperf -o -s-2 -k1,'$' -t -H imap_hash -N imap_tokenise -L ANSI-C
30
#define TOTAL_KEYWORDS 23
31
#define MIN_WORD_LENGTH 2
32
#define MAX_WORD_LENGTH 14
33
#define MIN_HASH_VALUE 2
34
#define MAX_HASH_VALUE 38
35
/* maximum key range = 37, duplicates = 0 */
41
imap_hash (register const gchar *str, register guint len)
43
static guchar asso_values[] =
45
39, 39, 39, 39, 39, 39, 39, 39, 39, 39,
46
39, 39, 39, 39, 39, 39, 39, 39, 39, 39,
47
39, 39, 39, 39, 39, 39, 39, 39, 39, 39,
48
39, 39, 39, 39, 39, 39, 39, 39, 39, 39,
49
39, 39, 39, 39, 39, 39, 39, 39, 39, 39,
50
39, 39, 39, 39, 39, 39, 39, 39, 39, 39,
51
39, 39, 39, 39, 39, 10, 15, 39, 20, 0,
52
0, 39, 0, 10, 39, 0, 39, 39, 10, 0,
53
0, 39, 0, 10, 5, 10, 39, 39, 39, 0,
54
39, 39, 39, 39, 39, 39, 39, 39, 39, 39,
55
39, 39, 39, 39, 39, 39, 39, 39, 39, 39,
56
39, 39, 39, 39, 39, 39, 39, 39, 39, 39,
57
39, 39, 39, 39, 39, 39, 39, 39, 39, 39,
58
39, 39, 39, 39, 39, 39, 39, 39, 39, 39,
59
39, 39, 39, 39, 39, 39, 39, 39, 39, 39,
60
39, 39, 39, 39, 39, 39, 39, 39, 39, 39,
61
39, 39, 39, 39, 39, 39, 39, 39, 39, 39,
62
39, 39, 39, 39, 39, 39, 39, 39, 39, 39,
63
39, 39, 39, 39, 39, 39, 39, 39, 39, 39,
64
39, 39, 39, 39, 39, 39, 39, 39, 39, 39,
65
39, 39, 39, 39, 39, 39, 39, 39, 39, 39,
66
39, 39, 39, 39, 39, 39, 39, 39, 39, 39,
67
39, 39, 39, 39, 39, 39, 39, 39, 39, 39,
68
39, 39, 39, 39, 39, 39, 39, 39, 39, 39,
69
39, 39, 39, 39, 39, 39, 39, 39, 39, 39,
70
39, 39, 39, 39, 39, 39
72
return len + asso_values[(guchar)str[len - 1]] + asso_values[(guchar)str[0]];
79
imap_tokenise (register const gchar *str, register guint len)
81
static struct _imap_keyword wordlist[] =
86
{"PARSE", IMAP_PARSE},
88
{"PREAUTH", IMAP_PREAUTH},
89
{"ENVELOPE", IMAP_ENVELOPE},
90
{"READ-ONLY", IMAP_READ_ONLY},
91
{"READ-WRITE", IMAP_READ_WRITE},
92
{"RFC822.SIZE", IMAP_RFC822_SIZE},
94
{"RFC822.HEADER", IMAP_RFC822_HEADER},
95
{"TRYCREATE", IMAP_TRYCREATE},
96
{"FLAGS", IMAP_FLAGS},
97
{"RFC822.TEXT", IMAP_RFC822_TEXT},
98
{"NEWNAME", IMAP_NEWNAME},
101
{"ALERT", IMAP_ALERT},
102
{"UIDVALIDITY", IMAP_UIDVALIDITY},
103
{"INTERNALDATE", IMAP_INTERNALDATE},
105
{"PERMANENTFLAGS", IMAP_PERMANENTFLAGS},
107
{"UNSEEN", IMAP_UNSEEN},
109
{"BODYSTRUCTURE", IMAP_BODYSTRUCTURE},
110
{""}, {""}, {""}, {""},
112
{""}, {""}, {""}, {""},
116
if (len <= MAX_WORD_LENGTH && len >= MIN_WORD_LENGTH)
118
register gint key = imap_hash (str, len);
120
if (key <= MAX_HASH_VALUE && key >= 0)
122
register const gchar *s = wordlist[key].name;
124
if (*str == *s && !strcmp (str + 1, s + 1))
125
return wordlist[key].id;
136
{ "\\ANSWERED", CAMEL_MESSAGE_ANSWERED },
137
{ "\\DELETED", CAMEL_MESSAGE_DELETED },
138
{ "\\DRAFT", CAMEL_MESSAGE_DRAFT },
139
{ "\\FLAGGED", CAMEL_MESSAGE_FLAGGED },
140
{ "\\SEEN", CAMEL_MESSAGE_SEEN },
141
{ "\\RECENT", CAMEL_IMAPP_MESSAGE_RECENT },
142
{ "\\*", CAMEL_MESSAGE_USER },
146
shoudl this be part of imapp-driver? */
147
/* mabye this should be a stream op? */
149
imap_parse_flags(CamelIMAPPStream *stream, guint32 *flagsp)
150
/* throws IO,PARSE exception */
153
guchar *token, *p, c;
158
tok = camel_imapp_stream_token(stream, &token, &len);
161
tok = camel_imapp_stream_token(stream, &token, &len);
162
if (tok == IMAP_TOK_TOKEN) {
166
for (i=0;i<(gint)(sizeof(flag_table)/sizeof(flag_table[0]));i++)
167
if (!strcmp(token, flag_table[i].name))
168
flags |= flag_table[i].flag;
169
} else if (tok != ')') {
170
camel_exception_throw(1, "expecting flag");
172
} while (tok != ')');
174
camel_exception_throw(1, "expecting flag list");
181
imap_write_flags(CamelStream *stream, guint32 flags)
182
/* throws IO exception */
186
/* all this ugly exception throwing goes away once camel streams throw their own? */
187
if (camel_stream_write(stream, "(", 1) == -1)
188
camel_exception_throw(1, "io error: %s", g_strerror(errno));
190
for (i=0;flags!=0 && i<(gint)(sizeof(flag_table)/sizeof(flag_table[0]));i++) {
191
if (flag_table[i].flag & flags) {
192
if (camel_stream_write(stream, flag_table[i].name, strlen(flag_table[i].name)) == -1)
193
camel_exception_throw(1, "io error: %s", g_strerror(errno));
194
flags &= ~flag_table[i].flag;
196
if (camel_stream_write(stream, " ", 1) == -1)
197
camel_exception_throw(1, "io error: %s", g_strerror(errno));
201
if (camel_stream_write(stream, ")", 1) == -1)
202
camel_exception_throw(1, "io error: %s", g_strerror(errno));
206
body ::= "(" body_type_1part / body_type_mpart ")"
208
body_extension ::= nstring / number / "(" 1#body_extension ")"
209
;; Future expansion. Client implementations
210
;; MUST accept body_extension fields. Server
211
;; implementations MUST NOT generate
212
;; body_extension fields except as defined by
213
;; future standard or standards-track
214
;; revisions of this specification.
216
body_ext_1part ::= body_fld_md5 [SPACE body_fld_dsp
218
[SPACE 1#body_extension]]]
219
;; MUST NOT be returned on non-extensible
222
body_ext_mpart ::= body_fld_param
223
[SPACE body_fld_dsp SPACE body_fld_lang
224
[SPACE 1#body_extension]]
225
;; MUST NOT be returned on non-extensible
228
body_fields ::= body_fld_param SPACE body_fld_id SPACE
229
body_fld_desc SPACE body_fld_enc SPACE
232
body_fld_desc ::= nstring
234
body_fld_dsp ::= "(" string SPACE body_fld_param ")" / nil
236
body_fld_enc ::= (<"> ("7BIT" / "8BIT" / "BINARY" / "BASE64"/
237
"QUOTED-PRINTABLE") <">) / string
239
body_fld_id ::= nstring
241
body_fld_lang ::= nstring / "(" 1#string ")"
243
body_fld_lines ::= number
245
body_fld_md5 ::= nstring
247
body_fld_octets ::= number
249
body_fld_param ::= "(" 1#(string SPACE string) ")" / nil
251
body_type_1part ::= (body_type_basic / body_type_msg / body_type_text)
252
[SPACE body_ext_1part]
254
body_type_basic ::= media_basic SPACE body_fields
255
;; MESSAGE subtype MUST NOT be "RFC822"
257
body_type_mpart ::= 1*body SPACE media_subtype
258
[SPACE body_ext_mpart]
260
body_type_msg ::= media_message SPACE body_fields SPACE envelope
261
SPACE body SPACE body_fld_lines
263
body_type_text ::= media_text SPACE body_fields SPACE body_fld_lines
265
envelope ::= "(" env_date SPACE env_subject SPACE env_from
266
SPACE env_sender SPACE env_reply_to SPACE env_to
267
SPACE env_cc SPACE env_bcc SPACE env_in_reply_to
268
SPACE env_message_id ")"
270
env_bcc ::= "(" 1*address ")" / nil
272
env_cc ::= "(" 1*address ")" / nil
276
env_from ::= "(" 1*address ")" / nil
278
env_in_reply_to ::= nstring
280
env_message_id ::= nstring
282
env_reply_to ::= "(" 1*address ")" / nil
284
env_sender ::= "(" 1*address ")" / nil
286
env_subject ::= nstring
288
env_to ::= "(" 1*address ")" / nil
290
media_basic ::= (<"> ("APPLICATION" / "AUDIO" / "IMAGE" /
291
"MESSAGE" / "VIDEO") <">) / string)
293
;; Defined in [MIME-IMT]
295
media_message ::= <"> "MESSAGE" <"> SPACE <"> "RFC822" <">
296
;; Defined in [MIME-IMT]
298
media_subtype ::= string
299
;; Defined in [MIME-IMT]
301
media_text ::= <"> "TEXT" <"> SPACE media_subtype
302
;; Defined in [MIME-IMT]
304
( "type" "subtype" body_fields [envelope body body_fld_lines]
307
(("TEXT" "PLAIN" ("CHARSET"
308
"US-ASCII") NIL NIL "7BIT" 1152 23)("TEXT" "PLAIN"
309
("CHARSET" "US-ASCII" "NAME" "cc.diff")
310
"<960723163407.20117h@cac.washington.edu>"
311
"Compiler diff" "BASE64" 4554 73) "MIXED"))
316
struct _body_fields {
317
CamelContentType *ct;
319
CamelTransferEncoding encoding;
324
imap_free_body(struct _CamelMessageContentInfo *cinfo)
326
struct _CamelMessageContentInfo *list, *next;
328
list = cinfo->childs;
331
imap_free_body(list);
336
camel_content_type_unref(cinfo->type);
338
g_free(cinfo->description);
339
g_free(cinfo->encoding);
344
imap_parse_param_list(CamelIMAPPStream *is, struct _camel_header_param **plist)
347
guchar *token, *param;
349
p(printf("body_fld_param\n"));
351
/* body_fld_param ::= "(" 1#(string SPACE string) ")" / nil */
352
tok = camel_imapp_stream_token(is, &token, &len);
355
tok = camel_imapp_stream_token(is, &token, &len);
358
camel_imapp_stream_ungettoken(is, tok, token, len);
360
camel_imapp_stream_astring(is, &token);
361
param = alloca(strlen(token)+1);
362
strcpy(param, token);
363
camel_imapp_stream_astring(is, &token);
364
camel_header_set_param(plist, param, token);
366
} /* else check nil? no need */
369
struct _CamelContentDisposition *
370
imap_parse_ext_optional(CamelIMAPPStream *is)
374
struct _CamelContentDisposition * volatile dinfo = NULL;
376
/* this parses both extension types, from the body_fld_dsp onwards */
377
/* although the grammars are different, they can be parsed the same way */
379
/* body_ext_1part ::= body_fld_md5 [SPACE body_fld_dsp
381
[SPACE 1#body_extension]]]
382
;; MUST NOT be returned on non-extensible
385
/* body_ext_mpart ::= body_fld_param
386
[SPACE body_fld_dsp SPACE body_fld_lang
387
[SPACE 1#body_extension]]
388
;; MUST NOT be returned on non-extensible
392
/* body_fld_dsp ::= "(" string SPACE body_fld_param ")" / nil */
394
tok = camel_imapp_stream_token(is, &token, &len);
397
dinfo = g_malloc0(sizeof(*dinfo));
399
/* should be string */
400
camel_imapp_stream_astring(is, &token);
402
dinfo->disposition = g_strdup(token);
403
imap_parse_param_list(is, &dinfo->params);
405
d(printf("body_fld_dsp: NIL\n"));
408
camel_exception_throw(1, "body_fld_disp: expecting nil or list");
411
p(printf("body_fld_lang\n"));
413
/* body_fld_lang ::= nstring / "(" 1#string ")" */
415
/* we just drop the lang string/list, save it somewhere? */
417
tok = camel_imapp_stream_token(is, &token, &len);
421
tok = camel_imapp_stream_token(is, &token, &len);
424
} else if (tok != IMAP_TOK_STRING) {
425
camel_exception_throw(1, "expecting string");
430
d(printf("body_fld_lang = nil\n"));
433
case IMAP_TOK_STRING:
434
/* we have a string */
436
case IMAP_TOK_LITERAL:
437
/* we have a literal string */
438
camel_imapp_stream_set_literal(is, len);
439
while ((tok = camel_imapp_stream_getl(is, &token, &len)) > 0) {
440
d(printf("Skip literal data '%.*s'\n", (gint)len, token));
447
camel_content_disposition_unref(dinfo);
448
camel_exception_throw_ex(ex);
454
struct _CamelMessageContentInfo *
455
imap_parse_body_fields(CamelIMAPPStream *is)
457
guchar *token, *type;
458
struct _CamelMessageContentInfo *cinfo;
460
/* body_fields ::= body_fld_param SPACE body_fld_id SPACE
461
body_fld_desc SPACE body_fld_enc SPACE
464
p(printf("body_fields\n"));
466
cinfo = g_malloc0(sizeof(*cinfo));
469
/* this should be string not astring */
470
camel_imapp_stream_astring(is, &token);
471
type = alloca(strlen(token)+1);
473
camel_imapp_stream_astring(is, &token);
474
cinfo->type = camel_content_type_new(type, token);
475
imap_parse_param_list(is, &cinfo->type->params);
477
/* body_fld_id ::= nstring */
478
camel_imapp_stream_nstring(is, &token);
479
cinfo->id = g_strdup(token);
481
/* body_fld_desc ::= nstring */
482
camel_imapp_stream_nstring(is, &token);
483
cinfo->description = g_strdup(token);
485
/* body_fld_enc ::= (<"> ("7BIT" / "8BIT" / "BINARY" / "BASE64"/
486
"QUOTED-PRINTABLE") <">) / string */
487
camel_imapp_stream_astring(is, &token);
488
cinfo->encoding = g_strdup(token);
490
/* body_fld_octets ::= number */
491
cinfo->size = camel_imapp_stream_number(is);
493
imap_free_body(cinfo);
494
camel_exception_throw_ex(ex);
500
struct _camel_header_address *
501
imap_parse_address_list(CamelIMAPPStream *is)
502
/* throws PARSE,IO exception */
505
guchar *token, *host, *mbox;
506
struct _camel_header_address *list = NULL;
508
/* "(" 1*address ")" / nil */
511
tok = camel_imapp_stream_token(is, &token, &len);
514
struct _camel_header_address *addr, *group = NULL;
516
/* address ::= "(" addr_name SPACE addr_adl SPACE addr_mailbox
517
SPACE addr_host ")" */
518
tok = camel_imapp_stream_token(is, &token, &len);
522
camel_exception_throw(1, "missing '(' for address");
524
addr = camel_header_address_new();
525
addr->type = CAMEL_HEADER_ADDRESS_NAME;
526
tok = camel_imapp_stream_nstring(is, &token);
527
addr->name = g_strdup(token);
528
/* we ignore the route, nobody uses it in the real world */
529
tok = camel_imapp_stream_nstring(is, &token);
531
/* [RFC-822] group syntax is indicated by a special
532
form of address structure in which the host name
533
field is NIL. If the mailbox name field is also
534
NIL, this is an end of group marker (semi-colon in
535
RFC 822 syntax). If the mailbox name field is
536
non-NIL, this is a start of group marker, and the
537
mailbox name field holds the group name phrase. */
539
tok = camel_imapp_stream_nstring(is, &mbox);
540
mbox = g_strdup(mbox);
541
tok = camel_imapp_stream_nstring(is, &host);
546
d(printf("adding group '%s'\n", mbox));
549
addr->type = CAMEL_HEADER_ADDRESS_GROUP;
550
camel_header_address_list_append(&list, addr);
554
addr->v.addr = g_strdup_printf("%s%s%s", mbox?(gchar *)mbox:"", host?"@":"", host?(gchar *)host:"");
556
d(printf("adding address '%s'\n", addr->v.addr));
558
camel_header_address_add_member(group, addr);
560
camel_header_address_list_append(&list, addr);
563
tok = camel_imapp_stream_token(is, &token, &len);
564
} while (tok != ')');
567
d(printf("empty, nil '%s'\n", token));
570
camel_header_address_list_clear(&list);
571
camel_exception_throw_ex(ex);
577
struct _CamelMessageInfo *
578
imap_parse_envelope(CamelIMAPPStream *is)
582
struct _camel_header_address *addr, *addr_from;
584
struct _CamelMessageInfoBase *minfo;
586
/* envelope ::= "(" env_date SPACE env_subject SPACE env_from
587
SPACE env_sender SPACE env_reply_to SPACE env_to
588
SPACE env_cc SPACE env_bcc SPACE env_in_reply_to
589
SPACE env_message_id ")" */
591
p(printf("envelope\n"));
593
minfo = (CamelMessageInfoBase *)camel_message_info_new(NULL);
596
tok = camel_imapp_stream_token(is, &token, &len);
598
camel_exception_throw(1, "envelope: expecting '('");
600
/* env_date ::= nstring */
601
camel_imapp_stream_nstring(is, &token);
602
minfo->date_sent = camel_header_decode_date(token, NULL);
604
/* env_subject ::= nstring */
605
tok = camel_imapp_stream_nstring(is, &token);
606
minfo->subject = camel_pstring_strdup(token);
608
/* we merge from/sender into from, append should probably merge more smartly? */
610
/* env_from ::= "(" 1*address ")" / nil */
611
addr_from = imap_parse_address_list(is);
613
/* env_sender ::= "(" 1*address ")" / nil */
614
addr = imap_parse_address_list(is);
616
camel_header_address_list_clear(&addr);
619
camel_header_address_list_append_list(&addr_from, &addr);
627
addrstr = camel_header_address_list_format(addr_from);
628
minfo->from = camel_pstring_strdup(addrstr);
630
camel_header_address_list_clear(&addr_from);
633
/* we dont keep reply_to */
635
/* env_reply_to ::= "(" 1*address ")" / nil */
636
addr = imap_parse_address_list(is);
637
camel_header_address_list_clear(&addr);
639
/* env_to ::= "(" 1*address ")" / nil */
640
addr = imap_parse_address_list(is);
642
addrstr = camel_header_address_list_format(addr);
643
minfo->to = camel_pstring_strdup(addrstr);
645
camel_header_address_list_clear(&addr);
648
/* env_cc ::= "(" 1*address ")" / nil */
649
addr = imap_parse_address_list(is);
651
addrstr = camel_header_address_list_format(addr);
652
minfo->cc = camel_pstring_strdup(addrstr);
654
camel_header_address_list_clear(&addr);
657
/* we dont keep bcc either */
659
/* env_bcc ::= "(" 1*address ")" / nil */
660
addr = imap_parse_address_list(is);
661
camel_header_address_list_clear(&addr);
663
/* FIXME: need to put in-reply-to into references hash list */
665
/* env_in_reply_to ::= nstring */
666
tok = camel_imapp_stream_nstring(is, &token);
668
/* FIXME: need to put message-id into message-id hash */
670
/* env_message_id ::= nstring */
671
tok = camel_imapp_stream_nstring(is, &token);
673
tok = camel_imapp_stream_token(is, &token, &len);
675
camel_exception_throw(1, "expecting ')'");
677
camel_message_info_free(minfo);
678
camel_exception_throw_ex(ex);
681
return (CamelMessageInfo *)minfo;
684
struct _CamelMessageContentInfo *
685
imap_parse_body(CamelIMAPPStream *is)
689
struct _CamelMessageContentInfo * volatile cinfo = NULL;
690
struct _CamelMessageContentInfo *subinfo, *last;
691
struct _CamelContentDisposition * volatile dinfo = NULL;
692
struct _CamelMessageInfo * volatile minfo = NULL;
694
/* body ::= "(" body_type_1part / body_type_mpart ")" */
699
tok = camel_imapp_stream_token(is, &token, &len);
701
camel_exception_throw(1, "body: expecting '('");
703
/* 1*body (optional for multiparts) */
704
tok = camel_imapp_stream_token(is, &token, &len);
705
camel_imapp_stream_ungettoken(is, tok, token, len);
707
/* body_type_mpart ::= 1*body SPACE media_subtype
708
[SPACE body_ext_mpart] */
710
cinfo = g_malloc0(sizeof(*cinfo));
711
last = (struct _CamelMessageContentInfo *)&cinfo->childs;
713
subinfo = imap_parse_body(is);
714
last->next = subinfo;
716
subinfo->parent = cinfo;
717
tok = camel_imapp_stream_token(is, &token, &len);
718
camel_imapp_stream_ungettoken(is, tok, token, len);
719
} while (tok == '(');
721
d(printf("media_subtype\n"));
723
camel_imapp_stream_astring(is, &token);
724
cinfo->type = camel_content_type_new("multipart", token);
726
/* body_ext_mpart ::= body_fld_param
727
[SPACE body_fld_dsp SPACE body_fld_lang
728
[SPACE 1#body_extension]]
729
;; MUST NOT be returned on non-extensible
732
d(printf("body_ext_mpart\n"));
734
tok = camel_imapp_stream_token(is, &token, &len);
735
camel_imapp_stream_ungettoken(is, tok, token, len);
737
imap_parse_param_list(is, &cinfo->type->params);
739
/* body_fld_dsp ::= "(" string SPACE body_fld_param ")" / nil */
741
tok = camel_imapp_stream_token(is, &token, &len);
742
camel_imapp_stream_ungettoken(is, tok, token, len);
743
if (tok == '(' || tok == IMAP_TOK_TOKEN) {
744
dinfo = imap_parse_ext_optional(is);
745
/* other extension fields?, soaked up below */
747
camel_imapp_stream_ungettoken(is, tok, token, len);
751
/* body_type_1part ::= (body_type_basic / body_type_msg / body_type_text)
752
[SPACE body_ext_1part]
754
body_type_basic ::= media_basic SPACE body_fields
755
body_type_text ::= media_text SPACE body_fields SPACE body_fld_lines
756
body_type_msg ::= media_message SPACE body_fields SPACE envelope
757
SPACE body SPACE body_fld_lines */
759
d(printf("Single part body\n"));
761
cinfo = imap_parse_body_fields(is);
763
d(printf("envelope?\n"));
765
/* do we have an envelope following */
766
tok = camel_imapp_stream_token(is, &token, &len);
767
camel_imapp_stream_ungettoken(is, tok, token, len);
769
/* what do we do with the envelope?? */
770
minfo = imap_parse_envelope(is);
771
/* what do we do with the message content info?? */
772
((CamelMessageInfoBase *)minfo)->content = imap_parse_body(is);
773
camel_message_info_free(minfo);
775
d(printf("Scanned envelope - what do i do with it?\n"));
778
d(printf("fld_lines?\n"));
780
/* do we have fld_lines following? */
781
tok = camel_imapp_stream_token(is, &token, &len);
782
if (tok == IMAP_TOK_INT) {
783
d(printf("field lines: %s\n", token));
784
tok = camel_imapp_stream_token(is, &token, &len);
786
camel_imapp_stream_ungettoken(is, tok, token, len);
788
/* body_ext_1part ::= body_fld_md5 [SPACE body_fld_dsp
790
[SPACE 1#body_extension]]]
791
;; MUST NOT be returned on non-extensible
794
d(printf("extension data?\n"));
797
camel_imapp_stream_nstring(is, &token);
799
d(printf("md5: %s\n", token?(gchar *)token:"NIL"));
801
/* body_fld_dsp ::= "(" string SPACE body_fld_param ")" / nil */
803
tok = camel_imapp_stream_token(is, &token, &len);
804
camel_imapp_stream_ungettoken(is, tok, token, len);
805
if (tok == '(' || tok == IMAP_TOK_TOKEN) {
806
dinfo = imap_parse_ext_optional(is);
807
/* then other extension fields, soaked up below */
812
/* soak up any other extension fields that may be present */
813
/* there should only be simple tokens, no lists */
815
tok = camel_imapp_stream_token(is, &token, &len);
817
d(printf("Dropping extension data '%s'\n", token));
818
} while (tok != ')');
821
imap_free_body(cinfo);
823
camel_content_disposition_unref(dinfo);
825
camel_message_info_free(minfo);
826
camel_exception_throw_ex(ex);
829
/* FIXME: do something with the disposition, currently we have no way to pass it out? */
831
camel_content_disposition_unref(dinfo);
837
imap_parse_section(CamelIMAPPStream *is)
841
gchar * volatile section = NULL;
843
/* currently we only return the part within the [section] specifier
844
any header fields are parsed, but dropped */
847
section ::= "[" [section_text /
848
(nz_number *["." nz_number] ["." (section_text / "MIME")])] "]"
850
section_text ::= "HEADER" / "HEADER.FIELDS" [".NOT"]
851
SPACE header_list / "TEXT"
855
tok = camel_imapp_stream_token(is, &token, &len);
857
camel_exception_throw(1, "section: expecting '['");
859
tok = camel_imapp_stream_token(is, &token, &len);
860
if (tok == IMAP_TOK_INT || tok == IMAP_TOK_TOKEN)
861
section = g_strdup(token);
862
else if (tok == ']') {
863
section = g_strdup("");
864
camel_imapp_stream_ungettoken(is, tok, token, len);
866
camel_exception_throw(1, "section: expecting token");
868
/* header_list ::= "(" 1#header_fld_name ")"
869
header_fld_name ::= astring */
871
/* we dont need the header specifiers */
872
tok = camel_imapp_stream_token(is, &token, &len);
875
tok = camel_imapp_stream_token(is, &token, &len);
876
if (tok == IMAP_TOK_STRING || tok == IMAP_TOK_TOKEN || tok == IMAP_TOK_INT) {
878
} else if (tok != ')')
879
camel_exception_throw(1, "section: header fields: expecting string");
880
} while (tok != ')');
881
tok = camel_imapp_stream_token(is, &token, &len);
885
camel_exception_throw(1, "section: expecting ']'");
888
camel_exception_throw_ex(ex);
895
imap_free_fetch(struct _fetch_info *finfo)
901
camel_object_unref((CamelObject *)finfo->body);
903
camel_object_unref((CamelObject *)finfo->text);
905
camel_object_unref((CamelObject *)finfo->header);
907
camel_message_info_free(finfo->minfo);
909
imap_free_body(finfo->cinfo);
911
g_free(finfo->section);
916
extern void camel_content_info_dump(CamelMessageContentInfo *ci, gint depth);
917
extern void camel_message_info_dump(CamelMessageInfo *mi);
919
#include "camel-stream-fs.h"
921
/* debug, dump one out */
923
imap_dump_fetch(struct _fetch_info *finfo)
928
printf("Fetch info:\n");
935
sout = camel_stream_fs_new_with_fd(fd);
937
camel_stream_printf(sout, "Body content:\n");
938
camel_stream_write_to_stream(finfo->body, sout);
941
camel_stream_printf(sout, "Text content:\n");
942
camel_stream_write_to_stream(finfo->text, sout);
945
camel_stream_printf(sout, "Header content:\n");
946
camel_stream_write_to_stream(finfo->header, sout);
949
camel_stream_printf(sout, "Message Info:\n");
950
camel_message_info_dump(finfo->minfo);
953
camel_stream_printf(sout, "Content Info:\n");
954
camel_content_info_dump(finfo->cinfo, 0);
956
if (finfo->got & FETCH_SIZE)
957
camel_stream_printf(sout, "Size: %d\n", (gint)finfo->size);
958
if (finfo->got & FETCH_BODY)
959
camel_stream_printf(sout, "Offset: %d\n", (gint)finfo->offset);
960
if (finfo->got & FETCH_FLAGS)
961
camel_stream_printf(sout, "Flags: %08x\n", (gint)finfo->flags);
963
camel_stream_printf(sout, "Date: '%s'\n", finfo->date);
965
camel_stream_printf(sout, "Section: '%s'\n", finfo->section);
967
camel_stream_printf(sout, "UID: '%s'\n", finfo->uid);
968
camel_object_unref((CamelObject *)sout);
972
imap_parse_fetch(CamelIMAPPStream *is)
975
guchar *token, *p, c;
976
struct _fetch_info *finfo;
978
finfo = g_malloc0(sizeof(*finfo));
981
tok = camel_imapp_stream_token(is, &token, &len);
983
camel_exception_throw(1, "fetch: expecting '('");
985
while ( (tok = camel_imapp_stream_token(is, &token, &len)) == IMAP_TOK_TOKEN ) {
991
switch (imap_tokenise(token, len)) {
993
finfo->minfo = imap_parse_envelope(is);
994
finfo->got |= FETCH_MINFO;
997
imap_parse_flags(is, &finfo->flags);
998
finfo->got |= FETCH_FLAGS;
1000
case IMAP_INTERNALDATE:
1001
camel_imapp_stream_nstring(is, &token);
1002
/* TODO: convert to camel format? */
1003
finfo->date = g_strdup(token);
1004
finfo->got |= FETCH_DATE;
1006
case IMAP_RFC822_HEADER:
1007
camel_imapp_stream_nstring_stream(is, &finfo->header);
1008
finfo->got |= FETCH_HEADER;
1010
case IMAP_RFC822_TEXT:
1011
camel_imapp_stream_nstring_stream(is, &finfo->text);
1012
finfo->got |= FETCH_TEXT;
1014
case IMAP_RFC822_SIZE:
1015
finfo->size = camel_imapp_stream_number(is);
1016
finfo->got |= FETCH_SIZE;
1018
case IMAP_BODYSTRUCTURE:
1019
finfo->cinfo = imap_parse_body(is);
1020
finfo->got |= FETCH_CINFO;
1023
tok = camel_imapp_stream_token(is, &token, &len);
1024
camel_imapp_stream_ungettoken(is, tok, token, len);
1026
finfo->cinfo = imap_parse_body(is);
1027
finfo->got |= FETCH_CINFO;
1028
} else if (tok == '[') {
1029
finfo->section = imap_parse_section(is);
1030
finfo->got |= FETCH_SECTION;
1031
tok = camel_imapp_stream_token(is, &token, &len);
1032
if (token[0] == '<') {
1033
finfo->offset = strtoul(token+1, NULL, 10);
1035
camel_imapp_stream_ungettoken(is, tok, token, len);
1037
camel_imapp_stream_nstring_stream(is, &finfo->body);
1038
finfo->got |= FETCH_BODY;
1040
camel_exception_throw(1, "unknown body response");
1044
tok = camel_imapp_stream_token(is, &token, &len);
1045
if (tok != IMAP_TOK_INT)
1046
camel_exception_throw(1, "uid not integer");
1047
finfo->uid = g_strdup(token);
1048
finfo->got |= FETCH_UID;
1051
camel_exception_throw(1, "unknown body response");
1056
camel_exception_throw(1, "missing closing ')' on fetch response");
1058
imap_free_fetch(finfo);
1059
camel_exception_throw_ex(ex);
1065
/* rfc 2060 section 7.1 Status Responses */
1066
/* shoudl this start after [ or before the [? token_unget anyone? */
1067
struct _status_info *
1068
imap_parse_status(CamelIMAPPStream *is)
1072
struct _status_info *sinfo;
1074
sinfo = g_malloc0(sizeof(*sinfo));
1077
camel_imapp_stream_atom(is, &token, &len);
1080
resp_cond_auth ::= ("OK" / "PREAUTH") SPACE resp_text
1081
;; Authentication condition
1083
resp_cond_bye ::= "BYE" SPACE resp_text
1085
resp_cond_state ::= ("OK" / "NO" / "BAD") SPACE resp_text
1089
sinfo->result = imap_tokenise(token, len);
1090
switch (sinfo->result) {
1098
camel_exception_throw(1, "expecting OK/NO/BAD");
1101
tok = camel_imapp_stream_token(is, &token, &len);
1103
camel_imapp_stream_atom(is, &token, &len);
1104
sinfo->condition = imap_tokenise(token, len);
1106
/* parse any details */
1107
switch (sinfo->condition) {
1108
case IMAP_READ_ONLY:
1109
case IMAP_READ_WRITE:
1112
case IMAP_TRYCREATE:
1115
/* the rfc doesn't specify the bnf for this */
1116
camel_imapp_stream_astring(is, &token);
1117
sinfo->u.newname.oldname = g_strdup(token);
1118
camel_imapp_stream_astring(is, &token);
1119
sinfo->u.newname.newname = g_strdup(token);
1121
case IMAP_PERMANENTFLAGS:
1122
imap_parse_flags(is, &sinfo->u.permanentflags);
1124
case IMAP_UIDVALIDITY:
1125
sinfo->u.uidvalidity = camel_imapp_stream_number(is);
1128
sinfo->u.unseen = camel_imapp_stream_number(is);
1131
sinfo->condition = IMAP_UNKNOWN;
1132
printf("Got unknown response code: %s: ignored\n", token);
1135
/* ignore anything we dont know about */
1137
tok = camel_imapp_stream_token(is, &token, &len);
1139
camel_exception_throw(1, "server response truncated");
1140
} while (tok != ']');
1142
camel_imapp_stream_ungettoken(is, tok, token, len);
1145
/* and take the human readable response */
1146
camel_imapp_stream_text(is, (guchar **)&sinfo->text);
1148
imap_free_status(sinfo);
1149
camel_exception_throw_ex(ex);
1156
imap_free_status(struct _status_info *sinfo)
1161
switch (sinfo->condition) {
1163
g_free(sinfo->u.newname.oldname);
1164
g_free(sinfo->u.newname.newname);
1169
g_free(sinfo->text);
1173
/* FIXME: use tokeniser? */
1174
/* FIXME: real flags */
1178
} list_flag_table[] = {
1179
{ "\\NOINFERIORS", CAMEL_FOLDER_NOINFERIORS },
1180
{ "\\NOSELECT", CAMEL_FOLDER_NOSELECT },
1181
{ "\\MARKED", 1<<8 },
1182
{ "\\UNMARKED", 1<<9 },
1186
imap_parse_list(CamelIMAPPStream *is)
1187
/* throws io, parse */
1190
guchar *token, *p, c;
1191
struct _list_info * volatile linfo;
1193
linfo = g_malloc0(sizeof(*linfo));
1196
/* mailbox_list ::= "(" #("\Marked" / "\Noinferiors" /
1197
"\Noselect" / "\Unmarked" / flag_extension) ")"
1198
SPACE (<"> QUOTED_CHAR <"> / nil) SPACE mailbox */
1200
tok = camel_imapp_stream_token(is, &token, &len);
1202
camel_exception_throw(1, "list: expecting '('");
1204
while ( (tok = camel_imapp_stream_token(is, &token, &len)) != ')' ) {
1205
if (tok == IMAP_TOK_STRING || tok == IMAP_TOK_TOKEN) {
1209
for (i=0;i<(gint)(sizeof(list_flag_table)/sizeof(list_flag_table[0]));i++)
1210
if (!strcmp(token, list_flag_table[i].name))
1211
linfo->flags |= list_flag_table[i].flag;
1213
camel_exception_throw(1, "list: expecting flag or ')'");
1217
camel_imapp_stream_nstring(is, &token);
1218
linfo->separator = token?*token:0;
1219
camel_imapp_stream_astring(is, &token);
1220
linfo->name = g_strdup(token);
1222
imap_free_list(linfo);
1223
camel_exception_throw_ex(ex);
1230
imapp_list_get_path(struct _list_info *li)
1236
if (li->separator != 0 && li->separator != '/') {
1237
p = path = alloca(strlen(li->name)*3+1);
1239
while ( (c = *f++ & 0xff) ) {
1240
if (c == li->separator)
1242
else if (c == '/' || c == '%')
1243
p += sprintf(p, "%%%02X", c);
1251
return camel_utf7_utf8(path);
1255
imap_free_list(struct _list_info *linfo)
1258
g_free(linfo->name);
1263
/* ********************************************************************** */
1264
/* utility functions */
1266
/* should the rest of imapp-utils go into imapp-parse? */
1268
/* this creates a uid (or sequence number) set directly into the command,
1269
optionally breaking it into smaller chunks */
1272
imapp_uidset_init(struct _uidset_state *ss, CamelIMAPPEngine *ie)
1281
imapp_uidset_done(struct _uidset_state *ss, CamelIMAPPCommand *ic)
1285
if (ss->last != 0 && ss->last != ss->start) {
1286
camel_imapp_engine_command_add(ss->ie, ic, ":%d", ss->last);
1287
printf(":%d", ss->last);
1290
ret = ss->last != 0;
1300
imapp_uidset_add(struct _uidset_state *ss, CamelIMAPPCommand *ic, const gchar *uid)
1304
uidn = strtoul(uid, NULL, 10);
1308
if (ss->last == 0) {
1309
camel_imapp_engine_command_add(ss->ie, ic, "%d", uidn);
1314
if (ss->last != uidn-1) {
1315
if (ss->last == ss->start) {
1316
camel_imapp_engine_command_add(ss->ie, ic, ",%d", uidn);
1317
printf(",%d", uidn);
1320
camel_imapp_engine_command_add(ss->ie, ic, ":%d,%d", ss->last, uidn);
1321
printf(":%d,%d", ss->last, uidn);
1331
imapp_uidset_done(ss, ic);