# MIME Configuration

You can change the default MIME configuration by editing the profiles that MH and nmh read. There are two personal profiles; a system-wide profile is used if yours don't have the needed entries. This section of the book has detailed information and examples of the entries you can put in the profiles.

MIME message handling differs between MH and nmh. Here's an overview of the differences:

- In MH, the mhn command handles all MIME processing. (This is mostly also true in nmh before version 1.0.) mhn can have its own profile entry (`mhn:`). It also uses a set of profile entries that start with `mhn-`, like `mhn-store-text/plain:`. mhn will also read two other profiles: the system-wide mhn_defaults and the personal profile named in the `MHN` environment variable.
- In nmh-1.0 and above, the commands mhbuild, mhlist, mhshow, and mhstore have replaced the does-it-all mhn command. Each has its own profile entry (`mhbuild:`, etc.). Each also uses a set of MIME profile entries that start with a string like `mhbuild-`, such as `mhbuild-compose-text/plain:`. These commands will also read two other profiles: the system-wide mhn.defaults for all commands, and the individual profile named in the environment variable `MHBUILD`, `MHLIST`, etc.

The Section Showing MIME Messages has an introduction that you may want to read before you get started with the detailed explanation here.

> NOTE: In general, nmh-1.0 seems to read old MH-style entries for the mhn command. But, as the nmh mhn(1) manual page says, mhn is deprecated (obsolete) from nmh version 1.0 and up. If you won't be using MH, I'd suggest learning the nmh syntax. I cover both MH and nmh here, emphasizing MH.

## MIME Profiles

Like all MH commands, a MIME-handling command reads an MH profile entry with its own name: `mhn:` for MH, or `mhbuild:`, etc., for nmh. That entry sets command-line options you want to use every time the command runs. There's a list of those options in the MH Reference Guides for mhn, mhbuild, mhlist, mhshow, and mhstore.

But MIME commands also use special profile entries that start with `mhn-` (like `mhn-store-text/plain:`) for MH -- or `mhbuild-`, etc. (like `mhbuild-compose-text/plain:`) for nmh. These can't be typed on the command line; they're only read from files. This section explains those special profile entries and how to set them.

MIME commands read those entries from three files, in this order:

1. If you've set the MHN environment variable, the MH mhn command reads the file that it points to. Or, for nmh, each command (mhbuild, etc.) checks its own environment variable: MHBUILD, MHLIST, MHSHOW, and MHSTORE.
2. If the command didn't find the entry it needed in file named by the environment variable (or if there wasn't one defined), it checks your MH profile. If the MH environment variable is set, that file is read; otherwise, the default is the .mh_profile in your home directory.
3. If the command didn't find the entry in the first two places, it reads the system-wide profile. For MH, that's the mhn_defaults file in the MH library directory (often /usr/local/lib/mh). nmh checks the mhn.defaults file in its etc directory (often /usr/local/nmh/etc).

The order that the profiles are read is important.

The person who installed MH on your host also installed the mhn_defaults or mhn.defaults file. It should have global entries, reasonable defaults for most accounts on the system to use. If you're lucky, it contains entries for all the specialized programs needed for handling different content types. Those programs may be scattered around your filesystem.

The entries in mhn_defaults or mhn.defaults may not be right for you, though. For instance, most of your system's accounts may use the X Window System, but you always work on an old ASCII terminal. The default profile entries that start X programs will just print errors on your ASCII terminal. In that case, you can add entries to your MH profile that work on your terminal; these will be used in place of the entries in mhn_defaults or mhn.defaults. (You don't have to duplicate the mhn_defaults or mhn.defaults entries that are correct for you. For instance, if the `mhn-show-text/enriched:` entry in mhn_defaults works on your ASCII terminal, there's no need to put another `mhn-show-text/enriched:` entry in your MH profile.)

Or, you may use several different types of terminals. You could have X at work and a terminal emulator program on your PC at home. The system defaults might be right on your X system, but not on your PC. So, you'll set the MHN or MHSHOW environment variable when you're at home; it can point to an alternate profile with entries that your PC needs.

Of course, a mixture of profile entries might be right for you: some in your MH profile, some in an MHN file for one of your displays, another in a second MHN file for another situation, a third MHN file that's read by a custom shell script you've set up. Who knows? `:-)`

## Making a MIME Profile

If you need the special profile pointed to by the environment variable MHN (for MH) or MHBUILD, etc. (for nmh), read this section. It has a few ideas for setting up your account. Remember that, if you only log in from one type of terminal or window system, you probably won't need to set the environment variable -- instead, you can probably put all the entries in your main MH profile.

Here's the basic idea. As a process starts, the UNIX environment variables set in the parent process are copied to the child process. The MHN environment variable must be set in the parent process of MH's mhn command. In the same way, for nmh, MHBUILD must be set for mhbuild, and so on.

In most cases, the right place to set the environment variable is the startup file, like .login or .profile, that your login shell reads. But if you run MIME commands from a process that isn't started by your login shell -- for instance, in some window systems or as a shell script that runs from cron(8)-- you'll need to set the appropriate environment variable there. X Window System users may want to use a file like .xsession.

If you'll have more than one MHN file, it helps to have a system for naming them. I put all of mine in my MH directory with filenames that include the TERM environment variable setting -- like mhn-prf.vt100 and mhn-prf.xterm. You may want to put all of them in a separate directory. (That's especially true for nmh users who could have multiple versions of the multiple files that nmh MIME commands need!)

To set a variable like MHN automatically from your login shell, you'll need to decide what characteristics of your login session change from place to place. Have your shell setup file execute the correct command to set the environment variable. As an example, for the MH mhn command:

`setenv MHN /full/pathname/of/file` ...csh-type shell

`MHN=/full/pathname/of/file`; export MHN ...sh-type shell

Here are some ideas for MH; if you use nmh, change the variable names. A book about UNIX shells will give the overview of the constructs below. If you need help with nitty-gritty details of your particular system, ask your system administrator:

- If your TERM environment variable is set differently on each terminal, and if you use an MHN profile on all terminals, use the terminal name as part of the filename. Then a simple substitution like this will do the job:

  `setenv MHN /path/to/directory/mhn-prf.$TERM` ...csh-type shell

  `MHN=/path/to/directory/mhn-prf.$TERM` ...sh-type shells
  export MHN

- To set one MHN on some terminals and a second MHN on others, try this:

  csh-type shell:

            switch ($TERM)
            case vt*:
            case wyse*:
               setenv MHN /path/mhn-prf.ascii
               breaksw
            default:
               setenv MHN /path/mhn-prf.X
               breaksw
            endsw

  sh-type shells:

            case "$TERM" in
            vt*|wyse*) MHN=/path/mhn-prf.ascii ;;
            *) MHN=/path/mhn-prf.X ;;
            esac
            export MHN

  This setup also works if you want to set MHN on some terminals but not others.
- If you log in from other hosts or from hosts running the X Window System, the who command will probably show a hostname and/or window information in parentheses:

            % who
            barbie   ttyq9   Aug 27 11:16   (ncd5.foo.com:0.0)
            ken      ttyqb   Aug 27 13:12   (macsnack.foo.com)

  (Long hostnames may be truncated. Check yours before you write this test.) If this will help, try commands like these for csh-type shells:

            set tty="`tty`"
            switch (`who | sed -n "/ $tty:t /s/.*(\(.*\))/\1/p"`)
            case *0.0:
                    ...set MHN for X display 0
            case macsnack*:
                    ...set MHN for the host macsnack.foo.com

  That uses the C shell's :t modifier to get a tty name like ttyq9. Then it uses sed to search for that line in the who output -- and give the text between the parentheses for that tty name to the `switch`. This `case *0.0:` matches lines ending with `0.0`, and the `case macsnack*:` matches lines that start with `macsnack`.

  To do the same thing in the sh-type shells, use a case statement:

            basetty=`basename \`tty\``
            case "`who | sed -n \"/ $basetty /s/.*(\(.*\))/\1/p\"`" in
            *0.0)
                    ...set MHN for X display 0
            macsnack*)
                    ...set MHN for the host macsnack.foo.com

