Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,4 @@ lib21_fts_elastic_plugin_la_LDFLAGS = -module -avoid-version

lib21_fts_elastic_plugin_ladir = $(dovecot_pkglibdir)
lib21_fts_elastic_plugin_la_LTLIBRARIES = lib21_fts_elastic_plugin.la
lib21_fts_elastic_plugin_la_SOURCES = fts-elastic-plugin.c fts-backend-elastic.c elastic-connection.c
lib21_fts_elastic_plugin_la_SOURCES = fts-elastic-plugin.c fts-backend-elastic.c elastic-connection.c fts-elastic-settings.c
13 changes: 7 additions & 6 deletions src/elastic-connection.c
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,8 @@ struct elastic_connection {
int elastic_connection_init(const struct fts_elastic_settings *set,
struct mail_namespace *ns,
struct elastic_connection **conn_r,
const char **error_r)
const char **error_r,
struct event *parent)
{
f_debug("start");
struct http_client_settings http_set;
Expand Down Expand Up @@ -111,11 +112,11 @@ int elastic_connection_init(const struct fts_elastic_settings *set,
http_set.max_idle_time_msecs = 5 * 1000;
http_set.max_parallel_connections = 1;
http_set.max_pipelined_requests = 1;
http_set.max_redirects = 1;
http_set.max_attempts = 3;
http_set.debug = set->debug;
http_set.rawlog_dir = set->rawlog_dir;
elastic_http_client = http_client_init(&http_set);
http_set.request_max_redirects = 1;
http_set.request_max_attempts = 3;
// http_set.debug = set->debug;
http_set.rawlog_dir = set->rawlog_dir;
elastic_http_client = http_client_init(&http_set, parent);
}

*conn_r = conn;
Expand Down
3 changes: 2 additions & 1 deletion src/elastic-connection.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@ struct elastic_search_context;
int elastic_connection_init(const struct fts_elastic_settings *set,
struct mail_namespace *ns,
struct elastic_connection **conn_r,
const char **error_r);
const char **error_r,
struct event *parent);

void elastic_connection_deinit(struct elastic_connection *conn);

Expand Down
73 changes: 41 additions & 32 deletions src/fts-backend-elastic.c
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,11 @@ fts_backend_elastic_init(struct fts_backend *_backend, const char **error_r)
}

f_debug("end");
return elastic_connection_init(&fuser->set, _backend->ns, &backend->conn, error_r);
return elastic_connection_init(fuser->set,
_backend->ns,
&backend->conn,
error_r,
_backend->event);
}

static void
Expand Down Expand Up @@ -477,7 +481,7 @@ fts_backend_elastic_uid_changed(struct fts_backend_update_context *_ctx,
}

