bcb4e51a409d94ae670de96afb8483a4f7855294Stephan Bosch/* Copyright (c) 2009-2018 Dovecot authors, see the included COPYING file */
6004fdf3cc8fe87a6c35ce297d39ab68feb707b0Aki Tuomi
6004fdf3cc8fe87a6c35ce297d39ab68feb707b0Aki Tuomi#include "lib.h"
6004fdf3cc8fe87a6c35ce297d39ab68feb707b0Aki Tuomi#include "array.h"
6004fdf3cc8fe87a6c35ce297d39ab68feb707b0Aki Tuomi#include "llist.h"
6004fdf3cc8fe87a6c35ce297d39ab68feb707b0Aki Tuomi#include "hook-build.h"
6004fdf3cc8fe87a6c35ce297d39ab68feb707b0Aki Tuomi
6004fdf3cc8fe87a6c35ce297d39ab68feb707b0Aki Tuomistruct hook_stack {
6004fdf3cc8fe87a6c35ce297d39ab68feb707b0Aki Tuomi struct hook_stack *prev, *next;
6004fdf3cc8fe87a6c35ce297d39ab68feb707b0Aki Tuomi
6004fdf3cc8fe87a6c35ce297d39ab68feb707b0Aki Tuomi /* Pointer to vfuncs struct. This assumes that a struct containing
6004fdf3cc8fe87a6c35ce297d39ab68feb707b0Aki Tuomi function pointers equals to an array of function pointers. Not
6004fdf3cc8fe87a6c35ce297d39ab68feb707b0Aki Tuomi ANSI-C, but should work in all OSes supported by Dovecot. Much
6004fdf3cc8fe87a6c35ce297d39ab68feb707b0Aki Tuomi easier anyway than doing this work manually.. */
6004fdf3cc8fe87a6c35ce297d39ab68feb707b0Aki Tuomi void (**vfuncs)();
6004fdf3cc8fe87a6c35ce297d39ab68feb707b0Aki Tuomi /* nonzero in the areas where vfuncs has been changed */
6004fdf3cc8fe87a6c35ce297d39ab68feb707b0Aki Tuomi void (**mask)();
6004fdf3cc8fe87a6c35ce297d39ab68feb707b0Aki Tuomi};
6004fdf3cc8fe87a6c35ce297d39ab68feb707b0Aki Tuomi
6004fdf3cc8fe87a6c35ce297d39ab68feb707b0Aki Tuomistruct hook_build_context {
6004fdf3cc8fe87a6c35ce297d39ab68feb707b0Aki Tuomi pool_t pool;
6004fdf3cc8fe87a6c35ce297d39ab68feb707b0Aki Tuomi /* size of the vfuncs struct */
6004fdf3cc8fe87a6c35ce297d39ab68feb707b0Aki Tuomi size_t size;
6004fdf3cc8fe87a6c35ce297d39ab68feb707b0Aki Tuomi /* number of function pointers in the struct */
6004fdf3cc8fe87a6c35ce297d39ab68feb707b0Aki Tuomi unsigned int count;
6004fdf3cc8fe87a6c35ce297d39ab68feb707b0Aki Tuomi
6004fdf3cc8fe87a6c35ce297d39ab68feb707b0Aki Tuomi struct hook_stack *head, *tail;
6004fdf3cc8fe87a6c35ce297d39ab68feb707b0Aki Tuomi};
6004fdf3cc8fe87a6c35ce297d39ab68feb707b0Aki Tuomi
6004fdf3cc8fe87a6c35ce297d39ab68feb707b0Aki Tuomistatic void hook_build_append(struct hook_build_context *ctx, void (**vfuncs)())
6004fdf3cc8fe87a6c35ce297d39ab68feb707b0Aki Tuomi{
6004fdf3cc8fe87a6c35ce297d39ab68feb707b0Aki Tuomi struct hook_stack *stack;
6004fdf3cc8fe87a6c35ce297d39ab68feb707b0Aki Tuomi
6004fdf3cc8fe87a6c35ce297d39ab68feb707b0Aki Tuomi stack = p_new(ctx->pool, struct hook_stack, 1);
6004fdf3cc8fe87a6c35ce297d39ab68feb707b0Aki Tuomi stack->vfuncs = vfuncs;
6004fdf3cc8fe87a6c35ce297d39ab68feb707b0Aki Tuomi stack->mask = p_malloc(ctx->pool, ctx->size);
6004fdf3cc8fe87a6c35ce297d39ab68feb707b0Aki Tuomi DLLIST2_APPEND(&ctx->head, &ctx->tail, stack);
6004fdf3cc8fe87a6c35ce297d39ab68feb707b0Aki Tuomi}
6004fdf3cc8fe87a6c35ce297d39ab68feb707b0Aki Tuomi
6004fdf3cc8fe87a6c35ce297d39ab68feb707b0Aki Tuomistruct hook_build_context *hook_build_init(void (**vfuncs)(), size_t size)
6004fdf3cc8fe87a6c35ce297d39ab68feb707b0Aki Tuomi{
6004fdf3cc8fe87a6c35ce297d39ab68feb707b0Aki Tuomi struct hook_build_context *ctx;
6004fdf3cc8fe87a6c35ce297d39ab68feb707b0Aki Tuomi pool_t pool;
6004fdf3cc8fe87a6c35ce297d39ab68feb707b0Aki Tuomi
6004fdf3cc8fe87a6c35ce297d39ab68feb707b0Aki Tuomi i_assert((size % sizeof(void (*)())) == 0);
6004fdf3cc8fe87a6c35ce297d39ab68feb707b0Aki Tuomi
6004fdf3cc8fe87a6c35ce297d39ab68feb707b0Aki Tuomi pool = pool_alloconly_create("hook build context", 2048);
6004fdf3cc8fe87a6c35ce297d39ab68feb707b0Aki Tuomi ctx = p_new(pool, struct hook_build_context, 1);
6004fdf3cc8fe87a6c35ce297d39ab68feb707b0Aki Tuomi ctx->pool = pool;
6004fdf3cc8fe87a6c35ce297d39ab68feb707b0Aki Tuomi ctx->size = size;
6004fdf3cc8fe87a6c35ce297d39ab68feb707b0Aki Tuomi ctx->count = size / sizeof(void (*)());
6004fdf3cc8fe87a6c35ce297d39ab68feb707b0Aki Tuomi hook_build_append(ctx, vfuncs);
6004fdf3cc8fe87a6c35ce297d39ab68feb707b0Aki Tuomi return ctx;
6004fdf3cc8fe87a6c35ce297d39ab68feb707b0Aki Tuomi}
6004fdf3cc8fe87a6c35ce297d39ab68feb707b0Aki Tuomi
6004fdf3cc8fe87a6c35ce297d39ab68feb707b0Aki Tuomistatic void
6004fdf3cc8fe87a6c35ce297d39ab68feb707b0Aki Tuomihook_update_mask(struct hook_build_context *ctx, struct hook_stack *stack,
6004fdf3cc8fe87a6c35ce297d39ab68feb707b0Aki Tuomi void (**vlast)())
6004fdf3cc8fe87a6c35ce297d39ab68feb707b0Aki Tuomi{
6004fdf3cc8fe87a6c35ce297d39ab68feb707b0Aki Tuomi unsigned int i;
6004fdf3cc8fe87a6c35ce297d39ab68feb707b0Aki Tuomi
6004fdf3cc8fe87a6c35ce297d39ab68feb707b0Aki Tuomi for (i = 0; i < ctx->count; i++) {
6004fdf3cc8fe87a6c35ce297d39ab68feb707b0Aki Tuomi if (stack->vfuncs[i] != vlast[i]) {
6004fdf3cc8fe87a6c35ce297d39ab68feb707b0Aki Tuomi i_assert(stack->vfuncs[i] != NULL);
6004fdf3cc8fe87a6c35ce297d39ab68feb707b0Aki Tuomi stack->mask[i] = stack->vfuncs[i];
6004fdf3cc8fe87a6c35ce297d39ab68feb707b0Aki Tuomi }
6004fdf3cc8fe87a6c35ce297d39ab68feb707b0Aki Tuomi }
6004fdf3cc8fe87a6c35ce297d39ab68feb707b0Aki Tuomi}
6004fdf3cc8fe87a6c35ce297d39ab68feb707b0Aki Tuomi
6004fdf3cc8fe87a6c35ce297d39ab68feb707b0Aki Tuomistatic void
6004fdf3cc8fe87a6c35ce297d39ab68feb707b0Aki Tuomihook_copy_stack(struct hook_build_context *ctx, struct hook_stack *stack)
6004fdf3cc8fe87a6c35ce297d39ab68feb707b0Aki Tuomi{
6004fdf3cc8fe87a6c35ce297d39ab68feb707b0Aki Tuomi unsigned int i;
6004fdf3cc8fe87a6c35ce297d39ab68feb707b0Aki Tuomi
6004fdf3cc8fe87a6c35ce297d39ab68feb707b0Aki Tuomi i_assert(stack->next != NULL);
6004fdf3cc8fe87a6c35ce297d39ab68feb707b0Aki Tuomi
6004fdf3cc8fe87a6c35ce297d39ab68feb707b0Aki Tuomi for (i = 0; i < ctx->count; i++) {
6004fdf3cc8fe87a6c35ce297d39ab68feb707b0Aki Tuomi if (stack->mask[i] == NULL) {
6004fdf3cc8fe87a6c35ce297d39ab68feb707b0Aki Tuomi stack->vfuncs[i] = stack->next->vfuncs[i];
6004fdf3cc8fe87a6c35ce297d39ab68feb707b0Aki Tuomi stack->mask[i] = stack->next->mask[i];
6004fdf3cc8fe87a6c35ce297d39ab68feb707b0Aki Tuomi }
6004fdf3cc8fe87a6c35ce297d39ab68feb707b0Aki Tuomi }
6004fdf3cc8fe87a6c35ce297d39ab68feb707b0Aki Tuomi}
6004fdf3cc8fe87a6c35ce297d39ab68feb707b0Aki Tuomi
6004fdf3cc8fe87a6c35ce297d39ab68feb707b0Aki Tuomivoid hook_build_update(struct hook_build_context *ctx, void *_vlast)
6004fdf3cc8fe87a6c35ce297d39ab68feb707b0Aki Tuomi{
6004fdf3cc8fe87a6c35ce297d39ab68feb707b0Aki Tuomi void (**vlast)() = _vlast;
6004fdf3cc8fe87a6c35ce297d39ab68feb707b0Aki Tuomi struct hook_stack *stack;
6004fdf3cc8fe87a6c35ce297d39ab68feb707b0Aki Tuomi
6004fdf3cc8fe87a6c35ce297d39ab68feb707b0Aki Tuomi if (ctx->tail->vfuncs == vlast) {
6004fdf3cc8fe87a6c35ce297d39ab68feb707b0Aki Tuomi /* no vfuncs overridden */
6004fdf3cc8fe87a6c35ce297d39ab68feb707b0Aki Tuomi return;
6004fdf3cc8fe87a6c35ce297d39ab68feb707b0Aki Tuomi }
6004fdf3cc8fe87a6c35ce297d39ab68feb707b0Aki Tuomi
6004fdf3cc8fe87a6c35ce297d39ab68feb707b0Aki Tuomi /* ctx->vfuncs_stack->vfuncs points to the root vfuncs,
6004fdf3cc8fe87a6c35ce297d39ab68feb707b0Aki Tuomi ctx->vfuncs_stack->next->vfuncs points to the first super function
6004fdf3cc8fe87a6c35ce297d39ab68feb707b0Aki Tuomi that is being called, and so on.
6004fdf3cc8fe87a6c35ce297d39ab68feb707b0Aki Tuomi
6004fdf3cc8fe87a6c35ce297d39ab68feb707b0Aki Tuomi the previous plugin added its vfuncs to the stack tail.
6004fdf3cc8fe87a6c35ce297d39ab68feb707b0Aki Tuomi vlast contains the previous plugin's super vfuncs, which is where
6004fdf3cc8fe87a6c35ce297d39ab68feb707b0Aki Tuomi the next plugin should put its own vfuncs.
6004fdf3cc8fe87a6c35ce297d39ab68feb707b0Aki Tuomi
6004fdf3cc8fe87a6c35ce297d39ab68feb707b0Aki Tuomi first we'll need to figure out what vfuncs the previous plugin
6004fdf3cc8fe87a6c35ce297d39ab68feb707b0Aki Tuomi changed and update the mask */
6004fdf3cc8fe87a6c35ce297d39ab68feb707b0Aki Tuomi hook_update_mask(ctx, ctx->tail, vlast);
6004fdf3cc8fe87a6c35ce297d39ab68feb707b0Aki Tuomi
6004fdf3cc8fe87a6c35ce297d39ab68feb707b0Aki Tuomi /* now go up in the stack as long as the mask isn't set,
6004fdf3cc8fe87a6c35ce297d39ab68feb707b0Aki Tuomi and update the vfuncs */
6004fdf3cc8fe87a6c35ce297d39ab68feb707b0Aki Tuomi for (stack = ctx->tail->prev; stack != NULL; stack = stack->prev)
6004fdf3cc8fe87a6c35ce297d39ab68feb707b0Aki Tuomi hook_copy_stack(ctx, stack);
6004fdf3cc8fe87a6c35ce297d39ab68feb707b0Aki Tuomi
6004fdf3cc8fe87a6c35ce297d39ab68feb707b0Aki Tuomi /* add vlast to stack */
6004fdf3cc8fe87a6c35ce297d39ab68feb707b0Aki Tuomi hook_build_append(ctx, vlast);
6004fdf3cc8fe87a6c35ce297d39ab68feb707b0Aki Tuomi}
6004fdf3cc8fe87a6c35ce297d39ab68feb707b0Aki Tuomi
6004fdf3cc8fe87a6c35ce297d39ab68feb707b0Aki Tuomivoid hook_build_deinit(struct hook_build_context **_ctx)
6004fdf3cc8fe87a6c35ce297d39ab68feb707b0Aki Tuomi{
6004fdf3cc8fe87a6c35ce297d39ab68feb707b0Aki Tuomi struct hook_build_context *ctx = *_ctx;
6004fdf3cc8fe87a6c35ce297d39ab68feb707b0Aki Tuomi *_ctx = NULL;
6004fdf3cc8fe87a6c35ce297d39ab68feb707b0Aki Tuomi pool_unref(&ctx->pool);
6004fdf3cc8fe87a6c35ce297d39ab68feb707b0Aki Tuomi}