- If you know that certain tty names are used for certain kinds of logins, you can test that. For example, many systems use ttyp0, ttyq1, etc. as network ptys for rlogin and telnet. This test will branch on the tty name:

  csh-type shell:

            switch ("`tty`")
            case /dev/tty[pqrs]?:
                # rlogin, telnet:
                setenv MHN ...
                breaksw
            case /dev/tty02:
                # at my desk:
                setenv MHN ...
                breaksw
            endsw

  sh-type shells:

            case "`tty`" in
            /dev/tty[pqrs]?)
                # rlogin, telnet:
                MHN=...
                ;;
            /dev/tty02)
                # at my desk:
                MHN=...
                ;;
            esac
            export MHN

- Certain systems set certain environment variables. For example, the X Window System sets a DISPLAY environment variable. (If you aren't sure about your system, use the env or printenv command to look for changes in your environment at different systems.) You can test that:

  csh-type shells:

            if ($?DISPLAY) then
                # X
                setenv MHN ...
                breaksw
            else if ($?WIN_PARENT)
                # SunView
                setenv MHN ...
                breaksw
            else
                ...
            endif

  sh-type shells:

            if [ -n "$DISPLAY" ]
                then
                # X
                MHN=...
            elsif [ -n "$WIN_PARENT" ]
                then
                # SunView
                MHN=...
            else
                ...
            fi
            export MHN

- If you have more than one computer or type of computer, compare the outputs of the uname(1) and arch(1) commands on the hosts.
- If you use the same home directory on several types of machines, you may need set things up differently for different machines. Try this:

  csh-type shell:

            set try="`mhpath +`/mhn-prf.`hostname`"
            if (-e "$try") setenv MHN $try

  sh-type shells:

            try="`mhpath +`/mhn-prf.`hostname`"
            test -f "$try" && { MHN="$try"; export MHN; }

  The command `mhpath +` returns the path of the MH directory on my current host. `hostname` returns the hostname (you may need `uname -n` instead). So, if you log in to the machine kumquat, and you have a file called mhn-prf.kumquat in your MH directory, it will be used.

Mix and match those techniques for your situation. Remember that you can use UNIX links (the ln(1) command) to give the same file several different names. And there's one unfortunate problem: some systems give the answer `Not a tty` when you run tty(1) from inside backquotes (``tty``).

To be sure the environment variable is being set correctly, type one of the following commands just before you use mhshow, mhn, or other MIME commands. You're set to go if you see the value you want (which might be an empty answer, if you don't want the variable set in that situation!):

        % printenv MHN
        /u/jerry/Mail/mhn-prf.vt100

...or...

        % env | grep MHN=
        MHN=/u/jerry/Mail/mhn-prf.vt100

And now you're ready for the real fun! `;-)`

## What Profile Entries Are There?

Start by finding your system's mhn_defaults or mhn.defaults file and reading it. These are entries you'll never have to duplicate -- unless you want to override them. Your MH profile (usually named .mh_profile) may also have some entries -- including one (like `mhn:`, `mhshow:`, etc.) for the command itself.

Any profile may have six kinds of mhn profile entries:

Entry | Purpose
--- | ---
MH | nmh-1.0+ |
`mhn-show-type` | `mhshow-show-type` | showing a message of this MIME type
`mhn-compose-type` | `mhbuild-compose-type` | composing a draft message content of this MIME type when directive doesn't give a filename to read content from
`mhn-charset-charset` | `mhshow-charset-charset` | how to render a message in the character set charset
`mhn-storage` `mhn-store-type` | `nmh-storage` `mhstore-store-type` | where and how to store messages of this MIME type
`mhn-cache` `mhn-private-cache` | `nmh-cache` `nmh-private-cache` | where external body parts are cached
`mhn-access-ftp` | `nmh-access-ftp` | program to handle FTP transfers of external body parts

Below is part of an mhn_defaults file for MH. An mhn.defaults file for nmh has the same format but directives have different prefixes. (For instance, the MH entry `mhn-charset-iso-8859-1` would be replaced by `mhshow-charset-iso-8859-1`. See the table above.)

        mhn-charset-iso-8859-1: xterm -fn '-*-*-*-*-*-*-*-*-*-*-*-*-iso8859-1' -e %s
        mhn-charset-iso-8859-8: xterm -fn '-*-*-*-*-*-*-*-*-*-*-*-*-iso8859-8' -e %s
        mhn-compose-audio/basic: cat < /dev/audio
        mhn-show-application/PostScript: %plpr -Pps
        mhn-show-audio/basic: %pcat > /dev/audio
        mhn-show-image/x-pbm: %ppbmtoxwd | /usr/bin/X11/xwud -geometry =-0+0
        mhn-show-image/x-pgm: %ppgmtopbm | pbmtoxwd | /usr/bin/X11/xwud -geometry =-0+0
        mhn-show-image/xwd: %p/usr/bin/X11/xwud -geometry =-0+0
        mhn-show-image/x-xwd: %p/usr/bin/X11/xwud -geometry =-0+0
        mhn-show-image: %p/usr/local/bin/xv -geometry =-0+0 '%f'
        mhn-store-application/PostScript: %m%P.ps
        mhn-store-text: %m%P.txt
        mhn-store-video/mpeg: %m%P.mpg

Each entry has two parts. The first part is the entry name, like `mhn-store-text:`. The rest of the entry has instructions for handling that content type. These instructions are called a string:

- A command string, for `mhn-charset-` and `mhshow-charset-` entries
- A composition string, for `mhn-compose-` and `mhbuild-compose-` entries
- A display string, for `mhn-show-` and `mhshow-show-` entries
- A formatting string, for `mhn-store-` and `mhstore-store-` entries

Most of the entries have a UNIX command. Some entries have escapes like `%p`. Escapes are used for two things:

- Make the MIME command do something particular. For example, `%p` in a display string tells mhn or mhshow to list content information on the terminal and then prompt `Press <return> to show content...` and wait.
- Replaced with text for a filename. For example, in the `mhn-store-` and `mhstore-store-` entries, if you're storing part of a multipart message, `%P` is replaced with a dot (`.`) followed by the part number.

If an entry holds a UNIX command, that command will be passed to a Bourne shell for processing. This means you can use Bourne shell features: I/O redirection (operators like `<`, `>`, `m>&n`, and `|`), quoting, command substitution (backquotes), and so on. You can basically write a small shell script in the profile entry! Or, of course, the entry can invoke a script file written for a shell, Perl, Tcl, and so on.

Here's an example of what you can do. The mhn_defaults file above is installed on a SPARCstation named server with its own built-in audio. If I do a remote login to server to get my email, I don't want audio to be played and recorded on server; it could be hundreds of miles away from me. I want audio to be processed on the host I'm using, slug. So I've added the entries in the next Example to a MIME profile on server. They use the rsh(1) command to send audio across the network, to and from slug:

Example: Server profile entry to handle audio on a remote client

        mhn-compose-audio/basic: rsh slug cat \< /dev/audio
        mhn-show-audio/basic: %prsh slug 'echo "Sending audio to slug...";
            set t=/tmp/mhn$$; cat > $t; cp $t /dev/audio; rm -f $t'

If I run comp on server to send an audio/basic content, mhn or mhbuild will run the command `rsh slug cat \< /dev/audio`. Because the input redirection character is escaped (`\<`), the shell on server will ignore it; the redirection will be done on slug, and the cat command will run on slug and send the audio down the network to server. Next, if a message on server has audio to "show" (play), the profile entry tells mhn or mhshow to output a warning that reminds me to wait (audio files can be big). Then it stores a temporary filename in a shell variable, sends the audio down the network to the temporary file on slug, and runs cp on slug to send the audio to slug's speaker. I used a temporary file to collect all the audio before playing it; this avoids network delays that could make the audio sound "choppy."

This example wasn't meant to impress you. `:-)` I wanted to show what you can do if you understand how UNIX shells work. For instance, even though the commands in MIME profile entries are interpreted by the Bourne shell, my account on slug uses the C shell... so I knew that the rsh commands had to use C shell syntax. Your entries probably won't need to be this complex -- but it's nice to know what MH and nmh let you do!

Next, let's take a close look at each kind of profile entry. The first section below is a tutorial. Sections near the end provide reference information.

## Showing MIME Contents

The Section Showing MIME Messages introduced the way that mhn and mhshow display the message header and body. There's much more information about all of this in the mhshow(1) manual page; if you use nmh-1.0 or higher, that's a good place to get the gory details. This section of the book starts with some details (not as many as the man page) about the display of MIME body contents, and it ends with several examples of changing that display.

