RabidFire Version 1.0

Click here to download our departmental (simplified) version.

Contents:

Updated November 14, 2002

Introduction

This document describes version 1.0 of the package. Most of the changes introduced since the first official version (0.6) affect the filter part. The killer has changed relatively little since version 0.6, and those changes are practically exclusively extensions rather than serious functional modifications.

RabidFire is a combination of an E-Mail filter and a program for semi-automated generation of SPAM complaints. Both programs are Tcl/Tk scripts. I wrote them (the original version number was 0.5) as an exercise to learn Tcl/Tk before embarking on a more serious scripting project, which turned out to be not so serious after all.

In the sequel, the two scripts will be referred to as RabidFire filter and RabidFire killer, or simply filter and killer. The name "RabidFire" is to "honor" one of the less famous spamming tools used by the competition, which was "popular" (at least in certain circles) at the time when I embarked on this project.

Please note that I am neither an active member of an anti-spam organization, nor a fanatical independent crusader in the war against spam. While my work on reducing the amount of garbage circulating in the network can be viewed as a hobby of sorts, I have other things to do in my life, and some of them I consider more serious than this little aberration. Consequently, although I may continue working on RabidFire in my spare time, I would prefer not to commit myself to any serious maintenance or development work related to this project. Needless to say, anybody willing to modify or extend these programs, and possibly redistribute them, is more than welcome to do so.

RabidFire is intended for UNIX systems. The filter assumes that you can set up a .forward file in your home directory. I believe that the killer can be ported to Windows with little effort because there exists a Tcl/Tk implementation for Windows, and programs like nslookup, traceroute, and whois used by the killer are also available there. However, I am not going to do this port myself.

