flail - a hacker's mailer in Perl


  # to run a single flail command:
  $ flail -1 -other_options [cmd ... args ...]

  # to get into the interactive command loop
  $ flail

  # to get a usage message:
  $ flail -h

  # to get the whole manual
  $ flail -hv


flail is a hacker's mailer, written in Perl, and sporting a command-line interface. It currently supports pop3 and imap for access to remote maildrops, as well as regular old Unix mail spool files for local maildrops (e.g. because you use fetchmail).

The commands are vaguely Unix-like (rm, cp, mv, cat, ls). There are facilities for mapping bits of perl code over some subset of the messages in a folder, crypto, external editors, and user-defined commands.


You naturally invoke flail on the command-line. It takes single-letter options, just like Pan intended. Where it makes sense, we note the name of the config variable corresponding to each option in parentheses.

-c ($NoDefaultCC)

Do not ask for Cc: addresses by default in the composer

-q ($AskBeforeSending)

Confirm with user before sending message

-o ($AllowCommandOverrides)

Allow the alias command to override built-in commands.

-Q ($Quiet)

Be Vewy Quiet: Only produce error messages and explicitly request output (e.g. ls)

-l ($RemoveFromServer)

Remove messages from server during get processing.


When by itself, display usage message. When specified with -v, display this POD.

-s ($SyncImmediately)

Automatically sync the current folder after operations that change it, e.g. mv, rm, ...

-p ($PipeStdin)

Read message from stdin; really only useful in conjunction with <-1>. Implies -c.


Run a single flail command, specified as arguments on the command line. For instance

  $ flail -1 send

can be used to send a single message to someone famous.

-v ($Verbose)

Make ourselves verbose.

-r rcfile

Specify an alternate rc file. The default is ~/.flailrc

-d folderdir ($FolderDir)

Specify an alternative directory for mail folders. The default is ~/mail

-i incfolder ($IncomingFolder)

Specify an alterntive incoming mail folder name. The default is INCOMING

-P pop3info
-I imapinfo

These options are largely outdated, but can still be useful, especially in conjunction with -1.

In both cases, a string of the form user@server:port, where user and port are both optional, and given the obvious default values if left unspecified.

-F fromaddr ($FromAddress)
-D domain ($Domain)
-S smtphost ($SMTPHost)

These are also probably only really useful with -1, since your ~/.flailrc will probably arrange to set them in less obvious ways. These options set the From address, domain, and SMTP relay used to send a message.

-T tempdir ($TempDir)

Set the temp dir used for e.g. message composition.

-e editor ($Editor)

Set the external editor used from the composer. Defaults to your $EDITOR environment variable.

-C fccfolder ($FCCFolder)

Set the folder name in which to automatically file outgoing messages.

-R pop3|imap ($CheckType)

Outdated: specify default message check method, pop3 or imap. Since you can have more than one account of each kind, sort of silly. If you do not specify any arguments to check, we use $CheckType as the default.

-A abook ($AddressBook)

File containing your addressbook as a dbm file.

-n ($NoAddressBook)

Do not use an addressbook.

-a ($AutoAddressBook)

Automatically try to look up all outgoing addresses in our addressbook.

-k ($AskAddressBook)

Interactively prompt the user for address book matches before sending.

-E ($ExactHostMatch)

Only match two addresses if the hostnames are identical.

-b ($QuietAddressBook)

Make addressbook functionality quiet.

-N newlabel ($NewLabel)

Label all incoming messages with newlabel by default.

-g dot.sig.dir ($SignatureDir)

Look for dot.sigs in dot.sig.dir

-G ($Debug)

Turn on debugging output. Not useful for ordinary use.

-u defaultsubj ($DefaultSubject)

Set the default subject for outgoing email. None by default

-U ($HeadersFromStdin)

If -p is specified, then -U says that the entire message, including headers, is coming in on stdin.


The command language is vaguely Unixy. Commands look like:

  word [arg arg...]

Some commands take slash-style options, ala TOPS-20:

  send/as:someone@somewhere ...

Many (most?) commands at least will pay attention to options named debug, verbose and quiet. For instance, to turn on SMTP debugging when you send an email:


The first character of a command might specify another action than a normal command:


Execute Unix command cmd and display results on stdout


Pipe the current message through cmd and display the results on stdout


Evaluate the perl code code. Does not display results by default, if you want them, use the print statement, e.g.

  ,print $Editor

