1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
3 * Authors: Jeffrey Stedfast <fejj@ximian.com>
5 * Copyright 2000 Ximian, Inc. (www.ximian.com)
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of version 2 of the GNU Lesser General Public
9 * License as published by the Free Software Foundation.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this program; if not, write to the
18 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19 * Boston, MA 02110-1301, USA.
35 #include <sys/types.h>
38 #include <glib/gstdio.h>
40 #include "camel-folder.h"
41 #include "camel-string-utils.h"
42 #include "camel-utf8.h"
44 #include "camel-imap-store.h"
45 #include "camel-imap-summary.h"
46 #include "camel-imap-utils.h"
50 #define SUBFOLDER_DIR_NAME "subfolders"
51 #define SUBFOLDER_DIR_NAME_LEN 10
54 imap_next_word (const char *buf)
58 /* skip over current word */
60 while (*word && *word != ' ')
63 /* skip over white space */
64 while (*word && *word == ' ')
72 imap_namespace_destroy (struct _namespace *namespace)
74 struct _namespace *node, *next;
79 g_free (node->prefix);
86 imap_namespaces_destroy (struct _namespaces *namespaces)
89 imap_namespace_destroy (namespaces->personal);
90 imap_namespace_destroy (namespaces->other);
91 imap_namespace_destroy (namespaces->shared);
97 imap_namespace_decode (const char **in, struct _namespace **namespace)
99 struct _namespace *list, *tail, *node;
107 tail = (struct _namespace *) &list;
109 if (g_ascii_strncasecmp (inptr, "NIL", 3) != 0) {
113 while (*inptr && *inptr != ')') {
117 node = g_new (struct _namespace, 1);
120 /* get the namespace prefix */
121 astring = imap_parse_astring (&inptr, &len);
127 /* decode IMAP's modified UTF-7 into UTF-8 */
128 node->prefix = imap_mailbox_decode (astring, len);
138 /* get the namespace directory delimiter */
139 inptr = imap_next_word (inptr);
141 if (!g_ascii_strncasecmp (inptr, "NIL", 3)) {
142 inptr = imap_next_word (inptr);
144 } else if (*inptr++ == '"') {
148 node->delim = *inptr++;
156 /* parse extra flags... for now we
157 don't save them, but in the future
159 while (*inptr == ' ')
162 while (*inptr && *inptr != ')') {
163 /* this should be a QSTRING or ATOM */
164 inptr = imap_next_word (inptr);
166 /* skip over the param list */
167 imap_skip_list (&inptr);
170 while (*inptr == ' ')
178 /* there shouldn't be spaces according to the
179 ABNF grammar, but we all know how closely
180 people follow specs */
181 while (*inptr == ' ')
198 /* clean up any namespaces we may have allocated */
199 imap_namespace_destroy (list);
206 namespace_dump (struct _namespace *namespace)
208 struct _namespace *node;
214 printf ("(\"%s\" ", node->prefix);
216 printf ("\"%c\")", node->delim);
232 namespaces_dump (struct _namespaces *namespaces)
234 printf ("namespace dump: ");
235 namespace_dump (namespaces->personal);
237 namespace_dump (namespaces->other);
239 namespace_dump (namespaces->shared);
245 imap_parse_namespace_response (const char *response)
247 struct _namespaces *namespaces;
250 d(printf ("parsing: %s\n", response));
252 if (*response != '*')
255 inptr = imap_next_word (response);
256 if (g_ascii_strncasecmp (inptr, "NAMESPACE", 9) != 0)
259 inptr = imap_next_word (inptr);
261 namespaces = g_new (struct _namespaces, 1);
262 namespaces->personal = NULL;
263 namespaces->other = NULL;
264 namespaces->shared = NULL;
266 if (!imap_namespace_decode (&inptr, &namespaces->personal))
272 while (*inptr == ' ')
275 if (!imap_namespace_decode (&inptr, &namespaces->other))
281 while (*inptr == ' ')
284 if (!imap_namespace_decode (&inptr, &namespaces->shared))
287 d(namespaces_dump (namespaces));
293 imap_namespaces_destroy (namespaces);
299 * imap_parse_list_response:
300 * @store: the IMAP store whose list response we're parsing
301 * @buf: the LIST or LSUB response
302 * @flags: a pointer to a variable to store the flags in, or %NULL
303 * @sep: a pointer to a variable to store the hierarchy separator in, or %NULL
304 * @folder: a pointer to a variable to store the folder name in, or %NULL
306 * Parses a LIST or LSUB response and returns the desired parts of it.
307 * If @folder is non-%NULL, its value must be freed by the caller.
309 * Return value: whether or not the response was successfully parsed.
312 imap_parse_list_response (CamelImapStore *store, const char *buf, int *flags, char *sep, char **folder)
314 gboolean is_lsub = FALSE;
321 word = imap_next_word (buf);
322 if (g_ascii_strncasecmp (word, "LIST", 4) && g_ascii_strncasecmp (word, "LSUB", 4))
325 /* check if we are looking at an LSUB response */
326 if (word[1] == 'S' || word[1] == 's')
330 word = imap_next_word (word);
338 while (*word != ')') {
339 len = strcspn (word, " )");
341 if (!g_ascii_strncasecmp (word, "\\NoInferiors", len))
342 *flags |= CAMEL_FOLDER_NOINFERIORS;
343 else if (!g_ascii_strncasecmp (word, "\\NoSelect", len))
344 *flags |= CAMEL_FOLDER_NOSELECT;
345 else if (!g_ascii_strncasecmp (word, "\\Marked", len))
346 *flags |= CAMEL_IMAP_FOLDER_MARKED;
347 else if (!g_ascii_strncasecmp (word, "\\Unmarked", len))
348 *flags |= CAMEL_IMAP_FOLDER_UNMARKED;
349 else if (!g_ascii_strncasecmp (word, "\\HasChildren", len))
350 *flags |= CAMEL_FOLDER_CHILDREN;
351 else if (!g_ascii_strncasecmp (word, "\\HasNoChildren", len))
352 *flags |= CAMEL_FOLDER_NOCHILDREN;
360 /* get the directory separator */
361 word = imap_next_word (word);
362 if (!strncmp (word, "NIL", 3)) {
365 } else if (*word++ == '"') {
380 /* get the folder name */
381 word = imap_next_word (word);
382 astring = imap_parse_astring (&word, &len);
388 mailbox = imap_mailbox_decode (astring, strlen (astring));
393 /* Kludge around Courier imap's LSUB response for INBOX when it
394 * isn't subscribed to.
396 * Ignore any \Noselect flags for INBOX when parsing
397 * an LSUB response to work around the following response:
399 * * LSUB (\Noselect \HasChildren) "." "INBOX"
401 * Fixes bug #28929 (albeight in a very dodgy way imho, but what
402 * can ya do when ya got the ignorance of marketing breathing
405 if (is_lsub && flags && !g_ascii_strcasecmp (mailbox, "INBOX"))
406 *flags &= ~CAMEL_FOLDER_NOSELECT;
417 * imap_parse_folder_name:
421 * Return an array of folder paths representing the folder heirarchy.
423 * Full/Path/"to / from"/Folder
425 * Full, Full/Path, Full/Path/"to / from", Full/Path/"to / from"/Folder
428 imap_parse_folder_name (CamelImapStore *store, const char *folder_name)
430 GPtrArray *heirarchy;
435 if (*p == store->dir_sep)
438 heirarchy = g_ptr_array_new ();
443 while (*p && *p != '"')
450 if (*p == store->dir_sep)
451 g_ptr_array_add (heirarchy, g_strndup (folder_name, p - folder_name));
456 g_ptr_array_add (heirarchy, g_strdup (folder_name));
457 g_ptr_array_add (heirarchy, NULL);
459 paths = (char **) heirarchy->pdata;
460 g_ptr_array_free (heirarchy, FALSE);
466 imap_create_flag_list (guint32 flags)
471 gstr = g_string_new ("(");
473 if (flags & CAMEL_MESSAGE_ANSWERED)
474 g_string_append (gstr, "\\Answered ");
475 if (flags & CAMEL_MESSAGE_DELETED)
476 g_string_append (gstr, "\\Deleted ");
477 if (flags & CAMEL_MESSAGE_DRAFT)
478 g_string_append (gstr, "\\Draft ");
479 if (flags & CAMEL_MESSAGE_FLAGGED)
480 g_string_append (gstr, "\\Flagged ");
481 if (flags & CAMEL_MESSAGE_SEEN)
482 g_string_append (gstr, "\\Seen ");
483 if (flags & CAMEL_MESSAGE_JUNK)
484 g_string_append (gstr, "Junk ");
485 if (flags & CAMEL_IMAP_MESSAGE_LABEL1)
486 g_string_append(gstr, "$Label1 ");
487 if (flags & CAMEL_IMAP_MESSAGE_LABEL2)
488 g_string_append(gstr, "$Label2 ");
489 if (flags & CAMEL_IMAP_MESSAGE_LABEL3)
490 g_string_append(gstr, "$Label3 ");
491 if (flags & CAMEL_IMAP_MESSAGE_LABEL4)
492 g_string_append(gstr, "$Label4 ");
493 if (flags & CAMEL_IMAP_MESSAGE_LABEL5)
494 g_string_append(gstr, "$Label5 ");
496 if (gstr->str[gstr->len - 1] == ' ')
497 gstr->str[gstr->len - 1] = ')';
499 g_string_append_c (gstr, ')');
501 flag_list = gstr->str;
502 g_string_free (gstr, FALSE);
507 imap_label_to_flags(CamelMessageInfo *info)
512 label = camel_message_info_user_tag(info, "label");
515 else if (!strcmp(label, "important"))
516 flags = CAMEL_IMAP_MESSAGE_LABEL1;
517 else if (!strcmp(label, "work"))
518 flags = CAMEL_IMAP_MESSAGE_LABEL2;
519 else if (!strcmp(label, "personal"))
520 flags = CAMEL_IMAP_MESSAGE_LABEL3;
521 else if (!strcmp(label, "todo"))
522 flags = CAMEL_IMAP_MESSAGE_LABEL4;
523 else if (!strcmp(label, "later"))
524 flags = CAMEL_IMAP_MESSAGE_LABEL5;
532 imap_parse_flag_list (char **flag_list_p)
534 char *flag_list = *flag_list_p;
538 if (*flag_list++ != '(') {
543 while (*flag_list && *flag_list != ')') {
544 len = strcspn (flag_list, " )");
545 if (!g_ascii_strncasecmp (flag_list, "\\Answered", len))
546 flags |= CAMEL_MESSAGE_ANSWERED;
547 else if (!g_ascii_strncasecmp (flag_list, "\\Deleted", len))
548 flags |= CAMEL_MESSAGE_DELETED;
549 else if (!g_ascii_strncasecmp (flag_list, "\\Draft", len))
550 flags |= CAMEL_MESSAGE_DRAFT;
551 else if (!g_ascii_strncasecmp (flag_list, "\\Flagged", len))
552 flags |= CAMEL_MESSAGE_FLAGGED;
553 else if (!g_ascii_strncasecmp (flag_list, "\\Seen", len))
554 flags |= CAMEL_MESSAGE_SEEN;
555 else if (!g_ascii_strncasecmp (flag_list, "\\Recent", len))
556 flags |= CAMEL_IMAP_MESSAGE_RECENT;
557 else if (!g_ascii_strncasecmp(flag_list, "\\*", len))
558 flags |= CAMEL_MESSAGE_USER|CAMEL_MESSAGE_JUNK|CAMEL_IMAP_MESSAGE_LABEL_MASK;
559 else if (!g_ascii_strncasecmp(flag_list, "Junk", len))
560 flags |= CAMEL_MESSAGE_JUNK;
561 else if (!g_ascii_strncasecmp(flag_list, "$Label1", len))
562 flags |= CAMEL_IMAP_MESSAGE_LABEL1;
563 else if (!g_ascii_strncasecmp(flag_list, "$Label2", len))
564 flags |= CAMEL_IMAP_MESSAGE_LABEL2;
565 else if (!g_ascii_strncasecmp(flag_list, "$Label3", len))
566 flags |= CAMEL_IMAP_MESSAGE_LABEL3;
567 else if (!g_ascii_strncasecmp(flag_list, "$Label4", len))
568 flags |= CAMEL_IMAP_MESSAGE_LABEL4;
569 else if (!g_ascii_strncasecmp(flag_list, "$Label5", len))
570 flags |= CAMEL_IMAP_MESSAGE_LABEL5;
573 if (*flag_list == ' ')
577 if (*flag_list++ != ')') {
582 *flag_list_p = flag_list;
587 From RFC3501, which adds resp_specials over RFC2060
589 ATOM_CHAR ::= <any CHAR except atom_specials>
591 atom_specials ::= "(" / ")" / "{" / SPACE / CTL / list_wildcards /
592 quoted_specials / resp_secials
594 CHAR ::= <any 7-bit US-ASCII character except NUL,
597 CTL ::= <any ASCII control character and DEL,
600 SPACE ::= <ASCII SP, space, 0x20>
602 list_wildcards ::= "%" / "*"
604 quoted_specials ::= <"> / "\"
606 resp_specials ::= "]"
609 static unsigned char imap_atom_specials[256] = {
610 /* 0 1 2 3 4 5 6 7 8 9 A B C D E F */
611 /* 00 */0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
612 /* 10 */0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
613 /* 20 */0, 1, 0, 1, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1,
614 /* 30 */1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
615 /* 40 */1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
616 /* 50 */1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1,
617 /* 60 */1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
618 /* 70 */1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0,
619 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
620 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
621 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
622 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
623 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
624 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
625 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
626 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
629 #define imap_is_atom_char(c) ((imap_atom_specials[(c)&0xff] & 0x01) != 0)
632 imap_is_atom(const char *in)
634 register unsigned char c;
635 register const char *p = in;
637 while ((c = (unsigned char)*p)) {
638 if (!imap_is_atom_char(c))
643 /* check for empty string */
648 * imap_parse_string_generic:
649 * @str_p: a pointer to a string
650 * @len: a pointer to a size_t to return the length in
651 * @type: type of string (#IMAP_STRING, #IMAP_ASTRING, or #IMAP_NSTRING)
654 * This parses an IMAP "string" (quoted string or literal), "nstring"
655 * (NIL or string), or "astring" (atom or string) starting at *@str_p.
656 * On success, *@str_p will point to the first character after the end
657 * of the string, and *@len will contain the length of the returned
658 * string. On failure, *@str_p will be set to %NULL.
660 * This assumes that the string is in the form returned by
661 * camel_imap_command(): that line breaks are indicated by LF rather
664 * Return value: the parsed string, or %NULL if a NIL or no string
665 * was parsed. (In the former case, *@str_p will be %NULL; in the
666 * latter, it will point to the character after the NIL.)
669 imap_parse_string_generic (const char **str_p, size_t *len, int type)
671 const char *str = *str_p;
676 else if (*str == '"') {
681 size = strcspn (str, "\"") + 1;
682 p = out = g_malloc (size);
684 /* a quoted string cannot be broken into multiple lines */
685 while (*str && *str != '"' && *str != '\n') {
689 if (p - out == size) {
690 out = g_realloc (out, size * 2);
704 } else if (*str == '{') {
705 *len = strtoul (str + 1, (char **)&str, 10);
706 if (*str++ != '}' || *str++ != '\n' || strlen (str) < *len) {
711 out = g_strndup (str, *len);
714 } else if (type == IMAP_NSTRING && !g_ascii_strncasecmp (str, "nil", 3)) {
718 } else if (type == IMAP_ASTRING && imap_is_atom_char ((unsigned char)*str)) {
719 while (imap_is_atom_char ((unsigned char) *str))
723 out = g_strndup (*str_p, *len);
733 skip_char (const char **in, char ch)
735 if (*in && **in == ch)
741 /* Skip atom, string, or number */
743 skip_asn (const char **str_p)
745 const char *str = *str_p;
749 else if (*str == '"') {
750 while (*++str && *str != '"') {
761 } else if (*str == '{') {
764 len = strtoul (str + 1, (char **) &str, 10);
765 if (*str != '}' || *(str + 1) != '\n' ||
766 strlen (str + 2) < len) {
770 *str_p = str + 2 + len;
772 /* We assume the string is well-formed and don't
773 * bother making sure it's a valid atom.
775 while (*str && *str != ')' && *str != ' ')
782 imap_skip_list (const char **str_p)
784 skip_char (str_p, '(');
785 while (*str_p && **str_p != ')') {
787 imap_skip_list (str_p);
790 if (*str_p && **str_p == ' ')
791 skip_char (str_p, ' ');
793 skip_char (str_p, ')');
797 parse_params (const char **parms_p, CamelContentType *type)
799 const char *parms = *parms_p;
803 if (!g_ascii_strncasecmp (parms, "nil", 3)) {
811 while (parms && *parms != ')') {
812 name = imap_parse_nstring (&parms, &len);
813 skip_char (&parms, ' ');
814 value = imap_parse_nstring (&parms, &len);
817 camel_content_type_set_param (type, name, value);
821 if (parms && *parms == ' ')
825 if (!parms || *parms++ != ')')
834 static CamelMessageContentInfo *
835 imap_body_decode (const char **in, CamelMessageContentInfo *ci, CamelFolder *folder, GPtrArray *cis)
837 const char *inptr = *in;
838 CamelMessageContentInfo *child = NULL;
839 char *type, *subtype, *id = NULL;
840 CamelContentType *ctype = NULL;
841 char *description = NULL;
842 char *encoding = NULL;
851 ci = camel_folder_summary_content_info_new (folder->summary);
852 g_ptr_array_add (cis, ci);
856 /* body_type_mpart */
857 CamelMessageContentInfo *tail, *children = NULL;
859 tail = (CamelMessageContentInfo *) &children;
862 if (!(child = imap_body_decode (&inptr, NULL, folder, cis)))
868 } while (*inptr == '(');
873 if (g_ascii_strncasecmp (inptr, "nil", 3) != 0) {
874 subtype = imap_parse_string (&inptr, &len);
880 ctype = camel_content_type_new ("multipart", subtype ? subtype : "mixed");
883 if (*inptr++ != ')') {
884 camel_content_type_unref (ctype);
889 ci->childs = children;
891 /* body_type_1part */
892 if (g_ascii_strncasecmp (inptr, "nil", 3) != 0) {
893 type = imap_parse_string (&inptr, &len);
900 if (*inptr++ != ' ') {
905 if (g_ascii_strncasecmp (inptr, "nil", 3) != 0) {
906 subtype = imap_parse_string (&inptr, &len);
912 if (!g_ascii_strcasecmp (type, "text"))
913 subtype = g_strdup ("plain");
919 camel_strdown (type);
920 camel_strdown (subtype);
921 ctype = camel_content_type_new (type, subtype);
928 /* content-type params */
929 if (parse_params (&inptr, ctype) == -1)
936 if (g_ascii_strncasecmp (inptr, "nil", 3) != 0) {
937 id = imap_parse_string (&inptr, &len);
947 if (g_ascii_strncasecmp (inptr, "nil", 3) != 0) {
948 description = imap_parse_string (&inptr, &len);
958 if (g_ascii_strncasecmp (inptr, "nil", 3) != 0) {
959 encoding = imap_parse_string (&inptr, &len);
969 size = strtoul ((const char *) inptr, &p, 10);
970 inptr = (const unsigned char *) p;
972 if (camel_content_type_is (ctype, "message", "rfc822")) {
978 imap_skip_list (&inptr);
984 if (!(child = imap_body_decode (&inptr, NULL, folder, cis)))
992 strtoul ((const char *) inptr, &p, 10);
993 inptr = (const unsigned char *) p;
994 } else if (camel_content_type_is (ctype, "text", "*")) {
999 strtoul ((const char *) inptr, &p, 10);
1000 inptr = (const unsigned char *) p;
1002 /* body_type_basic */
1005 if (*inptr++ != ')')
1010 ci->description = description;
1011 ci->encoding = encoding;
1022 camel_content_type_unref (ctype);
1024 g_free (description);
1033 * @body_p: pointer to the start of an IMAP "body"
1034 * @folder: an imap folder
1035 * @ci: a CamelMessageContentInfo to fill in
1037 * This fills in @ci with data from *@body_p. On success *@body_p
1038 * will point to the character after the body. On failure, it will be
1039 * set to %NULL and @ci will be unchanged.
1042 imap_parse_body (const char **body_p, CamelFolder *folder,
1043 CamelMessageContentInfo *ci)
1045 const char *inptr = *body_p;
1046 CamelMessageContentInfo *child;
1047 GPtrArray *children;
1050 if (!inptr || *inptr != '(') {
1055 children = g_ptr_array_new ();
1057 if (!(imap_body_decode (&inptr, ci, folder, children))) {
1058 for (i = 0; i < children->len; i++) {
1059 child = children->pdata[i];
1061 /* content_info_free will free all the child
1062 * nodes, but we don't want that. */
1064 child->parent = NULL;
1065 child->childs = NULL;
1067 camel_folder_summary_content_info_free (folder->summary, child);
1074 g_ptr_array_free (children, TRUE);
1079 * imap_quote_string:
1080 * @str: the string to quote, which must not contain CR or LF
1082 * Return value: an IMAP "quoted" corresponding to the string, which
1083 * the caller must free.
1086 imap_quote_string (const char *str)
1092 g_assert (strchr (str, '\r') == NULL);
1096 while ((p = strpbrk (p, "\"\\"))) {
1101 quoted = q = g_malloc (len + 3);
1103 for (p = str; *p; ) {
1104 if (strchr ("\"\\", *p))
1115 static inline unsigned long
1116 get_summary_uid_numeric (CamelFolderSummary *summary, int index)
1118 CamelMessageInfo *info;
1121 info = camel_folder_summary_index (summary, index);
1122 uid = strtoul (camel_message_info_uid (info), NULL, 10);
1123 camel_message_info_free(info);
1127 /* the max number of chars that an unsigned 32-bit int can be is 10 chars plus 1 for a possible : */
1128 #define UID_SET_FULL(setlen, maxlen) (maxlen > 0 ? setlen + 11 >= maxlen : FALSE)
1131 * imap_uid_array_to_set:
1132 * @summary: summary for the folder the UIDs come from
1133 * @uids: a (sorted) array of UIDs
1134 * @uid: uid index to start at
1135 * @maxlen: max length of the set string (or -1 for infinite)
1136 * @lastuid: index offset of the last uid used
1138 * Creates an IMAP "set" up to @maxlen bytes long, covering the listed
1139 * UIDs starting at index @uid and not covering any UIDs that are in
1140 * @summary but not in @uids. It doesn't actually require that all (or
1141 * any) of the UIDs be in @summary.
1143 * After calling, @lastuid will be set the index of the first uid
1144 * *not* included in the returned set string.
1146 * Note: @uids MUST be in sorted order for this code to work properly.
1148 * Return value: the set, which the caller must free with g_free()
1151 imap_uid_array_to_set (CamelFolderSummary *summary, GPtrArray *uids, int uid, ssize_t maxlen, int *lastuid)
1153 unsigned long last_uid, next_summary_uid, this_uid;
1154 gboolean range = FALSE;
1159 g_return_val_if_fail (uids->len > uid, NULL);
1161 gset = g_string_new (uids->pdata[uid]);
1162 last_uid = strtoul (uids->pdata[uid], NULL, 10);
1163 next_summary_uid = 0;
1164 scount = camel_folder_summary_count (summary);
1166 for (uid++, si = 0; uid < uids->len && !UID_SET_FULL (gset->len, maxlen); uid++) {
1167 /* Find the next UID in the summary after the one we
1170 for ( ; last_uid >= next_summary_uid && si < scount; si++)
1171 next_summary_uid = get_summary_uid_numeric (summary, si);
1172 if (last_uid >= next_summary_uid)
1173 next_summary_uid = (unsigned long) -1;
1175 /* Now get the next UID from @uids */
1176 this_uid = strtoul (uids->pdata[uid], NULL, 10);
1177 if (this_uid == next_summary_uid || this_uid == last_uid + 1)
1181 g_string_append_printf (gset, ":%lu", last_uid);
1184 g_string_append_printf (gset, ",%lu", this_uid);
1187 last_uid = this_uid;
1191 g_string_append_printf (gset, ":%lu", last_uid);
1196 g_string_free (gset, FALSE);
1202 * imap_uid_set_to_array:
1203 * @summary: summary for the folder the UIDs come from
1204 * @uids: a pointer to the start of an IMAP "set" of UIDs
1206 * Fills an array with the UIDs corresponding to @uids and @summary.
1207 * There can be text after the uid set in @uids, which will be
1210 * If @uids specifies a range of UIDs that extends outside the range
1211 * of @summary, the function will assume that all of the "missing" UIDs
1214 * Return value: the array of uids, which the caller must free with
1215 * imap_uid_array_free(). (Or %NULL if the uid set can't be parsed.)
1218 imap_uid_set_to_array (CamelFolderSummary *summary, const char *uids)
1222 unsigned long uid, suid;
1225 arr = g_ptr_array_new ();
1226 scount = camel_folder_summary_count (summary);
1231 uid = strtoul (p, &q, 10);
1234 g_ptr_array_add (arr, g_strndup (p, q - p));
1237 /* Find the summary entry for the UID after the one
1240 while (++si < scount) {
1241 suid = get_summary_uid_numeric (summary, si);
1248 uid = strtoul (q + 1, &p, 10);
1252 /* Add each summary UID until we find one
1253 * larger than the end of the range
1255 while (suid <= uid) {
1256 g_ptr_array_add (arr, g_strdup_printf ("%lu", suid));
1258 suid = get_summary_uid_numeric (summary, si);
1264 } while (*p++ == ',');
1269 g_warning ("Invalid uid set %s", uids);
1270 imap_uid_array_free (arr);
1275 * imap_uid_array_free:
1276 * @arr: an array returned from imap_uid_set_to_array()
1281 imap_uid_array_free (GPtrArray *arr)
1285 for (i = 0; i < arr->len; i++)
1286 g_free (arr->pdata[i]);
1287 g_ptr_array_free (arr, TRUE);
1291 imap_concat (CamelImapStore *imap_store, const char *prefix, const char *suffix)
1295 len = strlen (prefix);
1296 if (len == 0 || prefix[len - 1] == imap_store->dir_sep)
1297 return g_strdup_printf ("%s%s", prefix, suffix);
1299 return g_strdup_printf ("%s%c%s", prefix, imap_store->dir_sep, suffix);
1303 imap_mailbox_encode (const unsigned char *in, size_t inlen)
1307 buf = g_alloca (inlen + 1);
1308 memcpy (buf, in, inlen);
1311 return camel_utf8_utf7 (buf);
1315 imap_mailbox_decode (const unsigned char *in, size_t inlen)
1319 buf = g_alloca (inlen + 1);
1320 memcpy (buf, in, inlen);
1323 return camel_utf7_utf8 (buf);
1327 imap_path_to_physical (const char *prefix, const char *vpath)
1329 GString *out = g_string_new(prefix);
1330 const char *p = vpath;
1333 g_string_append_c(out, '/');
1335 while ((c = *p++)) {
1337 g_string_append(out, "/" SUBFOLDER_DIR_NAME "/");
1341 g_string_append_c(out, c);
1345 g_string_free(out, FALSE);
1351 find_folders_recursive (const char *physical_path, const char *path,
1352 IMAPPathFindFoldersCallback callback, gpointer data)
1355 char *subfolder_directory_path;
1359 if (!callback (physical_path, path, data))
1362 subfolder_directory_path = g_strdup_printf ("%s/%s", physical_path, SUBFOLDER_DIR_NAME);
1364 /* On the top level, we have no folders and,
1365 * consequently, no subfolder directory.
1368 subfolder_directory_path = g_strdup (physical_path);
1371 /* Now scan the subfolders and load them. */
1372 dir = g_dir_open (subfolder_directory_path, 0, NULL);
1374 g_free (subfolder_directory_path);
1380 struct stat file_stat;
1385 dirent = g_dir_read_name (dir);
1389 file_path = g_strdup_printf ("%s/%s", subfolder_directory_path,
1392 if (g_stat (file_path, &file_stat) < 0 ||
1393 ! S_ISDIR (file_stat.st_mode)) {
1398 new_path = g_strdup_printf ("%s/%s", path, dirent);
1400 ok = find_folders_recursive (file_path, new_path, callback, data);
1407 g_free (subfolder_directory_path);
1413 * imap_path_find_folders:
1414 * @prefix: directory to start from
1415 * @callback: Callback to invoke on each folder
1416 * @data: Data for @callback
1418 * Walks the folder tree starting at @prefix and calls @callback
1421 * Return value: %TRUE on success, %FALSE if an error occurs at any point
1424 imap_path_find_folders (const char *prefix, IMAPPathFindFoldersCallback callback, gpointer data)
1426 return find_folders_recursive (prefix, "", callback, data);