static void out_clear(void)
{
	o_stream_seek(server->output, 0);
	str_truncate(out, 0);
}
Beispiel #2
0
static void
service_dup_fds(struct service *service)
{
	struct service_listener *const *listeners;
	ARRAY_TYPE(dup2) dups;
	string_t *listener_settings;
	int fd = MASTER_LISTEN_FD_FIRST;
	unsigned int i, count, socket_listener_count;

	/* stdin/stdout is already redirected to /dev/null. Other master fds
	   should have been opened with fd_close_on_exec() so we don't have to
	   worry about them.

	   because the destination fd might be another one's source fd we have
	   to be careful not to overwrite anything. dup() the fd when needed */

        socket_listener_count = 0;
	listeners = array_get(&service->listeners, &count);
	t_array_init(&dups, count + 10);

	switch (service->type) {
	case SERVICE_TYPE_LOG:
		i_assert(fd == MASTER_LISTEN_FD_FIRST);
		services_log_dup2(&dups, service->list, fd,
				  &socket_listener_count);
		fd += socket_listener_count;
		break;
	case SERVICE_TYPE_ANVIL:
		dup2_append(&dups, service_anvil_global->log_fdpass_fd[0],
			    MASTER_ANVIL_LOG_FDPASS_FD);
		/* nonblocking anvil fd must be the first one. anvil treats it
		   as the master's fd */
		dup2_append(&dups, service_anvil_global->nonblocking_fd[0], fd++);
		dup2_append(&dups, service_anvil_global->blocking_fd[0], fd++);
		socket_listener_count += 2;
		break;
	default:
		break;
	}

	/* add listeners */
	listener_settings = t_str_new(256);
	for (i = 0; i < count; i++) {
		if (listeners[i]->fd != -1) {
			str_truncate(listener_settings, 0);
			str_append_tabescaped(listener_settings, listeners[i]->name);

			if (listeners[i]->type == SERVICE_LISTENER_INET) {
				if (listeners[i]->set.inetset.set->ssl)
					str_append(listener_settings, "\tssl");
				if (listeners[i]->set.inetset.set->haproxy)
					str_append(listener_settings, "\thaproxy");
			}
			
			dup2_append(&dups, listeners[i]->fd, fd++);

			env_put(t_strdup_printf("SOCKET%d_SETTINGS=%s",
				socket_listener_count, str_c(listener_settings)));
			socket_listener_count++;
		}
	}

	if (service->login_notify_fd != -1) {
		dup2_append(&dups, service->login_notify_fd,
			    MASTER_LOGIN_NOTIFY_FD);
	}
	switch (service->type) {
	case SERVICE_TYPE_LOG:
	case SERVICE_TYPE_ANVIL:
	case SERVICE_TYPE_CONFIG:
		dup2_append(&dups, null_fd, MASTER_ANVIL_FD);
		break;
	case SERVICE_TYPE_UNKNOWN:
	case SERVICE_TYPE_LOGIN:
	case SERVICE_TYPE_STARTUP:
		dup2_append(&dups, service_anvil_global->blocking_fd[1],
			    MASTER_ANVIL_FD);
		break;
	}
	dup2_append(&dups, service->status_fd[1], MASTER_STATUS_FD);
	if (service->type != SERVICE_TYPE_ANVIL) {
		dup2_append(&dups, service->list->master_dead_pipe_fd[1],
			    MASTER_DEAD_FD);
	} else {
		dup2_append(&dups, global_master_dead_pipe_fd[1],
			    MASTER_DEAD_FD);
	}

	if (service->type == SERVICE_TYPE_LOG) {
		/* keep stderr as-is. this is especially important when
		   log_path=/dev/stderr, but might be helpful even in other
		   situations for logging startup errors */
	} else {
		/* set log file to stderr. dup2() here immediately so that
		   we can set up logging to it without causing any log messages
		   to be lost. */
		i_assert(service->log_fd[1] != -1);

		env_put("LOG_SERVICE=1");
		if (dup2(service->log_fd[1], STDERR_FILENO) < 0)
			i_fatal("dup2(log fd) failed: %m");
		i_set_failure_internal();
	}

	/* make sure we don't leak syslog fd. try to do it as late as possible,
	   but also before dup2()s in case syslog fd is one of them. */
	closelog();

	if (dup2_array(&dups) < 0)
		i_fatal("service(%s): dup2s failed", service->set->name);

	i_assert(fd == MASTER_LISTEN_FD_FIRST + (int)socket_listener_count);
	env_put(t_strdup_printf("SOCKET_COUNT=%d", socket_listener_count));
}
Beispiel #3
0
static void test_istream_crlf_input(const char *input)
{
	string_t *output;
	const unsigned char *data;
	size_t size = 0;
	ssize_t ret1, ret2;
	unsigned int i, j, pos, input_len = strlen(input);
	struct istream *istream, *crlf_istream;

	output = t_str_new(256);

	for (j = 0; j < 4; j++) {
		istream = i_stream_create_from_data(input, input_len);
		str_truncate(output, 0);
		if (j%2 == 0) {
			/* drop CRs */
			crlf_istream = i_stream_create_lf(istream);
			for (i = 0; i < input_len; i++) {
				if (input[i] == '\r' &&
				    (i == input_len || input[i+1] == '\n'))
					;
				else
					str_append_c(output, input[i]);
			}
		} else {
			/* add missing CRs */
			crlf_istream = i_stream_create_crlf(istream);
			for (i = 0; i < input_len; i++) {
				if (input[i] == '\n' &&
				    (i == 0 || input[i-1] != '\r'))
					str_append_c(output, '\r');
				str_append_c(output, input[i]);
			}
		}

		pos = 0;
		for (i = 1; i <= input_len; i++) {
			if (j >= 2) {
				i_stream_unref(&istream);
				i_stream_unref(&crlf_istream);
				istream = i_stream_create_from_data(input,
								    input_len);
				crlf_istream = j%2 == 0 ?
					i_stream_create_lf(istream) :
					i_stream_create_crlf(istream);
				pos = 0;
			}
			istream->real_stream->pos = i;
			ret1 = i_stream_read(crlf_istream);
			if (crlf_istream->real_stream->buffer_size != 0) {
				/* this is pretty evil */
				crlf_istream->real_stream->buffer_size =
					I_MAX(crlf_istream->real_stream->pos, i);
			}
			ret2 = i_stream_read(crlf_istream);
			data = i_stream_get_data(crlf_istream, &size);
			if (ret1 > 0 || ret2 > 0) {
				ret1 = I_MAX(ret1, 0) + I_MAX(ret2, 0);
				test_assert(pos + (unsigned int)ret1 == size);
				pos += ret1;
			}
			test_assert(memcmp(data, str_data(output), size) == 0);
		}
		test_assert(size == str_len(output));
		i_stream_unref(&crlf_istream);
		i_stream_unref(&istream);
	}
}
int rfc2231_parse(struct rfc822_parser_context *ctx,
                  const char *const **result_r)
{
    ARRAY_TYPE(const_string) result;
    ARRAY(struct rfc2231_parameter) rfc2231_params_arr;
    struct rfc2231_parameter rfc2231_param;
    const struct rfc2231_parameter *rfc2231_params;
    const char *key, *value, *p, *p2;
    string_t *str;
    unsigned int i, j, count, next, next_idx;
    bool ok, have_extended, broken = FALSE;
    int ret;

    /* Get a list of all parameters. RFC 2231 uses key*<n>[*]=value pairs,
       which we want to merge to a key[*]=value pair. Save them to a
       separate array. */
    memset(&rfc2231_param, 0, sizeof(rfc2231_param));
    t_array_init(&result, 8);
    t_array_init(&rfc2231_params_arr, 8);
    while ((ret = rfc822_parse_content_param(ctx, &key, &value)) != 0) {
        if (ret < 0) {
            /* try to continue anyway.. */
            broken = TRUE;
            if (ctx->data == ctx->end)
                break;
            ctx->data++;
            continue;
        }
        p = strchr(key, '*');
        if (p != NULL) {
            p2 = p;
            if (p[1] != '\0') {
                p++;
                rfc2231_param.idx = 0;
                for (; *p >= '0' && *p <= '9'; p++) {
                    rfc2231_param.idx =
                        rfc2231_param.idx*10 + *p - '0';
                }
            }
            if (*p != '*')
                rfc2231_param.extended = FALSE;
            else {
                rfc2231_param.extended = TRUE;
                p++;
            }
            if (*p != '\0')
                p = NULL;
            else {
                rfc2231_param.key = t_strdup_until(key, p2);
                rfc2231_param.value = value;
                array_append(&rfc2231_params_arr,
                             &rfc2231_param, 1);
            }
        }
        if (p == NULL) {
            array_append(&result, &key, 1);
            array_append(&result, &value, 1);
        }
    }

    if (array_count(&rfc2231_params_arr) == 0) {
        /* No RFC 2231 parameters */
        array_append_zero(&result); /* NULL-terminate */
        *result_r = array_idx(&result, 0);
        return broken ? -1 : 0;
    }

    /* Merge the RFC 2231 parameters. Since their order isn't guaranteed to
       be ascending, start by sorting them. */
    array_sort(&rfc2231_params_arr, rfc2231_parameter_cmp);
    rfc2231_params = array_get(&rfc2231_params_arr, &count);

    /* keys are now sorted primarily by their name and secondarily by
       their index. If any indexes are missing, fallback to assuming
       these aren't RFC 2231 encoded parameters. */
    str = t_str_new(64);
    for (i = 0; i < count; i = next) {
        ok = TRUE;
        have_extended = FALSE;
        next_idx = 0;
        for (j = i; j < count; j++) {
            if (strcasecmp(rfc2231_params[i].key,
                           rfc2231_params[j].key) != 0)
                break;
            if (rfc2231_params[j].idx != next_idx) {
                /* missing indexes */
                ok = FALSE;
            }
            if (rfc2231_params[j].extended)
                have_extended = TRUE;
            next_idx++;
        }
        next = j;

        if (!ok) {
            /* missing indexes */
            for (j = i; j < next; j++) {
                key = t_strdup_printf(
                          rfc2231_params[j].extended ?
                          "%s*%u*" : "%s*%u",
                          rfc2231_params[j].key,
                          rfc2231_params[j].idx);
                array_append(&result, &key, 1);
                array_append(&result,
                             &rfc2231_params[j].value, 1);
            }
        } else {
            /* everything was successful */
            str_truncate(str, 0);
            if (!rfc2231_params[i].extended && have_extended)
                str_append(str, "''");
            for (j = i; j < next; j++) {
                if (!rfc2231_params[j].extended &&
                        have_extended) {
                    rfc2231_escape(str,
                                   rfc2231_params[j].value);
                } else {
                    str_append(str,
                               rfc2231_params[j].value);
                }
            }
            key = rfc2231_params[i].key;
            if (have_extended)
                key = t_strconcat(key, "*", NULL);
            value = t_strdup(str_c(str));
            array_append(&result, &key, 1);
            array_append(&result, &value, 1);
        }
    }
    array_append_zero(&result); /* NULL-terminate */
    *result_r = array_idx(&result, 0);
    return broken ? -1 : 0;
}
static bool arg_testsuite_string_validate
(struct sieve_validator *valdtr, struct sieve_ast_argument **arg,
	struct sieve_command *cmd)
{
	enum { ST_NONE, ST_OPEN, ST_SUBSTITUTION, ST_PARAM, ST_CLOSE } state =
		ST_NONE;
	pool_t pool = sieve_ast_pool((*arg)->ast);
	struct sieve_arg_catenated_string *catstr = NULL;
	string_t *str = sieve_ast_argument_str(*arg);
	const char *p, *strstart, *substart = NULL;
	const char *strval = (const char *) str_data(str);
	const char *strend = strval + str_len(str);
	bool result = TRUE;
	string_t *subs_name = t_str_new(256);
	string_t *subs_param = t_str_new(256);

	T_BEGIN {
		/* Initialize substitution structure */

		p = strval;
		strstart = p;
		while ( result && p < strend ) {
			switch ( state ) {

			/* Nothing found yet */
			case ST_NONE:
				if ( *p == '%' ) {
					substart = p;
					state = ST_OPEN;
					str_truncate(subs_name, 0);
					str_truncate(subs_param, 0);
				}
				p++;
				break;

			/* Got '%' */
			case ST_OPEN:
				if ( *p == '{' ) {
					state = ST_SUBSTITUTION;
					p++;
				} else
					state = ST_NONE;
				break;

			/* Got '%{' */
			case ST_SUBSTITUTION:
				state = ST_PARAM;

				while ( *p != '}' && *p != ':' ) {
					if ( !i_isalnum(*p) ) {
						state = ST_NONE;
						break;
					}
					str_append_c(subs_name, *p);
					p++;
				}
				break;

			/* Got '%{name' */
			case ST_PARAM:
				if ( *p == ':' ) {
					p++;
					while ( *p != '}' ) {
						str_append_c(subs_param, *p);
						p++;
					}
				}
				state = ST_CLOSE;
				break;

			/* Finished parsing param, expecting '}' */
			case ST_CLOSE:
				if ( *p == '}' ) {
					struct sieve_ast_argument *strarg;

					/* We now know that the substitution is valid */

					if ( catstr == NULL ) {
						catstr = sieve_arg_catenated_string_create(*arg);
					}

					/* Add the substring that is before the substitution to the
					 * variable-string AST.
					 */
					if ( substart > strstart ) {
						string_t *newstr = str_new(pool, substart - strstart);
						str_append_n(newstr, strstart, substart - strstart);

						strarg = sieve_ast_argument_string_create_raw
							((*arg)->ast, newstr, (*arg)->source_line);
						sieve_arg_catenated_string_add_element(catstr, strarg);

						/* Give other substitution extensions a chance to do their work */
						if ( !sieve_validator_argument_activate_super
							(valdtr, cmd, strarg, FALSE) ) {
							result = FALSE;
							break;
						}
					}

					strarg = testsuite_substitution_argument_create
						(valdtr, (*arg)->ast, (*arg)->source_line, str_c(subs_name),
							str_c(subs_param));

					if ( strarg != NULL )
						sieve_arg_catenated_string_add_element(catstr, strarg);
					else {
						sieve_argument_validate_error(valdtr, *arg,
							"unknown testsuite substitution type '%s'", str_c(subs_name));
					}

					strstart = p + 1;
					substart = strstart;

					p++;
				}

				/* Finished, reset for the next substitution */
				state = ST_NONE;
			}
		}
	} T_END;

	/* Bail out early if substitution is invalid */
	if ( !result ) return FALSE;

	/* Check whether any substitutions were found */
	if ( catstr == NULL ) {
		/* No substitutions in this string, pass it on to any other substution
		 * extension.
		 */
		return sieve_validator_argument_activate_super(valdtr, cmd, *arg, TRUE);
	}