/* chunk up our requests in to reasonable sizes */
if (str_len(ctx->json_request) > fuser->set.bulk_size) {
if (str_len(ctx->json_request) > fuser->set->bulk_size) {
/* do an early post */
elastic_connection_bulk(backend->conn, ctx->json_request);

Expand Down Expand Up @@ -612,7 +616,7 @@ static int fts_backend_elastic_refresh(struct fts_backend *_backend)
struct fts_elastic_user *fuser =
FTS_ELASTIC_USER_CONTEXT(_backend->ns->user);

if (fuser->set.refresh_by_fts) {
if (fuser->set->refresh_by_fts) {
elastic_connection_refresh(backend->conn);
}
f_debug("end");
Expand Down Expand Up @@ -1020,43 +1024,48 @@ fts_backend_elastic_lookup(struct fts_backend *_backend, struct mailbox *box,
ret = elastic_connection_search(backend->conn, pool, query, result_r);
}

/* FTS_LOOKUP_FLAG_NO_AUTO_FUZZY says that exact matches for non-fuzzy searches
* should go to maybe_uids instead of definite_uids. */
ARRAY_TYPE(seq_range) uids_tmp;
if ((flags & FTS_LOOKUP_FLAG_NO_AUTO_FUZZY) != 0) {
uids_tmp = result_r->definite_uids;
result_r->definite_uids = result_r->maybe_uids;
result_r->maybe_uids = uids_tmp;
}

/* clean-up */
pool_unref(&pool);
f_debug("return %d", ret);
return ret;
}

static int
fts_backend_elastic_is_uid_indexed(struct fts_backend *backend,
struct mailbox *mailbox,
uint32_t uid,
uint32_t *modseq_r)
{
/* suppress unused‐parameter warnings */
(void)backend;
(void)mailbox;
(void)uid;
(void)modseq_r;
/* index *every* message */
return 1;
}

struct fts_backend fts_backend_elastic = {
.name = "elastic",
.flags = FTS_BACKEND_FLAG_FUZZY_SEARCH,

{
fts_backend_elastic_alloc,
fts_backend_elastic_init,
fts_backend_elastic_deinit,
fts_backend_elastic_get_last_uid,
fts_backend_elastic_update_init,
fts_backend_elastic_update_deinit,
fts_backend_elastic_update_set_mailbox,
fts_backend_elastic_update_expunge,
fts_backend_elastic_update_set_build_key,
fts_backend_elastic_update_unset_build_key,
fts_backend_elastic_update_build_more,
fts_backend_elastic_refresh,
fts_backend_elastic_rescan,
fts_backend_elastic_optimize,
fts_backend_default_can_lookup,
fts_backend_elastic_lookup,
NULL,
NULL
}
.v = {
.alloc = fts_backend_elastic_alloc,
.init = fts_backend_elastic_init,
.deinit = fts_backend_elastic_deinit,
.get_last_uid = fts_backend_elastic_get_last_uid,
.is_uid_indexed = fts_backend_elastic_is_uid_indexed,
.update_init = fts_backend_elastic_update_init,
.update_deinit = fts_backend_elastic_update_deinit,
.update_set_mailbox = fts_backend_elastic_update_set_mailbox,
.update_expunge = fts_backend_elastic_update_expunge,
.update_set_build_key = fts_backend_elastic_update_set_build_key,
.update_unset_build_key = fts_backend_elastic_update_unset_build_key,
.update_build_more = fts_backend_elastic_update_build_more,
.refresh = fts_backend_elastic_refresh,
.rescan = fts_backend_elastic_rescan,
.optimize = fts_backend_elastic_optimize,
.can_lookup = fts_backend_default_can_lookup,
.lookup = fts_backend_elastic_lookup,
},
};
136 changes: 40 additions & 96 deletions src/fts-elastic-plugin.c
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#include "mail-storage-hooks.h"
#include "fts-user.h"
#include "fts-elastic-plugin.h"
#include "settings.h"

#include <stdlib.h>

Expand All @@ -18,125 +19,68 @@ struct http_client *elastic_http_client = NULL;
struct fts_elastic_user_module fts_elastic_user_module =
MODULE_CONTEXT_INIT(&mail_user_module_register);

static int
fts_elastic_plugin_init_settings(struct mail_user *user,
struct fts_elastic_settings *set,
const char *str)
{
f_debug("start");
const char *const *tmp;

/* validate our parameters */
if (user == NULL || set == NULL) {
i_error("fts_elastic: critical error initialisation");
return -1;
}

if (str == NULL) {
str = "";
}

set->bulk_size = 5*1024*1024; /* 5 MB */
set->refresh_by_fts = TRUE;
set->refresh_on_update = FALSE;

tmp = t_strsplit_spaces(str, " ");
for (; *tmp != NULL; tmp++) {
if (strncmp(*tmp, "url=", 4) == 0) {
set->url = p_strdup(user->pool, *tmp + 4);
} else if (strcmp(*tmp, "debug") == 0) {
set->debug = TRUE;
} else if (strncmp(*tmp, "rawlog_dir=", 11) == 0) {
set->rawlog_dir = p_strdup(user->pool, *tmp + 11);
} else if (strncmp(*tmp, "bulk_size=", 10) == 0) {
if (str_to_uint(*tmp+10, &set->bulk_size) < 0 || set->bulk_size == 0) {
i_error("fts_elastic: bulk_size='%s' must be a positive integer", *tmp+10);
return -1;
}
} else if (strncmp(*tmp, "refresh=", 8) == 0) {
if (strcmp(*tmp + 8, "never") == 0) {
set->refresh_on_update = FALSE;
set->refresh_by_fts = FALSE;
} else if (strcmp(*tmp + 8, "update") == 0) {
set->refresh_on_update = TRUE;
} else if (strcmp(*tmp + 8, "fts") == 0) {
set->refresh_by_fts = TRUE;
} else {
i_error("fts_elastic: Invalid setting for refresh: %s", *tmp+8);
return -1;
}
} else {
i_error("fts_elastic: Invalid setting: %s", *tmp);
return -1;
}
}

f_debug("end");
return 0;
}

#if defined(DOVECOT_PREREQ) && DOVECOT_PREREQ(2,3,17)
static void fts_elastic_mail_user_deinit(struct mail_user *user)
{
struct fts_elastic_user *fuser = FTS_ELASTIC_USER_CONTEXT_REQUIRE(user);

fts_mail_user_deinit(user);
fuser->module_ctx.super.deinit(user);
}
#endif

static void fts_elastic_mail_user_create(struct mail_user *user, const char *env)
{
f_debug("start");
struct fts_elastic_user *fuser = NULL;
#if defined(DOVECOT_PREREQ) && DOVECOT_PREREQ(2,3,17)
struct mail_user_vfuncs *v = user->vlast;
const char *error;
#endif
static struct event_category event_category_fts_elastic = {
.name = FTS_ELASTIC_LABEL,
.parent = &event_category_fts
};

/* validate our parameters */
if (user == NULL || env == NULL) {
i_error("fts_elastic: critical error during mail user creation");
return;
}
static int fts_elastic_mail_user_get(struct mail_user *user,
struct event *event,
struct fts_elastic_user **fuser_r,
const char **error_r)
{
struct fts_elastic_user *fuser;
struct fts_elastic_settings *set;

/* allocate per-user context */
fuser = p_new(user->pool, struct fts_elastic_user, 1);
if (fts_elastic_plugin_init_settings(user, &fuser->set, env) < 0) {
/* invalid settings, disabling */
return;
}

#if defined(DOVECOT_PREREQ) && DOVECOT_PREREQ(2,3,17)
if (fts_mail_user_init(user, FALSE, &error) < 0) {
i_error("fts_elastic: %s", error);
return;
/* parse plugin settings from the tagged event */
if (settings_get(event,
&fts_elastic_setting_parser_info,
0, &set, error_r) < 0) {
return -1;
}
fuser->set = set;

fuser->module_ctx.super = *v;
user->vlast = &fuser->module_ctx.super;
v->deinit = fts_elastic_mail_user_deinit;
#endif
/* initialize the core FTS user with the same event */
if (fts_mail_user_init(user, event, FALSE, error_r) < 0) {
return -1;
}

MODULE_CONTEXT_SET(user, fts_elastic_user_module, fuser);
f_debug("end");
*fuser_r = fuser;
return 0;
}

static void fts_elastic_mail_user_created(struct mail_user *user)
{
f_debug("start");
const char *env = NULL;
struct mail_user_vfuncs *v = user->vlast;
struct event *ev = event_create(user->event);
struct fts_elastic_user *fuser;
const char *error;

/* validate our parameters */
if (user == NULL) {
i_error("fts_elastic: critical error during mail user creation");
} else {
env = mail_user_plugin_getenv(user, "fts_elastic");
/* scope settings lookup to fts_elastic */
event_add_category(ev, &event_category_fts_elastic);

if (env != NULL) {
fts_elastic_mail_user_create(user, env);
}
/* pull in per-user config and init core FTS */
if (fts_elastic_mail_user_get(user, ev, &fuser, &error) < 0) {
event_unref(&ev);
return;
}
f_debug("end");

/* chain into dovecot’s vfunc stack */
fuser->module_ctx.super = *v;
user->vlast = &fuser->module_ctx.super;
v->deinit = fts_elastic_mail_user_deinit;
}

static struct mail_storage_hooks fts_elastic_mail_storage_hooks = {
Expand Down
7 changes: 6 additions & 1 deletion src/fts-elastic-plugin.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
#include "mail-user.h"
#include "fts-api-private.h"

#define FTS_ELASTIC_LABEL "fts-elastic"

#define FTS_ELASTIC_USER_CONTEXT(obj) \
MODULE_CONTEXT(obj, fts_elastic_user_module)
#define FTS_ELASTIC_USER_CONTEXT_REQUIRE(obj) \
Expand All @@ -16,6 +18,7 @@
#endif

struct fts_elastic_settings {
pool_t pool; /* must be first for the settings parser */
const char *url; /* base URL to an ElasticSearch instance */
const char *rawlog_dir; /* directory where raw http request and response will be saved */
unsigned int bulk_size; /* maximum size of values indexed in _bulk requests default=5MB */
Expand All @@ -24,9 +27,11 @@ struct fts_elastic_settings {
bool debug; /* whether or not debug is set */
};

extern const struct setting_parser_info fts_elastic_setting_parser_info;

struct fts_elastic_user {
union mail_user_module_context module_ctx; /* mail user context */
struct fts_elastic_settings set; /* loaded settings */
struct fts_elastic_settings *set; /* loaded settings */
};

extern const char *fts_elastic_plugin_dependencies[];
Expand Down
Loading