Example #1
0
static const char *acl_list_get_path(struct acl_backend_vfile *backend)
{
	return t_strconcat(acl_list_get_root_dir(backend),
			   "/"ACLLIST_FILENAME, NULL);
}
int mailbox_list_delete_maildir_via_trash(struct mailbox_list *list,
					  const char *name,
					  const char *trash_dir)
{
	const char *src, *trash_dest;
	unsigned int count;

	if (mailbox_list_get_path(list, name, MAILBOX_LIST_PATH_TYPE_MAILBOX,
				  &src) <= 0)
		i_unreached();
	if (mailbox_list_check_root_delete(list, name, src) < 0)
		return -1;

	/* rename the mailbox dir to trash dir, which atomically
	   marks it as being deleted. */
	count = 0; trash_dest = trash_dir;
	for (; rename(src, trash_dest) < 0; count++) {
		if (ENOTFOUND(errno)) {
			if (trash_dest != trash_dir && count < 5) {
				/* either the source was just deleted or
				   the trash dir was deleted. */
				trash_dest = trash_dir;
				continue;
			}
			mailbox_list_set_error(list, MAIL_ERROR_NOTFOUND,
				T_MAILBOX_LIST_ERR_NOT_FOUND(list, name));
			return -1;
		}
		if (errno == EXDEV) {
			/* can't do this the fast way */
			return 0;
		}
		if (!EDESTDIREXISTS(errno)) {
			if (mailbox_list_set_error_from_errno(list))
				return -1;
			mailbox_list_set_critical(list,
				"rename(%s, %s) failed: %m", src, trash_dest);
			return -1;
		}

		/* trash dir already exists. the reasons for this are:

		   a) another process is in the middle of deleting it
		   b) previous process crashed and didn't delete it
		   c) NFS: mailbox was recently deleted, but some connection
		      still has that mailbox open. the directory contains .nfs*
		      files that can't be deleted until the mailbox is closed.

		   Because of c) we'll first try to rename the mailbox under
		   the trash directory and only later try to delete the entire
		   trash directory. */
		if (trash_dir == trash_dest) {
			trash_dest = t_strconcat(trash_dir, "/",
						 unique_fname(), NULL);
		} else if (mailbox_list_delete_trash(trash_dest) < 0 &&
			   (errno != ENOTEMPTY || count >= 5)) {
			mailbox_list_set_critical(list,
				"unlink_directory(%s) failed: %m", trash_dest);
			return -1;
		}
	}

	if (mailbox_list_delete_trash(trash_dir) < 0 &&
	    errno != ENOTEMPTY && errno != EBUSY) {
		mailbox_list_set_critical(list,
			"unlink_directory(%s) failed: %m", trash_dir);

		/* it's already renamed to trash dir, which means it's
		   deleted as far as the client is concerned. Report
		   success. */
	}
	return 1;
}
Example #3
0
static int maildir_save_finish_real(struct mail_save_context *_ctx)
{
	struct maildir_save_context *ctx = (struct maildir_save_context *)_ctx;
	struct mail_storage *storage = &ctx->mbox->storage->storage;
	const char *path;
	off_t real_size;
	uoff_t size;
	int output_errno;

	ctx->last_save_finished = TRUE;
	if (ctx->failed && ctx->fd == -1) {
		/* tmp file creation failed */
		return -1;
	}

	path = t_strconcat(ctx->tmpdir, "/", ctx->file_last->tmp_name, NULL);
	if (!ctx->failed && o_stream_nfinish(_ctx->data.output) < 0) {
		if (!mail_storage_set_error_from_errno(storage)) {
			mail_storage_set_critical(storage,
				"write(%s) failed: %m", path);
		}
		ctx->failed = TRUE;
	}

	if (_ctx->data.save_date != (time_t)-1) {
		/* we can't change ctime, but we can add the date to cache */
		struct index_mail *mail = (struct index_mail *)_ctx->dest_mail;
		uint32_t t = _ctx->data.save_date;

		index_mail_cache_add(mail, MAIL_CACHE_SAVE_DATE, &t, sizeof(t));
	}

 	if (maildir_save_finish_received_date(ctx, path) < 0)
		ctx->failed = TRUE;

	if (ctx->cur_dest_mail != NULL) {
		index_mail_cache_parse_deinit(ctx->cur_dest_mail,
					      ctx->ctx.data.received_date,
					      !ctx->failed);
	}
	i_stream_unref(&ctx->input);

	/* remember the size in case we want to add it to filename */
	ctx->file_last->size = _ctx->data.output->offset;
	if (ctx->cur_dest_mail == NULL ||
	    mail_get_virtual_size(ctx->cur_dest_mail,
				  &ctx->file_last->vsize) < 0)
		ctx->file_last->vsize = (uoff_t)-1;

	output_errno = _ctx->data.output->last_failed_errno;
	o_stream_destroy(&_ctx->data.output);

	if (storage->set->parsed_fsync_mode != FSYNC_MODE_NEVER &&
	    !ctx->failed) {
		if (fsync(ctx->fd) < 0) {
			if (!mail_storage_set_error_from_errno(storage)) {
				mail_storage_set_critical(storage,
						  "fsync(%s) failed: %m", path);
			}
			ctx->failed = TRUE;
		}
	}
	real_size = lseek(ctx->fd, 0, SEEK_END);
	if (real_size == (off_t)-1) {
		mail_storage_set_critical(storage,
					  "lseek(%s) failed: %m", path);
	} else if (real_size != (off_t)ctx->file_last->size &&
		   (!maildir_filename_get_size(ctx->file_last->dest_basename,
					       MAILDIR_EXTRA_FILE_SIZE, &size) ||
		    size != ctx->file_last->size)) {
		/* e.g. zlib plugin was used. the "physical size" must be in
		   the maildir filename, since stat() will return wrong size */
		ctx->file_last->preserve_filename = FALSE;
		/* preserve the GUID if needed */
		if (ctx->file_last->guid == NULL)
			ctx->file_last->guid = ctx->file_last->dest_basename;
		/* reset the base name as well, just in case there's a
		   ,W=vsize */
		ctx->file_last->dest_basename = ctx->file_last->tmp_name;
	}
	if (close(ctx->fd) < 0) {
		if (!mail_storage_set_error_from_errno(storage)) {
			mail_storage_set_critical(storage,
						  "close(%s) failed: %m", path);
		}
		ctx->failed = TRUE;
	}
	ctx->fd = -1;

	if (ctx->failed) {
		/* delete the tmp file */
		i_unlink_if_exists(path);

		errno = output_errno;
		if (ENOQUOTA(errno)) {
			mail_storage_set_error(storage,
				MAIL_ERROR_NOQUOTA, MAIL_ERRSTR_NO_QUOTA);
		} else if (errno != 0) {
			mail_storage_set_critical(storage,
				"write(%s) failed: %m", path);
		}

		maildir_save_remove_last_filename(ctx);
		return -1;
	}

	ctx->file_last = NULL;
	return 0;
}
static int
client_handle_user_command(struct client *client, const char *cmd,
			   const char *const *args, const char **error_r)
{
	struct mail_storage_service_input input;
	struct imap_urlauth_worker_settings *set;
	struct mail_storage_service_user *user;
	struct imap_urlauth_config config;
	struct mail_user *mail_user;
	const char *error;
	unsigned int count;
	int ret;

	/* "USER\t"<username> */
	*error_r = NULL;

	/* check command syntax */
	if (strcmp(cmd, "USER") != 0) {
		*error_r = t_strconcat("Unknown or inappropriate command: ",
				       cmd, NULL);
		return -1;
	}

	if (args[0] == NULL || args[1] != NULL) {
		*error_r = "USER: Invalid number of parameters";
		return -1;
	}

	/* lookup user */
	memset(&input, 0, sizeof(input));
	input.module = "imap-urlauth-worker";
	input.service = "imap-urlauth-worker";
	input.username = args[0];

	if (client->debug)
		i_debug("Looking up user %s", input.username);

	ret = mail_storage_service_lookup_next(storage_service, &input,
					       &user, &mail_user, &error);
	if (ret < 0) {
		i_error("Failed to lookup user %s: %s", input.username, error);
		client_abort(client, "Session aborted: Failed to lookup user");
		return 0;
	} else if (ret == 0) {
		if (client->debug)
			i_debug("User %s doesn't exist", input.username);

		client_send_line(client, "NO");
		return 1;
	}

	client->debug = mail_user->mail_debug =
		client->debug || mail_user->mail_debug;

	/* drop privileges */
	restrict_access_allow_coredumps(TRUE);

	set = mail_storage_service_user_get_set(user)[1];
	settings_var_expand(&imap_urlauth_worker_setting_parser_info, set,
			    mail_user->pool,
			    mail_user_var_expand_table(mail_user));

	if (set->verbose_proctitle) {
		verbose_proctitle = TRUE;
		imap_urlauth_worker_refresh_proctitle();
	}

	client->service_user = user;
	client->mail_user = mail_user;
	client->set = set;

	if (client->debug) {
		i_debug("Found user account `%s' on behalf of user `%s'",
			mail_user->username, client->access_user);
	}

	/* initialize urlauth context */
#ifndef APPLE_OS_X_SERVER
	if (*set->imap_urlauth_host == '\0') {
		i_error("imap_urlauth_host setting is not configured for user %s",
			mail_user->username);
		client_send_line(client, "NO");
		client_abort(client, "Session aborted: URLAUTH not configured");
		return 0;
	}
#endif

	memset(&config, 0, sizeof(config));
	config.url_host = set->imap_urlauth_host;
#ifdef APPLE_OS_X_SERVER
	if (*config.url_host == '\0')
		config.url_host = my_hostname;
#endif
	config.url_port = set->imap_urlauth_port;
	config.access_user = client->access_user;
	config.access_anonymous = client->access_anonymous;
	config.access_applications =
		(const void *)array_get(&client->access_apps, &count);
		
	client->urlauth_ctx = imap_urlauth_init(client->mail_user, &config);
	if (client->debug) {
		i_debug("Providing access to user account `%s' on behalf of `%s'",
			mail_user->username, client->access_user);
	}

	i_set_failure_prefix("imap-urlauth[%s](%s->%s): ",
			     my_pid, client->access_user, mail_user->username);

	client_send_line(client, "OK");
	return 1;
}
Example #5
0
void client_destroy(struct client *client, const char *reason)
{
	if (client->destroyed)
		return;
	client->destroyed = TRUE;

	if (!client->login_success && reason != NULL) {
		reason = t_strconcat(reason, " ",
			client_get_extra_disconnect_reason(client), NULL);
	}
	if (reason != NULL)
		client_log(client, reason);

	if (last_client == client)
		last_client = client->prev;
	DLLIST_REMOVE(&clients, client);

	if (client->input != NULL)
		i_stream_close(client->input);
	if (client->output != NULL)
		o_stream_close(client->output);

	if (client->master_tag != 0) {
		i_assert(client->auth_request == NULL);
		i_assert(client->authenticating);
		i_assert(client->refcount > 1);
		client->authenticating = FALSE;
		master_auth_request_abort(master_auth, client->master_tag);
		client->refcount--;
	} else if (client->auth_request != NULL) {
		i_assert(client->authenticating);
		sasl_server_auth_abort(client);
	} else {
		i_assert(!client->authenticating);
	}

	if (client->io != NULL)
		io_remove(&client->io);
	if (client->to_disconnect != NULL)
		timeout_remove(&client->to_disconnect);
	if (client->to_auth_waiting != NULL)
		timeout_remove(&client->to_auth_waiting);
	if (client->auth_response != NULL)
		str_free(&client->auth_response);

	if (client->fd != -1) {
		net_disconnect(client->fd);
		client->fd = -1;
	}

	if (client->proxy_password != NULL) {
		safe_memset(client->proxy_password, 0,
			    strlen(client->proxy_password));
		i_free_and_null(client->proxy_password);
	}

	if (client->login_proxy != NULL)
		login_proxy_free(&client->login_proxy);
	if (client->ssl_proxy != NULL)
		ssl_proxy_free(&client->ssl_proxy);
	client->v.destroy(client);
	if (client_unref(&client) &&
	    master_service_get_service_count(master_service) == 1) {
		/* as soon as this connection is done with proxying
		   (or whatever), the process will die. there's no need for
		   authentication anymore, so close the connection. */
		auth_client_disconnect(auth_client);
	}
	login_client_destroyed();
	login_refresh_proctitle();
}
static int sieve_file_script_sequence_read_dir
(struct sieve_file_script_sequence *fseq, const char *path)
{
	struct sieve_storage *storage = fseq->seq.storage;
	DIR *dirp;
	int ret = 0;

	/* Open the directory */
	if ( (dirp = opendir(path)) == NULL ) {
		switch ( errno ) {
		case ENOENT:
			sieve_storage_set_error(storage,
				SIEVE_ERROR_NOT_FOUND,
				"Script sequence location not found");
			break;
		case EACCES:
			sieve_storage_set_error(storage,
				SIEVE_ERROR_NO_PERMISSION,
				"Script sequence location not accessible");
			sieve_storage_sys_error(storage,
				"Failed to open sieve sequence: "
				"%s",	eacces_error_get("stat", path));
			break;
		default:
			sieve_storage_set_critical(storage,
				"Failed to open sieve sequence: "
				"opendir(%s) failed: %m", path);
			break;
		}
		return -1;
	}

	/* Read and sort script files */
	for (;;) {
		const char *const *files;
		unsigned int count, i;
		const char *file;
		struct dirent *dp;
		struct stat st;

		errno = 0;
		if ( (dp=readdir(dirp)) == NULL )
			break;

		if ( !sieve_script_file_has_extension(dp->d_name) )
			continue;

		file = NULL;
		T_BEGIN {
			if ( path[strlen(path)-1] == '/' )
				file = t_strconcat(path, dp->d_name, NULL);
			else
				file = t_strconcat(path, "/", dp->d_name, NULL);

			if ( stat(file, &st) == 0 && S_ISREG(st.st_mode) )
				file = p_strdup(fseq->pool, dp->d_name);
			else
				file = NULL;
		} T_END;

		if (file == NULL)
			continue;
		
		/* Insert into sorted array */
		files = array_get(&fseq->script_files, &count);
		for ( i = 0; i < count; i++ ) {
			if ( strcmp(file, files[i]) < 0 )
				break;
		}

		if ( i == count )
			array_append(&fseq->script_files, &file, 1);
		else
			array_insert(&fseq->script_files, i, &file, 1);
	} 

	if ( errno != 0 ) {
		sieve_storage_set_critical(storage,
			"Failed to read sequence directory: "
			"readdir(%s) failed: %m", path);
		ret = -1;
	}

	/* Close the directory */
	if ( dirp != NULL && closedir(dirp) < 0 ) {
		sieve_storage_sys_error(storage,
			"Failed to close sequence directory: "
			"closedir(%s) failed: %m", path);
	}
	return ret;
}
static int
cmd_dsync_run_local(struct dsync_cmd_context *ctx, struct mail_user *user,
		    struct dsync_brain *brain, struct dsync_ibc *ibc2,
		    bool *changes_during_sync_r)
{
	struct dsync_brain *brain2;
	struct mail_user *user2;
	struct setting_parser_context *set_parser;
	const char *set_line, *location;
	bool brain1_running, brain2_running, changed1, changed2;
	int ret;

	if (ctx->local_location_from_arg)
		location = ctx->ctx.args[0];
	else {
		i_assert(ctx->local_location != NULL);
		location = ctx->local_location;
	}

	i_set_failure_prefix("dsync(%s): ", user->username);

	/* update mail_location and create another user for the
	   second location. */
	set_parser = mail_storage_service_user_get_settings_parser(ctx->ctx.cur_service_user);
	set_line = t_strconcat("mail_location=", location, NULL);
	if (settings_parse_line(set_parser, set_line) < 0)
		i_unreached();
	ret = mail_storage_service_next(ctx->ctx.storage_service,
					ctx->ctx.cur_service_user, &user2);
	if (ret < 0) {
		ctx->ctx.exit_code = ret == -1 ? EX_TEMPFAIL : EX_CONFIG;
		return -1;
	}
	doveadm_user_init_dsync(user2);

	if (mail_namespaces_get_root_sep(user->namespaces) !=
	    mail_namespaces_get_root_sep(user2->namespaces)) {
		i_error("Mail locations must use the same "
			"virtual mailbox hierarchy separator "
			"(specify separator for the default namespace)");
		ctx->ctx.exit_code = EX_CONFIG;
		mail_user_unref(&user2);
		return -1;
	}
	if (paths_are_equal(user, user2, MAILBOX_LIST_PATH_TYPE_MAILBOX) &&
	    paths_are_equal(user, user2, MAILBOX_LIST_PATH_TYPE_INDEX)) {
		i_error("Both source and destination mail_location "
			"points to same directory: %s",
			mailbox_list_get_root_forced(user->namespaces->list,
						     MAILBOX_LIST_PATH_TYPE_MAILBOX));
		ctx->ctx.exit_code = EX_CONFIG;
		mail_user_unref(&user2);
		return -1;
	}

	brain2 = dsync_brain_slave_init(user2, ibc2, TRUE);

	brain1_running = brain2_running = TRUE;
	changed1 = changed2 = TRUE;
	while (brain1_running || brain2_running) {
		if (dsync_brain_has_failed(brain) ||
		    dsync_brain_has_failed(brain2))
			break;

		i_assert(changed1 || changed2);
		brain1_running = dsync_brain_run(brain, &changed1);
		brain2_running = dsync_brain_run(brain2, &changed2);
	}
	mail_user_unref(&user2);
	*changes_during_sync_r = dsync_brain_has_unexpected_changes(brain2);
	if (dsync_brain_deinit(&brain2) < 0) {
		ctx->ctx.exit_code = EX_TEMPFAIL;
		return -1;
	}
	return 0;
}
int sieve_file_storage_quota_havespace
(struct sieve_storage *storage, const char *scriptname, size_t size,
	enum sieve_storage_quota *quota_r, uint64_t *limit_r)
{
	struct sieve_file_storage *fstorage =
		(struct sieve_file_storage *)storage;
	struct dirent *dp;
	DIR *dirp;
	uint64_t script_count = 1;
	uint64_t script_storage = size;
	int result = 1;