	/* Add the final substring that comes after the last substitution to the
	 * variable-string AST.
	 */
	if ( strend > strstart ) {
		struct sieve_ast_argument *strarg;
		string_t *newstr = str_new(pool, strend - strstart);
		str_append_n(newstr, strstart, strend - strstart);

		strarg = sieve_ast_argument_string_create_raw
			((*arg)->ast, newstr, (*arg)->source_line);
		sieve_arg_catenated_string_add_element(catstr, strarg);

		/* Give other substitution extensions a chance to do their work */
		if ( !sieve_validator_argument_activate_super
			(valdtr, cmd, strarg, FALSE) )
			return FALSE;
	}

	return TRUE;
}
Beispiel #6
0
static void client_connected(struct master_service_connection *conn)
{
	enum mail_storage_service_flags flags =
		MAIL_STORAGE_SERVICE_FLAG_NO_PLUGINS;
	string_t *instr, *keys;
	const char **args, *key, *value, *error, *version_line, *data_line;
	struct mail_storage_service_ctx *service_ctx;
	struct mail_storage_service_input input;
	struct mail_storage_service_user *user;
	char buf[1024];
	unsigned int i, socket_count;
	int fd = -1;
	ssize_t ret;

	alarm(SCRIPT_LOGIN_READ_TIMEOUT_SECS);

	net_set_nonblock(conn->fd, FALSE);
	instr = t_str_new(1024);
	ret = fd_read(conn->fd, buf, sizeof(buf), &fd);
	while (ret > 0) {
		str_append_n(instr, buf, ret);
		if (buf[ret-1] == '\n' &&
		    strchr(str_c(instr), '\n')[1] != '\0') {
			str_truncate(instr, str_len(instr)-1);
			break;
		}

		ret = read(conn->fd, buf, sizeof(buf));
	}

	version_line = str_c(instr);
	data_line = strchr(version_line, '\n');
	if (data_line != NULL)
		version_line = t_strdup_until(version_line, data_line++);
	else
		version_line = NULL;

	if (ret > 0 || version_line != NULL) {
		if (version_line == NULL ||
		    !version_string_verify(version_line, "script-login",
				SCRIPT_LOGIN_PROTOCOL_VERSION_MAJOR)) {
			i_fatal("Client not compatible with this binary "
				"(connecting to wrong socket?)");
		}
	}

	if (ret <= 0) {
		if (ret < 0)
			i_fatal("read() failed: %m");
		else
			i_fatal("read() failed: disconnected");
	}
	if (fd == -1)
		i_fatal("client fd not received");

	alarm(0);

	/* put everything to environment */
	env_clean();
	keys = t_str_new(256);
	args = t_strsplit_tab(data_line);

	if (str_array_length(args) < 3)
		i_fatal("Missing input fields");

	i = 0;
	memset(&input, 0, sizeof(input));
	input.module = "mail"; /* need to get mail_uid, mail_gid */
	input.service = "script-login";
	(void)net_addr2ip(args[i++], &input.local_ip);
	(void)net_addr2ip(args[i++], &input.remote_ip);
	input.username = args[i++];
	input.userdb_fields = args + i;

	env_put(t_strconcat("LOCAL_IP=", net_ip2addr(&input.local_ip), NULL));
	env_put(t_strconcat("IP=", net_ip2addr(&input.remote_ip), NULL));
	env_put(t_strconcat("USER="******"%s ", key);
		}
	}
	env_put(t_strconcat(ENV_USERDB_KEYS"=", str_c(keys), NULL));

	master_service_init_log(master_service,
		t_strdup_printf("script-login(%s): ", input.username));

	if (drop_to_userdb_privileges) {
		service_ctx = mail_storage_service_init(master_service, NULL, flags);
		if (mail_storage_service_lookup(service_ctx, &input, &user, &error) <= 0)
			i_fatal("%s", error);
		mail_storage_service_restrict_setenv(service_ctx, user);
		/* we can't exec anything in a chroot */
		env_remove("RESTRICT_CHROOT");
		restrict_access_by_env(getenv("HOME"), TRUE);
	}

	if (dup2(fd, STDIN_FILENO) < 0)
		i_fatal("dup2() failed: %m");
	if (dup2(fd, STDOUT_FILENO) < 0)
		i_fatal("dup2() failed: %m");
	if (close(fd) < 0)
		i_fatal("close() failed: %m");
	if (conn->fd != SCRIPT_COMM_FD) {
		if (dup2(conn->fd, SCRIPT_COMM_FD) < 0)
			i_fatal("dup2() failed: %m");
		if (close(conn->fd) < 0)
			i_fatal("close() failed: %m");
	}

	/* close all listener sockets */
	socket_count = master_service_get_socket_count(master_service);
	for (i = 0; i < socket_count; i++) {
		if (close(MASTER_LISTEN_FD_FIRST + i) < 0)
			i_error("close(listener) failed: %m");
	}
	if (close(MASTER_STATUS_FD) < 0)
		i_error("close(status) failed: %m");

	execvp_const(exec_args[0], exec_args);
}
Beispiel #7
0
static bool parse_x_keywords_real(struct mbox_sync_mail_context *ctx,
				  struct message_header_line *hdr)
{
	struct mailbox *box = &ctx->sync_ctx->mbox->box;
	ARRAY_TYPE(keyword_indexes) keyword_list;
	const unsigned int *list;
	string_t *keyword;
	size_t keyword_start;
	unsigned int i, idx, count;
	size_t pos;

	if (array_is_created(&ctx->mail.keywords))
		return FALSE; /* duplicate header, delete */

	/* read keyword indexes to temporary array first */
	keyword = t_str_new(128);
	t_array_init(&keyword_list, 16);

	for (pos = 0; pos < hdr->full_value_len; ) {
		if (IS_LWSP_LF(hdr->full_value[pos])) {
                        pos++;
			continue;
		}

		/* read the keyword string */
		keyword_start = pos;
		for (; pos < hdr->full_value_len; pos++) {
			if (IS_LWSP_LF(hdr->full_value[pos]))
				break;
		}

		str_truncate(keyword, 0);
		str_append_n(keyword, hdr->full_value + keyword_start,
			     pos - keyword_start);
		if (!mail_index_keyword_lookup(box->index, str_c(keyword),
					       &idx)) {
			/* keyword wasn't found. that means the sent mail
			   originally contained X-Keywords header. Delete it. */
			return FALSE;
		}

		/* check that the keyword isn't already added there.
		   we don't want duplicates. */
		list = array_get(&keyword_list, &count);
		for (i = 0; i < count; i++) {
			if (list[i] == idx)
				break;
		}

		if (i == count)
			array_append(&keyword_list, &idx, 1);
	}

	/* once we know how many keywords there are, we can allocate the array
	   from mail_keyword_pool without wasting memory. */
	if (array_count(&keyword_list) > 0) {
		p_array_init(&ctx->mail.keywords,
			     ctx->sync_ctx->mail_keyword_pool,
			     array_count(&keyword_list));
		array_append_array(&ctx->mail.keywords, &keyword_list);
	}

	ctx->hdr_pos[MBOX_HDR_X_KEYWORDS] = str_len(ctx->header);
	parse_trailing_whitespace(ctx, hdr);
	return TRUE;
}
Beispiel #8
0
static void
mail_log_append_mail_message_real(struct mail_log_mail_txn_context *ctx,
				  struct mail *mail, enum mail_log_event event,
				  const char *desc)
{
	struct mail_log_user *muser =
		MAIL_LOG_USER_CONTEXT(mail->box->storage->user);
	struct mail_log_message *msg;
	string_t *text;
	uoff_t size;
	
	msg = p_new(ctx->pool, struct mail_log_message, 1);

	text = t_str_new(128);
	str_append(text, desc);
	str_append(text, ": ");
	if ((muser->fields & MAIL_LOG_FIELD_BOX) != 0) {
		mail_log_append_mailbox_name(text, mail);
		str_append(text, ", ");
	}
	if ((muser->fields & MAIL_LOG_FIELD_UID) != 0) {
		if (event != MAIL_LOG_EVENT_SAVE &&
		    event != MAIL_LOG_EVENT_COPY)
			mail_log_append_uid(ctx, msg, text, mail->uid);
		else {
			/* with mbox mail->uid contains the uid, but handle
			   this consistently with all mailbox formats */
			mail_log_append_uid(ctx, msg, text, 0);
		}
		str_append(text, ", ");
	}
	if ((muser->fields & MAIL_LOG_FIELD_MSGID) != 0) {
		mail_log_append_mail_header(text, mail, "msgid", "Message-ID");
		str_append(text, ", ");
	}
	if ((muser->fields & MAIL_LOG_FIELD_PSIZE) != 0) {
		if (mail_get_physical_size(mail, &size) == 0)
			str_printfa(text, "size=%"PRIuUOFF_T, size);
		else
			str_printfa(text, "size=error");
		str_append(text, ", ");
	}
	if ((muser->fields & MAIL_LOG_FIELD_VSIZE) != 0) {
		if (mail_get_virtual_size(mail, &size) == 0)
			str_printfa(text, "vsize=%"PRIuUOFF_T, size);
		else
			str_printfa(text, "vsize=error");
		str_append(text, ", ");
	}
	if ((muser->fields & MAIL_LOG_FIELD_FROM) != 0) {
		mail_log_append_mail_header(text, mail, "from", "From");
		str_append(text, ", ");
	}
	if ((muser->fields & MAIL_LOG_FIELD_SUBJECT) != 0) {
		mail_log_append_mail_header(text, mail, "subject", "Subject");
		str_append(text, ", ");
	}
	if ((muser->fields & MAIL_LOG_FIELD_FLAGS) != 0) {
		str_printfa(text, "flags=(");
		imap_write_flags(text, mail_get_flags(mail),
				 mail_get_keywords(mail));
		str_append(text, "), ");
	}
	str_truncate(text, str_len(text)-2);

	msg->event = event;
	msg->text = p_strdup(ctx->pool, str_c(text));
	DLLIST2_APPEND(&ctx->messages, &ctx->messages_tail, msg);
}
static void test_message_address(void)
{
	static const char *input[] = {
		"user@domain", NULL,
		"<user@domain>", "user@domain",
		"foo bar <user@domain>", NULL,
		"\"foo bar\" <user@domain>", "foo bar <user@domain>",
		"<@route:user@domain>", NULL,
		"<@route@route2:user@domain>", "<@route,@route2:user@domain>",
		"hello <@route ,@route2:user@domain>", "hello <@route,@route2:user@domain>",
		"user (hello)", NULL,
		"hello <user>", NULL,
		"@domain", NULL
	};
	static struct message_address group_prefix = {
		NULL, NULL, NULL, "group", NULL, FALSE
	};
	static struct message_address group_suffix = {
		NULL, NULL, NULL, NULL, NULL, FALSE
	};
	static struct message_address output[] = {
		{ NULL, NULL, NULL, "user", "domain", FALSE },
		{ NULL, NULL, NULL, "user", "domain", FALSE },
		{ NULL, "foo bar", NULL, "user", "domain", FALSE },
		{ NULL, "foo bar", NULL, "user", "domain", FALSE },
		{ NULL, NULL, "@route", "user", "domain", FALSE },
		{ NULL, NULL, "@route,@route2", "user", "domain", FALSE },
		{ NULL, "hello", "@route,@route2", "user", "domain", FALSE },
		{ NULL, "hello", NULL, "user", "", TRUE },
		{ NULL, "hello", NULL, "user", "", TRUE },
		{ NULL, NULL, NULL, "", "domain", TRUE }
	};
	struct message_address *addr;
	string_t *str, *group;
	const char *wanted_string;
	unsigned int i;

	i_assert(N_ELEMENTS(input) == N_ELEMENTS(output)*2);

	test_begin("message address parsing");
	str = t_str_new(128);
	group = t_str_new(256);
	str_append(group, "group: ");

	for (i = 0; i < N_ELEMENTS(output); i++) {
		addr = message_address_parse(pool_datastack_create(),
					     (const unsigned char *)input[i*2],
					     strlen(input[i*2]), UINT_MAX, FALSE);
		test_assert(addr != NULL && addr->next == NULL &&
			    cmp_addr(addr, &output[i]));

		if (!output[i].invalid_syntax) {
			str_truncate(str, 0);
			message_address_write(str, addr);
			wanted_string = input[i*2+1] != NULL ?
				input[i*2+1] : input[i*2];
			test_assert(strcmp(str_c(str), wanted_string) == 0);
			if (i != 0) {
				if ((i % 2) == 0)
					str_append(group, ",");
				else
					str_append(group, " , \n ");
			}
			str_append(group, input[i*2]);
		}
	}
	str_append_c(group, ';');
	test_end();

	test_begin("message address parsing with groups");
	addr = message_address_parse(pool_datastack_create(), str_data(group),
				     str_len(group), UINT_MAX, FALSE);
	test_assert(addr != NULL && cmp_addr(addr, &group_prefix));
	addr = addr->next;
	for (i = 0; i < N_ELEMENTS(output) && addr != NULL; i++) {
		if (output[i].invalid_syntax)
			continue;
		test_assert(cmp_addr(addr, &output[i]));
		addr = addr->next;
	}
	test_assert(addr != NULL && addr->next == NULL &&
		    cmp_addr(addr, &group_suffix));
	test_end();

	test_begin("message address parsing with empty group");
	str_truncate(group, 0);
	str_append(group, "group:;");
	addr = message_address_parse(pool_datastack_create(), str_data(group),
				     str_len(group), UINT_MAX, FALSE);
	test_assert(addr != NULL && cmp_addr(addr, &group_prefix));
	addr = addr->next;
	test_assert(addr != NULL && addr->next == NULL &&
		    cmp_addr(addr, &group_suffix));
	test_end();
}
Beispiel #10
0
void
dates_delete_cb (GtkWidget *source, DatesData *d)
{
    ECalComponentText summary;
    GtkWidget *widget;
    const char *orig_value;
    char *value;
#ifdef WITH_HILDON
    gchar *tmp;
#endif

    e_cal_component_get_summary (d->comp, &summary);
    widget = d->details_dialog;
    if (GTK_WIDGET_VISIBLE (widget))
	orig_value = gtk_entry_get_text (GTK_ENTRY (d->details_summary_entry));
    else if (summary.value)
	orig_value = summary.value;
    else if (summary.altrep)
	orig_value = summary.altrep;
    else {
	g_warning ("Deleting event with no summary");
	orig_value = _("Unknown event");
    }

    value = str_truncate (orig_value, 50);

#ifdef WITH_HILDON
    tmp = g_strdup_printf(_("Are you sure you want to delete event '%s'?"), value);
    widget = hildon_note_new_confirmation_add_buttons(
        GTK_WINDOW (d->main_window),
        tmp,
        _("Keep event"), GTK_RESPONSE_NO,
        _("Delete event"), GTK_RESPONSE_YES,
        NULL);
    g_free(tmp);
#else
    widget = gtk_message_dialog_new (
	GTK_WINDOW (d->main_window),
	GTK_DIALOG_MODAL,
	GTK_MESSAGE_QUESTION,
	GTK_BUTTONS_NONE,
	_("Are you sure you want to delete event '%s'?"),
	value);
    gtk_dialog_add_buttons (GTK_DIALOG (widget),
			    _("Keep event"), GTK_RESPONSE_NO,
			    _("Delete event"), GTK_RESPONSE_YES,
			    NULL);
#endif
    if (gtk_dialog_run (GTK_DIALOG (widget)) == GTK_RESPONSE_YES) {
	/* Reset event type, in case this was a new event */
	d->event_type = NORMAL;
		
	if (d->waiting == NONE) {
	    const char *uid = NULL;
	    e_cal_component_get_uid (d->comp, &uid);
	    e_cal_remove_object (d->cal, uid, NULL);

	    /* Hide the details dialog, in case we deleted
	     * from there.
	     */
	    dates_platform_details_dlg (d, FALSE);
	} else {
	    d->widgets = contacts_set_widgets_desensitive (
		d->details_dialog);
	    d->waiting = PENDING_DELETE;
	}
    }
	
    gtk_widget_destroy (widget);
    g_free (value);
}
/*
 * Load background file frequencies into the array.
 */