Installation

  1. Get the package, e.g., by clicking here.

  2. Uncompress and untar the file wherever you want RabidFire to go, e.g.,

    cd ~/Mail
    zcat rf.tar.gz | tar -xvf -
    

    This will create subdirectory RabidFire containing all the relevant files and subdirectories, including two executable scripts filter and killer. Your (quite minimal) work on adjusting various setup parameters will be truly minimized, if the package is unpacked into Mail/RabidFire in your home directory. The directory where the package has been unpacked, whatever it is, will be called the base directory in the sequel. The default (and mildly recommended) location of the base directory is $HOME/Mail/RabidFire/.

  3. Move to directory RabidFire and execute make. This will create a few symbolic links and compile rfproxy (the mail server proxy daemon). You may not want to use the proxy, in which case you may prefer to execute make links instead. This will create the relevant links without compiling the proxy. You need gcc to compile the proxy. If there are linking errors, you may have to add some libraries, e.g., -lnsl, -lsocket, at the end of the first gcc statement in Makefile. I know, I know - I should have used configure for this, but the only compiled component of the package (i.e., the proxy daemon, being a wrapper calling a Tcl script, which is unable to fork by itself) is so trivially small that such a solution would appear totally out of proportion.

  4. Edit the file rfirerc_example to fit your environment. Read the comments in that file - they may clarify a couple of things that are not explicitly covered in this document. If some of them are confusing, don't worry - they will become clear in a short while (or perhaps they are irrelevant). Then copy rfirerc_example to your home directory as .rfirerc, i.e.,

    cp rfirerc_example ~/.rfirerc
    
    If you have decided to install RabidFire in a directory other than ~/Mail/RabidFire, make sure to specify the correct (home-relative) path to that directory in .rfirerc.

  5. In the long run, you may want to edit the file rules in RabidFire, as well as the pattern group files in subdirectory Rules and, possibly, the programs in subdirectory Tests. All these files describe the "rules" for detecting spam (and generally processing your E-Mail), which you may want to tailor to your personal criteria and taste. Of course, to understand what you are doing, you will have to read through the rest of this document (or most of it anyway), so perhaps you should wait until then. The spam filter will be functional if you skip this step for now.

  6. Edit the standard rejection letter (file preamble) and the standard complaint letter (file complaint) to your taste. You don't need the complaint letter, if you are not going to use the killer. You may also want to modify file vacation (containing the "vacation" message), if you are going to use the vacation feature of RabidFire.

  7. Make sure Tcl/Tk is installed on your system, i.e., the machine that delivers E-Mail to your mailbox and interprets the contents of your .forward file. RabidFire was developed under version 8.3 of Tcl/Tk and it seems to run happily under 8.2. If your version is significantly older, please upgrade. There is a bug in version 8.0.x that renders some of the fancier regular expressions from the filter's database useless.

  8. Run two simple tests (on the machine that delivers your mail). Move to directory RabidFire (you should be there already) and execute this:

    ./filter < test1
    ./filter < test2
    

    The first message should show up in your mailbox and the second one should be rejected. The rejected message will appear in directory RabidFire/JunkMail.

  9. Create a .forward file in your home directory. It should consist of a single line with the following contents:

    "|.../RabidFire/filter"
    

    where `...' stands for the full path leading to the directory where you have put the package. For example, my .forward file contains the following line:

    "|/home/pawel/Mail/RabidFire/filter"
    

    Notes

    Some mailers require that .forward be readable to the world, and they may quietly ignore that file if it isn't.

    Some mailers tend to be confused about the location of the home directory of the user for whom they deliver mail. If things don't work for you (or just to be on the safe side), you may indicate your home directory to the filter by specifying it as an argument, i.e.,

    "|/home/pawel/Mail/RabidFire/filter /home/pawel"
    

    If the filter fails to locate .rfirerc, it may still operate correctly, if the following two conditions are met:

    1. When the script is run by the mailer, the environment variables HOME and USER are set to the right values. This happens on many systems.

    2. The script's files are located in the standard directory, i.e., $HOME/Mail/RabidFire.

    In such a case, and if .rfirerc is absent, the filter will assume that its base directory is $HOME/Mail/RabidFire, your mail server is the current host (i.e., the host delivering your E-Mail), your E-Mail address is "your user name"@"current host", your mailbox is /var/spool/mail/"your user name", and your name is "The Spamfighter."

    End of notes

  10. Send an innocuous E-Mail to yourself and see if you are receiving it. Then send another message with something offensive in it (e.g., "adults only" in the subject field). This message should be rejected and it shouldn't show up in your mailbox. This may not happen, i.e., the offensive message may make it through the filter, e.g., if your rules have been set up to exempt messages arriving from your domain.

    Note that formally a rejected message bounces to its sender, but in this case you won't see it because YOU are the sender. This is to avoid bounce loops. Look in the folder dropped in RabidFire/JunkMail. This is where (with the standard set of rules distributed with the package) the filter deposits messages of this kind (i.e., ones that have been ignored for some good reason). Your message should be there preceded by the rejection letter. This is what the sender would see if he/she were somebody other than you (with a legitimate return address).


If all this works, then most likely everything is fine. If you are obsessive, you may make your .forward line look like this:

"|.../RabidFire/filter", "|cat > .../RabidFire/backup"

which will have the effect of additionally saving all incoming messages (including all spam) in file backup. You may want to keep this around for some time until you are absolutely convinced that nothing is getting lost.

No additional effort is required to install the killer. To run it, just execute the killer script in directory RabidFire. You may want to make a link to it from your bin directory, or make it callable by a click from your window manager. It also makes sense to include the base directory of the package in your PATH, as some other programs of a possible interest to you are available from there as well. The default directory with junk mail folders assumed by the killer coincides with the JunkMail directory of the filter. This way, whenever you invoke the killer, you will automatically get to your recent load of spam.

Needless to say, no filter can be guaranteed to be 100% effective. My personal rules, which are not very far from the set of rules distributed with the package, are intentionally very aggressive. I don't mind if they occasionally reject a legitimate E-Mail from somebody trying to contact me for the first time, as long as they eliminate virtually all spam aimed at my mailbox. In a completely automatic way, they exempt all people with whom I exchange E-Mail. With the assistance of the mail proxy and sendmail substitute that come with the package, whenever I send an E-Mail message, its recipients are automatically added to my list of exceptions, so that their replies are guaranteed to make it through.

A polite rejection letter (feel free to edit the default letter to suit your personal taste) will tell a legitimate sender whose message didn't make it through your filter how to get it delivered without problems. By including a magic phrase of your choice in the subject line, the sender will make sure that the message makes it through the filter and, as it happens, his E-Mail address will be automatically added to your list of exceptions. Consequently, unless the sender uses multiple addresses, such an incident can occur only once per sender, and it practically never occurs, if you are the party initiating the exchange of letters.

Note: Subdirectory PG of the package contains my personalized files, which you can view for comparison with your setup.

The filter

The operation of the filter is driven by a Tcl script stored in the rules file in the base directory. Note that the location of the base directory can be changed by setting a Tcl variable (BaseDirectory) in .rfirerc. The script in rules is organized into a sequence of rules describing actions to be taken when the incoming message meets certain conditions. Although it is possible to describe all these conditions and actions in a single file, it makes better sense (as we shall shortly see) to split them into several files that are referenced from the rules script.

In the simplest case, the action of a rule can be an unconditional single operation, e.g., save indicating that the message should be deposited in your default mailbox. On another extreme are possibly complex conditions expressed as Tcl programs performed on selected fragments of the message. Ultimately, those conditions may poll the mail servers appearing in the headers of the received message to check if they are secured against third party relays. They may even send probe messages to such servers and modify the contents of the rules database when those messages return. This functionality is already present in the filter and in the standard (or rather sample) database of rules that arrives with it, but it can be easily extended or modified to meet a specific need or suit a particular taste.

Note that the present version of the program is considerably more than merely a spam filter narrowly aimed at discarding messages that fit simple patterns. Owing to the fact that its behavior is driven by programmable rules triggering potentially complex actions, the filter can be used as a sophisticated sorter preprocessing and distributing incoming E-Mail to a number of different mailboxes. Needless to say, all those functions can be performed in addition to its original purpose, which of course is eliminating spam.

Terminology and conventions

Most constructs occurring in rules are Tcl commands or programs. Consequently, in the following discussion, we will be using some (quite minimal) terminology related to Tcl. If you are new to Tcl, don't worry - you will be able to use the package without any problems. As a matter of fact, you can use it without even reading this document. A quick look at the contents of the rules file and some of the files in the Rules directory will give you most of the insight needed for adding new patterns and adjusting the rules to your specific needs. But to take advantage of the full power of RabidFire, you have to know a little bit about its workings.

We shall assume the following convention for presenting the syntax of constructs interpreted by the filter. Any text in the teletype font stands for itself, i.e., it must literally appear in the respective place of the discussed construct. A piece of text in italics represents a parameter to be specified by the user. A fragment enclosed in square braces [...] is optional, i.e., it may or may not be present depending on the circumstances. Several alternative elements, of which only one can be legitimately specified, are separated by `|'. The ellipsis (...) indicates a sequence of items with a similar syntax and/or related meaning.

