Coverage Report

Created: 2026-08-08 06:43

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/pigeonhole/src/lib-sieve/storage/file/sieve-file-script.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 "str.h"
6
#include "path-util.h"
7
#include "istream.h"
8
#include "time-util.h"
9
#include "eacces-error.h"
10
11
#include "sieve-dump.h"
12
#include "sieve-binary.h"
13
#include "sieve-script-private.h"
14
15
#include "sieve-file-storage.h"
16
17
#include <stdio.h>
18
#include <unistd.h>
19
#include <ctype.h>
20
#include <time.h>
21
#include <fcntl.h>
22
23
/*
24
 * Filename to name/name to filename
25
 */
26
27
const char *sieve_script_file_get_scriptname(const char *filename)
28
0
{
29
0
  const char *ext;
30
31
  /* Extract the script name */
32
0
  ext = strrchr(filename, '.');
33
0
  if (ext == NULL || ext == filename ||
34
0
      strcmp(ext, "."SIEVE_SCRIPT_FILEEXT) != 0)
35
0
    return NULL;
36
37
0
  return t_strdup_until(filename, ext);
38
0
}
39
40
bool sieve_script_file_has_extension(const char *filename)
41
0
{
42
0
  return (sieve_script_file_get_scriptname(filename) != NULL);
43
0
}
44
45
const char *sieve_script_file_from_name(const char *name)
46
0
{
47
0
  return t_strconcat(name, "."SIEVE_SCRIPT_FILEEXT, NULL);
48
0
}
49
50
/*
51
 * Common error handling
52
 */
53
54
static void
55
sieve_file_script_handle_error(struct sieve_file_script *fscript,
56
             const char *op, const char *path,
57
             const char *name)
58
0
{
59
0
  struct sieve_script *script = &fscript->script;
60
0
  const char *abspath, *error;
61
62
0
  switch (errno) {
63
0
  case ENOENT:
64
0
    if (t_abspath(path, &abspath, &error) < 0) {
65
0
      sieve_script_set_critical(script,
66
0
              "t_abspath(%s) failed: %s",
67
0
              path, error);
68
0
      break;
69
0
    }
70
0
    e_debug(script->event, "File '%s' not found", abspath);
71
0
    sieve_script_set_not_found_error(script, name);
72
0
    break;
73
0
  case EACCES:
74
0
    sieve_script_set_critical(script,
75
0
            "Failed to %s sieve script: %s",
76
0
            op, eacces_error_get(op, path));
77
0
    script->storage->error_code = SIEVE_ERROR_NO_PERMISSION;
78
0
    break;
79
0
  default:
80
0
    sieve_script_set_critical(
81
0
      script, "Failed to %s sieve script: %s(%s) failed: %m",
82
0
      op, op, path);
83
0
    break;
84
0
  }
85
0
}
86
87
/*
88
 *
89
 */
90
91
static struct sieve_file_script *sieve_file_script_alloc(void)
92
0
{
93
0
  struct sieve_file_script *fscript;
94
0
  pool_t pool;
95
96
0
  pool = pool_alloconly_create("sieve_file_script", 2048);
97
0
  fscript = p_new(pool, struct sieve_file_script, 1);
98
0
  fscript->script = sieve_file_script;
99
0
  fscript->script.pool = pool;
100
101
0
  return fscript;
102
0
}
103
104
int sieve_file_script_init_from_filename(struct sieve_file_storage *fstorage,
105
           const char *filename,
106
           const char *scriptname,
107
           struct sieve_file_script **fscript_r)
108
0
{
109
0
  struct sieve_storage *storage = &fstorage->storage;
110
0
  struct sieve_file_script *fscript = NULL;
111
112
0
  *fscript_r = NULL;
113
114
  /* Prevent initializing the active script link as a script when it
115
     resides in the sieve storage directory.
116
   */
117
0
  if (scriptname != NULL && fstorage->link_path != NULL &&
118
0
      *(fstorage->link_path) == '\0') {
119
0
    if (strcmp(filename, fstorage->active_fname) == 0) {
120
0
      sieve_storage_set_error(
121
0
        storage, SIEVE_ERROR_NOT_FOUND,
122
0
        "Script '%s' does not exist.", scriptname);
123
0
      return -1;
124
0
    }
125
0
  }
126
127
0
  fscript = sieve_file_script_alloc();
128
0
  sieve_script_init(&fscript->script, storage, &sieve_file_script,
129
0
        scriptname);
130
0
  fscript->filename = p_strdup(fscript->script.pool, filename);
131
132
0
  event_add_str(fscript->script.event, "sieve_script_file_path",
133
0
          sieve_file_storage_path_extend(fstorage, filename));
134
135
0
  *fscript_r = fscript;
136
0
  return 0;
137
0
}
138
139
int sieve_file_script_open_from_filename(struct sieve_file_storage *fstorage,
140
           const char *filename,
141
           const char *scriptname,
142
           struct sieve_file_script **fscript_r)
