On 2015-02-25 at 00:57 -0500, Jim Trigg wrote:
> Munged example to elide actual domain names from my existing virtual.pcre:
> /^(abuse|policyd|root)@(anydomain).tld$/ blaise+${2}-${1}@localhost
> /^(host|list|post|web)master@(anydomain).tld$/ blaise+${2}-${1}@localhost
> /^jtrigg([-+].+)@(anydomain).tld$/ blaise+${2}${1}@localhost
> /^jtrigg@(anydomain).tld$/ blaise+${1}@localhost
I don't have a good solution, because Exim is missing one small feature,
but I think that Jan's Perl suggestion can be made sufficiently
efficient.
Drop the `/` delimiters; if you use a nwildlsearch lookup and the entry
starts with a circumflex, then it's interpreted as a regular expression.
See "9.3 Single-key lookup types" in the Specification. Note
"nwildlsearch", as you don't want string expansion (so don't need \N).
Directly, you can't use the back-references, so this is what you can
use:
virtualpcre:
driver = redirect
condition = ${lookup {$local_part@$domain}nwildlsearch{/path/to/file}{yes}{no}}
data = ${perl ...}
The problem is that while $value is available, $lookup_matched is not.
So there's no way to get back the pattern used. At first glance, this
_seems_ like a reasonable construct:
data = ${sg {$local_part@$domain}\
{${lookup {$local_part@$domain}nwildlsearch{/path/to/file}{$lookup_matched}}}\
{${lookup {$local_part@$domain}nwildlsearch{/path/to/file}}}}
The caching keeps the lookup efficient; the substitution of the
replacement text means that the ${2} coming in from the lookup doesn't
need any escaping, and everything would just work. But I can't see a
way to reference the pattern which matched the lookup key, and the
problem is the caching: we store the key and the value, but not the
match and I'm disinclined to pursue changing the list-match caching code
to be caching more data for a very rare case.
So I think that you should combine nwildsearch, to do the core lookup
fast from Exim's C code as the guard in the condition, with a short Perl
function to be used to do the transform once you already know this is
needed. So, with warnings that I haven't written Perl in years so this
is guaranteed to make any Perl programmer familiar with current good
style scream:
perl_startup = do '/etc/exim/perl_functions.pl'
virtualpcre:
driver = redirect
condition = ${lookup {$local_part@$domain}nwildlsearch{/path/to/file}{yes}{no}}
data = ${perl{virtualpcre}{$local_part@$domain}{/path/to/file}}
----------------------------8< cut here >8------------------------------
sub virtualpcre {
my ($lookup, $file) = @_;
open(my $fh, '<', $file) or do { warn "unable to read-open($file): $!\n"; return undef };
my $result = undef;
while (<$fh>) {
next if /^#/;
next unless /./;
chomp;
my ($matcher, $subpat) = split /\s+/, $_, 2;
my $p = qr/$matcher/;
next unless $lookup =~ $p;
($subpat = quotemeta $subpat) =~ s/\\\$/\$/g;
($result = $lookup) =~ s/$p/"\"${subpat}\""/ee;
last;
}
$fh->close();
return $result;
}
----------------------------8< cut here >8------------------------------
Today I learnt something new: Perl has gained 'ee' as a modifier; which
is an eval hidden behind a single letter, which means that there are
security consequences to doing so. Do not lose that quotemata. I
_think_ that the s/\\\$/\$/g is safe, because quotemeta will have
doubled any existing backslashes so I don't think an injection attack
will get around that.
But my Perl is very rusty, so consult with someone who has used the
language seriously in the past decade. My usage is mostly constrained
to keeping existing code working.
-Phil