	/* Open the directory */
	if ( (dirp = opendir(fstorage->path)) == NULL ) {
		sieve_storage_set_critical(storage,
			"quota: opendir(%s) failed: %m", fstorage->path);
		return -1;
	}

	/* Scan all files */
	for (;;) {
		const char *name;
		bool replaced = FALSE;

		/* Read next entry */
		errno = 0;
		if ( (dp = readdir(dirp)) == NULL ) {
			if ( errno != 0 ) {
				sieve_storage_set_critical(storage,
					"quota: readdir(%s) failed: %m", fstorage->path);
				result = -1;
			}
			break;
		}

		/* Parse filename */
		name = sieve_script_file_get_scriptname(dp->d_name);

		/* Ignore non-script files */
		if ( name == NULL )
			continue;

		/* Don't list our active sieve script link if the link
		 * resides in the script dir (generally a bad idea).
		 */
		i_assert( fstorage->link_path != NULL );
		if ( *(fstorage->link_path) == '\0' &&
			strcmp(fstorage->active_fname, dp->d_name) == 0 )
			continue;

		if ( strcmp(name, scriptname) == 0 )
			replaced = TRUE;

		/* Check count quota if necessary */
		if ( storage->max_scripts > 0 ) {
			if ( !replaced ) {
				script_count++;

				if ( script_count > storage->max_scripts ) {
					*quota_r = SIEVE_STORAGE_QUOTA_MAXSCRIPTS;
					*limit_r = storage->max_scripts;
					result = 0;
					break;
				}
			}
		}

		/* Check storage quota if necessary */
		if ( storage->max_storage > 0 ) {
			const char *path;
			struct stat st;

			path = t_strconcat(fstorage->path, "/", dp->d_name, NULL);

			if ( stat(path, &st) < 0 ) {
				sieve_storage_sys_warning(storage,
					"quota: stat(%s) failed: %m", path);
				continue;
			}

			if ( !replaced ) {
				script_storage += st.st_size;

				if ( script_storage > storage->max_storage ) {
					*quota_r = SIEVE_STORAGE_QUOTA_MAXSTORAGE;
					*limit_r = storage->max_storage;
					result = 0;
					break;
				}
			}
		}
	}