143
0
{
144
0
  struct sieve_file_script *fscript;
145
146
0
  *fscript_r = NULL;
147
148
0
  if (sieve_file_script_init_from_filename(fstorage, filename, scriptname,
149
0
             &fscript) < 0)
150
0
    return -1;
151
152
0
  if (sieve_script_open(&fscript->script, NULL) < 0) {
153
0
    struct sieve_script *script = &fscript->script;
154
155
0
    sieve_script_unref(&script);
156
0
    return -1;
157
0
  }
158
159
0
  *fscript_r = fscript;
160
0
  return 0;
161
0
}
162
163
int sieve_file_script_init_from_name(struct sieve_file_storage *fstorage,
164
             const char *name,
165
             struct sieve_file_script **fscript_r)
166
0
{
167
0
  struct sieve_storage *storage = &fstorage->storage;
168
0
  struct sieve_file_script *fscript;
169
170
0
  *fscript_r = NULL;
171
172
0
  if (name != NULL && S_ISDIR(fstorage->st.st_mode)) {
173
0
    return sieve_file_script_init_from_filename(
174
0
      fstorage, sieve_script_file_from_name(name), name,
175
0
      fscript_r);
176
0
  }
177
178
0
  fscript = sieve_file_script_alloc();
179
0
  sieve_script_init(&fscript->script, storage, &sieve_file_script, name);
180
181
0
  event_add_str(fscript->script.event, "sieve_script_file_path",
182
0
          fstorage->active_path);
183
184
0
  *fscript_r = fscript;
185
0
  return 0;
186
0
}
187
188
int sieve_file_script_open_from_name(struct sieve_file_storage *fstorage,
189
             const char *name,
190
             struct sieve_file_script **fscript_r)
191
0
{
192
0
  struct sieve_file_script *fscript;
193
194
0
  *fscript_r = NULL;
195
196
0
  if (sieve_file_script_init_from_name(fstorage, name, &fscript) < 0)
197
0
    return -1;
198
199
0
  if (sieve_script_open(&fscript->script, NULL) < 0) {
200
0
    struct sieve_script *script = &fscript->script;
201
202
0
    sieve_script_unref(&script);
203
0
    return -1;
204
0
  }
205
206
0
  *fscript_r = fscript;
207
0
  return 0;
208
0
}
209
210
int sieve_file_script_init_from_path(struct sieve_file_storage *fstorage,
211
             const char *path, const char *scriptname,
212
             struct sieve_file_script **fscript_r)
213
0
{
214
0
  struct sieve_storage *storage = &fstorage->storage;
215
0
  struct sieve_instance *svinst = storage->svinst;
216
0
  struct sieve_file_storage *fsubstorage;
217
0
  struct sieve_file_script *fscript;
218
0
  struct sieve_storage *substorage;
219
0
  enum sieve_error error_code;
220
0
  const char *error;
221
222
0
  *fscript_r = NULL;
223
224
0
  if (sieve_file_storage_init_from_path(svinst, storage->cause,
225
0
                storage->type, storage->name,
226
0
                path, 0, &fsubstorage,
227
0
                &error_code, &error) < 0) {
228
0
    sieve_storage_set_error(storage, error_code, "%s", error);
229
0
    return -1;
230
0
  }
231
0
  substorage = &fsubstorage->storage;
232
233
0
  fscript = sieve_file_script_alloc();
234
0
  sieve_script_init(&fscript->script, substorage, &sieve_file_script,
235
0
        scriptname);
236
0
  sieve_storage_unref(&substorage);
237
238
0
  event_add_str(fscript->script.event, "sieve_script_file_path",
239
0
          fstorage->active_path);
240
241
0
  *fscript_r = fscript;
242
0
  return 0;
243
0
}
244
245
int sieve_file_script_open_from_path(struct sieve_file_storage *fstorage,
246
             const char *path, const char *scriptname,
247
             struct sieve_file_script **fscript_r)
