1 /* Copyright (c) 2014-2018 Dovecot authors, see the included COPYING file */
2 
3 #include "lib.h"
4 #include "array.h"
5 #include "str.h"
6 #include "fts-language.h"
7 #include "fts-filter-private.h"
8 
9 #ifdef HAVE_LIBICU
10 #  include "fts-icu.h"
11 #endif
12 
13 static ARRAY(const struct fts_filter *) fts_filter_classes;
14 
fts_filters_init(void)15 void fts_filters_init(void)
16 {
17 	i_array_init(&fts_filter_classes, FTS_FILTER_CLASSES_NR);
18 
19 	fts_filter_register(fts_filter_stopwords);
20 	fts_filter_register(fts_filter_stemmer_snowball);
21 	fts_filter_register(fts_filter_normalizer_icu);
22 	fts_filter_register(fts_filter_lowercase);
23 	fts_filter_register(fts_filter_english_possessive);
24 	fts_filter_register(fts_filter_contractions);
25 }
26 
fts_filters_deinit(void)27 void fts_filters_deinit(void)
28 {
29 #ifdef HAVE_LIBICU
30 	fts_icu_deinit();
31 #endif
32 	array_free(&fts_filter_classes);
33 }
34 
fts_filter_register(const struct fts_filter * filter_class)35 void fts_filter_register(const struct fts_filter *filter_class)
36 {
37 	i_assert(fts_filter_find(filter_class->class_name) == NULL);
38 
39 	array_push_back(&fts_filter_classes, &filter_class);
40 }
41 
fts_filter_find(const char * name)42 const struct fts_filter *fts_filter_find(const char *name)
43 {
44 	const struct fts_filter *filter;
45 
46 	array_foreach_elem(&fts_filter_classes, filter) {
47 		if (strcmp(filter->class_name, name) == 0)
48 			return filter;
49 	}
50 	return NULL;
51 }
52 
fts_filter_create(const struct fts_filter * filter_class,struct fts_filter * parent,const struct fts_language * lang,const char * const * settings,struct fts_filter ** filter_r,const char ** error_r)53 int fts_filter_create(const struct fts_filter *filter_class,
54                       struct fts_filter *parent,
55                       const struct fts_language *lang,
56                       const char *const *settings,
57                       struct fts_filter **filter_r,
58                       const char **error_r)
59 {
60 	struct fts_filter *fp;
61 	const char *empty_settings = NULL;
62 
63 	i_assert(settings == NULL || str_array_length(settings) % 2 == 0);
64 
65 	if (settings == NULL)
66 		settings = &empty_settings;
67 
68 	if (filter_class->v.create != NULL) {
69 		if (filter_class->v.create(lang, settings, &fp, error_r) < 0) {
70 			*filter_r = NULL;
71 			return -1;
72 		}
73 	} else {
74 		/* default implementation */
75 		if (settings[0] != NULL) {
76 			*error_r = t_strdup_printf("Unknown setting: %s", settings[0]);
77 			return -1;
78 		}
79 		fp = i_new(struct fts_filter, 1);
80 		*fp = *filter_class;
81 	}
82 	fp->refcount = 1;
83 	fp->parent = parent;
84 	if (parent != NULL) {
85 		fts_filter_ref(parent);
86 	}
87 	*filter_r = fp;
88 	return 0;
89 }
fts_filter_ref(struct fts_filter * fp)90 void fts_filter_ref(struct fts_filter *fp)
91 {
92 	i_assert(fp->refcount > 0);
93 
94 	fp->refcount++;
95 }
96 
fts_filter_unref(struct fts_filter ** _fpp)97 void fts_filter_unref(struct fts_filter **_fpp)
98 {
99 	struct fts_filter *fp = *_fpp;
100 
101 	i_assert(fp->refcount > 0);
102 	*_fpp = NULL;
103 
104 	if (--fp->refcount > 0)
105 		return;
106 
107 	if (fp->parent != NULL)
108 		fts_filter_unref(&fp->parent);
109 	if (fp->v.destroy != NULL)
110 		fp->v.destroy(fp);
111 	else {
112 		/* default destroy implementation */
113 		str_free(&fp->token);
114 		i_free(fp);
115 	}
116 }
117 
fts_filter_filter(struct fts_filter * filter,const char ** token,const char ** error_r)118 int fts_filter_filter(struct fts_filter *filter, const char **token,
119 		      const char **error_r)
120 {
121 	int ret = 0;
122 
123 	i_assert((*token)[0] != '\0');
124 
125 	/* Recurse to parent. */
126 	if (filter->parent != NULL)
127 		ret = fts_filter_filter(filter->parent, token, error_r);
128 
129 	/* Parent returned token or no parent. */
130 	if (ret > 0 || filter->parent == NULL)
131 		ret = filter->v.filter(filter, token, error_r);
132 
133 	if (ret <= 0)
134 		*token = NULL;
135 	else {
136 		i_assert(*token != NULL);
137 		i_assert((*token)[0] != '\0');
138 	}
139 	return ret;
140 }
141