	/* Close directory */
	if ( closedir(dirp) < 0 ) {
		sieve_storage_set_critical(storage,
			"quota: closedir(%s) failed: %m", fstorage->path);
	}
	return result;
}
Example #9
0
static int sis_try_deduplicate(const char *rootdir, const char *fname)
{
	const char *p, *hash, *hashdir, *path, *hashes_dir, *hashes_path;
	struct stat st;
	ino_t inode;
	int ret;

	/* fname should be in <hash>-<guid> format */
	p = strchr(fname, '-');
	i_assert(p != NULL);

	hash = t_strdup_until(fname, p);
	hashdir = sis_get_dir(rootdir, hash);
	path = t_strdup_printf("%s/%s", hashdir, fname);

	hashes_dir = t_strconcat(hashdir, "/", HASH_DIR_NAME, NULL);
	hashes_path = t_strconcat(hashes_dir, "/", hash, NULL);
	if (link(path, hashes_path) == 0) {
		/* first file with this hash. we're done */
		return 0;
	}
	if (errno == ENOENT) {
		/* either path was already deleted or hashes dir
		   doesn't exist */
		if (mkdir(hashes_dir, 0700) < 0) {
			if (errno == EEXIST)
				return 0;
			i_error("mkdir(%s) failed: %m", hashes_dir);
			return -1;
		}
		/* try again */
		if (link(path, hashes_path) == 0 || errno == ENOENT)
			return 0;
	}
	if (errno != EEXIST) {
		i_error("link(%s, %s) failed: %m", path, hashes_path);
		return -1;
	}

	/* need to do a byte-by-byte comparison. but check first if someone
	   else already had deduplicated the file. */
	if (stat(path, &st) < 0) {
		if (errno == ENOENT) {
			/* just got deleted */
			return 0;
		}
		i_error("stat(%s) failed: %m", path);
		return -1;
	}
	if (st.st_nlink > 1) {
		/* already deduplicated */
		return 0;
	}

	ret = file_contents_equal(path, hashes_path, &inode);
	if (ret < 0) {
		if (errno == ENOENT) {
			/* either path or hashes_path was deleted. */
			return sis_try_deduplicate(rootdir, fname);
		}
		return -1;
	}
	if (ret > 0) {
		/* equal, replace with hard link */
		ret = hardlink_replace(hashes_path, path, inode);
		if (ret > 0)
			return 0;
		else if (ret < 0)
			return -1;
		/* too many hard links or inode changed */
	}

	/* replace hashes link with this  */
	return hardlink_replace(path, hashes_path, st.st_ino) < 0 ? -1 : 0;
}
Example #10
0
static int
maildir_rename_children(struct mailbox_list *oldlist, const char *oldname,
			struct mailbox_list *newlist, const char *newname)
{
	struct mailbox_list_iterate_context *iter;
        const struct mailbox_info *info;
	ARRAY(const char *) names_arr;
	const char *pattern, *oldpath, *newpath, *old_childname, *new_childname;
	const char *const *names, *old_vname, *new_vname;
	unsigned int i, count, old_vnamelen;
	pool_t pool;
	char old_ns_sep;
	int ret;

	ret = 0;

	/* first get the list of the children and save them to memory, because
	   we can't rely on readdir() not skipping files while the directory
	   is being modified. this doesn't protect against modifications by
	   other processes though. */
	pool = pool_alloconly_create("Maildir++ children list", 1024);
	i_array_init(&names_arr, 64);

	old_vname = mailbox_list_get_vname(oldlist, oldname);
	old_vnamelen = strlen(old_vname);

	new_vname = mailbox_list_get_vname(newlist, newname);

	old_ns_sep = mail_namespace_get_sep(oldlist->ns);
	pattern = t_strdup_printf("%s%c*", old_vname, old_ns_sep);
	iter = mailbox_list_iter_init(oldlist, pattern,
				      MAILBOX_LIST_ITER_RETURN_NO_FLAGS |
				      MAILBOX_LIST_ITER_RAW_LIST);
	while ((info = mailbox_list_iter_next(iter)) != NULL) {
		const char *name;

		/* verify that the prefix matches, otherwise we could have
		   problems with mailbox names containing '%' and '*' chars */
		if (strncmp(info->vname, old_vname, old_vnamelen) == 0 &&
		    info->vname[old_vnamelen] == old_ns_sep) {
			name = p_strdup(pool, info->vname + old_vnamelen);
			array_append(&names_arr, &name, 1);
		}
	}
	if (mailbox_list_iter_deinit(&iter) < 0) {
		ret = -1;
		names = NULL; count = 0;
	} else {
		names = array_get(&names_arr, &count);
	}

	for (i = 0; i < count; i++) {
		old_childname = mailbox_list_get_storage_name(oldlist,
					t_strconcat(old_vname, names[i], NULL));
		if (strcmp(old_childname, new_vname) == 0) {
			/* When doing RENAME "a" "a.b" we see "a.b" here.
			   We don't want to rename it anymore to "a.b.b". */
			continue;
		}

		new_childname = mailbox_list_get_storage_name(newlist,
					t_strconcat(new_vname, names[i], NULL));
		if (mailbox_list_get_path(oldlist, old_childname,
					  MAILBOX_LIST_PATH_TYPE_MAILBOX,
					  &oldpath) <= 0 ||
		    mailbox_list_get_path(newlist, new_childname,
					  MAILBOX_LIST_PATH_TYPE_MAILBOX,
					  &newpath) <= 0)
			i_unreached();

		/* FIXME: it's possible to merge two mailboxes if either one of
		   them doesn't have existing root mailbox. We could check this
		   but I'm not sure if it's worth it. It could be even
		   considered as a feature.

		   Anyway, the bug with merging is that if both mailboxes have
		   identically named child mailbox they conflict. Just ignore
		   those and leave them under the old mailbox. */
		if (rename(oldpath, newpath) == 0 || EDESTDIREXISTS(errno))
			ret = 1;
		else {
			mailbox_list_set_critical(oldlist,
				"rename(%s, %s) failed: %m", oldpath, newpath);
			ret = -1;
			break;
		}

		(void)rename_dir(oldlist, old_childname, newlist, new_childname,
				 MAILBOX_LIST_PATH_TYPE_CONTROL);
		(void)rename_dir(oldlist, old_childname, newlist, new_childname,
				 MAILBOX_LIST_PATH_TYPE_INDEX);
	}
	array_free(&names_arr);
	pool_unref(&pool);

	return ret;
}
Example #11
0
static int
mail_transaction_log_file_create2(struct mail_transaction_log_file *file,
				  int new_fd, bool reset,
				  struct dotlock **dotlock)
{
	struct mail_index *index = file->log->index;
	struct stat st;
	const char *path2;
	buffer_t *writebuf;
	int fd, ret;
	bool rename_existing, need_lock;

	need_lock = file->log->head != NULL && file->log->head->locked;

	if (fcntl(new_fd, F_SETFL, O_APPEND) < 0) {
		log_file_set_syscall_error(file, "fcntl(O_APPEND)");
		return -1;
	}

	if (file->log->nfs_flush) {
		/* although we check also mtime and file size below, it's done
		   only to fix broken log files. we don't bother flushing
		   attribute cache just for that. */
		nfs_flush_file_handle_cache(file->filepath);
	}