After showing the header, MIME body parts are shown one-by-one. Or, for a multipart/parallel message, parts are shown at the same time unless a `%e` escape (listed in the Table Display String Escapes) prevents it. In a MIME message, each body part has a content-type. To display a content, mhn and mhshow run a UNIX command. The UNIX command is found in a profile entry, as explained in the Section MIME Profiles.

If mhn or mhshow can't find a profile entry, it has two defaults: the `moreproc:` (your MH profile entry, or the system default) is used for text/plain contents, and the MH show command is used for message/rfc822. (Yes, mhn and mhshow can run show -- and, if the message/rfc822 content is itself a MIME message, show can start mhn or mhshow again. MIME messages can contain other MIME messages, so this behavior makes sense.)

If mhn or mhshow runs out of choices, it complains and quits with an error like:

        mhn: don't know how to display content
             (content application/x-foobar in message 6, part 2)

You might want to add defaults for all content-types -- as a fallback, in case there isn't a command for a particular subtype. Be careful that the defaults won't do anything unexpected with subtypes you haven't heard of (like `x-foobar` above). Because all MIME-encoded messages are 7-bit ASCII text (for now, at least), a safe way to display unknown content-types is with a pager -- for instance, your moreproc. Then, if the pager shows you garbage, you can just quit it and go on to the next part of the message.

(Although I said "all MIME messages are 7-bit for now," some companies use 8-bit or binary content, and more will do that as mail transfer starts to handle 8-bit data. In that case, be sure that the default display program you choose can handle any 8-bit data (including characters like NUL) without locking up your terminal or causing you other grief. It might be better to let mhn or mhshow complain and quit instead of trying to handle all defaults.)

> CAUTION: Some graphic file formats may contain commands that can be dangerous security holes. For instance, PostScript files can hold commands that delete other files.

> File viewers are beginning to have security features built in. For instance, GNU ghostscript has a -safer option to disable the PostScript file-deletion "feature." I can't possibly tell you how safe every file viewing program is. But, if you're concerned about security, you should check before you use an unknown file viewer to display an unknown content.

Here are three example mhn entries to get you started. Remember that you can change the `mhn-` to mhshow- if you're using nmh-1.0 or above:

        mhn-show-image: %pxv '%f'
        mhn-show-audio/basic: %lraw2audio 2>/dev/null | play
        mhn-show-video/mpeg:

Let's look at those three entries.

1. The first entry displays image content-types if there's no entry for the particular subtype used in your message. For example, let's say that your message has an image/x-pbm part, but the only other entry is for image/gif. mhn and mhshow would default to the `mhn-show-image:` display string for general image types.

   The message part (for example, translate 7-bit base64 into an 8-bit binary file) would be decoded and stored in a temporary file. Then the first profile entry would be used, `%f` would get the temporary filename, and the command `xv 'tempfile'` will run. (xv is a shareware image viewing program for the X Window System that knows how to display a lot of graphic file formats.)

   The `%p` in the display string above is an escape that tells mhn and mhshow to list content information on the terminal (like mhn -list or mhlist would), then prompt and wait:

             part 3     image/x-pbm                40K Sample waveform
             Press <return> to show content...

   When you see that prompt, you can skip the part (not run xv to display it) by pressing your interrupt key -- often, that's CTRL-C. (In MH version 6.8.4, you can also type `q` to skip a part.) Or, you can skip the rest of the message and get back to a shell prompt by pressing your QUIT key -- typically CTRL-\. There are two ways to prevent pausing or printing the description of the part:
   - Don't put `%p` in the display string. For example:

                 mhn-show-image: %pxv '%f'

   - Or, give mhn or mhshow the -nopause switch. That's usually done in the command's profile entry, like this:

                 mhshow: -nopause

2. In the second entry, there's no `%f` to stand for the temporary file. So the decoded part is fed to the standard input of the command line given. As always, a Bourne shell executes the command line. (So, the Bourne shell's `2>/dev/null` redirection operator throws away the standard error from the `raw2audio` command.) In this example, the decoded raw audio data will be fed to the standard input of the raw2audio program. The standard output of raw2audio is piped to the play program.

   The escape `%l` tells mhn or mhshow to give a listing, like mhn -list or mhlist would, before it runs the "display" command. Unlike `%p`, the `%l` doesn't prompt and wait for you to press RETURN. So this command will print a one-line listing of the part, then play the sound immediately.

3. The third entry is empty. It makes mhn and mhshow complain "don't know how to display content" when you try to display video/mpeg. Use an entry like this in your personal profile when the system default isn't what you want -- and you don't have anything else acceptable.

> NOTE: A good general-purpose viewer for text, especially text with 8-bit characters, is less. Add `moreproc: less` to your MH profile. MH 6.8.3 comes with less version 177. The latest versions of less are even better at handling international text. Check an up-to-date freeware archive site.

> If you use less, read its manual page for configuration information. In less version 177, you can display the ISO-8859-1 character set by setting the LESSCHARSET environment variable to latin1 and the LESS environment variable to r.

Now is a good time to edit your MH profile (or an MHN or MHSHOW file, if you'd rather) and experiment. The steps below will lead you through the basics of display strings.

The examples above showed the `%f`, `%l` and `%p` escapes. The Table below lists all of the escapes for display strings.

Table: Display String Escapes

`%a`
  Insert parameters from `Content-type:` field.
`%d`
  Insert content description.
`%e`
  Exclusive execution.
`%f`
  Insert filename with content. Take standard input from content.
`%F`
  Insert filename with content. Take standard input from terminal. Exclusive execution.
`%l`
  Display listing, then display content.
`%p`
  Display listing, prompt, then display content.
`%s`
  Insert content subtype.

The `%a`, `%d`, and `%s` escapes are the most straightforward. They pass information about the content to the display program. Let's start with a simple profile entry. Add the following entry to your profile. (In nmh-1.0 and above, you can replace `mhn-` with `mhshow-`.) The command `head -3` should display the first three lines of the content. If your system doesn't have the head(1) command, use the command `sed 3q` instead:

        mhn-show-text/plain: %phead -3

Find a junk text/plain message that you'll want to remove. If your folders are full of various MIME messages, use mhn -list to find a message that has the content types you want. (If you don't have any, send yourself one now.) When you show the message, you should see the header, the prompt from `%p`, and the first three lines of the body:

        % show
        (Message inbox:172)
        Date:    Thu, 25 Aug 1994 06:37:23 PDT
        To:      Randy Wyse <randy@xmaaw.com>
        From:    Jerry Peek <jpeek@jpeek.com>
        Subject: Re: Book outline
        MIME-Version: 1.0

        part       text/plain                2441
        Press <return> to show content...
        First three lines
        of the body
        appear on your screen
        %

Of course, that's not a useful way to display most messages: you'd usually like to see all of the content. But don't delete that entry in your profile yet.

Notice that you didn't give a filename to that head command. mhn or mhshow passed the content to the standard input of the head command. You can give a filename wih the `%f` or `%F` escapes. We'll try that next.

Let's add a more complex entry to your profile. This one runs the pr(1) command, which formats a file for printing. The pr -h option specifies a heading you want to print at the top of a page. But your entry won't send the content to a printer. Instead, it will display the pr-formatted version on your terminal -- using the more(1) pager program. If you don't have more, substitute the pager you use -- like pg or less -- in the command. Use `mhshow-` instead of `mhn-` with nmh-1.0 or above. Be sure to use the escape `%F` (uppercase letter "F"), not `%f`:

        mhn-show-text: pr -h '%d (content-type: text/%s)' '%F' | more

That display string is a command like one you might type at a shell prompt. In fact, before you show a message, try typing that command at a shell prompt to see what it does. Replace the `%F` escape with the filename of a text file in your current directory:

        % pr -h '%d (content-type: text/%s)' 'somefile' | more

        Jul 31 09:17 1994  %d (content-type: text/%s) Page 1

            first screenful of somefile appears...

         -- More --

Because mhn or mhshow wasn't interpreting the `%s` and `%d` escapes, the pr command displays them literally in its heading.

Next, let's see if we can make that profile entry work on a real message. (Note: I'm trying to surprise you here.) Let's show the same message with the content-type text/plain that you did in the previous example:

        % show
            ...

What happens? You should see the first three lines of the body; that's all. Your new `mhn-show-text:` display string should not have been used! Why? Because your profile entry, `mhn-show-text:`, is the default for text contents. It's used when there isn't a profile entry for the particular subtype (in this example, text/plain). But you also have a `mhn-show-text/plain:` entry, so it's used instead.