Other than those special cases, we look at our first word and dispatched based on it. The complete list of built-in commands follows, grouped by function. For information on adding your own commands, see the section on CONFIGURATION, below.

Message Sequences (also called "Range Expressions") are an important part of the flail command language. The following are all valid range expressions:

  1. Message 1

  2. 1:3

    Messages 1 through 3

  3. 3:$

    Messages 3 through the end of the folder.

  4. $-3:$

    The last three messages in a folder

  5. 1,3,5,$-3:$-1

    The first, third, and fifth messages, as well as the second two from the end (not including the last one)

In addition, you can specify -label to include all messages tagged with the given label, so the range expression -marked expands to all marked messages.

Many commands take message sequences in place of single message numbers. Some do not. Hopefully, I'll do a good job of telling you which is which.

Checking and Retrieving Mail

Commands to query and fetch mail from mail spools.

check: check pop3, imap or local spool mailbox

Check a mailbox for mail. The full syntax is

  check type user server


  check pop3 attila mailserver

looks for mail using pop3 on mailserver as the user attila. You will be prompted for a password, unless flail remembers what it is. The remember_password internal function can be useful in your configuration file to keep you from having to type your password all the time. If you insist, you can specify your password as the final argument on the command line, but we don't recommend it.

To check a local spool file, use

  check spool /path/to/spool

The default spool is /var/mail/yourusername, so if this is correct you can just do

  check spool

to check your local mail spool. You can override both the file name and directory with the $SpoolFile and $SpoolDir configuration variables in your ~/.flailrc.

get: download mail from a remote mailbox

Just like the check command, except that we fetch the mail and incorporate it into the incoming folder. It takes the same type parameter as its first argument, e.g.

  get spool

grabs (and expunges) your local mail spool.

These commands are for stumbling around in the folder tree and looking at messages.

If you have a folder selected, flail shows you what it is in your prompt. If your prompt is simply


then you are not in any folder, and many of these commands will fail with an error.

next: goto next message

Move to the next message.

prev: goto previous message

Move to the previous message.

goto: goto arbitrary message by number

Go to a message by number, e.g.

  goto 3

moves to the message numbered 3 in the output of ls

cat: display message contents

Displays the current message, or one by number if specified. Output is paginated if we know how to do that on your terminal.

headers: display message headers in detail

The cat command normally shows abbreviated headers. The headers command shows only the headers for a message, and it shows them all.

decode: decode a MIME message

Not yet implemented.

cp: copy a message to another folder

Copy a message to another folder, e.g.

  cp 3 spam

copies message 3 to the folder named SPAM

mv: move a message to another folder

Like cp, but removes the message from the current folder after it is copied. If $SyncImmediately is true, we sync the folder afterwards. Otherwise, the message appears with the D (Deleted) flag turned on until you sync.

A message sequence can be specified, e.g.

  mv 1,2,$-3:$ odd_folder

rm: delete a message

Delete a message or messages, e.g.

  rm 1:$

deletes all messages in the current folder.

flail uses Mail::Folder, and can thus support any type of mail folder that it supports. Generally, we use mbox folders, which are single files containing multiple messages. None the less, we treat "folders" as if they were directories from the command language, for consistency.

cd: enter a folder

Change the object of your affections to another folder, e.g.


pwd: display current folder

Show the current folder and your state in it.

ls: list folder contents

List the contents of the folder, one message per line. If $PlainOutput is not true, we try to color it nicely for ttys that support ANSI color sequences, like xterm. If a message sequence is specified, we only show those messages. Output is paginated.

If you are not in a folder, then ls shows you the subfolders in whatever part of the folder tree you happen to be in. If you are at top level, this is all of the top-level folders under the root.

You can specify an arbitrary range expression to ls, as you can with many other commands. For instance,

  ls -marked

will list all marked messages.

mkdir: create a folder

Not yet implemented. For now, use the following idiom

  !touch foldername

to create a new blank folder (remember: we chdir to your $FolderDir on startup...).

Sending Messages

Things that call the composer. See MESSAGE COMPOSITION INTERFACE, below, for details.

send: send a new message

Send a message to the addresses given as arguments. The From address can be set explicitly with the as option, e.g.


If there is a problem, you might re-trying the send command with the debug option, which turns on Net::SMTP debugging and will produce a large amount of output.