	/* log creation is locked now - see if someone already created it.
	   note that if we're rotating, we need to keep the log locked until
	   the file has been rewritten. and because fcntl() locks are stupid,
	   if we go and open()+close() the file and we had it already opened,
	   its locks are lost. so we use stat() to check if the file has been
	   recreated, although it almost never is. */
	if (reset)
		rename_existing = FALSE;
	else if (nfs_safe_stat(file->filepath, &st) < 0) {
		if (errno != ENOENT) {
			log_file_set_syscall_error(file, "stat()");
			return -1;
		}
		rename_existing = FALSE;
	} else if (st.st_ino == file->st_ino &&
		   CMP_DEV_T(st.st_dev, file->st_dev) &&
		   /* inode/dev checks are enough when we're rotating the file,
		      but not when we're replacing a broken log file */
		   st.st_mtime == file->last_mtime &&
		   (uoff_t)st.st_size == file->last_size) {
		/* no-one else recreated the file */
		rename_existing = TRUE;
	} else {
		/* recreated. use the file if its header is ok */
		fd = nfs_safe_open(file->filepath, O_RDWR | O_APPEND);
		if (fd == -1) {
			if (errno != ENOENT) {
				log_file_set_syscall_error(file, "open()");
				return -1;
			}
		} else {
			file->fd = fd;
			file->last_size = 0;
			if (mail_transaction_log_file_read_hdr(file,
							       FALSE) > 0 &&
			    mail_transaction_log_file_stat(file, FALSE) == 0) {
				/* yes, it was ok */
				file_dotlock_delete(dotlock);
				mail_transaction_log_file_add_to_list(file);
				return 0;
			}
			file->fd = -1;
			if (close(fd) < 0)
				log_file_set_syscall_error(file, "close()");
		}
		rename_existing = FALSE;
	}

	if (index->fd == -1 && !rename_existing) {
		/* creating the initial index */
		reset = TRUE;
	}

	if (mail_transaction_log_init_hdr(file->log, &file->hdr) < 0)
		return -1;

	if (reset) {
		/* don't reset modseqs. if we're reseting due to rebuilding
		   indexes we'll probably want to keep uidvalidity and in such
		   cases we really don't want to shrink modseqs. */
		file->hdr.prev_file_seq = 0;
		file->hdr.prev_file_offset = 0;
	}

	writebuf = buffer_create_dynamic(pool_datastack_create(), 128);
	buffer_append(writebuf, &file->hdr, sizeof(file->hdr));

	if (index->ext_hdr_init_data != NULL && reset)
		log_write_ext_hdr_init_data(index, writebuf);
	if (write_full(new_fd, writebuf->data, writebuf->used) < 0) {
		log_file_set_syscall_error(file, "write_full()");
		return -1;
	}

	if (file->log->index->fsync_mode == FSYNC_MODE_ALWAYS) {
		/* the header isn't important, so don't bother calling
		   fdatasync() unless it's required */
		if (fdatasync(new_fd) < 0) {
			log_file_set_syscall_error(file, "fdatasync()");
			return -1;
		}
	}

	file->fd = new_fd;
	ret = mail_transaction_log_file_stat(file, FALSE);

	if (need_lock) {
		/* we'll need to preserve the lock */
		if (mail_transaction_log_file_lock(file) < 0)
			ret = -1;
	}

	/* if we return -1 the dotlock deletion code closes the fd */
	file->fd = -1;
	if (ret < 0)
		return -1;

	/* keep two log files */
	if (rename_existing) {
		/* rename() would be nice and easy way to do this, except then
		   there's a race condition between the rename and
		   file_dotlock_replace(). during that time the log file
		   doesn't exist, which could cause problems. */
		path2 = t_strconcat(file->filepath, ".2", NULL);
		if (i_unlink_if_exists(path2) < 0) {
			/* try to link() anyway */
		}
		if (nfs_safe_link(file->filepath, path2, FALSE) < 0 &&
		    errno != ENOENT && errno != EEXIST) {
                        mail_index_set_error(index, "link(%s, %s) failed: %m",
					     file->filepath, path2);
			/* ignore the error. we don't care that much about the
			   second log file and we're going to overwrite this
			   first one. */
		}
		/* NOTE: here's a race condition where both .log and .log.2
		   point to the same file. our reading code should ignore that
		   though by comparing the inodes. */
	}

	if (file_dotlock_replace(dotlock,
				 DOTLOCK_REPLACE_FLAG_DONT_CLOSE_FD) <= 0)
		return -1;

	/* success */
	file->fd = new_fd;
	mail_transaction_log_file_add_to_list(file);

	i_assert(!need_lock || file->locked);
	return 1;
}
Example #12
0
static uint32_t
mailbox_uidvalidity_next_rescan(struct mailbox_list *list, const char *path)
{
	DIR *d;
	struct dirent *dp;
	const char *fname, *dir, *prefix, *tmp;
	char *endp;
	unsigned int i, prefix_len;
	uint32_t cur_value, min_value, max_value;
	mode_t old_mask;
	int fd;

	fname = strrchr(path, '/');
	if (fname == NULL) {
		dir = ".";
		fname = path;
	} else {
		dir = t_strdup_until(path, fname);
		fname++;
	}

	d = opendir(dir);
	if (d == NULL && errno == ENOENT) {
		/* FIXME: the PATH_TYPE_CONTROL should come as a parameter, but
		   that's an API change, do it in v2.3. it's not really a
		   problem though, since currently all backends use control
		   dirs for the uidvalidity file. */
		(void)mailbox_list_mkdir_root(list, dir, MAILBOX_LIST_PATH_TYPE_CONTROL);
		d = opendir(dir);
	}
	if (d == NULL) {
		i_error("opendir(%s) failed: %m", dir);
		return mailbox_uidvalidity_next_fallback();
	}
	prefix = t_strconcat(fname, ".", NULL);
	prefix_len = strlen(prefix);

	/* just in case there happens to be multiple matching uidvalidity
	   files, track the min/max values. use the max value and delete the
	   min value file. */
	max_value = 0; min_value = (uint32_t)-1;
	while ((dp = readdir(d)) != NULL) {
		if (strncmp(dp->d_name, prefix, prefix_len) == 0) {
			cur_value = strtoul(dp->d_name + prefix_len, &endp, 16);
			if (*endp == '\0') {
				if (min_value > cur_value)
					min_value = cur_value;
				if (max_value < cur_value)
					max_value = cur_value;
			}
		}
	}
	if (closedir(d) < 0)
		i_error("closedir(%s) failed: %m", dir);

	if (max_value == 0) {
		/* no uidvalidity files. create one. */
		for (i = 0; i < RETRY_COUNT; i++) {
			cur_value = mailbox_uidvalidity_next_fallback();
			tmp = t_strdup_printf("%s.%08x", path, cur_value);
			/* the file is empty, don't bother with permissions */
			old_mask = umask(0);
			fd = open(tmp, O_RDWR | O_CREAT | O_EXCL, 0444);
			umask(old_mask);
			if (fd != -1 || errno != EEXIST)
				break;
			/* already exists. although it's quite unlikely we'll
			   hit this race condition. more likely we'll create
			   a duplicate file.. */
		}
		if (fd == -1) {
			i_error("creat(%s) failed: %m", tmp);
			return cur_value;
		}
		i_close_fd(&fd);
		mailbox_uidvalidity_write(list, path, cur_value);
		return cur_value;
	}
	if (min_value != max_value) {
		/* duplicate uidvalidity files, delete the oldest */
		tmp = t_strdup_printf("%s.%08x", path, min_value);
		if (unlink(tmp) < 0 && errno != ENOENT)
			i_error("unlink(%s) failed: %m", tmp);
	}

	cur_value = max_value;
	if (mailbox_uidvalidity_rename(path, &cur_value, TRUE) < 0)
		return mailbox_uidvalidity_next_fallback();
	mailbox_uidvalidity_write(list, path, cur_value);
	return cur_value;
}
Example #13
0
int dbox_file_fix(struct dbox_file *file, uoff_t start_offset)
{
	struct ostream *output;
	const char *dir, *p, *temp_path, *broken_path;
	bool deleted;
	int fd, ret;

	i_assert(dbox_file_is_open(file));

	p = strrchr(file->cur_path, '/');
	i_assert(p != NULL);
	dir = t_strdup_until(file->cur_path, p);

	temp_path = t_strdup_printf("%s/%s", dir, dbox_generate_tmp_filename());
	fd = file->storage->v.file_create_fd(file, temp_path, FALSE);
	if (fd == -1)
		return -1;

	output = o_stream_create_fd_file(fd, 0, FALSE);
	ret = dbox_file_fix_write_stream(file, start_offset, temp_path, output);
	o_stream_unref(&output);
	if (close(fd) < 0) {
		mail_storage_set_critical(&file->storage->storage,
					  "close(%s) failed: %m", temp_path);
		ret = -1;
	}
	if (ret < 0) {
		if (unlink(temp_path) < 0) {
			mail_storage_set_critical(&file->storage->storage,
				"unlink(%s) failed: %m", temp_path);
		}
		return -1;
	}
	/* keep a copy of the original file in case someone wants to look
	   at it */
	broken_path = t_strconcat(file->cur_path,
				  DBOX_MAIL_FILE_BROKEN_COPY_SUFFIX, NULL);
	if (link(file->cur_path, broken_path) < 0) {
		mail_storage_set_critical(&file->storage->storage,
					  "link(%s, %s) failed: %m",
					  file->cur_path, broken_path);
	} else {
		i_warning("dbox: Copy of the broken file saved to %s",
			  broken_path);
	}
	if (rename(temp_path, file->cur_path) < 0) {
		mail_storage_set_critical(&file->storage->storage,
					  "rename(%s, %s) failed: %m",
					  temp_path, file->cur_path);
		return -1;
	}

	/* file was successfully recreated - reopen it */
	dbox_file_close(file);
	if (dbox_file_open(file, &deleted) <= 0) {
		mail_storage_set_critical(&file->storage->storage,
			"dbox_file_fix(%s): reopening file failed",
			file->cur_path);
		return -1;
	}
	return 0;
}
Example #14
0
static int
doveadm_mail_cmd_server_parse(const struct doveadm_mail_cmd *cmd,
			      const struct doveadm_settings *set,
			      const struct mail_storage_service_input *input,
			      int argc, char *argv[],
			      struct doveadm_mail_cmd_context **ctx_r)
{
	struct doveadm_mail_cmd_context *ctx;
	const char *getopt_args;
	bool add_username_header = FALSE;
	int c;