248
0
{
249
0
  struct sieve_storage *storage = &fstorage->storage;
250
0
  struct sieve_file_script *fscript;
251
252
0
  *fscript_r = NULL;
253
254
0
  if (sieve_file_script_init_from_path(fstorage, path, scriptname,
255
0
               &fscript) < 0)
256
0
    return -1;
257
258
0
  if (sieve_script_open(&fscript->script, NULL) < 0) {
259
0
    struct sieve_script *script = &fscript->script;
260
261
0
    sieve_storage_copy_error(storage, script->storage);
262
0
    sieve_script_unref(&script);
263
0
    return -1;
264
0
  }
265
266
0
  *fscript_r = fscript;
267
0
  return 0;
268
0
}
269
270
/*
271
 * Open
272
 */
273
274
static int
275
sieve_file_script_stat(const char *path, struct stat *st, struct stat *lnk_st)
276
0
{
277
0
  if (lstat(path, st) < 0)
278
0
    return -1;
279
280
0
  *lnk_st = *st;
281
282
0
  if (S_ISLNK(st->st_mode) && stat(path, st) < 0)
283
0
    return -1;
284
0
  return 0;
285
0
}
286
287
static const char *
288
path_split_filename(const char *path, const char **dir_path_r)
289
0
{
290
0
  const char *filename;
291
292
0
  filename = strrchr(path, '/');
293
0
  if (filename == NULL) {
294
0
    *dir_path_r = "";
295
0
    filename = path;
296
0
  } else {
297
0
    *dir_path_r = t_strdup_until(path, filename);
298
0
    filename++;
299
0
  }
300
0
  return filename;
301
0
}
302
303
static int sieve_file_script_open(struct sieve_script *script)
304
0
{
305
0
  struct sieve_file_script *fscript =
306
0
    container_of(script, struct sieve_file_script, script);
307
0
  struct sieve_storage *storage = script->storage;
308
0
  struct sieve_file_storage *fstorage =
309
0
    container_of(storage, struct sieve_file_storage, storage);
310
0
  pool_t pool = script->pool;
311
0
  const char *filename, *name, *path;
312
0
  const char *dir_path, *basename, *bin_path, *bin_prefix;
313
0
  struct stat st, lnk_st;
314
0
  bool success = TRUE;
315
0
  int ret = 0;
316
317
0
  filename = fscript->filename;
318
0
  basename = NULL;
319
0
  name = script->name;
320
0
  st = fstorage->st;
321
0
  lnk_st = fstorage->lnk_st;
322
323
0
  if (name == NULL && storage->script_name != NULL &&
324
0
      *storage->script_name != '\0')
325
0
    name = storage->script_name;
326
327
0
  T_BEGIN {
328
0
    if (S_ISDIR(st.st_mode)) {
329
      /* Storage is a directory */
330
0
      path = fstorage->path;
331
332
0
      if ((filename == NULL || *filename == '\0') &&
333
0
          name != NULL && *name != '\0') {
334
        /* Name is used to find actual filename */
335
0
        filename = sieve_script_file_from_name(name);
336
0
        basename = name;
337
0
      }
338
0
      if (filename == NULL || *filename == '\0') {
339
0
        sieve_script_set_critical(
340
0
          script, "Sieve script file path '%s' is a directory.", path);
341
0
        success = FALSE;
342
0
      } else {
343
        /* Extend storage path with filename */
344
0
        if (name == NULL) {
345
0
          if (basename == NULL &&
346
0
              (basename = sieve_script_file_get_scriptname(filename)) == NULL)
347
0
            basename = filename;
348
0
          name = basename;
349
0
        } else if (basename == NULL) {
350
0
          basename = name;
351
0
        }
352
0
        dir_path = path;
353
354
0
        path = sieve_file_storage_path_extend(fstorage, filename);
355
0
        ret = sieve_file_script_stat(path, &st, &lnk_st);
356
0
      }
357
358
0
    } else {
359
      /* Storage is a single file */
360
0
      path = fstorage->active_path;
361
362
      /* Extract filename from path */
363
0
      filename = path_split_filename(path, &dir_path);
364
365
0
      basename = sieve_script_file_get_scriptname(filename);
366
0
      if (basename == NULL)
367
0
        basename = filename;
368
369
0
      if (name == NULL)
370
0
        name = basename;
371
0
    }
372
373
0
    if (success) {
374
0
      if (ret < 0) {
375
        /* Make sure we have a script name for the error
376
         */
377
0
        if (name == NULL) {
378
0
          i_assert(basename != NULL);
379
0
          name = basename;
380
0
        }
381
0
        sieve_file_script_handle_error(fscript, "stat",
382
0
                     path, name);
383
0
        success = FALSE;
384
385
0
      } else if (!S_ISREG(st.st_mode)) {
386
0
        sieve_script_set_critical(
387
0
          script, "Sieve script file '%s' is not a regular file.",
388
0
          path);
389
0
        success = FALSE;
390
0
      }
391
0
    }
392
393
0
    if (success) {
394
0
      const char *bpath, *bfile, *bprefix;
395
396
0
      if (storage->bin_path != NULL) {
397
0
        bpath = storage->bin_path;
398
0
        bfile = sieve_binfile_from_name(name);
399
0
        bprefix = name;
400
401
0
      } else {
402
0
        bpath = dir_path;
403
0
        bfile = sieve_binfile_from_name(basename);
404
0
        bprefix = basename;
405
0
      }
406
407
0
      if (*bpath == '\0') {
408
0
        bin_path = bfile;
409
0
        bin_prefix = bprefix;
410
0
      } else if (bpath[strlen(bpath)-1] == '/') {
411
0
        bin_path = t_strconcat(bpath, bfile, NULL);
412
0
        bin_prefix = t_strconcat(bpath, bprefix, NULL);
413
0
      } else {
414
0
        bin_path = t_strconcat(bpath, "/", bfile, NULL);
415
0
        bin_prefix = t_strconcat(bpath, "/", bprefix, NULL);
416
0
      }
417
418
0
      fscript->st = st;
419
0
      fscript->lnk_st = lnk_st;
420
0
      fscript->path = p_strdup(pool, path);
421
0
      fscript->filename = p_strdup(pool, filename);
422
0
      fscript->dir_path = p_strdup(pool, dir_path);
423
0
      fscript->bin_path = p_strdup(pool, bin_path);
424
0
      fscript->bin_prefix = p_strdup(pool, bin_prefix);
425
426
0
      if (fscript->script.name == NULL)
427
0
        fscript->script.name = p_strdup(pool, basename);
428
429
0
      event_add_str(script->event, "sieve_script_file_path",
430
0
              fscript->path);
431
0
    }
432
0
  } T_END;
433
434
0
  return (success ? 0 : -1);
435
0
}
436
437
static int
438
sieve_file_script_get_stream(struct sieve_script *script,
439
           struct istream **stream_r)