ARRAY_T* get_file_frequencies(ALPH_T *alph, char *bg_filename, ARRAY_T *freqs) {
  regmatch_t matches[4];
  STR_T *line;
  char chunk[BG_CHUNK_SIZE+1], letter[2], *key;
  int size, terminate, offset, i;
  FILE *fp;
  regex_t bgfreq;
  double freq;
  RBTREE_T *letters;
  RBNODE_T *node;
  
  regcomp_or_die("bg freq", &bgfreq, BGFREQ_RE, REG_EXTENDED);
  letters = rbtree_create(rbtree_strcasecmp, rbtree_strcpy, free, rbtree_dblcpy, free);
  line = str_create(100);
  if (!(fp = fopen(bg_filename, "r"))) {
    die("Unable to open background file \"%s\" for reading.\n", bg_filename);
  }
  
  terminate = feof(fp);
  while (!terminate) {
    size = fread(chunk, sizeof(char), BG_CHUNK_SIZE, fp);
    chunk[size] = '\0';
    terminate = feof(fp);
    offset = 0;
    while (offset < size) {
      // skip mac newline
      if (str_len(line) == 0 && chunk[offset] == '\r') {
        offset++;
        continue;
      }
      // find next new line
      for (i = offset; i < size; ++i) {
        if (chunk[i] == '\n') break;
      }
      // append portion up to the new line or end of chunk
      str_append(line, chunk+offset, i - offset);
      // read more if we didn't find a new line
      if (i == size && !terminate) break;
      // move the offset past the new line
      offset = i + 1;
      // handle windows new line
      if (str_char(line, -1) == '\r') str_truncate(line, -1);
      // remove everything to the right of a comment character
      for (i = 0; i < str_len(line); ++i) {
        if (str_char(line, i) == '#') {
          str_truncate(line, i);
          break;
        }
      }
      // check the line for a single letter followed by a number
      if (regexec_or_die("bg freq", &bgfreq, str_internal(line), 4, matches, 0)) {
        // parse the letter and frequency value
        regex_strncpy(matches+1, str_internal(line), letter, 2);
        freq = regex_dbl(matches+2, str_internal(line));
        // check the frequency is acceptable
        if (freq < 0 || freq > 1) {
          die("The background file lists the illegal probability %g for "
            "the letter %s.\n", freq, letter);
        } else if (freq == 0) {
          die("The background file lists a probability of zero for the "
            "letter %s\n", letter);
        }
        if (freq >= 0 && freq <= 1) rbtree_put(letters, letter, &freq);
      }
      str_clear(line);
    }
  }
  // finished with the file so clean up file parsing stuff
  fclose(fp);
  str_destroy(line, FALSE);
  regfree(&bgfreq);
  // guess the alphabet
  if (*alph == INVALID_ALPH) {
    switch (rbtree_size(letters)) {
      case PROTEIN_ASIZE:
        *alph = PROTEIN_ALPH;
        break;
      case DNA_ASIZE:
        *alph = DNA_ALPH;
        break;
      default:
        die("Number of single character entries in background does not match "
            "an alphabet.\n");
    }
  }
  // make the background
  if (freqs == NULL) freqs = allocate_array(alph_size(*alph, ALL_SIZE));
  assert(get_array_length(freqs) >= alph_size(*alph, ALL_SIZE));
  init_array(-1, freqs);
  for (node = rbtree_first(letters); node != NULL; node = rbtree_next(node)) {
    key = (char*)rbtree_key(node);
    i = alph_index(*alph, key[0]);
    freq = *((double*)rbtree_value(node));
    if (i == -1) {
      die("Background contains letter %s which is not in the %s alphabet.\n", 
          key, alph_name(*alph));
    }
    if (get_array_item(i, freqs) != -1) {
      die("Background contains letter %s which has the same meaning as an "
          "already listed letter.\n", key);
    }
    set_array_item(i, freq, freqs);
  }
  // check that all items were set
  for (i = 0; i < alph_size(*alph, ALPH_SIZE); i++) {
    if (get_array_item(i, freqs) == -1) {
      die("Background is missing letter %c.\n", alph_char(*alph, i));
    }
  }
  // disabled for backwards compatability (AMA test was failing)
  //normalize_subarray(0, ALPH_ASIZE[*alph], 0.0, freqs);
  // calculate the values of the ambiguous letters from the concrete ones
  calc_ambigs(*alph, FALSE, freqs);
  // cleanup
  rbtree_destroy(letters);
  // return result
  return freqs;
}
Beispiel #12
0
int mbox_sync_try_rewrite(struct mbox_sync_mail_context *ctx, off_t move_diff)
{
        struct mbox_sync_context *sync_ctx = ctx->sync_ctx;
	size_t old_hdr_size, new_hdr_size;

	i_assert(sync_ctx->mbox->mbox_lock_type == F_WRLCK);

	old_hdr_size = ctx->body_offset - ctx->hdr_offset;
	new_hdr_size = str_len(ctx->header);

	if (new_hdr_size <= old_hdr_size) {
		/* add space. note that we must call add_space() even if we're
		   not adding anything so mail.offset gets fixed. */
		mbox_sync_headers_add_space(ctx, old_hdr_size - new_hdr_size);
	} else if (new_hdr_size > old_hdr_size) {
		/* try removing the space where we can */
		mbox_sync_headers_remove_space(ctx,
					       new_hdr_size - old_hdr_size);
		new_hdr_size = str_len(ctx->header);

		if (new_hdr_size <= old_hdr_size) {
			/* good, we removed enough. */
			i_assert(new_hdr_size == old_hdr_size);
		} else if (move_diff < 0 &&
			   new_hdr_size - old_hdr_size <= (uoff_t)-move_diff) {
			/* moving backwards - we can use the extra space from
			   it, just update expunged_space accordingly */
			i_assert(ctx->mail.space == 0);
			i_assert(sync_ctx->expunged_space >=
				 (off_t)(new_hdr_size - old_hdr_size));
			sync_ctx->expunged_space -= new_hdr_size - old_hdr_size;
		} else {
			/* couldn't get enough space */
			i_assert(ctx->mail.space == 0);
			ctx->mail.space =
				-(ssize_t)(new_hdr_size - old_hdr_size);
			return 0;
		}
	}

	i_assert(ctx->mail.space >= 0);

	if (ctx->header_first_change == (size_t)-1 && move_diff == 0) {
		/* no changes actually. we get here if index sync record told
		   us to do something that was already there */
		return 1;
	}

	if (move_diff != 0) {
		/* forget about partial write optimizations */
		ctx->header_first_change = 0;
		ctx->header_last_change = 0;
	}

	if (ctx->header_last_change != (size_t)-1 &&
	    ctx->header_last_change != 0)
		str_truncate(ctx->header, ctx->header_last_change);

	if (pwrite_full(sync_ctx->write_fd,
			str_data(ctx->header) + ctx->header_first_change,
			str_len(ctx->header) - ctx->header_first_change,
			ctx->hdr_offset + ctx->header_first_change +
			move_diff) < 0) {
		mbox_set_syscall_error(sync_ctx->mbox, "pwrite_full()");
		return -1;
	}

	if (sync_ctx->dest_first_mail &&
	    (ctx->imapbase_updated || ctx->sync_ctx->base_uid_last != 0)) {
		/* the position might have moved as a result of moving
		   whitespace */
		mbox_sync_first_mail_written(ctx, ctx->hdr_offset + move_diff);
	}

	mbox_sync_file_updated(sync_ctx, FALSE);
	return 1;
}
int mail_send_rejection(struct mail_deliver_context *ctx, const char *recipient,
			const char *reason)
{
    struct mail *mail = ctx->src_mail;
    struct istream *input;
    struct smtp_client *smtp_client;
    struct ostream *output;
    const char *return_addr, *hdr;
    const char *value, *msgid, *orig_msgid, *boundary;
    string_t *str;
    int ret;

    if (mail_get_first_header(mail, "Message-ID", &orig_msgid) < 0)
	    orig_msgid = NULL;

    if (mail_get_first_header(mail, "Auto-Submitted", &value) > 0 &&
	strcasecmp(value, "no") != 0) {
	    i_info("msgid=%s: Auto-submitted message discarded: %s",
		   orig_msgid == NULL ? "" : str_sanitize(orig_msgid, 80),
		   str_sanitize(reason, 512));
	    return 0;
    }

    return_addr = mail_deliver_get_return_address(ctx);
    if (return_addr == NULL) {
	    i_info("msgid=%s: Return-Path missing, rejection reason: %s",
		   orig_msgid == NULL ? "" : str_sanitize(orig_msgid, 80),
		   str_sanitize(reason, 512));
	    return 0;
    }

    if (mailbox_get_settings(mail->box)->mail_debug) {
	    i_debug("Sending a rejection to %s: %s", recipient,
		    str_sanitize(reason, 512));
    }

    smtp_client = smtp_client_open(ctx->set, return_addr, NULL, &output);

    msgid = mail_deliver_get_new_message_id(ctx);
    boundary = t_strdup_printf("%s/%s", my_pid, ctx->set->hostname);

    str = t_str_new(512);
    str_printfa(str, "Message-ID: %s\r\n", msgid);
    str_printfa(str, "Date: %s\r\n", message_date_create(ioloop_time));
    str_printfa(str, "From: Mail Delivery Subsystem <%s>\r\n",
		ctx->set->postmaster_address);
    str_printfa(str, "To: <%s>\r\n", return_addr);
    str_append(str, "MIME-Version: 1.0\r\n");
    str_printfa(str, "Content-Type: "
		"multipart/report; report-type=%s;\r\n"
		"\tboundary=\"%s\"\r\n",
		ctx->dsn ? "delivery-status" : "disposition-notification",
		boundary);
    str_append(str, "Subject: ");
    var_expand(str, ctx->set->rejection_subject,
	       get_var_expand_table(mail, reason, recipient));
    str_append(str, "\r\n");

    str_append(str, "Auto-Submitted: auto-replied (rejected)\r\n");
    str_append(str, "Precedence: bulk\r\n");
    str_append(str, "\r\nThis is a MIME-encapsulated message\r\n\r\n");

    /* human readable status report */
    str_printfa(str, "--%s\r\n", boundary);
    str_append(str, "Content-Type: text/plain; charset=utf-8\r\n");
    str_append(str, "Content-Disposition: inline\r\n");
    str_append(str, "Content-Transfer-Encoding: 8bit\r\n\r\n");

    var_expand(str, ctx->set->rejection_reason,
	       get_var_expand_table(mail, reason, recipient));
    str_append(str, "\r\n");

    if (ctx->dsn) {
	    /* DSN status report: For LDA rejects. currently only used when
	       user is out of quota */
	    str_printfa(str, "--%s\r\n"
			"Content-Type: message/delivery-status\r\n\r\n",
			boundary);
	    str_printfa(str, "Reporting-MTA: dns; %s\r\n", ctx->set->hostname);
	    if (mail_get_first_header(mail, "Original-Recipient", &hdr) > 0)
		    str_printfa(str, "Original-Recipient: rfc822; %s\r\n", hdr);
	    str_printfa(str, "Final-Recipient: rfc822; %s\r\n", recipient);
	    str_append(str, "Action: failed\r\n");
	    str_printfa(str, "Status: %s\r\n", ctx->mailbox_full ? "5.2.2" : "5.2.0");
    } else {
	    /* MDN status report: For Sieve "reject" */
	    str_printfa(str, "--%s\r\n"
			"Content-Type: message/disposition-notification\r\n\r\n",
			boundary);
	    str_printfa(str, "Reporting-UA: %s; Dovecot Mail Delivery Agent\r\n",
			ctx->set->hostname);
	    if (mail_get_first_header(mail, "Original-Recipient", &hdr) > 0)
		    str_printfa(str, "Original-Recipient: rfc822; %s\r\n", hdr);
	    str_printfa(str, "Final-Recipient: rfc822; %s\r\n", recipient);

	    if (orig_msgid != NULL)
		    str_printfa(str, "Original-Message-ID: %s\r\n", orig_msgid);
	    str_append(str, "Disposition: "
		       "automatic-action/MDN-sent-automatically; deleted\r\n");
    }
    str_append(str, "\r\n");

    /* original message's headers */
    str_printfa(str, "--%s\r\nContent-Type: message/rfc822\r\n\r\n", boundary);
    o_stream_nsend(output, str_data(str), str_len(str));

    if (mail_get_hdr_stream(mail, NULL, &input) == 0) {
	    /* Note: If you add more headers, they need to be sorted.
	       We'll drop Content-Type because we're not including the message
	       body, and having a multipart Content-Type may confuse some
	       MIME parsers when they don't see the message boundaries. */
	    static const char *const exclude_headers[] = {
		    "Content-Type"
	    };

	    input = i_stream_create_header_filter(input,
	    		HEADER_FILTER_EXCLUDE | HEADER_FILTER_NO_CR |
			HEADER_FILTER_HIDE_BODY, exclude_headers,
			N_ELEMENTS(exclude_headers),
			*null_header_filter_callback, (void *)NULL);

	    ret = o_stream_send_istream(output, input);
	    i_stream_unref(&input);

	    i_assert(ret != 0);
    }

    str_truncate(str, 0);
    str_printfa(str, "\r\n\r\n--%s--\r\n", boundary);
    o_stream_nsend(output, str_data(str), str_len(str));
    return smtp_client_close(smtp_client);
}
Beispiel #14
0
static int uri_parse_host(struct uri_parser *parser, struct uri_authority *auth)
{
	const unsigned char *preserve;
	struct in_addr ip4;
	struct in6_addr ip6;
	string_t *literal = NULL;
	int ret;

	/* RFC 3986:
	 *
	 * host          = IP-literal / IPv4address / reg-name
	 */

	literal = uri_parser_get_tmpbuf(parser, 256);

	/* IP-literal / */
	if (parser->cur < parser->end && *parser->cur == '[') {
#ifdef HAVE_IPV6
		if ((ret=uri_parse_ip_literal(parser, literal, &ip6)) <= 0)
			return -1;

		if (auth != NULL) {
			auth->host_literal = t_strdup(str_c(literal));
			auth->host_ip.family = AF_INET6;
			auth->host_ip.u.ip6 = ip6;
			auth->have_host_ip = TRUE;
		}
		return 1;
#else
		parser->error = "IPv6 host address is not supported";
		return -1;
#endif
	}

	/* IPv4address /
	 *
	 * If it fails to parse, we try to parse it as a reg-name
	 */
	preserve = parser->cur;
	if ((ret = uri_parse_ipv4address(parser, literal, &ip4)) > 0) {
		if (auth != NULL) {
			auth->host_literal = t_strdup(str_c(literal));
			auth->host_ip.family = AF_INET;
			auth->host_ip.u.ip4 = ip4;
			auth->have_host_ip = TRUE;
		}
		return ret;
	}
	parser->cur = preserve;
	str_truncate(literal, 0);

	/* reg-name */
	if ((ret = uri_parse_reg_name(parser, literal)) != 0) {
		if (ret > 0 && auth != NULL) {
			auth->host_literal = t_strdup(str_c(literal));
			auth->have_host_ip = FALSE;
		}
		return ret;
	}
	return 0;
}
Beispiel #15
0
int imap_proxy_parse_line(struct client *client, const char *line)
{
	struct imap_client *imap_client = (struct imap_client *)client;
	struct ostream *output;
	string_t *str;
	const unsigned char *data;
	unsigned int data_len;
	const char *error;
	int ret;

	i_assert(!client->destroyed);

	output = login_proxy_get_ostream(client->login_proxy);
	if (!imap_client->proxy_seen_banner) {
		/* this is a banner */
		client->proxy_state = IMAP_PROXY_STATE_BANNER;
		imap_client->proxy_seen_banner = TRUE;
		if (proxy_input_banner(imap_client, output, line) < 0) {
			client_proxy_failed(client, TRUE);
			return -1;
		}
		return 0;
	} else if (*line == '+') {
		/* AUTHENTICATE started. finish it. */
		if (client->proxy_sasl_client == NULL) {
			/* used literals with LOGIN command, just ignore. */
			return 0;
		}
		client->proxy_state = IMAP_PROXY_STATE_AUTH_CONTINUE;

		str = t_str_new(128);
		if (line[1] != ' ' ||
		    base64_decode(line+2, strlen(line+2), NULL, str) < 0) {
			client_log_err(client,
				"proxy: Server sent invalid base64 data in AUTHENTICATE response");
			client_proxy_failed(client, TRUE);
			return -1;
		}
		ret = dsasl_client_input(client->proxy_sasl_client,
					 str_data(str), str_len(str), &error);
		if (ret == 0) {
			ret = dsasl_client_output(client->proxy_sasl_client,
						  &data, &data_len, &error);
		}
		if (ret < 0) {
			client_log_err(client, t_strdup_printf(
				"proxy: Server sent invalid authentication data: %s",
				error));
			client_proxy_failed(client, TRUE);
			return -1;
		}
		i_assert(ret == 0);

		str_truncate(str, 0);
		base64_encode(data, data_len, str);
		str_append(str, "\r\n");

		o_stream_nsend(output, str_data(str), str_len(str));
		return 0;
	} else if (strncmp(line, "S ", 2) == 0) {
		if (strncmp(line, "S OK ", 5) != 0) {
			/* STARTTLS failed */
			client_log_err(client, t_strdup_printf(
				"proxy: Remote STARTTLS failed: %s",
				str_sanitize(line + 5, 160)));
			client_proxy_failed(client, TRUE);
			return -1;
		}
		/* STARTTLS successful, begin TLS negotiation. */
		client->proxy_state = IMAP_PROXY_STATE_STARTTLS;
		if (login_proxy_starttls(client->login_proxy) < 0) {
			client_proxy_failed(client, TRUE);
			return -1;
		}
		/* i/ostreams changed. */
		output = login_proxy_get_ostream(client->login_proxy);
		str = t_str_new(128);
		if (proxy_write_login(imap_client, str) < 0) {
			client_proxy_failed(client, TRUE);
			return -1;
		}
		o_stream_nsend(output, str_data(str), str_len(str));
		return 1;
	} else if (strncmp(line, "L OK ", 5) == 0) {
		/* Login successful. Send this line to client. */
		client->proxy_state = IMAP_PROXY_STATE_LOGIN;
		str = t_str_new(128);
		client_send_login_reply(imap_client, str, line + 5);
		o_stream_nsend(client->output, str_data(str), str_len(str));

		(void)client_skip_line(imap_client);
		client_proxy_finish_destroy_client(client);
		return 1;
	} else if (strncmp(line, "L ", 2) == 0) {
		line += 2;
		if (client->set->auth_verbose) {
			const char *log_line = line;

			if (strncasecmp(log_line, "NO ", 3) == 0)
				log_line += 3;
			client_proxy_log_failure(client, log_line);
		}
#define STR_NO_IMAP_RESP_CODE_AUTHFAILED "NO ["IMAP_RESP_CODE_AUTHFAILED"]"
		if (strncmp(line, STR_NO_IMAP_RESP_CODE_AUTHFAILED,
			    strlen(STR_NO_IMAP_RESP_CODE_AUTHFAILED)) == 0) {
			/* the remote sent a generic "authentication failed"
			   error. replace it with our one, so that in case
			   the remote is sending a different error message
			   an attacker can't find out what users exist in
			   the system. */
			client_send_reply_code(client, IMAP_CMD_REPLY_NO,
					       IMAP_RESP_CODE_AUTHFAILED,
					       AUTH_FAILED_MSG);
		} else if (strncmp(line, "NO [", 4) == 0) {
			/* remote sent some other resp-code. forward it. */
			client_send_raw(client, t_strconcat(
				imap_client->cmd_tag, " ", line, "\r\n", NULL));
		} else {
			/* there was no [resp-code], so remote isn't Dovecot
			   v1.2+. we could either forward the line as-is and
			   leak information about what users exist in this
			   system, or we could hide other errors than password
			   failures. since other errors are pretty rare,
			   it's safer to just hide them. they're still
			   available in logs though. */
			client_send_reply_code(client, IMAP_CMD_REPLY_NO,
					       IMAP_RESP_CODE_AUTHFAILED,
					       AUTH_FAILED_MSG);
		}

		client->proxy_auth_failed = TRUE;
		client_proxy_failed(client, FALSE);
		return -1;
	} else if (strncasecmp(line, "* CAPABILITY ", 13) == 0) {
		i_free(imap_client->proxy_backend_capability);
		imap_client->proxy_backend_capability = i_strdup(line + 13);
		return 0;
	} else if (strncmp(line, "C ", 2) == 0) {
		/* Reply to CAPABILITY command we sent */
		client->proxy_state = IMAP_PROXY_STATE_CAPABILITY;
		if (strncmp(line, "C OK ", 5) == 0 &&
		    client->proxy_password != NULL) {
			/* pipelining was disabled, send the login now. */
			str = t_str_new(128);
			if (proxy_write_login(imap_client, str) < 0)
				return -1;
			o_stream_nsend(output, str_data(str), str_len(str));
			return 1;
		}
		return 0;
	} else if (strncasecmp(line, "I ", 2) == 0) {
		/* Reply to ID command we sent, ignore it unless
		   pipelining is disabled, in which case send
		   either STARTTLS or login */
		client->proxy_state = IMAP_PROXY_STATE_ID;

		if (client->proxy_nopipelining) {
			str = t_str_new(128);
			if ((ret = proxy_write_starttls(imap_client, str)) < 0) {
				return -1;
			} else if (ret == 0) {
				if (proxy_write_login(imap_client, str) < 0)
					return -1;
			}
			o_stream_nsend(output, str_data(str), str_len(str));
			return 1;
		}
		return 0;
	} else if (strncasecmp(line, "* ID ", 5) == 0) {
		/* Reply to ID command we sent, ignore it */
		return 0;
	} else if (strncmp(line, "* ", 2) == 0) {
		/* untagged reply. just forward it. */
		client_send_raw(client, t_strconcat(line, "\r\n", NULL));
		return 0;
	} else {
		/* tagged reply, shouldn't happen. */
		client_log_err(client, t_strdup_printf(
			"proxy: Unexpected input, ignoring: %s",
			str_sanitize(line, 160)));
		return 0;
	}
}
Beispiel #16
0
/* This parser is rather liberal in what it accepts:
   The IMAP standard mandates seperate character sets for tags, commands,
   unquoted strings.  Since they all must be seperated by spaces, this
   parser allows all non-whitespace characters for each of the above. */
