1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
3 * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of version 2 of the GNU Lesser General Public
7 * License as published by the Free Software Foundation.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 * Boston, MA 02110-1301, USA.
24 #include "camel-imapx-folder.h"
25 #include "camel-imapx-settings.h"
26 #include "camel-imapx-stream.h"
27 #include "camel-imapx-summary.h"
28 #include "camel-imapx-store.h"
29 #include "camel-imapx-store-summary.h"
30 #include "camel-imapx-utils.h"
32 /* high-level parser state */
33 #define p(...) camel_imapx_debug(parse, __VA_ARGS__)
35 #define d(...) camel_imapx_debug(debug, __VA_ARGS__)
37 gint camel_imapx_debug_flags;
38 extern gint camel_verbose_debug;
40 #define debug_set_flag(flag) do { \
41 if ((CAMEL_IMAPX_DEBUG_ALL & CAMEL_IMAPX_DEBUG_ ## flag) && \
42 camel_debug("imapx:" #flag)) \
43 camel_imapx_debug_flags |= CAMEL_IMAPX_DEBUG_ ## flag; \
46 static void camel_imapx_set_debug_flags (void)
48 if (camel_verbose_debug || camel_debug("imapx")) {
49 camel_imapx_debug_flags = CAMEL_IMAPX_DEBUG_ALL;
53 debug_set_flag (command);
54 debug_set_flag (debug);
55 debug_set_flag (extra);
57 debug_set_flag (token);
58 debug_set_flag (parse);
59 debug_set_flag (conman);
62 #include "camel-imapx-tokenise.h"
63 #define SUBFOLDER_DIR_NAME "subfolders"
69 imapx_tokenise (register const gchar *str,
72 struct _imapx_keyword *k = imapx_tokenise_struct (str, len);
79 static void imapx_namespace_clear (CamelIMAPXStoreNamespace **ns);
80 static const gchar * rename_label_flag (const gchar *flag, gint len, gboolean server_to_evo);
87 { "\\ANSWERED", CAMEL_MESSAGE_ANSWERED },
88 { "\\DELETED", CAMEL_MESSAGE_DELETED },
89 { "\\DRAFT", CAMEL_MESSAGE_DRAFT },
90 { "\\FLAGGED", CAMEL_MESSAGE_FLAGGED },
91 { "\\SEEN", CAMEL_MESSAGE_SEEN },
92 { "\\RECENT", CAMEL_IMAPX_MESSAGE_RECENT },
93 { "JUNK", CAMEL_MESSAGE_JUNK },
94 { "NOTJUNK", CAMEL_MESSAGE_NOTJUNK },
95 { "\\*", CAMEL_MESSAGE_USER }
99 * should this be part of imapx-driver? */
100 /* maybe this should be a stream op? */
102 imapx_parse_flags (CamelIMAPXStream *stream,
104 CamelFlag **user_flagsp,
105 GCancellable *cancellable,
107 /* throws IO,PARSE exception */
116 tok = camel_imapx_stream_token (stream, &token, &len, cancellable, NULL);
119 tok = camel_imapx_stream_token (stream, &token, &len, cancellable, NULL);
120 if (tok == IMAPX_TOK_TOKEN || tok == IMAPX_TOK_INT) {
121 gchar *upper = g_ascii_strup ((gchar *) token, len);
123 for (i = 0; i < G_N_ELEMENTS (flag_table); i++)
124 if (!strcmp (upper, flag_table[i].name)) {
125 flags |= flag_table[i].flag;
129 const gchar *flag_name = rename_label_flag ((gchar *) token, strlen ((gchar *) token), TRUE);
131 camel_flag_set (user_flagsp, flag_name, TRUE);
135 tok = tok; /* fixes stupid warning */
137 } else if (tok != ')') {
138 g_set_error (error, CAMEL_IMAPX_ERROR, 1, "expecting flag");
141 } while (tok != ')');
143 g_set_error (error, CAMEL_IMAPX_ERROR, 1, "execting flag list");
152 * Converts label flag name on server to name used in Evolution or back.
153 * if the flags does not match returns the original one as it is.
154 * It will never return NULL, it will return empty string, instead.
156 * @flag: Flag to rename.
157 * @len: Length of the flag name.
158 * @server_to_evo: if TRUE, then converting server names to evo's names, if FALSE then opposite.
161 rename_label_flag (const gchar *flag,
163 gboolean server_to_evo)
166 const gchar *labels[] = {
167 "$Label1", "$Labelimportant",
168 "$Label2", "$Labelwork",
169 "$Label3", "$Labelpersonal",
170 "$Label4", "$Labeltodo",
171 "$Label5", "$Labellater",
174 /* It really can pass zero-length flags inside, in that case it was able
175 * to always add first label, which is definitely wrong. */
176 if (!len || !flag || !*flag)
179 for (i = 0 + (server_to_evo ? 0 : 1); labels[i]; i = i + 2) {
180 if (!g_ascii_strncasecmp (flag, labels[i], len))
181 return labels[i + (server_to_evo ? 1 : -1)];
188 imapx_write_flags (GString *string,
190 CamelFlag *user_flags)
193 gboolean first = TRUE;
195 g_string_append_c (string, '(');
197 for (i = 0; flags != 0 && i< G_N_ELEMENTS (flag_table); i++) {
198 if (flag_table[i].flag & flags) {
199 if (flags & CAMEL_IMAPX_MESSAGE_RECENT)
202 g_string_append_c (string, ' ');
204 g_string_append (string, flag_table[i].name);
206 flags &= ~flag_table[i].flag;
211 const gchar *flag_name;
213 flag_name = rename_label_flag (
214 user_flags->name, strlen (user_flags->name), FALSE);
217 g_string_append_c (string, ' ');
219 g_string_append (string, flag_name);
221 user_flags = user_flags->next;
224 g_string_append_c (string, ')');
228 imapx_update_user_flags (CamelMessageInfo *info,
229 CamelFlag *server_user_flags)
231 gboolean changed = FALSE;
232 CamelMessageInfoBase *binfo = (CamelMessageInfoBase *) info;
233 CamelIMAPXMessageInfo *xinfo = (CamelIMAPXMessageInfo *) info;
234 gboolean set_cal = FALSE;
236 if (camel_flag_get (&binfo->user_flags, "$has_cal"))
239 changed = camel_flag_list_copy (&binfo->user_flags, &server_user_flags);
240 camel_flag_list_copy (&xinfo->server_user_flags, &server_user_flags);
242 /* reset the calendar flag if it was set in messageinfo before */
244 camel_flag_set (&binfo->user_flags, "$has_cal", TRUE);
250 imapx_update_message_info_flags (CamelMessageInfo *info,
251 guint32 server_flags,
252 CamelFlag *server_user_flags,
253 guint32 permanent_flags,
255 gboolean unsolicited)
257 gboolean changed = FALSE;
258 CamelIMAPXMessageInfo *xinfo = (CamelIMAPXMessageInfo *) info;
260 if (server_flags != xinfo->server_flags) {
261 guint32 server_set, server_cleared;
263 server_set = server_flags & ~xinfo->server_flags;
264 server_cleared = xinfo->server_flags & ~server_flags;
266 /* Don't clear non-permanent server-side flags.
267 * This avoids overwriting local flags that we
268 * do store permanently, such as junk flags. */
269 if (permanent_flags > 0)
270 server_cleared &= permanent_flags;
272 camel_message_info_set_flags ((CamelMessageInfo *) xinfo, server_set | server_cleared, (xinfo->info.flags | server_set) & ~server_cleared);
274 xinfo->server_flags = server_flags;
275 xinfo->info.flags = xinfo->info.flags & ~CAMEL_MESSAGE_FOLDER_FLAGGED;
276 xinfo->info.dirty = TRUE;
281 if ((folder->permanent_flags & CAMEL_MESSAGE_USER) != 0 && imapx_update_user_flags (info, server_user_flags))
288 imapx_set_message_info_flags_for_new_message (CamelMessageInfo *info,
289 guint32 server_flags,
290 CamelFlag *server_user_flags,
293 CamelMessageInfoBase *binfo = (CamelMessageInfoBase *) info;
294 CamelIMAPXMessageInfo *xinfo = (CamelIMAPXMessageInfo *) info;
296 binfo->flags |= server_flags;
297 camel_message_info_set_flags (info, server_flags, binfo->flags | server_flags);
299 xinfo->server_flags = server_flags;
301 if (folder->permanent_flags & CAMEL_MESSAGE_USER)
302 imapx_update_user_flags (info, server_user_flags);
304 binfo->flags &= ~CAMEL_MESSAGE_FOLDER_FLAGGED;
309 imapx_update_store_summary (CamelFolder *folder)
312 CamelStore *parent_store;
313 const gchar *full_name;
314 CamelService *service;
315 CamelSettings *settings;
316 gboolean mobile_mode;
318 full_name = camel_folder_get_full_name (folder);
319 parent_store = camel_folder_get_parent_store (folder);
320 service = CAMEL_SERVICE (parent_store);
321 settings = camel_service_get_settings (service);
322 mobile_mode = camel_imapx_settings_get_mobile_mode (
323 CAMEL_IMAPX_SETTINGS (settings));
325 si = camel_store_summary_path ((CamelStoreSummary *) ((CamelIMAPXStore *) parent_store)->summary, full_name);
327 guint32 unread, total;
329 total = camel_folder_summary_count (folder->summary);
330 unread = camel_folder_summary_get_unread_count (folder->summary);
332 if (si->unread != unread || si->total != total) {
337 si->unread = ((CamelIMAPXFolder *) folder)->unread_on_server;
340 camel_store_summary_touch ((CamelStoreSummary *)((CamelIMAPXStore *) parent_store)->summary);
341 camel_store_summary_save ((CamelStoreSummary *)((CamelIMAPXStore *) parent_store)->summary);
347 * capability_data ::= "CAPABILITY" SPACE [1#capability SPACE] "IMAP4rev1"
348 * [SPACE 1#capability]
349 * ;; IMAP4rev1 servers which offer RFC 1730
350 * ;; compatibility MUST list "IMAP4" as the first
358 { "IMAP4", IMAPX_CAPABILITY_IMAP4 },
359 { "IMAP4REV1", IMAPX_CAPABILITY_IMAP4REV1 },
360 { "STATUS", IMAPX_CAPABILITY_STATUS } ,
361 { "NAMESPACE", IMAPX_CAPABILITY_NAMESPACE },
362 { "UIDPLUS", IMAPX_CAPABILITY_UIDPLUS },
363 { "LITERAL+", IMAPX_CAPABILITY_LITERALPLUS },
364 { "STARTTLS", IMAPX_CAPABILITY_STARTTLS },
365 { "IDLE", IMAPX_CAPABILITY_IDLE },
366 { "CONDSTORE", IMAPX_CAPABILITY_CONDSTORE },
367 { "QRESYNC", IMAPX_CAPABILITY_QRESYNC },
368 { "LIST-EXTENDED", IMAPX_CAPABILITY_LIST_EXTENDED },
369 { "LIST-STATUS", IMAPX_CAPABILITY_LIST_STATUS },
372 struct _capability_info *
373 imapx_parse_capability (CamelIMAPXStream *stream,
374 GCancellable *cancellable,
379 guchar *token, *p, c;
380 gboolean free_token = FALSE;
381 struct _capability_info * cinfo;
382 GError *local_error = NULL;
384 cinfo = g_malloc0 (sizeof (*cinfo));
385 cinfo->auth_types = g_hash_table_new_full (g_str_hash, g_str_equal, (GDestroyNotify) g_free, NULL);
387 /* FIXME: handle auth types */
388 while ((tok = camel_imapx_stream_token (stream, &token, &len, cancellable, &local_error)) != '\n' &&
389 local_error == NULL) {
392 /* Put it back so that imapx_untagged() isn't unhappy */
393 camel_imapx_stream_ungettoken (stream, tok, token, len);
396 token = (guchar *) g_strconcat ((gchar *)token, "+", NULL);
398 case IMAPX_TOK_TOKEN:
399 case IMAPX_TOK_STRING:
403 if (!strncmp ((gchar *) token, "AUTH=", 5)) {
404 g_hash_table_insert (cinfo->auth_types,
405 g_strdup ((gchar *) token + 5),
406 GINT_TO_POINTER (1));
410 d(stream->tagprefix, " cap: '%s'\n", token);
411 for (i = 0; i < G_N_ELEMENTS (capa_table); i++)
412 if (!strcmp ((gchar *) token, capa_table[i].name))
413 cinfo->capa |= capa_table[i].flag;
421 g_set_error (error, CAMEL_IMAPX_ERROR, 1, "capability: expecting name");
426 if (local_error != NULL) {
427 g_propagate_error (error, local_error);
428 imapx_free_capability (cinfo);
435 void imapx_free_capability (struct _capability_info *cinfo)
437 g_hash_table_destroy (cinfo->auth_types);
441 struct _CamelIMAPXNamespaceList *
442 imapx_parse_namespace_list (CamelIMAPXStream *stream,
443 GCancellable *cancellable,
446 CamelIMAPXStoreNamespace *namespaces[3], *node, *tail;
447 CamelIMAPXNamespaceList *nsl = NULL;
453 nsl = g_malloc0 (sizeof (CamelIMAPXNamespaceList));
454 nsl->personal = NULL;
458 tok = camel_imapx_stream_token (stream, &token, &len, cancellable, NULL);
460 namespaces[n] = NULL;
461 tail = (CamelIMAPXStoreNamespace *) &namespaces[n];
464 tok = camel_imapx_stream_token (stream, &token, &len, cancellable, NULL);
467 tok = camel_imapx_stream_token (stream, &token, &len, cancellable, NULL);
468 if (tok != IMAPX_TOK_STRING) {
469 g_set_error (error, 1, CAMEL_IMAPX_ERROR, "namespace: expected a string path name");
473 node = g_new0 (CamelIMAPXStoreNamespace, 1);
475 node->path = g_strdup ((gchar *) token);
477 tok = camel_imapx_stream_token (stream, &token, &len, cancellable, NULL);
479 if (tok == IMAPX_TOK_STRING) {
480 if (strlen ((gchar *) token) == 1) {
484 node->sep = node->path[strlen (node->path) - 1];
488 } else if (tok == IMAPX_TOK_TOKEN) {
489 /* will a NIL be possible here? */
492 g_set_error (error, CAMEL_IMAPX_ERROR, 1, "namespace: expected a string separtor");
501 if (*node->path && node->path[strlen (node->path) -1] == node->sep)
502 node->path[strlen (node->path) - 1] = '\0';
504 if (!g_ascii_strncasecmp (node->path, "INBOX", 5) &&
505 (node->path[6] == '\0' || node->path[6] == node->sep ))
506 memcpy (node->path, "INBOX", 5);
508 /* TODO remove full_name later. not required */
509 node->full_name = g_strdup (node->path);
511 tok = camel_imapx_stream_token (stream, &token, &len, cancellable, NULL);
513 g_set_error (error, CAMEL_IMAPX_ERROR, 1, "namespace: expected a ')'");
517 tok = camel_imapx_stream_token (stream, &token, &len, cancellable, NULL);
521 g_set_error (error, CAMEL_IMAPX_ERROR, 1, "namespace: expected a ')'");
525 } else if (tok == IMAPX_TOK_TOKEN && !strcmp ((gchar *) token, "NIL")) {
526 namespaces[n] = NULL;
528 g_set_error (error, CAMEL_IMAPX_ERROR, 1, "namespace: expected either a '(' or NIL");
532 tok = camel_imapx_stream_token (stream, &token, &len, cancellable, NULL);
536 nsl->personal = namespaces[0];
537 nsl->shared = namespaces[1];
538 nsl->other = namespaces[2];
543 for (i = 0; i < 3; i++)
544 imapx_namespace_clear (&namespaces[i]);
550 * body ::= "(" body_type_1part / body_type_mpart ")"
552 * body_extension ::= nstring / number / "(" 1#body_extension ")"
553 * ;; Future expansion. Client implementations
554 * ;; MUST accept body_extension fields. Server
555 * ;; implementations MUST NOT generate
556 * ;; body_extension fields except as defined by
557 * ;; future standard or standards-track
558 * ;; revisions of this specification.
560 * body_ext_1part ::= body_fld_md5[SPACE body_fld_dsp
561 * [SPACE body_fld_lang
562 * [SPACE 1#body_extension]]]
563 * ;; MUST NOT be returned on non-extensible
566 * body_ext_mpart ::= body_fld_param
567 * [SPACE body_fld_dsp SPACE body_fld_lang
568 * [SPACE 1#body_extension]]
569 * ;; MUST NOT be returned on non-extensible
572 * body_fields ::= body_fld_param SPACE body_fld_id SPACE
573 * body_fld_desc SPACE body_fld_enc SPACE
576 * body_fld_desc ::= nstring
578 * body_fld_dsp ::= "(" string SPACE body_fld_param ")" / nil
580 * body_fld_enc ::= (<"> ("7BIT" / "8BIT" / "BINARY" / "BASE64"/
581 * "QUOTED-PRINTABLE") <">) / string
583 * body_fld_id ::= nstring
585 * body_fld_lang ::= nstring / "(" 1#string ")"
587 * body_fld_lines ::= number
589 * body_fld_md5 ::= nstring
591 * body_fld_octets ::= number
593 * body_fld_param ::= "(" 1#(string SPACE string) ")" / nil
595 * body_type_1part ::= (body_type_basic / body_type_msg / body_type_text)
596 * [SPACE body_ext_1part]
598 * body_type_basic ::= media_basic SPACE body_fields
599 * ;; MESSAGE subtype MUST NOT be "RFC822"
601 * body_type_mpart ::= 1*body SPACE media_subtype
602 * [SPACE body_ext_mpart]
604 * body_type_msg ::= media_message SPACE body_fields SPACE envelope
605 * SPACE body SPACE body_fld_lines
607 * body_type_text ::= media_text SPACE body_fields SPACE body_fld_lines
609 * envelope ::= "(" env_date SPACE env_subject SPACE env_from
610 * SPACE env_sender SPACE env_reply_to SPACE env_to
611 * SPACE env_cc SPACE env_bcc SPACE env_in_reply_to
612 * SPACE env_message_id ")"
614 * env_bcc ::= "(" 1*address ")" / nil
616 * env_cc ::= "(" 1*address ")" / nil
618 * env_date ::= nstring
620 * env_from ::= "(" 1*address ")" / nil
622 * env_in_reply_to ::= nstring
624 * env_message_id ::= nstring
626 * env_reply_to ::= "(" 1*address ")" / nil
628 * env_sender ::= "(" 1*address ")" / nil
630 * env_subject ::= nstring
632 * env_to ::= "(" 1*address ")" / nil
634 * media_basic ::= (<"> ("APPLICATION" / "AUDIO" / "IMAGE" /
635 * "MESSAGE" / "VIDEO") <">) / string)
636 * SPACE media_subtype
637 * ;; Defined in[MIME-IMT]
639 * media_message ::= <"> "MESSAGE" <"> SPACE <"> "RFC822" <">
640 * ;; Defined in[MIME-IMT]
642 * media_subtype ::= string
643 * ;; Defined in[MIME-IMT]
645 * media_text ::= <"> "TEXT" <"> SPACE media_subtype
646 * ;; Defined in[MIME-IMT]
648 * ( "type" "subtype" body_fields [envelope body body_fld_lines]
651 * (("TEXT" "PLAIN" ("CHARSET"
652 * "US-ASCII") NIL NIL "7BIT" 1152 23)("TEXT" "PLAIN"
653 * ("CHARSET" "US-ASCII" "NAME" "cc.diff")
654 * "<960723163407.20117h@cac.washington.edu>"
655 * "Compiler diff" "BASE64" 4554 73) "MIXED"))
660 struct _body_fields {
661 CamelContentType *ct;
663 CamelTransferEncoding encoding;
668 imapx_free_body (struct _CamelMessageContentInfo *cinfo)
670 struct _CamelMessageContentInfo *list, *next;
672 list = cinfo->childs;
675 imapx_free_body (list);
680 camel_content_type_unref (cinfo->type);
682 g_free (cinfo->description);
683 g_free (cinfo->encoding);
688 imapx_parse_param_list (CamelIMAPXStream *is,
689 struct _camel_header_param **plist,
690 GCancellable *cancellable,
698 p(is->tagprefix, "body_fld_param\n");
700 /* body_fld_param ::= "(" 1#(string SPACE string) ")" / nil */
701 tok = camel_imapx_stream_token (is, &token, &len, cancellable, NULL);
704 tok = camel_imapx_stream_token (is, &token, &len, cancellable, NULL);
707 camel_imapx_stream_ungettoken (is, tok, token, len);
709 camel_imapx_stream_astring (is, &token, cancellable, NULL);
710 param = alloca (strlen ((gchar *) token) + 1);
711 strcpy (param, (gchar *) token);
712 camel_imapx_stream_astring (is, &token, cancellable, NULL);
713 camel_header_set_param (plist, param, (gchar *) token);
715 } /* else check nil? no need */
720 struct _CamelContentDisposition *
721 imapx_parse_ext_optional (CamelIMAPXStream *is,
722 GCancellable *cancellable,
728 struct _CamelContentDisposition *dinfo = NULL;
729 GError *local_error = NULL;
731 /* this parses both extension types, from the body_fld_dsp onwards */
732 /* although the grammars are different, they can be parsed the same way */
734 /* body_ext_1part ::= body_fld_md5 [SPACE body_fld_dsp
735 * [SPACE body_fld_lang
736 * [SPACE 1#body_extension]]]
737 * ;; MUST NOT be returned on non-extensible
740 /* body_ext_mpart ::= body_fld_param
741 * [SPACE body_fld_dsp SPACE body_fld_lang
742 * [SPACE 1#body_extension]]
743 * ;; MUST NOT be returned on non-extensible
746 /* body_fld_dsp ::= "(" string SPACE body_fld_param ")" / nil */
748 tok = camel_imapx_stream_token (is, &token, &len, cancellable, NULL);
751 dinfo = g_malloc0 (sizeof (*dinfo));
753 /* should be string */
754 camel_imapx_stream_astring (is, &token, cancellable, NULL);
756 dinfo->disposition = g_strdup ((gchar *) token);
757 imapx_parse_param_list (is, &dinfo->params, cancellable, NULL);
758 case IMAPX_TOK_TOKEN:
759 d(is->tagprefix, "body_fld_dsp: NIL\n");
762 g_set_error (error, CAMEL_IMAPX_ERROR, 1, "body_fld_disp: expecting nil or list");
766 p(is->tagprefix, "body_fld_lang\n");
768 /* body_fld_lang ::= nstring / "(" 1#string ")" */
770 /* we just drop the lang string/list, save it somewhere? */
772 tok = camel_imapx_stream_token (is, &token, &len, cancellable, &local_error);
776 tok = camel_imapx_stream_token (is, &token, &len, cancellable, &local_error);
779 } else if (tok != IMAPX_TOK_STRING) {
780 g_clear_error (&local_error);
781 g_set_error (&local_error, CAMEL_IMAPX_ERROR, 1, "expecting string");
786 case IMAPX_TOK_TOKEN:
787 d(is->tagprefix, "body_fld_lang = nil\n");
790 case IMAPX_TOK_STRING:
791 /* we have a string */
793 case IMAPX_TOK_LITERAL:
794 /* we have a literal string */
795 camel_imapx_stream_set_literal (is, len);
796 while (camel_imapx_stream_getl (is, &token, &len, cancellable, NULL) > 0) {
797 d(is->tagprefix, "Skip literal data '%.*s'\n", (gint)len, token);
803 if (local_error != NULL) {
804 g_propagate_error (error, local_error);
806 camel_content_disposition_unref (dinfo);
813 struct _CamelMessageContentInfo *
814 imapx_parse_body_fields (CamelIMAPXStream *is,
815 GCancellable *cancellable,
820 struct _CamelMessageContentInfo *cinfo;
821 GError *local_error = NULL;
823 /* body_fields ::= body_fld_param SPACE body_fld_id SPACE
824 * body_fld_desc SPACE body_fld_enc SPACE
827 p(is->tagprefix, "body_fields\n");
829 cinfo = g_malloc0 (sizeof (*cinfo));
831 /* this should be string not astring */
832 if (camel_imapx_stream_astring (is, &token, cancellable, error))
834 type = alloca (strlen ( (gchar *) token) + 1);
835 strcpy (type, (gchar *) token);
836 if (camel_imapx_stream_astring (is, &token, cancellable, error))
838 cinfo->type = camel_content_type_new (type, (gchar *) token);
839 if (!imapx_parse_param_list (is, &cinfo->type->params, cancellable, error))
842 /* body_fld_id ::= nstring */
843 if (!camel_imapx_stream_nstring (is, &token, cancellable, error))
845 cinfo->id = g_strdup ((gchar *) token);
847 /* body_fld_desc ::= nstring */
848 if (!camel_imapx_stream_nstring (is, &token, cancellable, error))
850 cinfo->description = g_strdup ((gchar *) token);
852 /* body_fld_enc ::= (<"> ("7BIT" / "8BIT" / "BINARY" / "BASE64"/
853 * "QUOTED-PRINTABLE") <">) / string */
854 if (camel_imapx_stream_astring (is, &token, cancellable, error))
856 cinfo->encoding = g_strdup ((gchar *) token);
858 /* body_fld_octets ::= number */
859 cinfo->size = camel_imapx_stream_number (is, cancellable, &local_error);
860 if (local_error != NULL) {
861 g_propagate_error (error, local_error);
867 imapx_free_body (cinfo);
871 struct _camel_header_address *
872 imapx_parse_address_list (CamelIMAPXStream *is,
873 GCancellable *cancellable,
875 /* throws PARSE,IO exception */
879 guchar *token, *host;
881 struct _camel_header_address *list = NULL;
882 GError *local_error = NULL;
884 /* "(" 1*address ")" / nil */
886 tok = camel_imapx_stream_token (is, &token, &len, cancellable, &local_error);
888 struct _camel_header_address *addr, *group = NULL;
890 /* address ::= "(" addr_name SPACE addr_adl SPACE addr_mailbox
891 * SPACE addr_host ")" */
892 tok = camel_imapx_stream_token (is, &token, &len, cancellable, &local_error);
896 g_clear_error (&local_error);
897 camel_header_address_list_clear (&list);
898 g_set_error (error, CAMEL_IMAPX_ERROR, 1, "missing '(' for address");
902 addr = camel_header_address_new ();
903 addr->type = CAMEL_HEADER_ADDRESS_NAME;
904 tok = camel_imapx_stream_nstring (is, &token, cancellable, &local_error);
905 addr->name = g_strdup ((gchar *) token);
906 /* we ignore the route, nobody uses it in the real world */
907 tok = camel_imapx_stream_nstring (is, &token, cancellable, &local_error);
909 /* [RFC-822] group syntax is indicated by a special
910 * form of address structure in which the host name
911 * field is NIL. If the mailbox name field is also
912 * NIL, this is an end of group marker (semi-colon in
913 * RFC 822 syntax). If the mailbox name field is
914 * non-NIL, this is a start of group marker, and the
915 * mailbox name field holds the group name phrase. */
917 tok = camel_imapx_stream_nstring (is,(guchar **) &mbox, cancellable, &local_error);
918 mbox = g_strdup (mbox);
919 tok = camel_imapx_stream_nstring (is, &host, cancellable, &local_error);
924 d(is->tagprefix, "adding group '%s'\n", mbox);
927 addr->type = CAMEL_HEADER_ADDRESS_GROUP;
928 camel_header_address_list_append (&list, addr);
932 addr->v.addr = g_strdup_printf ("%s@%s", mbox? mbox :"", (const gchar *) host);
934 d(is->tagprefix, "adding address '%s'\n", addr->v.addr);
936 camel_header_address_add_member (group, addr);
938 camel_header_address_list_append (&list, addr);
941 tok = camel_imapx_stream_token (is, &token, &len, cancellable, &local_error);
942 } while (tok != ')');
945 d(is->tagprefix, "empty, nil '%s'\n", token);
948 /* CHEN TODO handle exception at required places */
949 if (local_error != NULL)
950 g_propagate_error (error, local_error);
955 struct _CamelMessageInfo *
956 imapx_parse_envelope (CamelIMAPXStream *is,
957 GCancellable *cancellable,
963 struct _camel_header_address *addr, *addr_from;
965 struct _CamelMessageInfoBase *minfo;
966 GError *local_error = NULL;
968 /* envelope ::= "(" env_date SPACE env_subject SPACE env_from
969 * SPACE env_sender SPACE env_reply_to SPACE env_to
970 * SPACE env_cc SPACE env_bcc SPACE env_in_reply_to
971 * SPACE env_message_id ")" */
973 p(is->tagprefix, "envelope\n");
975 minfo = (CamelMessageInfoBase *) camel_message_info_new (NULL);
977 tok = camel_imapx_stream_token (is, &token, &len, cancellable, &local_error);
979 g_clear_error (&local_error);
980 camel_message_info_free (minfo);
981 g_set_error (error, CAMEL_IMAPX_ERROR, 1, "envelope: expecting '('");
985 /* env_date ::= nstring */
986 camel_imapx_stream_nstring (is, &token, cancellable, &local_error);
987 minfo->date_sent = camel_header_decode_date ((gchar *) token, NULL);
989 /* env_subject ::= nstring */
990 tok = camel_imapx_stream_nstring (is, &token, cancellable, &local_error);
991 minfo->subject = camel_pstring_strdup ((gchar *) token);
993 /* we merge from/sender into from, append should probably merge more smartly? */
995 /* env_from ::= "(" 1*address ")" / nil */
996 addr_from = imapx_parse_address_list (is, cancellable, &local_error);
998 /* env_sender ::= "(" 1*address ")" / nil */
999 addr = imapx_parse_address_list (is, cancellable, &local_error);
1001 camel_header_address_list_clear (&addr);
1004 camel_header_address_list_append_list (&addr_from, &addr);
1012 addrstr = camel_header_address_list_format (addr_from);
1013 minfo->from = camel_pstring_strdup (addrstr);
1015 camel_header_address_list_clear (&addr_from);
1018 /* we dont keep reply_to */
1020 /* env_reply_to ::= "(" 1*address ")" / nil */
1021 addr = imapx_parse_address_list (is, cancellable, &local_error);
1022 camel_header_address_list_clear (&addr);
1024 /* env_to ::= "(" 1*address ")" / nil */
1025 addr = imapx_parse_address_list (is, cancellable, &local_error);
1027 addrstr = camel_header_address_list_format (addr);
1028 minfo->to = camel_pstring_strdup (addrstr);
1030 camel_header_address_list_clear (&addr);
1033 /* env_cc ::= "(" 1*address ")" / nil */
1034 addr = imapx_parse_address_list (is, cancellable, &local_error);
1036 addrstr = camel_header_address_list_format (addr);
1037 minfo->cc = camel_pstring_strdup (addrstr);
1039 camel_header_address_list_clear (&addr);
1042 /* we dont keep bcc either */
1044 /* env_bcc ::= "(" 1*address ")" / nil */
1045 addr = imapx_parse_address_list (is, cancellable, &local_error);
1046 camel_header_address_list_clear (&addr);
1048 /* FIXME: need to put in-reply-to into references hash list */
1050 /* env_in_reply_to ::= nstring */
1051 tok = camel_imapx_stream_nstring (is, &token, cancellable, &local_error);
1053 /* FIXME: need to put message-id into message-id hash */
1055 /* env_message_id ::= nstring */
1056 tok = camel_imapx_stream_nstring (is, &token, cancellable, &local_error);
1058 tok = camel_imapx_stream_token (is, &token, &len, cancellable, &local_error);
1060 g_clear_error (&local_error);
1061 camel_message_info_free (minfo);
1062 g_set_error (error, CAMEL_IMAPX_ERROR, 1, "expecting ')'");
1066 /* CHEN TODO handle exceptions better */
1067 if (local_error != NULL)
1068 g_propagate_error (error, local_error);
1070 return (CamelMessageInfo *) minfo;
1073 struct _CamelMessageContentInfo *
1074 imapx_parse_body (CamelIMAPXStream *is,
1075 GCancellable *cancellable,
1081 struct _CamelMessageContentInfo * cinfo = NULL;
1082 struct _CamelMessageContentInfo *subinfo, *last;
1083 struct _CamelContentDisposition * dinfo = NULL;
1084 GError *local_error = NULL;
1086 /* body ::= "(" body_type_1part / body_type_mpart ")" */
1088 p(is->tagprefix, "body\n");
1090 tok = camel_imapx_stream_token (is, &token, &len, cancellable, &local_error);
1092 g_set_error (error, CAMEL_IMAPX_ERROR, 1, "body: expecting '('");
1096 /* 1*body (optional for multiparts) */
1097 tok = camel_imapx_stream_token (is, &token, &len, cancellable, &local_error);
1098 camel_imapx_stream_ungettoken (is, tok, token, len);
1100 /* body_type_mpart ::= 1*body SPACE media_subtype
1101 [SPACE body_ext_mpart] */
1103 cinfo = g_malloc0 (sizeof (*cinfo));
1104 last = (struct _CamelMessageContentInfo *) &cinfo->childs;
1106 subinfo = imapx_parse_body (is, cancellable, &local_error);
1107 last->next = subinfo;
1109 subinfo->parent = cinfo;
1110 tok = camel_imapx_stream_token (is, &token, &len, cancellable, &local_error);
1111 camel_imapx_stream_ungettoken (is, tok, token, len);
1112 } while (tok == '(');
1114 d(is->tagprefix, "media_subtype\n");
1116 camel_imapx_stream_astring (is, &token, cancellable, &local_error);
1117 cinfo->type = camel_content_type_new("multipart", (gchar *) token);
1119 /* body_ext_mpart ::= body_fld_param
1120 * [SPACE body_fld_dsp SPACE body_fld_lang
1121 * [SPACE 1#body_extension]]
1122 * ;; MUST NOT be returned on non-extensible
1123 * ;; "BODY" fetch */
1125 d(is->tagprefix, "body_ext_mpart\n");
1127 tok = camel_imapx_stream_token (is, &token, &len, cancellable, &local_error);
1128 camel_imapx_stream_ungettoken (is, tok, token, len);
1130 imapx_parse_param_list (is, &cinfo->type->params, cancellable, &local_error);
1132 /* body_fld_dsp ::= "(" string SPACE body_fld_param ")" / nil */
1134 tok = camel_imapx_stream_token (is, &token, &len, cancellable, &local_error);
1135 camel_imapx_stream_ungettoken (is, tok, token, len);
1136 if (tok == '(' || tok == IMAPX_TOK_TOKEN) {
1137 dinfo = imapx_parse_ext_optional (is, cancellable, &local_error);
1138 /* other extension fields?, soaked up below */
1140 camel_imapx_stream_ungettoken (is, tok, token, len);
1144 /* body_type_1part ::= (body_type_basic / body_type_msg / body_type_text)
1145 * [SPACE body_ext_1part]
1147 * body_type_basic ::= media_basic SPACE body_fields
1148 * body_type_text ::= media_text SPACE body_fields SPACE body_fld_lines
1149 * body_type_msg ::= media_message SPACE body_fields SPACE envelope
1150 * SPACE body SPACE body_fld_lines */
1152 d(is->tagprefix, "Single part body\n");
1154 cinfo = imapx_parse_body_fields (is, cancellable, &local_error);
1156 d(is->tagprefix, "envelope?\n");
1158 /* do we have an envelope following */
1159 tok = camel_imapx_stream_token (is, &token, &len, cancellable, &local_error);
1160 camel_imapx_stream_ungettoken (is, tok, token, len);
1162 struct _CamelMessageInfo * minfo = NULL;
1164 /* what do we do with the envelope?? */
1165 minfo = imapx_parse_envelope (is, cancellable, &local_error);
1166 /* what do we do with the message content info?? */
1167 //((CamelMessageInfoBase *) minfo)->content = imapx_parse_body (is);
1168 camel_message_info_free (minfo);
1170 d(is->tagprefix, "Scanned envelope - what do i do with it?\n");
1173 d(is->tagprefix, "fld_lines?\n");
1175 /* do we have fld_lines following? */
1176 tok = camel_imapx_stream_token (is, &token, &len, cancellable, &local_error);
1177 if (tok == IMAPX_TOK_INT) {
1178 d(is->tagprefix, "field lines: %s\n", token);
1179 tok = camel_imapx_stream_token (is, &token, &len, cancellable, &local_error);
1181 camel_imapx_stream_ungettoken (is, tok, token, len);
1183 /* body_ext_1part ::= body_fld_md5 [SPACE body_fld_dsp
1184 [SPACE body_fld_lang
1185 [SPACE 1#body_extension]]]
1186 * ;; MUST NOT be returned on non - extensible
1187 * ;; "BODY" fetch */
1189 d(is->tagprefix, "extension data?\n");
1192 camel_imapx_stream_nstring (is, &token, cancellable, &local_error);
1194 d(is->tagprefix, "md5: %s\n", token?(gchar *)token:"NIL");
1196 /* body_fld_dsp ::= "(" string SPACE body_fld_param ")" / nil */
1198 tok = camel_imapx_stream_token (is, &token, &len, cancellable, &local_error);
1199 camel_imapx_stream_ungettoken (is, tok, token, len);
1200 if (tok == '(' || tok == IMAPX_TOK_TOKEN) {
1201 dinfo = imapx_parse_ext_optional (is, cancellable, &local_error);
1202 /* then other extension fields, soaked up below */
1207 /* soak up any other extension fields that may be present */
1208 /* there should only be simple tokens, no lists */
1210 tok = camel_imapx_stream_token (is, &token, &len, cancellable, &local_error);
1212 d(is->tagprefix, "Dropping extension data '%s'\n", token);
1214 } while (tok != ')');
1216 /* CHEN TODO handle exceptions better */
1217 if (local_error != NULL) {
1218 g_propagate_error (error, local_error);
1220 imapx_free_body (cinfo);
1222 camel_content_disposition_unref (dinfo);
1226 /* FIXME: do something with the disposition, currently we have no way to pass it out? */
1228 camel_content_disposition_unref (dinfo);
1234 imapx_parse_section (CamelIMAPXStream *is,
1235 GCancellable *cancellable,
1241 gchar * section = NULL;
1243 /* currently we only return the part within the [section] specifier
1244 * any header fields are parsed, but dropped */
1247 * section ::= "[" [section_text /
1248 * (nz_number *["." nz_number] ["." (section_text / "MIME")])] "]"
1250 * section_text ::= "HEADER" / "HEADER.FIELDS" [".NOT"]
1251 * SPACE header_list / "TEXT"
1254 tok = camel_imapx_stream_token (is, &token, &len, cancellable, NULL);
1256 g_set_error (error, CAMEL_IMAPX_ERROR, 1, "section: expecting '['");
1260 tok = camel_imapx_stream_token (is, &token, &len, cancellable, NULL);
1261 if (tok == IMAPX_TOK_INT || tok == IMAPX_TOK_TOKEN)
1262 section = g_strdup ((gchar *) token);
1263 else if (tok == ']') {
1264 section = g_strdup("");
1265 camel_imapx_stream_ungettoken (is, tok, token, len);
1267 g_set_error (error, CAMEL_IMAPX_ERROR, 1, "section: expecting token");
1271 /* header_list ::= "(" 1#header_fld_name ")"
1272 * header_fld_name ::= astring */
1274 /* we dont need the header specifiers */
1275 tok = camel_imapx_stream_token (is, &token, &len, cancellable, NULL);
1278 tok = camel_imapx_stream_token (is, &token, &len, cancellable, NULL);
1279 if (tok == IMAPX_TOK_STRING || tok == IMAPX_TOK_TOKEN || tok == IMAPX_TOK_INT) {
1280 /* ?do something? */
1281 } else if (tok != ')') {
1282 g_set_error (error, CAMEL_IMAPX_ERROR, 1, "section: header fields: expecting string");
1286 } while (tok != ')');
1287 tok = camel_imapx_stream_token (is, &token, &len, cancellable, NULL);
1291 g_set_error (error, CAMEL_IMAPX_ERROR, 1, "section: expecting ']'");
1300 imapx_parse_modseq (CamelIMAPXStream *is,
1301 GCancellable *cancellable,
1309 tok = camel_imapx_stream_token (is, &token, &len, cancellable, NULL);
1311 g_set_error (error, CAMEL_IMAPX_ERROR, 1, "fetch: expecting '('");
1314 ret = camel_imapx_stream_number (is, cancellable, error);
1318 tok = camel_imapx_stream_token (is, &token, &len, cancellable, NULL);
1320 g_set_error (error, CAMEL_IMAPX_ERROR, 1, "fetch: expecting '('");
1327 imapx_free_fetch (struct _fetch_info *finfo)
1333 g_object_unref (finfo->body);
1335 g_object_unref (finfo->text);
1337 g_object_unref (finfo->header);
1339 camel_message_info_free (finfo->minfo);
1341 imapx_free_body (finfo->cinfo);
1342 camel_flag_list_free (&finfo->user_flags);
1343 g_free (finfo->date);
1344 g_free (finfo->section);
1345 g_free (finfo->uid);
1349 /* debug, dump one out */
1351 imapx_dump_fetch (struct _fetch_info *finfo)
1357 d('?', "Fetch info:\n");
1358 if (finfo == NULL) {
1364 sout = camel_stream_fs_new_with_fd (fd);
1366 camel_stream_write_string (sout, "Body content:\n", NULL, NULL);
1367 camel_stream_write_to_stream (finfo->body, sout, NULL, NULL);
1369 G_SEEKABLE (finfo->body),
1370 0, G_SEEK_SET, NULL, NULL);
1373 camel_stream_write_string (sout, "Text content:\n", NULL, NULL);
1374 camel_stream_write_to_stream (finfo->text, sout, NULL, NULL);
1376 G_SEEKABLE (finfo->text),
1377 0, G_SEEK_SET, NULL, NULL);
1379 if (finfo->header) {
1380 camel_stream_write_string (sout, "Header content:\n", NULL, NULL);
1381 camel_stream_write_to_stream (finfo->header, sout, NULL, NULL);
1383 G_SEEKABLE (finfo->header),
1384 0, G_SEEK_SET, NULL, NULL);
1387 camel_stream_write_string (sout, "Message Info:\n", NULL, NULL);
1388 camel_message_info_dump (finfo->minfo);
1391 camel_stream_write_string (sout, "Content Info:\n", NULL, NULL);
1392 //camel_content_info_dump (finfo->cinfo, 0);
1394 if (finfo->got & FETCH_SIZE) {
1395 string = g_strdup_printf ("Size: %d\n", (gint) finfo->size);
1396 camel_stream_write_string (sout, string, NULL, NULL);
1399 if (finfo->got & FETCH_BODY) {
1400 string = g_strdup_printf ("Offset: %d\n", (gint) finfo->offset);
1401 camel_stream_write_string (sout, string, NULL, NULL);
1404 if (finfo->got & FETCH_FLAGS) {
1405 string = g_strdup_printf ("Flags: %08x\n", (gint) finfo->flags);
1406 camel_stream_write_string (sout, string, NULL, NULL);
1410 string = g_strdup_printf ("Data: '%s'\n", finfo->date);
1411 camel_stream_write_string (sout, string, NULL, NULL);
1414 if (finfo->section) {
1415 string = g_strdup_printf ("Section: '%s'\n", finfo->section);
1416 camel_stream_write_string (sout, string, NULL, NULL);
1420 string = g_strdup_printf ("UID: '%s'\n", finfo->uid);
1421 camel_stream_write_string (sout, string, NULL, NULL);
1424 g_object_unref (sout);
1427 struct _fetch_info *
1428 imapx_parse_fetch (CamelIMAPXStream *is,
1429 GCancellable *cancellable,
1434 guchar *token, *p, c;
1435 struct _fetch_info *finfo;
1437 finfo = g_malloc0 (sizeof (*finfo));
1439 tok = camel_imapx_stream_token (is, &token, &len, cancellable, NULL);
1441 g_set_error (error, CAMEL_IMAPX_ERROR, 1, "fetch: expecting '('");
1446 while ((tok = camel_imapx_stream_token (is, &token, &len, cancellable, NULL)) == IMAPX_TOK_TOKEN) {
1452 switch (imapx_tokenise ((gchar *) token, len)) {
1453 case IMAPX_ENVELOPE:
1454 finfo->minfo = imapx_parse_envelope (is, cancellable, NULL);
1455 finfo->got |= FETCH_MINFO;
1458 imapx_parse_flags (is, &finfo->flags, &finfo->user_flags, cancellable, NULL);
1459 finfo->got |= FETCH_FLAGS;
1461 case IMAPX_INTERNALDATE:
1462 camel_imapx_stream_nstring (is, &token, cancellable, NULL);
1463 /* TODO: convert to camel format? */
1464 finfo->date = g_strdup ((gchar *) token);
1465 finfo->got |= FETCH_DATE;
1467 case IMAPX_RFC822_HEADER:
1468 camel_imapx_stream_nstring_stream (is, &finfo->header, cancellable, NULL);
1469 finfo->got |= FETCH_HEADER;
1471 case IMAPX_RFC822_TEXT:
1472 camel_imapx_stream_nstring_stream (is, &finfo->text, cancellable, NULL);
1473 finfo->got |= FETCH_TEXT;
1475 case IMAPX_RFC822_SIZE:
1476 finfo->size = camel_imapx_stream_number (is, cancellable, NULL);
1477 finfo->got |= FETCH_SIZE;
1479 case IMAPX_BODYSTRUCTURE:
1480 finfo->cinfo = imapx_parse_body (is, cancellable, NULL);
1481 finfo->got |= FETCH_CINFO;
1484 finfo->modseq = imapx_parse_modseq (is, cancellable, NULL);
1485 finfo->got |= FETCH_MODSEQ;
1488 tok = camel_imapx_stream_token (is, &token, &len, cancellable, NULL);
1489 camel_imapx_stream_ungettoken (is, tok, token, len);
1491 finfo->cinfo = imapx_parse_body (is, cancellable, NULL);
1492 finfo->got |= FETCH_CINFO;
1493 } else if (tok == '[') {
1494 finfo->section = imapx_parse_section (is, cancellable, NULL);
1495 finfo->got |= FETCH_SECTION;
1496 tok = camel_imapx_stream_token (is, &token, &len, cancellable, NULL);
1497 if (token[0] == '<') {
1498 finfo->offset = strtoul ((gchar *) token + 1, NULL, 10);
1500 camel_imapx_stream_ungettoken (is, tok, token, len);
1502 camel_imapx_stream_nstring_stream (is, &finfo->body, cancellable, NULL);
1503 finfo->got |= FETCH_BODY;
1505 g_set_error (error, CAMEL_IMAPX_ERROR, 1, "unknown body response");
1506 imapx_free_fetch (finfo);
1511 tok = camel_imapx_stream_token (is, &token, &len, cancellable, NULL);
1512 if (tok != IMAPX_TOK_INT) {
1513 g_set_error (error, CAMEL_IMAPX_ERROR, 1, "uid not integer");
1516 finfo->uid = g_strdup ((gchar *) token);
1517 finfo->got |= FETCH_UID;
1520 imapx_free_fetch (finfo);
1521 g_set_error (error, CAMEL_IMAPX_ERROR, 1, "unknown body response");
1527 g_set_error (error, CAMEL_IMAPX_ERROR, 1, "missing closing ')' on fetch response");
1528 imapx_free_fetch (finfo);
1535 struct _state_info *
1536 imapx_parse_status_info (CamelIMAPXStream *is,
1537 GCancellable *cancellable,
1540 struct _state_info *sinfo;
1545 sinfo = g_malloc0 (sizeof (*sinfo));
1547 /* skip the folder name */
1548 if (camel_imapx_stream_astring (is, &token, cancellable, error)) {
1552 sinfo->name = camel_utf7_utf8 ((gchar *) token);
1554 tok = camel_imapx_stream_token (is, &token, &len, cancellable, NULL);
1556 g_set_error (error, CAMEL_IMAPX_ERROR, 1, "parse status info: expecting '('");
1561 while ((tok = camel_imapx_stream_token (is, &token, &len, cancellable, NULL)) == IMAPX_TOK_TOKEN) {
1562 switch (imapx_tokenise ((gchar *) token, len)) {
1563 case IMAPX_MESSAGES:
1564 sinfo->messages = camel_imapx_stream_number (is, cancellable, NULL);
1567 sinfo->recent = camel_imapx_stream_number (is, cancellable, NULL);
1570 sinfo->uidnext = camel_imapx_stream_number (is, cancellable, NULL);
1572 case IMAPX_UIDVALIDITY:
1573 sinfo->uidvalidity = camel_imapx_stream_number (is, cancellable, NULL);
1576 sinfo->unseen = camel_imapx_stream_number (is, cancellable, NULL);
1578 case IMAPX_HIGHESTMODSEQ:
1579 sinfo->highestmodseq = camel_imapx_stream_number (is, cancellable, NULL);
1581 case IMAPX_NOMODSEQ:
1585 g_set_error (error, CAMEL_IMAPX_ERROR, 1, "unknown status response");
1591 g_set_error (error, CAMEL_IMAPX_ERROR, 1, "missing closing ')' on status response");
1600 generate_uids_from_sequence (GPtrArray *uids,
1606 for (i = begin_uid; i <= end_uid; i++)
1607 g_ptr_array_add (uids, GUINT_TO_POINTER (i));
1611 imapx_parse_uids (CamelIMAPXStream *is,
1612 GCancellable *cancellable,
1615 GPtrArray *uids = g_ptr_array_new ();
1621 tok = camel_imapx_stream_token (is, &token, &len, cancellable, error);
1625 splits = g_strsplit ((gchar *) token, ",", -1);
1626 str_len = g_strv_length (splits);
1628 for (i = 0; i < str_len; i++) {
1629 if (g_strstr_len (splits [i], -1, ":")) {
1630 gchar **seq = g_strsplit (splits [i], ":", -1);
1631 guint32 uid1 = strtoul ((gchar *) seq[0], NULL, 10);
1632 guint32 uid2 = strtoul ((gchar *) seq[1], NULL, 10);
1634 generate_uids_from_sequence (uids, uid1, uid2);
1637 guint32 uid = strtoul ((gchar *) splits[i], NULL, 10);
1638 g_ptr_array_add (uids, GUINT_TO_POINTER (uid));
1642 g_strfreev (splits);
1647 /* rfc 2060 section 7.1 Status Responses */
1648 /* shoudl this start after [ or before the [? token_unget anyone? */
1649 struct _status_info *
1650 imapx_parse_status (CamelIMAPXStream *is,
1651 GCancellable *cancellable,
1657 struct _status_info *sinfo;
1659 sinfo = g_malloc0 (sizeof (*sinfo));
1661 camel_imapx_stream_atom (is, &token, &len, cancellable, NULL);
1664 * resp_cond_auth ::= ("OK" / "PREAUTH") SPACE resp_text
1665 * ;; Authentication condition
1667 * resp_cond_bye ::= "BYE" SPACE resp_text
1669 * resp_cond_state ::= ("OK" / "NO" / "BAD") SPACE resp_text
1670 * ;; Status condition
1673 sinfo->result = imapx_tokenise ((gchar *) token, len);
1674 switch (sinfo->result) {
1682 g_set_error (error, CAMEL_IMAPX_ERROR, 1, "expecting OK/NO/BAD");
1687 tok = camel_imapx_stream_token (is, &token, &len, cancellable, NULL);
1689 camel_imapx_stream_atom (is, &token, &len, cancellable, NULL);
1690 sinfo->condition = imapx_tokenise ((gchar *) token, len);
1692 /* parse any details */
1693 switch (sinfo->condition) {
1694 case IMAPX_READ_ONLY:
1695 case IMAPX_READ_WRITE:
1698 case IMAPX_TRYCREATE:
1701 case IMAPX_APPENDUID:
1702 sinfo->u.appenduid.uidvalidity = camel_imapx_stream_number (is, cancellable, NULL);
1703 sinfo->u.appenduid.uid = camel_imapx_stream_number (is, cancellable, NULL);
1706 sinfo->u.copyuid.uidvalidity = camel_imapx_stream_number (is, cancellable, NULL);
1707 sinfo->u.copyuid.uids = imapx_parse_uids (is, cancellable, NULL);
1708 sinfo->u.copyuid.copied_uids = imapx_parse_uids (is, cancellable, NULL);
1711 /* the rfc doesn't specify the bnf for this */
1712 camel_imapx_stream_astring (is, &token, cancellable, NULL);
1713 sinfo->u.newname.oldname = g_strdup ((gchar *) token);
1714 camel_imapx_stream_astring (is, &token, cancellable, NULL);
1715 sinfo->u.newname.newname = g_strdup ((gchar *) token);
1717 case IMAPX_PERMANENTFLAGS:
1718 /* we only care about \* for permanent flags, not user flags */
1719 imapx_parse_flags (is, &sinfo->u.permanentflags, NULL, cancellable, NULL);
1721 case IMAPX_UIDVALIDITY:
1722 sinfo->u.uidvalidity = camel_imapx_stream_number (is, cancellable, NULL);
1725 sinfo->u.uidnext = camel_imapx_stream_number (is, cancellable, NULL);
1728 sinfo->u.unseen = camel_imapx_stream_number (is, cancellable, NULL);
1730 case IMAPX_HIGHESTMODSEQ:
1731 sinfo->u.highestmodseq = camel_imapx_stream_number (is, cancellable, NULL);
1733 case IMAPX_CAPABILITY:
1734 sinfo->u.cinfo = imapx_parse_capability (is, cancellable, NULL);
1737 sinfo->condition = IMAPX_UNKNOWN;
1738 d(is->tagprefix, "Got unknown response code: %s: ignored\n", token);
1741 /* ignore anything we dont know about */
1743 tok = camel_imapx_stream_token (is, &token, &len, cancellable, NULL);
1744 if (tok == '\n' || tok < 0) {
1745 g_set_error (error, CAMEL_IMAPX_ERROR, 1, "server response truncated");
1746 imapx_free_status (sinfo);
1749 } while (tok != ']');
1751 camel_imapx_stream_ungettoken (is, tok, token, len);
1754 /* and take the human readable response */
1755 camel_imapx_stream_text (is, (guchar **) &sinfo->text, cancellable, NULL);
1760 struct _status_info *
1761 imapx_copy_status (struct _status_info *sinfo)
1763 struct _status_info *out;
1765 out = g_malloc (sizeof (*out));
1766 memcpy (out, sinfo, sizeof (*out));
1767 out->text = g_strdup (out->text);
1768 if (out->condition == IMAPX_NEWNAME) {
1769 out->u.newname.oldname = g_strdup (out->u.newname.oldname);
1770 out->u.newname.newname = g_strdup (out->u.newname.newname);
1777 imapx_free_status (struct _status_info *sinfo)
1782 switch (sinfo->condition) {
1784 g_free (sinfo->u.newname.oldname);
1785 g_free (sinfo->u.newname.newname);
1788 g_ptr_array_free (sinfo->u.copyuid.uids, FALSE);
1789 g_ptr_array_free (sinfo->u.copyuid.copied_uids, FALSE);
1791 case IMAPX_CAPABILITY:
1793 imapx_free_capability (sinfo->u.cinfo);
1799 g_free (sinfo->text);
1803 /* FIXME: use tokeniser? */
1804 /* FIXME: real flags */
1808 } list_flag_table[] = {
1809 { "\\NOINFERIORS", CAMEL_FOLDER_NOINFERIORS },
1810 { "\\NOSELECT", CAMEL_FOLDER_NOSELECT },
1811 { "\\MARKED", 1<< 16},
1812 { "\\UNMARKED", 1<< 17},
1813 { "\\SUBSCRIBED", CAMEL_FOLDER_SUBSCRIBED },
1817 imapx_parse_list (CamelIMAPXStream *is,
1818 GCancellable *cancellable,
1820 /* throws io, parse */
1824 guchar *token, *p, c;
1825 struct _list_info * linfo;
1827 linfo = g_malloc0 (sizeof (*linfo));
1829 /* mailbox_list ::= "(" #("\Marked" / "\Noinferiors" /
1830 * "\Noselect" / "\Unmarked" / flag_extension) ")"
1831 * SPACE (<"> QUOTED_CHAR <"> / nil) SPACE mailbox */
1833 tok = camel_imapx_stream_token (is, &token, &len, cancellable, NULL);
1835 g_set_error (error, CAMEL_IMAPX_ERROR, 1, "list: expecting '('");
1840 while ((tok = camel_imapx_stream_token (is, &token, &len, cancellable, NULL)) != ')') {
1841 if (tok == IMAPX_TOK_STRING || tok == IMAPX_TOK_TOKEN) {
1845 for (i = 0; i < G_N_ELEMENTS (list_flag_table); i++)
1846 if (!strcmp ((gchar *) token, list_flag_table[i].name))
1847 linfo->flags |= list_flag_table[i].flag;
1849 imapx_free_list (linfo);
1850 g_set_error (error, CAMEL_IMAPX_ERROR, 1, "list: execting flag or ')'");
1855 camel_imapx_stream_nstring (is, &token, cancellable, NULL);
1856 linfo->separator = token?*token : 0;
1857 camel_imapx_stream_astring (is, &token, cancellable, NULL);
1858 linfo->name = camel_utf7_utf8 ((gchar *) token);
1864 imapx_list_get_path (struct _list_info *li)
1870 if (li->separator != 0 && li->separator != '/') {
1871 p = path = alloca (strlen (li->name) * 3 + 1);
1873 while ((c = *f++ & 0xff)) {
1874 if (c == li->separator)
1876 else if (c == '/' || c == '%')
1877 p += sprintf(p, "%%%02X", c);
1885 return camel_utf7_utf8 (path);
1889 imapx_free_list (struct _list_info *linfo)
1892 g_free (linfo->name);
1897 /* ********************************************************************** */
1902 * ATOM_CHAR ::= <any CHAR except atom_specials>
1904 * atom_specials ::= "(" / ")" / "{" / SPACE / CTL / list_wildcards /
1907 * CHAR ::= <any 7 - bit US - ASCII character except NUL,
1910 * CTL ::= <any ASCII control character and DEL,
1911 * 0x00 - 0x1f, 0x7f>
1913 * SPACE ::= <ASCII SP, space, 0x20>
1915 * list_wildcards ::= "%" / "*"
1917 * quoted_specials ::= <"> / "\"
1919 * string ::= quoted / literal
1921 * literal ::= "{" number "}" CRLF *CHAR8
1922 * ;; Number represents the number of CHAR8 octets
1924 * quoted ::= <"> *QUOTED_CHAR <">
1926 * QUOTED_CHAR ::= <any TEXT_CHAR except quoted_specials> /
1927 * "\" quoted_specials
1929 * TEXT_CHAR ::= <any CHAR except CR and LF>
1940 guchar imapx_specials[256] = {
1941 /* 00 */0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 4, 0, 0,
1942 /* 10 */0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1943 /* 20 */4, 1, 0, 1, 1, 0, 1, 1, 0, 0, 2, 7, 1, 1, 1, 1,
1944 /* 30 */1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1945 /* 40 */7, 7, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1946 /* 50 */1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 7, 0, 7, 1, 1,
1947 /* 60 */1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1948 /* 70 */1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0,
1949 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1950 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1951 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1952 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1953 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1954 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1955 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1956 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1959 #define list_wildcards "*%"
1960 #define quoted_specials "\\\""
1961 #define atom_specials "(){" list_wildcards quoted_specials /* + CTL */
1963 /* special types for the tokeniser, come out as raw tokens */
1964 #define token_specials "\n*()[]+"
1965 #define notid_specials "\x20\r\n()[]+"
1967 void imapx_utils_init (void)
1972 for (i = 0; i < 128; i++) {
1974 if (i >= 1 && i <= 0x7f) {
1975 v |= IMAPX_TYPE_CHAR;
1976 if (i != 0x0a && i != 0x0d) {
1977 v |= IMAPX_TYPE_TEXT_CHAR;
1978 if (i != '"' && i != '\\')
1979 v |= IMAPX_TYPE_QUOTED_CHAR;
1981 if (i> 0x20 && i <0x7f && strchr (atom_specials, i) == NULL)
1982 v |= IMAPX_TYPE_ATOM_CHAR;
1983 if (strchr (token_specials, i) != NULL)
1984 v |= IMAPX_TYPE_TOKEN_CHAR;
1985 if (strchr (notid_specials, i) != NULL)
1986 v |= IMAPX_TYPE_NOTID_CHAR;
1989 imapx_specials[i] = v;
1991 camel_imapx_set_debug_flags ();
1994 guchar imapx_is_mask (const gchar *p)
1999 v &= imapx_specials[((guchar) * p) & 0xff];
2007 imapx_path_to_physical (const gchar *prefix,
2010 GString *out = g_string_new (prefix);
2011 const gchar *p = vpath;
2014 g_string_append_c (out, '/');
2016 while ((c = *p++)) {
2018 g_string_append(out, "/" SUBFOLDER_DIR_NAME "/");
2022 g_string_append_c (out, c);
2026 g_string_free (out, FALSE);
2032 imapx_concat (CamelIMAPXStore *imapx_store,
2033 const gchar *prefix,
2034 const gchar *suffix)
2038 len = strlen (prefix);
2039 if (len == 0 || prefix[len - 1] == imapx_store->dir_sep)
2040 return g_strdup_printf ("%s%s", prefix, suffix);
2042 return g_strdup_printf ("%s%c%s", prefix, imapx_store->dir_sep, suffix);
2046 imapx_namespace_clear (CamelIMAPXStoreNamespace **ns)
2048 CamelIMAPXStoreNamespace *node, *next;
2051 while (node != NULL) {
2053 g_free (node->full_name);
2054 g_free (node->path);
2063 camel_imapx_namespace_list_clear (struct _CamelIMAPXNamespaceList *nsl)
2068 imapx_namespace_clear (&nsl->personal);
2069 imapx_namespace_clear (&nsl->shared);
2070 imapx_namespace_clear (&nsl->other);
2076 static CamelIMAPXStoreNamespace *
2077 imapx_namespace_copy (const CamelIMAPXStoreNamespace *ns)
2079 CamelIMAPXStoreNamespace *list, *node, *tail;
2082 tail = (CamelIMAPXStoreNamespace *) &list;
2084 while (ns != NULL) {
2085 tail->next = node = g_malloc (sizeof (CamelIMAPXStoreNamespace));
2086 node->path = g_strdup (ns->path);
2087 node->sep = ns->sep;
2097 struct _CamelIMAPXNamespaceList *
2098 camel_imapx_namespace_list_copy (const struct _CamelIMAPXNamespaceList *nsl)
2100 CamelIMAPXNamespaceList *new;
2102 new = g_malloc (sizeof (CamelIMAPXNamespaceList));
2103 new->personal = imapx_namespace_copy (nsl->personal);
2104 new->other = imapx_namespace_copy (nsl->other);
2105 new->shared = imapx_namespace_copy (nsl->shared);
2111 imapx_get_temp_uid (void)
2115 static gint counter = 0;
2116 G_LOCK_DEFINE_STATIC (lock);
2119 res = g_strdup_printf ("tempuid-%lx-%d",
2120 (gulong) time (NULL),
2128 camel_imapx_destroy_job_queue_info (IMAPXJobQueueInfo *jinfo)
2130 g_hash_table_destroy (jinfo->folders);