440
0
{
441
0
  struct sieve_file_script *fscript =
442
0
    container_of(script, struct sieve_file_script, script);
443
0
  struct stat st;
444
0
  struct istream *result;
445
0
  int fd;
446
447
0
  fd = open(fscript->path, O_RDONLY);
448
0
  if (fd < 0) {
449
0
    sieve_file_script_handle_error(fscript, "open", fscript->path,
450
0
                 fscript->script.name);
451
0
    return -1;
452
0
  }
453
454
0
  if (fstat(fd, &st) != 0) {
455
0
    sieve_script_set_critical(
456
0
      script,
457
0
      "Failed to open sieve script: fstat(fd=%s) failed: %m",
458
0
      fscript->path);
459
0
    result = NULL;
460
  /* Re-check the file type just to be sure */
461
0
  } else if (!S_ISREG(st.st_mode)) {
462
0
    sieve_script_set_critical(
463
0
      script, "Sieve script file '%s' is not a regular file",
464
0
      fscript->path);
465
0
    result = NULL;
466
0
  } else {
467
0
    result = i_stream_create_fd_autoclose(
468
0
      &fd, SIEVE_FILE_READ_BLOCK_SIZE);
469
0
    fscript->st = fscript->lnk_st = st;
470
0
  }
471
472
0
  if (result == NULL) {
473
    /* Something went wrong, close the fd */
474
0
    if (fd >= 0 && close(fd) != 0) {
475
0
      e_error(script->event,
476
0
        "Failed to close sieve script: "
477
0
        "close(fd=%s) failed: %m", fscript->path);
478
0
    }
479
0
    return -1;
480
0
  }
481
482
0
  *stream_r = result;
483
0
  return 0;
484
0
}
485
486
/*
487
 * Binary
488
 */
