Coverage Report

Created: 2026-07-30 06:43

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/pigeonhole/src/testsuite/testsuite-mailstore.c
Line
Count
Source
1
/* Copyright (c) Pigeonhole authors, see top-level COPYING file */
2
3
#include "lib.h"
4
#include "mempool.h"
5
#include "imem.h"
6
#include "array.h"
7
#include "strfuncs.h"
8
#include "str-sanitize.h"
9
#include "path-util.h"
10
#include "unlink-directory.h"
11
#include "env-util.h"
12
#include "mail-storage-service.h"
13
#include "mail-namespace.h"
14
#include "mail-storage.h"
15
#include "imap-metadata.h"
16
17
#include "sieve-common.h"
18
#include "sieve-error.h"
19
#include "sieve-actions.h"
20
#include "sieve-interpreter.h"
21
22
#include "testsuite-message.h"
23
#include "testsuite-common.h"
24
#include "testsuite-smtp.h"
25
26
#include "testsuite-mailstore.h"
27
28
#include <sys/stat.h>
29
#include <sys/types.h>
30
31
struct testsuite_mailstore_mail {
32
  struct testsuite_mailstore_mail *next;
33
34
  char *folder;
35
  struct mailbox *box;
36
  struct mailbox_transaction_context *trans;
37
  struct mail *mail;
38
};
39
40
/*
41
 * Forward declarations
42
 */
43
44
static void testsuite_mailstore_free(bool all);
45
46
/*
47
 * State
48
 */
49
50
static struct mail_user *testsuite_mailstore_user = NULL;
51
52
static struct testsuite_mailstore_mail *testsuite_mailstore_mail = NULL;
53
54
static char *testsuite_mailstore_location = NULL;
55
static char *testsuite_mailstore_attrs = NULL;
56
57
/*
58
 * Initialization
59
 */
60
61
void testsuite_mailstore_init(void)
62
0
{
63
0
  struct mail_user *mail_user_dovecot, *mail_user;
64
0
  struct mail_namespace *ns;
65
0
  struct mail_namespace_settings *ns_set;
66
0
  struct mail_storage *storage;
67
0
  const char *tmpdir, *error, *cwd;
68
69
0
  tmpdir = testsuite_tmp_dir_get();
70
0
  testsuite_mailstore_location =
71
0
    i_strconcat(tmpdir, "/mailstore", NULL);
72
0
  testsuite_mailstore_attrs =
73
0
    i_strconcat(tmpdir, "/mail-attrs.dict", NULL);
74
75
0
  if (mkdir(testsuite_mailstore_location, 0700) < 0) {
76
0
    i_fatal("failed to create temporary directory '%s': %m.",
77
0
      testsuite_mailstore_location);
78
0
  }
79
80
0
  mail_user_dovecot = sieve_tool_get_mail_user(sieve_tool);
81
82
0
  if (t_get_working_dir(&cwd, &error) < 0)
83
0
    i_fatal("Failed to get working directory: %s", error);
84
0
  const char *const code_override_fields[] = {
85
0
    t_strconcat("mail_home=", cwd, NULL),
86
0
    "mail_driver=maildir",
87
0
    t_strconcat("mail_path=", testsuite_mailstore_location, NULL),
88
#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
89
    "mail_attribute/dict=file",
90
    "mail_attribute/dict/file/driver=file",
91
    t_strconcat("mail_attribute/dict/file/path=", testsuite_mailstore_attrs, NULL),
92
#endif
93
0
    NULL,
94
0
  };
95
0
  struct settings_instance *set_instance =
96
0
    mail_storage_service_user_get_settings_instance(mail_user_dovecot->service_user);
97
0
  struct mail_storage_service_input input = {
98
0
    .username = "testsuite-mail-user@example.org",
99
0
    .set_instance = set_instance,
100
0
    .no_userdb_lookup = TRUE,
101
0
    .code_override_fields = code_override_fields,
102
0
  };
103
0
  if (mail_storage_service_lookup_next(
104
0
      sieve_tool_get_mail_storage_service(sieve_tool),
105
0
      &input, &mail_user, &error) < 0)
106
0
    i_fatal("Test user initialization failed: %s", error);
107
0
  mail_user->autocreated = TRUE;
108
109
0
  ns_set = p_new(mail_user->pool, struct mail_namespace_settings, 1);
110
0
  ns_set->name = "";
111
0
  ns_set->separator = ".";
112
113
0
  ns = mail_namespaces_init_empty(mail_user);
114
0
  ns->flags |= NAMESPACE_FLAG_INBOX_USER;
115
0
  ns->set = ns_set;
116
117
0
  if (mail_storage_create(ns, mail_user->event, 0, &storage, &error) < 0)
118
0
    i_fatal("Couldn't create testsuite storage: %s", error);
119
0
  if (mail_namespaces_init_finish(ns, &error) < 0)
120
0
    i_fatal("Couldn't create testsuite namespace: %s", error);
121
122
0
  testsuite_mailstore_user = mail_user;
123
0
}
124
125
void testsuite_mailstore_deinit(void)
126
0
{
127
0
  const char *error;
128
129
0
  testsuite_mailstore_free(TRUE);
130
131
0
  if (unlink_directory(testsuite_mailstore_location,
132
0
           UNLINK_DIRECTORY_FLAG_RMDIR, &error) < 0) {
133
0
    i_warning("failed to remove temporary directory '%s': %s.",
134
0
        testsuite_mailstore_location, error);
135
0
  }
136
137
0
  i_free(testsuite_mailstore_location);
138
0
  i_free(testsuite_mailstore_attrs);
139
0
  mail_user_unref(&testsuite_mailstore_user);
140
0
}
141
142
/*
143
 * Mail user
144
 */