Now, let's make your profile entry take effect. Change the content-type of the message by editing it with your favorite editor (emacs, vi, etc.):

        % emacs `mhpath cur`

(The ``mhpath cur`` command, in backquotes, puts the absolute pathname of the current message on your editor command line.) Find the `Content-type:` header field and change it to the unlikely value `text/x-haha`. If there's a `; charset=something` parameter on that `Content-type:` header field, remove it. Also, if the message doesn't have a `Content-Description:` field, add one. The two fields should look like this:

        Content-type: text/x-haha
        Content-Description: Test message!

Okay? Now your test message has a different content-type that (probably!) no other profile entry will match. Let's try it:

        % show

        Jan 09 09:17 1995  Test message! (content-type: text/x-haha) Page 1

            first screenful of content appears...

         -- More --

Please do not read the rest of the message yet. Think: Which command is in control of your display at this moment? It's the command started by the shell, which was started by mhn or mhshow. The shell's command line looks like this:

        pr -h 'Test message! (content-type: text/x-haha)' 'filename' | more

mhn or mhshow filled in the escapes as the Table Display String Escapes showed: `%d` got the description, `%s` got the subtype, and `%F` got the temporary filename (shown here as `filename`) that mhn or mhshow chose for holding the content.

The more pager is printing a prompt. You can give more's "quit" command (usually, q) to make the pager stop. Or, you can finish displaying the message and quit the pager. You wouldn't use the mhn/mhshow "quit" command (CTRL-C or CTRL-\) at the `-- More --` prompt because mhn or mhshow isn't in control now. When your display command finishes, mhn or mhshow is in control again: it can show the next part of the message, if any, or quit.

Ready for another example? Before you get started, make a note of the message number you used in the previous example. You'll be using it again in the Section Displaying Other Character Sets.

The Metamail package is a group of programs for working with MIME mail. If it isn't on your system, The Section Metamail explains where to get it. The MH and nmh MIME programs do some of the things that Metamail was written to do: create and understand MIME messages and deal with multipart contents. But mhn and mhshow don't know how to display other contents -- for instance, they can't handle enriched text (the text/enriched content type). The Metamail package comes with a program named richtext that was written for handling the obsolete text/richtext content. The -e option makes versions 2.7 and above of richtext display enriched text.

Let's make a profile entry that runs richtext -e. You may already have a profile entry for displaying enriched text. Search for it with grep -i, which ignores the difference between upper- and lowercase letters as it searches. Fill in the filenames below (though you can leave `$MHN` or `$MHSHOW` as it is; your shell will replace it with the filename from the environment variable):

        % grep -i "text/enriched" $MHN .mh_profile /path/to/library/directory/mhn_defaults

        % grep -i "text/enriched" $MHSHOW .mh_profile /path/to/library/directory/mhn.defaults

If you didn't see a matching line, you're ready to go. If you saw a matching line in your system mhn_defaults or mhn.defaults file, remember that your personal entry will override it. Make an entry like one of the two below. If the richtext program isn't in a standard system directory (in your shell's search path), you'll need to use an absolute pathname:

        mhn-show-text/enriched: %prichtext -e -t '%F'

        mhshow-show-text/enriched: %prichtext -e -t '%F'

The -t option is just for this exercise. It puts asterisks around bold text (`*bold*`) and underscores around italic text (`_italic_`).

Now, you'll need an enriched text message. You can search for a non-multipart message by using pick and its `--content-type` switch. If that doesn't match a message, you can search all the messages, multipart included, with pick -search:

`pick --content-type text/enriched` ...single-part messages
`pick -search "^content-type: text/enriched"` ...multipart, too

No luck? Just create a simple enriched text message with a text editor. The section Sending MIME Mail has an example. Include at least one word in boldface and one word in italic text.

Next, show the message. The `%p` escape should make mhn pause before the text/enriched content. The whole body should be shown at once, without stopping, because you haven't used a pager. richtext has a simple pager built in; you can get it by using the -p option. Now that you've seen what the -t option does, you can remove it (unless you have a dumb terminal, in which case you might want to keep it). If you have the richtext(1) manual page on your system, check it for other interesting options that you might want. Your entry should look something like this (maybe with `mhshow-` instead):

        mhn-show-text/enriched: %prichtext -e -p '%F'

Try it again. Optimize the boldface and italic handling for your display. If you have other displays, you can try showing the message there, too; if you need different settings, make a MIME profile for one display or the other.

I haven't shown the `%a` escape, the `Content-Type:` parameters, yet. There's an example in the getrich script in Section Composing MIME Content.

At this point, if you haven't read the MH mhn(1) manual page, you're ready to read the first few pages of it. Read through the end of the section called "Showing the Contents." It mentions character sets, which will be a good introduction to the next section of this tutorial. Or, if you're using nmh-1.0 or above, read the mhshow(1) manpage.

## Displaying Other Character Sets

Not everyone in the world uses the same character set you do (whatever that is). English-speaking people probably use the us-ascii character set, a series of 128 characters that include every letter used in the English language. Other people use other character sets. One of the most common is iso-8859-1, a 256-character set that handles many non-English languages.

(MH has other support for "international" characters -- that is, non-English characters. See the Section International Character Support.)

There are three things mhn and mhshow need to know to work with multiple character sets:

- What character set is used in a message that you want to show? This is defined by the charset= parameter in the `Content-type:` header field. For example:

            Content-type: text/plain; charset="iso-8859-1"

  Messages that don't have a `Content-type:` header field are, by default (in MIME), text/plain; charset="us-ascii".
- What character set are you using on your terminal? This is defined by the MM_CHARSET environment variable. (The nmh mh-profile(5) manual page describes MM_CHARSET.) If MM_CHARSET isn't defined, the us-ascii character set is assumed.

  Note that MM_CHARSET is not necessarily the character set you'd like to use. It's the character set that your current display can show (and, presumably, that your keyboard can produce).

  When you send a message that has any 8-bit (non-ASCII) characters, the value of MM_CHARSET is copied into the charset= parameter.

- If you have a message that isn't in the character set specified by MM_CHARSET (in other words, a different character set than the one on your terminal), how should mhn or mhshow show it? The `mhn-charset-` profile entry (if any) controls that.

  For example, if your current character set is us-ascii and you try to show an iso-8859-1 message, mhn will search your profiles for the entry `mhn-charset-iso-8859-1:`; mhshow will search for `mhshow-charset-iso-8859-1:`. The entry should have a command string that will display the character set. And that's what this section is about.

For comparison, here are three typical `mhn-charset-iso-8859-1:` entries:

        mhn-charset-iso-8859-1: xterm -fn '-*-*-medium-r-normal-*-*-120-*-*-c-*-iso8859-1' -e '%s'
        mhn-charset-iso-8859-1: %s | iso2asc 0
        mhn-charset-iso-8859-1: %s

All examples use the `%s` escape; it is required.

