1
/* Copyright (c) 2002-2009 Dovecot Sieve authors, see the included COPYING file
11
#include "sieve-script-private.h"
13
#include "sieve-storage.h"
14
#include "sieve-storage-private.h"
15
#include "sieve-storage-script.h"
25
struct sieve_storage_script {
26
struct sieve_script script;
28
struct sieve_storage *storage;
31
struct sieve_script *sieve_storage_script_init_from_path
32
(struct sieve_storage *storage, const char *path,
33
const char *scriptname, bool *exists_r)
36
struct sieve_storage_script *st_script = NULL;
38
/* Prevent initializing the active script link as a script when it
39
* resides in the sieve storage directory.
41
if ( *(storage->link_path) == '\0' ) {
44
fname = strrchr(path, '/');
50
if ( strcmp(fname, storage->active_fname) == 0 ) {
51
if ( exists_r != NULL )
57
pool = pool_alloconly_create("sieve_storage_script", 4096);
58
st_script = p_new(pool, struct sieve_storage_script, 1);
59
st_script->script.pool = pool;
60
st_script->storage = storage;
62
if ( sieve_script_init(&st_script->script, path, scriptname,
63
sieve_storage_get_error_handler(storage), exists_r) != NULL ) {
65
return &st_script->script;
73
struct sieve_script *sieve_storage_script_init
74
(struct sieve_storage *storage, const char *scriptname, bool *exists_r)
76
struct sieve_script *script;
79
/* Disallow '/' characters in script name */
80
if ( strchr(scriptname, '/') != NULL ) {
81
sieve_storage_set_error(storage,
82
SIEVE_STORAGE_ERROR_IMPOSSIBLE,
83
"Invalid script name '%s'.", scriptname);
88
path = t_strconcat( storage->dir, "/", scriptname, ".sieve", NULL );
90
script = sieve_storage_script_init_from_path
91
(storage, path, NULL, exists_r);
97
static struct sieve_script *sieve_storage_script_init_from_file
98
(struct sieve_storage *storage, const char *scriptfile, bool *exists_r)
100
struct sieve_script *script;
104
path = t_strconcat( storage->dir, "/", scriptfile, NULL );
106
script = sieve_storage_script_init_from_path
107
(storage, path, NULL, exists_r);
113
const char *sieve_storage_file_get_scriptname
114
(const struct sieve_storage *storage ATTR_UNUSED, const char *filename)
118
ext = strrchr(filename, '.');
120
if ( ext == NULL || ext == filename || strcmp(ext,".sieve") != 0 )
123
return t_strdup_until(filename, ext);
126
static const char *sieve_storage_read_active_link
127
(struct sieve_storage *storage, bool *not_link)
129
char linkbuf[PATH_MAX];
132
if ( not_link != NULL )
135
ret = readlink(storage->active_path, linkbuf, sizeof(linkbuf));
138
if (errno == EINVAL) {
139
/* Our symlink is no symlink. Report 'no active script'.
140
* Activating a script will automatically resolve this, so
141
* there is no need to panic on this one.
144
("sieve-storage: Active sieve script symlink %s is no symlink.",
145
storage->active_path);
146
if ( not_link != NULL )
151
if (errno != ENOENT ) {
152
/* We do need to panic otherwise */
153
sieve_storage_set_critical
155
"Performing readlink() on active sieve symlink '%s' failed: %m",
156
storage->active_path);
163
/* ret is now assured to be valid, i.e. > 0 */
164
return t_strndup(linkbuf, ret);
167
static const char *sieve_storage_parse_link
168
(struct sieve_storage *storage, const char *link)
170
const char *fname, *scriptname, *scriptpath;
172
/* Split link into path and filename */
173
fname = strrchr(link, '/');
174
if ( fname != NULL ) {
175
scriptpath = t_strdup_until(link, fname+1);
182
/* Check the script name */
183
scriptname = sieve_storage_file_get_scriptname(storage, fname);
185
/* Warn if link is deemed to be invalid */
186
if ( scriptname == NULL ) {
188
("sieve-storage: Active sieve script symlink %s is broken: "
189
"invalid scriptname (points to %s).",
190
storage->active_path, link);
194
/* Check whether the path is any good */
195
if ( strcmp(scriptpath, storage->link_path) != 0 &&
196
strcmp(scriptpath, storage->dir) != 0 ) {
198
("sieve-storage: Active sieve script symlink %s is broken: "
199
"invalid/unknown path to storage (points to %s).",
200
storage->active_path, link);
207
const char *sieve_storage_get_active_scriptfile
208
(struct sieve_storage *storage)
210
const char *link, *scriptfile;
212
/* Read the active link */
213
link = sieve_storage_read_active_link(storage, NULL);
215
if ( link == NULL || *link == '\0' )
219
scriptfile = sieve_storage_parse_link(storage, link);
221
if (scriptfile == NULL) {
222
/* Obviously someone has been playing with our symlink,
223
* ignore this situation and report 'no active script'.
224
* Activation should fix this situation.
232
struct sieve_script *sieve_storage_get_active_script
233
(struct sieve_storage *storage, bool *no_active)
235
bool exists, no_link;
236
struct sieve_script *script;
237
const char *scriptfile, *link;
241
/* Read the active link */
242
link = sieve_storage_read_active_link(storage, &no_link);
251
/* Try to open the active_path as a regular file */
252
return sieve_storage_script_init_from_path
253
(storage, storage->active_path, NULL, NULL);
261
scriptfile = sieve_storage_parse_link(storage, link);
263
if (scriptfile == NULL) {
264
/* Obviously someone has been playing with our symlink,
265
* ignore this situation and report 'no active script'.
272
script = sieve_storage_script_init_from_file(storage, scriptfile, &exists);
276
("sieve-storage: Active sieve script symlink %s "
277
"points to non-existent script (points to %s).",
278
storage->active_path, link);
281
*no_active = !exists;
285
int sieve_storage_script_is_active(struct sieve_script *script)
287
struct sieve_storage_script *st_script = (struct sieve_storage_script *) script;
292
afile = sieve_storage_get_active_scriptfile(st_script->storage);
298
/* Is the requested script active? */
299
if ( strcmp(script->filename, afile) == 0 )
307
int sieve_storage_script_delete(struct sieve_script **script)
309
struct sieve_storage_script *st_script = (struct sieve_storage_script *) *script;
310
struct sieve_storage *storage = st_script->storage;
313
/* Is the requested script active? */
314
if ( sieve_storage_script_is_active(*script) ) {
315
sieve_storage_set_error(storage, SIEVE_STORAGE_ERROR_ACTIVE,
316
"Cannot delete the active sieve script.");
319
ret = unlink((*script)->path);
322
if ( errno == ENOENT )
323
sieve_storage_set_error(storage, SIEVE_STORAGE_ERROR_NOTFOUND,
324
"Sieve script does not exist.");
326
sieve_storage_set_critical(
327
storage, "Performing unlink() failed on sieve file '%s': %m",
332
/* Always deinitialize the script object */
333
sieve_script_unref(script);
338
static bool sieve_storage_rescue_regular_file(struct sieve_storage *storage)
343
if ( lstat(storage->active_path, &st) != 0 ) {
344
if ( errno != ENOENT ) {
345
sieve_storage_set_critical(storage,
346
"Failed to stat active sieve script symlink (%s): %m.",
347
storage->active_path);
353
if ( S_ISLNK( st.st_mode ) ) {
354
if ( getenv("DEBUG") != NULL )
355
i_info( "sieve-storage: nothing to rescue %s.", storage->active_path);
356
return TRUE; /* Nothing to rescue */
359
/* Only regular files can be rescued */
360
if ( S_ISREG( st.st_mode ) ) {
366
dstpath = t_strconcat
367
( storage->dir, "/dovecot.orig.sieve", NULL );
368
if ( file_copy(storage->active_path, dstpath, 1) < 1 ) {
369
sieve_storage_set_critical(storage,
370
"Active sieve script file '%s' is a regular file and copying it to the "
371
"script storage as '%s' failed. This needs to be fixed manually.",
372
storage->active_path, dstpath);
375
i_info("sieve-storage: Moved active sieve script file '%s' "
376
"to script storage as '%s'.",
377
storage->active_path, dstpath);
384
sieve_storage_set_critical( storage,
385
"Active sieve script file '%s' is no symlink nor a regular file. "
386
"This needs to be fixed manually.", storage->active_path );
390
int sieve_storage_deactivate(struct sieve_storage *storage)
394
if ( !sieve_storage_rescue_regular_file(storage) )
397
/* Delete the symlink, so no script is active */
398
ret = unlink(storage->active_path);
401
if ( errno != ENOENT ) {
402
sieve_storage_set_critical(storage, "sieve_storage_deactivate(): "
403
"error on unlink(%s): %m", storage->active_path);
412
static int sieve_storage_replace_active_link
413
(struct sieve_storage *storage, const char *link_path)
415
const char *active_path_new;
416
struct timeval *tv, tv_now;
419
tv = &ioloop_timeval;
422
/* First the new symlink is created with a different filename */
423
active_path_new = t_strdup_printf
424
("%s-new.%s.P%sM%s.%s.sieve",
425
storage->active_path,
426
dec2str(tv->tv_sec), my_pid,
427
dec2str(tv->tv_usec), my_hostname);
429
ret = symlink(link_path, active_path_new);
432
/* If link exists we try again later */
433
if ( errno == EEXIST ) {
434
/* Wait and try again - very unlikely */
437
if (gettimeofday(&tv_now, NULL) < 0)
438
i_fatal("gettimeofday(): %m");
442
/* Other error, critical */
443
sieve_storage_set_critical
444
(storage, "Creating symlink() %s to %s failed: %m",
445
active_path_new, link_path);
453
/* Replace the existing link. This activates the new script */
454
ret = rename(active_path_new, storage->active_path);
457
/* Failed; created symlink must be deleted */
458
(void)unlink(active_path_new);
459
sieve_storage_set_critical
460
(storage, "Performing rename() %s to %s failed: %m",
461
active_path_new, storage->active_path);
468
static int _sieve_storage_script_activate(struct sieve_script *script)
470
struct sieve_storage_script *st_script = (struct sieve_storage_script *) script;
471
struct sieve_storage *storage = st_script->storage;
473
const char *link_path, *afile;
477
/* Find out whether there is an active script, but recreate
478
* the symlink either way. This way, any possible error in the symlink
479
* resolves automatically. This step is only necessary to provide a
480
* proper return value indicating whether the script was already active.
482
afile = sieve_storage_get_active_scriptfile(storage);
484
/* Is the requested script already active? */
485
if ( afile == NULL || strcmp(script->filename, afile) != 0 )
488
/* Check the scriptfile we are trying to activate */
489
if ( lstat(script->path, &st) != 0 ) {
490
sieve_storage_set_critical(storage,
491
"Stat on sieve script %s failed, but it is to be activated: %m.",
496
/* Rescue a possible .dovecot.sieve regular file remaining from old
499
if ( !sieve_storage_rescue_regular_file(storage) ) {
500
/* Rescue failed, manual intervention is necessary */
504
/* Just try to create the symlink first */
505
link_path = t_strconcat
506
( storage->link_path, script->filename, NULL );
508
ret = symlink(link_path, storage->active_path);
511
if ( errno == EEXIST ) {
512
ret = sieve_storage_replace_active_link(storage, link_path);
517
/* Other error, critical */
518
sieve_storage_set_critical
520
"Creating symlink() %s to %s failed: %m",
521
storage->active_path, link_path);
529
int sieve_storage_script_activate(struct sieve_script *script)
534
ret = _sieve_storage_script_activate(script);
540
int sieve_storage_script_rename(struct sieve_script *script, const char *newname)
542
struct sieve_storage_script *st_script = (struct sieve_storage_script *) script;
543
struct sieve_storage *storage = st_script->storage;
544
const char *newpath, *newfile, *link_path;
547
/* Disallow '/' characters in script name */
548
if ( strchr(newname, '/') != NULL ) {
549
sieve_storage_set_error(storage,
550
SIEVE_STORAGE_ERROR_IMPOSSIBLE,
551
"Invalid new script name '%s'.", newname);
556
newfile = t_strconcat( newname, ".sieve", NULL );
557
newpath = t_strconcat( storage->dir, "/", newfile, NULL );
559
/* The normal rename() system call overwrites the existing file without notice.
560
* Also, active scripts must not be disrupted by renaming a script. That is why
561
* we use a link(newpath) [activate newpath] unlink(oldpath)
564
/* Link to the new path */
565
ret = link(script->path, newpath);
567
/* Is the requested script active? */
568
if ( sieve_storage_script_is_active(script) ) {
569
/* Active; make active link point to the new copy */
570
link_path = t_strconcat
571
( storage->link_path, newfile, NULL );
573
ret = sieve_storage_replace_active_link(storage, link_path);
577
/* If all is good, remove the old link */
578
if ( unlink(script->path) < 0 ) {
579
i_error("Failed to clean up old file link '%s' after rename: %m",
583
if ( script->name != NULL && *script->name != '\0' )
584
script->name = p_strdup(script->pool, newname);
585
script->path = p_strdup(script->pool, newpath);
586
script->filename = p_strdup(script->pool, newfile);
587
script->basename = p_strdup(script->pool, newname);
589
/* If something went wrong, remove the new link to restore previous state */
590
if ( unlink(newpath) < 0 ) {
591
i_error("Failed to clean up new file link '%s'"
592
" after failed rename: %m", newpath);
596
/* Our efforts failed right away */
599
sieve_storage_set_error(storage, SIEVE_STORAGE_ERROR_NOTFOUND,
600
"Sieve script does not exist.");
603
sieve_storage_set_error(storage, SIEVE_STORAGE_ERROR_EXISTS,
604
"A sieve script with that name already exists.");
607
sieve_storage_set_critical(
608
storage, "Performing link(%s, %s) failed: %m",
609
script->path, newpath);