Coverage Report

Created: 2026-07-30 06:43

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/pigeonhole/src/lib-sieve/plugins/mailbox/tst-mailboxexists.c
Line
Count
Source
1
/* Copyright (c) Pigeonhole authors, see top-level COPYING file */
2
3
#include "lib.h"
4
#include "str-sanitize.h"
5
#include "mail-storage.h"
6
#include "mail-namespace.h"
7
8
#include "sieve-common.h"
9
#include "sieve-actions.h"
10
#include "sieve-extensions.h"
11
#include "sieve-commands.h"
12
#include "sieve-stringlist.h"
13
#include "sieve-code.h"
14
#include "sieve-validator.h"
15
#include "sieve-generator.h"
16
#include "sieve-interpreter.h"
17
#include "sieve-dump.h"
18
19
#include "ext-mailbox-common.h"
20
21
/*
22
 * Mailboxexists command
23
 *
24
 * Syntax:
25
 *    mailboxexists <mailbox-names: string-list>
26
 */
27
28
static bool
29
tst_mailboxexists_validate(struct sieve_validator *valdtr,
30
         struct sieve_command *tst);
31
static bool
32
tst_mailboxexists_generate(const struct sieve_codegen_env *cgenv,
33
         struct sieve_command *ctx);
34
35
const struct sieve_command_def mailboxexists_test = {
36
  .identifier = "mailboxexists",
37
  .type = SCT_TEST,
38
  .positional_args = 1,
39
  .subtests = 0,
40
  .block_allowed = FALSE,
41
  .block_required = FALSE,
42
  .validate = tst_mailboxexists_validate,
43
  .generate = tst_mailboxexists_generate,
44
};
45
46
/*
47
 * Mailboxexists operation
48
 */
49
50
static bool
51
tst_mailboxexists_operation_dump(const struct sieve_dumptime_env *denv,
52
         sieve_size_t *address);
53
static int
54
tst_mailboxexists_operation_execute(const struct sieve_runtime_env *renv,
55
            sieve_size_t *address);
56
57
const struct sieve_operation_def mailboxexists_operation = {
58
  .mnemonic = "MAILBOXEXISTS",
59
  .ext_def = &mailbox_extension,
60
  .dump = tst_mailboxexists_operation_dump,
61
  .execute = tst_mailboxexists_operation_execute,
62
};
63
64
/*
65
 * Test validation
66
 */
67
68
struct _validate_context {
69
  struct sieve_validator *valdtr;
70
  struct sieve_command *tst;
71
};
72
73
static int
74
tst_mailboxexists_mailbox_validate(void *context,
75
           struct sieve_ast_argument *arg)
76
0
{
77
0
  struct _validate_context *valctx =
78
0
    (struct _validate_context *)context;
79
80
0
  if (sieve_argument_is_string_literal(arg)) {
81
0
    const char *mailbox = sieve_ast_argument_strc(arg), *error;
82
83
0
    if (!sieve_mailbox_check_name(mailbox, &error)) {
84
0
      sieve_argument_validate_warning(
85
0
        valctx->valdtr, arg, "%s test: "
86
0
        "invalid mailbox name '%s' specified: %s",
87
0
        sieve_command_identifier(valctx->tst),
88
0
        str_sanitize(mailbox, 256), error);
89
0
    }
90
0
  }
91
92
0
  return 1;
93
0
}
94
95
static bool
96
tst_mailboxexists_validate(struct sieve_validator *valdtr,
97
         struct sieve_command *tst)
98
0
{
99
0
  struct sieve_ast_argument *arg = tst->first_positional;
100
0
  struct sieve_ast_argument *aarg;
101
0
  struct _validate_context valctx;
102
103
0
  if (!sieve_validate_positional_argument(
104
0
    valdtr, tst, arg, "mailbox-names", 1, SAAT_STRING_LIST))
105
0
    return FALSE;
106
107
0
  if (!sieve_validator_argument_activate(valdtr, tst, arg, FALSE))
108
0
    return FALSE;
109
110
0
  aarg = arg;
111
0
  i_zero(&valctx);
112
0
  valctx.valdtr = valdtr;
113
0
  valctx.tst = tst;
114
115
0
  return (sieve_ast_stringlist_map(
116
0
    &aarg, &valctx,
117
0
    tst_mailboxexists_mailbox_validate) >= 0);
118
0
}
119
120
/*
121
 * Test generation
122
 */
123
124
static bool
125
tst_mailboxexists_generate(const struct sieve_codegen_env *cgenv,
126
         struct sieve_command *tst)
127
0
{
128
0
  sieve_operation_emit(cgenv->sblock, tst->ext, &mailboxexists_operation);
129
130
  /* Generate arguments */
131
0
  return sieve_generate_arguments(cgenv, tst, NULL);
132
0
}
133
134
/*
135
 * Code dump
136
 */
137
138
static bool
139
tst_mailboxexists_operation_dump(const struct sieve_dumptime_env *denv,
140
         sieve_size_t *address)
141
0
{
142
0
  sieve_code_dumpf(denv, "MAILBOXEXISTS");
143
0
  sieve_code_descend(denv);
144
145
0
  return sieve_opr_stringlist_dump(denv, address, "mailbox-names");
146
0
}
147
148
/*
149
 * Code execution
150
 */
