Coverage Report

Created: 2026-08-31 07:00

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/dovecot/src/lib-storage/mail-search-build.c
Line
Count
Source
1
/* Copyright (c) Dovecot authors, see top-level COPYING file */
2
3
#include "lib.h"
4
#include "str.h"
5
#include "charset-utf8.h"
6
#include "mail-storage-private.h"
7
#include "mail-search-register.h"
8
#include "mail-search-parser.h"
9
#include "mail-search-build.h"
10
11
12
static int mail_search_build_list(struct mail_search_build_context *ctx,
13
          struct mail_search_arg **arg_r);
14
15
struct mail_search_arg *
16
mail_search_build_new(struct mail_search_build_context *ctx,
17
          enum mail_search_arg_type type)
18
0
{
19
0
  struct mail_search_arg *arg;
20
21
0
  arg = p_new(ctx->pool, struct mail_search_arg, 1);
22
0
  arg->type = type;
23
0
  return arg;
24
0
}
25
26
struct mail_search_arg *
27
mail_search_build_str(struct mail_search_build_context *ctx,
28
          enum mail_search_arg_type type)
29
0
{
30
0
  struct mail_search_arg *sarg;
31
0
  const char *value;
32
33
0
  sarg = mail_search_build_new(ctx, type);
34
0
  if (mail_search_parse_string(ctx->parser, &value) < 0)
35
0
    return NULL;
36
0
  sarg->value.str = p_strdup(ctx->pool, value);
37
0
  return sarg;
38
0
}
39
40
static int
41
mail_search_build_key_int(struct mail_search_build_context *ctx,
42
        struct mail_search_arg *parent,
43
        struct mail_search_arg **arg_r)
44
0
{
45
0
  struct mail_search_arg *sarg;
46
0
  struct mail_search_arg *old_parent = ctx->parent;
47
0
  const char *key;
48
0
  const struct mail_search_register_arg *reg_arg;
49
0
  mail_search_register_fallback_t *fallback;
50
0
  int ret;
51
52
0
  ctx->parent = parent;
53
54
0
  if ((ret = mail_search_parse_key(ctx->parser, &key)) <= 0)
55
0
    return ret;
56
57
0
  if (strcmp(key, MAIL_SEARCH_PARSER_KEY_LIST) == 0) {
58
0
    if (mail_search_build_list(ctx, &sarg) < 0)
59
0
      return -1;
60
0
    if (sarg->value.subargs == NULL) {
61
0
      ctx->_error = "No search parameters inside list";
62
0
      return -1;
63
0
    }
64
65
0
    ctx->parent = old_parent;
66
0
    *arg_r = sarg;
67
0
    return 1;
68
0
  }
69
0
  key = t_str_ucase(key);
70
71
0
  reg_arg = mail_search_register_find(ctx->reg, key);
72
0
  if (reg_arg != NULL)
73
0
    sarg = reg_arg->build(ctx);
74
0
  else if (mail_search_register_get_fallback(ctx->reg, &fallback))
75
0
    sarg = fallback(ctx, key);
76
0
  else {
77
0
    sarg = NULL;
78
0
    ctx->_error = p_strconcat(ctx->pool, "Unknown argument ",
79
0
            key, NULL);
80
0
  }
81
82
0
  ctx->parent = old_parent;
83
0
  *arg_r = sarg;
84
0
  return sarg == NULL ? -1 : 1;
85
0
}
86
87
int mail_search_build_key(struct mail_search_build_context *ctx,
88
        struct mail_search_arg *parent,
89
        struct mail_search_arg **arg_r)
90
0
{
91
0
  int ret;
92
93
0
  ret = mail_search_build_key_int(ctx, parent, arg_r);
94
0
  if (ret <= 0) {
95
0
    if (ret == 0)
96
0
      ctx->_error = "Missing argument";
97
0
    return -1;
98
0
  }
99
0
  return 0;
100
0
}
101
102
static int mail_search_build_list(struct mail_search_build_context *ctx,
103
          struct mail_search_arg **arg_r)
104
0
{
105
0
  struct mail_search_arg *sarg, **subargs;
106
0
  enum mail_search_arg_type cur_type = SEARCH_SUB;
107
0
  int ret;
108
109
0
  sarg = p_new(ctx->pool, struct mail_search_arg, 1);
110
0
  sarg->type = cur_type;
111
112
0
  subargs = &sarg->value.subargs;
113
0
  while ((ret = mail_search_build_key_int(ctx, sarg, subargs)) > 0) {
114
0
    if (cur_type == sarg->type) {
115
      /* expected type */
116
0
    } else if (cur_type == SEARCH_SUB) {
117
      /* type changed. everything in this list must now
118
         belong to this type. */
119
0
      cur_type = sarg->type;
120
0
    } else {
121
0
      ctx->_error =
122
0
        "Use parenthesis when mixing ANDs and ORs";
123
0
      return -1;
124
0
    }
125
0
    subargs = &(*subargs)->next;
126
0
    sarg->type = SEARCH_SUB;
127
0
  }
128
0
  if (ret < 0)
129
0
    return -1;
130
0
  sarg->type = cur_type;
131
0
  *arg_r = sarg;
132
0
  return 0;
133
0
}
134
135
int mail_search_build(struct mail_search_register *reg,
136
          struct mail_search_parser *parser, const char **charset,
137
          struct mail_search_args **args_r,
138
          const char **client_error_r)
