in lib/Mail/SpamAssassin/Plugin/DecodeShortURLs.pm [505:656]
sub initialise_url_shortener_cache {
my ($self, $conf) = @_;
return if $self->{dbh};
return if !$conf->{url_shortener_cache_type};
if (!$conf->{url_shortener_cache_dsn}) {
warn "DecodeShortURLs: invalid cache configuration\n";
return;
}
##
## SQLite
##
if ($conf->{url_shortener_cache_type} =~ /^(?:dbi|sqlite)$/i
&& $conf->{url_shortener_cache_dsn} =~ /^dbi:SQLite/)
{
eval {
local $SIG{'__DIE__'};
require DBI;
require DBD::SQLite;
DBD::SQLite->VERSION(1.59_01); # Required for ON CONFLICT
$self->{dbh} = DBI->connect_cached(
$conf->{url_shortener_cache_dsn}, '', '',
{RaiseError => 1, PrintError => 0, InactiveDestroy => 1, AutoCommit => 1}
);
$self->{dbh}->do("
CREATE TABLE IF NOT EXISTS short_url_cache (
short_url TEXT PRIMARY KEY NOT NULL,
decoded_url TEXT NOT NULL,
hits INTEGER NOT NULL DEFAULT 1,
created INTEGER NOT NULL,
modified INTEGER NOT NULL
)
");
# Maintaining index for cleaning is likely more expensive than occasional full table scan
#$self->{dbh}->do("
# CREATE INDEX IF NOT EXISTS short_url_modified
# ON short_url_cache(created)
#");
$self->{sth_insert} = $self->{dbh}->prepare("
INSERT INTO short_url_cache (short_url, decoded_url, created, modified)
VALUES (?,?,strftime('%s','now'),strftime('%s','now'))
ON CONFLICT(short_url) DO UPDATE
SET decoded_url = excluded.decoded_url,
modified = excluded.modified,
hits = hits + 1
");
$self->{sth_select} = $self->{dbh}->prepare("
SELECT decoded_url FROM short_url_cache
WHERE short_url = ?
");
$self->{sth_delete} = $self->{dbh}->prepare("
DELETE FROM short_url_cache
WHERE short_url = ? AND created < strftime('%s','now') - $conf->{url_shortener_cache_ttl}
");
$self->{sth_clean} = $self->{dbh}->prepare("
DELETE FROM short_url_cache
WHERE created < strftime('%s','now') - $conf->{url_shortener_cache_ttl}
");
};
}
##
## MySQL/MariaDB
##
elsif (lc $conf->{url_shortener_cache_type} eq 'dbi'
&& $conf->{url_shortener_cache_dsn} =~ /^dbi:(?:mysql|MariaDB)/i)
{
eval {
local $SIG{'__DIE__'};
require DBI;
$self->{dbh} = DBI->connect_cached(
$conf->{url_shortener_cache_dsn},
$conf->{url_shortener_cache_username},
$conf->{url_shortener_cache_password},
{RaiseError => 1, PrintError => 0, InactiveDestroy => 1, AutoCommit => 1}
);
$self->{sth_insert} = $self->{dbh}->prepare("
INSERT INTO short_url_cache (short_url, decoded_url, created, modified)
VALUES (?,?,UNIX_TIMESTAMP(),UNIX_TIMESTAMP())
ON DUPLICATE KEY UPDATE
decoded_url = VALUES(decoded_url),
modified = VALUES(modified),
hits = hits + 1
");
$self->{sth_select} = $self->{dbh}->prepare("
SELECT decoded_url FROM short_url_cache
WHERE short_url = ?
");
$self->{sth_delete} = $self->{dbh}->prepare("
DELETE FROM short_url_cache
WHERE short_url = ? AND created < UNIX_TIMESTAMP() - $conf->{url_shortener_cache_ttl}
");
$self->{sth_clean} = $self->{dbh}->prepare("
DELETE FROM short_url_cache
WHERE created < UNIX_TIMESTAMP() - $conf->{url_shortener_cache_ttl}
");
};
}
##
## PostgreSQL
##
elsif (lc $conf->{url_shortener_cache_type} eq 'dbi'
&& $conf->{url_shortener_cache_dsn} =~ /^dbi:Pg/i)
{
eval {
local $SIG{'__DIE__'};
require DBI;
$self->{dbh} = DBI->connect_cached(
$conf->{url_shortener_cache_dsn},
$conf->{url_shortener_cache_username},
$conf->{url_shortener_cache_password},
{RaiseError => 1, PrintError => 0, InactiveDestroy => 1, AutoCommit => 1}
);
$self->{sth_insert} = $self->{dbh}->prepare("
INSERT INTO short_url_cache (short_url, decoded_url, created, modified)
VALUES (?,?,CAST(EXTRACT(epoch FROM NOW()) AS INT),CAST(EXTRACT(epoch FROM NOW()) AS INT))
ON CONFLICT (short_url) DO UPDATE SET
decoded_url = EXCLUDED.decoded_url,
modified = EXCLUDED.modified,
hits = short_url_cache.hits + 1
");
$self->{sth_select} = $self->{dbh}->prepare("
SELECT decoded_url FROM short_url_cache
WHERE short_url = ?
");
$self->{sth_delete} = $self->{dbh}->prepare("
DELETE FROM short_url_cache
WHERE short_url = ? AND created < CAST(EXTRACT(epoch FROM NOW()) AS INT) - $conf->{url_shortener_cache_ttl}
");
$self->{sth_clean} = $self->{dbh}->prepare("
DELETE FROM short_url_cache
WHERE created < CAST(EXTRACT(epoch FROM NOW()) AS INT) - $conf->{url_shortener_cache_ttl}
");
};
##
## ...
##
} else {
warn "DecodeShortURLs: invalid cache configuration\n";
return;
}
if ($@ || !$self->{sth_clean}) {
warn "DecodeShortURLs: cache connect failed: $@\n";
undef $self->{dbh};
undef $self->{sth_insert};
undef $self->{sth_select};
undef $self->{sth_delete};
undef $self->{sth_clean};
}
}