예제 #1
0
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)));
}
예제 #2
0
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;
}
예제 #3
0
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;
	}
}
예제 #4
0
파일: charset-utf8.c 프로젝트: bdraco/core
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;
}
예제 #5
0
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;
}
예제 #6
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, '"');
}
예제 #7
0
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;
}