ext-enotify-common.c [plain text]
#include "lib.h"
#include "str.h"
#include "str-sanitize.h"
#include "array.h"
#include "sieve-common.h"
#include "sieve-ast.h"
#include "sieve-stringlist.h"
#include "sieve-code.h"
#include "sieve-commands.h"
#include "sieve-validator.h"
#include "sieve-interpreter.h"
#include "sieve-result.h"
#include "ext-enotify-limits.h"
#include "ext-enotify-common.h"
#include <ctype.h>
static const char *ext_notify_get_methods_string
(const struct sieve_extension *ntfy_ext);
const struct sieve_extension_capabilities notify_capabilities = {
"notify",
ext_notify_get_methods_string
};
extern const struct sieve_enotify_method_def mailto_notify;
static const struct sieve_enotify_method *ext_enotify_method_register
(struct sieve_instance *svinst, struct ext_enotify_context *ectx,
const struct sieve_enotify_method_def *nmth_def)
{
struct sieve_enotify_method *nmth;
int nmth_id = (int) array_count(&ectx->notify_methods);
nmth = array_append_space(&ectx->notify_methods);
nmth->def = nmth_def;
nmth->id = nmth_id;
nmth->svinst = svinst;
if ( nmth_def->load != NULL )
nmth_def->load(nmth, &nmth->context);
return nmth;
}
void ext_enotify_methods_init
(struct sieve_instance *svinst, struct ext_enotify_context *ectx)
{
p_array_init(&ectx->notify_methods, default_pool, 4);
ext_enotify_method_register(svinst, ectx, &mailto_notify);
}
void ext_enotify_methods_deinit(struct ext_enotify_context *ectx)
{
const struct sieve_enotify_method *methods;
unsigned int meth_count, i;
methods = array_get(&ectx->notify_methods, &meth_count);
for ( i = 0; i < meth_count; i++ ) {
if ( methods[i].def != NULL && methods[i].def->unload != NULL )
methods[i].def->unload(&methods[i]);
}
array_free(&ectx->notify_methods);
}
const struct sieve_enotify_method *sieve_enotify_method_register
(struct sieve_instance *svinst,
const struct sieve_enotify_method_def *nmth_def)
{
const struct sieve_extension *ntfy_ext =
sieve_extension_get_by_name(svinst, "enotify");
if ( ntfy_ext != NULL ) {
struct ext_enotify_context *ectx =
(struct ext_enotify_context *) ntfy_ext->context;
return ext_enotify_method_register(svinst, ectx, nmth_def);
}
return NULL;
}
void sieve_enotify_method_unregister
(const struct sieve_enotify_method *nmth)
{
struct sieve_instance *svinst = nmth->svinst;
const struct sieve_extension *ntfy_ext =
sieve_extension_get_by_name(svinst, "enotify");
if ( ntfy_ext != NULL ) {
struct ext_enotify_context *ectx =
(struct ext_enotify_context *) ntfy_ext->context;
int nmth_id = nmth->id;
if ( nmth_id >= 0 && nmth_id < (int)array_count(&ectx->notify_methods) ) {
struct sieve_enotify_method *nmth_mod =
array_idx_modifiable(&ectx->notify_methods, nmth_id);
nmth_mod->def = NULL;
}
}
}
const struct sieve_enotify_method *ext_enotify_method_find
(const struct sieve_extension *ntfy_ext, const char *identifier)
{
struct ext_enotify_context *ectx =
(struct ext_enotify_context *) ntfy_ext->context;
unsigned int meth_count, i;
const struct sieve_enotify_method *methods;
methods = array_get(&ectx->notify_methods, &meth_count);
for ( i = 0; i < meth_count; i++ ) {
if ( methods[i].def == NULL ) continue;
if ( strcasecmp(methods[i].def->identifier, identifier) == 0 ) {
return &methods[i];
}
}
return NULL;
}
static const char *ext_notify_get_methods_string
(const struct sieve_extension *ntfy_ext)
{
struct ext_enotify_context *ectx =
(struct ext_enotify_context *) ntfy_ext->context;
unsigned int meth_count, i;
const struct sieve_enotify_method *methods;
string_t *result = t_str_new(128);
methods = array_get(&ectx->notify_methods, &meth_count);
if ( meth_count > 0 ) {
for ( i = 0; i < meth_count; i++ ) {
if ( str_len(result) > 0 )
str_append_c(result, ' ');
if ( methods[i].def != NULL )
str_append(result, methods[i].def->identifier);
}
return str_c(result);
}
return NULL;
}
static const char *ext_enotify_uri_scheme_parse(const char **uri_p)
{
string_t *scheme = t_str_new(EXT_ENOTIFY_MAX_SCHEME_LEN);
const char *p = *uri_p;
unsigned int len = 0;
if ( !i_isalpha(*p) )
return NULL;
str_append_c(scheme, *p);
p++;
while ( *p != '\0' && len < EXT_ENOTIFY_MAX_SCHEME_LEN ) {
if ( !i_isalnum(*p) && *p != '+' && *p != '-' && *p != '.' )
break;
str_append_c(scheme, *p);
p++;
len++;
}
if ( *p != ':' )
return NULL;
p++;
*uri_p = p;
return str_c(scheme);
}
static bool ext_enotify_option_parse
(struct sieve_enotify_env *nenv, const char *option, bool name_only,
const char **opt_name_r, const char **opt_value_r)
{
const char *p = option;
if ( *p == '\0' ) {
sieve_enotify_error(nenv, "empty option specified");
return FALSE;
}
if ( i_isalnum(*p) ) {
p++;
while ( i_isalnum(*p) || *p == '.' || *p == '-' || *p == '_' )
p++;
}
if ( *p != '=' || p == option ) {
sieve_enotify_error(nenv, "invalid option name specified in option '%s'",
str_sanitize(option, 80));
return FALSE;
}
if ( opt_name_r != NULL )
*opt_name_r = t_strdup_until(option, p);
p++;
if ( name_only )
return TRUE;
while ( *p != '\0' && *p != 0x0A && *p != 0x0D )
p++;
if ( *p != '\0' ) {
sieve_enotify_error(nenv,
"notify command: invalid option value specified in option '%s'",
str_sanitize(option, 80));
return FALSE;
}
if ( opt_value_r != NULL )
*opt_value_r = p;
return TRUE;
}
struct _ext_enotify_option_check_context {
struct sieve_instance *svinst;
struct sieve_validator *valdtr;
const struct sieve_enotify_method *method;
};
static int _ext_enotify_option_check
(void *context, struct sieve_ast_argument *arg)
{
struct _ext_enotify_option_check_context *optn_context =
(struct _ext_enotify_option_check_context *) context;
struct sieve_validator *valdtr = optn_context->valdtr;
const struct sieve_enotify_method *method = optn_context->method;
struct sieve_enotify_env nenv;
const char *option = sieve_ast_argument_strc(arg);
const char *opt_name = NULL, *opt_value = NULL;
bool result = TRUE, check = TRUE;
memset(&nenv, 0, sizeof(nenv));
nenv.svinst = optn_context->svinst;
nenv.method = method;
nenv.ehandler = sieve_prefix_ehandler_create
(sieve_validator_error_handler(valdtr),
sieve_error_script_location
(sieve_validator_script(valdtr), arg->source_line),
"notify command");
if ( !sieve_argument_is_string_literal(arg) ) {
if ( !ext_enotify_option_parse(NULL, option, TRUE, &opt_name, &opt_value) )
check = FALSE;
} else {
if ( !ext_enotify_option_parse
(&nenv, option, FALSE, &opt_name, &opt_value) )
result = FALSE;
}
if ( result && check && method->def != NULL &&
method->def->compile_check_option != NULL )
result = method->def->compile_check_option(&nenv, opt_name, opt_value);
sieve_error_handler_unref(&nenv.ehandler);
return result;
}
bool ext_enotify_compile_check_arguments
(struct sieve_validator *valdtr, struct sieve_command *cmd,
struct sieve_ast_argument *uri_arg, struct sieve_ast_argument *msg_arg,
struct sieve_ast_argument *from_arg, struct sieve_ast_argument *options_arg)
{
const struct sieve_extension *this_ext = cmd->ext;
struct sieve_instance *svinst = this_ext->svinst;
const char *uri = sieve_ast_argument_strc(uri_arg);
const char *scheme;
const struct sieve_enotify_method *method;
struct sieve_enotify_env nenv;
bool result = TRUE;
if ( !sieve_argument_is_string_literal(uri_arg) )
return TRUE;
if ( (scheme=ext_enotify_uri_scheme_parse(&uri)) == NULL ) {
sieve_argument_validate_error(valdtr, uri_arg,
"notify command: invalid scheme part for method URI '%s'",
str_sanitize(sieve_ast_argument_strc(uri_arg), 80));
return FALSE;
}
if ( (method=ext_enotify_method_find(this_ext, scheme)) == NULL ) {
sieve_argument_validate_error(valdtr, uri_arg,
"notify command: invalid method '%s'", scheme);
return FALSE;
}
if ( method->def == NULL ) return TRUE;
memset(&nenv, 0, sizeof(nenv));
nenv.svinst = svinst;
nenv.method = method;
if ( result && method->def->compile_check_uri != NULL ) {
nenv.ehandler = sieve_prefix_ehandler_create
(sieve_validator_error_handler(valdtr),
sieve_error_script_location
(sieve_validator_script(valdtr), uri_arg->source_line),
"notify command");
result = method->def->compile_check_uri
(&nenv, sieve_ast_argument_strc(uri_arg), uri);
}
if ( result && msg_arg != NULL && sieve_argument_is_string_literal(msg_arg)
&& method->def->compile_check_message != NULL ) {
sieve_error_handler_unref(&nenv.ehandler);
nenv.ehandler = sieve_prefix_ehandler_create
(sieve_validator_error_handler(valdtr),
sieve_error_script_location
(sieve_validator_script(valdtr), msg_arg->source_line),
"notify command");
result = method->def->compile_check_message
(&nenv, sieve_ast_argument_str(msg_arg));
}
if ( result && from_arg != NULL && sieve_argument_is_string_literal(from_arg)
&& method->def->compile_check_from != NULL ) {
sieve_error_handler_unref(&nenv.ehandler);
nenv.ehandler = sieve_prefix_ehandler_create
(sieve_validator_error_handler(valdtr),
sieve_error_script_location
(sieve_validator_script(valdtr), from_arg->source_line),
"notify command");
result = method->def->compile_check_from
(&nenv, sieve_ast_argument_str(from_arg));
}
sieve_error_handler_unref(&nenv.ehandler);
if ( result && options_arg != NULL ) {
struct sieve_ast_argument *option = options_arg;
struct _ext_enotify_option_check_context optn_context =
{ svinst, valdtr, method };
result = ( sieve_ast_stringlist_map
(&option, (void *) &optn_context, _ext_enotify_option_check) > 0 );
if ( result && method->def->compile_check_option == NULL ) {
sieve_argument_validate_warning(valdtr, options_arg,
"notify command: method '%s' accepts no options", scheme);
(void)sieve_ast_arguments_detach(options_arg,1);
}
}
return result;
}
bool ext_enotify_runtime_method_validate
(const struct sieve_runtime_env *renv, string_t *method_uri)
{
const struct sieve_extension *this_ext = renv->oprtn->ext;
const struct sieve_enotify_method *method;
const char *uri = str_c(method_uri);
const char *scheme;
bool result = TRUE;
if ( (scheme=ext_enotify_uri_scheme_parse(&uri)) == NULL )
return FALSE;
if ( (method=ext_enotify_method_find(this_ext, scheme)) == NULL )
return FALSE;
if ( method->def != NULL && method->def->runtime_check_uri != NULL ) {
struct sieve_enotify_env nenv;
memset(&nenv, 0, sizeof(nenv));
nenv.svinst = renv->svinst;
nenv.method = method;
nenv.ehandler = sieve_prefix_ehandler_create
(sieve_interpreter_get_error_handler(renv->interp),
sieve_runtime_get_full_command_location(renv),
"valid_notify_method test");
result = method->def->runtime_check_uri(&nenv, str_c(method_uri), uri);
sieve_error_handler_unref(&nenv.ehandler);
}
return result;
}
static const struct sieve_enotify_method *ext_enotify_get_method
(const struct sieve_runtime_env *renv, string_t *method_uri,
const char **uri_body_r)
{
const struct sieve_extension *this_ext = renv->oprtn->ext;
const struct sieve_enotify_method *method;
const char *uri = str_c(method_uri);
const char *scheme;
if ( (scheme=ext_enotify_uri_scheme_parse(&uri)) == NULL ) {
sieve_runtime_error(renv, NULL,
"invalid scheme part for method URI '%s'",
str_sanitize(str_c(method_uri), 80));
return NULL;
}
if ( (method=ext_enotify_method_find(this_ext, scheme)) == NULL ) {
sieve_runtime_error(renv, NULL,
"invalid notify method '%s'", scheme);
return NULL;
}
*uri_body_r = uri;
return method;
}
const char *ext_enotify_runtime_get_method_capability
(const struct sieve_runtime_env *renv,
string_t *method_uri, const char *capability)
{
const struct sieve_enotify_method *method;
const char *uri_body;
const char *result = NULL;
method = ext_enotify_get_method(renv, method_uri, &uri_body);
if ( method == NULL ) return NULL;
if ( method->def != NULL &&
method->def->runtime_get_method_capability != NULL ) {
struct sieve_enotify_env nenv;
memset(&nenv, 0, sizeof(nenv));
nenv.svinst = renv->svinst;
nenv.method = method;
nenv.ehandler = sieve_prefix_ehandler_create
(sieve_interpreter_get_error_handler(renv->interp),
sieve_runtime_get_full_command_location(renv),
"notify_method_capability test");
result = method->def->runtime_get_method_capability
(&nenv, str_c(method_uri), uri_body, capability);
sieve_error_handler_unref(&nenv.ehandler);
}
return result;
}
int ext_enotify_runtime_check_operands
(const struct sieve_runtime_env *renv,
string_t *method_uri, string_t *message, string_t *from,
struct sieve_stringlist *options,
const struct sieve_enotify_method **method_r, void **method_context)
{
const struct sieve_enotify_method *method;
const char *uri_body;
method = ext_enotify_get_method(renv, method_uri, &uri_body);
if ( method == NULL ) return SIEVE_EXEC_FAILURE;
if ( method->def != NULL && method->def->runtime_check_operands != NULL ) {
struct sieve_enotify_env nenv;
int result = SIEVE_EXEC_OK;
memset(&nenv, 0, sizeof(nenv));
nenv.svinst = renv->svinst;
nenv.method = method;
nenv.ehandler = sieve_prefix_ehandler_create
(sieve_interpreter_get_error_handler(renv->interp),
sieve_runtime_get_full_command_location(renv),
"notify action");
if ( method->def->runtime_check_operands
(&nenv, str_c(method_uri), uri_body, message, from,
sieve_result_pool(renv->result), method_context) ) {
if ( options != NULL ) {
string_t *option = NULL;
int ret;
while ( (ret=sieve_stringlist_next_item(options, &option)) > 0 ) {
const char *opt_name = NULL, *opt_value = NULL;
if ( ext_enotify_option_parse
(&nenv, str_c(option), FALSE, &opt_name, &opt_value) ) {
if ( method->def->runtime_set_option != NULL ) {
(void) method->def->runtime_set_option
(&nenv, *method_context, opt_name, opt_value);
}
}
}
if ( ret >= 0 ) {
*method_r = method;
} else {
sieve_runtime_trace_error
(renv, "invalid item in options string list");
result = SIEVE_EXEC_BIN_CORRUPT;
}
} else {
*method_r = method;
}
} else {
result = SIEVE_EXEC_FAILURE;
}
sieve_error_handler_unref(&nenv.ehandler);
return result;
}
*method_context = NULL;
*method_r = method;
return SIEVE_EXEC_OK;
}
void sieve_enotify_method_printf
(const struct sieve_enotify_print_env *penv, const char *fmt, ...)
{
va_list args;
va_start(args, fmt);
sieve_result_vprintf(penv->result_penv, fmt, args);
va_end(args);
}