static void dsync_fix_mailbox_name(struct mail_namespace *ns, string_t *vname, char alt_char) { const char *old_vname; char *p, list_sep = mailbox_list_get_hierarchy_sep(ns->list); guid_128_t guid; /* replace control chars */ for (p = str_c_modifiable(vname); *p != '\0'; p++) { if ((unsigned char)*p < ' ') *p = alt_char; } /* make it valid UTF8 */ if (!uni_utf8_str_is_valid(str_c(vname))) { old_vname = t_strdup(str_c(vname)); str_truncate(vname, 0); if (uni_utf8_get_valid_data((const void *)old_vname, strlen(old_vname), vname)) i_unreached(); } if (dsync_is_valid_name(ns, str_c(vname))) return; /* 1) change any real separators to alt separators (this wouldn't be necessary with listescape, but don't bother detecting it) */ if (list_sep != mail_namespace_get_sep(ns)) { for (p = str_c_modifiable(vname); *p != '\0'; p++) { if (*p == list_sep) *p = alt_char; } if (dsync_is_valid_name(ns, str_c(vname))) return; } /* 2) '/' characters aren't valid without listescape */ if (mail_namespace_get_sep(ns) != '/' && list_sep != '/') { for (p = str_c_modifiable(vname); *p != '\0'; p++) { if (*p == '/') *p = alt_char; } if (dsync_is_valid_name(ns, str_c(vname))) return; } /* 3) probably some reserved name (e.g. dbox-Mails) */ str_insert(vname, ns->prefix_len, "_"); if (dsync_is_valid_name(ns, str_c(vname))) return; /* 4) name is too long? just give up and generate a unique name */ guid_128_generate(guid); str_truncate(vname, 0); str_append(vname, ns->prefix); str_append(vname, guid_128_to_string(guid)); i_assert(dsync_is_valid_name(ns, str_c(vname))); }
static bool message_decode_header(struct message_decoder_context *ctx, struct message_header_line *hdr, struct message_block *output) { size_t value_len; if (hdr->continues) { hdr->use_full_value = TRUE; return FALSE; } T_BEGIN { if (hdr->name_len == 12 && strcasecmp(hdr->name, "Content-Type") == 0) parse_content_type(ctx, hdr); if (hdr->name_len == 25 && strcasecmp(hdr->name, "Content-Transfer-Encoding") == 0) ctx->message_cte = message_decoder_parse_cte(hdr); } T_END; buffer_set_used_size(ctx->buf, 0); message_header_decode_utf8(hdr->full_value, hdr->full_value_len, ctx->buf, ctx->normalizer); value_len = ctx->buf->used; if (ctx->normalizer != NULL) { (void)ctx->normalizer(hdr->name, hdr->name_len, ctx->buf); buffer_append_c(ctx->buf, '\0'); } else { if (!uni_utf8_get_valid_data((const unsigned char *)hdr->name, hdr->name_len, ctx->buf)) buffer_append_c(ctx->buf, '\0'); } ctx->hdr = *hdr; ctx->hdr.full_value = ctx->buf->data; ctx->hdr.full_value_len = value_len; ctx->hdr.value_len = 0; if (ctx->buf->used != value_len) { ctx->hdr.name = CONST_PTR_OFFSET(ctx->buf->data, ctx->hdr.full_value_len); ctx->hdr.name_len = ctx->buf->used - 1 - value_len; } output->hdr = &ctx->hdr; return TRUE; }
void fts_parser_more(struct fts_parser *parser, struct message_block *block) { if (parser->v.more != NULL) parser->v.more(parser, block); if (!uni_utf8_data_is_valid(block->data, block->size) || data_has_nuls(block->data, block->size)) { /* output isn't valid UTF-8. make it. */ if (parser->utf8_output == NULL) { parser->utf8_output = buffer_create_dynamic(default_pool, 4096); } else { buffer_set_used_size(parser->utf8_output, 0); } (void)uni_utf8_get_valid_data(block->data, block->size, parser->utf8_output); replace_nul_bytes(parser->utf8_output); block->data = parser->utf8_output->data; block->size = parser->utf8_output->used; } }
enum charset_result charset_utf8_to_utf8(normalizer_func_t *normalizer, const unsigned char *src, size_t *src_size, buffer_t *dest) { enum charset_result res = CHARSET_RET_OK; size_t pos; uni_utf8_partial_strlen_n(src, *src_size, &pos); if (pos < *src_size) { i_assert(*src_size - pos <= CHARSET_MAX_PENDING_BUF_SIZE); *src_size = pos; res = CHARSET_RET_INCOMPLETE_INPUT; } if (normalizer != NULL) { if (normalizer(src, *src_size, dest) < 0) return CHARSET_RET_INVALID_INPUT; } else if (!uni_utf8_get_valid_data(src, *src_size, dest)) { return CHARSET_RET_INVALID_INPUT; } else { buffer_append(dest, src, *src_size); } return res; }
static int maildir_fill_readdir_entry(struct maildir_list_iterate_context *ctx, struct imap_match_glob *glob, const struct dirent *d, bool update_only) { struct mailbox_list *list = ctx->ctx.list; const char *fname, *storage_name, *vname; enum mailbox_info_flags flags; enum imap_match_result match; struct mailbox_node *node; bool created; int ret; fname = d->d_name; if (fname[0] == ctx->prefix_char) storage_name = fname + 1; else { if (ctx->prefix_char != '\0' || fname[0] == '.') return 0; storage_name = fname; } /* skip . and .. */ if (fname[0] == '.' && (fname[1] == '\0' || (fname[1] == '.' && fname[2] == '\0'))) return 0; vname = mailbox_list_get_vname(list, storage_name); if (!uni_utf8_str_is_valid(vname)) { /* the storage_name is completely invalid, rename it to something more sensible. we could do this for all names that aren't valid mUTF-7, but that might lead to accidents in future when UTF-8 storage names are used */ const char *src = t_strdup_printf("%s/%s", ctx->dir, fname); string_t *destvname = t_str_new(128); string_t *dest = t_str_new(128); (void)uni_utf8_get_valid_data((const void *)fname, strlen(fname), destvname); str_append(dest, ctx->dir); str_append_c(dest, '/'); (void)imap_utf8_to_utf7(str_c(destvname), dest); if (rename(src, str_c(dest)) < 0 && errno != ENOENT) i_error("rename(%s, %s) failed: %m", src, str_c(dest)); /* just skip this in this iteration, we'll see it on the next list */ return 0; } /* make sure the pattern matches */ match = imap_match(glob, vname); if ((match & (IMAP_MATCH_YES | IMAP_MATCH_PARENT)) == 0) return 0; /* check if this is an actual mailbox */ if (maildir_delete_trash_dir(ctx, fname)) return 0; if ((ctx->ctx.flags & MAILBOX_LIST_ITER_SKIP_ALIASES) != 0) { ret = mailbox_list_dirent_is_alias_symlink(list, ctx->dir, d); if (ret != 0) return ret < 0 ? -1 : 0; } T_BEGIN { ret = list->v.get_mailbox_flags(list, ctx->dir, fname, mailbox_list_get_file_type(d), &flags); } T_END; if (ret <= 0) return ret; /* we know the children flags ourself, so ignore if any of them were set. */ flags &= ~(MAILBOX_NOINFERIORS | MAILBOX_CHILDREN | MAILBOX_NOCHILDREN); if ((match & IMAP_MATCH_PARENT) != 0) maildir_fill_parents(ctx, glob, update_only, vname); else { created = FALSE; node = update_only ? mailbox_tree_lookup(ctx->tree_ctx, vname) : mailbox_tree_get(ctx->tree_ctx, vname, &created); if (node != NULL) { if (created) node->flags = MAILBOX_NOCHILDREN; else node->flags &= ~MAILBOX_NONEXISTENT; if (!update_only) node->flags |= MAILBOX_MATCHED; node->flags |= flags; node_fix_parents(node); } else { i_assert(update_only); maildir_set_children(ctx, vname); } } return 0; }
/* Turn the value string into a valid MANAGESIEVE string or literal, no matter * what. QUOTED-SPECIALS are escaped, but any invalid (UTF-8) character * is simply removed. Linebreak characters are not considered invalid, but * they do force the generation of a string literal. */ void managesieve_quote_append(string_t *str, const unsigned char *value, size_t value_len, bool compress_lwsp) { size_t i, extra = 0, escape = 0; string_t *tmp; bool last_lwsp = TRUE, literal = FALSE, modify = FALSE; if (value == NULL) { str_append(str, "\"\""); return; } if (value_len == (size_t)-1) value_len = strlen((const char *) value); for (i = 0; i < value_len; i++) { switch (value[i]) { case ' ': case '\t': if (last_lwsp && compress_lwsp) { modify = TRUE; extra++; } last_lwsp = TRUE; break; case '"': case '\\': escape++; last_lwsp = FALSE; break; case 13: case 10: literal = TRUE; last_lwsp = TRUE; break; default: last_lwsp = FALSE; } } if (!literal) { /* no linebreak chars, return as (escaped) "string" */ str_append_c(str, '"'); } else { /* return as literal */ str_printfa(str, "{%"PRIuSIZE_T"}\r\n", value_len - extra); } tmp = t_str_new(value_len+escape+4); if (!modify && (literal || escape == 0)) str_append_n(tmp, value, value_len); else { last_lwsp = TRUE; for (i = 0; i < value_len; i++) { switch (value[i]) { case '"': case '\\': last_lwsp = FALSE; if (!literal) str_append_c(tmp, '\\'); str_append_c(tmp, value[i]); break; case ' ': case '\t': if (!last_lwsp || !compress_lwsp) str_append_c(tmp, ' '); last_lwsp = TRUE; break; case 13: case 10: last_lwsp = TRUE; str_append_c(tmp, value[i]); break; default: last_lwsp = FALSE; str_append_c(tmp, value[i]); break; } } } if ( uni_utf8_get_valid_data(str_data(tmp), str_len(tmp), str) ) str_append_str(str, tmp); if (!literal) str_append_c(str, '"'); }
static bool message_decode_body(struct message_decoder_context *ctx, struct message_block *input, struct message_block *output) { const unsigned char *data = NULL; size_t pos = 0, size = 0; int ret; if (ctx->encoding_buf->used != 0) { /* @UNSAFE */ buffer_append(ctx->encoding_buf, input->data, input->size); } switch (ctx->message_cte) { case MESSAGE_CTE_UNKNOWN: /* just skip this body */ return FALSE; case MESSAGE_CTE_78BIT: case MESSAGE_CTE_BINARY: data = input->data; size = pos = input->size; break; case MESSAGE_CTE_QP: buffer_set_used_size(ctx->buf, 0); if (ctx->encoding_buf->used != 0) { (void)quoted_printable_decode(ctx->encoding_buf->data, ctx->encoding_buf->used, &pos, ctx->buf); } else { (void)quoted_printable_decode(input->data, input->size, &pos, ctx->buf); } data = ctx->buf->data; size = ctx->buf->used; break; case MESSAGE_CTE_BASE64: buffer_set_used_size(ctx->buf, 0); if (ctx->encoding_buf->used != 0) { ret = base64_decode(ctx->encoding_buf->data, ctx->encoding_buf->used, &pos, ctx->buf); } else { ret = base64_decode(input->data, input->size, &pos, ctx->buf); } if (ret < 0) { /* corrupted base64 data, don't bother with the rest of it */ return FALSE; } if (ret == 0) { /* end of base64 input */ pos = input->size; buffer_set_used_size(ctx->encoding_buf, 0); } data = ctx->buf->data; size = ctx->buf->used; break; } if (ctx->encoding_buf->used != 0) buffer_delete(ctx->encoding_buf, 0, pos); else if (pos != input->size) { buffer_append(ctx->encoding_buf, input->data + pos, input->size - pos); } if (ctx->binary_input) { output->data = data; output->size = size; } else if (ctx->charset_utf8 || ctx->charset_trans == NULL) { /* handle unknown charsets the same as UTF-8. it might find usable ASCII text. */ buffer_set_used_size(ctx->buf2, 0); if (ctx->normalizer != NULL) { (void)ctx->normalizer(data, size, ctx->buf2); output->data = ctx->buf2->data; output->size = ctx->buf2->used; } else if (uni_utf8_get_valid_data(data, size, ctx->buf2)) { output->data = data; output->size = size; } else { output->data = ctx->buf2->data; output->size = ctx->buf2->used; } } else { buffer_set_used_size(ctx->buf2, 0); if (ctx->translation_size != 0) translation_buf_decode(ctx, &data, &size); pos = size; (void)charset_to_utf8(ctx->charset_trans, data, &pos, ctx->buf2); if (pos != size) { ctx->translation_size = size - pos; i_assert(ctx->translation_size <= sizeof(ctx->translation_buf)); memcpy(ctx->translation_buf, data + pos, ctx->translation_size); } output->data = ctx->buf2->data; output->size = ctx->buf2->used; } output->hdr = NULL; return TRUE; }