summaryrefslogtreecommitdiffstats
path: root/doc/wiki/Plugins.Expire.txt
diff options
context:
space:
mode:
Diffstat (limited to 'doc/wiki/Plugins.Expire.txt')
-rw-r--r--doc/wiki/Plugins.Expire.txt354
1 files changed, 354 insertions, 0 deletions
diff --git a/doc/wiki/Plugins.Expire.txt b/doc/wiki/Plugins.Expire.txt
new file mode 100644
index 0000000..12b9f08
--- /dev/null
+++ b/doc/wiki/Plugins.Expire.txt
@@ -0,0 +1,354 @@
+Expire plugin
+=============
+
+(See also <autoexpunge> [MailboxSettings.txt] setting, which partially
+obsoletes this plugin.)
+
+Typically this plugin is used to optimize expunging old mails from users'
+mailboxes. For example:
+
+---%<-------------------------------------------------------------------------
+doveadm expunge -A mailbox Trash savedbefore 30d
+---%<-------------------------------------------------------------------------
+
+Note that:
+
+ * *This command runs fine even without the expire plugin.* Then it just goes
+ through all users rather than those users who have something to actually
+ expunge.
+ * The plugin actually works for all doveadm mail commands, not just expunge.
+ So for testing you could use e.g.'doveadm search -A ...' to see which
+ messages it matches.
+ * The optimization is done only when -A parameter is used to go through all
+ users. If you use -u parameter to separately go through users, the plugin
+ does nothing.
+ * The optimization is done only when "savedbefore" is used in the search
+ query. It's possible to have further restrictions also, but the user
+ filtering is based only on the "savedbefore" timestamp.
+ * The optimization is done only when the given mailbox or mailboxes match the
+ list of expire mailboxes specified in 'plugin { expire* } ' settings.
+
+So the expire plugin's job is to reduce disk I/O when figuring out which users
+have work to do. Unless you have thousands of users, you probably shouldn't
+bother with this plugin.
+
+Details
+-------
+
+When expire plugin is enabled, it keeps track of the "oldest mail's saved
+timestamp" for the specified mailboxes. When doveadm command uses "savedbefore"
+search key, the timestamps in the database can be used to figure out which
+users have matching mails.
+
+Expire plugin tracks/optimizes only "savedbefore" search query, i.e. the date
+when message was *saved or copied to the mailbox* (*NOT the time message was
+originally received*) while expire plugin was loaded. If mailbox contained
+existing messages before the plugin was loaded for the first time, they'll get
+expunged eventually when the first message saved/copied after expire plugin was
+enabled gets expunged.
+
+The save/copy date may not be exact if it's not cached in
+'dovecot.index.cache':
+
+ * mbox: The current lookup time is used and added to cache.
+ * maildir: File's ctime is used.
+ * dbox: Save/copy time is taken from the dbox file if it exists (it normally
+ should), fallbacking to file's ctime if not.
+
+You need to configure a list of mailboxes that are tracked. Mailbox patterns
+can contain IMAP LIST command-compatible wildcards:
+
+ * "*" works in a standard way: It matches any number of characters.
+ * "%" works by matching any number of characters, but it stops at the
+ hierarchy separator. Currently the separator is hardcoded to "/", so it
+ doesn't work correctly if you've configured separator to something else
+ (e.g. "." is the default for Maildir).
+
+Example configuration
+---------------------
+
+Make sure you enable the plugin globally, not just for specific protocols.
+
+---%<-------------------------------------------------------------------------
+mail_plugins = $mail_plugins expire
+
+plugin {
+ expire = Trash
+ expire2 = Trash/*
+ expire3 = Spam
+
+ # Enable caching of dict value in dovecot.index file. This significantly
+reduces
+ # the number of dict lookups. It makes initial testing more confusing though,
+so
+ # it's better to enable it only after you've verified that the expire plugin
+is
+ # working as wanted. (v2.2.16+)
+ expire_cache = yes
+}
+---%<-------------------------------------------------------------------------
+
+ * See <Dict.txt> for more information about how to use dict service,
+ especially about permission issues.
+ * You need to get 'doveadm -A' working using your userdb. See iterate settings
+ for <SQL> [AuthDatabase.SQL.txt] and <LDAP> [AuthDatabase.LDAP.txt].
+
+MySQL Backend
+-------------
+
+dovecot.conf:
+
+---%<-------------------------------------------------------------------------
+plugin {
+ expire_dict = proxy::expire
+}
+dict {
+ expire = mysql:/etc/dovecot/dovecot-dict-expire.conf.ext
+}
+---%<-------------------------------------------------------------------------
+
+Create the table like this:
+
+---%<-------------------------------------------------------------------------
+CREATE TABLE expires (
+ username varchar(75) not null,
+ mailbox varchar(255) not null,
+ expire_stamp integer not null,
+ primary key (username, mailbox)
+);
+---%<-------------------------------------------------------------------------
+
+dovecot-dict-expire.conf.ext:
+
+---%<-------------------------------------------------------------------------
+connect = host=localhost dbname=mails user=sqluser password=sqlpass
+
+map {
+ pattern = shared/expire/$user/$mailbox
+ table = expires
+ value_field = expire_stamp
+
+ fields {
+ username = $user
+ mailbox = $mailbox
+ }
+}
+---%<-------------------------------------------------------------------------
+
+PostgreSQL Backend
+------------------
+
+Like MySQL configuration above, but you'll also need to create a trigger:
+
+---%<-------------------------------------------------------------------------
+CREATE OR REPLACE FUNCTION merge_expires() RETURNS TRIGGER AS $$
+BEGIN
+ UPDATE expires SET expire_stamp = NEW.expire_stamp
+ WHERE username = NEW.username AND mailbox = NEW.mailbox;
+ IF FOUND THEN
+ RETURN NULL;
+ ELSE
+ RETURN NEW;
+ END IF;
+END;
+$$ LANGUAGE plpgsql;
+
+CREATE TRIGGER mergeexpires BEFORE INSERT ON expires
+ FOR EACH ROW EXECUTE PROCEDURE merge_expires();
+---%<-------------------------------------------------------------------------
+
+SQLite Backend
+--------------
+
+Like MySQL configuration above, but you'll also need to create a trigger:
+
+---%<-------------------------------------------------------------------------
+CREATE TRIGGER mergeexpires BEFORE INSERT ON expires FOR EACH ROW
+BEGIN
+ UPDATE expires SET expire_stamp=NEW.expire_stamp
+ WHERE username = NEW.username AND mailbox = NEW.mailbox;
+ SELECT raise(ignore)
+ WHERE (SELECT 1 FROM expires WHERE username = NEW.username AND
+mailbox = NEW.mailbox) IS NOT NULL;
+END;
+---%<-------------------------------------------------------------------------
+
+Vpopmail (+MySQL)
+-----------------
+
+Configure expire plugin like MySQL configuration. From here, there are two
+cases:
+
+If you are using the mysql database for authentication directly, you only need
+to make iterate work for <MySQL> [AuthDatabase.SQL.txt].
+
+When using Vpopmail backend, doveadm -A won't work. To be able to use the
+expire database, you can use a bash script like this:
+
+---%<-------------------------------------------------------------------------
+#!/bin/bash
+
+# SQL connection data
+HOST="localhost"
+USER="dovecot"
+PWD="yourpassword"
+DBNAME="vpopmail"
+TBLNAME="expires"
+# expunge messages older than this (days)
+TTL=30
+
+expiredsql=`cat <<EOF
+ USE $DBNAME;
+ SELECT CONCAT(username, "~", mailbox)
+ FROM $TBLNAME
+ WHERE expire_stamp>0 AND expire_stamp<UNIX_TIMESTAMP()-86400*$TTL;
+EOF`
+
+for row in `echo "$expiredsql" | mysql -h $HOST -u $USER -p$PWD -N`; do
+ username=`echo $row | cut -d"~" -f1`
+ mailbox=`echo $row | cut -d"~" -f2-`
+
+ # Check if mailbox name is empty. Just in case.
+ if [ -n $mailbox ]; then
+ echo "Expunging: "$row
+ doveadm expunge -u $username mailbox "$mailbox" savedbefore $TTL"d"
+ fi
+done
+---%<-------------------------------------------------------------------------
+
+Example #1 timeline
+-------------------
+
+FIXME: expire-tool no longer exists, update these examples. Let's say Trash is
+configured to expire in 7 days and today is 2009-07-10. Initially the database
+and the Trash mailbox is empty.
+
+User moves the first message to Trash. The expires table is updated:
+
+---%<-------------------------------------------------------------------------
+mysql> select mailbox, from_unixtime(expire_stamp), username from expires;
++---------+-----------------------------+----------+
+| mailbox | from_unixtime(expire_stamp) | username |
++---------+-----------------------------+----------+
+| Trash | 2009-07-17 15:57:36 | tss |
++---------+-----------------------------+----------+
+---%<-------------------------------------------------------------------------
+
+The expire_stamp contains the date when expire-tool will look into that mailbox
+and try to find messages to expunge. Until then it skips the mailbox.
+
+A day later user moves another message to Trash. The expire_stamp isn't
+updated, because the second message's save date is newer than the first one's.
+Checking Trash's contents via IMAP you can see something like:
+
+---%<-------------------------------------------------------------------------
+1 fetch 1:* (internaldate x-savedate)
+* 1 FETCH (INTERNALDATE "16-Dec-2008 09:52:38 -0500" X-SAVEDATE "10-Jul-2009
+15:57:36 -0400")
+* 2 FETCH (INTERNALDATE "29-Jun-2003 23:20:09 -0400" X-SAVEDATE "11-Jul-2009
+16:03:11 -0400")
+1 OK Fetch completed.
+---%<-------------------------------------------------------------------------
+
+Note how the message's INTERNALDATE (received date) can be very old compared to
+the save date. Now, running expire-tool --test:
+
+---%<-------------------------------------------------------------------------
+Info: tss/Trash: stop, expire time in future: Fri Jul 17 15:57:36 2009
+---%<-------------------------------------------------------------------------
+
+So it does nothing, because the expire time is in future. Fast forward 6 more
+days into future. Running expire-tool --test:
+
+---%<-------------------------------------------------------------------------
+Info: tss/Trash: seq=1 uid=1: Expunge
+Info: tss/Trash: timestamp 1247860656 (Fri Jul 17 15:57:36 2009) -> 1247947391
+(Sat Jul 18 16:03:11 2009)
+---%<-------------------------------------------------------------------------
+
+The first message would be expunged and the second message's timestamp would
+become the new expire_stamp in database. After running expire-tool without
+--test, the database is updated:
+
+---%<-------------------------------------------------------------------------
+mysql> select mailbox, from_unixtime(expire_stamp), username from expires;
++---------+-----------------------------+----------+
+| mailbox | from_unixtime(expire_stamp) | username |
++---------+-----------------------------+----------+
+| Trash | 2009-07-18 16:03:11 | tss |
++---------+-----------------------------+----------+
+---%<-------------------------------------------------------------------------
+
+Also you can see the first message has been expunged from Trash:
+
+---%<-------------------------------------------------------------------------
+2 fetch 1:* (internaldate x-savedate)
+* 1 FETCH (INTERNALDATE "29-Jun-2003 23:20:09 -0400" X-SAVEDATE "11-Jul-2009
+16:03:11 -0400")
+2 OK Fetch completed.
+---%<-------------------------------------------------------------------------
+
+Example #2 timeline
+-------------------
+
+Again you have Trash configured for 7 days, but this time you have an existing
+message there before expire plugin has been enabled. Initially the expire
+database is empty. Today is 2009-07-20.
+
+---%<-------------------------------------------------------------------------
+1 fetch 1:* (internaldate x-savedate)
+* 1 FETCH (INTERNALDATE "29-Jun-2003 23:20:09 -0400" X-SAVEDATE "11-Jul-2009
+16:03:11 -0400")
+1 OK Fetch completed.
+---%<-------------------------------------------------------------------------
+
+If you run expire-tool, you'll notice that it does nothing for the mailbox.
+There's nothing in expire database, so expire-tool doesn't even mention it when
+running with --test.
+
+After user moves the first message to Trash, the database gets updated:
+
+---%<-------------------------------------------------------------------------
+mysql> select mailbox, from_unixtime(expire_stamp), username from expires;
++---------+-----------------------------+----------+
+| mailbox | from_unixtime(expire_stamp) | username |
++---------+-----------------------------+----------+
+| Trash | 2009-07-27 16:32:11 | tss |
++---------+-----------------------------+----------+
+---%<-------------------------------------------------------------------------
+
+The messages in Trash are:
+
+---%<-------------------------------------------------------------------------
+2 fetch 1:* (internaldate x-savedate)
+* 1 FETCH (INTERNALDATE "29-Jun-2003 23:20:09 -0400" X-SAVEDATE "11-Jul-2009
+16:03:11 -0400")
+* 2 FETCH (INTERNALDATE "16-Dec-2002 11:02:39 -0500" X-SAVEDATE "20-Jul-2009
+16:32:11 -0400")
+2 OK Fetch completed.
+---%<-------------------------------------------------------------------------
+
+So the first message should be expiring already, right? No. It doesn't because
+the timestamp in database is still in future. expire-tool --test says:
+
+---%<-------------------------------------------------------------------------
+Info: tss/Trash: stop, expire time in future: Mon Jul 27 16:32:11 2009
+---%<-------------------------------------------------------------------------
+
+OK, let's see what happens when we finally reach July 27th:
+
+---%<-------------------------------------------------------------------------
+Info: tss/Trash: seq=1 uid=3: Expunge
+Info: tss/Trash: seq=2 uid=4: Expunge
+Info: tss/Trash: no messages left
+---%<-------------------------------------------------------------------------
+
+They both got expunged! The expire database's timestamp simply tells
+expire-tool when to start looking into messages in that mailbox. After that
+expire-tool looks at the actual save dates and figures out which messages
+exactly need to be expunged.
+
+After running expire-tool without --test you'll see that the Trash mailbox is
+empty and the database row is deleted.
+
+(This file was created from the wiki on 2019-06-19 12:42)