All predefined operations accessible to the rules script are in fact Tcl functions. Besides the standard functions provided by the Tcl interpreter, the filter offers a number of specific functions related to pattern matching and mail handling. Some of those functions accept flags that look like keywords starting with the `-' sign. Some of the flags may be exclusive, i.e., refer to multiple options of a single property of the respective function. Sometimes a flag may require a parameter that must immediately follow the flag keyword. In all cases, it is sufficient to specify an initial portion of the flag keyword that uniquely corresponds to a single recognizable flag.

If a function accepting (possibly optional) flags expects other arguments, those arguments must follow all flags. As a flag must begin with `-', there is usually no problem determining where the flag list ends and the argument list begins. In ambiguous cases, the sequence `--' (two consecutive minus signs) can be used to explicitly terminate the flags part.

Principles of operation

The filter is always invoked to process a single E-Mail message that arrives on its standard input. The way this message is processed is described by a Tcl script stored in the rules file in the base directory. The contents of that file are read in and evaluated (as a typical Tcl script) in the context of some definitions provided by the filter. These definitions set up some global variables and functions accessible by the script.

Being a Tcl program, the rules script can be put together in many different ways and perform many different things, not even related to processing incoming E-Mail. There exists a recommended standard way of organizing it into a series of rules that evaluate certain conditions related to the received message and determine its fate based on the outcome of those tests. One reason for this recommendation is that the rules script is also read and executed by four other incarnations of the filter (and also by the killer), which expect to execute it sensibly in circumstances somewhat different from its standard role. The other reason is methodological. By splitting the actions of the filter into a series of finely grained rules, one can better control its operation and easily modify it on-the-fly. In particular, it is easy to insert new rules without affecting the integrity and semantics of the existing ones.

The rules have immediate access to various components of the received message, which are mostly extracts from its headers. They can also invoke functions defined by the filter for checking various conditions on those components (typically involving pattern matching) and for performing actions on the incoming message. One example of such an action is bounce, which returns the message to its sender preceded by a rejection letter (preamble) provided by the user.

