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 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 General Public
17 * License along with this program; if not, write to the
18 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19 * Boston, MA 02111-1307, USA.
34 #include "camel-imap-utils.h"
35 #include "camel-imap-summary.h"
36 #include "camel-imap-store.h"
37 #include "camel-folder.h"
38 #include "camel-string-utils.h"
39 #include "camel-utf8.h"
44 imap_next_word (const char *buf)
48 /* skip over current word */
50 while (*word && *word != ' ')
53 /* skip over white space */
54 while (*word && *word == ' ')
62 imap_namespace_destroy (struct _namespace *namespace)
64 struct _namespace *node, *next;
69 g_free (node->prefix);
76 imap_namespaces_destroy (struct _namespaces *namespaces)
79 imap_namespace_destroy (namespaces->personal);
80 imap_namespace_destroy (namespaces->other);
81 imap_namespace_destroy (namespaces->shared);
87 imap_namespace_decode (const char **in, struct _namespace **namespace)
89 struct _namespace *list, *tail, *node;
97 tail = (struct _namespace *) &list;
99 if (strncasecmp (inptr, "NIL", 3) != 0) {
103 while (*inptr && *inptr != ')') {
107 node = g_new (struct _namespace, 1);
110 /* get the namespace prefix */
111 astring = imap_parse_astring (&inptr, &len);
117 /* decode IMAP's modified UTF-7 into UTF-8 */
118 node->prefix = imap_mailbox_decode (astring, len);
128 /* get the namespace directory delimiter */
129 inptr = imap_next_word (inptr);
131 if (!strncasecmp (inptr, "NIL", 3)) {
132 inptr = imap_next_word (inptr);
134 } else if (*inptr++ == '"') {
138 node->delim = *inptr++;
146 /* parse extra flags... for now we
147 don't save them, but in the future
149 while (*inptr == ' ')
152 while (*inptr && *inptr != ')') {
153 /* this should be a QSTRING or ATOM */
154 inptr = imap_next_word (inptr);
156 /* skip over the param list */
157 imap_skip_list (&inptr);
160 while (*inptr == ' ')
168 /* there shouldn't be spaces according to the
169 ABNF grammar, but we all know how closely
170 people follow specs */
171 while (*inptr == ' ')
188 /* clean up any namespaces we may have allocated */
189 imap_namespace_destroy (list);
196 namespace_dump (struct _namespace *namespace)
198 struct _namespace *node;
204 printf ("(\"%s\" ", node->prefix);
206 printf ("\"%c\")", node->delim);
222 namespaces_dump (struct _namespaces *namespaces)
224 printf ("namespace dump: ");
225 namespace_dump (namespaces->personal);
227 namespace_dump (namespaces->other);
229 namespace_dump (namespaces->shared);
235 imap_parse_namespace_response (const char *response)
237 struct _namespaces *namespaces;
240 d(printf ("parsing: %s\n", response));
242 if (*response != '*')
245 inptr = imap_next_word (response);
246 if (strncasecmp (inptr, "NAMESPACE", 9) != 0)
249 inptr = imap_next_word (inptr);
251 namespaces = g_new (struct _namespaces, 1);
252 namespaces->personal = NULL;
253 namespaces->other = NULL;
254 namespaces->shared = NULL;
256 if (!imap_namespace_decode (&inptr, &namespaces->personal))
262 while (*inptr == ' ')
265 if (!imap_namespace_decode (&inptr, &namespaces->other))
271 while (*inptr == ' ')
274 if (!imap_namespace_decode (&inptr, &namespaces->shared))
277 d(namespaces_dump (namespaces));
283 imap_namespaces_destroy (namespaces);
289 * imap_parse_list_response:
290 * @store: the IMAP store whose list response we're parsing
291 * @buf: the LIST or LSUB response
292 * @flags: a pointer to a variable to store the flags in, or %NULL
293 * @sep: a pointer to a variable to store the hierarchy separator in, or %NULL
294 * @folder: a pointer to a variable to store the folder name in, or %NULL
296 * Parses a LIST or LSUB response and returns the desired parts of it.
297 * If @folder is non-%NULL, its value must be freed by the caller.
299 * Return value: whether or not the response was successfully parsed.
302 imap_parse_list_response (CamelImapStore *store, const char *buf, int *flags, char *sep, char **folder)
304 gboolean is_lsub = FALSE;
311 word = imap_next_word (buf);
312 if (strncasecmp (word, "LIST", 4) && strncasecmp (word, "LSUB", 4))
315 /* check if we are looking at an LSUB response */
316 if (word[1] == 'S' || word[1] == 's')
320 word = imap_next_word (word);
328 while (*word != ')') {
329 len = strcspn (word, " )");
331 if (!strncasecmp (word, "\\NoInferiors", len))
332 *flags |= CAMEL_FOLDER_NOINFERIORS;
333 else if (!strncasecmp (word, "\\NoSelect", len))
334 *flags |= CAMEL_FOLDER_NOSELECT;
335 else if (!strncasecmp (word, "\\Marked", len))
336 *flags |= CAMEL_IMAP_FOLDER_MARKED;
337 else if (!strncasecmp (word, "\\Unmarked", len))
338 *flags |= CAMEL_IMAP_FOLDER_UNMARKED;
339 else if (!strncasecmp (word, "\\HasChildren", len))
340 *flags |= CAMEL_FOLDER_CHILDREN;
341 else if (!strncasecmp (word, "\\HasNoChildren", len))
342 *flags |= CAMEL_IMAP_FOLDER_NOCHILDREN;
350 /* get the directory separator */
351 word = imap_next_word (word);
352 if (!strncmp (word, "NIL", 3)) {
355 } else if (*word++ == '"') {
369 /* get the folder name */
370 word = imap_next_word (word);
371 astring = imap_parse_astring (&word, &len);
379 mailbox = imap_mailbox_decode (astring, strlen (astring));
384 /* Kludge around Courier imap's LSUB response for INBOX when it
385 * isn't subscribed to.
387 * Ignore any \Noselect flags for INBOX when parsing
388 * an LSUB response to work around the following response:
390 * * LSUB (\Noselect \HasChildren) "." "INBOX"
392 * Fixes bug #28929 (albeight in a very dodgy way imho, but what
393 * can ya do when ya got the ignorance of marketing breathing
396 if (is_lsub && flags && !strcasecmp (mailbox, "INBOX"))
397 *flags &= ~CAMEL_FOLDER_NOSELECT;
408 * imap_parse_folder_name:
412 * Return an array of folder paths representing the folder heirarchy.
414 * Full/Path/"to / from"/Folder
416 * Full, Full/Path, Full/Path/"to / from", Full/Path/"to / from"/Folder
419 imap_parse_folder_name (CamelImapStore *store, const char *folder_name)
421 GPtrArray *heirarchy;
426 if (*p == store->dir_sep)
429 heirarchy = g_ptr_array_new ();
434 while (*p && *p != '"')
441 if (*p == store->dir_sep)
442 g_ptr_array_add (heirarchy, g_strndup (folder_name, p - folder_name));
447 g_ptr_array_add (heirarchy, g_strdup (folder_name));
448 g_ptr_array_add (heirarchy, NULL);
450 paths = (char **) heirarchy->pdata;
451 g_ptr_array_free (heirarchy, FALSE);
457 imap_create_flag_list (guint32 flags)
462 gstr = g_string_new ("(");
464 if (flags & CAMEL_MESSAGE_ANSWERED)
465 g_string_append (gstr, "\\Answered ");
466 if (flags & CAMEL_MESSAGE_DELETED)
467 g_string_append (gstr, "\\Deleted ");
468 if (flags & CAMEL_MESSAGE_DRAFT)
469 g_string_append (gstr, "\\Draft ");
470 if (flags & CAMEL_MESSAGE_FLAGGED)
471 g_string_append (gstr, "\\Flagged ");
472 if (flags & CAMEL_MESSAGE_SEEN)
473 g_string_append (gstr, "\\Seen ");
475 if (gstr->str[gstr->len - 1] == ' ')
476 gstr->str[gstr->len - 1] = ')';
478 g_string_append_c (gstr, ')');
480 flag_list = gstr->str;
481 g_string_free (gstr, FALSE);
486 imap_parse_flag_list (char **flag_list_p)
488 char *flag_list = *flag_list_p;
492 if (*flag_list++ != '(') {
497 while (*flag_list && *flag_list != ')') {
498 len = strcspn (flag_list, " )");
499 if (!strncasecmp (flag_list, "\\Answered", len))
500 flags |= CAMEL_MESSAGE_ANSWERED;
501 else if (!strncasecmp (flag_list, "\\Deleted", len))
502 flags |= CAMEL_MESSAGE_DELETED;
503 else if (!strncasecmp (flag_list, "\\Draft", len))
504 flags |= CAMEL_MESSAGE_DRAFT;
505 else if (!strncasecmp (flag_list, "\\Flagged", len))
506 flags |= CAMEL_MESSAGE_FLAGGED;
507 else if (!strncasecmp (flag_list, "\\Seen", len))
508 flags |= CAMEL_MESSAGE_SEEN;
509 else if (!strncasecmp (flag_list, "\\Recent", len))
510 flags |= CAMEL_IMAP_MESSAGE_RECENT;
513 if (*flag_list == ' ')
517 if (*flag_list++ != ')') {
522 *flag_list_p = flag_list;
529 ATOM_CHAR ::= <any CHAR except atom_specials>
531 atom_specials ::= "(" / ")" / "{" / SPACE / CTL / list_wildcards /
534 CHAR ::= <any 7-bit US-ASCII character except NUL,
537 CTL ::= <any ASCII control character and DEL,
540 SPACE ::= <ASCII SP, space, 0x20>
542 list_wildcards ::= "%" / "*"
544 quoted_specials ::= <"> / "\"
547 static unsigned char imap_atom_specials[256] = {
548 /* 00 */0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
549 /* 10 */0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
550 /* 20 */0, 1, 0, 1, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1,
551 /* 30 */1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
552 /* 40 */1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
553 /* 50 */1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1,
554 /* 60 */1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
555 /* 70 */1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0,
556 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
557 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
558 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
559 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
560 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
561 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
562 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
563 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
566 #define imap_is_atom_char(c) ((imap_atom_specials[(c)&0xff] & 0x01) != 0)
569 imap_is_atom(const char *in)
571 register unsigned char c;
572 register const char *p = in;
574 while ((c = (unsigned char)*p)) {
575 if (!imap_is_atom_char(c))
580 /* check for empty string */
585 * imap_parse_string_generic:
586 * @str_p: a pointer to a string
587 * @len: a pointer to a size_t to return the length in
588 * @type: type of string (#IMAP_STRING, #IMAP_ASTRING, or #IMAP_NSTRING)
591 * This parses an IMAP "string" (quoted string or literal), "nstring"
592 * (NIL or string), or "astring" (atom or string) starting at *@str_p.
593 * On success, *@str_p will point to the first character after the end
594 * of the string, and *@len will contain the length of the returned
595 * string. On failure, *@str_p will be set to %NULL.
597 * This assumes that the string is in the form returned by
598 * camel_imap_command(): that line breaks are indicated by LF rather
601 * Return value: the parsed string, or %NULL if a NIL or no string
602 * was parsed. (In the former case, *@str_p will be %NULL; in the
603 * latter, it will point to the character after the NIL.)
606 imap_parse_string_generic (const char **str_p, size_t *len, int type)
608 const char *str = *str_p;
613 else if (*str == '"') {
618 size = strcspn (str, "\"") + 1;
619 p = out = g_malloc (size);
621 /* a quoted string cannot be broken into multiple lines */
622 while (*str && *str != '"' && *str != '\n') {
626 if (p - out == size) {
627 out = g_realloc (out, size * 2);
641 } else if (*str == '{') {
642 *len = strtoul (str + 1, (char **)&str, 10);
643 if (*str++ != '}' || *str++ != '\n' || strlen (str) < *len) {
648 out = g_strndup (str, *len);
651 } else if (type == IMAP_NSTRING && !strncasecmp (str, "nil", 3)) {
655 } else if (type == IMAP_ASTRING && imap_is_atom_char ((unsigned char)*str)) {
656 while (imap_is_atom_char ((unsigned char) *str))
660 out = g_strndup (*str_p, *len);
670 skip_char (const char **in, char ch)
672 if (*in && **in == ch)
678 /* Skip atom, string, or number */
680 skip_asn (const char **str_p)
682 const char *str = *str_p;
686 else if (*str == '"') {
687 while (*++str && *str != '"') {
698 } else if (*str == '{') {
701 len = strtoul (str + 1, (char **) &str, 10);
702 if (*str != '}' || *(str + 1) != '\n' ||
703 strlen (str + 2) < len) {
707 *str_p = str + 2 + len;
709 /* We assume the string is well-formed and don't
710 * bother making sure it's a valid atom.
712 while (*str && *str != ')' && *str != ' ')
719 imap_skip_list (const char **str_p)
721 skip_char (str_p, '(');
722 while (*str_p && **str_p != ')') {
724 imap_skip_list (str_p);
727 if (*str_p && **str_p == ' ')
728 skip_char (str_p, ' ');
730 skip_char (str_p, ')');
734 parse_params (const char **parms_p, CamelContentType *type)
736 const char *parms = *parms_p;
740 if (!strncasecmp (parms, "nil", 3)) {
748 while (parms && *parms != ')') {
749 name = imap_parse_nstring (&parms, &len);
750 skip_char (&parms, ' ');
751 value = imap_parse_nstring (&parms, &len);
754 header_content_type_set_param (type, name, value);
758 if (parms && *parms == ' ')
762 if (!parms || *parms++ != ')')
771 static CamelMessageContentInfo *
772 imap_body_decode (const char **in, CamelMessageContentInfo *ci, CamelFolder *folder, GPtrArray *cis)
774 const char *inptr = *in;
775 CamelMessageContentInfo *child = NULL;
776 char *type, *subtype, *id = NULL;
777 CamelContentType *ctype = NULL;
778 char *description = NULL;
779 char *encoding = NULL;
788 ci = camel_folder_summary_content_info_new (folder->summary);
789 g_ptr_array_add (cis, ci);
793 /* body_type_mpart */
794 CamelMessageContentInfo *tail, *children = NULL;
796 tail = (CamelMessageContentInfo *) &children;
799 if (!(child = imap_body_decode (&inptr, NULL, folder, cis)))
805 } while (*inptr == '(');
810 if (!strncasecmp (inptr, "nil", 3) != 0) {
811 subtype = imap_parse_string (&inptr, &len);
817 ctype = header_content_type_new ("multipart", subtype ? subtype : "mixed");
820 if (*inptr++ != ')') {
821 header_content_type_unref (ctype);
826 ci->childs = children;
828 /* body_type_1part */
829 if (strncasecmp (inptr, "nil", 3) != 0) {
830 type = imap_parse_string (&inptr, &len);
837 if (*inptr++ != ' ') {
842 if (strncasecmp (inptr, "nil", 3) != 0) {
843 subtype = imap_parse_string (&inptr, &len);
849 if (!strcasecmp (type, "text"))
850 subtype = g_strdup ("plain");
856 camel_strdown (type);
857 camel_strdown (subtype);
858 ctype = header_content_type_new (type, subtype);
865 /* content-type params */
866 if (parse_params (&inptr, ctype) == -1)
873 if (strncasecmp (inptr, "nil", 3) != 0) {
874 id = imap_parse_string (&inptr, &len);
884 if (strncasecmp (inptr, "nil", 3) != 0) {
885 description = imap_parse_string (&inptr, &len);
895 if (strncasecmp (inptr, "nil", 3) != 0) {
896 encoding = imap_parse_string (&inptr, &len);
906 size = strtoul ((const char *) inptr, &p, 10);
907 inptr = (const unsigned char *) p;
909 if (header_content_type_is (ctype, "message", "rfc822")) {
915 imap_skip_list (&inptr);
921 if (!(child = imap_body_decode (&inptr, NULL, folder, cis)))
929 strtoul ((const char *) inptr, &p, 10);
930 inptr = (const unsigned char *) p;
931 } else if (header_content_type_is (ctype, "text", "*")) {
936 strtoul ((const char *) inptr, &p, 10);
937 inptr = (const unsigned char *) p;
939 /* body_type_basic */
947 ci->description = description;
948 ci->encoding = encoding;
959 header_content_type_unref (ctype);
961 g_free (description);
970 * @body_p: pointer to the start of an IMAP "body"
971 * @folder: an imap folder
972 * @ci: a CamelMessageContentInfo to fill in
974 * This fills in @ci with data from *@body_p. On success *@body_p
975 * will point to the character after the body. On failure, it will be
976 * set to %NULL and @ci will be unchanged.
979 imap_parse_body (const char **body_p, CamelFolder *folder,
980 CamelMessageContentInfo *ci)
982 const char *inptr = *body_p;
983 CamelMessageContentInfo *child;
987 if (!inptr || *inptr != '(') {
992 children = g_ptr_array_new ();
994 if (!(imap_body_decode (&inptr, ci, folder, children))) {
995 for (i = 0; i < children->len; i++) {
996 child = children->pdata[i];
998 /* content_info_free will free all the child
999 * nodes, but we don't want that. */
1001 child->parent = NULL;
1002 child->childs = NULL;
1004 camel_folder_summary_content_info_free (folder->summary, child);
1011 g_ptr_array_free (children, TRUE);
1016 * imap_quote_string:
1017 * @str: the string to quote, which must not contain CR or LF
1019 * Return value: an IMAP "quoted" corresponding to the string, which
1020 * the caller must free.
1023 imap_quote_string (const char *str)
1029 g_assert (strchr (str, '\r') == NULL);
1033 while ((p = strpbrk (p, "\"\\"))) {
1038 quoted = q = g_malloc (len + 3);
1040 for (p = str; *p; ) {
1041 if (strchr ("\"\\", *p))
1052 static inline unsigned long
1053 get_summary_uid_numeric (CamelFolderSummary *summary, int index)
1055 CamelMessageInfo *info;
1058 info = camel_folder_summary_index (summary, index);
1059 uid = strtoul (camel_message_info_uid (info), NULL, 10);
1060 camel_folder_summary_info_free (summary, info);
1064 /* the max number of chars that an unsigned 32-bit int can be is 10 chars plus 1 for a possible : */
1065 #define UID_SET_FULL(setlen, maxlen) (maxlen > 0 ? setlen + 11 >= maxlen : FALSE)
1068 * imap_uid_array_to_set:
1069 * @summary: summary for the folder the UIDs come from
1070 * @uids: a (sorted) array of UIDs
1071 * @uid: uid index to start at
1072 * @maxlen: max length of the set string (or -1 for infinite)
1073 * @lastuid: index offset of the last uid used
1075 * Creates an IMAP "set" up to @maxlen bytes long, covering the listed
1076 * UIDs starting at index @uid and not covering any UIDs that are in
1077 * @summary but not in @uids. It doesn't actually require that all (or
1078 * any) of the UIDs be in @summary.
1080 * After calling, @lastuid will be set the index of the first uid
1081 * *not* included in the returned set string.
1083 * Note: @uids MUST be in sorted order for this code to work properly.
1085 * Return value: the set, which the caller must free with g_free()
1088 imap_uid_array_to_set (CamelFolderSummary *summary, GPtrArray *uids, int uid, ssize_t maxlen, int *lastuid)
1090 unsigned long last_uid, next_summary_uid, this_uid;
1091 gboolean range = FALSE;
1096 g_return_val_if_fail (uids->len > uid, NULL);
1098 gset = g_string_new (uids->pdata[uid]);
1099 last_uid = strtoul (uids->pdata[uid], NULL, 10);
1100 next_summary_uid = 0;
1101 scount = camel_folder_summary_count (summary);
1103 for (uid++, si = 0; uid < uids->len && !UID_SET_FULL (gset->len, maxlen); uid++) {
1104 /* Find the next UID in the summary after the one we
1107 for ( ; last_uid >= next_summary_uid && si < scount; si++)
1108 next_summary_uid = get_summary_uid_numeric (summary, si);
1109 if (last_uid >= next_summary_uid)
1110 next_summary_uid = (unsigned long) -1;
1112 /* Now get the next UID from @uids */
1113 this_uid = strtoul (uids->pdata[uid], NULL, 10);
1114 if (this_uid == next_summary_uid || this_uid == last_uid + 1)
1118 g_string_append_printf (gset, ":%lu", last_uid);
1121 g_string_append_printf (gset, ",%lu", this_uid);
1124 last_uid = this_uid;
1128 g_string_append_printf (gset, ":%lu", last_uid);
1133 g_string_free (gset, FALSE);
1139 * imap_uid_set_to_array:
1140 * @summary: summary for the folder the UIDs come from
1141 * @uids: a pointer to the start of an IMAP "set" of UIDs
1143 * Fills an array with the UIDs corresponding to @uids and @summary.
1144 * There can be text after the uid set in @uids, which will be
1147 * If @uids specifies a range of UIDs that extends outside the range
1148 * of @summary, the function will assume that all of the "missing" UIDs
1151 * Return value: the array of uids, which the caller must free with
1152 * imap_uid_array_free(). (Or %NULL if the uid set can't be parsed.)
1155 imap_uid_set_to_array (CamelFolderSummary *summary, const char *uids)
1159 unsigned long uid, suid;
1162 arr = g_ptr_array_new ();
1163 scount = camel_folder_summary_count (summary);
1168 uid = strtoul (p, &q, 10);
1171 g_ptr_array_add (arr, g_strndup (p, q - p));
1174 /* Find the summary entry for the UID after the one
1177 while (++si < scount) {
1178 suid = get_summary_uid_numeric (summary, si);
1185 uid = strtoul (q + 1, &p, 10);
1189 /* Add each summary UID until we find one
1190 * larger than the end of the range
1192 while (suid <= uid) {
1193 g_ptr_array_add (arr, g_strdup_printf ("%lu", suid));
1195 suid = get_summary_uid_numeric (summary, si);
1201 } while (*p++ == ',');
1206 g_warning ("Invalid uid set %s", uids);
1207 imap_uid_array_free (arr);
1212 * imap_uid_array_free:
1213 * @arr: an array returned from imap_uid_set_to_array()
1218 imap_uid_array_free (GPtrArray *arr)
1222 for (i = 0; i < arr->len; i++)
1223 g_free (arr->pdata[i]);
1224 g_ptr_array_free (arr, TRUE);
1228 imap_concat (CamelImapStore *imap_store, const char *prefix, const char *suffix)
1232 len = strlen (prefix);
1233 if (len == 0 || prefix[len - 1] == imap_store->dir_sep)
1234 return g_strdup_printf ("%s%s", prefix, suffix);
1236 return g_strdup_printf ("%s%c%s", prefix, imap_store->dir_sep, suffix);
1240 imap_mailbox_encode (const unsigned char *in, size_t inlen)
1244 buf = g_alloca (inlen + 1);
1245 memcpy (buf, in, inlen);
1248 return camel_utf8_utf7 (buf);
1252 imap_mailbox_decode (const unsigned char *in, size_t inlen)
1256 buf = g_alloca (inlen + 1);
1257 memcpy (buf, in, inlen);
1260 return camel_utf7_utf8 (buf);