	ctx = doveadm_mail_cmd_init(cmd, set);
	ctx->full_args = (const void *)(argv + 1);
	ctx->proxying = TRUE;

	ctx->service_flags |=
		MAIL_STORAGE_SERVICE_FLAG_NO_LOG_INIT |
		MAIL_STORAGE_SERVICE_FLAG_USERDB_LOOKUP;
	if (doveadm_debug)
		ctx->service_flags |= MAIL_STORAGE_SERVICE_FLAG_DEBUG;

	i_getopt_reset();
	getopt_args = t_strconcat("AF:S:u:", ctx->getopt_args, NULL);
	while ((c = getopt(argc, argv, getopt_args)) > 0) {
		switch (c) {
		case 'A':
		case 'F':
			add_username_header = TRUE;
			break;
		case 'S':
			/* ignore */
			break;
		case 'u':
			if (strchr(optarg, '*') != NULL ||
			    strchr(optarg, '?') != NULL)
				add_username_header = TRUE;
			break;
		default:
			if ((ctx->v.parse_arg == NULL ||
			     !ctx->v.parse_arg(ctx, c))) {
				i_error("doveadm %s: "
					"Client sent unknown parameter: %c",
					cmd->name, c);
				ctx->v.deinit(ctx);
				pool_unref(&ctx->pool);
				return -1;
			}
		}
	}
	argv += optind;

	if (argv[0] != NULL && cmd->usage_args == NULL) {
		i_error("doveadm %s: Client sent unknown parameter: %s",
			cmd->name, argv[0]);
		ctx->v.deinit(ctx);
		pool_unref(&ctx->pool);
		return -1;
	}
	ctx->args = (const void *)argv;

