ext-imap4flags-common.c [plain text]
#include "lib.h"
#include "str.h"
#include "str-sanitize.h"
#include "sieve-common.h"
#include "sieve-commands.h"
#include "sieve-code.h"
#include "sieve-stringlist.h"
#include "sieve-actions.h"
#include "sieve-validator.h"
#include "sieve-generator.h"
#include "sieve-interpreter.h"
#include "sieve-result.h"
#include "sieve-dump.h"
#include "sieve-ext-variables.h"
#include "ext-imap4flags-common.h"
static bool flag_is_valid(const char *flag);
extern const struct sieve_argument_def tag_flags;
extern const struct sieve_argument_def tag_flags_implicit;
bool ext_imap4flags_command_validate
(struct sieve_validator *valdtr, struct sieve_command *cmd)
{
struct sieve_ast_argument *arg = cmd->first_positional;
struct sieve_ast_argument *arg2;
const struct sieve_extension *var_ext;
if ( arg == NULL ) {
sieve_command_validate_error(valdtr, cmd,
"the %s %s expects at least one argument, but none was found",
sieve_command_identifier(cmd), sieve_command_type_name(cmd));
return FALSE;
}
if ( sieve_ast_argument_type(arg) != SAAT_STRING &&
sieve_ast_argument_type(arg) != SAAT_STRING_LIST )
{
sieve_argument_validate_error(valdtr, arg,
"the %s %s expects either a string (variable name) or "
"a string-list (list of flags) as first argument, but %s was found",
sieve_command_identifier(cmd), sieve_command_type_name(cmd),
sieve_ast_argument_name(arg));
return FALSE;
}
arg2 = sieve_ast_argument_next(arg);
if ( arg2 != NULL ) {
if ( sieve_ast_argument_type(arg) != SAAT_STRING )
{
if ( sieve_command_is(cmd, tst_hasflag) ) {
if ( sieve_ast_argument_type(arg) != SAAT_STRING_LIST ) {
sieve_argument_validate_error(valdtr, arg,
"if a second argument is specified for the hasflag, the first "
"must be a string-list (variable-list), but %s was found",
sieve_ast_argument_name(arg));
return FALSE;
}
} else {
sieve_argument_validate_error(valdtr, arg,
"if a second argument is specified for the %s %s, the first "
"must be a string (variable name), but %s was found",
sieve_command_identifier(cmd), sieve_command_type_name(cmd),
sieve_ast_argument_name(arg));
return FALSE;
}
}
var_ext = sieve_ext_variables_get_extension(cmd->ext->svinst);
if ( var_ext == NULL || !sieve_ext_variables_is_active(var_ext, valdtr) )
{
sieve_argument_validate_error(valdtr,arg,
"the %s %s only allows for the specification of a "
"variable name when the variables extension is active",
sieve_command_identifier(cmd), sieve_command_type_name(cmd));
return FALSE;
}
if ( !sieve_variable_argument_activate
(var_ext, valdtr, cmd, arg, !sieve_command_is(cmd, tst_hasflag) ) )
return FALSE;
if ( sieve_ast_argument_type(arg2) != SAAT_STRING &&
sieve_ast_argument_type(arg2) != SAAT_STRING_LIST )
{
sieve_argument_validate_error(valdtr, arg2,
"the %s %s expects a string list (list of flags) as "
"second argument when two arguments are specified, "
"but %s was found",
sieve_command_identifier(cmd), sieve_command_type_name(cmd),
sieve_ast_argument_name(arg2));
return FALSE;
}
} else
arg2 = arg;
if ( !sieve_validator_argument_activate(valdtr, cmd, arg2, FALSE) )
return FALSE;
if ( !sieve_command_is(cmd, tst_hasflag) &&
sieve_argument_is_string_literal(arg2) ) {
struct ext_imap4flags_iter fiter;
const char *flag;
ext_imap4flags_iter_init(&fiter, sieve_ast_argument_str(arg));
while ( (flag=ext_imap4flags_iter_get_flag(&fiter)) != NULL ) {
if ( !flag_is_valid(flag) ) {
sieve_argument_validate_warning(valdtr, arg,
"IMAP flag '%s' specified for the %s command is invalid "
"and will be ignored (only first invalid is reported)",
str_sanitize(flag, 64), sieve_command_identifier(cmd));
break;
}
}
}
return TRUE;
}
void ext_imap4flags_attach_flags_tag
(struct sieve_validator *valdtr, const struct sieve_extension *ext,
const char *command, bool implicit)
{
if ( !implicit ) {
sieve_validator_register_external_tag
(valdtr, command, ext, &tag_flags, SIEVE_OPT_SIDE_EFFECT);
}
sieve_validator_register_persistent_tag
(valdtr, command, ext, &tag_flags_implicit);
}
struct ext_imap4flags_result_context {
string_t *internal_flags;
};
static void _get_initial_flags
(struct sieve_result *result, string_t *flags)
{
const struct sieve_message_data *msgdata =
sieve_result_get_message_data(result);
enum mail_flags mail_flags;
const char *const *mail_keywords;
mail_flags = mail_get_flags(msgdata->mail);
mail_keywords = mail_get_keywords(msgdata->mail);
if ( (mail_flags & MAIL_FLAGGED) > 0 )
str_printfa(flags, " \\flagged");
if ( (mail_flags & MAIL_ANSWERED) > 0 )
str_printfa(flags, " \\answered");
if ( (mail_flags & MAIL_DELETED) > 0 )
str_printfa(flags, " \\deleted");
if ( (mail_flags & MAIL_SEEN) > 0 )
str_printfa(flags, " \\seen");
if ( (mail_flags & MAIL_DRAFT) > 0 )
str_printfa(flags, " \\draft");
while ( *mail_keywords != NULL ) {
str_printfa(flags, " %s", *mail_keywords);
mail_keywords++;
}
}
static inline struct ext_imap4flags_result_context *_get_result_context
(const struct sieve_extension *this_ext, struct sieve_result *result)
{
struct ext_imap4flags_result_context *rctx =
(struct ext_imap4flags_result_context *)
sieve_result_extension_get_context(result, this_ext);
if ( rctx == NULL ) {
pool_t pool = sieve_result_pool(result);
rctx =p_new(pool, struct ext_imap4flags_result_context, 1);
rctx->internal_flags = str_new(pool, 32);
_get_initial_flags(result, rctx->internal_flags);
sieve_result_extension_set_context
(result, this_ext, rctx);
}
return rctx;
}
static string_t *_get_flags_string
(const struct sieve_extension *this_ext, struct sieve_result *result)
{
struct ext_imap4flags_result_context *ctx =
_get_result_context(this_ext, result);
return ctx->internal_flags;
}
static void ext_imap4flags_runtime_init
(const struct sieve_extension *ext, const struct sieve_runtime_env *renv,
void *context ATTR_UNUSED)
{
sieve_result_add_implicit_side_effect
(renv->result, NULL, TRUE, ext, &flags_side_effect, NULL);
}
const struct sieve_interpreter_extension imap4flags_interpreter_extension = {
&imap4flags_extension,
ext_imap4flags_runtime_init,
NULL,
};
static bool flag_is_valid(const char *flag)
{
if (*flag == '\\') {
const char *atom = t_str_ucase(flag);
if (
(strcmp(atom, "\\ANSWERED") != 0) &&
(strcmp(atom, "\\FLAGGED") != 0) &&
(strcmp(atom, "\\DELETED") != 0) &&
(strcmp(atom, "\\SEEN") != 0) &&
(strcmp(atom, "\\DRAFT") != 0) )
{
return FALSE;
}
} else {
const char *p;
p = flag;
while ( *p != '\0' ) {
if ( *p == '(' || *p == ')' || *p == '{' || *p == ' ' ||
*p <= 0x1F || *p == 0x7F || *p == '%' || *p == '*' ||
*p == '"' || *p == '\\' || *p == ']' )
return FALSE;
p++;
}
}
return TRUE;
}
static void ext_imap4flags_iter_clear
(struct ext_imap4flags_iter *iter)
{
memset(iter, 0, sizeof(*iter));
}
void ext_imap4flags_iter_init
(struct ext_imap4flags_iter *iter, string_t *flags_list)
{
ext_imap4flags_iter_clear(iter);
iter->flags_list = flags_list;
}
static string_t *ext_imap4flags_iter_get_flag_str
(struct ext_imap4flags_iter *iter)
{
unsigned int len;
const unsigned char *fp;
const unsigned char *fbegin;
const unsigned char *fstart;
const unsigned char *fend;
if ( iter->flags_list == NULL ) return NULL;
len = str_len(iter->flags_list);
if ( iter->offset >= len ) return NULL;
fbegin = str_data(iter->flags_list);
fend = fbegin + len;
fstart = fbegin + iter->offset;
fp = fstart;
for (;;) {
if ( fp >= fend || *fp == ' ' ) {
if ( fp > fstart ) {
string_t *flag = t_str_new(fp-fstart+1);
str_append_n(flag, fstart, fp-fstart);
iter->last = fstart - fbegin;
iter->offset = fp - fbegin;
return flag;
}
fstart = fp + 1;
}
if ( fp >= fend ) break;
fp++;
}
iter->last = fstart - fbegin;
iter->offset = fp - fbegin;
return NULL;
}
const char *ext_imap4flags_iter_get_flag
(struct ext_imap4flags_iter *iter)
{
string_t *flag = ext_imap4flags_iter_get_flag_str(iter);
if ( flag == NULL ) return NULL;
return str_c(flag);
}
static void ext_imap4flags_iter_delete_last
(struct ext_imap4flags_iter *iter)
{
iter->offset++;
if ( iter->offset > str_len(iter->flags_list) )
iter->offset = str_len(iter->flags_list);
if ( iter->offset == str_len(iter->flags_list) && iter->last > 0 )
iter->last--;
str_delete(iter->flags_list, iter->last, iter->offset - iter->last);
iter->offset = iter->last;
}
static bool flags_list_flag_exists
(string_t *flags_list, const char *flag)
{
const char *flg;
struct ext_imap4flags_iter flit;
ext_imap4flags_iter_init(&flit, flags_list);
while ( (flg=ext_imap4flags_iter_get_flag(&flit)) != NULL ) {
if ( strcasecmp(flg, flag) == 0 )
return TRUE;
}
return FALSE;
}
static void flags_list_flag_delete
(string_t *flags_list, const char *flag)
{
const char *flg;
struct ext_imap4flags_iter flit;
ext_imap4flags_iter_init(&flit, flags_list);
while ( (flg=ext_imap4flags_iter_get_flag(&flit)) != NULL ) {
if ( strcasecmp(flg, flag) == 0 ) {
ext_imap4flags_iter_delete_last(&flit);
}
}
}
static void flags_list_add_flags
(string_t *flags_list, string_t *flags)
{
const char *flg;
struct ext_imap4flags_iter flit;
ext_imap4flags_iter_init(&flit, flags);
while ( (flg=ext_imap4flags_iter_get_flag(&flit)) != NULL ) {
if ( flag_is_valid(flg) && !flags_list_flag_exists(flags_list, flg) ) {
if ( str_len(flags_list) != 0 )
str_append_c(flags_list, ' ');
str_append(flags_list, flg);
}
}
}
static void flags_list_remove_flags
(string_t *flags_list, string_t *flags)
{
const char *flg;
struct ext_imap4flags_iter flit;
ext_imap4flags_iter_init(&flit, flags);
while ( (flg=ext_imap4flags_iter_get_flag(&flit)) != NULL ) {
flags_list_flag_delete(flags_list, flg);
}
}
static void flags_list_set_flags
(string_t *flags_list, string_t *flags)
{
str_truncate(flags_list, 0);
flags_list_add_flags(flags_list, flags);
}
static void flags_list_clear_flags
(string_t *flags_list)
{
str_truncate(flags_list, 0);
}
static string_t *ext_imap4flags_get_flag_variable
(const struct sieve_runtime_env *renv, struct sieve_variable_storage *storage,
unsigned int var_index)
{
string_t *flags;
if ( storage != NULL ) {
if ( sieve_runtime_trace_active(renv, SIEVE_TRLVL_COMMANDS) ) {
const char *var_name, *var_id;
(void)sieve_variable_get_identifier(storage, var_index, &var_name);
var_id = sieve_variable_get_varid(storage, var_index);
sieve_runtime_trace(renv, 0, "update variable `%s' [%s]",
var_name, var_id);
}
if ( !sieve_variable_get_modifiable(storage, var_index, &flags) )
return NULL;
} else {
flags = _get_flags_string(renv->oprtn->ext, renv->result);
}
return flags;
}
int ext_imap4flags_set_flags
(const struct sieve_runtime_env *renv, struct sieve_variable_storage *storage,
unsigned int var_index, struct sieve_stringlist *flags)
{
string_t *cur_flags = ext_imap4flags_get_flag_variable
(renv, storage, var_index);
if ( cur_flags != NULL ) {
string_t *flags_item;
int ret;
flags_list_clear_flags(cur_flags);
while ( (ret=sieve_stringlist_next_item(flags, &flags_item)) > 0 ) {
sieve_runtime_trace(renv, SIEVE_TRLVL_COMMANDS,
"set flags `%s'", str_c(flags_item));
flags_list_add_flags(cur_flags, flags_item);
}
if ( ret < 0 ) return SIEVE_EXEC_BIN_CORRUPT;
return SIEVE_EXEC_OK;
}
return SIEVE_EXEC_BIN_CORRUPT;
}
int ext_imap4flags_add_flags
(const struct sieve_runtime_env *renv, struct sieve_variable_storage *storage,
unsigned int var_index, struct sieve_stringlist *flags)
{
string_t *cur_flags = ext_imap4flags_get_flag_variable
(renv, storage, var_index);
if ( cur_flags != NULL ) {
string_t *flags_item;
int ret;
while ( (ret=sieve_stringlist_next_item(flags, &flags_item)) > 0 ) {
sieve_runtime_trace(renv, SIEVE_TRLVL_COMMANDS,
"add flags `%s'", str_c(flags_item));
flags_list_add_flags(cur_flags, flags_item);
}
if ( ret < 0 ) return SIEVE_EXEC_BIN_CORRUPT;
return SIEVE_EXEC_OK;
}
return SIEVE_EXEC_BIN_CORRUPT;
}
int ext_imap4flags_remove_flags
(const struct sieve_runtime_env *renv, struct sieve_variable_storage *storage,
unsigned int var_index, struct sieve_stringlist *flags)
{
string_t *cur_flags = ext_imap4flags_get_flag_variable
(renv, storage, var_index);
if ( cur_flags != NULL ) {
string_t *flags_item;
int ret;
while ( (ret=sieve_stringlist_next_item(flags, &flags_item)) > 0 ) {
sieve_runtime_trace(renv, SIEVE_TRLVL_COMMANDS,
"remove flags `%s'", str_c(flags_item));
flags_list_remove_flags(cur_flags, flags_item);
}
if ( ret < 0 ) return SIEVE_EXEC_BIN_CORRUPT;
return SIEVE_EXEC_OK;
}
return SIEVE_EXEC_BIN_CORRUPT;
}
static int ext_imap4flags_stringlist_next_item
(struct sieve_stringlist *_strlist, string_t **str_r);
static void ext_imap4flags_stringlist_reset
(struct sieve_stringlist *_strlist);
struct ext_imap4flags_stringlist {
struct sieve_stringlist strlist;
struct sieve_stringlist *flags_list;
string_t *flags_string;
struct ext_imap4flags_iter flit;
unsigned int normalize:1;
};
static struct sieve_stringlist *ext_imap4flags_stringlist_create
(const struct sieve_runtime_env *renv, struct sieve_stringlist *flags_list,
bool normalize)
{
struct ext_imap4flags_stringlist *strlist;
strlist = t_new(struct ext_imap4flags_stringlist, 1);
strlist->strlist.exec_status = SIEVE_EXEC_OK;
strlist->strlist.runenv = renv;
strlist->strlist.next_item = ext_imap4flags_stringlist_next_item;
strlist->strlist.reset = ext_imap4flags_stringlist_reset;
strlist->normalize = normalize;
strlist->flags_list = flags_list;
return &strlist->strlist;
}
static struct sieve_stringlist *ext_imap4flags_stringlist_create_single
(const struct sieve_runtime_env *renv, string_t *flags_string, bool normalize)
{
struct ext_imap4flags_stringlist *strlist;
strlist = t_new(struct ext_imap4flags_stringlist, 1);
strlist->strlist.exec_status = SIEVE_EXEC_OK;
strlist->strlist.runenv = renv;
strlist->strlist.next_item = ext_imap4flags_stringlist_next_item;
strlist->strlist.reset = ext_imap4flags_stringlist_reset;
strlist->normalize = normalize;
if ( normalize ) {
strlist->flags_string = t_str_new(256);
flags_list_set_flags(strlist->flags_string, flags_string);
} else {
strlist->flags_string = flags_string;
}
ext_imap4flags_iter_init(&strlist->flit, strlist->flags_string);
return &strlist->strlist;
}
static int ext_imap4flags_stringlist_next_item
(struct sieve_stringlist *_strlist, string_t **str_r)
{
struct ext_imap4flags_stringlist *strlist =
(struct ext_imap4flags_stringlist *)_strlist;
while ( (*str_r=ext_imap4flags_iter_get_flag_str(&strlist->flit)) == NULL ) {
int ret;
if ( strlist->flags_list == NULL )
return 0;
if ( (ret=sieve_stringlist_next_item
(strlist->flags_list, &strlist->flags_string)) <= 0 )
return ret;
if ( strlist->flags_string == NULL )
return -1;
if ( strlist->normalize ) {
string_t *flags_string = t_str_new(256);
flags_list_set_flags(flags_string, strlist->flags_string);
strlist->flags_string = flags_string;
}
ext_imap4flags_iter_init(&strlist->flit, strlist->flags_string);
}
return 1;
}
static void ext_imap4flags_stringlist_reset
(struct sieve_stringlist *_strlist)
{
struct ext_imap4flags_stringlist *strlist =
(struct ext_imap4flags_stringlist *)_strlist;
if ( strlist->flags_list != NULL ) {
sieve_stringlist_reset(strlist->flags_list);
ext_imap4flags_iter_clear(&strlist->flit);
} else {
ext_imap4flags_iter_init(&strlist->flit, strlist->flags_string);
}
}
struct sieve_stringlist *ext_imap4flags_get_flags
(const struct sieve_runtime_env *renv, struct sieve_stringlist *flags_list)
{
if ( flags_list == NULL )
return ext_imap4flags_stringlist_create_single
(renv, _get_flags_string(renv->oprtn->ext, renv->result), FALSE);
return ext_imap4flags_stringlist_create(renv, flags_list, TRUE);
}
void ext_imap4flags_get_implicit_flags_init
(struct ext_imap4flags_iter *iter, const struct sieve_extension *this_ext,
struct sieve_result *result)
{
string_t *cur_flags = _get_flags_string(this_ext, result);
ext_imap4flags_iter_init(iter, cur_flags);
}