#include "lib.h"
#include "str-sanitize.h"
#include "array.h"
#include "mail-storage.h"
#include "sieve-code.h"
#include "sieve-stringlist.h"
#include "sieve-extensions.h"
#include "sieve-commands.h"
#include "sieve-result.h"
#include "sieve-validator.h"
#include "sieve-generator.h"
#include "sieve-interpreter.h"
#include "sieve-actions.h"
#include "sieve-dump.h"
#include "ext-imap4flags-common.h"
#include <ctype.h>
static bool tag_flags_validate
(struct sieve_validator *valdtr, struct sieve_ast_argument **arg,
struct sieve_command *cmd);
static bool tag_flags_validate_persistent
(struct sieve_validator *valdtr, struct sieve_command *cmd,
const struct sieve_extension *ext);
static bool tag_flags_generate
(const struct sieve_codegen_env *cgenv, struct sieve_ast_argument *arg,
struct sieve_command *cmd);
const struct sieve_argument_def tag_flags = {
"flags",
NULL,
tag_flags_validate,
NULL, NULL,
tag_flags_generate
};
const struct sieve_argument_def tag_flags_implicit = {
"flags-implicit",
NULL, NULL, NULL,
tag_flags_validate_persistent,
tag_flags_generate
};
static bool seff_flags_dump_context
(const struct sieve_side_effect *seffect,
const struct sieve_dumptime_env *denv, sieve_size_t *address);
static int seff_flags_read_context
(const struct sieve_side_effect *seffect,
const struct sieve_runtime_env *renv, sieve_size_t *address,
void **context);
static int seff_flags_merge
(const struct sieve_runtime_env *renv, const struct sieve_action *action,
const struct sieve_side_effect *old_seffect,
const struct sieve_side_effect *new_seffect, void **old_context);
static void seff_flags_print
(const struct sieve_side_effect *seffect, const struct sieve_action *action,
const struct sieve_result_print_env *rpenv, bool *keep);
static bool seff_flags_pre_execute
(const struct sieve_side_effect *seffect, const struct sieve_action *action,
const struct sieve_action_exec_env *aenv, void **context, void *tr_context);
const struct sieve_side_effect_def flags_side_effect = {
SIEVE_OBJECT("flags", &flags_side_effect_operand, 0),
&act_store,
seff_flags_dump_context,
seff_flags_read_context,
seff_flags_merge,
seff_flags_print,
seff_flags_pre_execute,
NULL, NULL, NULL
};
static const struct sieve_extension_objects ext_side_effects =
SIEVE_EXT_DEFINE_SIDE_EFFECT(flags_side_effect);
const struct sieve_operand_def flags_side_effect_operand = {
"flags operand",
&imap4flags_extension,
0,
&sieve_side_effect_operand_class,
&ext_side_effects
};
static bool tag_flags_validate_persistent
(struct sieve_validator *valdtr ATTR_UNUSED, struct sieve_command *cmd,
const struct sieve_extension *ext)
{
if ( sieve_command_find_argument(cmd, &tag_flags) == NULL ) {
sieve_command_add_dynamic_tag(cmd, ext, &tag_flags_implicit, -1);
}
return TRUE;
}
static bool tag_flags_validate
(struct sieve_validator *valdtr, struct sieve_ast_argument **arg,
struct sieve_command *cmd)
{
struct sieve_ast_argument *tag = *arg;
*arg = sieve_ast_argument_next(*arg);
if ( !sieve_validate_tag_parameter
(valdtr, cmd, tag, *arg, NULL, 0, SAAT_STRING_LIST, FALSE) ) {
return FALSE;
}
tag->parameters = *arg;
*arg = sieve_ast_arguments_detach(*arg,1);
return TRUE;
}
static bool tag_flags_generate
(const struct sieve_codegen_env *cgenv, struct sieve_ast_argument *arg,
struct sieve_command *cmd)
{
struct sieve_ast_argument *param;
if ( sieve_ast_argument_type(arg) != SAAT_TAG ) {
return FALSE;
}
sieve_opr_side_effect_emit
(cgenv->sblock, arg->argument->ext, &flags_side_effect);
if ( sieve_argument_is(arg, tag_flags) ) {
param = arg->parameters;
if ( param->argument != NULL && param->argument->def != NULL &&
param->argument->def->generate != NULL &&
!param->argument->def->generate(cgenv, param, cmd) )
return FALSE;
} else if ( sieve_argument_is(arg, tag_flags_implicit) ) {
sieve_opr_omitted_emit(cgenv->sblock);
} else {
i_unreached();
}
return TRUE;
}
struct seff_flags_context {
ARRAY_DEFINE(keywords, const char *);
enum mail_flags flags;
};
static bool seff_flags_dump_context
(const struct sieve_side_effect *seffect ATTR_UNUSED,
const struct sieve_dumptime_env *denv, sieve_size_t *address)
{
struct sieve_operand oprnd;
if ( !sieve_operand_read(denv->sblock, address, "flags", &oprnd) ) {
sieve_code_dumpf(denv, "ERROR: INVALID OPERAND");
return FALSE;
}
if ( sieve_operand_is_omitted(&oprnd) ) {
sieve_code_dumpf(denv, "flags: INTERNAL");
return TRUE;
}
return sieve_opr_stringlist_dump_data(denv, &oprnd, address, "flags");
}
static struct seff_flags_context *seff_flags_get_implicit_context
(const struct sieve_extension *this_ext, struct sieve_result *result)
{
pool_t pool = sieve_result_pool(result);
struct seff_flags_context *ctx;
const char *flag;
struct ext_imap4flags_iter flit;
ctx = p_new(pool, struct seff_flags_context, 1);
p_array_init(&ctx->keywords, pool, 2);
T_BEGIN {
ext_imap4flags_get_implicit_flags_init(&flit, this_ext, result);
while ( (flag=ext_imap4flags_iter_get_flag(&flit)) != NULL ) {
if (flag != NULL && *flag != '\\') {
const char *keyword = p_strdup(pool, flag);
array_append(&ctx->keywords, &keyword, 1);
} else {
if (flag == NULL || strcasecmp(flag, "\\flagged") == 0)
ctx->flags |= MAIL_FLAGGED;
else if (strcasecmp(flag, "\\answered") == 0)
ctx->flags |= MAIL_ANSWERED;
else if (strcasecmp(flag, "\\deleted") == 0)
ctx->flags |= MAIL_DELETED;
else if (strcasecmp(flag, "\\seen") == 0)
ctx->flags |= MAIL_SEEN;
else if (strcasecmp(flag, "\\draft") == 0)
ctx->flags |= MAIL_DRAFT;
}
}
} T_END;
return ctx;
}
static int seff_flags_read_context
(const struct sieve_side_effect *seffect,
const struct sieve_runtime_env *renv, sieve_size_t *address,
void **se_context)
{
struct sieve_operand oprnd;
pool_t pool = sieve_result_pool(renv->result);
struct seff_flags_context *ctx;
string_t *flags_item;
struct sieve_stringlist *flag_list;
int ret;
t_push();
if ( (ret=sieve_operand_runtime_read(renv, address, "flags", &oprnd)) <= 0 ) {
t_pop();
return ret;
}
if ( sieve_operand_is_omitted(&oprnd) ) {
*se_context = seff_flags_get_implicit_context
(SIEVE_OBJECT_EXTENSION(seffect), renv->result);
t_pop();
return SIEVE_EXEC_OK;
}
if ( (ret=sieve_opr_stringlist_read_data
(renv, &oprnd, address, NULL, &flag_list)) <= 0 ) {
t_pop();
return ret;
}
ctx = p_new(pool, struct seff_flags_context, 1);
p_array_init(&ctx->keywords, pool, 2);
flags_item = NULL;
while ( (ret=sieve_stringlist_next_item(flag_list, &flags_item)) > 0 ) {
const char *flag;
struct ext_imap4flags_iter flit;
ext_imap4flags_iter_init(&flit, flags_item);
while ( (flag=ext_imap4flags_iter_get_flag(&flit)) != NULL ) {
if (flag != NULL && *flag != '\\') {
const char *keyword = p_strdup(pool, flag);
array_append(&ctx->keywords, &keyword, 1);
} else {
if (flag == NULL || strcasecmp(flag, "\\flagged") == 0)
ctx->flags |= MAIL_FLAGGED;
else if (strcasecmp(flag, "\\answered") == 0)
ctx->flags |= MAIL_ANSWERED;
else if (strcasecmp(flag, "\\deleted") == 0)
ctx->flags |= MAIL_DELETED;
else if (strcasecmp(flag, "\\seen") == 0)
ctx->flags |= MAIL_SEEN;
else if (strcasecmp(flag, "\\draft") == 0)
ctx->flags |= MAIL_DRAFT;
}
}
}
*se_context = (void *) ctx;
t_pop();
return SIEVE_EXEC_OK;
}
static int seff_flags_merge
(const struct sieve_runtime_env *renv ATTR_UNUSED,
const struct sieve_action *action ATTR_UNUSED,
const struct sieve_side_effect *old_seffect ATTR_UNUSED,
const struct sieve_side_effect *new_seffect,
void **old_context)
{
if ( new_seffect != NULL )
*old_context = new_seffect->context;
return 1;
}
static void seff_flags_print
(const struct sieve_side_effect *seffect,
const struct sieve_action *action ATTR_UNUSED,
const struct sieve_result_print_env *rpenv,bool *keep ATTR_UNUSED)
{
struct sieve_result *result = rpenv->result;
struct seff_flags_context *ctx =
(struct seff_flags_context *) seffect->context;
unsigned int i;
if ( ctx == NULL )
ctx = seff_flags_get_implicit_context
(SIEVE_OBJECT_EXTENSION(seffect), result);
if ( ctx->flags != 0 || array_count(&ctx->keywords) > 0 ) {
T_BEGIN {
string_t *flags = t_str_new(128);
if ( (ctx->flags & MAIL_FLAGGED) > 0 )
str_printfa(flags, " \\flagged");
if ( (ctx->flags & MAIL_ANSWERED) > 0 )
str_printfa(flags, " \\answered");
if ( (ctx->flags & MAIL_DELETED) > 0 )
str_printfa(flags, " \\deleted");
if ( (ctx->flags & MAIL_SEEN) > 0 )
str_printfa(flags, " \\seen");
if ( (ctx->flags & MAIL_DRAFT) > 0 )
str_printfa(flags, " \\draft");
for ( i = 0; i < array_count(&ctx->keywords); i++ ) {
const char *const *keyword = array_idx(&ctx->keywords, i);
str_printfa(flags, " %s", str_sanitize(*keyword, 64));
}
sieve_result_seffect_printf(rpenv, "add IMAP flags:%s", str_c(flags));
} T_END;
}
}
static bool seff_flags_pre_execute
(const struct sieve_side_effect *seffect,
const struct sieve_action *action ATTR_UNUSED,
const struct sieve_action_exec_env *aenv, void **context, void *tr_context)
{
struct seff_flags_context *ctx = (struct seff_flags_context *) *context;
const char *const *keywords;
if ( ctx == NULL ) {
ctx = seff_flags_get_implicit_context
(SIEVE_OBJECT_EXTENSION(seffect), aenv->result);
*context = (void *) ctx;
}
(void)array_append_space(&ctx->keywords);
keywords = array_idx(&ctx->keywords, 0);
sieve_act_store_add_flags(aenv, tr_context, keywords, ctx->flags);
return TRUE;
}