	if (doveadm_print_is_initialized() && add_username_header) {
		doveadm_print_header("username", "Username",
				     DOVEADM_PRINT_HEADER_FLAG_STICKY |
				     DOVEADM_PRINT_HEADER_FLAG_HIDE_TITLE);
		doveadm_print_sticky("username", input->username);
	}
	*ctx_r = ctx;
	return 0;
}
Example #15
0
passwd_file_add(struct passwd_file *pw, const char *username,
		const char *pass, const char *const *args)
{
	/* args = uid, gid, user info, home dir, shell, extra_fields */
	struct passwd_user *pu;
	const char *extra_fields = NULL;
	char *user;
	size_t len;

	if (hash_table_lookup(pw->users, username) != NULL) {
		i_error("passwd-file %s: User %s exists more than once",
			pw->path, username);
		return;
	}

	pu = p_new(pw->pool, struct passwd_user, 1);
	user = p_strdup(pw->pool, username);

	len = pass == NULL ? 0 : strlen(pass);
	if (len > 4 && pass[0] != '{' && pass[0] != '$' &&
	    pass[len-1] == ']' && pass[len-4] == '[') {
		/* password[type] - we're being libpam-pwdfile compatible
		   here. it uses 13 = DES and 34 = MD5. For backwards
		   comaptibility with ourself, we have also 56 = Digest-MD5. */
		int num = (pass[len-3] - '0') * 10 + (pass[len-2] - '0');

		pass = t_strndup(pass, len-4);
		if (num == 34) {
			pu->password = p_strconcat(pw->pool, "{PLAIN-MD5}",
						   pass, NULL);
		} else if (num == 56) {
			pu->password = p_strconcat(pw->pool, "{DIGEST-MD5}",
						   pass, NULL);
			if (strlen(pu->password) != 32 + 12) {
				i_error("passwd-file %s: User %s "
					"has invalid password",
					pw->path, username);
				return;
			}
		} else {
			pu->password = p_strconcat(pw->pool, "{CRYPT}",
						   pass, NULL);
		}
	} else {
		pu->password = p_strdup(pw->pool, pass);
	}

	pu->uid = (uid_t)-1;
	pu->gid = (gid_t)-1;

	if (*args == NULL)
		;
	else if (!pw->db->userdb || **args == '\0') {
		args++;
	} else {
		pu->uid = userdb_parse_uid(NULL, *args);
		if (pu->uid == 0 || pu->uid == (uid_t)-1) {
			i_error("passwd-file %s: User %s has invalid UID '%s'",
				pw->path, username, *args);
			return;
		}
		args++;
	}

	if (*args == NULL) {
		if (pw->db->userdb_warn_missing) {
			i_error("passwd-file %s: User %s is missing "
				"userdb info", pw->path, username);
		}
		/* don't allow userdb lookups */
		pu->uid = 0;
		pu->gid = 0;
	} else if (!pw->db->userdb || **args == '\0')
		args++;
	else {
		pu->gid = userdb_parse_gid(NULL, *args);
		if (pu->gid == 0 || pu->gid == (gid_t)-1) {
			i_error("passwd-file %s: User %s has invalid GID '%s'",
				pw->path, username, *args);
			return;
		}
		args++;
	}

	/* user info */
	if (*args != NULL)
		args++;

	/* home */
	if (*args != NULL) {
		if (pw->db->userdb)
			pu->home = p_strdup_empty(pw->pool, *args);
		args++;
	}

	/* shell */
	if (*args != NULL)
		args++;

	if (*args != NULL && **args == '\0') {
		/* old format, this field is empty and next field may
		   contain MAIL */
		args++;
		if (*args != NULL && **args != '\0' && pw->db->userdb) {
			extra_fields =
                                t_strconcat("userdb_mail=",
                                            t_strarray_join(args, ":"), NULL);
		}
	} else if (*args != NULL) {
		/* new format, contains a space separated list of
		   extra fields */
                extra_fields = t_strarray_join(args, ":");
        }

        if (extra_fields != NULL) {
                pu->extra_fields =
                        p_strsplit_spaces(pw->pool, extra_fields, " ");
        }

	hash_table_insert(pw->users, user, pu);
}
Example #16
0
static int
shared_storage_create(struct mail_storage *_storage, struct mail_namespace *ns,
		      const char **error_r)
{
	struct shared_storage *storage = (struct shared_storage *)_storage;
	struct mail_storage *storage_class;
	const char *driver, *p;
	char *wildcardp, key;
	bool have_username;

	/* location must begin with the actual mailbox driver */
	p = strchr(ns->set->location, ':');
	if (p == NULL) {
		*error_r = "Shared mailbox location not prefixed with driver";
		return -1;
	}
	driver = t_strdup_until(ns->set->location, p);
	storage->location = p_strdup(_storage->pool, ns->set->location);
	storage->unexpanded_location =
		p_strdup(_storage->pool, ns->unexpanded_set->location);
	storage->storage_class_name = p_strdup(_storage->pool, driver);

	storage_class = mail_user_get_storage_class(_storage->user, driver);
	if (storage_class != NULL)
		_storage->class_flags = storage_class->class_flags;
	else if (strcmp(driver, "auto") != 0) {
		*error_r = t_strconcat("Unknown shared storage driver: ",
				       driver, NULL);
		return -1;
	}

	wildcardp = strchr(ns->prefix, '%');
	if (wildcardp == NULL) {
		*error_r = "Shared namespace prefix doesn't contain %";
		return -1;
	}
	storage->ns_prefix_pattern = p_strdup(_storage->pool, wildcardp);

	have_username = FALSE;
	for (p = storage->ns_prefix_pattern; *p != '\0'; p++) {
		if (*p != '%')
			continue;

		key = p[1];
		if (key == 'u' || key == 'n')
			have_username = TRUE;
		else if (key != '%' && key != 'd')
			break;
	}
	if (*p != '\0') {
		*error_r = "Shared namespace prefix contains unknown variables";
		return -1;
	}
	if (!have_username) {
		*error_r = "Shared namespace prefix doesn't contain %u or %n";
		return -1;
	}
	if (p[-1] != mail_namespace_get_sep(ns) &&
	    (ns->flags & (NAMESPACE_FLAG_LIST_PREFIX |
			  NAMESPACE_FLAG_LIST_CHILDREN)) != 0) {
		*error_r = "Shared namespace prefix doesn't end with hierarchy separator";
		return -1;
	}

	/* truncate prefix after the above checks are done, so they can log
	   the full prefix in error conditions */
	*wildcardp = '\0';
	ns->prefix_len = strlen(ns->prefix);
	return 0;
}
Example #17
0
static void
ssl_params_if_unchanged(const char *path, time_t mtime,
			unsigned int ssl_dh_parameters_length ATTR_UNUSED)
{
	const char *temp_path;
	struct file_lock *lock;
	struct stat st, st2;
	mode_t old_mask;
	int fd, ret;

#ifdef HAVE_SETPRIORITY
	if (setpriority(PRIO_PROCESS, 0, SSL_PARAMS_PRIORITY) < 0)
		i_error("setpriority(%d) failed: %m", SSL_PARAMS_PRIORITY);
#endif

	temp_path = t_strconcat(path, ".tmp", NULL);

	old_mask = umask(0);
	fd = open(temp_path, O_WRONLY | O_CREAT, 0644);
	umask(old_mask);

	if (fd == -1)
		i_fatal("creat(%s) failed: %m", temp_path);

	/* If multiple dovecot instances are running, only one of them needs
	   to regenerate this file. */
	ret = file_wait_lock(fd, temp_path, F_WRLCK,
			     FILE_LOCK_METHOD_FCNTL,
			     SSL_BUILD_PARAM_TIMEOUT_SECS, &lock);
	if (ret < 0)
		i_fatal("file_try_lock(%s) failed: %m", temp_path);
	if (ret == 0) {
		/* someone else is writing this */
		i_fatal("Timeout while waiting for %s generation to complete",
			path);
	}

	/* make sure the .tmp file is still the one we created */
	if (fstat(fd, &st) < 0)
		i_fatal("fstat(%s) failed: %m", temp_path);
	if (stat(temp_path, &st2) < 0) {
		if (errno != ENOENT)
			i_fatal("stat(%s) failed: %m", temp_path);
		st2.st_ino = st.st_ino+1;
	}
	if (st.st_ino != st2.st_ino) {
		/* nope. so someone else just generated the file. */
		i_close_fd(&fd);
		return;
	}

	/* check that the parameters file is still the same */
	if (stat(path, &st) == 0) {
		if (st.st_mtime != mtime) {
			i_close_fd(&fd);
			return;
		}
	} else if (errno != ENOENT)
		i_fatal("stat(%s) failed: %m", path);

	/* ok, we really want to generate it. */
	if (ftruncate(fd, 0) < 0)
		i_fatal("ftruncate(%s) failed: %m", temp_path);

	i_info("Generating SSL parameters");
#ifdef HAVE_SSL
	ssl_generate_parameters(fd, ssl_dh_parameters_length, temp_path);
#endif

	if (rename(temp_path, path) < 0)
		i_fatal("rename(%s, %s) failed: %m", temp_path, path);
	if (close(fd) < 0)
		i_fatal("close(%s) failed: %m", temp_path);
	file_lock_free(&lock);

	i_info("SSL parameters regeneration completed");
}
Example #18
0
static int
openssl_iostream_create(struct ssl_iostream_context *ctx, const char *host,
			const struct ssl_iostream_settings *set,
			struct istream **input, struct ostream **output,
			struct ssl_iostream **iostream_r,
			const char **error_r)
{
	struct ssl_iostream *ssl_io;
	SSL *ssl;
	BIO *bio_int, *bio_ext;

	ssl = SSL_new(ctx->ssl_ctx);
	if (ssl == NULL) {
		*error_r = t_strdup_printf("SSL_new() failed: %s",
					   openssl_iostream_error());
		return -1;
	}

	/* BIO pairs use default buffer sizes (17 kB in OpenSSL 0.9.8e).
	   Each of the BIOs have one "write buffer". BIO_write() copies data
	   to them, while BIO_read() reads from the other BIO's write buffer
	   into the given buffer. The bio_int is used by OpenSSL and bio_ext
	   is used by this library. */
	if (BIO_new_bio_pair(&bio_int, 0, &bio_ext, 0) != 1) {
		*error_r = t_strdup_printf("BIO_new_bio_pair() failed: %s",
					   openssl_iostream_error());
		SSL_free(ssl);
		return -1;
	}

	ssl_io = i_new(struct ssl_iostream, 1);
	ssl_io->refcount = 1;
	ssl_io->ctx = ctx;
	ssl_io->ssl = ssl;
	ssl_io->bio_ext = bio_ext;
	ssl_io->plain_input = *input;
	ssl_io->plain_output = *output;
	ssl_io->host = i_strdup(host);
	ssl_io->log_prefix = host == NULL ? i_strdup("") :
		i_strdup_printf("%s: ", host);
	/* bio_int will be freed by SSL_free() */
	SSL_set_bio(ssl_io->ssl, bio_int, bio_int);
        SSL_set_ex_data(ssl_io->ssl, dovecot_ssl_extdata_index, ssl_io);
#ifdef HAVE_SSL_GET_SERVERNAME
	SSL_set_tlsext_host_name(ssl_io->ssl, host);
#endif

	if (openssl_iostream_set(ssl_io, set, error_r) < 0) {
		openssl_iostream_free(ssl_io);
		return -1;
	}

	o_stream_uncork(ssl_io->plain_output);

	*input = openssl_i_stream_create_ssl(ssl_io);
	*output = openssl_o_stream_create_ssl(ssl_io);
	i_stream_set_name(*input, t_strconcat("SSL ",
		i_stream_get_name(ssl_io->plain_input), NULL));
	o_stream_set_name(*output, t_strconcat("SSL ",
		o_stream_get_name(ssl_io->plain_output), NULL));

	if (ssl_io->plain_output->real_stream->error_handling_disabled)
		o_stream_set_no_error_handling(*output, TRUE);

	ssl_io->ssl_output = *output;
	*iostream_r = ssl_io;
	return 0;
}
Example #19
0
static const char *log_record_type(unsigned int type)
{
	const char *name;

	switch (type & MAIL_TRANSACTION_TYPE_MASK) {
	case MAIL_TRANSACTION_EXPUNGE|MAIL_TRANSACTION_EXPUNGE_PROT:
		name = "expunge";
		break;
	case MAIL_TRANSACTION_EXPUNGE_GUID|MAIL_TRANSACTION_EXPUNGE_PROT:
		name = "expunge-guid";
		break;
	case MAIL_TRANSACTION_APPEND:
		name = "append";
		break;
	case MAIL_TRANSACTION_FLAG_UPDATE:
		name = "flag-update";
		break;
	case MAIL_TRANSACTION_HEADER_UPDATE:
		name = "header-update";
		break;
	case MAIL_TRANSACTION_EXT_INTRO:
		name = "ext-intro";
		break;
	case MAIL_TRANSACTION_EXT_RESET:
		name = "ext-reset";
		break;
	case MAIL_TRANSACTION_EXT_HDR_UPDATE:
		name = "ext-hdr";
		break;
	case MAIL_TRANSACTION_EXT_HDR_UPDATE32:
		name = "ext-hdr32";
		break;
	case MAIL_TRANSACTION_EXT_REC_UPDATE:
		name = "ext-rec";
		break;
	case MAIL_TRANSACTION_KEYWORD_UPDATE:
		name = "keyword-update";
		break;
	case MAIL_TRANSACTION_KEYWORD_RESET:
		name = "keyword-reset";
		break;
	case MAIL_TRANSACTION_EXT_ATOMIC_INC:
		name = "ext-atomic-inc";
		break;
	case MAIL_TRANSACTION_MODSEQ_UPDATE:
		name = "modseq-update";
		break;
	case MAIL_TRANSACTION_INDEX_DELETED:
		name = "index-deleted";
		break;
	case MAIL_TRANSACTION_INDEX_UNDELETED:
		name = "index-undeleted";
		break;
	case MAIL_TRANSACTION_BOUNDARY:
		name = "boundary";
		break;
	default:
		name = t_strdup_printf("unknown: %x", type);
		break;
	}

	if (type & MAIL_TRANSACTION_EXTERNAL)
		name = t_strconcat(name, " (ext)", NULL);
	return name;
}
Example #20
0
int cmd_rcpt(struct client *client, const char *args)
{
	struct mail_recipient *rcpt;
	struct mail_storage_service_input input;
	const char *params, *address, *username, *detail, *prefix;
	const char *const *argv;
	const char *error = NULL;
	char delim = '\0';
	int ret = 0;

	if (client->state.mail_from == NULL) {
		client_send_line(client, "503 5.5.1 MAIL needed first");
		return 0;
	}

	if (strncasecmp(args, "TO:", 3) != 0 ||
	    parse_address(args + 3, &address, &params) < 0) {
		client_send_line(client, "501 5.5.4 Invalid parameters");
		return 0;
	}

	rcpt = p_new(client->state_pool, struct mail_recipient, 1);
	rcpt->client = client;
	address = lmtp_unescape_address(address);

	argv = t_strsplit(params, " ");
	for (; *argv != NULL; argv++) {
		if (strncasecmp(*argv, "ORCPT=", 6) == 0) {
			rcpt->params.dsn_orcpt = parse_xtext(client, *argv + 6);
		} else {
			client_send_line(client, "501 5.5.4 Unsupported options");
			return 0;
		}
	}
	rcpt_address_parse(client, address, &username, &delim, &detail);

	client_state_set(client, "RCPT TO", address);

	if (client->lmtp_set->lmtp_proxy) {
		if (client_proxy_rcpt(client, address, username, detail, delim,
				      &rcpt->params))
			return 0;
	}

	/* Use a unique session_id for each mail delivery. This is especially
	   important for stats process to not see duplicate sessions. */
	if (array_count(&client->state.rcpt_to) == 0)
		rcpt->session_id = client->state.session_id;
	else {
		rcpt->session_id =
			p_strdup_printf(client->state_pool, "%s:%u",
					client->state.session_id,
					array_count(&client->state.rcpt_to)+1);
	}

	memset(&input, 0, sizeof(input));
	input.module = input.service = "lmtp";
	input.username = username;
	input.local_ip = client->local_ip;
	input.remote_ip = client->remote_ip;
	input.local_port = client->local_port;
	input.remote_port = client->remote_port;
	input.session_id = rcpt->session_id;

	ret = mail_storage_service_lookup(storage_service, &input,
					  &rcpt->service_user, &error);

	if (ret < 0) {
		prefix = t_strdup_printf(ERRSTR_TEMP_USERDB_FAIL_PREFIX,
					 username);
		client_send_line(client, "%s%s", prefix, error);
		return 0;
	}
	if (ret == 0) {
		client_send_line(client,
				 "550 5.1.1 <%s> User doesn't exist: %s",
				 address, username);
		return 0;
	}
	if (client->proxy != NULL) {
		/* NOTE: if this restriction is ever removed, we'll also need
		   to send different message bodies to local and proxy
		   (with and without Return-Path: header) */
		client_send_line(client, "451 4.3.0 <%s> "
			"Can't handle mixed proxy/non-proxy destinations",
			address);
		mail_storage_service_user_free(&rcpt->service_user);
		return 0;
	}

	lmtp_address_translate(client, &address);

	rcpt->address = p_strdup(client->state_pool, address);
	rcpt->detail = p_strdup(client->state_pool, detail);
	if ((ret = lmtp_rcpt_to_is_over_quota(client, rcpt)) != 0) {
		if (ret < 0) {
			client_send_line(client, ERRSTR_TEMP_MAILBOX_FAIL,
					 rcpt->address);
		}
		mail_storage_service_user_free(&rcpt->service_user);
		return 0;
	}
	array_append(&client->state.rcpt_to, &rcpt, 1);
	client_send_line(client, "250 2.1.5 OK");

	if (client->lmtp_set->lmtp_user_concurrency_limit > 0) {
		const char *query = t_strconcat("LOOKUP\t",
			master_service_get_name(master_service),
			"/", str_tabescape(username), NULL);
		client->state.anvil_queries++;
		rcpt->anvil_query = anvil_client_query(anvil, query,
					rcpt_anvil_lookup_callback, rcpt);
	}
	return 0;
}
int mailbox_list_subscriptions_refresh(struct mailbox_list *src_list,
                                       struct mailbox_list *dest_list)
{
    struct subsfile_list_context *subsfile_ctx;
    struct stat st;
    enum mailbox_list_path_type type;
    const char *path, *name;
    char sep;
    int ret;