static int parse_line(void)
{
#define RESPOND(T,S) do{ respond(T,S); return 0; }while(0)
  unsigned i;
  unsigned len;
  const char* ptr;
  str* arg;
  
  /* Parse out the command tag */
  str_truncate(&tag, 0);
  for (i = 0, ptr = line.s; i < line.len && isspace(*ptr); ++i, ++ptr) ;
  for (; i < line.len && !isspace(*ptr); ++i, ++ptr)
    str_catc(&tag, line.s[i]);
  if (!tag.len) RESPOND(NOTAG, "BAD Syntax error");
  if (i >= line.len) RESPOND(0, "BAD Syntax error");

  /* Parse out the command itself */
  str_truncate(&cmd, 0);
  for (; i < line.len && isspace(*ptr); ++i, ++ptr) ;
  for (; i < line.len && !isspace(*ptr); ++i, ++ptr)
    str_catc(&cmd, line.s[i]);
  if (!cmd.len) RESPOND(0, "BAD Syntax error");

  /* Parse out the command-line args */
  for (line_argc = 0; line_argc < MAX_ARGC; ++line_argc) {
    arg = &line_args[line_argc];
    str_truncate(arg, 0);
    for (; i < line.len && isspace(*ptr); ++i, ++ptr) ;
    if (i >= line.len) break;

    switch (*ptr) {
    case LBRACE:
      /* Handle a string literal */
      ++i, ++ptr;
      if (!isdigit(*ptr)) RESPOND(0, "BAD Syntax error: missing integer");
      for (len = 0; i < line.len && *ptr != RBRACE; ++i, ++ptr) {
	if (!isdigit(*ptr))
	  RESPOND(0, "BAD Syntax error: invalid integer");
	len = len * 10 + *ptr - '0';
      }
      ++i, ++ptr;
      if (*ptr != 0) RESPOND(0, "BAD Syntax error: missing LF after integer");
      str_ready(arg, len);
      respond(CONT, "OK");
      if (len > 0)
	ibuf_read(&inbuf, arg->s, len);
      arg->s[arg->len = len] = 0;
      ibuf_getstr_crlf(&inbuf, &line);
      i = 0;
      ptr = line.s;
      break;

    case QUOTE:
      /* Handle a quoted string */
      for (++i, ++ptr; i < line.len && *ptr != QUOTE; ++i, ++ptr) {
	if (*ptr == ESCAPE) {
	  if (++i >= line.len) break;
	  ++ptr;
	}
	str_catc(arg, *ptr);
      }
      if (i >= line.len || *ptr != QUOTE)
	RESPOND(0, "BAD Syntax error: unterminated quoted string");
      ++i, ++ptr;
      break;

    default:
      /* Normal case is very simple */
      for (; i < line.len && !isspace(*ptr); ++i, ++ptr)
	str_catc(arg, *ptr);
    }
  }
  for (; i < line.len && isspace(*ptr); ++i, ++ptr) ;
  if (i < line.len) RESPOND(0, "BAD Too many command arguments");
  return 1;
}
bool mail_search_arg_to_imap(string_t *dest, const struct mail_search_arg *arg,
			     const char **error_r)
{
	unsigned int start_pos;

	if (arg->match_not)
		str_append(dest, "NOT ");
	start_pos = str_len(dest);
	switch (arg->type) {
	case SEARCH_OR:
		if (!mail_search_subargs_to_imap(dest, arg->value.subargs,
						 "OR ", error_r))
			return FALSE;
		break;
	case SEARCH_SUB:
		if (!mail_search_subargs_to_imap(dest, arg->value.subargs,
						 "", error_r))
			return FALSE;
		break;
	case SEARCH_ALL:
		str_append(dest, "ALL");
		break;
	case SEARCH_SEQSET:
		imap_write_seq_range(dest, &arg->value.seqset);
		break;
	case SEARCH_UIDSET:
		str_append(dest, "UID ");
		imap_write_seq_range(dest, &arg->value.seqset);
		break;
	case SEARCH_FLAGS:
		i_assert((arg->value.flags & MAIL_FLAGS_MASK) != 0);
		str_append_c(dest, '(');
		if ((arg->value.flags & MAIL_ANSWERED) != 0)
			str_append(dest, "ANSWERED ");
		if ((arg->value.flags & MAIL_FLAGGED) != 0)
			str_append(dest, "FLAGGED ");
		if ((arg->value.flags & MAIL_DELETED) != 0)
			str_append(dest, "DELETED ");
		if ((arg->value.flags & MAIL_SEEN) != 0)
			str_append(dest, "SEEN ");
		if ((arg->value.flags & MAIL_DRAFT) != 0)
			str_append(dest, "DRAFT ");
		if ((arg->value.flags & MAIL_RECENT) != 0)
			str_append(dest, "RECENT ");
		str_truncate(dest, str_len(dest)-1);
		str_append_c(dest, ')');
		break;
	case SEARCH_KEYWORDS: {
		const struct mail_keywords *kw = arg->value.keywords;
		const ARRAY_TYPE(keywords) *names_arr;
		const char *const *namep;
		unsigned int i;

		if (kw == NULL) {
			/* uninitialized */
			str_printfa(dest, "KEYWORD %s", arg->value.str);
			break;
		}

		names_arr = mail_index_get_keywords(kw->index);

		str_append_c(dest, '(');
		for (i = 0; i < kw->count; i++) {
			namep = array_idx(names_arr, kw->idx[i]);
			if (i > 0)
				str_append_c(dest, ' ');
			str_printfa(dest, "KEYWORD %s", *namep);
		}
		str_append_c(dest, ')');
		break;
	}

	case SEARCH_BEFORE:
		switch (arg->value.date_type) {
		case MAIL_SEARCH_DATE_TYPE_SENT:
			str_append(dest, "SENTBEFORE");
			break;
		case MAIL_SEARCH_DATE_TYPE_RECEIVED:
			str_append(dest, "BEFORE");
			break;
		case MAIL_SEARCH_DATE_TYPE_SAVED:
			str_append(dest, "X-SAVEDBEFORE");
			break;
		}
		if (mail_search_arg_to_imap_date(dest, arg))
			;
		else if (arg->value.date_type != MAIL_SEARCH_DATE_TYPE_RECEIVED ||
			 arg->value.time > ioloop_time) {
			*error_r = t_strdup_printf(
				"SEARCH_BEFORE can't be written as IMAP for timestamp %ld (type=%d, use_tz=%d)",
				(long)arg->value.time, arg->value.date_type,
				(arg->value.search_flags & MAIL_SEARCH_ARG_FLAG_USE_TZ) != 0);
			return FALSE;
		} else {
			str_truncate(dest, start_pos);
			str_printfa(dest, "OLDER %u",
				    (unsigned int)(ioloop_time - arg->value.time + 1));
		}
		break;
	case SEARCH_ON:
		switch (arg->value.date_type) {
		case MAIL_SEARCH_DATE_TYPE_SENT:
			str_append(dest, "SENTON");
			break;
		case MAIL_SEARCH_DATE_TYPE_RECEIVED:
			str_append(dest, "ON");
			break;
		case MAIL_SEARCH_DATE_TYPE_SAVED:
			str_append(dest, "X-SAVEDON");
			break;
		}
		if (!mail_search_arg_to_imap_date(dest, arg)) {
			*error_r = t_strdup_printf(
				"SEARCH_ON can't be written as IMAP for timestamp %ld (type=%d, use_tz=%d)",
				(long)arg->value.time, arg->value.date_type,
				(arg->value.search_flags & MAIL_SEARCH_ARG_FLAG_USE_TZ) != 0);
			return FALSE;
		}
		break;
	case SEARCH_SINCE:
		switch (arg->value.date_type) {
		case MAIL_SEARCH_DATE_TYPE_SENT:
			str_append(dest, "SENTSINCE");
			break;
		case MAIL_SEARCH_DATE_TYPE_RECEIVED:
			str_append(dest, "SINCE");
			break;
		case MAIL_SEARCH_DATE_TYPE_SAVED:
			str_append(dest, "X-SAVEDSINCE");
			break;
		}
		if (mail_search_arg_to_imap_date(dest, arg))
			;
		else if (arg->value.date_type != MAIL_SEARCH_DATE_TYPE_RECEIVED ||
			 arg->value.time >= ioloop_time) {
			*error_r = t_strdup_printf(
				"SEARCH_SINCE can't be written as IMAP for timestamp %ld (type=%d, use_tz=%d)",
				(long)arg->value.time, arg->value.date_type,
				(arg->value.search_flags & MAIL_SEARCH_ARG_FLAG_USE_TZ) != 0);
			return FALSE;
		} else {
			str_truncate(dest, start_pos);
			str_printfa(dest, "YOUNGER %u",
				    (unsigned int)(ioloop_time - arg->value.time));
		}
		break;
	case SEARCH_SMALLER:
		str_printfa(dest, "SMALLER %llu", (unsigned long long)arg->value.size);
		break;
	case SEARCH_LARGER:
		str_printfa(dest, "LARGER %llu", (unsigned long long)arg->value.size);
		break;
	case SEARCH_HEADER:
	case SEARCH_HEADER_ADDRESS:
	case SEARCH_HEADER_COMPRESS_LWSP:
		if (strcasecmp(arg->hdr_field_name, "From") == 0 ||
		    strcasecmp(arg->hdr_field_name, "To") == 0 ||
		    strcasecmp(arg->hdr_field_name, "Cc") == 0 ||
		    strcasecmp(arg->hdr_field_name, "Bcc") == 0 ||
		    strcasecmp(arg->hdr_field_name, "Subject") == 0)
			str_append(dest, t_str_ucase(arg->hdr_field_name));
		else {
			str_append(dest, "HEADER ");
			imap_append_astring(dest, arg->hdr_field_name);
		}
		str_append_c(dest, ' ');
		imap_append_astring(dest, arg->value.str);
		break;

	case SEARCH_BODY:
		str_append(dest, "BODY ");
		imap_append_astring(dest, arg->value.str);
		break;
	case SEARCH_TEXT:
		str_append(dest, "TEXT ");
		imap_append_astring(dest, arg->value.str);
		break;

	/* extensions */
	case SEARCH_MODSEQ: {
		bool extended_output = FALSE;

		str_append(dest, "MODSEQ ");
		if (arg->value.str != NULL) {
			str_printfa(dest, "/flags/%s", arg->value.str);
			extended_output = TRUE;
		} else if (arg->value.flags != 0) {
			str_append(dest, "/flags/");
			imap_write_flags(dest, arg->value.flags, NULL);
			extended_output = TRUE;
		}
		if (extended_output) {
			str_append_c(dest, ' ');
			switch (arg->value.modseq->type) {
			case MAIL_SEARCH_MODSEQ_TYPE_ANY:
				str_append(dest, "all");
				break;
			case MAIL_SEARCH_MODSEQ_TYPE_PRIVATE:
				str_append(dest, "priv");
				break;
			case MAIL_SEARCH_MODSEQ_TYPE_SHARED:
				str_append(dest, "shared");
				break;
			}
			str_append_c(dest, ' ');
		}
		str_printfa(dest, "%llu", (unsigned long long)arg->value.modseq->modseq);
		break;
	}
	case SEARCH_INTHREAD:
		str_append(dest, "INTHREAD ");
		imap_append_astring(dest, mail_thread_type_to_str(arg->value.thread_type));
		str_append_c(dest, ' ');
		if (!mail_search_subargs_to_imap(dest, arg->value.subargs,
						 "", error_r))
			return FALSE;
		break;
	case SEARCH_GUID:
		str_append(dest, "X-GUID ");
		imap_append_astring(dest, arg->value.str);
		break;
	case SEARCH_MAILBOX:
		*error_r = "SEARCH_MAILBOX can't be written as IMAP";
		return FALSE;
	case SEARCH_MAILBOX_GUID:
		*error_r = "SEARCH_MAILBOX_GUID can't be written as IMAP";
		return FALSE;
	case SEARCH_MAILBOX_GLOB:
		str_append(dest, "X-MAILBOX ");
		imap_append_astring(dest, arg->value.str);
		break;
	case SEARCH_REAL_UID:
		str_append(dest, "X-REAL-UID ");
		imap_write_seq_range(dest, &arg->value.seqset);
		break;
	}
	return TRUE;
}
Beispiel #18
0
static bool cmd_copy_full(struct client_command_context *cmd, bool move)
{
	struct client *client = cmd->client;
	struct mail_storage *dest_storage;
	struct mailbox *destbox;
	struct mailbox_transaction_context *t, *src_trans;
        struct mail_search_args *search_args;
	const char *messageset, *mailbox, *src_uidset;
	enum mailbox_sync_flags sync_flags = 0;
	enum imap_sync_flags imap_flags = 0;
	struct mail_transaction_commit_changes changes;
	unsigned int copy_count;
	string_t *msg;
	int ret;

	/* <message set> <mailbox> */
	if (!client_read_string_args(cmd, 2, &messageset, &mailbox))
		return FALSE;

	if (!client_verify_open_mailbox(cmd))
		return TRUE;

	ret = imap_search_get_seqset(cmd, messageset, cmd->uid, &search_args);
	if (ret <= 0)
		return ret < 0;

	if (client_open_save_dest_box(cmd, mailbox, &destbox) < 0) {
		mail_search_args_unref(&search_args);
		return TRUE;
	}

	t = mailbox_transaction_begin(destbox,
				      MAILBOX_TRANSACTION_FLAG_EXTERNAL |
				      MAILBOX_TRANSACTION_FLAG_ASSIGN_UIDS);
	ret = fetch_and_copy(client, move, t, &src_trans, search_args,
			     &src_uidset, &copy_count);
	mail_search_args_unref(&search_args);

	msg = t_str_new(256);
	if (ret <= 0)
		mailbox_transaction_rollback(&t);
	else if (mailbox_transaction_commit_get_changes(&t, &changes) < 0)
		ret = -1;
	else if (copy_count == 0) {
		str_append(msg, "OK No messages found.");
		pool_unref(&changes.pool);
	} else if (seq_range_count(&changes.saved_uids) == 0 ||
		   changes.no_read_perm) {
		/* not supported by backend (virtual) or no read permissions
		   for mailbox */
		str_append(msg, move ? "OK Move completed." :
			   "OK Copy completed.");
		pool_unref(&changes.pool);
	} else if (move) {
		i_assert(copy_count == seq_range_count(&changes.saved_uids));

		str_printfa(msg, "* OK [COPYUID %u %s ",
			    changes.uid_validity, src_uidset);
		imap_write_seq_range(msg, &changes.saved_uids);
		str_append(msg, "] Moved UIDs.");
		client_send_line(client, str_c(msg));

		str_truncate(msg, 0);
		str_append(msg, "OK Move completed.");
		pool_unref(&changes.pool);
	} else {
		i_assert(copy_count == seq_range_count(&changes.saved_uids));

		str_printfa(msg, "OK [COPYUID %u %s ", changes.uid_validity,
			    src_uidset);
		imap_write_seq_range(msg, &changes.saved_uids);
		str_append(msg, "] Copy completed.");
		pool_unref(&changes.pool);
	}

	if (ret <= 0 && move) {
		/* move failed, don't expunge anything */
		mailbox_transaction_rollback(&src_trans);
	} else {
		if (mailbox_transaction_commit(&src_trans) < 0)
			ret = -1;
	}

 	dest_storage = mailbox_get_storage(destbox);
	if (destbox != client->mailbox) {
		if (move)
			sync_flags |= MAILBOX_SYNC_FLAG_EXPUNGE;
		else
			sync_flags |= MAILBOX_SYNC_FLAG_FAST;
		imap_flags |= IMAP_SYNC_FLAG_SAFE;
		mailbox_free(&destbox);
	} else if (move) {
		sync_flags |= MAILBOX_SYNC_FLAG_EXPUNGE;
		imap_flags |= IMAP_SYNC_FLAG_SAFE;
	}

	if (ret > 0)
		return cmd_sync(cmd, sync_flags, imap_flags, str_c(msg));
	else if (ret == 0) {
		/* some messages were expunged, sync them */
		return cmd_sync(cmd, 0, 0,
			"NO ["IMAP_RESP_CODE_EXPUNGEISSUED"] "
			"Some of the requested messages no longer exist.");
	} else {
		client_send_storage_error(cmd, dest_storage);
		return TRUE;
	}
}
Beispiel #19
0
int index_mail_get_special(struct mail *_mail,
			   enum mail_fetch_field field, const char **value_r)
{
	struct index_mail *mail = (struct index_mail *)_mail;
	struct index_mail_data *data = &mail->data;
	struct mail_cache_field *cache_fields = mail->ibox->cache_fields;
	string_t *str;

