Coverage Report

Created: 2026-09-14 07:03

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/pigeonhole/src/lib-sieve/sieve-plugins.c
Line
Count
Source
1
/* Copyright (c) Pigeonhole authors, see top-level COPYING file */
2
3
#include "lib.h"
4
#include "str.h"
5
#include "settings-parser.h"
6
#include "module-dir.h"
7
#include "master-service.h"
8
9
#include "sieve-extensions.h"
10
11
#include "sieve-common.h"
12
#include "sieve-plugins.h"
13
14
/*
15
 * Types
16
 */
17
18
typedef int
19
(*sieve_plugin_load_func_t)(struct sieve_instance *svinst, void **context);
20
typedef void
21
(*sieve_plugin_unload_func_t)(struct sieve_instance *svinst, void *context);
22
23
struct sieve_plugin {
24
  struct module *module;
25
26
  void *context;
27
28
  struct sieve_plugin *next;
29
};
30
31
/*
32
 * Plugin support
33
 */
34
35
static struct module *sieve_modules = NULL;
36
static int sieve_modules_refcount = 0;
37
38
static struct module *sieve_plugin_module_find(const char *name)
39
0
{
40
0
  struct module *module;
41
42
0
  module = sieve_modules;
43
0
  while (module != NULL) {
44
0
    const char *mod_name;
45
46
    /* Strip module names */
47
0
    mod_name = module_get_plugin_name(module);
48
0
    if (strcmp(mod_name, name) == 0)
49
0
      return module;
50
51
0
    module = module->next;
52
0
  }
53
0
  return NULL;
54
0
}
55
56
int sieve_plugins_load(struct sieve_instance *svinst, const char *path,
57
           const char *plugins)
58
0
{
59
0
  struct module *module;
60
0
  struct module_dir_load_settings mod_set;
61
0
  const char *const *module_names;
62
0
  unsigned int i;
63
64
  /* Determine what to load */
65
66
0
  if (path == NULL && plugins == NULL) {
67
    /* From settings */
68
0
    module_names = settings_boollist_get(&svinst->set->plugins);
69
0
    path = svinst->set->plugin_dir;
70
0
  } else {
71
    /* From function parameters */
72
0
    const char **module_names_mod;
73
74
0
    if (plugins == NULL || *plugins == '\0')
75
0
      return 0;
76
0
    module_names_mod = t_strsplit_spaces(plugins, ", ");
77
78
0
    if (path == NULL || *path == '\0')
79
0
      path = sieve_default_settings.plugin_dir;
80
81
0
    for (i = 0; module_names_mod[i] != NULL; i++) {
82
      /* Allow giving the module names also in non-base form.
83
       */
84
0
      module_names_mod[i] =
85
0
        module_file_get_name(module_names_mod[i]);
86
0
    }
87
0
    module_names = module_names_mod;
88
0
  }
89
90
0
  if (module_names == NULL || *module_names == NULL)
91
0
    return 0;
92
93
0
  i_zero(&mod_set);
94
0
  mod_set.abi_version = PIGEONHOLE_ABI_VERSION;
95
0
  mod_set.binary_name = master_service_get_name(master_service);
96
0
  mod_set.setting_name = "sieve_plugins";
97
0
  mod_set.require_init_funcs = TRUE;
98
0
  mod_set.debug = svinst->debug;
99
100
  /* Load missing plugin modules */
101
102
0
  sieve_modules = module_dir_load_missing(sieve_modules, path,
103
0
            module_names, &mod_set);
104
105
  /* Call plugin load functions for this Sieve instance */
106
107
0
  if (svinst->plugins == NULL)
108
0
    sieve_modules_refcount++;
109
110
0
  for (i = 0; module_names[i] != NULL; i++) {
111
0
    struct sieve_plugin *plugin;
112
0
    const char *name = module_names[i];
113
0
    sieve_plugin_load_func_t load_func;
114
115
    /* Find the module */
116
0
    module = sieve_plugin_module_find(name);
117
0
    i_assert(module != NULL);
118
119
    /* Check whether the plugin is already loaded in this instance */
120
0
    plugin = svinst->plugins;
121
0
    while (plugin != NULL) {
122
0
      if (plugin->module == module)
123
0
        break;
124
0
      plugin = plugin->next;
125
0
    }
126
127
    /* Skip it if it is loaded already */
128
0
    if (plugin != NULL)
129
0
      continue;
130
131
    /* Create plugin list item */
132
0
    plugin = p_new(svinst->pool, struct sieve_plugin, 1);
133
0
    plugin->module = module;
134
135
    /* Call load function */
136
0
    load_func = (sieve_plugin_load_func_t)
137
0
      module_get_symbol(module,
138
0
        t_strdup_printf("%s_load", module->name));
139
0
    if (load_func != NULL &&
140
0
        load_func(svinst, &plugin->context) < 0)
141
0
      return -1;
142
143
    /* Add plugin to the instance */
144
0
    if (svinst->plugins == NULL)
145
0
      svinst->plugins = plugin;
146
0
    else {
147
0
      struct sieve_plugin *plugin_last;
148
149
0
      plugin_last = svinst->plugins;
150
0
      while ( plugin_last->next != NULL )
151
0
        plugin_last = plugin_last->next;
152
153
0
      plugin_last->next = plugin;
154
0
    }
155
0
  }
156
0
  return 0;
157
0
}
158
159
void sieve_plugins_unload(struct sieve_instance *svinst)
160
0
{
161
0
  struct sieve_plugin *plugin;
162
163
0
  if (svinst->plugins == NULL)
164
0
    return;
165
166
  /* Call plugin unload functions for this instance */
167
168
0
  plugin = svinst->plugins;
169
0
  while (plugin != NULL) {
170
0
    struct module *module = plugin->module;
171
0
    sieve_plugin_unload_func_t unload_func;
172
173
0
    unload_func = (sieve_plugin_unload_func_t)
174
0
      module_get_symbol(
175
0
        module,
176
0
        t_strdup_printf("%s_unload", module->name));
177
0
    if (unload_func != NULL)
178
0
      unload_func(svinst, plugin->context);
179
180
0
    plugin = plugin->next;
181
0
  }
182
183
  /* Physically unload modules */
184
185
0
  i_assert(sieve_modules_refcount > 0);
186
187
0
  if (--sieve_modules_refcount != 0)
188
0
    return;
189
190
0
  module_dir_unload(&sieve_modules);
191
0
}