1. The first entry starts xterm, an X Window System terminal emulator. This opens a new window on your X display with a font that includes all iso-8859-1 characters. (Although I haven't seen the problems myself, I've heard that some X fonts which are labelled as iso8859 don't contain all of the ISO-8859 characters.)
2. The second entry is for people who can't display iso-8859-1 because they have an ASCII-only terminal. It uses the iso2asc ISOto-ASCII converter program. (If iso2asc isn't on your system, you can get a copy from this book's online archive. The code is in ../examples/mh/iso2asc/iso2asc.c, and there's extensive documentation in ../examples/mh/iso2asc/iso2asc.txt.
3. The third example entry is the simplest. It tells mhn that (basically) your terminal can handle iso-8859-1 characters with no other help. So mhn will run the default display program.

To set up for the next exercise, please put the following two entries in your profile. (If you did the tutorial in the previous section, you probably already have the `text/plain` entry.) Do the usual `mhn`/`mhshow` swap for nmh-1.0+:

        mhn-show-text/plain: %phead -3
        mhn-charset-x-upperlower: %s | tr '[a-z]' '[A-Z]'

If you know the UNIX tr(1) command, the second entry should look familiar. It translates all lowercase characters to uppercase.

Next, edit the text/plain message you used in the previous section. Change the `Content-type:` field to read:

        Content-type: text/plain; charset="x-upperlower"

If the message body has any non-ASCII characters, please delete them for this example. (Sorry.) The message should have only upper- and lowercase English letters (A-Z and a-z), digits and punctuation.

Now on to the example. "What is this x-upperlower?," you're probably asking. To make a simpler example for people who are new to the idea of displaying other character sets -- and to be sure no one can accuse me of character-set favoritism `:-)` -- I've just invented a new character set named x-upperlower. The x-upperlower character set has all the English letters, both upper- and lowercase. (Actually, it's the us-ascii character set. But we'll ignore that.)

Here's our problem in this exercise. You'll play the part of a person with an old computer display. Your terminal can't display lowercase characters. It's ALL UPPERCASE. When people with these old terminals display a message that has this field:

        Content-type: text/something; charset="x-upperlower"

they can't see the lowercase letters. They need a `mhn-charset-x-upperlower:` profile entry to translate the lowercase letters into something they can see: that is, to translate the lowercase letters into ALL UPPERCASE.

Show the text/plain message that you just edited. The content (the body, that is) should come out ALL UPPERCASE. That's because the message has the parameter charset=x-upperlower in it. But the MM_CHARSET environment variable on your terminal isn't set to x-upperlower. So, mhn uses your `mhn-charset-x-upperlower:` profile entry to translate the x-upperlower characters to a character set you can see. (Please ignore those lowercase letters in the header. `;-)` The MIME RFC 2045 spec only applies to the message body.)

        % show
        (Message inbox:172)
        Date:    Thu, 25 Aug 1994 06:37:23 PDT
        To:      Randy Wyse <randy@xmaaw.com>
        From:    Jerry Peek <jpeek@jpeek.com>
        Subject: Re: Book outline
        MIME-Version: 1.0

        part       text/plain                2441
        Press <return> to show content...
        FIRST THREE LINES
        OF THE BODY
        APPEAR ON YOUR SCREEN IN UPPERCASE
        %

To build its command line for displaying the message in a different character set, mhn or mhshow:

1. Finds the `mhn-show-` or `mhshow-show-` entry for this content type (here, `mhn-show-text/plain:`).
2. Finds the `mhn-charset-` or `mhshow-charset-` entry for this character set. It replaces the `%s` with the command to show the content.

In this example, the command line mhn uses with your test message is:

        head -3 | tr '[a-z]' '[A-Z]'

(Also look back at the profile entries, if you'd like.) The `head -3` command displays the first three lines of the content. That output is piped to `tr '[a-z]' '[A-Z]'`, which translates the content into uppercase for your ancient uppercase-only terminal.

Now, let's say you've moved to a terminal that can show both upper- and lowercase letters. To tell mhn/mhshow about this, you need to set the MM_CHARSET environment variable to the name of your character set -- that is, to x-upperlower. This syntax works for both MH and nmh (believe it or not!!):

`% setenv MM_CHARSET "x-upperlower"` ...csh-type shell

`$ MM_CHARSET="x-upperlower"` ...sh-type shell
`$ export MM_CHARSET`

Now when you show your text/plain message, two interesting things should happen. Try it:

        % show
        (Message inbox:172)
        Date:    Thu, 25 Aug 1994 06:37:23 PDT
        To:      Randy Wyse <randy@xmaaw.com>
        From:    Jerry Peek <jpeek@jpeek.com>
        Subject: Re: Book outline
        MIME-Version: 1.0

        All the lines
        of the body
        appear on your screen
        with no "Press <return>..." prompt.
        %

Because your terminal can display x-upperlower, and because the message is plain text, MH/nmh don't need a special display command to show it on your terminal. It ignores the `%phead -3` command for text/plain. It shows the message as it would show a non-MIME message. In fact, show doesn't even invoke mhn or mhshow for this message.

If you want to see what mhn/mhshow would do with the message if show had invoked mhn/mhshow, try it. (Just type mhn or mhshow at the shell prompt.) mhn should use your `mhn-show-text/plain:` profile entry, and mhshow should use your `mhshow-show-text/plain:` entry. It'll prompt you to `Press <return>...`, then run `%phead -3` to show the first three lines.

To avoid confusion later, you should probably take the `mhn-show-text/plain:` display string out of your profile. Also set the MM_CHARSET environment variable to the character set your terminal can actually handle -- for example, us-ascii or iso-8859-1.

## Composing MIME Content

The Sections Sending MIME Mail and Composing and Sending MIME Messages introduced message composition with mhn and mhbuild. If you haven't used mhn or mime (which invokes mhbuild) from a `What now?` prompt, please do that before you work through this section of the tutorial. In this section, to show you more of what happens "under the hood" as mhn and mhbuild compose a draft, you'll start by running them from a shell prompt instead of from the usual whatnow program prompt. Doing things this way should also help people who want to use mhn or mhbuild to build draft messages for their own email programs.

If you use a window system, open a new window for this part of the tutorial. Otherwise, start a subshell by typing the name of your shell at the prompt. For example, if you use the C shell:

        % csh
        %

The separate window, or subshell, will make it easy to undo the changes to your environment in this tutorial.

Make a little shell script named showenv that shows the UNIX environment. If your system has the env(1) command, use it; otherwise, you probably have printenv(1). Put that command in the showenv file and make it executable. Then run the shell script:

        % vi showenv
        ...put these two lines in the file:
        #!/bin/sh
        env
        % chmod 755 showenv
        % showenv
        DISPLAY=:0.0
           ...
        VISUAL=/usr/ucb/vi

(If the current directory isn't in your search path, you may need to type `./showenv` to run the script.)

Next, compose a draft message in the usual way, with the comp program. Make a multipart message with three parts: enriched text entered from the draft (a `#<` directive), a non-text file, and the output of the who(1) program. Then get to a `What now?` prompt and wait; do not run mhn or mime to encode the draft.

        % comp
        To: someone
        cc:
        Subject: Testing mhn
        ------
        #<text/enriched
        The stuff in this message is <bold>junk</bold>,
        just some stuff I'm using to test <italic>mhn</italic>.
        #text/plain [The output of who(1)] |who
        #application/octet-stream [The who(1) program binary] /bin/who

        CTRL-D

        What now?

At this point, the whatnow program is prompting you. If you haven't read the overview of what's happening at this point, you should probably look at the Section What now? -- and the whatnow Program.

Now, a question for you MH users. (nmh users, browse through and see why nmh created the mhbuild command... to avoid the messy situation with mhn, I suspect.) What makes mhn act the way it does when you type mhn at a `What now?` prompt? (Note: don't run it yet!) When you run mhn from a shell prompt, it displays a message. But when you run mhn from a `What now?` prompt, it encodes a message. What's happening? The difference is in the UNIX environment set by the whatnow program. Let's look at the environment. Tell whatnow to run your showenv script:

        What now? edit showenv
        DISPLAY=:0.0
            ...
        mhdraft=/u/joe/Mail/drafts/3
            ...

(If you get a "command not found" error, use a pathname -- for example, `edit ./showenv`.) The whatnow program has stored the absolute pathname of the draft file in the mhdraft environment variable. When you run mhn from within whatnow, mhn sees the mhdraft environment variable and becomes a message-encoder instead of a message-displayer. (nmh users: this is ugly, isn't it? mhbuild doesn't need this trick.)

Now quit and leave your unencoded draft message where it is. Then change your current directory to the one where your draft file is. (Note that older versions of whatnow don't show the draft pathname as they quit; if you don't know where your draft is, look back at the value of the mhdraft variable.) List the directory and look for your draft:

        What now? q
        whatnow: draft left on /u/joe/Mail/drafts/3
        % cd /u/joe/Mail/drafts
        % ls
        ...    3   ...

If you run showenv again, you'll see that the mhdraft variable isn't set. Because we need mhn to encode the draft, add mhdraft to your environment. Use the pathname to your draft message:

`$ mhdraft=/u/joe/Mail/drafts/3` ...sh-type shells
`$ export mhdraft`

`% setenv mhdraft /u/joe/Mail/drafts/3` ...csh-type shells

> NOTE: If you're using nmh, you have another choice. Instead of setting mhdraft, you can simply use mhbuild and give a filename on the command line -- for example, mhnbuild draftfile.

Now you're ready to encode the draft. MH users, run mhn (nmh users, run mhbuild):

        % mhn /u/joe/Mail/drafts/3
        composing content text/plain from command
                who

If you get errors -- for example, mhn can't read the file /bin/who because your system administrator has denied read permission -- edit the draft file and change the directives. (Use your favorite editor and the draft filename, like this: `vi 3`.) Then rerun mhn -- and be sure you don't get the error.

Now list your directory again. You should see the encoded draft and another file: the original draft with the directives in it. Read them both with a pager program like more(1):

        % ls
        ...   ,3.orig  ...  3   ...
        % more ,3.orig 3
        ::::::::::::::
        ,3.orig
        ::::::::::::::
        To: someone
        Subject: Testing mhn
        ------
        #<text/enriched
        The stuff in this message is <bold>junk</bold>,
        just some stuff I'm using to test <italic>mhn</italic>.
        #text/plain [The output of who(1)] |who
        #application/octet-stream [The who(1) program binary] /bin/who
         -- More-- (Next file: 3)
        ::::::::::::::
        3
        ::::::::::::::
        To: someone
        Subject: Testing mhn
        MIME-Version: 1.0
        Content-Type: multipart/mixed; boundary="----- =_aaaaaaaaaa0"
        Content-ID: <904.783368683.0@joebob.kermit.tx.us>

        ------- =_aaaaaaaaaa0
        Content-Type: text/enriched; charset="us-ascii"
        Content-ID: <904.783368683.1@joebob.kermit.tx.us>

        The stuff in this message is <bold>junk</bold>,
        just some stuff I'm using to test <italic>mhn</italic>.

        ------- =_aaaaaaaaaa0
        Content-Type: text/plain; charset="us-ascii"
        Content-ID: <904.783368683.2@joebob.kermit.tx.us>
        Content-Description: The output of who(1)

        joe      console Oct 28 06:23
        joe      ttyp0   Oct 28 06:24   (:0.0)
        joe      ttyp1   Oct 28 06:24   (:0.0)
        joe      ttyp2   Oct 28 06:24   (:0.0)

        ------- =_aaaaaaaaaa0
        Content-Type: application/octet-stream
        Content-ID: <904.783368683.3@joebob.kermit.tx.us>
        Content-Description: The who(1) program binary
        Content-Transfer-Encoding: base64

        gQMBCAAACZAAAARAAAAASAAAAAAAACAAAAAAAAAAAAC8ECAA0AOgQJIDoESVKiAClAKgBJQCQAoX
           ...
        AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==

        ------- =_aaaaaaaaaa0--

Now assume you've changed your mind about your draft. You'd like to include another part: your signature file. Editing the draft (the file 3) can be a hassle; it isn't always easy to create MIME-format messages by hand. But you can replace the encoded draft with the original file that has the directives. Then edit the draft and re-run mhn. Let's do it:

        % mv ,3.orig 3
        % vi 3
            ...add this directive to the end:
        #text/plain [signature] /u/joe/.signature
        % mhn /u/joe/Mail/drafts/3
        composing content text/plain from command
                who
        % more 3
            ...re-encoded draft appears...

That's enough experimenting with mhn+mhbuild at a shell prompt. Unless you want to keep working from a shell prompt, close the separate window or end the subshell by typing exit:

        % exit
        %

You should be back at your original shell in the original current directory. The mhdraft environment variable should not be set. (If you want to check, run showenv again.) Now you're ready to experiment with MIME directives.

Let's start by looking at the type directives, `#type/subtype`. (If you need a reminder of the syntax, check the first three Tables in the Section Syntax of MIME Draft Directives.) mhn and mhbuild get the content for those directives by looking at the last argument:

- If the last argument is a filename, mhn or mhbuild reads it.
- If the last argument starts with a vertical bar (`|`), mhn or mnbuild runs the program named in the argument. The standard output of that program is used as the message content.
- If the last argument isn't a filename or command (starting with `|`), mhn or mhbuild looks for a composition string in the profiles. First, it looks for `mhn-compose-type/subtype:` or `mhbuild-compose-type/subtype:`. If mhn or mhbuild can't find that, it looks for `mhn-compose-type:` or `mhbuild-compose-type:`. It that isn't found, mhn/mhbuild complains.

  mhn and mhbuild handle any escapes in the composition string. Then the string is run with a Bourne shell; the standard output of this command is used as the content.

  The rest of this section is about these composition strings.

The Table below lists the escapes that work in composition strings.

Table: Composition String Escapes

`%a`
  Insert parameters (example: `x-conversions=compress`) from directive.
`%f`
  Insert temporary filename where content should be written.
`%F`
  Insert temporary filename where content should be written. Don't redirect standard output to content.
`%s`
  Insert content subtype.

To make some use of those parameters, let's assume that you have a program for entering and editing enriched text. You'd like mhn or mhbuild to run your editor, let you compose the part, and then insert the edited part when the editor quits. Call the editor program getrich.

How do you give the editor a filename to edit from the composition string? The `%f` and `%F` escapes do that. Both escapes give the filename, but one of them changes the way that mhn/mhbuild handles output. Normally -- if you don't use any escapes, or if you use any escape except `%F` -- the standard output of the composition command is directed to the message content. This is good; it lets the output of the command be sent in the mail message. But you won't want that to happen when you use an interactive text editor. If it did, you couldn't see the screen or prompts that the editor makes: those would be redirected into the content, instead of shown on your screen! Notice in the previous Table that the `%F` escape is replaced with a filename and it tells mhn not to redirect the composition command standard output to the content. That's what you want in a case like this.

Add a composition string to your profile that runs getrich. The script will need some parameters, so add the escapes shown (and change `mhn-` to `mhbuild-` if you should):

        mhn-compose-text/enriched: getrich '%F' %a

If getrich was supposed to make more than one subtype (as in an `mhn-compose-text:` or `mhbuild-compose-text:` composition string), it would probably need to know what subtype it should produce. In that case, you would also put a `%s` parameter in the composition string. In this example, though, `%s` would be useless because getrich will always make text/enriched content.

To get started, make a simple getrich that displays the parameters passed to it and then outputs some dummy enriched text. (It won't start a text editor yet.) A Bourne shell script is shown below; you can also get it from this book's online archive in examples/mh/bin/getrich. You could write the script in Perl or some other language if you'd rather.

        #!/bin/sh
        ### getrich - create enriched text for mhn
        ### Usage (in MIME profile):   mhn-compose-text/enriched: getrich '%F' %a

        # show command line arguments.  Write to standard error so
        # mhn won't add this text to the message content:
        echo "getrich: got these arguments: $*" 1>&2
        # Output two lines of enriched text:
        echo 'This is a <bold>bold</bold> word from the "getrich" script.
        This one is <italic>italic</italic>.' > "$1"
        # Make sure mhn keeps the output:
        exit 0

The two-line `echo` command at the end of the script writes two lines into `$1`, which is the filename from the first `%F` parameter. Use chmod +x to make the getrich script executable.

Next, make a short file named sampletext with two or three lines of enriched text in it.

Now, make a new draft with three different flavors of enriched text directive. One includes text you type into the draft, one reads the short enriched text you created above, and the third runs getrich. (If your directive doesn't give a pathname to another directory, mhn and mhbuild will read sampletext from the current directory.)

        #<text/enriched [text typed into the draft]
        The stuff in this part is <bold>junk</bold>,
        some text I'm using to test <italic>mhn</italic>.
        #text/enriched [text from a file] sampletext
        #text/enriched [text from getrich]

After you run mhn or mhbuild, take a look at the draft. It should contain the text from all three parts.

        What now? edit mhn   (on nmh, type mime)
        composing content text/enriched from command
                /tmp/getrich '/tmp/mhnb00987'
        getrich: got these arguments: /tmp/mhnb00987

        What now? list
           ...
        ------- =_aaaaaaaaaa0
        Content-Type: text/enriched; charset="us-ascii"
        Content-ID: <987.783374275.1@ora.com>
        Content-Description: text typed into the draft

        The stuff in this part is <bold>junk</bold>,
        some text I'm using to test <italic>mhn</italic>.

        ------- =_aaaaaaaaaa0
        Content-Type: text/enriched; charset="us-ascii"
        Content-ID: <987.783374275.2@ora.com>
        Content-Description: text from a file

        This is sample enriched text from the <italic>sampletext</italic>
        file.  Are we having fun yet???

        ------- =_aaaaaaaaaa0
        Content-Type: text/enriched; charset="us-ascii"
        Content-ID: <987.783374275.3@ora.com>
        Content-Description: text from getrich

        This is a <bold>bold</bold> word from the "getrich" script.
        This one is <italic>italic</italic>.

        ------- =_aaaaaaaaaa0--

        What now? quit delete

(If your lproc is set to use show when you list the draft, you won't see the encoded draft. In that case, you can see the encoded draft with the more(1) pager by typing `edit more`.)

The last line of getrich sets a zero exit status. This is important: if the composition command returns a nonzero status, mhn/mhbuild will abort and not encode the draft. If you'd like to experiment, change the `exit 0` to `exit 1`, compose a new message for getrich and run mhn/mhbuild:

           ...
        #text/enriched [output from getrich script?]
        CTRL-D

        What now? e mhn   (on nmh, type mime)
        composing content text/enriched from command
                /tmp/getrich '/tmp/mhnb00993'
        getrich: got these arguments: /tmp/mhnb00993
        Exit 1

        What now?

Let's hack getrich to use the parameters passed from the `%a` escape and to actually run a text editor. The order of the composition string parameters is important. You put the content filename (`%F`) first, before the optional parameters. The script will grab the filename from its first argument; the rest of the arguments will be parameters from the directive. Here's the revised getrich:

        #!/bin/sh
        ### getrich - create enriched text for mhn
        ### Usage (in mhn profile):   mhn-compose-text/enriched: getrich '%F' %a

        # Grab content filename:
        file="$1"; shift
        # If there are parameters, "handle" them:
        for param
        do
                echo "getrich: what is this '$param' parameter for??" 1>&2
        done
        # Create content by running enriched text editor (actually, vi).
        vi "$file"
        # Make sure mhn keeps the output:
        exit 0

If you use a window system, you might change the editor line to open a separate window with a text editor in it. For instance, in the X Window System you could open a new xterm running the vi editor with:

        xterm -e "vi $file"

Now compose a draft with a directive that will run getrich. Add some parameters if you'd like to. (If you don't add parameters, the for loop in getrich won't run to process the parameters -- and you won't see the sassy remark about each parameter.)

        % comp
           ...
        #text/enriched; x-temperature="98.6 F"; x-foo=bar [text from getrich]
        CTRL-D

        What now? edit mhn   (on nmh, type mime)

        composing content text/enriched from command
                /tmp/getrich '/tmp/mhna01003' x-temperature="98.6 F" x-foo="bar"
        getrich: what is this 'x-temperature=98.6 F' parameter for??
        getrich: what is this 'x-foo=bar' parameter for??
           ...editor starts on the empty /tmp/mhna01003 file
        What now? list
            ...
        MIME-Version: 1.0
        Content-Type: text/enriched; x-temperature="98.6 F"; x-foo="bar";
                charset="us-ascii"
        Content-ID: <1003.783375452.1@ora.com>
        Content-Description: text from getrich

        This is the enriched text I'm entering with <italic>vi</italic>.

        What now?

When you list the draft, you should see the enriched text that you entered with the editor.

This simple version of getrich displayed the x-temperature and x-foo parameters you passed to it. The script could have used them for something -- for example, post-processing the output with a command like compress -- if you set it up that way. Notice that mhn or mhbuild also copied the parameters to the `Content-Type:` header field (and added a charset parameter, too).

## Storing Content

As the Section Decoding and Storing MIME Messages explained, mhn and mhstore can decode message contents and store them in a file. Note that storing contents isn't the same as the same as caching contents. The cache is used for local copies of external body parts; it's for internal MH and nmh use. On the other hand, storing contents has the same purpose as storing a standard UNIX file: so you can use the stored content with any program you want to.

Because the Section Decoding and Storing MIME Messages introduced MIME message storage with examples from configuration files, this section will be mostly reference.

mhn -store and mhstore use the profile entries `mhn-store-type/subtype:` and `mhstore-store-type/subtype:`, respectively. otherwise they use `mhn-store-type:` and `mhstore-store-type:`. If a profile entry isn't found, the following defaults are attempted:

- If the content is application/octet-stream, mhn/mhstore checks for more parameters. (The viamail program adds these parameters.)
  If the content has the parameter `type=tar`, a filename that ends in .tar will be chosen. If the content also has the parameters `conversions=compress`, `x-conversions=x-compress`, or another combination, the filename chosen will end in .tar.Z.
- Otherwise, if the content is a message, mhn/mhstore uses the formatting string `+`, which stores the content as a new message in the current folder. As the Section Partial Messages shows, this also works when you're combining partial messages.
- Otherwise, the string `%m%P.%s` is used.

The string can be one of several types, as the Table below shows.

Table: MIME Storage Formatting Strings

`|command`
  Pass content to standard input of `command`. Can use escapes in the Table MIME Storage Escapes.
`+folder`
  Write content to `folder` (default: current folder)
`@folder`
  Write content to subfolder `folder` (see the Section Relative Folder Names)
`-`
  Write content to standard output of mhn (Bug: this does not work in MH 6.8.3 or versions of nmh before 0.18. See the workaround in the mimecat script.)
`pathname`
  Write file to `pathname`. If `pathname` starts with `/`, that directory is used; else, directory named by `mhn-storage:` (MH) or `nmh-storage:` (nmh-1.0+); else current directory. Can use escapes in the Table MIME Storage Escapes.

The next Table lists the escapes you can use in the `|` (pipe) and `pathname` storage formatting strings.

Table: MIME Storage Escapes

`%a`
  Parameters from `Content-type:` (only for `|`-type formatting strings).
`%m`
  Message number
`%P`
  Part number with leading dot. (Usually used in middle of formatting string.) Ignored if message not multipart.
`%p`
  Part number without leading dot. Ignored if message not multipart.
`%s`
  Content subtype.

Here are some examples.

- To store text data in files named with the message number, an optional part number (for multipart messages), and an extension named for the subtype do the following:

            mhn-store-text: %m%P.%s

  (For nmh-1.0+, use `mhstore-store-text:`.) For example, if message 23 is text/plain, it would be stored in a file named 23.plain. If you were storing the multipart message 67, part 1.3 is text/enriched and part 3 is text/x-pgp; the files would be named 67.1.3.enriched and 67.3.x-pgp.
- The next profile entry stores audio/basic in a file named with the message number, an optional part number, and the extension .au. (For mhstore, use `mhstore-store-audio/basic:`.) Before storing the file, mhn and mhstore run the raw2audio command to convert the data:

            mhn-store-audio/basic: | raw2audio -e ulaw -s 8000 -c 1 > %m%P.au

- The next example runs a processor script you wrote named cruncher. On its command line, your program reads the content type, subtype, and any content parameters. It reads the decoded content from its standard input. (Again, change `mhn-` to `mhstore-` if you use nmh-1.0+.)

            mhn-store-text: | cruncher text %s %a

  If cruncher were a Bourne shell script, `$1` would be text, `$2` might be (for example) plain or enriched, and the other arguments (if any) might look like type=tar or x-conversions=compress.

## Caching External Body Parts

When mhn, mhlist, mhshow, or mhsstore is run, and the message has an external body part with a `Content-ID:` field, the content might be read from a cache. The Section Cached Contents has a listing of a cache and examples of mhn and mhshow using caches. This section has tips for configuring the caches.

Each user can access two cache directories, public and private.

The Public Cache

The public cache directory is usually named in your system's mhn_defaults or mhn.defaults file. That lets everyone on the system share the public cache. If you need to set a different public cache directory, put an `mhn-cache:` or `nmh-cache:` entry in your MH profile or MIME profile. And, of course, any user can ignore the public cache by setting the -rcache and -wcache switches to private or never.

If everyone on the system is allowed to write to the public cache, the system administrator needs to make its directory world-writable (mode 777). mhn and mhshow will create all the cache files read-only (mode 444), but that doesn't stop people from removing cached files when they shouldn't. On many UNIX systems, the owner of a directory can set its sticky bit by using chmod 1777. On those systems, people will be able to add files to the directory, but they can't remove or rename files that were created by someone else.

Computers with networked filesystems can share a public cache between hosts. Any user on a host sharing that filesystem can save a public content. The mhn_defaults and mhn.defaults files on all those systems need to point to the right directory. Of course, if the network connections are slow, you may be undoing some of the benefit of cached contents: fast access to message parts without network delays.

The Private Cache

Each user can have her own private cache. Unless you name one in the `mhn-private-cache:` or `nmh-private-cache:` profile entry, the default will be a subdirectory of your MH directory named .cache. The default name starts with a dot (`.`) to "hide" it from the ls command. But the default cache will still show up in the folders command output. I put my cache somewhere else:

        mhn-private-cache: /home/jerry/tmp/mhn-private-cache

If you don't want to use space in your home directory, you could try putting the cache in a subdirectory of a system temporary-file directory like /tmp/mhn-cache-yourname. Remember to protect the directory (chmod 700) if you might be storing private messages. Also remember that a cache directory in /tmp may be deleted or emptied by the system. That may be just what you want, of course. But if you want your cache to be more permanent, put it somewhere else and use the cleanup scripts below.

Cleaning the Caches

Cached copies of external body parts can get out of date. For instance, a cached copy of today's weather map probably wouldn't be much use by next week. mhn and mhshow are good at writing to the caches, but they don't have a way to clean them up. This section has a few ideas.

The MIME standard, RFC 2045, describes an optional expiration= parameter for external body parts. The parameter says that after the date given, the content might be missing or invalid. Unfortunately, not all messages use that parameter. Even if they do, mhn and mhshow don't save that date anywhere that I can see. Tidy users can get the expiration date from mhn -list -verbose or mhlist -verbose and remove old cached contents. (If your system has many cache "housekeepers" like that, setting the sticky bit on the public cache directory might be a bad idea!) You'll probably still get plenty of old files in your caches.

Unless someone goes through the caches by hand every week or two to clean out old files, the best way to clean up is with a script run by cron(8) or at(1). The script can use find(1) to search for and remove files that are more than some number of days old. With its -mtime switch, find will remove the files based on the date they were stored. Or, if you use the -atime switch, the test will be on when the file was last accessed. If a cached file hasn't been accessed for a while, it might be a safe bet to remove. Finally, you might use the size of the file (find -size) as a factor. Big files take longer to copy over the network, so you could keep them longer. Of course, big files take more disk space, so you might want to delete them sooner. Whatever. `:-)`

If you have a better scheme for cleaning out caches, please tell me about it! Just click on the email link at the end of this section.

## Getting External Body Parts by FTP

mhn, mhshow and mhstore don't run ftp(1) to get external body parts with ftp access types. Instead, they have their own simple built-in FTP code. You can also name your own FTP program at the `mhn-access-ftp:` profile entry; in nmh-1.0 and above, the entry is `nmh-access-ftp:`. If there's a program named in `mhn-access-ftp:`, it will be used instead of the built-in FTP code.

Why would you want to write your own program? You might be on a site that's behind an Internet firewall -- and need access beyond the firewall. You might be on a site with no Internet access -- and need to send a request to an email-to-FTP gateway like ftpmail. Or your host might not support a "sockets" interface.

The mhnftpmail shell script below is an example of a program you can run from an `mhn-access-ftp:` or `nhm-access-ftp:` profile entry. It uses the seven parameters passed from MH or nmh to build an email message to an ftpmail server. You can customize it for any ftpmail server you want to use. (ftpmail is explained in detail in O'Reilly Media's book Managing Internet Information Services, now out of print.)

This example shows the script being called as I show a message. The script doesn't have to be this verbose: edit it if you'd like to.

        % next
           ...
        Retrieve book.catalog (content 1.2)
            using anonymous FTP from site ftp.ora.com? y
        mhnftpmail: sending mail to 'ftpmail@online.ora.com':

        open ftp.ora.com anonymous jpeek@jpeek.com
        binary
        mime
        chdir pub
        get book.catalog

        The 'Exit 1' error below makes sure mhn won't try to cache the missing response:
        Exit 1
        mhn: don't know how to display any of the contents
             (content multipart/alternative in message 55)

The script returns a nonzero exit status to be sure that MH and nmh won't think that the FTP job succeeded. You'll have to manually handle the file that ftpmail sends.

Here's the mhnftpmail Bourne shell script. You can also get the script from this book's online archive in examples/mh/bin/mhnftpmail.

        #!/bin/sh
        ### mhnftpmail - submit ftpmail request for mhn
        ### Usage (in mhn profile):   mhn-access-ftp: mhnftpmail
        # Customize this script for your site and ftpmail server.

        mhmail=/usr/local/mh/mhmail     # Absolute path to mhmail program

        ftpmail='ftpmail@online.ora.com'
        #ftpmail=yourname@yourhost      # Uncomment for debugging

        # Build message to send in $msg (multi-line) shell variable.
        # arguments passed by mhn:  $1 - domain name of ftp site, $2 - username,
        # $3 - password, $4 - remote directory, $5 - remote filename,
        # $6 - local filename (not used here), $7 - "ascii" or "binary":
        msg="open $1 $2 $3
        $7
        mime
        chdir $4
        get $5"

        # Say what's happening (on standard error):
        cat << END_OF_STUFF 1>&2
        `basename $0`: sending mail to '$ftpmail':

        $msg

        The 'Exit 1' error below makes sure mhn won't try to cache the missing response:
        END_OF_STUFF

        # Send mail, exit with status 1 so mhn won't think we have the content.
        $mhmail "$ftpmail" -body "$msg"
        exit 1

## Building MIME Drafts Automatically

By default, when you compose a message, comp (actually, the whatnow command) doesn't run mhn or mhbuild. If you forget that as well, you can send a message full of directives instead of the encoded body parts you want. The easy way to take care of this is by adding an MH profile entry that runs mhn, mhbuild, and/or another program you choose to build the MIME draft. The program you name will be run when you type send or push at the `What now?` prompt. (If you use push, the automatic processing runs before the message delivery is moved to the background. You'll be able to handle any interactive prompts or problems first.) Here are the methods for MH and nmh:

- In MH, add an entry like this to your MH profile:

            automhnproc: automatic-processing-command

  If the automatic-processing-command named is mhn, when you type send or push at the `What now?` prompt, mhn will run automatically. If you name another program (like a script you've written), it will run instead.

  Note that the program in the `automhnproc:` entry will not run if you type edit mhn at the `What now?` prompt.

- In nmh, you have two entries to choose from. (Actually, you have a third choice: using an MH-style `automhnproc:` entry. It still works -- in nmh-1.0 and before, at least.)

  The following MH profile entry will run mhn automatically when you type send or push at the `What now?` prompt:

            automimeproc: 1

  If you want to run a program other than mhn automatically, you should also add an nmh profile entry like this:

            buildmimeproc: automatic-processing-command

  That command will run instead of mhbuild.

The automhnproc has another advantage if you use 8-bit (non-ASCII) characters: mhn will use the minimum required character set (for example, us-ascii for English-only) when it sends the message.

Automatic MIME processing isn't right for everyone. If you send drafts that have lines starting with a hash mark (`#`) which aren't directives, mhn will complain about an "unknown directive." You'll have to edit the message and change the non-directive lines starting with `#` to start with `##` instead (or, use a script like automhn).

You can use any program you want for the `automhnproc:` or `buildmimeproc:`. For instance, if you've switched to MH from another MIME MUA, you could use the familiar Metamail program and your own .mailcap file (with some hackery, that is). The program you use should be able to take the absolute pathname of the draft from its first argument, encode the draft, and write it back. It should also return a zero exit status if it succeeded. Otherwise, if whatnow gets a nonzero status from the automhnproc, it will prompt you again with `What now?`.

Here's a simple but very useful example: a script named automhn. One problem with using mhn or mhbuild as the automhnproc or buildmimeproc is that they complain about lines in a draft that start with `#` but aren't MIME directives. The automhn script reads the draft message and adds an extra `#` to the start of any line which doesn't seem to be a directive; then mhn or mhbuild will change the `##` back to a single `#` when it sends the message. For example, automhn will convert this draft message:

        To: joe
        Subject: My new script
        -------
        #<text/enriched
        Hey Joe, here's my new shell script.  It wipes out your directory!
        <BOLD>Cool</BOLD>, eh, d00d?
        #<text/plain [nukeall script]
        #!/bin/sh
        # Clean thoroughly:
        rm -rf $HOME  # Maybe we should try / next time...

Into this:

        To: joe
        Subject: My new script
        -------
        #<text/enriched
        Hey Joe, here's my new shell script.  It wipes out your directory!
        <BOLD>Cool</BOLD>, eh, d00d?
        #<text/plain [nukeall script]
        ##!/bin/sh
        ## Clean thoroughly:
        rm -rf $HOME  # Maybe we should try / next time...

You can adapt automhn to do any automhnproc or buildmimeproc work you need to do. There's more information, and a link to the downloadable script, in the Section Explanation of automhn.