    /* src_list is subscriptions=yes, dest_list is subscriptions=no
       (or the same as src_list) */
    i_assert((src_list->ns->flags & NAMESPACE_FLAG_SUBSCRIPTIONS) != 0);

    if (dest_list->subscriptions == NULL) {
        sep = mail_namespace_get_sep(src_list->ns);
        dest_list->subscriptions = mailbox_tree_init(sep);
    }

    type = src_list->set.control_dir != NULL ?
           MAILBOX_LIST_PATH_TYPE_CONTROL : MAILBOX_LIST_PATH_TYPE_DIR;
    if (!mailbox_list_get_root_path(src_list, type, &path) ||
            src_list->set.subscription_fname == NULL) {
        /* no subscriptions (e.g. pop3c) */
        return 0;
    }
    path = t_strconcat(path, "/", src_list->set.subscription_fname, NULL);
    if (stat(path, &st) < 0) {
        if (errno == ENOENT) {
            /* no subscriptions */
            mailbox_tree_clear(dest_list->subscriptions);
            dest_list->subscriptions_mtime = 0;
            return 0;
        }
        mailbox_list_set_critical(dest_list, "stat(%s) failed: %m",
                                  path);
        return -1;
    }
    if (st.st_mtime == dest_list->subscriptions_mtime &&
            st.st_mtime < dest_list->subscriptions_read_time-1) {
        /* we're up to date */
        return 0;
    }

    mailbox_tree_clear(dest_list->subscriptions);
    dest_list->subscriptions_read_time = ioloop_time;

    subsfile_ctx = subsfile_list_init(dest_list, path);
    if (subsfile_list_fstat(subsfile_ctx, &st) == 0)
        dest_list->subscriptions_mtime = st.st_mtime;
    while ((name = subsfile_list_next(subsfile_ctx)) != NULL) T_BEGIN {
        T_BEGIN {
            ret = mailbox_list_subscription_fill_one(dest_list,
            src_list, name);
        } T_END;
        if (ret < 0) {
            i_warning("Subscriptions file %s: "
            "Removing invalid entry: %s",
            path, name);
            (void)subsfile_set_subscribed(src_list, path,
            mailbox_list_get_temp_prefix(src_list),
            name, FALSE);

        }
    } T_END;