The filter uses a database of textual patterns that are matched against selected fragments of message headers and, possibly, its body. Although those patterns can be kept directly within the rules script, it makes better sense to organize them into sets of patterns with related functionality and keep as a collection of separate files. Such files describe the so-called groups. A pattern group can be referenced by the rules as a single object and matched against the incoming message. Groups can also be modified by the filter (and by the killer), e.g., by adding new patterns. There exists a special class of tagged groups, which can be viewed as databases of patterns (or other strings) referenced by keys.

Besides filtering, the same filter program can be used to perform four other functions identified by the name under which the program has been invoked. In two of those functions, the program preprocesses outgoing E-Mail messages before they are submitted to the proper mail server. The purpose of this processing is to add the recipients of outgoing E-Mail to one selected group of patterns identifying trusted senders whose E-Mail should never be suspected of being spam. In its another guise, the filter provides a user-callable program for introducing modifications to the pattern groups. The fourth function is performing open relay tests on indicated mail servers.

Organization of the rules script

As stated in the preceding section, the same filter program can be invoked to perform one of four functions. This multiple functionality is accomplished by providing four extra links (different program names) to the executable file filter.

Regardless of the function for which the program has been invoked, it always reads and evaluates the contents of the rules script fetched from the base directory of the package. This is needed because, at least for some of those functions, the program must know the locations and definitions of the same pattern groups that are used by the filter (and are described in rules). However, only the filter function cares about the rules used to classify incoming E-Mail messages, and executing those rules for other functions would not make much sense. Therefore, the simple approach is to make sure that the actual rules are described using a special operation (Tcl procedure) named rule which is ignored (redefined as empty) in all incarnations of the filter except for the primary one. All other statements appearing in the rules script are always executed in the same way, regardless of the program function. In consequence, the declaration part of the rules file is shared by all incarnations of the script while the actual rules are only executed by the filter.

Practically, the only non-trivial operation whose execution makes sense in all functions of the filter program is a group definition. Consequently, the rules script should be organized in such a way that all other operations (i.e., those specific to its primary function of filtering E-Mail messages) are encapsulated into actual rules. This does not restrict the power of the script in any way, because the body of a rule is an unrestricted piece of Tcl code that executes at the global level of the interpreter and may itself be organized as a complete program (defining procedures, variables, and so on).

For illustration, let us have a look at an initial fragment of the standard rules script that comes with the package:

setdirectory Rules
#
# Some groups may be needed by the updater, mail proxy, and killer.
# These are just declarations, so it doesn't hurt to keep them all here.
#
group -file         -lock             hard_from_exc "hard_from.exc"
group -file -nocase             -reg  hard_head_exc "hard_head.exc"
group -file         -lock             hard_addr_spm "hard_addr.spm"
group -file         -lock             open_rlay_exc "open_rlay.exc"
group -file -nocase       -tags -reg  open_rlay_spm "open_rlay.spm"
group -file         -lock -tags       temp_from_exc "temp_from.exc"
group -file -nocase -lock -tags -reg  temp_subj_exc "temp_subj.exc"
group -file -nocase -lock -tags -reg  temp_body_exc "temp_body.exc"
group -file -nocase             -reg  hard_subj_exc "hard_subj.exc"
group -file -nocase             -reg  hard_body_exc "hard_body.exc"
group -file                           hard_from_spm "hard_from.spm"
group -file -nocase             -reg  hard_subj_spm "hard_subj.spm"
group -file -nocase             -reg  hard_head_spm "hard_head.spm"
group -file -nocase             -reg  hard_body_spm "hard_body.spm"
group -file                           mild_from_exc "mild_from.exc"
group -file -nocase             -reg  mild_subj_exc "mild_subj.exc"
group -file -nocase             -reg  mild_body_exc "mild_body.exc"
group -file -nocase             -reg  soft_from_spm "soft_from.spm"
group -file -nocase             -reg  soft_addr_spm "soft_addr.spm"
group -file -nocase             -reg  soft_subj_spm "soft_subj.spm"
group -file -nocase             -reg  soft_head_spm "soft_head.spm"
group -file -nocase             -reg  soft_body_spm "soft_body.spm"

setdirectory

rule Init {
#
# This unconditional rule initializes things for the filter
#
    group -nocase -reg complaint_subj { "spam +complaint" }
}
...