We invoke the external editor of your choice for composition, which should return a valid status code. You will generally have a chance to go over it, re-edit it, abort, cryptosign, attach .sig, etc. after you're done editing.

reply: reply to a message

Like send, but replies to the sender of a previous message.

forward: forward a message to a new recipient

Like send, but forwards an existing message to a third party.

resend: resend a bounced message

Like send, but resends a message that was bounced.

The Address Book

address: interface to the address book

The address command has several subcommands to help you manage your addresses. Flail stores these in a dbm file, typically called ~/.flail_addressbook.


add nickname email

Add a new entry. Nickname must be unique in your addressbook.

show nickname

Show the address associated with the given nickname.

list [regexp]

Search the addressbook by regexp, or list the whole thing if no regexp specified

del nickname

Remove an entry by nickname

import filename

Import address book data stored in a file. The two kinds of files we currently support are LDIF and CSV; files should have extensions that reflect their type, e.g .ldif or .csv.

take [label]

Extract email addresses from one or more messages and import them into your addressbook. If no label is specified, the current message is examined; otherwise, all messages in the current folder with the specified label are examined.

Marking, Mapping and Other Fun Stuff

Messages can have an arbitrary set of labels associated with them. Some, such as deleted and filed, have meaning to flail itself. Some, such as marked or whatever other string you might use, are just for your own purposes. Marking messages lets you apply code to them, so flail users do it alot.

mark: add a label to a message

In the absence of any arguments, applies the marked label to the current message.

If a message sequence is specified, then all messages in that sequence get the marked label. If the word all is specified, then all messages get marked.

If the first character of our first argument is a comma, then the rest of the arguments are treated as a bit of perl code that is invoked for every message in the folder. The token %m will be substituted for a variable that is bound to an Mail::Internet object representing each message in the folder on subsequence calls.

For instance, if we have in our configuration

  sub is_blue { shift->get("Subject") =~ /\[blue\]/; }

Then we can do

  mark ,is_blue(%m)

to mark all messages whose Subject headers contain the string [blue].

unmark: remove a label from a message

Unmark takes the same argumentology as mark, but removes the marked label instead.

map: map a piece of code over some messages

  map label ...

Run a command or piece of code over a set of messages, specified by a label. If the label is all, then all messages have the action applied to them. The action can be a flail command, or a piece of random Perl code. In the latter case, the first character of the code should be a curly brace, and the code should end with one as well, e.g.

  map marked { grep_msg('Foo'); }

Runs the given code for each message. The code is called in a context where the following globals are available in the main namespace:

$N the message number in the folder
$M a Mail::Internet object that represents the message
$F the Mail::Folder object corresponding to the folder
$H the Mail::Header object associated with $M

If there are no curly braces, the command should be a legal flail command. In both cases, two additional substitutions take place:

%n is substituted with the message number
%* is substituted for any arguments that appear after the command

This last substitution is a bit odd, and requires explaining.

Supposing we have our hypothetical sub grep_msg (which does in fact come with the dot.flailrc in the distribution). It wants a regexp as its argument. It uses $M, etc. to get at the message it is grepping. Supposing we want to be able to type

  mgrep pattern

to run grep_msg("pattern") over each message. How do we do it?

  alias mgrep map all { grep_msg("%*"); }

Now, when we type

  mgrep pattern

The %* is substituted with pattern. This is why map is useful even when operating on a single message.

Another enlightening example:

  alias mmv map marked mv %n %*

This creates an alias, mmv, which can be used thusly:

  # arrange to mark the messages that are spam,
  # either by hand ...
  mark 1,3,5

  # or by some automated oracle
  mark ,is_spam(%m)

  # now, move it all to a folder named spam
  mmv spam

Of course this is just a contrived example, since you can accomplish the same thing with labels, e.g.

  mv -marked spam

will move all marked messages to the folder named spam.

count: count labeled messages

Given a label, count the messages that match it. If no label is specified, marked is assumed.

run: run message hooks for label

Given a label, run all of the message hooks associated with that label over every message that has that label in the current folder. See the discussion on message hooks in Hacking Flail, below.

State Management

sync: flush changes to the current folder

Flush any changes to the current folder to disk. This includes expunging messages labeled deleted.

reset: reset various bits of state

Resets the password cache and/or the connection cache.


decrypt: decrypt a PGP-encrypted message

