1 /* Copyright (c) 2009-2018 Dovecot authors, see the included COPYING file */
2
3 #include "lib.h"
4 #include "array.h"
5 #include "llist.h"
6 #include "hook-build.h"
7
8 struct hook_stack {
9 struct hook_stack *prev, *next;
10
11 /* Pointer to vfuncs struct. This assumes that a struct containing
12 function pointers equals to an array of function pointers. Not
13 ANSI-C, but should work in all OSes supported by Dovecot. Much
14 easier anyway than doing this work manually.. */
15 void (**vfuncs)();
16 /* nonzero in the areas where vfuncs has been changed */
17 void (**mask)();
18 };
19
20 struct hook_build_context {
21 pool_t pool;
22 /* size of the vfuncs struct */
23 size_t size;
24 /* number of function pointers in the struct */
25 unsigned int count;
26
27 struct hook_stack *head, *tail;
28 };
29
hook_build_append(struct hook_build_context * ctx,void (** vfuncs)())30 static void hook_build_append(struct hook_build_context *ctx, void (**vfuncs)())
31 {
32 struct hook_stack *stack;
33
34 stack = p_new(ctx->pool, struct hook_stack, 1);
35 stack->vfuncs = vfuncs;
36 stack->mask = p_malloc(ctx->pool, ctx->size);
37 DLLIST2_APPEND(&ctx->head, &ctx->tail, stack);
38 }
39
hook_build_init(void (** vfuncs)(),size_t size)40 struct hook_build_context *hook_build_init(void (**vfuncs)(), size_t size)
41 {
42 struct hook_build_context *ctx;
43 pool_t pool;
44
45 i_assert((size % sizeof(void (*)())) == 0);
46
47 pool = pool_alloconly_create("hook build context", 2048);
48 ctx = p_new(pool, struct hook_build_context, 1);
49 ctx->pool = pool;
50 ctx->size = size;
51 ctx->count = size / sizeof(void (*)());
52 hook_build_append(ctx, vfuncs);
53 return ctx;
54 }
55
56 static void
hook_update_mask(struct hook_build_context * ctx,struct hook_stack * stack,void (** vlast)())57 hook_update_mask(struct hook_build_context *ctx, struct hook_stack *stack,
58 void (**vlast)())
59 {
60 unsigned int i;
61
62 for (i = 0; i < ctx->count; i++) {
63 if (stack->vfuncs[i] != vlast[i]) {
64 i_assert(stack->vfuncs[i] != NULL);
65 stack->mask[i] = stack->vfuncs[i];
66 }
67 }
68 }
69
70 static void
hook_copy_stack(struct hook_build_context * ctx,struct hook_stack * stack)71 hook_copy_stack(struct hook_build_context *ctx, struct hook_stack *stack)
72 {
73 unsigned int i;
74
75 i_assert(stack->next != NULL);
76
77 for (i = 0; i < ctx->count; i++) {
78 if (stack->mask[i] == NULL) {
79 stack->vfuncs[i] = stack->next->vfuncs[i];
80 stack->mask[i] = stack->next->mask[i];
81 }
82 }
83 }
84
hook_build_update(struct hook_build_context * ctx,void * _vlast)85 void hook_build_update(struct hook_build_context *ctx, void *_vlast)
86 {
87 void (**vlast)() = _vlast;
88 struct hook_stack *stack;
89
90 if (ctx->tail->vfuncs == vlast) {
91 /* no vfuncs overridden */
92 return;
93 }
94
95 /* ctx->vfuncs_stack->vfuncs points to the root vfuncs,
96 ctx->vfuncs_stack->next->vfuncs points to the first super function
97 that is being called, and so on.
98
99 the previous plugin added its vfuncs to the stack tail.
100 vlast contains the previous plugin's super vfuncs, which is where
101 the next plugin should put its own vfuncs.
102
103 first we'll need to figure out what vfuncs the previous plugin
104 changed and update the mask */
105 hook_update_mask(ctx, ctx->tail, vlast);
106
107 /* now go up in the stack as long as the mask isn't set,
108 and update the vfuncs */
109 for (stack = ctx->tail->prev; stack != NULL; stack = stack->prev)
110 hook_copy_stack(ctx, stack);
111
112 /* add vlast to stack */
113 hook_build_append(ctx, vlast);
114 }
115
hook_build_deinit(struct hook_build_context ** _ctx)116 void hook_build_deinit(struct hook_build_context **_ctx)
117 {
118 struct hook_build_context *ctx = *_ctx;
119 *_ctx = NULL;
120 pool_unref(&ctx->pool);
121 }
122