Being nothing more than a Tcl script, the rules script conforms to the Tcl syntax. In particular, all lines starting with `#' are treated as comments. The portion of the script following the second setdirectory command consists exclusively of a series of rules; thus, that portion is only meaningful to the filter. Note that the first rule (Init) is not really a rule. Its body is executed unconditionally and it defines one more pattern group - to be accessible to the filter but not to the other functions.

Pattern groups and files

From the viewpoint of the rules script, a pattern group provides a single identifier for a set of textual patterns to be matched against something. The actual patterns comprising that set can be fetched from an external file or can be specified directly when the group is defined. The following operation (Tcl function) is used to define a pattern group:

group flags groupname filename|patternlist

The flags part is a sequence of options describing the properties of the pattern group. The following flags are accepted:

-or or -and

This flag indicates whether the pattern set should be treated as an alternative (-or) or a conjunction (-and) of patterns. In particular, if -or is selected, then the group is assumed to match a given string if any of the patterns in it matches the string. With -and, all patterns must match the string for the matching operation to be considered successful. The default, assumed when neither -or nor -and is explicitly specified, is -or.

-file

If this flag is present, the patterns comprising the group will be read from the file indicated by the filename argument. Otherwise, the list of patterns is specified directly as patternlist.

-lock

This flag is only meaningful together with -file. It ensures that when the pattern file is read by the filter, it will be locked, but only for the duration of the read operation. This option is intended to guarantee the integrity of a pattern file that may be written into while being read - as explained below.

-nocase

The case of letters appearing in the patterns, as well as in the text being matched, is ignored. By default, the case of letters is significant, unless -address is selected (see below), which automatically forces -nocase.

-regular or -address

Selects one of two interpretations of the patterns comprising the group. With -regular, the patterns are regular expressions, as understood by Tcl, with a minor extension. With -address, the patterns are assumed to represent IP addresses (symbolic or numeric) and, possibly, E-Mail addresses. The details of these interpretations are explained in section Patterns. By default, if neither -regular nor -address is explicitly specified, -address is assumed.

-tags

If present, this flag indicates a group in which patterns are tagged with keywords that can be viewed as handles for referencing them. Such groups can be used as simple databases.

-multiple

This flag indicates that the group may have been already defined, in which case the present definition should be ignored. Without this flag, multiple attempts to define the same group are treated as errors.

The first of the two mandatory arguments following the keyword list assigns a name to the group. Group names must be unique and they must be legitimate Tcl identifiers.

Depending on whether -file was included among the flags, the second argument is interpreted as the name of the file from which the patterns should be read, or as the list of patterns specified directly as part of the group command. For example, the Init rule in the above example defines a group consisting of a directly specified single pattern, which happens to be a regular expression.

The pattern list of a "file" group is not read until the group is referenced for the first time. This way, no overhead is incurred by declaring many groups, and it actually pays to have more small groups rather than few large ones. If some of the groups never get referenced (because the fate of the received message is determined earlier), their pattern files will never be read.

We say that a file group is accessed passively if its pattern file has to be read but is never written back. This is the most typical reference of a group, e.g., to be matched against a piece of text. A file group may also be updated, which means that its pattern file is modified and overwrites the previous version. When this happens, the filter automatically locks the pattern file, to make sure that no other copy of the filter (or perhaps the killer) is performing a similar operation at the same time.

A file group that is not unlikely to be modified while it is being referenced passively (i.e., read) should be declared with the -lock flag. This will guarantee that the the pattern file is locked also while being read, but only for the duration of the read operation (to reduce contention). For example, the -lock flag is specified for the hard_from_exc group in the rules file that comes with the package. That group contains patterns identifying the trusted senders whose messages should be always delivered. Further down in the rules file, there exists a rule that will automatically add a new pattern to that group whenever the filter receives a message with the magic subject phrase. Therefore, as multiple copies of the filter are allowed to run concurrently, the file may be modified at any time, also while being read for passive access.

For as long as a group is being used passively (i.e., for matching), its pattern file is read only once - at the first use. Whenever a group is modified, this operation always consists of the following steps: locking the pattern file (which may involve waiting until the lock is available), then (re)reading the pattern file (even if it was read before, e.g., for passive access), modifying the group, writing the pattern file back, and finally unlocking it.

It may be worthwhile to mention that the scripts use their private method of locking files, because the standard UNIX file locks are not available from Tcl. Subdirectory Lock in the base directory is used for this purpose. Locking is accomplished by creating dummy files in that subdirectory. It is assumed that no file is ever locked for more than 2 minutes. Locks older than this much are considered stale and forcibly removed.

For an untagged file group (i.e., one whose declaration does not specify the -tagged flag), the corresponding pattern file is expected to contain one pattern per line. Empty lines and comments (i.e., lines containing `#' as the first non-blank character) are ignored, as are leading and trailing blanks of the lines containing patterns. If a pattern starts or ends with a blank (and also if it starts with `#'), it should be encapsulated in double quotes. Quoting patterns always makes sense and is recommended as a standard practice.