489
490
static int
491
sieve_file_script_binary_read_metadata(struct sieve_script *script,
492
               struct sieve_binary_block *sblock,
493
               sieve_size_t *offset)
494
0
{
495
0
  struct sieve_instance *svinst = script->storage->svinst;
496
0
  struct sieve_file_script *fscript =
497
0
    container_of(script, struct sieve_file_script, script);
498
0
  struct sieve_binary *sbin = sieve_binary_block_get_binary(sblock);
499
0
  string_t *path;
500
501
  /* Open if not open already */
502
0
  if (sieve_script_open(script, NULL) < 0)
503
0
    return 0;
504
505
  /* Metadata: path */
506
0
  if (!sieve_binary_read_string(sblock, offset, &path)) {
507
0
    e_error(script->event,
508
0
      "Binary '%s' has invalid metadata for script '%s': "
509
0
      "Invalid file path",
510
0
      sieve_binary_path(sbin), sieve_script_label(script));
511
0
    return -1;
512
0
  }
513
0
  i_assert(fscript->path != NULL);
514
0
  if (strcmp(str_c(path), fscript->path) != 0) {
515
0
    e_debug(script->event,
516
0
      "Binary '%s' reports different file path for script '%s' "
517
0
      "('%s' rather than '%s')",
518
0
      sieve_binary_path(sbin), sieve_script_label(script),
519
0
      str_c(path), fscript->path);
520
0
    return 0;
521
0
  }
522
523
0
  const struct stat *sstat, *bstat;
524
525
0
  bstat = sieve_binary_stat(sbin);
526
0
  if (fscript->st.st_mtime > fscript->lnk_st.st_mtime ||
527
0
      (fscript->st.st_mtime == fscript->lnk_st.st_mtime &&
528
0
       ST_MTIME_NSEC(fscript->st) >= ST_MTIME_NSEC(fscript->lnk_st))) {
529
0
    sstat = &fscript->st;
530
0
  } else {
531
0
    sstat = &fscript->lnk_st;
532
0
  }
533
534
0
  if (bstat->st_mtime < sstat->st_mtime ||
535
0
      (bstat->st_mtime == sstat->st_mtime &&
536
0
       ST_MTIME_NSEC(*bstat) <= ST_MTIME_NSEC(*sstat))) {
537
0
    if (svinst->debug) {
538
0
      e_debug(script->event,
539
0
        "Sieve binary '%s' is not newer "
540
0
        "than the Sieve script '%s' (path=%s, %s.%lu <= %s.%lu)",
541
0
        sieve_binary_path(sbin), sieve_script_label(script),
542
0
        fscript->path,
543
0
        t_strflocaltime("%Y-%m-%d %H:%M:%S",
544
0
            bstat->st_mtime),
545
0
        ST_MTIME_NSEC(*bstat),
546
0
        t_strflocaltime("%Y-%m-%d %H:%M:%S",
547
0
            sstat->st_mtime),
548
0
        ST_MTIME_NSEC(*sstat));
549
0
    }
550
0
    return 0;
551
0
  }
552
553
0
  return 1;
554
0
}
555
556
static void
557
sieve_file_script_binary_write_metadata(struct sieve_script *script,
558
          struct sieve_binary_block *sblock)
559
0
{
560
0
  struct sieve_file_script *fscript =
561
0
    container_of(script, struct sieve_file_script, script);
562
563
0
  sieve_binary_emit_cstring(sblock, fscript->path);
564
0
}
565
566
static bool
567
sieve_file_script_binary_dump_metadata(struct sieve_script *script ATTR_UNUSED,
568
               struct sieve_dumptime_env *denv,
569
               struct sieve_binary_block *sblock,
570
               sieve_size_t *offset)
571
0
{
572
0
  string_t *path;
573
574
0
  if (!sieve_binary_read_string(sblock, offset, &path))
575
0
    return FALSE;
576
0
  sieve_binary_dumpf(denv, "file.path = %s\n", str_c(path));
577
578
0
  return TRUE;
579
0
}
580
581
static int
582
sieve_file_script_binary_load(struct sieve_script *script,
583
            struct sieve_binary **sbin_r)