145
146
struct mail_user *testsuite_mailstore_get_user(void)
147
0
{
148
0
  if (testsuite_mailstore_user == NULL)
149
0
    return sieve_tool_get_mail_user(sieve_tool);
150
0
  return testsuite_mailstore_user;
151
0
}
152
153
/*
154
 * Mailbox Access
155
 */
156
157
bool testsuite_mailstore_mailbox_create(
158
  const struct sieve_runtime_env *renv ATTR_UNUSED, const char *folder)
159
0
{
160
0
  struct mail_user *mail_user = testsuite_mailstore_user;
161
0
  struct mail_namespace *ns = mail_user->namespaces;
162
0
  struct mailbox *box;
163
164
0
  box = mailbox_alloc(ns->list, folder, 0);
165
166
0
  if (mailbox_create(box, NULL, FALSE) < 0) {
167
0
    mailbox_free(&box);
168
0
    return FALSE;
169
0
  }
170
171
0
  mailbox_free(&box);
172
173
0
  return TRUE;
174
0
}
175
176
static struct testsuite_mailstore_mail *
177
testsuite_mailstore_open(const char *folder)
178
0
{
179
0
  enum mailbox_flags flags =
180
0
    MAILBOX_FLAG_SAVEONLY | MAILBOX_FLAG_POST_SESSION;
181
0
  struct mail_user *mail_user = testsuite_mailstore_user;
182
0
  struct mail_namespace *ns = mail_user->namespaces;
183
0
  struct mailbox *box;
184
0
  struct mailbox_transaction_context *t;
185
0
  struct testsuite_mailstore_mail *tmail, *tmail_prev;
186
0
  const char *error;
187
188
0
  if (!sieve_mailbox_check_name(folder, &error)) {
189
0
    e_error(testsuite_sieve_instance->event,
190
0
      "testsuite: invalid mailbox name '%s' specified: %s",
191
0
      folder, error);
192
0
    return NULL;
193
0
  }
194
195
0
  tmail = testsuite_mailstore_mail;
196
0
  tmail_prev = NULL;
197
0
  while (tmail != NULL) {
198
0
    if (strcmp(tmail->folder, folder) == 0) {
199
0
      if (tmail_prev != NULL) {
200
        /* Remove it from list if it is not first. */
201
0
        tmail_prev->next = tmail->next;
202
0
      }
203
0
      break;
204
0
    }
205
0
    tmail_prev = tmail;
206
0
    tmail = tmail->next;
207
0
  }
208
0
  if (tmail != NULL) {
209
0
    if (tmail != testsuite_mailstore_mail) {
210
      /* Bring it to front */
211
0
      tmail->next = testsuite_mailstore_mail;
212
0
      testsuite_mailstore_mail = tmail;
213
0
    }
214
0
    return tmail;
215
0
  }
216
217
0
  box = mailbox_alloc(ns->list, folder, flags);
218
0
  if (mailbox_open(box) < 0) {
219
0
    e_error(testsuite_sieve_instance->event,
220
0
      "testsuite: failed to open mailbox '%s'", folder);
221
0
    mailbox_free(&box);
222
0
    return NULL;
223
0
  }
224
225
  /* Sync mailbox */
226
227
0
  if (mailbox_sync(box, MAILBOX_SYNC_FLAG_FULL_READ) < 0) {
228
0
    e_error(testsuite_sieve_instance->event,
229
0
      "testsuite: failed to sync mailbox '%s'", folder);
230
0
    mailbox_free(&box);
231
0
    return NULL;
232
0
  }
233
234
  /* Start transaction */
235
236
0
  t = mailbox_transaction_begin(box, 0, __func__);
237
238
0
  tmail = i_new(struct testsuite_mailstore_mail, 1);
239
0
  tmail->next = testsuite_mailstore_mail;
240
0
  testsuite_mailstore_mail = tmail;
241
242
0
  tmail->folder = i_strdup(folder);
243
0
  tmail->box = box;
244
0
  tmail->trans = t;
245
0
  tmail->mail = mail_alloc(t, 0, NULL);
246
247
0
  return tmail;
248
0
}
249
250
static void testsuite_mailstore_free(bool all)
251
0
{
252
0
  struct testsuite_mailstore_mail *tmail;
253
254
0
  if (testsuite_mailstore_mail == NULL)
255
0
    return;
256
257
0
  tmail = (all ?
258
0
     testsuite_mailstore_mail : testsuite_mailstore_mail->next);
259
0
  while (tmail != NULL) {
260
0
    struct testsuite_mailstore_mail *tmail_next = tmail->next;
261
262
0
    mail_free(&tmail->mail);
263
0
    mailbox_transaction_rollback(&tmail->trans);
264
0
    mailbox_free(&tmail->box);
265
0
    i_free(tmail->folder);
266
0
    i_free(tmail);
267
268
0
    tmail = tmail_next;
269
0
  }
270
0
  if (all)
271
0
    testsuite_mailstore_mail = NULL;
272
0
  else
273
0
    testsuite_mailstore_mail->next = NULL;
274
0
}
275
276
void testsuite_mailstore_flush(void)
277
0
{
278
0
  testsuite_mailstore_free(FALSE);
279
0
}
280
281
bool testsuite_mailstore_mail_index(const struct sieve_runtime_env *renv,
282
            const char *folder, unsigned int index)
