Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F117748076
cyr_expire.c
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Authored By
Unknown
Size
26 KB
Referenced Files
None
Subscribers
None
cyr_expire.c
View Options
/* cyr_expire.c -- Program to expire deliver.db entries and messages
*
* Copyright (c) 1994-2017 Carnegie Mellon University. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The name "Carnegie Mellon University" must not be used to
* endorse or promote products derived from this software without
* prior written permission. For permission or any legal
* details, please contact
* Carnegie Mellon University
* Center for Technology Transfer and Enterprise Creation
* 4615 Forbes Avenue
* Suite 302
* Pittsburgh, PA 15213
* (412) 268-7393, fax: (412) 268-7395
* innovation@andrew.cmu.edu
*
* 4. Redistributions of any form whatsoever must retain the following
* acknowledgment:
* "This product includes software developed by Computing Services
* at Carnegie Mellon University (http://www.cmu.edu/computing/)."
*
* CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
* THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
* FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
* AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
/*
NOTES:
* Precedence of configuration in `cyr_expire`:
Highest Lowest
|----------------------------------|
Annotation -> Command-line -> Config
*/
#include
<config.h>
#ifdef HAVE_UNISTD_H
#include
<unistd.h>
#endif
#include
<stdlib.h>
#include
<stdio.h>
#include
<string.h>
#include
<fcntl.h>
#include
<sys/stat.h>
#include
<syslog.h>
#include
<signal.h>
#include
<errno.h>
#include
<stdbool.h>
#include
<libgen.h>
#include
<sasl/sasl.h>
#include
"annotate.h"
#include
"duplicate.h"
#include
"exitcodes.h"
#include
"global.h"
#include
"hash.h"
#include
"libcyr_cfg.h"
#include
"mboxevent.h"
#include
"mboxlist.h"
#include
"conversations.h"
#include
"util.h"
#include
"xmalloc.h"
#include
"strarray.h"
/* generated headers are not necessarily in current directory */
#include
"imap/imap_err.h"
#define SECS_IN_A_MIN 60
#define SECS_IN_AN_HR (60 * SECS_IN_A_MIN)
#define SECS_IN_A_DAY (24 * SECS_IN_AN_HR)
/* global state */
static
volatile
sig_atomic_t
sigquit
=
0
;
static
int
verbose
=
0
;
static
const
char
*
progname
=
NULL
;
static
struct
namespace
expire_namespace
;
/* current namespace */
/* command line arguments */
struct
arguments
{
int
archive_seconds
;
int
delete_seconds
;
int
expire_seconds
;
int
expunge_seconds
;
int
do_cid_expire
;
/* bools */
bool
do_expunge
;
bool
do_userflags
;
bool
skip_annotate
;
const
char
*
altconfig
;
const
char
*
mbox_prefix
;
const
char
*
userid
;
};
struct
archive_rock
{
time_t
archive_mark
;
unsigned
long
messages_archived
;
bool
skip_annotate
;
};
struct
expire_rock
{
struct
hash_table
table
;
time_t
expire_mark
;
time_t
expunge_mark
;
unsigned
long
mailboxes_seen
;
unsigned
long
messages_seen
;
unsigned
long
messages_expired
;
unsigned
long
messages_expunged
;
unsigned
long
userflags_expunged
;
bit32
userflags
[
MAX_USER_FLAGS
/
32
];
bool
do_userflags
;
bool
skip_annotate
;
};
struct
conversations_rock
{
struct
hash_table
seen
;
time_t
expire_mark
;
unsigned
long
databases_seen
;
unsigned
long
msgids_seen
;
unsigned
long
msgids_expired
;
};
struct
delete_rock
{
time_t
delete_mark
;
strarray_t
to_delete
;
bool
skip_annotate
;
};
/* The global context */
struct
cyr_expire_ctx
{
struct
arguments
args
;
struct
archive_rock
arock
;
struct
conversations_rock
crock
;
struct
delete_rock
drock
;
struct
expire_rock
erock
;
};
static
const
struct
cyr_expire_ctx
zero_ctx
;
static
void
sighandler
(
int
sig
);
/* verbosep - a wrapper to print if the 'verbose' option is
turned on.
*/
__attribute__
((
format
(
printf
,
1
,
2
)))
static
inline
void
verbosep
(
const
char
*
fmt
,
...)
{
va_list
params
;
if
(
!
verbose
)
return
;
va_start
(
params
,
fmt
);
vfprintf
(
stderr
,
fmt
,
params
);
va_end
(
params
);
fputc
(
'\n'
,
stderr
);
}
static
void
cyr_expire_init
(
const
char
*
progname
,
struct
cyr_expire_ctx
*
ctx
)
{
struct
sigaction
action
;
/* Initialise signal handlers */
sigemptyset
(
&
action
.
sa_mask
);
action
.
sa_flags
=
0
;
action
.
sa_handler
=
sighandler
;
if
(
sigaction
(
SIGQUIT
,
&
action
,
NULL
)
<
0
)
fatal
(
"unable to install signal handler for SIGQUIT"
,
EC_TEMPFAIL
);
if
(
sigaction
(
SIGINT
,
&
action
,
NULL
)
<
0
)
fatal
(
"unable to install signal handler for SIGINT"
,
EC_TEMPFAIL
);
if
(
sigaction
(
SIGTERM
,
&
action
,
NULL
)
<
0
)
fatal
(
"unable to install signal handler for SIGTERM"
,
EC_TEMPFAIL
);
construct_hash_table
(
&
ctx
->
erock
.
table
,
10000
,
1
);
strarray_init
(
&
ctx
->
drock
.
to_delete
);
construct_hash_table
(
&
ctx
->
crock
.
seen
,
100
,
1
);
cyrus_init
(
ctx
->
args
.
altconfig
,
progname
,
0
,
0
);
global_sasl_init
(
1
,
0
,
NULL
);
ctx
->
erock
.
do_userflags
=
ctx
->
args
.
do_userflags
;
/* TODO: Ideally all the functions should just use the skip_annotate from
* args. But that would require a change in the callback signatures.
* So retaining it as is for now.
*/
ctx
->
arock
.
skip_annotate
=
ctx
->
args
.
skip_annotate
;
ctx
->
erock
.
skip_annotate
=
ctx
->
args
.
skip_annotate
;
ctx
->
drock
.
skip_annotate
=
ctx
->
args
.
skip_annotate
;
}
static
void
cyr_expire_cleanup
(
struct
cyr_expire_ctx
*
ctx
)
{
free_hash_table
(
&
ctx
->
erock
.
table
,
free
);
free_hash_table
(
&
ctx
->
crock
.
seen
,
NULL
);
strarray_fini
(
&
ctx
->
drock
.
to_delete
);
duplicate_done
();
sasl_done
();
cyrus_done
();
}
static
void
usage
(
void
)
{
fprintf
(
stderr
,
"Usage: %s [OPTIONS] {mailbox|users}
\n
"
,
progname
);
fprintf
(
stderr
,
"Expire messages and duplicate delivery database entries.
\n
"
);
fprintf
(
stderr
,
"
\n
"
);
fprintf
(
stderr
,
"-a skip annotation lookup
\n
"
);
fprintf
(
stderr
,
"-c do not expire conversations
\n
"
);
fprintf
(
stderr
,
"-h print this help and exit
\n
"
);
fprintf
(
stderr
,
"-p <mailbox-prefix> specify prefix for mailboxes
\n
"
);
fprintf
(
stderr
,
"-t remove user flags which are not used
\n
"
);
fprintf
(
stderr
,
"-u <user-id> specify user id for mailbox lookup
\n
"
);
fprintf
(
stderr
,
"-v enable verbose output
\n
"
);
fprintf
(
stderr
,
"-x do not expunge messages
\n
"
);
fprintf
(
stderr
,
"-C <config-file> use <config-file> instead of config from imapd.conf
\n
"
);
fprintf
(
stderr
,
"-A <archive-duration>
\n
"
);
fprintf
(
stderr
,
"-D <delete-duration>
\n
"
);
fprintf
(
stderr
,
"-E <expire-duration>
\n
"
);
fprintf
(
stderr
,
"-X <expunge-duration>
\n
"
);
fprintf
(
stderr
,
"
\n
"
);
exit
(
EC_USAGE
);
}
/*
* Parse a non-negative duration string as seconds.
*
* Convert "23.5m" to fractional days. Accepts the suffixes "d" (day),
* (day), "h" (hour), "m" (minute) and "s" (second). If no suffix, assume
* days.
* Returns 1 if successful and *secondsp is filled in, or 0 if the suffix
* is unknown or on error.
*/
static
int
parse_duration
(
const
char
*
s
,
int
*
secondsp
)
{
char
*
end
=
NULL
;
double
val
;
int
multiplier
=
SECS_IN_A_DAY
;
/* default is days */
/* no negative or empty numbers please */
if
(
!*
s
||
*
s
==
'-'
)
return
0
;
val
=
strtod
(
s
,
&
end
);
/* Allow 'd', 'h', 'm' and 's' as end, else return error. */
if
(
*
end
)
{
if
(
end
[
1
])
return
0
;
/* trailing extra junk */
switch
(
*
end
)
{
case
'd'
:
/* already the default */
break
;
case
'h'
:
multiplier
=
SECS_IN_AN_HR
;
break
;
case
'm'
:
multiplier
=
SECS_IN_A_MIN
;
break
;
case
's'
:
multiplier
=
1
;
break
;
default
:
return
0
;
}
}
*
secondsp
=
multiplier
*
val
;
return
1
;
}
/*
* Given an annotation, reads it, and converts it into 'seconds',
* using `parse_duration`.
*
* On Success: Returns 1
* On Failure: Returns 0
*/
static
int
get_annotation_value
(
const
char
*
mboxname
,
const
char
*
annot_entry
,
int
*
secondsp
,
bool
iterate
)
{
struct
buf
attrib
=
BUF_INITIALIZER
;
int
ret
=
0
;
/* mboxname needs to be copied since `mboxname_make_parent`
* runs a strrchr() on it.
*/
char
*
buf
=
xstrdup
(
mboxname
);
/*
* Mailboxes inherit /vendo/cmu/cyrus-imapd/{expire, archive, delete},
* so we need to iterate all the way up to "" (server entry).
*/
do
{
buf_free
(
&
attrib
);
ret
=
annotatemore_lookup
(
buf
,
annot_entry
,
""
,
&
attrib
);
if
(
ret
||
/* error */
attrib
.
s
)
/* found an entry */
break
;
}
while
(
mboxname_make_parent
(
buf
)
&&
iterate
);
if
(
attrib
.
s
&&
parse_duration
(
attrib
.
s
,
secondsp
))
ret
=
1
;
else
ret
=
0
;
buf_free
(
&
attrib
);
free
(
buf
);
syslog
(
LOG_DEBUG
,
"get_annotation_value: ret(%d):secondsp(%d)
\n
"
,
ret
,
*
secondsp
);
return
ret
;
}
static
int
expunge_userflags
(
struct
mailbox
*
mailbox
,
struct
expire_rock
*
erock
)
{
unsigned
int
i
;
int
r
;
for
(
i
=
0
;
i
<
MAX_USER_FLAGS
;
i
++
)
{
if
(
erock
->
userflags
[
i
/
32
]
&
1
<<
(
i
&
31
))
continue
;
if
(
!
mailbox
->
flagname
[
i
])
continue
;
verbosep
(
"Expunging userflag %u (%s) from %s
\n
"
,
i
,
mailbox
->
flagname
[
i
],
mailbox
->
name
);
r
=
mailbox_remove_user_flag
(
mailbox
,
i
);
if
(
r
)
return
r
;
erock
->
userflags_expunged
++
;
}
return
0
;
}
/*
* mailbox_expunge() callback to *only* count userflags.
*/
static
unsigned
userflag_cb
(
struct
mailbox
*
mailbox
__attribute__
((
unused
)),
const
struct
index_record
*
record
,
void
*
rock
)
{
struct
expire_rock
*
erock
=
(
struct
expire_rock
*
)
rock
;
unsigned
int
i
;
/* record which user flags are set */
for
(
i
=
0
;
i
<
(
MAX_USER_FLAGS
/
32
);
i
++
)
erock
->
userflags
[
i
]
|=
record
->
user_flags
[
i
];
return
0
;
/* always keep the message */
}
static
int
archive
(
const
mbentry_t
*
mbentry
,
void
*
rock
)
{
struct
archive_rock
*
arock
=
(
struct
archive_rock
*
)
rock
;
struct
mailbox
*
mailbox
=
NULL
;
int
archive_seconds
=
-1
;
if
(
sigquit
)
return
1
;
if
(
mbentry
->
mbtype
&
MBTYPE_DELETED
)
goto
done
;
if
(
mbentry
->
mbtype
&
MBTYPE_REMOTE
)
goto
done
;
if
(
mailbox_open_iwl
(
mbentry
->
name
,
&
mailbox
))
goto
done
;
/* check /vendor/cmu/cyrus-imapd/archive */
if
(
!
arock
->
skip_annotate
&&
get_annotation_value
(
mbentry
->
name
,
IMAP_ANNOT_NS
"archive"
,
&
archive_seconds
,
false
))
{
arock
->
archive_mark
=
archive_seconds
?
time
(
0
)
-
archive_seconds
:
0
;
}
/* The default callback for mailbox_archive() is mailbox_should_archive()
* in imap/mailbox.c. This one takes the arock->archive_mark as the
* callback data.
*/
mailbox_archive
(
mailbox
,
NULL
,
&
arock
->
archive_mark
,
ITER_SKIP_EXPUNGED
);
done
:
mailbox_close
(
&
mailbox
);
/* move on to the next mailbox regardless of errors */
return
0
;
}
/*
* mailbox_expunge() callback to expunge expired articles.
*/
static
unsigned
expire_cb
(
struct
mailbox
*
mailbox
__attribute__
((
unused
)),
const
struct
index_record
*
record
,
void
*
rock
)
{
struct
expire_rock
*
erock
=
(
struct
expire_rock
*
)
rock
;
unsigned
int
i
;
/* otherwise, we're expiring messages by sent date */
if
(
record
->
gmtime
<
erock
->
expire_mark
)
{
erock
->
messages_expired
++
;
return
1
;
}
/* record which user flags are set */
for
(
i
=
0
;
i
<
(
MAX_USER_FLAGS
/
32
);
i
++
)
erock
->
userflags
[
i
]
|=
record
->
user_flags
[
i
];
return
0
;
}
/*
* callback function to:
* - expire messages from mailboxes,
* - build a hash table of mailboxes in which we expired messages,
* - and perform a cleanup of expunged messages
*/
static
int
expire
(
const
mbentry_t
*
mbentry
,
void
*
rock
)
{
struct
expire_rock
*
erock
=
(
struct
expire_rock
*
)
rock
;
int
r
;
struct
mailbox
*
mailbox
=
NULL
;
unsigned
numexpunged
=
0
;
int
expire_seconds
=
0
;
int
did_expunge
=
0
;
if
(
sigquit
)
{
/* don't care if we leak some memory, we are shutting down */
return
1
;
}
/* Skip remote mailboxes */
if
(
mbentry
->
mbtype
&
MBTYPE_REMOTE
)
goto
done
;
/* clean up deleted entries after 7 days */
if
(
mbentry
->
mbtype
&
MBTYPE_DELETED
)
{
if
(
time
(
0
)
-
mbentry
->
mtime
>
SECS_IN_A_DAY
*
7
)
{
verbosep
(
"Removing stale tombstone for %s
\n
"
,
mbentry
->
name
);
syslog
(
LOG_NOTICE
,
"Removing stale tombstone for %s"
,
mbentry
->
name
);
mboxlist_delete
(
mbentry
->
name
);
}
goto
done
;
}
memset
(
erock
->
userflags
,
0
,
sizeof
(
erock
->
userflags
));
r
=
mailbox_open_iwl
(
mbentry
->
name
,
&
mailbox
);
if
(
r
)
{
/* mailbox corrupt/nonexistent -- skip it */
syslog
(
LOG_WARNING
,
"unable to open mailbox %s: %s"
,
mbentry
->
name
,
error_message
(
r
));
goto
done
;
}
/* see if we need to expire messages.
* since mailboxes inherit /vendor/cmu/cyrus-imapd/expire,
* we need to iterate all the way up to "" (server entry)
*/
if
(
!
erock
->
skip_annotate
&&
get_annotation_value
(
mbentry
->
name
,
IMAP_ANNOT_NS
"expire"
,
&
expire_seconds
,
true
))
{
/* add mailbox to table */
erock
->
expire_mark
=
expire_seconds
?
time
(
0
)
-
expire_seconds
:
0
/* never */
;
hash_insert
(
mbentry
->
name
,
xmemdup
(
&
erock
->
expire_mark
,
sizeof
(
erock
->
expire_mark
)),
&
erock
->
table
);
if
(
expire_seconds
)
{
verbosep
(
"expiring messages in %s older than %0.2f days
\n
"
,
mbentry
->
name
,
((
double
)
expire_seconds
/
SECS_IN_A_DAY
));
r
=
mailbox_expunge
(
mailbox
,
expire_cb
,
erock
,
NULL
,
EVENT_MESSAGE_EXPIRE
);
if
(
r
)
syslog
(
LOG_ERR
,
"failed to expire old messages: %s"
,
mbentry
->
name
);
did_expunge
=
1
;
}
}
if
(
!
did_expunge
&&
erock
->
do_userflags
)
{
r
=
mailbox_expunge
(
mailbox
,
userflag_cb
,
erock
,
NULL
,
EVENT_MESSAGE_EXPIRE
);
if
(
r
)
syslog
(
LOG_ERR
,
"failed to scan user flags for %s: %s"
,
mbentry
->
name
,
error_message
(
r
));
}
erock
->
messages_seen
+=
mailbox
->
i
.
num_records
;
if
(
erock
->
do_userflags
)
expunge_userflags
(
mailbox
,
erock
);
verbosep
(
"cleaning up expunged messages in %s
\n
"
,
mbentry
->
name
);
r
=
mailbox_expunge_cleanup
(
mailbox
,
erock
->
expunge_mark
,
&
numexpunged
);
erock
->
messages_expunged
+=
numexpunged
;
erock
->
mailboxes_seen
++
;
if
(
r
)
{
syslog
(
LOG_WARNING
,
"failure expiring %s: %s"
,
mbentry
->
name
,
error_message
(
r
));
annotate_state_abort
(
&
mailbox
->
annot_state
);
}
done
:
mailbox_close
(
&
mailbox
);
/* Even if we had a problem with one mailbox, continue with the others */
return
0
;
}
static
int
delete
(
const
mbentry_t
*
mbentry
,
void
*
rock
)
{
struct
delete_rock
*
drock
=
(
struct
delete_rock
*
)
rock
;
time_t
timestamp
;
int
delete_seconds
=
-1
;
if
(
sigquit
)
return
1
;
if
(
mbentry
->
mbtype
&
MBTYPE_DELETED
)
goto
done
;
if
(
mbentry
->
mbtype
&
MBTYPE_REMOTE
)
goto
done
;
/* check if this is a mailbox we want to examine */
if
(
!
mboxname_isdeletedmailbox
(
mbentry
->
name
,
&
timestamp
))
goto
done
;
/* check /vendor/cmu/cyrus-imapd/delete */
if
(
!
drock
->
skip_annotate
&&
get_annotation_value
(
mbentry
->
name
,
IMAP_ANNOT_NS
"delete"
,
&
delete_seconds
,
false
))
{
drock
->
delete_mark
=
delete_seconds
?
time
(
0
)
-
delete_seconds
:
0
;
}
if
((
timestamp
==
0
)
||
(
timestamp
>
drock
->
delete_mark
))
goto
done
;
verbosep
(
"Cleaning up %s
\n
"
,
mbentry
->
name
);
/* Add this mailbox to list of mailboxes to delete */
strarray_append
(
&
drock
->
to_delete
,
mbentry
->
name
);
done
:
/* Even if we had a problem with one mailbox, continue with the others */
return
0
;
}
static
int
expire_conversations
(
const
mbentry_t
*
mbentry
,
void
*
rock
)
{
struct
conversations_rock
*
crock
=
(
struct
conversations_rock
*
)
rock
;
struct
conversations_state
*
state
=
NULL
;
unsigned
int
nseen
=
0
,
ndeleted
=
0
;
char
*
filename
=
NULL
;
if
(
sigquit
)
return
1
;
if
(
mbentry
->
mbtype
&
MBTYPE_DELETED
)
goto
done
;
if
(
mbentry
->
mbtype
&
MBTYPE_REMOTE
)
goto
done
;
filename
=
conversations_getmboxpath
(
mbentry
->
name
);
if
(
!
filename
)
goto
done
;
if
(
hash_lookup
(
filename
,
&
crock
->
seen
))
goto
done
;
verbosep
(
"Pruning conversations from db %s
\n
"
,
filename
);
if
(
!
conversations_open_mbox
(
mbentry
->
name
,
&
state
))
{
conversations_prune
(
state
,
crock
->
expire_mark
,
&
nseen
,
&
ndeleted
);
conversations_commit
(
&
state
);
}
hash_insert
(
filename
,
(
void
*
)
1
,
&
crock
->
seen
);
crock
->
databases_seen
++
;
crock
->
msgids_seen
+=
nseen
;
crock
->
msgids_expired
+=
ndeleted
;
done
:
free
(
filename
);
return
0
;
}
static
void
sighandler
(
int
sig
__attribute
((
unused
)))
{
sigquit
=
1
;
return
;
}
static
int
do_archive
(
struct
cyr_expire_ctx
*
ctx
)
{
if
(
ctx
->
args
.
archive_seconds
>=
0
)
{
syslog
(
LOG_DEBUG
,
">> do_archive: archive_seconds(%d) >= 0
\n
"
,
ctx
->
args
.
archive_seconds
);
ctx
->
arock
.
archive_mark
=
time
(
0
)
-
ctx
->
args
.
archive_seconds
;
if
(
ctx
->
args
.
userid
)
mboxlist_usermboxtree
(
ctx
->
args
.
userid
,
archive
,
&
ctx
->
arock
,
MBOXTREE_DELETED
);
else
mboxlist_allmbox
(
ctx
->
args
.
mbox_prefix
,
archive
,
&
ctx
->
arock
,
0
);
}
return
0
;
}
static
int
do_expunge
(
struct
cyr_expire_ctx
*
ctx
)
{
if
(
ctx
->
args
.
do_expunge
&&
(
ctx
->
args
.
expunge_seconds
>=
0
||
ctx
->
args
.
expire_seconds
||
ctx
->
erock
.
do_userflags
))
{
/* XXX: better way to determine a size for this table? */
/* expire messages from mailboxes,
* build a hash table of mailboxes in which we expired messages,
* and perform a cleanup of expunged messages
*/
if
(
ctx
->
args
.
expunge_seconds
<
0
)
{
ctx
->
erock
.
expunge_mark
=
0
;
}
else
{
ctx
->
erock
.
expunge_mark
=
time
(
0
)
-
ctx
->
args
.
expunge_seconds
;
verbosep
(
"Expunging deleted messages in mailboxes older than %0.2f days
\n
"
,
((
double
)
ctx
->
args
.
expunge_seconds
/
SECS_IN_A_DAY
));
}
if
(
ctx
->
args
.
userid
)
mboxlist_usermboxtree
(
ctx
->
args
.
userid
,
expire
,
&
ctx
->
erock
,
MBOXTREE_DELETED
);
else
mboxlist_allmbox
(
ctx
->
args
.
mbox_prefix
,
expire
,
&
ctx
->
erock
,
0
);
syslog
(
LOG_NOTICE
,
"Expired %lu and expunged %lu out of %lu "
"messages from %lu mailboxes"
,
ctx
->
erock
.
messages_expired
,
ctx
->
erock
.
messages_expunged
,
ctx
->
erock
.
messages_seen
,
ctx
->
erock
.
mailboxes_seen
);
verbosep
(
"
\n
Expired %lu and expunged %lu out of %lu "
"messages from %lu mailboxes
\n
"
,
ctx
->
erock
.
messages_expired
,
ctx
->
erock
.
messages_expunged
,
ctx
->
erock
.
messages_seen
,
ctx
->
erock
.
mailboxes_seen
);
if
(
ctx
->
erock
.
do_userflags
)
{
syslog
(
LOG_NOTICE
,
"Expunged %lu user flags"
,
ctx
->
erock
.
userflags_expunged
);
verbosep
(
"Expunged %lu user flags
\n
"
,
ctx
->
erock
.
userflags_expunged
);
}
}
return
0
;
}
static
int
do_cid_expire
(
struct
cyr_expire_ctx
*
ctx
)
{
if
(
ctx
->
args
.
do_cid_expire
)
{
int
cid_expire_seconds
;
cid_expire_seconds
=
config_getint
(
IMAPOPT_CONVERSATIONS_EXPIRE_DAYS
)
*
SECS_IN_A_DAY
;
ctx
->
crock
.
expire_mark
=
time
(
0
)
-
cid_expire_seconds
;
verbosep
(
"Removing conversation entries older than %0.2f days
\n
"
,
(
double
)(
cid_expire_seconds
/
SECS_IN_A_DAY
));
if
(
ctx
->
args
.
userid
)
mboxlist_usermboxtree
(
ctx
->
args
.
userid
,
expire_conversations
,
&
ctx
->
crock
,
MBOXTREE_DELETED
);
else
mboxlist_allmbox
(
ctx
->
args
.
mbox_prefix
,
expire_conversations
,
&
ctx
->
crock
,
0
);
syslog
(
LOG_NOTICE
,
"Expired %lu entries of %lu entries seen "
"in %lu conversation databases"
,
ctx
->
crock
.
msgids_expired
,
ctx
->
crock
.
msgids_seen
,
ctx
->
crock
.
databases_seen
);
verbosep
(
"Expired %lu entries of %lu entries seen "
"in %lu conversation databases
\n
"
,
ctx
->
crock
.
msgids_expired
,
ctx
->
crock
.
msgids_seen
,
ctx
->
crock
.
databases_seen
);
}
return
0
;
}
static
int
do_delete
(
struct
cyr_expire_ctx
*
ctx
)
{
int
ret
=
0
;
if
((
ctx
->
args
.
delete_seconds
>=
0
)
&&
mboxlist_delayed_delete_isenabled
()
&&
config_getstring
(
IMAPOPT_DELETEDPREFIX
))
{
int
count
=
0
;
int
i
;
verbosep
(
"Removing deleted mailboxes older than %0.2f days
\n
"
,
((
double
)
ctx
->
args
.
delete_seconds
/
SECS_IN_A_DAY
));
ctx
->
drock
.
delete_mark
=
time
(
0
)
-
ctx
->
args
.
delete_seconds
;
if
(
ctx
->
args
.
userid
)
mboxlist_usermboxtree
(
ctx
->
args
.
userid
,
delete
,
&
ctx
->
drock
,
MBOXTREE_DELETED
);
else
mboxlist_allmbox
(
ctx
->
args
.
mbox_prefix
,
delete
,
&
ctx
->
drock
,
0
);
for
(
i
=
0
;
i
<
ctx
->
drock
.
to_delete
.
count
;
i
++
)
{
char
*
name
=
ctx
->
drock
.
to_delete
.
data
[
i
];
if
(
sigquit
)
return
ret
;
/* return from here, will quit in main. */
verbosep
(
"Removing: %s
\n
"
,
name
);
ret
=
mboxlist_deletemailbox
(
name
,
1
,
NULL
,
NULL
,
NULL
,
0
,
0
,
0
);
/* XXX: Ignoring the return from mboxlist_deletemailbox() ??? */
count
++
;
}
verbosep
(
"Removed %d deleted mailboxes
\n
"
,
count
);
syslog
(
LOG_NOTICE
,
"Removed %d deleted mailboxes"
,
count
);
}
return
ret
;
}
static
int
do_duplicate_prune
(
struct
cyr_expire_ctx
*
ctx
)
{
int
ret
=
0
;
if
(
ctx
->
args
.
expire_seconds
>
0
)
ret
=
duplicate_prune
(
ctx
->
args
.
expire_seconds
,
&
ctx
->
erock
.
table
);
return
ret
;
}
static
int
parse_args
(
int
argc
,
char
*
argv
[],
struct
arguments
*
args
)
{
extern
char
*
optarg
;
int
opt
;
args
->
archive_seconds
=
-1
;
args
->
delete_seconds
=
-1
;
args
->
expire_seconds
=
-1
;
args
->
expunge_seconds
=
-1
;
args
->
do_expunge
=
true
;
args
->
do_cid_expire
=
-1
;
while
((
opt
=
getopt
(
argc
,
argv
,
"C:D:E:X:A:p:u:vaxtch"
))
!=
EOF
)
{
switch
(
opt
)
{
case
'A'
:
if
(
!
parse_duration
(
optarg
,
&
args
->
archive_seconds
))
usage
();
break
;
case
'C'
:
args
->
altconfig
=
optarg
;
break
;
case
'D'
:
if
(
!
parse_duration
(
optarg
,
&
args
->
delete_seconds
))
usage
();
break
;
case
'E'
:
if
(
!
parse_duration
(
optarg
,
&
args
->
expire_seconds
))
usage
();
break
;
case
'X'
:
if
(
!
parse_duration
(
optarg
,
&
args
->
expunge_seconds
))
usage
();
break
;
case
'a'
:
args
->
skip_annotate
=
true
;
break
;
case
'c'
:
args
->
do_cid_expire
=
0
;
break
;
case
'p'
:
args
->
mbox_prefix
=
optarg
;
break
;
case
't'
:
args
->
do_userflags
=
true
;
break
;
case
'u'
:
args
->
userid
=
optarg
;
break
;
case
'v'
:
verbose
++
;
break
;
case
'x'
:
args
->
do_expunge
=
false
;
break
;
case
'h'
:
default
:
usage
();
break
;
}
}
if
(
args
->
archive_seconds
==
-1
&&
args
->
delete_seconds
==
-1
&&
args
->
expire_seconds
==
-1
&&
args
->
expunge_seconds
==
-1
&&
!
args
->
do_userflags
)
{
/* TODO: Print a more useful error message here. */
fprintf
(
stderr
,
"Missing arguments.
\n
"
);
usage
();
return
-
EINVAL
;
}
return
0
;
}
int
main
(
int
argc
,
char
*
argv
[])
{
int
r
=
0
;
struct
cyr_expire_ctx
ctx
=
zero_ctx
;
progname
=
basename
(
argv
[
0
]);
if
(
parse_args
(
argc
,
argv
,
&
ctx
.
args
)
!=
0
)
exit
(
EXIT_FAILURE
);
cyr_expire_init
(
progname
,
&
ctx
);
/* do_cid_expire defaults to whatever IMAP options are set */
if
(
ctx
.
args
.
do_cid_expire
<
0
)
ctx
.
args
.
do_cid_expire
=
config_getswitch
(
IMAPOPT_CONVERSATIONS
);
/* Set namespace -- force standard (internal) */
if
((
r
=
mboxname_init_namespace
(
&
expire_namespace
,
1
))
!=
0
)
{
syslog
(
LOG_ERR
,
"%s"
,
error_message
(
r
));
fatal
(
error_message
(
r
),
EC_CONFIG
);
}
mboxevent_setnamespace
(
&
expire_namespace
);
if
(
duplicate_init
(
NULL
)
!=
0
)
{
fprintf
(
stderr
,
"cyr_expire: unable to init duplicate delivery database
\n
"
);
exit
(
1
);
}
r
=
do_archive
(
&
ctx
);
if
(
sigquit
)
goto
finish
;
r
=
do_expunge
(
&
ctx
);
if
(
sigquit
)
goto
finish
;
r
=
do_cid_expire
(
&
ctx
);
if
(
sigquit
)
goto
finish
;
r
=
do_delete
(
&
ctx
);
if
(
sigquit
)
goto
finish
;
/* purge deliver.db entries of expired messages */
r
=
do_duplicate_prune
(
&
ctx
);
finish
:
cyr_expire_cleanup
(
&
ctx
);
exit
(
r
);
}
File Metadata
Details
Attached
Mime Type
text/x-c
Expires
Sat, Apr 4, 12:46 AM (3 w, 5 d ago)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
18754389
Default Alt Text
cyr_expire.c (26 KB)
Attached To
Mode
R111 cyrus-imapd
Attached
Detach File
Event Timeline