RewriteMap [ map-name map-type:map-soure]
Default
None
Context
server config, <Global>, <VirtualHost>
Module
mod_rewrite
Compatibility
1.2.6rc1 and later
The RewriteMap directive defines a rewriting map which can be used inside rule substitution strings by the mapping-functions to insert/substitute fields through a key lookup. The source of this lookup can be of various types.
The map-name is the name of the map and will be used to specify a mapping-function for the substitution strings of a rewriting rule via one of the following constructs:
${ map-name : lookup-key }
${ map-name : lookup-key | default-value
When such a construct occurs the map map-name is consulted and the key lookup-key is resolved. If the key is found, the map-function construct is substituted by subst-value. If the key is not found then it is substituted by default-value or by the empty string if no default-value was specified.
The following combinations for map-type and map-src can be used:
Standard Plain Text
map-type: txt, map-src: Unix filesystem path to valid regular file.
This is the standard rewriting map feature where the map-src is a plain ASCII file containing either blank lines, comment lines (starting with a '#' character) or pairs like the following - one per line.
matching-key subst-value
Example 1-1. Example Usermap
# --------------------------------------------
# usermap.txt -- map for rewriting user names
# --------------------------------------------
Dave.Admin dave # The Uber-admin
root anonymous # no one should be logging in as root anyway
And, to configure this map to be used:
RewriteMap real-to-user txt:/path/to/file/usermap.txt
FIFO/Named Pipe
map-type: fifo, map-src: Unix filesystem path to valid FIFO.
For this rewriting map, map-src is a FIFO (a.k.a. named pipe). To create it, you can use the mkfifo(1) command. An external program that opens the FIFO for reading and writing must be started before proftpd is started. This program can communicate with the rewriting engine via the FIFO. For each mapping lookup, it can read the key to lookup as a newline-terminated string from the FIFO. It then has to write back to the FIFO the looked-up value as a newline-terminated string, or just simply newline character (denoting an empty string) if there is no corresponding value for the given key).
An example program which will implement a 1:1 mapping (i.e., key == value) could be:
Example 1-2. Example FIFO/Named Pipe 1:1 mapping
#!/usr/bin/perl
use strict;
use File::Basename qw(basename);
use Getopt::Long;
use IO::Handle;
use IO::Select;
my $default_delay = 0.5;
my $program = basename($0);
my %opts = ();
GetOptions(\%opts, 'delay=f', 'fifo=s', 'help', 'verbose');
usage() if $opts{'help'};
my $delay = $opts{'delay'} ? $opts{'delay'} : $default_delay;
die "$program: missing required --fifo parameter\n" unless $opts{'fifo'};
my $fifo = $opts{'fifo'};
my $verbose = $opts{'verbose'} ? 1 : 0;
open(my $fifo_fh, "+> $fifo") or die "$program: unable to open $fifo: $!\n";
# Instantiate a Select object for knowing when to read from and write to
# the FIFO.
my $sel = IO::Select->new();
while (1) {
# Blocking select() for reading.
$sel->add($fifo_fh);
print STDERR "$program: selecting for reading\n" if $verbose;
my ($rfh) = $sel->can_read();
my $key = <$rfh>;
print STDERR "$program: read '$key'\n" if $verbose;
# Lookup a value for the given key.
my $value = lookup_value($key);
# Clear the Select object's filehandles.
$sel->remove();
print $fifo_fh "$value\n" if $verbose;
$fifo_fh->flush();
print STDERR "$program: wrote '$value'\n" if $verbose;
# Wait for the buffer's byte to be cleared before reading again.
wait_fifo($fifo_fh);
}
close($fifo_fh);
print STDOUT "$program: done\n" if $verbose;
exit 0;
# --------------------------------------------------------------------------
sub lookup_value {
my ($key) = @_;
# NOTE: do something to obtain a value for the given key here.
chomp(my $value = $key);
return $value;
}
# --------------------------------------------------------------------------
sub usage {
print STDOUT <<END_OF_USAGE;
usage: $program [options]
--delay Configure the buffer check delay.
The default is $default_delay seconds.
--fifo Configure the path to the FIFO. Required.
--help Displays this message.
--verbose Enables verbose output while $program runs.
END_OF_USAGE
exit 0;
}
# --------------------------------------------------------------------------
sub wait_fifo {
my ($fh) = @_;
# Now we get tricky. Use ioctl(2) to poll the number of bytes to
# be read from the FIFO filehandle. When the number drops to zero,
# it means that the data we just wrote has been read from the buffer
# by some other process, so we can go back to the top of this loop.
# Otherwise, if this program loops faster than the reader/writer on
# the other end of the FIFO, we'd end up reading the data we just
# wrote. Quite annoying, actually.
#
# Note: this value must be manually extracted from the system header files
# using the following program:
#
# -------- fionread.c -------------------
# #include <sys/ioctl.h>
#
# int main(int argc, char *argv[]) {
# printf("%#08x\n", FIONREAD);
# return 0;
# }
# ---------------------------------------
#
# > cc -o fionread fionread.c
# > ./fionread
my $FIONREAD = 0x00541b;
my $size = pack('L', 0);
ioctl($fh, $FIONREAD, $size) or die "$program: unable to use ioctl: $!\n";
$size = unpack('L', $size);
while ($size != 0) {
print STDERR "$program: waiting for buffer to be read\n" if $verbose;
select(undef, undef, undef, $delay);
$size = pack('L', 0);
ioctl($fh, $FIONREAD, $size) or die "$program: unable to use ioctl: $!\n";
$size = unpack('L', $size);
}
}
To make use of this example script, simply implement your lookup code in the lookup_value() subroutine. Be very careful with such scripts, though:
"Keep it simple, stupid" (KISS), because if this program hangs it will hang proftpd when the rule occurs. Well, keep it as simple as possible...
Avoid one common mistake: avoid buffered I/O if possible. This can cause a deadloop. If necessary, be sure to flush the filehandle before reading, and after writing.
Use the RewriteLock directive to define a lockfile mod_rewrite can use to synchronize the communication to the FIFO program. By default no such synchronization takes place.
Internal Function
map-type: int, map-src: Internal mod_rewrite function.
Here the map-src is a mod_rewrite built-in function. Currently you cannot create your own, but the following functions already exist:
toupper
Converts the looked up key to all upper case.
tolower
Converts the looked up key to all lower case.
unescape
Translates hex-encodings in the looked up key back to special characters.
utf8trans
Translates UTF-8 encodings in the lookup up key into Latin-1 characters.
The RewriteMap directive can occur more than once. For each mapping-function use one RewriteMap directive to declare its rewriting map name.
Note: For plain text files the looked-up keys are cached in-core until the mtime of the text map file changes or the server does a restart. This way you can have map-functions in rules which are used for every request. This is no problem, because the parsing of the text files only happens once!