151
152
static int
153
tst_mailboxexists_test_mailbox(const struct sieve_runtime_env *renv,
154
             const char *mailbox, bool trace,
155
             bool *all_exist_r)
156
0
{
157
0
  const struct sieve_execute_env *eenv = renv->exec_env;
158
0
  struct mailbox *box;
159
0
  const char *error;
160
161
  /* Check validity of mailbox name */
162
0
  if (!sieve_mailbox_check_name(mailbox, &error)) {
163
0
    sieve_runtime_warning(
164
0
      renv, NULL, "mailboxexists test: "
165
0
      "invalid mailbox name '%s' specified: %s",
166
0
      str_sanitize(mailbox, 256), error);
167
0
    *all_exist_r = FALSE;
168
0
    return SIEVE_EXEC_OK;
169
0
  }
170
171
  /* Open the box */
172
0
  box = mailbox_alloc_for_user(eenv->scriptenv->user,
173
0
             mailbox,
174
0
             MAILBOX_FLAG_POST_SESSION);
175
176
0
  if (mailbox_open(box) < 0) {
177
0
    if (trace) {
178
0
      sieve_runtime_trace(
179
0
        renv, 0,
180
0
        "mailbox '%s' cannot be opened",
181
0
        str_sanitize(mailbox, 80));
182
0
    }
183
0
    mailbox_free(&box);
184
0
    *all_exist_r = FALSE;
185
0
    return SIEVE_EXEC_OK;
186
0
  }
187
188
  /* Also fail when it is readonly */
189
0
  if (mailbox_is_readonly(box)) {
190
0
    if (trace) {
191
0
      sieve_runtime_trace(
192
0
        renv, 0,
193
0
        "mailbox '%s' is read-only",
194
0
        str_sanitize(mailbox, 80));
195
0
    }
196
0
    mailbox_free(&box);
197
0
    *all_exist_r = FALSE;
198
0
    return SIEVE_EXEC_OK;
199
0
  }
200
201
  /* FIXME: check acl for 'p' or 'i' ACL permissions as
202
     required by RFC */
203
204
0
  if (trace) {
205
0
    sieve_runtime_trace(
206
0
      renv, 0, "mailbox '%s' exists",
207
0
      str_sanitize(mailbox, 80));
208
0
  }
209
210
  /* Close mailbox */
211
0
  mailbox_free(&box);
212
0
  return SIEVE_EXEC_OK;
213
0
}
214
215
static int
216
tst_mailboxexists_operation_execute(const struct sieve_runtime_env *renv,
217
            sieve_size_t *address)
218
0
{
219
0
  const struct sieve_execute_env *eenv = renv->exec_env;
220
0
  struct sieve_stringlist *mailbox_names;
221
0
  string_t *mailbox_item;
222
0
  bool trace = FALSE;
223
0
  bool all_exist = TRUE;
224
0
  int ret;
225
226
  /*
227
   * Read operands
228
   */
229
230
  /* Read notify uris */
231
0
  ret = sieve_opr_stringlist_read(renv, address, "mailbox-names",
232
0
          &mailbox_names);
233
0
  if (ret <= 0)
234
0
    return ret;
235
236
  /*
237
   * Perform operation
238
   */
239
240
0
  if (sieve_runtime_trace_active(renv, SIEVE_TRLVL_TESTS)) {
241
0
    sieve_runtime_trace(renv, 0, "mailboxexists test");
242
0
    sieve_runtime_trace_descend(renv);
243
244
0
    trace = sieve_runtime_trace_active(renv, SIEVE_TRLVL_MATCHING);
245
0
  }
246
247
0
  if (eenv->scriptenv->user == NULL) {
248
0
    sieve_runtime_trace(renv, 0, "no mail user; yield true");
249
0
    sieve_interpreter_set_test_result(renv->interp, TRUE);
250
0
    return SIEVE_EXEC_OK;
251
0
  }
252
253
0
  mailbox_item = NULL;
254
0
  while (all_exist &&
255
0
         (ret = sieve_stringlist_next_item(mailbox_names,
256
0
             &mailbox_item)) > 0) {
257
0
    const char *mailbox = str_c(mailbox_item);
258
259
0
    ret = tst_mailboxexists_test_mailbox(renv, mailbox,
260
0
                 trace, &all_exist);
261
0
    if (ret <= 0)
262
0
      return ret;
263
0
  }
264
265
0
  if (ret < 0) {
266
0
    sieve_runtime_trace_error(
267
0
      renv, "invalid mailbox name item");
268
0
    return SIEVE_EXEC_BIN_CORRUPT;
269
0
  }
270
271
0
  if (trace) {
272
0
    if (all_exist) {
273
0
      sieve_runtime_trace(renv, 0,
274
0
              "all mailboxes are available");
275
0
    } else {
276
0
      sieve_runtime_trace(renv, 0,
277
0
              "some mailboxes are unavailable");
278
0
    }
279
0
  }
280
281
0
  sieve_interpreter_set_test_result(renv->interp, all_exist);
282
0
  return SIEVE_EXEC_OK;
283
0
}