This needs a rewrite, as my crypto fu relies on outdated modules and must be rewritten.

Other Commands

alias: create a new command

Create a new command. You cannot overwrite existing command table entries this way; use Perl code in your config file instead.


  alias mvspam map marked mv %n spam

unalias: remove an alias

Remove an alias.

help: get help

You can ask for help on any specific command or alias by using it as an argument, e.g.

  help ls

Invoking help with no arguments produces a list of commands.

In addition to commands, you can ask for help on the following subjects


spit out the flail manual (thanks to Pod::Usage)


show our version information


show our full license


show our warranty

quit: exit flail

Bug out, flushing all changes.

If you want to bug out without saving anything, use Perl:



When you are sending a message, you will be generally be prompted before it goes out. The prompt allows you to perform some fairly complex sequences of actions with a few keystrokes, and is usually called the composer.

XXX Write more.


flail is a hacker's mailer. Configuring flail means writing Perl code. If this does not fill you with joy, you're in the wrong bar.

Your .flailrc

Flail loads ~/.flailrc upon startup. An extensive example comes with the distribution (dot.flailrc). You can specify another file with the -c command-line option.

Command-line options will already have been parsed by the time your rc file is loaded. This means you can check for the value of e.g. $SingleCommand to see if -1 was specified on the command line, etc.

Managing Multiple Identities and Mailboxes

Passwords and the Pipe Trick

Hacking Flail

Flail is ultimately just a bunch of Perl subs. It currently all lives in the main package, which is where your flailrc is loaded as well. This means you can write code that calls any flail primitive, add new primitives, or extend the command set using the same API (if you want to diginfy it with that name) that I use.

WARNING: I will be rewriting flail to use OO techniques in the very near future. You should get on the flail-dev mailing list if you are interested.

Alphabetical Listing of All Configuration Variables


Too many bugs to count at the moment...

Finish the OO/modular rewrite
Get rid of all I/O in signal handlers
Move main into a MAIN: { } block
Re-write this turkey so it's not so fugly
Just finally stop reading email already, everybody is already so over email.


Sean Levy <> wrote this thing, sometime in or around 2000 most likely, although he can't quite remember the exact year. It started out as a pile of hacks and has matured and blossomed into a GREAT FREAKING HUGE PILE OF HACKS.

Sean is also known as attila <>, for historical reasons almost entirely under his control. That's "Saint Alphonsos".


Alice: Well I must say I've never heard it that way before...

Caterpillar: I know, I have improved it.

  0.2.5    06 Sep 08     attila  found some lost hacks from a source
                                 tree recovered from a dead laptop:
                                 semi-colon-separated commands,
                                 send_via_program, a couple other things.
  0.2.4    05 Aug 08     attila  revived from the dead AGAIN after
                                 t-bird screwed me hard.
  0.2.3    30 Jun 06     attila  released on freshmeat
  0.2.2    26 Jun 06     attila  wrote pod, use strict, blah blah
                                 added local spools
  0.2.1    25 Jun 06     attila  fixed horrible bug in get_imap
  0.2.0    24 Jun 06     attila  resurrected after i got sick of VM
  0.1.28   26 Feb 03     attila  0.1.28 released
  0.1.?    ?? ??? 02     attila  Somewhere around 2002 I found myself
                                 using flail everyday and thought perhaps
                                 I should release it or something
  0.0.0    ?? ??? 00     attila  Sometime in y2k I had a brain schizm
                                 and decided to write an MUA in perl


  Copyright (C) 1999,2000 St. Alphonsos.
  Copyright (C) 2000-2008 by Sean Levy <>.
  All Rights Reserved.

  Redistribution and use in any form, with or without modification, are
  permitted provided that the following conditions are met:

  1. Redistributions of source code must retain the above copyright
     notice, this list of conditions and the following disclaimer. 

  2. The names "St. Alphonsos", "The Clue Factory", and "Sean Levy"
     must not be used to endorse or promote products derived from this
     software without prior written permission. To obtain permission,
     contact or

  3. Redistributions of any form whatsoever must retain the following
     "This product includes software developed by St. Alphonsos and Sean Levy <>"


4 POD Errors

The following errors were encountered while parsing the POD:

Around line 3951:

Expected '=item 2'

Around line 3955:

Expected '=item 3'

Around line 3959:

Expected '=item 4'

Around line 3963:

Expected '=item 5'