584
0
{
585
0
  struct sieve_file_script *fscript =
586
0
    container_of(script, struct sieve_file_script, script);
587
588
0
  return sieve_script_binary_load_default(script, fscript->bin_path,
589
0
            sbin_r);
590
0
}
591
592
static int
593
sieve_file_script_binary_save(struct sieve_script *script,
594
            struct sieve_binary *sbin, bool update)
595
0
{
596
0
  struct sieve_file_script *fscript =
597
0
    container_of(script, struct sieve_file_script, script);
598
599
0
  return sieve_script_binary_save_default(
600
0
    script, sbin, fscript->bin_path, update,
601
0
    (fscript->st.st_mode & 0777));
602
0
}
603
604
static const char *
605
sieve_file_script_binary_get_prefix(struct sieve_script *script)
606
0
{
607
0
  struct sieve_file_script *fscript =
608
0
    container_of(script, struct sieve_file_script, script);
609
610
0
  return fscript->bin_prefix;
611
0
}
612
613
/*
614
 * Management
615
 */
616
617
static int sieve_file_storage_script_is_active(struct sieve_script *script)
618
0
{
619
0
  struct sieve_file_script *fscript =
620
0
    container_of(script, struct sieve_file_script, script);
621
0
  struct sieve_file_storage *fstorage =
622
0
    container_of(script->storage, struct sieve_file_storage,
623
0
           storage);
624
0
  const char *afile;
625
0
  int ret = 0;
626
627
0
  T_BEGIN {
628
0
    ret = sieve_file_storage_active_script_get_file(
629
0
      fstorage, &afile);
630
631
0
    if (ret > 0) {
632
      /* Is the requested script active? */
633
0
      ret = (strcmp(fscript->filename, afile) == 0 ? 1 : 0);
634
0
    }
635
0
  } T_END;
636
637
0
  return ret;
638
0
}
639
640
static int sieve_file_storage_script_delete(struct sieve_script *script)
641
0
{
642
0
  struct sieve_file_script *fscript =
643
0
    container_of(script, struct sieve_file_script, script);
644
0
  int ret = 0;
645
646
0
  if (sieve_file_storage_pre_modify(script->storage) < 0)
647
0
    return -1;
648
649
0
  ret = unlink(fscript->path);
650
0
  if (ret < 0) {
651
0
    if (errno == ENOENT) {
652
0
      sieve_script_set_error(script, SIEVE_ERROR_NOT_FOUND,
653
0
                 "Sieve script does not exist.");
654
0
    } else {
655
0
      sieve_script_set_critical(
656
0
        script,
657
0
        "Performing unlink() failed on sieve file '%s': %m",
658
0
        fscript->path);
659
0
    }
660
0
  }
661
0
  return ret;
662
0
}
663
664
static int
665
_sieve_file_storage_script_activate(struct sieve_file_script *fscript)
666
0
{
667
0
  struct sieve_script *script = &fscript->script;
668
0
  struct sieve_storage *storage = script->storage;
669
0
  struct sieve_file_storage *fstorage =
670
0
    container_of(storage, struct sieve_file_storage, storage);
671
0
  struct stat st;
672
0
  const char *link_path, *afile;
673
0
  int activated = 0;
674
0
  int ret;
675
676
  /* Find out whether there is an active script, but recreate
677
     the symlink either way. This way, any possible error in the symlink
678
     resolves automatically. This step is only necessary to provide a
679
     proper return value indicating whether the script was already active.
680
   */
681
0
  ret = sieve_file_storage_active_script_get_file(fstorage, &afile);
682
683
  /* Is the requested script already active? */
684
0
  if (ret <= 0 || strcmp(fscript->filename, afile) != 0)
685
0
    activated = 1;
686
687
0
  i_assert(fstorage->link_path != NULL);
688
689
  /* Check the scriptfile we are trying to activate */
690
0
  if (lstat(fscript->path, &st) != 0) {
691
0
    sieve_script_set_critical(
692
0
      script,
693
0
      "Failed to activate Sieve script: lstat(%s) failed: %m.",
694
0
      fscript->path);
695
0
    return -1;
696
0
  }
697
698
  /* Rescue a possible ".dovecot.sieve" regular file remaining from old
699
     installations.
700
   */
701
0
  if (!sieve_file_storage_active_rescue_regular(fstorage)) {
702
    /* Rescue failed, manual intervention is necessary */
703
0
    return -1;
704
0
  }
705
706
  /* Just try to create the symlink first */
707
0
  link_path = t_strconcat(fstorage->link_path, fscript->filename, NULL);
708
709
0
  ret = symlink(link_path, fstorage->active_path);
710
0
  if (ret < 0) {
711
0
    if (errno == EEXIST) {
712
0
      ret = sieve_file_storage_active_replace_link(
713
0
        fstorage, link_path);
714
0
      if (ret < 0)
715
0
        return ret;
716
0
    } else {
717
      /* Other error, critical */
718
0
      sieve_script_set_critical(
719
0
        script, "Failed to activate Sieve script: "
720
0
        "symlink(%s, %s) failed: %m",
721
0
        link_path, fstorage->active_path);
722
0
      return -1;
723
0
    }
724
0
  }
725
0
  return activated;
726
0
}
727
728
static int sieve_file_storage_script_activate(struct sieve_script *script)
729
0
{
730
0
  struct sieve_file_script *fscript =
731
0
    container_of(script, struct sieve_file_script, script);
732
0
  int ret;
733
734
0
  if (sieve_file_storage_pre_modify(script->storage) < 0)
735
0
    return -1;
736
737
0
  T_BEGIN {
738
0
    ret = _sieve_file_storage_script_activate(fscript);
739
0
  } T_END;
740
741
0
  return ret;
742
0
}
743
744
static int
745
sieve_file_storage_script_rename(struct sieve_script *script,
746
         const char *newname)