	switch (field) {
	case MAIL_FETCH_IMAP_BODY: {
		unsigned int body_cache_field =
                        cache_fields[MAIL_CACHE_IMAP_BODY].idx;
		unsigned int bodystructure_cache_field =
                        cache_fields[MAIL_CACHE_IMAP_BODYSTRUCTURE].idx;

		if (data->body != NULL) {
			*value_r = data->body;
			return 0;
		}

		/* 1) use plain-7bit-ascii flag if it exists
		   2) get BODY if it exists
		   3) get it using BODYSTRUCTURE if it exists
		   4) parse body structure, and save BODY/BODYSTRUCTURE
		      depending on what we want cached */

		str = str_new(mail->data_pool, 128);
		if ((mail->data.cache_flags &
		     MAIL_CACHE_FLAG_TEXT_PLAIN_7BIT_ASCII) != 0 &&
		    get_cached_parts(mail)) {
			index_mail_get_plain_bodystructure(mail, str, FALSE);
			data->body = str_c(str);
		} else if (index_mail_cache_lookup_field(mail, str,
							 body_cache_field) > 0)
			data->body = str_c(str);
		else if (index_mail_cache_lookup_field(mail, str,
					bodystructure_cache_field) > 0) {
			data->bodystructure =
				p_strdup(mail->data_pool, str_c(str));
			str_truncate(str, 0);

			if (imap_body_parse_from_bodystructure(
						data->bodystructure, str))
				data->body = str_c(str);
			else {
				/* broken, continue.. */
				mail_set_cache_corrupted(_mail,
					MAIL_FETCH_IMAP_BODYSTRUCTURE);
			}
		}

		if (data->body == NULL) {
			str_free(&str);
			if (index_mail_parse_bodystructure(mail,
						MAIL_CACHE_IMAP_BODY) < 0)
				return -1;
		}
		i_assert(data->body != NULL);
		*value_r = data->body;
		return 0;
	}
	case MAIL_FETCH_IMAP_BODYSTRUCTURE: {
		unsigned int bodystructure_cache_field =
                        cache_fields[MAIL_CACHE_IMAP_BODYSTRUCTURE].idx;

		if (data->bodystructure != NULL) {
			*value_r = data->bodystructure;
			return 0;
		}

		str = str_new(mail->data_pool, 128);
		if ((mail->data.cache_flags &
		     MAIL_CACHE_FLAG_TEXT_PLAIN_7BIT_ASCII) != 0 &&
		    get_cached_parts(mail)) {
			index_mail_get_plain_bodystructure(mail, str, TRUE);
			data->bodystructure = str_c(str);
		} else if (index_mail_cache_lookup_field(mail, str,
					bodystructure_cache_field) > 0) {
			data->bodystructure = str_c(str);
		} else {
			str_free(&str);
			if (index_mail_parse_bodystructure(mail,
					MAIL_CACHE_IMAP_BODYSTRUCTURE) < 0)
				return -1;
		}
		i_assert(data->bodystructure != NULL);
		*value_r = data->bodystructure;
		return 0;
	}
	case MAIL_FETCH_IMAP_ENVELOPE:
		if (data->envelope == NULL) {
			if (index_mail_headers_get_envelope(mail) < 0)
				return -1;
		}
		*value_r = data->envelope;
		return 0;
	case MAIL_FETCH_FROM_ENVELOPE:
	case MAIL_FETCH_UIDL_FILE_NAME:
	case MAIL_FETCH_UIDL_BACKEND:
	case MAIL_FETCH_SEARCH_SCORE:
	case MAIL_FETCH_GUID:
	case MAIL_FETCH_HEADER_MD5:
		*value_r = "";
		return 0;
	case MAIL_FETCH_MAILBOX_NAME:
		*value_r = _mail->box->vname;
		return 0;
	default:
		i_unreached();
		return -1;
	}
}
static void
dsync_fix_mailbox_name(struct mail_namespace *ns, string_t *vname_str,
		       char alt_char)
{
	const char *old_vname;
	char *vname, list_sep = mailbox_list_get_hierarchy_sep(ns->list);
	guid_128_t guid;
	unsigned int i, start_pos;

	vname = str_c_modifiable(vname_str);
	if (strncmp(vname, ns->prefix, ns->prefix_len) == 0)
		start_pos = ns->prefix_len;
	else
		start_pos = 0;

	/* replace control chars */
	for (i = start_pos; vname[i] != '\0'; i++) {
		if ((unsigned char)vname[i] < ' ')
			vname[i] = alt_char;
	}
	/* make it valid UTF8 */
	if (!uni_utf8_str_is_valid(vname)) {
		old_vname = t_strdup(vname + start_pos);
		str_truncate(vname_str, start_pos);
		if (uni_utf8_get_valid_data((const void *)old_vname,
					    strlen(old_vname), vname_str))
			i_unreached();
		vname = str_c_modifiable(vname_str);
	}
	if (dsync_is_valid_name(ns, 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 (i = start_pos; vname[i] != '\0'; i++) {
			if (vname[i] == list_sep)
				vname[i] = alt_char;
		}
		if (dsync_is_valid_name(ns, vname))
			return;
	}
	/* 2) '/' characters aren't valid without listescape */
	if (mail_namespace_get_sep(ns) != '/' && list_sep != '/') {
		for (i = start_pos; vname[i] != '\0'; i++) {
			if (vname[i] == '/')
				vname[i] = alt_char;
		}
		if (dsync_is_valid_name(ns, vname))
			return;
	}
	/* 3) probably some reserved name (e.g. dbox-Mails) */
	str_insert(vname_str, ns->prefix_len, "_");
	if (dsync_is_valid_name(ns, str_c(vname_str)))
		return;

	/* 4) name is too long? just give up and generate a unique name */
	guid_128_generate(guid);
	str_truncate(vname_str, 0);
	str_append(vname_str, ns->prefix);
	str_append(vname_str, guid_128_to_string(guid));
	i_assert(dsync_is_valid_name(ns, str_c(vname_str)));
}
Beispiel #21
0
int mbox_sync_parse_next_mail(struct istream *input,
			      struct mbox_sync_mail_context *ctx)
{
	struct mbox_sync_context *sync_ctx = ctx->sync_ctx;
	struct message_header_parser_ctx *hdr_ctx;
	struct message_header_line *hdr;
	struct mbox_sync_header_func *func;
	struct mbox_md5_context *mbox_md5_ctx;
	size_t line_start_pos;
	int i, ret;

	ctx->hdr_offset = ctx->mail.offset;
	ctx->mail.flags = MAIL_RECENT; /* default to having recent flag */

        ctx->header_first_change = (size_t)-1;
	ctx->header_last_change = 0;

	for (i = 0; i < MBOX_HDR_COUNT; i++)
		ctx->hdr_pos[i] = (size_t)-1;

	ctx->content_length = (uoff_t)-1;
	str_truncate(ctx->header, 0);

        mbox_md5_ctx = ctx->sync_ctx->mbox->md5_v.init();

        line_start_pos = 0;
	hdr_ctx = message_parse_header_init(input, NULL, 0);
	while ((ret = message_parse_header_next(hdr_ctx, &hdr)) > 0) {
		if (hdr->eoh) {
			ctx->have_eoh = TRUE;
			break;
		}

		if (!hdr->continued) {
			line_start_pos = str_len(ctx->header);
			str_append(ctx->header, hdr->name);
			str_append_n(ctx->header, hdr->middle, hdr->middle_len);
		}

		func = bsearch(hdr->name, header_funcs,
			       N_ELEMENTS(header_funcs), sizeof(*header_funcs),
			       mbox_sync_bsearch_header_func_cmp);

		if (func != NULL) {
			if (hdr->continues) {
				hdr->use_full_value = TRUE;
				continue;
			}

			if (!func->func(ctx, hdr)) {
				/* this header is broken, remove it */
				ctx->need_rewrite = TRUE;
				str_truncate(ctx->header, line_start_pos);
				if (ctx->header_first_change == (size_t)-1) {
					ctx->header_first_change =
						line_start_pos;
				}
				continue;
			}
			buffer_append(ctx->header, hdr->full_value,
				      hdr->full_value_len);
		} else {
			ctx->sync_ctx->mbox->md5_v.more(mbox_md5_ctx, hdr);
			buffer_append(ctx->header, hdr->value,
				      hdr->value_len);
		}
		if (!hdr->no_newline) {
			if (hdr->crlf_newline)
				str_append_c(ctx->header, '\r');
			str_append_c(ctx->header, '\n');
		}
	}
	i_assert(ret != 0);
	message_parse_header_deinit(&hdr_ctx);

	ctx->sync_ctx->mbox->md5_v.finish(mbox_md5_ctx, ctx->hdr_md5_sum);

	if ((ctx->seq == 1 && !ctx->seen_imapbase) ||
	    (ctx->seq > 1 && sync_ctx->dest_first_mail)) {
		/* missing X-IMAPbase */
		ctx->need_rewrite = TRUE;
		if (sync_ctx->base_uid_validity == 0) {
			/* figure out a new UIDVALIDITY for us. */
			sync_ctx->base_uid_validity =
				sync_ctx->hdr->uid_validity != 0 &&
				!sync_ctx->renumber_uids ?
				sync_ctx->hdr->uid_validity :
				I_MAX((uint32_t)ioloop_time, 1);
		}
	}

	ctx->body_offset = input->v_offset;
	if (input->stream_errno != 0) {
		mbox_sync_set_critical(ctx->sync_ctx, "read(%s) failed: %s",
			i_stream_get_name(input), i_stream_get_error(input));
		return -1;
	}
	return 0;
}
Beispiel #22
0
static void test_fts_filter_stopwords_fin(void)
{
	const struct fts_language finnish = { .name = "fi" };
	struct fts_filter *filter;
	const char *error;
	int ret;
	const char *input[] = {"olla", "vaiko", "eik\xC3\xB6", "olla",
	                       "kenest\xC3\xA4", "ja", "joista", "jonka",
	                       "testi", NULL};
	const char *output[] = {NULL, "vaiko", "eik\xC3\xB6", NULL, NULL,
	                        NULL, NULL, NULL, "testi"};
	const char *input2[] =
		{"kuka", "kenet", "keneen", "testi", "eiv\xC3\xA4t", NULL};
	const char *output2[] = {NULL, NULL, NULL, "testi", NULL};
	const char **ip, **op;
	const char *token;

	test_begin("fts filter stopwords, Finnish");
	test_assert(fts_filter_create(fts_filter_stopwords, NULL, &finnish, stopword_settings, &filter, &error) == 0);

	ip = input;
	op = output;
	while (*ip != NULL) {
		token = *ip;
		ret = fts_filter_filter(filter, &token, &error);
		if (ret <= 0) {
			test_assert(ret == 0);
			test_assert(*op == NULL);
		} else {
			test_assert(*op != NULL);
			test_assert(strcmp(*ip, token)  == 0);
		}
		op++;
		ip++;
	}

	fts_filter_unref(&filter);
	test_assert(filter == NULL);

	test_assert(fts_filter_create(fts_filter_stopwords, NULL, &finnish, stopword_settings, &filter, &error) == 0);
	ip = input2;
	op = output2;
	while (*ip != NULL) {
		token = *ip;
		ret = fts_filter_filter(filter, &token, &error);
		if (ret <= 0) {
			test_assert(ret == 0);
			test_assert(*op == NULL);
		} else {
			test_assert(*op != NULL);
			test_assert(strcmp(*ip, token)  == 0);
		}
		op++;
		ip++;
	}

	fts_filter_unref(&filter);
	test_assert(filter == NULL);
	test_end();
}

static void test_fts_filter_stopwords_fra(void)
{
	struct fts_filter *filter;
	const char *error;
	int ret;

	const char *input[] = {"e\xC3\xBBt", "soyez", "soi", "peut", "que",
	                       "quelconque", "\xC3\xA9t\xC3\xA9",
	                       "l\xE2\x80\x99""av\xC3\xA8nement",
	                       NULL};
	const char *output[] = {NULL, NULL, NULL, "peut", NULL,
	                        "quelconque", NULL, 
	                        "l\xE2\x80\x99""av\xC3\xA8nement",};
	const char **ip, **op;
	const char *token;

	test_begin("fts filter stopwords, French");
	test_assert(fts_filter_create(fts_filter_stopwords, NULL, &french_language, stopword_settings, &filter, &error) == 0);

	ip = input;
	op = output;
	while (*ip != NULL) {
		token = *ip;
		ret = fts_filter_filter(filter, &token, &error);
		if (ret <= 0) {
			test_assert(ret == 0);
			test_assert(*op == NULL);
		} else {
			test_assert(*op != NULL);
			test_assert(strcmp(*ip, token)  == 0);
		}
		op++;
		ip++;
	}

	fts_filter_unref(&filter);
	test_assert(filter == NULL);
	test_end();
}

static void test_fts_filter_stopwords_no(void)
{
	struct fts_filter *filter;
	const char *error;
	int ret;

	const char *input[] = {"og", "d\xC3\xA5", "medlemsstatane", "har",
	                       "bunde", "seg", "til", "\xC3\xA5", "fremje",
	                       "allmenn", "v\xC3\xB8rdnad", "for", "pakta",
	                       "og", "halde", "seg", "etter", "menneskerettane",
	                       "og", "den", "grunnleggjande", "fridomen", "i",
	                       "samarbeid", "med", "Dei", "Sameinte",
	                       "Nasjonane", NULL};

	const char *output[] = {NULL, NULL, "medlemsstatane", NULL,
	                       "bunde", NULL, NULL, NULL, "fremje",
	                       "allmenn", "v\xC3\xB8rdnad", NULL, "pakta",
	                       NULL, "halde", NULL, NULL, "menneskerettane",
	                       NULL, NULL, "grunnleggjande", "fridomen", NULL,
	                       "samarbeid", NULL, "Dei", "Sameinte",
	                       "Nasjonane"};
	const char **ip, **op;
	const char *token;

	test_begin("fts filter stopwords, Norwegian");
	test_assert(fts_filter_create(fts_filter_stopwords, NULL, &norwegian_language, stopword_settings, &filter, &error) == 0);

	ip = input;
	op = output;
	while (*ip != NULL) {
		token = *ip;
		ret = fts_filter_filter(filter, &token, &error);
		if (ret <= 0) {
			test_assert(ret == 0);
			test_assert(*op == NULL);
		} else {
			test_assert(*op != NULL);
			test_assert(strcmp(*ip, token)  == 0);
		}
		op++;
		ip++;
	}

	fts_filter_unref(&filter);
	test_assert(filter == NULL);
	test_end();
}

static void test_fts_filter_stopwords_fail_lazy_init(void)
{
	const struct fts_language unknown = { .name = "bebobidoop" };
	struct fts_filter *filter = NULL;
	const char *error = NULL, *token = "foobar";

	test_begin("fts filter stopwords, fail filter() (lazy init)");
	test_assert(fts_filter_create(fts_filter_stopwords, NULL, &unknown, stopword_settings, &filter, &error) == 0);
	test_assert(filter != NULL && error == NULL);
	test_assert(fts_filter_filter(filter, &token, &error) < 0 && error != NULL);
	fts_filter_unref(&filter);
	test_end();

}

#ifdef HAVE_FTS_STEMMER
static void test_fts_filter_stemmer_snowball_stem_english(void)
{
	struct fts_filter *stemmer;
	const char *error;
	const char *token = NULL;
	const char * const tokens[] = {
		"dries" ,"friendlies", "All", "human", "beings", "are",
		 "born", "free", "and", "equal", "in", "dignity", "and",
		 "rights", "They", "are", "endowed", "with", "reason", "and",
		 "conscience", "and", "should", "act", "towards", "one",
		 "another", "in", "a", "spirit", "of", "brotherhood", NULL};
	const char * const bases[] = {
		"dri" ,"friend", "All", "human", "be", "are", "born", "free",
		"and", "equal", "in", "digniti", "and", "right", "They", "are",
		"endow", "with", "reason", "and", "conscienc", "and", "should",
		"act", "toward", "one", "anoth", "in", "a", "spirit", "of",
		"brotherhood", NULL};
	const char * const *tpp;
	const char * const *bpp;

	test_begin("fts filter stem English");
	test_assert(fts_filter_create(fts_filter_stemmer_snowball, NULL, &english_language, NULL, &stemmer, &error) == 0);
	bpp = bases;
	for (tpp=tokens; *tpp != NULL; tpp++) {
		token = *tpp;
		test_assert(fts_filter_filter(stemmer, &token, &error) > 0);
		test_assert(token != NULL);
		test_assert(null_strcmp(token, *bpp) == 0);
		bpp++;
	}
	fts_filter_unref(&stemmer);
	test_assert(stemmer == NULL);
	test_end();
}

static void test_fts_filter_stemmer_snowball_stem_french(void)
{
	struct fts_filter *stemmer;
	const char *error;
	const char *token = NULL;
	const char * const tokens[] = {
		"Tous", "les", "\xC3\xAAtres", "humains", "naissent",
		"libres", "et",	"\xC3\xA9gaux", "en", "dignit\xC3\xA9",
		"et", "en", "droits", NULL};
	const char * const bases[] = {
		"Tous" ,"le", "\xC3\xAAtre", "humain", "naissent", "libr", "et",
		"\xC3\xA9gal", "en", "dignit", "et", "en", "droit", NULL};
	const char * const *tpp;
	const char * const *bpp;

	test_begin("fts filter stem French");
	test_assert(fts_filter_create(fts_filter_stemmer_snowball, NULL, &french_language, NULL, &stemmer, &error) == 0);
	bpp = bases;
	for (tpp=tokens; *tpp != NULL; tpp++) {
		token = *tpp;
		test_assert(fts_filter_filter(stemmer, &token, &error) > 0);
		test_assert(token != NULL);
		test_assert(null_strcmp(token, *bpp) == 0);
		bpp++;
	}
	fts_filter_unref(&stemmer);
	test_assert(stemmer == NULL);
	test_end();
}

static void test_fts_filter_stopwords_stemmer_eng(void)
{
	int ret;
	struct fts_filter *stemmer;
	struct fts_filter *filter;
	const char *error;
	const char *token = NULL;
	const char * const tokens[] = {
		"dries" ,"friendlies", "All", "human", "beings", "are",
		 "born", "free", "and", "equal", "in", "dignity", "and",
		 "rights", "They", "are", "endowed", "with", "reason", "and",
		 "conscience", "and", "should", "act", "towards", "one",
		 "another", "in", "a", "spirit", "of", "brotherhood", NULL};
	const char * const bases[] = {
		"dri" ,"friend", "All", "human", "be", NULL, "born", "free",
		NULL, "equal", NULL, "digniti", NULL, "right", "They", NULL,
		"endow", NULL, "reason", NULL, "conscienc", NULL, "should",
		"act", "toward", "one", "anoth", NULL, NULL, "spirit", NULL,
		"brotherhood", NULL};
	const char * const *tpp;
	const char * const *bpp;

	test_begin("fts filters stopwords and stemming chained, English");

	test_assert(fts_filter_create(fts_filter_stopwords, NULL, &english_language, stopword_settings, &filter, &error) == 0);
	test_assert(fts_filter_create(fts_filter_stemmer_snowball, filter, &english_language, NULL, &stemmer, &error) == 0);

	bpp = bases;
	for (tpp=tokens; *tpp != NULL; tpp++) {
		token = *tpp;
		ret = fts_filter_filter(stemmer, &token, &error);
		test_assert(ret >= 0);
		if (ret == 0)
			test_assert(*bpp == NULL);
		else {
			test_assert(*bpp != NULL);
			test_assert(null_strcmp(*bpp, token)  == 0);
		}
		bpp++;
	}
	fts_filter_unref(&stemmer);
	fts_filter_unref(&filter);
	test_assert(stemmer == NULL);
	test_assert(filter == NULL);
	test_end();
}
#endif

#ifdef HAVE_LIBICU
static void test_fts_filter_normalizer_swedish_short(void)
{
	struct fts_filter *norm = NULL;
	const char *input[] = {
		"Vem",
		"\xC3\x85",
		"\xC3\x85\xC3\x84\xC3\x96",
		"Vem kan segla f\xC3\xB6rutan vind?\n"
		"\xC3\x85\xC3\x84\xC3\x96\xC3\xB6\xC3\xA4\xC3\xA5"
	};
	const char *expected_output[] = {
		"vem",
		"a",
		"aao",
		"vem kan segla forutan vind?\naaooaa"
	};
	const char * const settings[] =
		{"id", "Any-Lower; NFKD; [: Nonspacing Mark :] Remove; NFC", NULL};
	const char *error = NULL;
	const char *token = NULL;
	unsigned int i;

	test_begin("fts filter normalizer Swedish short text");

	test_assert(fts_filter_create(fts_filter_normalizer_icu, NULL, NULL, settings, &norm, &error) == 0);
	for (i = 0; i < N_ELEMENTS(input); i++) {
		token = input[i];
		test_assert_idx(fts_filter_filter(norm, &token, &error) == 1, i);
		test_assert_idx(null_strcmp(token, expected_output[i]) == 0, i);
	}
	fts_filter_unref(&norm);
	test_assert(norm == NULL);
	test_end();
}

static void test_fts_filter_normalizer_swedish_short_default_id(void)
{
	struct fts_filter *norm = NULL;
	const char *input[] = {
		"Vem",
		"\xC3\x85",
		"\xC3\x85\xC3\x84\xC3\x96",
		"Vem kan segla f\xC3\xB6rutan vind?\n"
		"\xC3\x85\xC3\x84\xC3\x96\xC3\xB6\xC3\xA4\xC3\xA5"
	};
	const char *expected_output[] = {
		"vem",
		"a",
		"aao",
		"vemkanseglaforutanvind?\naaooaa"
	};
	const char *error = NULL;
	const char *token = NULL;
	unsigned int i;

	test_begin("fts filter normalizer Swedish short text using default ID");

	test_assert(fts_filter_create(fts_filter_normalizer_icu, NULL, NULL, NULL, &norm, &error) == 0);
	for (i = 0; i < N_ELEMENTS(input); i++) {
		token = input[i];
		test_assert_idx(fts_filter_filter(norm, &token, &error) == 1, i);
		test_assert_idx(null_strcmp(token, expected_output[i]) == 0, i);
	}
	fts_filter_unref(&norm);
	test_assert(norm == NULL);
	test_end();
}

/* UDHRDIR comes from Automake AM_CPPFLAGS */
#define UDHR_FRA_NAME "/udhr_fra.txt"
static void test_fts_filter_normalizer_french(void)
{
	struct fts_filter *norm = NULL;
	FILE *input;
	const char * const settings[] =
		{"id", "Any-Lower; NFKD; [: Nonspacing Mark :] Remove", NULL};
	char buf[4096] = {0};
	const char *error = NULL;
	const char *tokens;
	unsigned char sha512_digest[SHA512_RESULTLEN];
	struct sha512_ctx ctx;
	const unsigned char correct_digest[] = {
		0x78, 0x1e, 0xb9, 0x04, 0xa4, 0x92, 0xca, 0x88,
		0x1e, 0xef, 0x7b, 0xc8, 0x3e, 0x4a, 0xa8, 0xdb,
		0x9c, 0xd4, 0x42, 0x5c, 0x64, 0x81, 0x06, 0xd5,
		0x72, 0x93, 0x38, 0x0c, 0x09, 0xce, 0xbe, 0xdf,
		0x65, 0xff, 0x36, 0x35, 0x05, 0x77, 0xcc, 0xc6,
		0xff, 0x44, 0x2c, 0x31, 0x10, 0x00, 0xf6, 0x8d,
		0x15, 0x25, 0x1e, 0x54, 0x67, 0x2a, 0x5b, 0xc1,
		0xdb, 0x84, 0xc5, 0x0d, 0x43, 0x7e, 0x8c, 0x70};
	const char *udhr_path;

	test_begin("fts filter normalizer French UDHR");

	udhr_path = t_strconcat(UDHRDIR, UDHR_FRA_NAME, NULL);
	test_assert(fts_filter_create(fts_filter_normalizer_icu, NULL, NULL, settings, &norm, &error) == 0);
	input = fopen(udhr_path, "r");
	test_assert(input != NULL);
	sha512_init(&ctx);
	while (NULL != fgets(buf, sizeof(buf), input)) {
		tokens = buf;
		if (fts_filter_filter(norm, &tokens, &error) != 1){
			break;
		}
		sha512_loop(&ctx, tokens, strlen(tokens));
	}
	fclose(input);
	sha512_result(&ctx, sha512_digest);
	test_assert(memcmp(sha512_digest, correct_digest,
			   sizeof(sha512_digest)) == 0);
	fts_filter_unref(&norm);
	test_assert(norm == NULL);
	test_end();
}

static void test_fts_filter_normalizer_empty(void)
{
	/* test just a couple of these */
	static const char *empty_tokens[] = {
		"\xC2\xAF", /* U+00AF */
		"\xCC\x80", /* U+0300 */
		"\xF3\xA0\x87\xAF", /* U+E01EF */
		"\xCC\x80\xF3\xA0\x87\xAF" /* U+0300 U+E01EF */
	};
	const char * const settings[] =
		{"id", "Any-Lower; NFKD; [: Nonspacing Mark :] Remove; [\\x20] Remove", NULL};
	struct fts_filter *norm;
	const char *error;
	unsigned int i;

	test_begin("fts filter normalizer empty tokens");
	test_assert(fts_filter_create(fts_filter_normalizer_icu, NULL, NULL, settings, &norm, &error) == 0);
	for (i = 0; i < N_ELEMENTS(empty_tokens); i++) {
		const char *token = empty_tokens[i];
		test_assert_idx(fts_filter_filter(norm, &token, &error) == 0, i);
	}
	fts_filter_unref(&norm);
	test_end();
}

static void test_fts_filter_normalizer_baddata(void)
{
	const char * const settings[] =
		{"id", "Any-Lower; NFKD; [: Nonspacing Mark :] Remove", NULL};
	struct fts_filter *norm;
	const char *token, *error;
	string_t *str;
	unsigned int i;

	test_begin("fts filter normalizer bad data");

	test_assert(fts_filter_create(fts_filter_normalizer_icu, NULL, NULL, settings, &norm, &error) == 0);
	str = t_str_new(128);
	for (i = 1; i < 0x1ffff; i++) {
		str_truncate(str, 0);
		uni_ucs4_to_utf8_c(i, str);
		token = str_c(str);
		T_BEGIN {
			test_assert_idx(fts_filter_filter(norm, &token, &error) >= 0, i);
		} T_END;
	}

	str_truncate(str, 0);
	uni_ucs4_to_utf8_c(0x7fffffff, str);
	token = str_c(str);
	test_assert(fts_filter_filter(norm, &token, &error) >= 0);

	fts_filter_unref(&norm);
	test_end();
}

static void test_fts_filter_normalizer_invalid_id(void)
{
	struct fts_filter *norm = NULL;
	const char *settings[] =
		{"id", "Any-One-Out-There; DKFN; [: Nonspacing Mark :] Remove",
		 NULL};
	const char *error = NULL, *token = "foo";

	test_begin("fts filter normalizer invalid id");
	test_assert(fts_filter_create(fts_filter_normalizer_icu, NULL, NULL, settings, &norm, &error) == 0);
	test_assert(error == NULL);
	test_assert(fts_filter_filter(norm, &token, &error) < 0 && error != NULL);
	fts_filter_unref(&norm);
	test_end();
}

#ifdef HAVE_FTS_STEMMER
static void test_fts_filter_normalizer_stopwords_stemmer_eng(void)
{
	int ret;
	struct fts_filter *normalizer;
	struct fts_filter *stemmer;
	struct fts_filter *filter;
	const char *error;
	const char * const id_settings[] =
		//{"id", "Any-Lower; NFKD; [: Nonspacing Mark :] Remove; NFC", NULL};
		{"id", "Lower", NULL};
	const char *token = NULL;
	const char * const tokens[] = {
		"dries" ,"friendlies", "All", "human", "beings", "are",
		"born", "free", "and", "equal", "in", "dignity", "and",
		"rights", "They", "are", "endowed", "with", "reason", "and",
		"conscience", "and", "should", "act", "towards", "one",
		"another", "in", "a", "spirit", "of", "brotherhood", "ABCFoo",
		NULL};
	const char * const bases[] = {
		"dri" ,"friend", "all", "human", "be", NULL, "born", "free",
		NULL, "equal", NULL, "digniti", NULL, "right", NULL, NULL,
		"endow", NULL, "reason", NULL, "conscienc", NULL, "should",
		"act", "toward", "one", "anoth", NULL, NULL, "spirit", NULL,
		"brotherhood", "abcfoo", NULL};
	const char * const *tpp;
	const char * const *bpp;

	test_begin("fts filters normalizer, stopwords and stemming chained, English");

	test_assert(fts_filter_create(fts_filter_normalizer_icu, NULL, NULL, id_settings, &normalizer, &error) == 0);
	test_assert(fts_filter_create(fts_filter_stopwords, normalizer, &english_language, stopword_settings, &filter, &error) == 0);
	test_assert(fts_filter_create(fts_filter_stemmer_snowball, filter, &english_language, NULL, &stemmer, &error) == 0);

	bpp = bases;
	for (tpp = tokens; *tpp != NULL; tpp++) {
		token = *tpp;
		ret = fts_filter_filter(stemmer, &token, &error);
		if (ret <= 0) {
			test_assert(ret == 0);
			test_assert(*bpp == NULL);
		} else {
			test_assert(*bpp != NULL);
			test_assert(strcmp(*bpp, token)  == 0);
		}
		bpp++;
	}
	fts_filter_unref(&stemmer);
	fts_filter_unref(&filter);
	fts_filter_unref(&normalizer);
	test_assert(stemmer == NULL);
	test_assert(filter == NULL);
	test_assert(normalizer == NULL);
	test_end();
}
static void
sasl_callback(struct client *client, enum sasl_server_reply sasl_reply,
	      const char *data, const char *const *args)
{
	struct client_auth_reply reply;

	i_assert(!client->destroyed ||
		 sasl_reply == SASL_SERVER_REPLY_AUTH_ABORTED ||
		 sasl_reply == SASL_SERVER_REPLY_MASTER_FAILED);

	switch (sasl_reply) {
	case SASL_SERVER_REPLY_SUCCESS:
		if (client->to_auth_waiting != NULL)
			timeout_remove(&client->to_auth_waiting);
		if (args != NULL) {
			client_auth_parse_args(client, args, &reply);
			if (client_auth_handle_reply(client, &reply, TRUE))
				break;
		}
		client_auth_result(client, CLIENT_AUTH_RESULT_SUCCESS,
				   NULL, NULL);
		client_destroy_success(client, "Login");
		break;
	case SASL_SERVER_REPLY_AUTH_FAILED:
	case SASL_SERVER_REPLY_AUTH_ABORTED:
		if (client->to_auth_waiting != NULL)
			timeout_remove(&client->to_auth_waiting);
		if (args != NULL) {
			client_auth_parse_args(client, args, &reply);
			reply.nologin = TRUE;
			if (client_auth_handle_reply(client, &reply, FALSE))
				break;
		}

		if (sasl_reply == SASL_SERVER_REPLY_AUTH_ABORTED) {
			client_auth_result(client, CLIENT_AUTH_RESULT_ABORTED,
				NULL, "Authentication aborted by client.");
		} else if (data == NULL) {
			client_auth_result(client,
				CLIENT_AUTH_RESULT_AUTHFAILED, NULL,
				AUTH_FAILED_MSG);
		} else {
			client_auth_result(client,
				CLIENT_AUTH_RESULT_AUTHFAILED_REASON, NULL,
				data);
		}

		if (!client->destroyed)
			client_auth_failed(client);
		break;
	case SASL_SERVER_REPLY_MASTER_FAILED:
		if (data != NULL) {
			/* authentication itself succeeded, we just hit some
			   internal failure. */
			client_auth_result(client, CLIENT_AUTH_RESULT_TEMPFAIL,
					   NULL, data);
		}

		/* the fd may still be hanging somewhere in kernel or another
		   process. make sure the client gets disconnected. */
		if (shutdown(client->fd, SHUT_RDWR) < 0 && errno != ENOTCONN)
			i_error("shutdown() failed: %m");

		if (data == NULL)
			client_destroy_internal_failure(client);
		else
			client_destroy_success(client, data);
		break;
	case SASL_SERVER_REPLY_CONTINUE:
		i_assert(client->v.auth_send_challenge != NULL);
		client->v.auth_send_challenge(client, data);

		if (client->to_auth_waiting != NULL)
			timeout_remove(&client->to_auth_waiting);

		if (client->auth_response != NULL)
			str_truncate(client->auth_response, 0);

		i_assert(client->io == NULL);
		client->auth_waiting = TRUE;
		client->io = io_add(client->fd, IO_READ,
				    client_auth_input, client);
		client_auth_input(client);
		return;
	}

	client_unref(&client);
}
Beispiel #24
0
static int json_parse_string(struct json_parser *parser, bool allow_skip,
			     const char **value_r)
{
	if (*parser->data != '"')
		return -1;
	parser->data++;

	if (parser->skipping && allow_skip) {
		*value_r = NULL;
		return json_skip_string(parser);
	}

	str_truncate(parser->value, 0);
	for (; parser->data != parser->end; parser->data++) {
		if (*parser->data == '"') {
			parser->data++;
			*value_r = str_c(parser->value);
			return 1;
		}
		if (*parser->data != '\\')
			str_append_c(parser->value, *parser->data);
		else {
			if (++parser->data == parser->end)
				return 0;
			switch (*parser->data) {
			case '"':
			case '\\':
			case '/':
				str_append_c(parser->value, *parser->data);
				break;
			case 'b':
				str_append_c(parser->value, '\b');
				break;
			case 'f':
				str_append_c(parser->value, '\f');
				break;
			case 'n':
				str_append_c(parser->value, '\n');
				break;
			case 'r':
				str_append_c(parser->value, '\r');
				break;
			case 't':
				str_append_c(parser->value, '\t');
				break;
			case 'u':
				parser->data++;
				if (parser->end - parser->data < 4) {
					/* wait for more data */
					parser->data = parser->end;
					return 0;
				}
				uni_ucs4_to_utf8_c(hex2dec(parser->data, 4),
						   parser->value);
				parser->data += 3;
				break;
			default:
				return -1;
			}
		}
	}
	return 0;
}
Beispiel #25
0
int mailbox_list_delete_mailbox_nonrecursive(struct mailbox_list *list,
					     const char *name, const char *path,
					     bool rmdir_path)
{
	DIR *dir;
	struct dirent *d;
	string_t *full_path;
	unsigned int dir_len;
	bool mailbox_dir, unlinked_something = FALSE;

	if (mailbox_list_check_root_delete(list, name, path) < 0)
		return -1;

	dir = opendir(path);
	if (dir == NULL) {
		if (errno == ENOENT) {
			mailbox_list_set_error(list, MAIL_ERROR_NOTFOUND,
				T_MAIL_ERR_MAILBOX_NOT_FOUND(name));
		} else {
			if (!mailbox_list_set_error_from_errno(list)) {
				mailbox_list_set_critical(list,
					"opendir(%s) failed: %m", path);
			}
		}
		return -1;
	}

	full_path = t_str_new(256);
	str_append(full_path, path);
	str_append_c(full_path, '/');
	dir_len = str_len(full_path);

	for (errno = 0; (d = readdir(dir)) != NULL; errno = 0) {
		if (d->d_name[0] == '.') {
			/* skip . and .. */
			if (d->d_name[1] == '\0')
				continue;
			if (d->d_name[1] == '.' && d->d_name[2] == '\0')
				continue;
		}

		mailbox_dir = list->v.is_internal_name != NULL &&
			list->v.is_internal_name(list, d->d_name);

		str_truncate(full_path, dir_len);
		str_append(full_path, d->d_name);

		if (mailbox_dir) {
			if (mailbox_list_delete_trash(str_c(full_path)) < 0) {
				mailbox_list_set_critical(list,
					"unlink_directory(%s) failed: %m",
					str_c(full_path));
			} else {
				unlinked_something = TRUE;
			}
			continue;
		}

		/* trying to unlink() a directory gives either EPERM or EISDIR
		   (non-POSIX). it doesn't really work anywhere in practise,
		   so don't bother stat()ing the file first */
		if (unlink(str_c(full_path)) == 0)
			unlinked_something = TRUE;
		else if (errno != ENOENT && errno != EISDIR && errno != EPERM) {
			mailbox_list_set_critical(list,
				"unlink_directory(%s) failed: %m",
				str_c(full_path));
		}
	}
	if (errno != 0)
		mailbox_list_set_critical(list, "readdir(%s) failed: %m", path);
	if (closedir(dir) < 0) {
		mailbox_list_set_critical(list, "closedir(%s) failed: %m",
					  path);
	}

	if (rmdir_path) {
		if (rmdir(path) == 0)
			unlinked_something = TRUE;
		else if (errno != ENOENT &&
			 errno != ENOTEMPTY && errno != EEXIST) {
			mailbox_list_set_critical(list, "rmdir(%s) failed: %m",
						  path);
			return -1;
		}
	}

	if (!unlinked_something) {
		mailbox_list_set_error(list, MAIL_ERROR_NOTPOSSIBLE,
				       "Mailbox has children, can't delete it");
		return -1;
	}
	return 0;
}
static void doveadm_print_formatted_flush(void)
{
	o_stream_nsend(doveadm_print_ostream, str_data(ctx.buf), str_len(ctx.buf));
	str_truncate(ctx.buf, 0);
}
static int http_header_parse(struct http_header_parser *parser)
{
	int ret;

	/* RFC 7230, Section 3.2: Header Fields

	   'header'       = *( header-field CRLF ) CRLF
	                  ; Actually part of HTTP-message syntax

	   header-field   = field-name ":" OWS field-value OWS
	   field-name     = token
	   field-value    = *( field-content / obs-fold )
	   field-content  = field-vchar [ 1*( SP / HTAB ) field-vchar ]
	   field-vchar    = VCHAR / obs-text
	   obs-fold       = CRLF 1*( SP / HTAB )
	                  ; obsolete line folding
	                  ; see Section 3.2.4
	 */

	for (;;) {
		switch (parser->state) {
		case HTTP_HEADER_PARSE_STATE_INIT:
			buffer_set_used_size(parser->value_buf, 0);
			str_truncate(parser->name, 0);
			if (*parser->cur == '\r') {
				/* last CRLF */
				parser->cur++;
				parser->state = HTTP_HEADER_PARSE_STATE_EOH;
				if (parser->cur == parser->end)
					return 0;
				break;
			} else if (*parser->cur == '\n') {
				/* last LF */
				parser->state = HTTP_HEADER_PARSE_STATE_EOH;
				break;
			}
			/* next line */
			parser->state = HTTP_HEADER_PARSE_STATE_NAME;
			/* fall through */
		case HTTP_HEADER_PARSE_STATE_NAME:
			if ((ret=http_header_parse_name(parser)) <= 0)
				return ret;
			parser->state = HTTP_HEADER_PARSE_STATE_COLON;
			/* fall through */
		case HTTP_HEADER_PARSE_STATE_COLON:
			if (*parser->cur != ':') {
				parser->error = t_strdup_printf
					("Expected ':' after header field name '%s', but found %s",
						str_sanitize(str_c(parser->name),64),
						_chr_sanitize(*parser->cur));
				return -1;
			}
			parser->cur++;
			if (str_len(parser->name) == 0) {
				parser->error = "Empty header field name";
				return -1;
			}
			if (++parser->field_count > parser->limits.max_fields) {
				parser->error = "Excessive number of header fields";
				return -1;
			}
			parser->state = HTTP_HEADER_PARSE_STATE_OWS;
			/* fall through */
		case HTTP_HEADER_PARSE_STATE_OWS:
			if ((ret=http_header_parse_ows(parser)) <= 0)
				return ret;
			parser->state = HTTP_HEADER_PARSE_STATE_CONTENT;
			/* fall through */
		case HTTP_HEADER_PARSE_STATE_CONTENT:
			if ((ret=http_header_parse_content(parser)) <= 0)
				return ret;
			parser->state = HTTP_HEADER_PARSE_STATE_CR;
			/* fall through */
		case HTTP_HEADER_PARSE_STATE_CR:
			if (*parser->cur == '\r') {
				parser->cur++;
			} else if (*parser->cur != '\n') {
				parser->error = t_strdup_printf
					("Invalid character %s in content of header field '%s'",
						_chr_sanitize(*parser->cur),
						str_sanitize(str_c(parser->name),64));
				return -1;
			}
			parser->state = HTTP_HEADER_PARSE_STATE_LF;
			if (parser->cur == parser->end)
				return 0;
			/* fall through */
		case HTTP_HEADER_PARSE_STATE_LF:
			if (*parser->cur != '\n') {
				parser->error = t_strdup_printf
					("Expected LF after CR at end of header field '%s', but found %s",
						str_sanitize(str_c(parser->name),64),
						_chr_sanitize(*parser->cur));
				return -1;
			}
			parser->cur++;
			parser->state = HTTP_HEADER_PARSE_STATE_NEW_LINE;
			if (parser->cur == parser->end)
				return 0;
			/* fall through */
		case HTTP_HEADER_PARSE_STATE_NEW_LINE:
			if (*parser->cur == ' ' || *parser->cur == '\t') {
				/* obs-fold */
				buffer_append_c(parser->value_buf, ' ');
				parser->state = HTTP_HEADER_PARSE_STATE_OWS;
				break;
			} 
			/* next header line */
			parser->state = HTTP_HEADER_PARSE_STATE_INIT;
			return 1;
		case HTTP_HEADER_PARSE_STATE_EOH:
			if (*parser->cur != '\n') {
				parser->error = t_strdup_printf
					("Encountered stray CR at beginning of header line, followed by %s",
						_chr_sanitize(*parser->cur));
				return -1;
			}
			/* header fully parsed */
			parser->cur++;
			return 1;

		default:
			i_unreached();
		}
	}

	i_unreached();
	return -1;
}
static int
astream_part_finish(struct attachment_istream *astream, const char **error_r)
{
	struct attachment_istream_part *part = &astream->part;
	struct istream_attachment_info info;
	struct istream *input;
	struct ostream *output;
	string_t *digest_str;
	const unsigned char *data;
	size_t size;
	int ret = 0;

	if (o_stream_nfinish(part->temp_output) < 0) {
		*error_r = t_strdup_printf("write(%s) failed: %s",
					   o_stream_get_name(part->temp_output),
					   o_stream_get_error(part->temp_output));
		return -1;
	}

	memset(&info, 0, sizeof(info));
	info.start_offset = astream->part.start_offset;
	/* base64_bytes contains how many valid base64 bytes there are so far.
	   if the base64 ends properly, it'll specify how much of the MIME part
	   is saved as an attachment. the rest of the data (typically
	   linefeeds) is added back to main stream */
	info.encoded_size = part->base64_bytes;
	/* get the hash before base64-decoder resets it */
	digest_str = t_str_new(128);
	hash_format_write(astream->set.hash_format, digest_str);
	info.hash = str_c(digest_str);

	/* if it looks like we can decode base64 without any data loss,
	   do it and write the decoded data to another temp file. */
	if (!part->base64_failed) {
		if (part->base64_state == BASE64_STATE_0 &&
		    part->base64_bytes > 0) {
			/* there is no trailing LF or '=' characters,
			   but it's not completely empty */
			part->base64_state = BASE64_STATE_EOM;
		}
		if (part->base64_state == BASE64_STATE_EOM) {
			/* base64 data looks ok. */
			if (astream_decode_base64(astream) < 0)
				part->base64_failed = TRUE;
		} else {
			part->base64_failed = TRUE;
		}
	}

	/* open attachment output file */
	info.part = astream->cur_part;
	if (!part->base64_failed) {
		info.base64_blocks_per_line = part->base64_line_blocks;
		info.base64_have_crlf = part->base64_have_crlf;
		/* base64-decoder updated the hash, use it */
		str_truncate(digest_str, 0);
		hash_format_write(astream->set.hash_format, digest_str);
		info.hash = str_c(digest_str);
	} else {
		/* couldn't decode base64, so write the entire MIME part
		   as attachment */
		info.encoded_size = part->temp_output->offset;
	}
	if (astream->set.open_attachment_ostream(&info, &output, error_r,
						 astream->context) < 0)
		return -1;

	/* copy data to attachment from temp file */
	input = i_stream_create_fd(part->temp_fd, IO_BLOCK_SIZE);
	while (i_stream_read_more(input, &data, &size) > 0) {
		o_stream_nsend(output, data, size);
		i_stream_skip(input, size);
	}

	if (input->stream_errno != 0) {
		*error_r = t_strdup_printf("read(%s) failed: %s",
			i_stream_get_name(input), i_stream_get_error(input));
		ret = -1;
	}
	i_stream_destroy(&input);

	if (astream->set.close_attachment_ostream(output, ret == 0, error_r,
						  astream->context) < 0)
		ret = -1;
	return ret;
}
Beispiel #29
0
char *auth_cache_parse_key(pool_t pool, const char *query)
{
	string_t *str;
	bool key_seen[AUTH_REQUEST_VAR_TAB_COUNT];
	const char *extra_vars;
	unsigned int i, idx, size, tab_idx;

	memset(key_seen, 0, sizeof(key_seen));

	str = t_str_new(32);
	for (; *query != '\0'; ) {
		if (*query != '%') {
			query++;
			continue;
		}

		var_get_key_range(++query, &idx, &size);
		if (size == 0) {
			/* broken %variable ending too early */
			break;
		}
		query += idx;

		if (!auth_request_var_expand_tab_find(query, size, &tab_idx)) {
			/* just add the key. it would be nice to prevent
			   duplicates here as well, but that's just too
			   much trouble and probably very rare. */
			auth_cache_key_add_var(str, query, size);
		} else {
			i_assert(tab_idx < N_ELEMENTS(key_seen));
			key_seen[tab_idx] = TRUE;
		}
		query += size;
	}

	if (key_seen[AUTH_REQUEST_VAR_TAB_USERNAME_IDX] &&
	    key_seen[AUTH_REQUEST_VAR_TAB_DOMAIN_IDX]) {
		/* %n and %d both used -> replace with %u */
		key_seen[AUTH_REQUEST_VAR_TAB_USER_IDX] = TRUE;
		key_seen[AUTH_REQUEST_VAR_TAB_USERNAME_IDX] = FALSE;
		key_seen[AUTH_REQUEST_VAR_TAB_DOMAIN_IDX] = FALSE;
	}

	/* we rely on these being at the beginning */
	i_assert(AUTH_REQUEST_VAR_TAB_USER_IDX == 0);
	i_assert(AUTH_REQUEST_VAR_TAB_USERNAME_IDX == 1);
	i_assert(AUTH_REQUEST_VAR_TAB_DOMAIN_IDX == 2);

	extra_vars = t_strdup(str_c(str));
	str_truncate(str, 0);
	for (i = 0; i < N_ELEMENTS(key_seen); i++) {
		if (key_seen[i])
			auth_cache_key_add_tab_idx(str, i);
	}

	if (*extra_vars != '\0') {
		if (str_len(str) > 0)
			str_append_c(str, '\t');
		str_append(str, extra_vars);
	}

	return p_strdup(pool, str_c(str));
}
Beispiel #30
0
static int proxy_send_login(struct pop3_client *client, struct ostream *output)
{
	struct dsasl_client_settings sasl_set;
	const unsigned char *sasl_output;
	size_t len;
	const char *mech_name, *error;
	string_t *str = t_str_new(128);

	i_assert(client->common.proxy_ttl > 1);
	if (client->proxy_xclient &&
	    !client->common.proxy_not_trusted) {
		string_t *fwd = t_str_new(128);
                for(const char *const *ptr = client->common.auth_passdb_args;*ptr != NULL; ptr++) {
                        if (strncasecmp(*ptr, "forward_", 8) == 0) {
                                if (str_len(fwd) > 0)
                                        str_append_c(fwd, '\t');
                                str_append_tabescaped(fwd, (*ptr)+8);
                        }
		}

		str_printfa(str, "XCLIENT ADDR=%s PORT=%u SESSION=%s TTL=%u",
			    net_ip2addr(&client->common.ip),
			    client->common.remote_port,
			    client_get_session_id(&client->common),
			    client->common.proxy_ttl - 1);
		if (str_len(fwd) > 0) {
			str_append(str, " FORWARD=");
			base64_encode(str_data(fwd), str_len(fwd), str);
		}
		str_append(str, "\r\n");
		/* remote supports XCLIENT, send it */
		o_stream_nsend(output, str_data(str), str_len(str));
		client->proxy_state = POP3_PROXY_XCLIENT;
	} else {
		client->proxy_state = POP3_PROXY_LOGIN1;
	}

	str_truncate(str, 0);

	if (client->common.proxy_mech == NULL) {
		/* send USER command */
		str_append(str, "USER ");
		str_append(str, client->common.proxy_user);
		str_append(str, "\r\n");
		o_stream_nsend(output, str_data(str), str_len(str));
		return 0;
	}

	i_assert(client->common.proxy_sasl_client == NULL);
	i_zero(&sasl_set);
	sasl_set.authid = client->common.proxy_master_user != NULL ?
		client->common.proxy_master_user : client->common.proxy_user;
	sasl_set.authzid = client->common.proxy_user;
	sasl_set.password = client->common.proxy_password;
	client->common.proxy_sasl_client =
		dsasl_client_new(client->common.proxy_mech, &sasl_set);
	mech_name = dsasl_client_mech_get_name(client->common.proxy_mech);

	str_printfa(str, "AUTH %s ", mech_name);
	if (dsasl_client_output(client->common.proxy_sasl_client,
				&sasl_output, &len, &error) < 0) {
		client_log_err(&client->common, t_strdup_printf(
			"proxy: SASL mechanism %s init failed: %s",
			mech_name, error));
		return -1;
	}
	if (len == 0)
		str_append_c(str, '=');
	else
		base64_encode(sasl_output, len, str);
	str_append(str, "\r\n");
	o_stream_nsend(output, str_data(str), str_len(str));

	proxy_free_password(&client->common);
	if (client->proxy_state != POP3_PROXY_XCLIENT)
		client->proxy_state = POP3_PROXY_LOGIN2;
	return 0;
}