139
0
{
140
0
        struct mail_search_build_context ctx;
141
0
  struct mail_search_args *args;
142
0
  struct mail_search_arg *root;
143
0
  const char *str;
144
0
  int ret;
145
146
0
  *args_r = NULL;
147
0
  *client_error_r = NULL;
148
149
0
  i_zero(&ctx);
150
0
  ctx.args = args = mail_search_build_init();
151
0
  ctx.pool = args->pool;
152
0
  ctx.reg = reg;
153
0
  ctx.parser = parser;
154
0
  ctx.charset = p_strdup(ctx.pool, *charset);
155
156
0
  ret = mail_search_build_list(&ctx, &root);
157
0
  if (!ctx.charset_checked && ret == 0) {
158
    /* make sure we give an error message if charset is invalid */
159
0
    ret = mail_search_build_get_utf8(&ctx, "", &str);
160
0
  }
161
0
  if (ret < 0) {
162
0
    *client_error_r = ctx._error != NULL ? t_strdup(ctx._error) :
163
0
      t_strdup(mail_search_parser_get_error(parser));
164
0
    if (ctx.unknown_charset)
165
0
      *charset = NULL;
166
0
    pool_unref(&args->pool);
167
0
    return -1;
168
0
  }
169
170
0
  if (root->type == SEARCH_SUB && !root->match_not) {
171
    /* simple SUB root */
172
0
    args->args = root->value.subargs;
173
0
  } else {
174
0
    args->args = root;
175
0
  }
176
177
0
  *args_r = args;
178
0
  return 0;
179
0
}
180
181
struct mail_search_args *mail_search_build_init(void)
182
0
{
183
0
  struct mail_search_args *args;
184
0
  pool_t pool;
185
186
0
  pool = pool_alloconly_create("mail search args", 4096);
187
0
  args = p_new(pool, struct mail_search_args, 1);
188
0
  args->pool = pool;
189
0
  args->refcount = 1;
190
0
  return args;
191
0
}
192
193
struct mail_search_arg *
194
mail_search_build_add(struct mail_search_args *args,
195
          enum mail_search_arg_type type)
196
0
{
197
0
  struct mail_search_arg *arg;
198
199
0
  arg = p_new(args->pool, struct mail_search_arg, 1);
200
0
  arg->type = type;
201
202
0
  arg->next = args->args;
203
0
  args->args = arg;
204
0
  return arg;
205
0
}
206
207
void mail_search_build_add_all(struct mail_search_args *args)
208
0
{
209
0
  (void)mail_search_build_add(args, SEARCH_ALL);
210
0
}
211
212
void mail_search_build_add_seqset(struct mail_search_args *args,
213
          uint32_t seq1, uint32_t seq2)
214
0
{
215
0
  struct mail_search_arg *arg;
216
217
0
  arg = mail_search_build_add(args, SEARCH_SEQSET);
218
219
0
  p_array_init(&arg->value.seqset, args->pool, 1);
220
0
  seq_range_array_add_range(&arg->value.seqset, seq1, seq2);
221
0
}
222
223
int mail_search_build_get_utf8(struct mail_search_build_context *ctx,
224
             const char *input, const char **output_r)
225
0
{
226
0
  int ret;
227
228
0
  T_BEGIN {
229
0
    string_t *utf8 = t_str_new(128);
230
0
    enum charset_result result;
231
232
0
    if (charset_to_utf8_str(ctx->charset, NULL,
233
0
          input, utf8, &result) < 0) {
234
      /* unknown charset */
235
0
      ctx->_error = "Unknown charset";
236
0
      ctx->unknown_charset = TRUE;
237
0
      ret = -1;
238
0
    } else if (result != CHARSET_RET_OK) {
239
      /* invalid key */
240
0
      ctx->_error = "Invalid search key";
241
0
      ret = -1;
242
0
    } else {
243
0
      *output_r = p_strdup(ctx->pool, str_c(utf8));
244
0
      ret = 0;
245
0
    }
246
0
  } T_END;
247
248
0
  ctx->charset_checked = TRUE;
249
0
  return ret;
250
0
}