#include "lib.h"
#include "str.h"
#include "str-sanitize.h"
#include "mempool.h"
#include "buffer.h"
#include "hash.h"
#include "array.h"
#include "ostream.h"
#include "eacces-error.h"
#include "safe-mkstemp.h"
#include "sieve-error.h"
#include "sieve-extensions.h"
#include "sieve-code.h"
#include "sieve-script.h"
#include "sieve-binary-private.h"
static inline struct sieve_binary_extension_reg *sieve_binary_extension_get_reg
(struct sieve_binary *sbin, const struct sieve_extension *ext,
bool create);
static inline int sieve_binary_extension_register
(struct sieve_binary *sbin, const struct sieve_extension *ext,
struct sieve_binary_extension_reg **reg);
struct sieve_binary *sieve_binary_create
(struct sieve_instance *svinst, struct sieve_script *script)
{
pool_t pool;
struct sieve_binary *sbin;
const struct sieve_extension *const *ext_preloaded;
unsigned int i, ext_count;
pool = pool_alloconly_create("sieve_binary", 8192);
sbin = p_new(pool, struct sieve_binary, 1);
sbin->pool = pool;
sbin->refcount = 1;
sbin->svinst = svinst;
sbin->script = script;
if ( script != NULL )
sieve_script_ref(script);
ext_count = sieve_extensions_get_count(svinst);
p_array_init(&sbin->linked_extensions, pool, ext_count);
p_array_init(&sbin->extensions, pool, ext_count);
p_array_init(&sbin->extension_index, pool, ext_count);
p_array_init(&sbin->blocks, pool, 3);
ext_preloaded = sieve_extensions_get_preloaded(svinst, &ext_count);
for ( i = 0; i < ext_count; i++ ) {
const struct sieve_extension_def *ext_def = ext_preloaded[i]->def;
if ( ext_def != NULL && ext_def->binary_load != NULL )
(void)ext_def->binary_load(ext_preloaded[i], sbin);
}
return sbin;
}
struct sieve_binary *sieve_binary_create_new(struct sieve_script *script)
{
struct sieve_binary *sbin = sieve_binary_create
(sieve_script_svinst(script), script);
(void) sieve_binary_block_create(sbin);
(void) sieve_binary_block_create(sbin);
return sbin;
}
void sieve_binary_ref(struct sieve_binary *sbin)
{
sbin->refcount++;
}
static inline void sieve_binary_extensions_free(struct sieve_binary *sbin)
{
struct sieve_binary_extension_reg *const *regs;
unsigned int ext_count, i;
regs = array_get(&sbin->extensions, &ext_count);
for ( i = 0; i < ext_count; i++ ) {
const struct sieve_binary_extension *binext = regs[i]->binext;
if ( binext != NULL && binext->binary_free != NULL )
binext->binary_free(regs[i]->extension, sbin, regs[i]->context);
}
}
void sieve_binary_unref(struct sieve_binary **sbin)
{
i_assert((*sbin)->refcount > 0);
if (--(*sbin)->refcount != 0)
return;
sieve_binary_extensions_free(*sbin);
if ( (*sbin)->file != NULL )
sieve_binary_file_close(&(*sbin)->file);
if ( (*sbin)->script != NULL )
sieve_script_unref(&(*sbin)->script);
pool_unref(&((*sbin)->pool));
*sbin = NULL;
}
pool_t sieve_binary_pool(struct sieve_binary *sbin)
{
return sbin->pool;
}
struct sieve_script *sieve_binary_script(struct sieve_binary *sbin)
{
return sbin->script;
}
const char *sieve_binary_path(struct sieve_binary *sbin)
{
return sbin->path;
}
bool sieve_binary_saved(struct sieve_binary *sbin)
{
return ( sbin->path != NULL );
}
bool sieve_binary_loaded(struct sieve_binary *sbin)
{
return ( sbin->file != NULL );
}
const char *sieve_binary_source(struct sieve_binary *sbin)
{
if ( sbin->script != NULL && (sbin->path == NULL || sbin->file == NULL) )
return sieve_script_path(sbin->script);
return sbin->path;
}
struct sieve_instance *sieve_binary_svinst(struct sieve_binary *sbin)
{
return sbin->svinst;
}
bool sieve_binary_script_newer
(struct sieve_binary *sbin, struct sieve_script *script)
{
i_assert(sbin->file != NULL);
return ( sieve_script_newer(script, sbin->file->st.st_mtime) );
}
const char *sieve_binary_script_name(struct sieve_binary *sbin)
{
return ( sbin->script == NULL ? NULL : sieve_script_name(sbin->script) );
}
const char *sieve_binary_script_path(struct sieve_binary *sbin)
{
return ( sbin->script == NULL ? NULL : sieve_script_path(sbin->script) );
}
unsigned int sieve_binary_block_count
(struct sieve_binary *sbin)
{
return array_count(&sbin->blocks);
}
struct sieve_binary_block *sieve_binary_block_create(struct sieve_binary *sbin)
{
unsigned int id = sieve_binary_block_count(sbin);
struct sieve_binary_block *sblock;
sblock = p_new(sbin->pool, struct sieve_binary_block, 1);
sblock->data = buffer_create_dynamic(sbin->pool, 64);
sblock->sbin = sbin;
sblock->id = id;
array_append(&sbin->blocks, &sblock, 1);
return sblock;
}
struct sieve_binary_block *sieve_binary_block_create_id
(struct sieve_binary *sbin, unsigned int id)
{
struct sieve_binary_block *sblock;
sblock = p_new(sbin->pool, struct sieve_binary_block, 1);
array_idx_set(&sbin->blocks, id, &sblock);
sblock->data = NULL;
sblock->sbin = sbin;
sblock->id = id;
return sblock;
}
static bool sieve_binary_block_fetch(struct sieve_binary_block *sblock)
{
struct sieve_binary *sbin = sblock->sbin;
if ( sbin->file ) {
if ( !sieve_binary_load_block(sblock) || sblock->data == NULL )
return FALSE;
} else {
sblock->data = buffer_create_dynamic(sbin->pool, 64);
return TRUE;
}
return TRUE;
}
struct sieve_binary_block *sieve_binary_block_get
(struct sieve_binary *sbin, unsigned int id)
{
struct sieve_binary_block *sblock = sieve_binary_block_index(sbin, id);
if ( sblock == NULL )
return NULL;
if ( sblock->data == NULL && !sieve_binary_block_fetch(sblock) )
return NULL;
return sblock;
}
void sieve_binary_block_clear
(struct sieve_binary_block *sblock)
{
buffer_reset(sblock->data);
}
buffer_t *sieve_binary_block_get_buffer
(struct sieve_binary_block *sblock)
{
if ( sblock->data == NULL && !sieve_binary_block_fetch(sblock) )
return NULL;
return sblock->data;
}
struct sieve_binary *sieve_binary_block_get_binary
(const struct sieve_binary_block *sblock)
{
return sblock->sbin;
}
unsigned int sieve_binary_block_get_id
(const struct sieve_binary_block *sblock)
{
return sblock->id;
}
size_t sieve_binary_block_get_size
(const struct sieve_binary_block *sblock)
{
return _sieve_binary_block_get_size(sblock);
}
bool sieve_binary_up_to_date(struct sieve_binary *sbin)
{
struct sieve_binary_extension_reg *const *regs;
unsigned int ext_count, i;
i_assert(sbin->file != NULL);
if ( sbin->script == NULL || sieve_script_newer
(sbin->script, sbin->file->st.st_mtime) )
return FALSE;
regs = array_get(&sbin->extensions, &ext_count);
for ( i = 0; i < ext_count; i++ ) {
const struct sieve_binary_extension *binext = regs[i]->binext;
if ( binext != NULL && binext->binary_up_to_date != NULL &&
!binext->binary_up_to_date(regs[i]->extension, sbin, regs[i]->context) )
return FALSE;
}
return TRUE;
}
void sieve_binary_activate(struct sieve_binary *sbin)
{
struct sieve_binary_extension_reg *const *regs;
unsigned int i, ext_count;
regs = array_get(&sbin->linked_extensions, &ext_count);
for ( i = 0; i < ext_count; i++ ) {
const struct sieve_extension *ext = regs[i]->extension;
if ( ext != NULL && ext->def != NULL && ext->def->binary_load != NULL )
ext->def->binary_load(ext, sbin);
}
}
void sieve_binary_extension_set_context
(struct sieve_binary *sbin, const struct sieve_extension *ext, void *context)
{
struct sieve_binary_extension_reg *ereg =
sieve_binary_extension_get_reg(sbin, ext, TRUE);
if ( ereg != NULL )
ereg->context = context;
}
const void *sieve_binary_extension_get_context
(struct sieve_binary *sbin, const struct sieve_extension *ext)
{
struct sieve_binary_extension_reg *ereg =
sieve_binary_extension_get_reg(sbin, ext, TRUE);
if ( ereg != NULL ) {
return ereg->context;
}
return NULL;
}
void sieve_binary_extension_set
(struct sieve_binary *sbin, const struct sieve_extension *ext,
const struct sieve_binary_extension *bext, void *context)
{
struct sieve_binary_extension_reg *ereg =
sieve_binary_extension_get_reg(sbin, ext, TRUE);
if ( ereg != NULL ) {
ereg->binext = bext;
if ( context != NULL )
ereg->context = context;
}
}
struct sieve_binary_block *sieve_binary_extension_create_block
(struct sieve_binary *sbin, const struct sieve_extension *ext)
{
struct sieve_binary_block *sblock;
struct sieve_binary_extension_reg *ereg =
sieve_binary_extension_get_reg(sbin, ext, TRUE);
i_assert(ereg != NULL);
sblock = sieve_binary_block_create(sbin);
if ( ereg->block_id < SBIN_SYSBLOCK_LAST )
ereg->block_id = sblock->id;
sblock->ext_index = ereg->index;
return sblock;
}
struct sieve_binary_block *sieve_binary_extension_get_block
(struct sieve_binary *sbin, const struct sieve_extension *ext)
{
struct sieve_binary_extension_reg *ereg =
sieve_binary_extension_get_reg(sbin, ext, TRUE);
i_assert(ereg != NULL);
if ( ereg->block_id < SBIN_SYSBLOCK_LAST )
return NULL;
return sieve_binary_block_get(sbin, ereg->block_id);
}
int sieve_binary_extension_link
(struct sieve_binary *sbin, const struct sieve_extension *ext)
{
return sieve_binary_extension_register(sbin, ext, NULL);
}
const struct sieve_extension *sieve_binary_extension_get_by_index
(struct sieve_binary *sbin, int index)
{
struct sieve_binary_extension_reg * const *ereg;
if ( index < (int) array_count(&sbin->extensions) ) {
ereg = array_idx(&sbin->extensions, (unsigned int) index);
return (*ereg)->extension;
}
return NULL;
}
int sieve_binary_extension_get_index
(struct sieve_binary *sbin, const struct sieve_extension *ext)
{
struct sieve_binary_extension_reg *ereg =
sieve_binary_extension_get_reg(sbin, ext, FALSE);
return ( ereg == NULL ? -1 : ereg->index );
}
int sieve_binary_extensions_count(struct sieve_binary *sbin)
{
return (int) array_count(&sbin->extensions);
}