    if (subsfile_list_deinit(&subsfile_ctx) < 0) {
        dest_list->subscriptions_mtime = (time_t)-1;
        return -1;
    }
    return 0;
}
Example #22
0
struct client *client_create(int fd_in, int fd_out,
			     const char *session_id, struct mail_user *user,
			     struct mail_storage_service_user *service_user,
			     const struct submission_settings *set,
			     const char *helo,
			     const unsigned char *pdata, unsigned int pdata_len)
{
	const struct mail_storage_settings *mail_set;
	struct smtp_server_settings smtp_set;
	const char *ident;
	struct client *client;

	/* always use nonblocking I/O */
	net_set_nonblock(fd_in, TRUE);
	net_set_nonblock(fd_out, TRUE);

	client = i_new(struct client, 1);
	client->user = user;
	client->service_user = service_user;
	client->set = set;
	client->session_id = i_strdup(session_id);

	i_zero(&smtp_set);
	smtp_set.hostname = set->hostname;
	smtp_set.login_greeting = set->login_greeting;
	smtp_set.max_recipients = set->submission_max_recipients;
	smtp_set.max_client_idle_time_msecs = CLIENT_IDLE_TIMEOUT_MSECS;
	smtp_set.debug = user->mail_debug;

	client->conn = smtp_server_connection_create(smtp_server,
		fd_in, fd_out, user->conn.remote_ip, user->conn.remote_port,
		FALSE, &smtp_set, &smtp_callbacks, client);

	client_proxy_create(client, set);

	smtp_server_connection_login(client->conn,
		client->user->username, helo,
		pdata, pdata_len, user->conn.ssl_secured);
	smtp_server_connection_start_pending(client->conn);

	mail_set = mail_user_set_get_storage_set(user);
	if (*set->imap_urlauth_host != '\0' &&
	    *mail_set->mail_attribute_dict != '\0') {
		/* Enable BURL capability only when urlauth dict is
		   configured correctly */
		client_init_urlauth(client);
	}

	submission_client_count++;
	DLLIST_PREPEND(&submission_clients, client);

	ident = mail_user_get_anvil_userip_ident(client->user);
	if (ident != NULL) {
		master_service_anvil_send(master_service, t_strconcat(
			"CONNECT\t", my_pid, "\tsubmission/",
			ident, "\n", NULL));
		client->anvil_sent = TRUE;
	}

	if (hook_client_created != NULL)
		hook_client_created(&client);

	submission_refresh_proctitle();
	return client;
}
static int
mailbox_list_subscription_fill_one(struct mailbox_list *list,
                                   struct mailbox_list *src_list,
                                   const char *name)
{
    struct mail_namespace *ns, *default_ns = list->ns;
    struct mail_namespace *namespaces = default_ns->user->namespaces;
    struct mailbox_node *node;
    const char *vname, *ns_name, *error;
    unsigned int len;
    bool created;

    /* default_ns is whatever namespace we're currently listing.
       if we have e.g. prefix="" and prefix=pub/ namespaces with
       pub/ namespace having subscriptions=no, we want to:

       1) when listing "" namespace we want to skip over any names
       that begin with pub/. */
    if (src_list->ns->prefix_len == 0)
        ns_name = name;
    else {
        /* we could have two-level namespace: ns/ns2/ */
        ns_name = t_strconcat(src_list->ns->prefix, name, NULL);
    }
    ns = mail_namespace_find_unsubscribable(namespaces, ns_name);
    if (ns != NULL && ns != default_ns) {
        if (ns->prefix_len > 0)
            return 0;
        /* prefix="" namespace=no : catching this is basically the
           same as not finding any namespace. */
        ns = NULL;
    }

    /* 2) when listing pub/ namespace, skip over entries that don't
       begin with pub/. */
    if (ns == NULL &&
            (default_ns->flags & NAMESPACE_FLAG_SUBSCRIPTIONS) == 0)
        return 0;

    /* When listing shared namespace's subscriptions, we need to
       autocreate all the visible child namespaces. their subscriptions
       are listed later. */
    if (ns != NULL && ns->type == MAIL_NAMESPACE_TYPE_SHARED &&
            (ns->flags & NAMESPACE_FLAG_AUTOCREATED) == 0) {
        /* we'll need to get the namespace autocreated.
           one easy way is to just ask to join a reference and
           pattern */
        (void)mailbox_list_join_refpattern(ns->list, ns_name, "");
    }

    /* When listing pub/ namespace, skip over the namespace
       prefix in the name. the rest of the name is storage_name. */
    if (ns == NULL)
        ns = default_ns;
    else if (strncmp(ns_name, ns->prefix, ns->prefix_len) == 0) {
        ns_name += ns->prefix_len;
        name = ns_name;
    } else {
        /* "pub" entry - this shouldn't be possible normally, because
           it should be saved as "pub/", but handle it anyway */
        i_assert(strncmp(ns_name, ns->prefix, ns->prefix_len-1) == 0 &&
                 ns_name[ns->prefix_len-1] == '\0');
        name = ns_name = "";
    }

    len = strlen(name);
    if (len > 0 && name[len-1] == mail_namespace_get_sep(ns)) {
        /* entry ends with hierarchy separator, remove it.
           this exists mainly for backwards compatibility with old
           Dovecot versions and non-Dovecot software that added them */
        name = t_strndup(name, len-1);
    }

    if (!mailbox_list_is_valid_name(list, name, &error)) {
        /* we'll only get into trouble if we show this */
        return -1;
    } else {
        vname = mailbox_list_get_vname(list, name);
        if (!uni_utf8_str_is_valid(vname))
            return -1;
        node = mailbox_tree_get(list->subscriptions, vname, &created);
        node->flags = MAILBOX_SUBSCRIBED;
    }
    return 0;
}
Example #24
0
static int maildir_fix_duplicate(struct maildir_sync_context *ctx,
				 const char *dir, const char *fname2)
{
	const char *fname1, *path1, *path2;
	const char *new_fname, *new_path;
	struct stat st1, st2;
	uoff_t size;

	fname1 = maildir_uidlist_sync_get_full_filename(ctx->uidlist_sync_ctx,
							fname2);
	i_assert(fname1 != NULL);

	path1 = t_strconcat(dir, "/", fname1, NULL);
	path2 = t_strconcat(dir, "/", fname2, NULL);

	if (stat(path1, &st1) < 0 || stat(path2, &st2) < 0) {
		/* most likely the files just don't exist anymore.
		   don't really care about other errors much. */
		return 0;
	}
	if (st1.st_ino == st2.st_ino &&
	    CMP_DEV_T(st1.st_dev, st2.st_dev)) {
		/* Files are the same. this means either a race condition
		   between stat() calls, or that the files were link()ed. */
		if (st1.st_nlink > 1 && st2.st_nlink == st1.st_nlink &&
		    st1.st_ctime == st2.st_ctime &&
		    st1.st_ctime < ioloop_time - DUPE_LINKS_DELETE_SECS) {
			/* The file has hard links and it hasn't had any
			   changes (such as renames) for a while, so this
			   isn't a race condition.

			   rename()ing one file on top of the other would fix
			   this safely, except POSIX decided that rename()
			   doesn't work that way. So we'll have unlink() one
			   and hope that another process didn't just decide to
			   unlink() the other (uidlist lock prevents this from
			   happening) */
			if (i_unlink(path2) == 0)
				i_warning("Unlinked a duplicate: %s", path2);
		}
		return 0;
	}

	new_fname = maildir_filename_generate();
	/* preserve S= and W= sizes if they're available.
	   (S=size is required for zlib plugin to work) */
	if (maildir_filename_get_size(fname2, MAILDIR_EXTRA_FILE_SIZE, &size)) {
		new_fname = t_strdup_printf("%s,%c=%"PRIuUOFF_T,
			new_fname, MAILDIR_EXTRA_FILE_SIZE, size);
	}
	if (maildir_filename_get_size(fname2, MAILDIR_EXTRA_VIRTUAL_SIZE, &size)) {
		new_fname = t_strdup_printf("%s,%c=%"PRIuUOFF_T,
			new_fname, MAILDIR_EXTRA_VIRTUAL_SIZE, size);
	}
	new_path = t_strconcat(mailbox_get_path(&ctx->mbox->box),
			       "/new/", new_fname, NULL);

	if (rename(path2, new_path) == 0)
		i_warning("Fixed a duplicate: %s -> %s", path2, new_fname);
	else if (errno != ENOENT) {
		mail_storage_set_critical(&ctx->mbox->storage->storage,
			"Couldn't fix a duplicate: rename(%s, %s) failed: %m",
			path2, new_path);
		return -1;
	}
	return 0;
}
	}
	if (auth_master_user_list_deinit(&ctx) < 0)
		i_error("listing users failed, can't replicate existing data");
	auth_master_deinit(&auth_conn);

	/* add updates from replicator db, if it exists */
	path = t_strconcat(service_set->state_dir, "/"REPLICATOR_DB_FNAME, NULL);
	(void)replicator_queue_import(queue, path);
}

static void ATTR_NULL(1)
replicator_dump_timeout(void *context ATTR_UNUSED)
{
	const char *path;

	path = t_strconcat(service_set->state_dir, "/"REPLICATOR_DB_FNAME, NULL);
	(void)replicator_queue_export(queue, path);
}

static void main_init(void)
{
	void **sets;

	service_set = master_service_settings_get(master_service);
	sets = master_service_settings_get_others(master_service);
	set = sets[0];

	queue = replicator_queue_init(set->replication_full_sync_interval,
				      REPLICATOR_FAILURE_RESYNC_INTERVAL_SECS);
	replication_add_users(queue);
	to_dump = timeout_add(REPLICATOR_DB_DUMP_INTERVAL_MSECS,
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 ||
               strncasecmp(line, "* ID ", 5) == 0) {
        /* Reply to ID command we sent, ignore it */
        client->proxy_state = IMAP_PROXY_STATE_ID;
        return 0;
    } else if (strncmp(line, "* ", 2) == 0) {
        /* untagged reply. just foward 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;
    }
}
Example #27
0
static int config_connection_request(struct config_connection *conn,
				     const char *const *args)
{
	struct config_export_context *ctx;
	struct master_service_settings_output output;
	struct config_filter filter;
	const char *path, *error, *module, *const *wanted_modules;
	ARRAY(const char *) modules;
	bool is_master = FALSE;

	/* [<args>] */
	t_array_init(&modules, 4);
	memset(&filter, 0, sizeof(filter));
	for (; *args != NULL; args++) {
		if (strncmp(*args, "service=", 8) == 0)
			filter.service = *args + 8;
		else if (strncmp(*args, "module=", 7) == 0) {
			module = *args + 7;
			if (strcmp(module, "master") == 0)
				is_master = TRUE;
			array_append(&modules, &module, 1);
		} else if (strncmp(*args, "lname=", 6) == 0)
			filter.local_name = *args + 6;
		else if (strncmp(*args, "lip=", 4) == 0) {
			if (net_addr2ip(*args + 4, &filter.local_net) == 0) {
				filter.local_bits =
					IPADDR_IS_V4(&filter.local_net) ?
					32 : 128;
			}
		} else if (strncmp(*args, "rip=", 4) == 0) {
			if (net_addr2ip(*args + 4, &filter.remote_net) == 0) {
				filter.remote_bits =
					IPADDR_IS_V4(&filter.remote_net) ?
					32 : 128;
			}
		}
	}
	array_append_zero(&modules);
	wanted_modules = array_count(&modules) == 1 ? NULL :
		array_idx(&modules, 0);

	if (is_master) {
		/* master reads configuration only when reloading settings */
		path = master_service_get_config_path(master_service);
		if (config_parse_file(path, TRUE, NULL, &error) <= 0) {
			o_stream_nsend_str(conn->output,
				t_strconcat("\nERROR ", error, "\n", NULL));
			config_connection_destroy(conn);
			return -1;
		}
	}

	o_stream_cork(conn->output);

	ctx = config_export_init(wanted_modules, CONFIG_DUMP_SCOPE_SET, 0,
				 config_request_output, conn->output);
	config_export_by_filter(ctx, &filter);
	config_export_get_output(ctx, &output);

	if (output.specific_services != NULL) {
		const char *const *s;

		for (s = output.specific_services; *s != NULL; s++) {
			o_stream_nsend_str(conn->output,
				t_strdup_printf("service=%s\t", *s));
		}
	}
	if (output.service_uses_local)
		o_stream_nsend_str(conn->output, "service-uses-local\t");
	if (output.service_uses_remote)
		o_stream_nsend_str(conn->output, "service-uses-remote\t");
	if (output.used_local)
		o_stream_nsend_str(conn->output, "used-local\t");
	if (output.used_remote)
		o_stream_nsend_str(conn->output, "used-remote\t");
	o_stream_nsend_str(conn->output, "\n");

	if (config_export_finish(&ctx) < 0) {
		config_connection_destroy(conn);
		return -1;
	}
	o_stream_nsend_str(conn->output, "\n");
	o_stream_uncork(conn->output);
	return 0;
}