For a tagged file group, every line of the pattern file, except for a comment or an empty line, is assumed to include a pair of strings separated by at least one space or tab character. As each of those strings is allowed to contain spaces and tabs, it is recommended to use double quotes (for each of them) to avoid ambiguity. The first string is assumed to be a pattern (essentially in the same way as for an untagged group), and the second string is the tag of that pattern. If no second string is specified, the tag is assumed to be empty. It is not required that all tags be different.

For a non-file group, i.e., one whose patterns are specified directly as an argument to the group function, the specification comes as a list of strings representing the patterns. Note that double quotes in such a list are stripped automatically by Tcl when the list items are interpreted. For a tagged group, every element of the specified list must be itself a two-element list describing the pattern and its tag, respectively. Directly specified tagged groups do not seem to be very useful. A tagged group is meant to be a database, and a database requires non-volatile storage to make sense.

When a file group is modified (operation modify) and its pattern file is rewritten, the filter tries to preserve the structure of comments and empty lines in the pattern file, even if some patterns are removed. All patterns and tags in a pattern file rewritten by the filter are always quoted.

Patterns

Depending on how the group was defined (with the group operation), its patterns can be viewed as representing either addresses or general regular expressions. The second interpretation is somewhat simpler to explain because it essentially coincides with the standard interpretation of regular expressions in Tcl. If you don't know Tcl, you may assume that its regular expressions are more or less the same as those acceptable by sed, perl, or vi. Remember that the characters `.*^$()[]' are special. They should be escaped with `\' if they are meant to stand for themselves.

In fact, there is one somewhat inconvenient difference between the regular expressions formally accepted by Tcl and those used by most other programs. Namely, Tcl patterns don't let you specify word boundaries, i.e., `\<...\>'. To compensate for this inconvenience, the filter preprocesses patterns belonging to a regular group, replacing `\<' and `\>' with sequences that simulate world boundary matches. This only works at the very beginning and end of a pattern, however.

Below I include (without permission) an excerpt from the on-line documentation of Tcl succinctly summarizing all the features of regular expressions in their Tcl edition.

Begin of excerpt

Regular expressions are implemented using Henry Spencer's package (thanks, Henry!), and much of the description of regular expressions below is copied verbatim from his manual entry.

A regular expression is zero or more branches, separated by `|'. It matches anything that matches one of the branches.

A branch is zero or more pieces, concatenated. It matches a match for the first, followed by a match for the second, etc.

A piece is an atom possibly followed by `*', `+', or `?'. An atom followed by `*' matches a sequence of 0 or more matches of the atom. An atom followed by `+' matches a sequence of 1 or more matches of the atom. An atom followed by `?' matches a match of the atom, or the null string.

An atom is a regular expression in parentheses (matching a match for the regular expression), a range (see below), `.' (matching any single character), `^' (matching the null string at the beginning of the input string), `$' (matching the null string at the end of the input string), a `\' followed by a single character (matching that character), or a single character with no other significance (matching that character).

A range is a sequence of characters enclosed in `[]'. It normally matches any single character from the sequence. If the sequence begins with `^', it matches any single character not from the rest of the sequence. If two characters in the sequence are separated by `-', this is shorthand for the full list of ASCII characters between them (e.g. `[0-9]' matches any decimal digit). To include a literal `]' in the sequence, make it the first character (following a possible `^'). To include a literal `-', make it the first or last character.


Copyright © 1993 The Regents of the University of California.
Copyright © 1994-1996 Sun Microsystems, Inc.
Copyright © 1995-1997 Roger E. Critchlow Jr.

End of excerpt

The same regular expression matching apparatus of Tcl is used to match patterns in both types of groups, i.e., regular and address. The difference is in how the expressions in the two types of groups are preprocessed before being used for matching. Also, the case of letters is always ignored when matching a pattern from an address group, whereas for a regular group this option is settable by a flag.

An address pattern is assumed to describe a class of IP addresses (symbolic or numeric) or E-Mail addresses. The following rules apply: