#include "lib.h"
#include "ioloop.h"
#include "str-sanitize.h"
#include "istream.h"
#include "istream-crlf.h"
#include "istream-header-filter.h"
#include "rfc2822.h"
#include "sieve-common.h"
#include "sieve-limits.h"
#include "sieve-address.h"
#include "sieve-commands.h"
#include "sieve-code.h"
#include "sieve-actions.h"
#include "sieve-validator.h"
#include "sieve-generator.h"
#include "sieve-interpreter.h"
#include "sieve-code-dumper.h"
#include "sieve-result.h"
#include "sieve-smtp.h"
#include "sieve-message.h"
#include <stdio.h>
#define CMD_REDIRECT_DUPLICATE_KEEP (3600 * 24)
static bool cmd_redirect_validate
(struct sieve_validator *validator, struct sieve_command *cmd);
static bool cmd_redirect_generate
(const struct sieve_codegen_env *cgenv, struct sieve_command *cmd);
const struct sieve_command_def cmd_redirect = {
"redirect",
SCT_COMMAND,
1, 0, FALSE, FALSE,
NULL, NULL,
cmd_redirect_validate,
NULL,
cmd_redirect_generate,
NULL
};
static bool cmd_redirect_operation_dump
(const struct sieve_dumptime_env *denv, sieve_size_t *address);
static int cmd_redirect_operation_execute
(const struct sieve_runtime_env *renv, sieve_size_t *address);
const struct sieve_operation_def cmd_redirect_operation = {
"REDIRECT",
NULL,
SIEVE_OPERATION_REDIRECT,
cmd_redirect_operation_dump,
cmd_redirect_operation_execute
};
static bool act_redirect_equals
(const struct sieve_script_env *senv, const struct sieve_action *act1,
const struct sieve_action *act2);
static int act_redirect_check_duplicate
(const struct sieve_runtime_env *renv,
const struct sieve_action *act,
const struct sieve_action *act_other);
static void act_redirect_print
(const struct sieve_action *action, const struct sieve_result_print_env *rpenv,
bool *keep);
static bool act_redirect_commit
(const struct sieve_action *action, const struct sieve_action_exec_env *aenv,
void *tr_context, bool *keep);
const struct sieve_action_def act_redirect = {
"redirect",
SIEVE_ACTFLAG_TRIES_DELIVER,
act_redirect_equals,
act_redirect_check_duplicate,
NULL,
act_redirect_print,
NULL, NULL,
act_redirect_commit,
NULL
};
struct act_redirect_context {
const char *to_address;
};
static bool cmd_redirect_validate
(struct sieve_validator *validator, struct sieve_command *cmd)
{
struct sieve_ast_argument *arg = cmd->first_positional;
if ( !sieve_validate_positional_argument
(validator, cmd, arg, "address", 1, SAAT_STRING) ) {
return FALSE;
}
if ( !sieve_validator_argument_activate(validator, cmd, arg, FALSE) )
return FALSE;
if ( sieve_argument_is_string_literal(arg) ) {
string_t *address = sieve_ast_argument_str(arg);
const char *error;
const char *norm_address;
T_BEGIN {
norm_address = sieve_address_normalize(address, &error);
if ( norm_address == NULL ) {
sieve_argument_validate_error(validator, arg,
"specified redirect address '%s' is invalid: %s",
str_sanitize(str_c(address),128), error);
} else {
sieve_ast_argument_string_setc(arg, norm_address);
}
} T_END;
return ( norm_address != NULL );
}
return TRUE;
}
static bool cmd_redirect_generate
(const struct sieve_codegen_env *cgenv, struct sieve_command *cmd)
{
sieve_operation_emit(cgenv->sblock, NULL, &cmd_redirect_operation);
return sieve_generate_arguments(cgenv, cmd, NULL);
}
static bool cmd_redirect_operation_dump
(const struct sieve_dumptime_env *denv, sieve_size_t *address)
{
sieve_code_dumpf(denv, "REDIRECT");
sieve_code_descend(denv);
if ( sieve_action_opr_optional_dump(denv, address, NULL) != 0 )
return FALSE;
return sieve_opr_string_dump(denv, address, "address");
}
static int cmd_redirect_operation_execute
(const struct sieve_runtime_env *renv, sieve_size_t *address)
{
struct sieve_instance *svinst = renv->svinst;
struct sieve_side_effects_list *slist = NULL;
struct act_redirect_context *act;
string_t *redirect;
bool literal_address;
const char *norm_address;
pool_t pool;
int ret;
if ( sieve_action_opr_optional_read(renv, address, NULL, &ret, &slist) != 0 )
return ret;
if ( (ret=sieve_opr_string_read_ex(renv, address, "address", &redirect,
&literal_address)) <= 0 )
return ret;
if ( !literal_address ) {
const char *error;
norm_address = sieve_address_normalize(redirect, &error);
if ( norm_address == NULL ) {
sieve_runtime_error(renv, NULL,
"specified redirect address '%s' is invalid: %s",
str_sanitize(str_c(redirect),128), error);
return SIEVE_EXEC_FAILURE;
}
} else {
norm_address = str_c(redirect);
}
if ( sieve_runtime_trace_active(renv, SIEVE_TRLVL_ACTIONS) ) {
sieve_runtime_trace(renv, 0, "redirect action");
sieve_runtime_trace_descend(renv);
sieve_runtime_trace(renv, 0, "forward message to address `%s'",
str_sanitize(norm_address, 80));
}
pool = sieve_result_pool(renv->result);
act = p_new(pool, struct act_redirect_context, 1);
act->to_address = p_strdup(pool, norm_address);
if ( sieve_result_add_action
(renv, NULL, &act_redirect, slist, (void *) act,
svinst->max_redirects) < 0 )
return SIEVE_EXEC_FAILURE;
return SIEVE_EXEC_OK;
}
static bool act_redirect_equals
(const struct sieve_script_env *senv ATTR_UNUSED,
const struct sieve_action *act1, const struct sieve_action *act2)
{
struct act_redirect_context *rd_ctx1 =
(struct act_redirect_context *) act1->context;
struct act_redirect_context *rd_ctx2 =
(struct act_redirect_context *) act2->context;
return ( sieve_address_compare
(rd_ctx1->to_address, rd_ctx2->to_address, TRUE) == 0 );
}
static int act_redirect_check_duplicate
(const struct sieve_runtime_env *renv ATTR_UNUSED,
const struct sieve_action *act,
const struct sieve_action *act_other)
{
return ( act_redirect_equals(renv->scriptenv, act, act_other) ? 1 : 0 );
}
static void act_redirect_print
(const struct sieve_action *action,
const struct sieve_result_print_env *rpenv, bool *keep)
{
struct act_redirect_context *ctx =
(struct act_redirect_context *) action->context;
sieve_result_action_printf(rpenv, "redirect message to: %s",
str_sanitize(ctx->to_address, 128));
*keep = FALSE;
}
static bool act_redirect_send
(const struct sieve_action_exec_env *aenv, struct act_redirect_context *ctx)
{
static const char *hide_headers[] =
{ "Return-Path", "X-Sieve", "X-Sieve-Redirected-From" };
const struct sieve_message_data *msgdata = aenv->msgdata;
const struct sieve_script_env *senv = aenv->scriptenv;
const char *sender = sieve_message_get_sender(aenv->msgctx);
const char *recipient = sieve_message_get_final_recipient(aenv->msgctx);
struct istream *input, *crlf_input;
void *smtp_handle;
FILE *f;
const unsigned char *data;
size_t size;
int ret;
if ( !sieve_smtp_available(senv) ) {
sieve_result_global_warning
(aenv, "redirect action has no means to send mail.");
return TRUE;
}
if (mail_get_stream(msgdata->mail, NULL, NULL, &input) < 0)
return FALSE;
smtp_handle = sieve_smtp_open(senv, ctx->to_address, sender, &f);
input = i_stream_create_header_filter
(input, HEADER_FILTER_EXCLUDE, hide_headers,
N_ELEMENTS(hide_headers), null_header_filter_callback, NULL);
crlf_input = i_stream_create_crlf(input);
rfc2822_header_field_write(f, "X-Sieve", SIEVE_IMPLEMENTATION);
if ( recipient != NULL )
rfc2822_header_field_write(f, "X-Sieve-Redirected-From", recipient);
while ((ret = i_stream_read_data(crlf_input, &data, &size, 0)) > 0) {
if (fwrite(data, size, 1, f) == 0)
break;
i_stream_skip(crlf_input, size);
}
i_stream_unref(&crlf_input);
i_stream_unref(&input);
if ( !sieve_smtp_close(senv, smtp_handle) ) {
sieve_result_global_error(aenv,
"failed to redirect message to <%s> "
"(refer to server log for more information)",
str_sanitize(ctx->to_address, 80));
return FALSE;
}
return TRUE;
}
static bool act_redirect_commit
(const struct sieve_action *action,
const struct sieve_action_exec_env *aenv, void *tr_context ATTR_UNUSED,
bool *keep)
{
struct act_redirect_context *ctx =
(struct act_redirect_context *) action->context;
const struct sieve_message_data *msgdata = aenv->msgdata;
const struct sieve_script_env *senv = aenv->scriptenv;
const char *dupeid;
dupeid = msgdata->id == NULL ?
NULL : t_strdup_printf("%s-%s", msgdata->id, ctx->to_address);
if (dupeid != NULL) {
if (sieve_action_duplicate_check(senv, dupeid, strlen(dupeid))) {
sieve_result_global_log(aenv, "discarded duplicate forward to <%s>",
str_sanitize(ctx->to_address, 128));
return TRUE;
}
}
if ( act_redirect_send(aenv, ctx) ) {
if (dupeid != NULL) {
sieve_action_duplicate_mark(senv, dupeid, strlen(dupeid),
ioloop_time + CMD_REDIRECT_DUPLICATE_KEEP);
}
sieve_result_global_log(aenv, "forwarded to <%s>",
str_sanitize(ctx->to_address, 128));
aenv->exec_status->message_forwarded = TRUE;
*keep = FALSE;
return TRUE;
}
return FALSE;
}