747
0
{
748
0
  struct sieve_file_script *fscript =
749
0
    container_of(script, struct sieve_file_script, script);
750
0
  struct sieve_storage *storage = script->storage;
751
0
  struct sieve_file_storage *fstorage =
752
0
    container_of(storage, struct sieve_file_storage, storage);
753
0
  const char *newpath, *newfile, *link_path;
754
0
  int ret = 0;
755
756
0
  if (sieve_file_storage_pre_modify(storage) < 0)
757
0
    return -1;
758
759
0
  T_BEGIN {
760
0
    newfile = sieve_script_file_from_name(newname);
761
0
    newpath = t_strconcat(fstorage->path, "/", newfile, NULL);
762
763
    /* The normal rename() system call overwrites the existing file
764
       without notice. Also, active scripts must not be disrupted by
765
       renaming a script. That is why we use a link(newpath)
766
       [activate newpath] unlink(oldpath)
767
     */
768
769
    /* Link to the new path */
770
0
    ret = link(fscript->path, newpath);
771
0
    if (ret >= 0) {
772
      /* Is the requested script active? */
773
0
      if (sieve_script_is_active(script) > 0) {
774
        /* Active; make active link point to the new
775
           copy */
776
0
        i_assert(fstorage->link_path != NULL);
777
0
        link_path = t_strconcat(fstorage->link_path,
778
0
              newfile, NULL);
779
780
0
        ret = sieve_file_storage_active_replace_link(
781
0
          fstorage, link_path);
782
0
      }
783
784
0
      if (ret >= 0) {
785
        /* If all is good, remove the old link */
786
0
        if (unlink(fscript->path) < 0) {
787
0
          e_error(script->event,
788
0
            "Failed to clean up after rename: "
789
0
            "unlink(%s) failed: %m",
790
0
            fscript->path);
791
0
        }
792
793
0
        if (script->name != NULL && *script->name != '\0')
794
0
          script->name = p_strdup(script->pool, newname);
795
0
        fscript->path = p_strdup(script->pool, newpath);
796
0
        fscript->filename = p_strdup(script->pool, newfile);
797
0
      } else {
798
        /* If something went wrong, remove the new link
799
           to restore previous state */
800
0
        if (unlink(newpath) < 0) {
801
0
          e_error(script->event,
802
0
            "Failed to clean up after failed rename: "
803
0
            "unlink(%s) failed: %m", newpath);
804
0
        }
805
0
      }
806
0
    } else {
807
      /* Our efforts failed right away */
808
0
      switch (errno) {
809
0
      case ENOENT:
810
0
        sieve_script_set_error(
811
0
          script, SIEVE_ERROR_NOT_FOUND,
812
0
          "Sieve script does not exist.");
813
0
        break;
814
0
      case EEXIST:
815
0
        sieve_script_set_error(
816
0
          script, SIEVE_ERROR_EXISTS,
817
0
          "A sieve script with that name already exists.");
818
0
        break;
819
0
      default:
820
0
        sieve_script_set_critical(
821
0
          script, "Failed to rename Sieve script: "
822
0
          "link(%s, %s) failed: %m",
823
0
          fscript->path, newpath);
824
0
      }
825
0
    }
826
0
  } T_END;
827
828
0
  return ret;
829
0
}
830
831
/*
832
 * Properties
833
 */
834
835
static int
836
sieve_file_script_get_size(const struct sieve_script *script, uoff_t *size_r)
837
0
{
838
0
  const struct sieve_file_script *fscript =
839
0
    container_of(script, const struct sieve_file_script, script);
840
841
0
  *size_r = fscript->st.st_size;
842
0
  return 1;
843
0
}
844
845
const char *sieve_file_script_get_dir_path(const struct sieve_script *script)
846
0
{
847
0
  const struct sieve_file_script *fscript =
848
0
    container_of(script, const struct sieve_file_script, script);
849
850
0
  if (script->driver_name != sieve_file_script.driver_name)
851
0
    return NULL;
852
853
0
  return fscript->dir_path;
854
0
}
855
856
const char *sieve_file_script_get_path(const struct sieve_script *script)
857
0
{
858
0
  const struct sieve_file_script *fscript =
859
0
    container_of(script, const struct sieve_file_script, script);
860
861
0
  if (script->driver_name != sieve_file_script.driver_name)
862
0
    return NULL;
863
864
0
  return fscript->path;
865
0
}
866
867
/*
868
 * Matching
869
 */
870
871
static int
872
sieve_file_script_cmp(const struct sieve_script *script1,
873
          const struct sieve_script *script2)
874
0
{
875
0
  const struct sieve_file_script *fscript1 =
876
0
    container_of(script1, const struct sieve_file_script, script);
877
0
  const struct sieve_file_script *fscript2 =
878
0
    container_of(script2, const struct sieve_file_script, script);
879
0
  int ret;
880
881
0
  if (!script1->open || !script2->open) {
882
0
    ret = sieve_storage_cmp(script1->storage, script2->storage);
883
0
    if (ret != 0)
884
0
      return ret;
885
886
0
    return null_strcmp(script1->name, script2->name);
887
0
  }
888
889
0
  if (major(fscript1->st.st_dev) != major(fscript2->st.st_dev)) {
890
0
    return (major(fscript1->st.st_dev) >
891
0
      major(fscript2->st.st_dev) ? 1 : -1);
892
0
  }
893
0
  if (minor(fscript1->st.st_dev) != minor(fscript2->st.st_dev)) {
894
0
    return (minor(fscript1->st.st_dev) >
895
0
      minor(fscript2->st.st_dev) ? 1 : -1);
896
0
  }
897
898
0
  if (fscript1->st.st_ino != fscript2->st.st_ino)
899
0
    return (fscript1->st.st_ino > fscript2->st.st_ino ? 1 : -1);
900
901
0
  return 0;
902
0
}
903
904
/*
905
 * Driver definition
906
 */
907
908
const struct sieve_script sieve_file_script = {
909
  .driver_name = SIEVE_FILE_STORAGE_DRIVER_NAME,
910
  .v = {
911
    .open = sieve_file_script_open,
912
913
    .get_stream = sieve_file_script_get_stream,
914
915
    .binary_read_metadata = sieve_file_script_binary_read_metadata,
916
    .binary_write_metadata =
917
      sieve_file_script_binary_write_metadata,
918
    .binary_dump_metadata = sieve_file_script_binary_dump_metadata,
919
    .binary_load = sieve_file_script_binary_load,
920
    .binary_save = sieve_file_script_binary_save,
921
    .binary_get_prefix = sieve_file_script_binary_get_prefix,
922
923
    .rename = sieve_file_storage_script_rename,
924
    .delete = sieve_file_storage_script_delete,
925
    .is_active = sieve_file_storage_script_is_active,
926
    .activate = sieve_file_storage_script_activate,
927
928
    .get_size = sieve_file_script_get_size,
929
930
    .cmp = sieve_file_script_cmp,
931
  }
932
};