283
0
{
284
0
  struct testsuite_mailstore_mail *tmail;
285
0
  struct mailbox_status status;
286
287
0
  tmail = testsuite_mailstore_open(folder);
288
0
  if (tmail == NULL) {
289
0
    testsuite_message_set_default(renv);
290
0
    return FALSE;
291
0
  }
292
293
0
  mailbox_get_open_status(tmail->box, STATUS_MESSAGES, &status);
294
0
  if (index >= status.messages)
295
0
    return FALSE;
296
297
0
  mail_set_seq(tmail->mail, index+1);
298
0
  testsuite_message_set_mail(renv, tmail->mail);
299
300
0
  return TRUE;
301
0
}
302
303
/*
304
 * IMAP metadata
305
 */
306
307
int testsuite_mailstore_set_imap_metadata(const char *mailbox,
308
            const char *annotation,
309
            const char *value)
310
0
{
311
0
  struct imap_metadata_transaction *imtrans;
312
0
  struct mail_attribute_value avalue;
313
0
  struct mailbox *box;
314
0
  enum mail_error error_code;
315
0
  const char *error;
316
0
  int ret;
317
318
0
  if (!imap_metadata_verify_entry_name(annotation, &error)) {
319
0
    e_error(testsuite_sieve_instance->event,
320
0
      "testsuite: imap metadata: "
321
0
      "specified annotation name '%s' is invalid: %s",
322
0
      str_sanitize(annotation, 256), error);
323
0
    return -1;
324
0
  }
325
326
0
  if (mailbox != NULL) {
327
0
    struct mail_namespace *ns;
328
0
    ns = mail_namespace_find(testsuite_mailstore_user->namespaces,
329
0
           mailbox);
330
0
    box = mailbox_alloc(ns->list, mailbox, 0);
331
0
    imtrans = imap_metadata_transaction_begin(box);
332
0
  } else {
333
0
    box = NULL;
334
0
    imtrans = imap_metadata_transaction_begin_server(
335
0
      testsuite_mailstore_user);
336
0
  }
337
338
0
  i_zero(&avalue);
339
0
  avalue.value = value;
340
0
  if ((ret = imap_metadata_set(imtrans, annotation, &avalue)) < 0) {
341
0
    error = imap_metadata_transaction_get_last_error(
342
0
      imtrans, &error_code);
343
0
    imap_metadata_transaction_rollback(&imtrans);
344
0
  } else {
345
0
    ret = imap_metadata_transaction_commit(&imtrans,
346
0
                   &error_code, &error);
347
0
  }
348
0
  if (box != NULL)
349
0
    mailbox_free(&box);
350
351
0
  if (ret < 0) {
352
0
    e_error(testsuite_sieve_instance->event,
353
0
      "testsuite: imap metadata: "
354
0
      "failed to assign annotation '%s': %s",
355
0
      str_sanitize(annotation, 256), error);
356
0
    return -1;
357
0
  }
358
0
  return 0;
359
0
}