MIME message format can be complex. Luckily, you don't need to enter all of the MIME stuff by hand. Instead, as you've probably read in earlier sections of this book, you enter special directives in your draft; the MH mhn command or the nmh mhbuild command checks the directives and converts them into a MIME-formatted message. The Section Sending MIME Mail introduces MIME message composition; Sections Building MIME Drafts and Recovering MIME Drafts cover the processing of MIME message drafts. That information isn't repeated here.
Some people like examples, then explanation. This section is for you to "learn by doing." Other people like to read the rules before they try the examples. If you'd rather see the rules first, read the Section MIME Draft Directives and see the tables of directives in the Section Syntax of MIME Draft Directives.
You shouldn't use these examples exactly as they're shown. You'll need to substitute your own messages and your own files. If you can't do an example because your terminal doesn't support that type of content -- for instance, you're using a plain character terminal that can't show images -- try the example once, anyway, to see what happens. If you have access to several kinds of displays, try the customization steps in the Section MIME Configuration; these will make each display work the best it can.
Find a MIME-equipped friend to exchange messages with. You can have some fun by finding images and sounds from your local host (with the "fast find(1)" or locate(1) commands) or from anonymous FTP archives on the Internet (with archie or a Web search engine). Search for files whose names end with .gif and audio filenames ending in .au. There are also some interesting files on ftp://thumper.bellcore.com/pub/nsb/.
(If you always use an X Window System display, you might also want to try exmh. It handles MIME in a different and much more graphical way.)
Before we get into the examples below, here are rough steps to follow as you compose and encode the test messages.
Text and a Picture
This is a typical MIME message. It has two parts: plain text and a non-ASCII image. If you found an image file in GIF format, you can send it in this message. Otherwise, pick some non-ASCII data; choose the correct content-type from the Section Some MIME Content Types and Subtypes.
If your terminal's character set isn't us-ascii, be sure to use some non-ASCII text (for instance, accented characters) in your message. Then look at the encoded message; it should have a quoted-printable encoding now. The Section Choosing MIME Encodings has more information about encoding non-ASCII text.
% comp To: "V.I. Editor" <firstname.lastname@example.org> cc: me Subject: something ------- Dear someone, Here's a picture of something. Blah blah blah... #image/gif [Picture of something] /u/joe/testfiles/something.gif CTRL-D ------- What now? edit mhn What now? list ..or.. edit more ...message appears... What now? s ..or.. q dNotice that to send plain text without a Content-Description: field, you can just enter the text without a directive. The plain text should have become a text/plain body part. The image/gif part should come next, with a Content-Transfer-Encoding: of base64 and a Content-Description: "Picture of something."
Plain Text with a Content-description
You may want to describe your plain text body parts by adding a Content-Description: field. There are two ways to do that. I like to use the #< directive. But you can also type the Content-Description: field as the first line of the plain text part -- and then (important!) follow it by an empty line.
The message below has two text parts. The first is information; the second is a "signature." Most people put an end-of-message signature file in their home directories in a file named .signature (with a leading dot). mhn and mhbuild can include that signature for you as a separate body part. (The Sections Automatic Signature on End of Messages and Add Text to Drafts: mysend explain how to add a signature automatically.) If you have a long signature, putting it in a separate part is courteous. People who don't need to read it can just skip that part of the message. (On the other hand, if your message would have had only one part, adding the signature in a separate part makes the message multipart. That adds more complication for the person who's reading your message.)
Here's the sample body. (In examples from here on, I won't show the message header or the What now? prompts.)
#<text/plain [Some useless garbage] Hi, whoever. Blah blah blah... etc. etc. etc. #text/plain [signature] /home/joe/.signatureAfter you build the MIME draft, it should have two parts: text/plain with the Content-Description: "Some useless garbage," and text/plain with your signature and a description.
A text/enriched body part lets you do simple formatting: changing fonts, text size, and layout. (A similar but obsolete type is text/richtext.) RFC 1896 is a complete specification of enriched text; Section RFCs and Internet Drafts explains how to get it. The most popular use of enriched text is probably for boldfacing and centering:
You may have an editor that supports enriched text. Enriched text is similar to SGML, HTML, and other markup languages, so you might be able to adapt an editor for one of those. But enriched text is really simple to enter, so I've never looked for a fancy editor. Plain old vi makes it pretty easy. I've written a set of two-character vi macros (key maps) that enter the tags. There are versions for both command mode and text-input mode. Emacs functions and keybindings to help you enter enriched text are covered in Section Adding Attachments and Section Composing of The MH-E Manual respectively. If you don't use vi or Emacs, you can still use this idea.) For instance, when I put my cursor on a word in the command mode and type *b (asterisk, then a lowercase "b"), the macro surrounds the word with <bold> and </bold>. In text-input mode, if I want to enter a bold word, I type *b; the macro inserts the tags <bold></bold> and puts the cursor between them, ready to enter text. In both cases, when I've finished editing the bold text, I can type *e to go to the end of the bold area (the final > character).
Some of the macros are shown in the example below. The sequence ^[ stands for the ESC character, which you enter by typing CTRL-V first:
Example: vi Macros for entering enriched
; *b macros: tag current word as bold, add bold tags then insert between: map *b ea</bold>^[F<bi<bold>^[ map! *b <bold></bold>^[F<i ; *e macros: go to end of tagged area by searching for ">": map *e f> map! *e ^[f>a ...You can get the whole batch of macros from the online archive in the examples/mh/misc/exrc.enriched file; there's more informetion in the examples/mh/misc/exrc.README file. Of course, you can edit them to work the way you want. For instance, you might want the command-mode *b macro to let you boldface a whole area of text instead of a single word. You could change the map *b to insert <bold> before the cursor and add a new *B to append </bold> after the cursor. If you often need to enter text that contains the literal characters *b without invoking the boldfacing macro, try naming your macro with an equal sign (=b) instead of an asterisk. Also, many versions of vi let you map the function keys (F1, F2, etc.) with mapping commands like map #1. If you have an enriched-text editor, mhn and mhbuild can be configured to run it automatically as you compose a message. You'll need the mhn-compose- profile entry.
On to the example message:
#<text/enriched [Line-filling tests] This is the first line of the message. It is <bold>very long</bold>. When this line is encoded, it should be broken (with an = at the end of the line) into quoted-printable text. The empty line above will cause a line break. So, the word <italic>The</italic> should be at the left-hand margin. The two empty lines above make this into a new paragraph.Because the default body part is text/plain, you have to use a #<text/enriched directive before the enriched text part. If you enter any lines that are too long (more than 76 characters), mhn or mhbuild will automatically use quoted-printable encoding for your text -- even if your text has only ASCII characters.
Remember that the recipient's enriched text viewer will reformat your message to fit the screen. Line breaks are handled this way:
This is the first line of the message. It is very long. When this line is encoded, it should be broken (with an = at the end of the line) into quoted-printable text.
The empty line above will cause a line break. So, the word The should be at the left-hand margin.
The two empty lines above make this into a new paragraph.
Enriched text is designed to be fairly readable on non-MIME mail programs. When you look at your encoded draft (which isn't shown here), compare the encoded quoted-printable message to the formatted version.
Data from a Program, Character Sets
mhn and mhbuild can run a program to provide content for a message part. The Section about mhn-compose- and mhbuild-compose- entries tells how to specify the default program to be run for each content-type. You can also give a program name at the end of a directive:
#type/subtype [description] |some-programThis will run some-program instead of any default composition program that might be defined for that content-type. The standard output of the program will be used as the content. But if the program returns a non-zero exit status, mhn or mhbuild will abort; it won't encode the draft. To fool mhn/mhbuild into using the output of the program anyway, use a subshell with an exit 0 command on the end. Using the subshell also lets you combine several commands, and anything they write to their standard output, into one content. For example, to change to another directory (not the one where you're composing the draft) and mail the output of an rcsdiff command (which returns an exit status of 1 if there were differences):
#text/plain [diff] |(cd /u/elsa/project/summaries; rcsdiff -r8.7 refguide; \ exit 0)You can continue a directive to another line by typing a backslash (\) at the end of the first line.
By the way, to simply mail the output of a program -- with no other message -- using comp and a directive is probably more work than you need to do. The mhmail and viamail programs are probably what you want.
For this example, let's mail the output of a simple shell script. This script, named makechars, is useful for people like me who live in an "ASCII culture." It outputs a string of 26 eight-bit characters. If your keyboard isn't configured for a non-ASCII character set, you can run makechars to get some non-ASCII text.
To make makechars, put the following three lines in an executable file named makechars. (If you've never made a shell script before, the Section Writing Shell Scripts for MH explains how.)
#!/bin/sh # Output characters with octal values 300 to 331 echo abcdefghijklmnopqrstuvwxyz | tr '[a-z]' '[\300-\331]'If your terminal is configured for a character set like ISO-8859-1, here's what should happen when you run the script:
% makechars ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖ×ØÙWhen you want to send non-ASCII text in a MIME message, you must tell mhn or mhbuild to use the non-ASCII character set. To do that, store the character set name in the MM_CHARSET environment variable. Use one of these two commands:
$ MM_CHARSET=iso-8859-1; export
MM_CHARSET sh-type shells
% setenv MM_CHARSET iso-8859-1 csh-type shells
If you always use that character set, you should make sure that environment variable is always set -- by your shell's startup file, for instance. (nmh users: there's more about MM_CHARSET in your mh_profile(5) manual page.)
It's time to make the draft. As you saw in the sample directive above, put the program name where a filename would go and start the program name with a vertical bar (|):
#text/plain [output of makechars] |makechars(If the makechars program isn't in your shell's search path, you may need to use its absolute pathname, like |/home/andy/makechars.)
When you run a program to compose a content, it's often an interactive program that does something like recording your voice message. So mhn or mhbuild prints a message to tell you that it's starting the program:
What now? e mhn composing content text/plain from command makecharsBecause makechars isn't interactive, you should get another What now? prompt right away. View the encoded draft on your screen by "editing" the file with cat(1):
What now? e cat To: jerry Subject: testing makechars MIME-Version: 1.0 Content-Type: text/plain; charset="iso-8859-1" Content-ID: <email@example.com> Content-Description: output of makechars Content-Transfer-Encoding: quoted-printable =C0=C1=C2=C3=C4=C5=C6=C7=C8=C9=CA=CB=CC=CD=CE=CF=D0=D1=D2=D3=D4=D5=D6=D7=D8= =D9There are a few things to notice in that encoded message. The character set parameter charset="iso-8859-1" has been added to the Content-Type: field. The Content-Transfer-Encoding: field shows that mhn or mhbuild has used quoted-printable encoding. You can see that in the body: each non-ASCII character is encoded as its hexadecimal value with = before. The encoded output of makechars is too long for one line, so mhn/mhbuild has split the line and added an = at the end of the first part.
If your lproc is set to run show, you should see the decoded message on your screen:
What now? list To: jerry Subject: testing makechars MIME-Version: 1.0 Content-Description: output of makechars part text/plain 27 output of makechars Press <return> to show content... ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖ×ØÙ
Including non-text parts in your messages can make them big. Messages can easily get too big to send; systems will truncate or reject messages that are too long. The current MIME standard doesn't include any kind of compression because of the uncertain legal status of popular compression methods.
One way MIME works around the problem of large messages is by splitting a message into pieces and reassembling it automatically. That's called partial messages. But partial messages don't solve another problem: Some people may not want their mailboxes filled by huge messages. MIME has a nice answer for that problem: external parts. A MIME mail reader can ask the recipient whether to fetch a copy of some data by anonymous FTP, from an email file server, or from a local file. In other words, the mail message doesn't contain the data; it contains instructions for getting the data.
NOTE: Not everyone can use anonymous FTP: many email users aren't on the Internet or are behind a firewall. There are workarounds for MH users (the mhn-access-ftp: or nmh-access-ftp: entry). Some MIME MUAs can't handle external body parts, though. The recipient may be able view the encoded body part and get the data manually. Still, before you send a message with external body parts, you may want to ask the recipient.
The next example message has two external parts. It's a multipart/alternative message. One part refers to a file by anonymous FTP; the other refers to a local file. Remember that the order of parts in a multipart/alternative message is important: the last part is "best." In this message, I've decided that getting the contents of a local file is better than getting the file by anonymous FTP. So, I've put the local-file part at the end of the message body. If the recipient can't get the file by the second method (a local file), their MIME-capable mail program will use the first method (anonymous FTP).
This message has dummy directives to get and display the mhn(1) manual page. You should edit the directives. If you can put files in an anonymous FTP area, or if your site has an anonymous FTP area with some interesting files to get, use those parameters in the first external body part below. If you can't put files in an anonymous FTP area, send a message that recovers files from a public FTP area like ftp.uu.net or ftp.ics.uci.edu. Modify the second part to point to the formatted version, if any, of the mhn manual page on your system.
#<text/plain [instructions] Please read the O'Reilly catalog. It's in the parts below. Thanks. #begin alternative #@text/plain [O'Reilly catalog by FTP] \ access-type=anon-ftp; \ permission=read; \ size=180312; \ directory="/pub"; \ name="book.catalog" #@text/plain [O'Reilly catalog or something else from local file] \ access-type=local-file; \ size=nnnn; \ name="/directory/filename"; \ permission=read #endBe sure to give a [description] in your external-part directives; mhn and mhbuild require it. If you don't want to add a description, use a pair of brackets with no text between them: . For a complete explanation of the parameters for each external body part (which takes something like ten pages!), get a copy of RFC 2045.
This section is a (slightly) more formal description of the draft files that mhn and mhbuild read and encode into MIME messages.
A draft message body can have two types of lines:
#forw [A mail message about my friend Charlie Jones] +people 23 #@image/gif [a picture of Charlie] \ access-type=anon-ftp; name="charlie_jones.gif"; \ directory="pub/staff_pictures"; site="ftp.foo.bar"You can indent the continued lines if you want to.
If you want to start a plain text line with a hash mark, use two hash marks. The two hash marks will be translated into a single hash mark; the rest of the line won't be interpreted. For example, here's how to make the second line of a draft start with #1 -- to leave a single hash mark after mhn or mhbuild encodes the message:
Melissa, most of the project is coming along. But my ##1 problem is the ...If you don't want to double the hash marks, one workaround is simply moving the hash mark away from the left-hand edge. For example, you could change the text to read:
Melissa, most of the project is coming along. But my #1 problem is the ...If you don't double a leading hash mark and the first word on that line isn't a legal directive, you'll get a complaint during encoding about unknown directive "#1". If that happens, you can re-edit and re-encode the draft.
If you're including lots of lines that start with hash marks but are not directives -- for example, a program with comment lines that start with # -- doubling all the hash marks can be a pain. It's easier to put the message text in a file, then use a #text/plain directive followed by a filename. mhn and mhbuild will include the file directly without interpreting lines that start with hash marks.
If a filename isn't given, mhn and mhbuild search the profiles for a composition string (see the Section What Profile Entries Are There?) that tell how to compose the content. For example, if you specify #audio/basic without giving an audio filename, mhn/mhbuild wants to run a program that captures sound.
#application/octet-stream; type=tar; x-conversions=gzip /u/joe/fs.tar.gzwould make the following field in the message:
Content-Type: application/octet-stream; type="tar"; x-conversions="gzip"
#text/plain (using British spelling) [Sales report] /usr/reports/sales.UKwould create these two fields in the message:
Content-Type: text/plain; charset="us-ascii" (using British spelling) Content-Description: Sales report
If you omit <content-id>, mhn or mhbuild will choose a value with the date, time, and your local hostname. If you use empty angle brackets, <>, the message part won't have a Content-ID: field.
Messages that are too long will be rejected or truncated as they're sent across a network. (Often, messages that stay on a particular host can be longer, but they still may have length limits.) The send -split option creates MIME partial messages as your draft is sent. Your draft will be split into parts that are about 50,000 ASCII characters long.
The mhn -store and mhstore commands shown in the Section Partial Messages can put the pieces together automatically. Or, if your message is just plain text without multipart contents, the recipient can probably put the pieces together by hand, without a MIME-capable mail reader -- although it may be inconvenient or impossible for users with menu-driven MUAs.
After you type -split, enter the number of seconds that send should pause before it sends the next part. For example, send -split 10 will give your mail transfer agent 10 seconds to process a part before the next one is sent. The right number to use depends a lot on your particular situation; if you're curious, talk to your system postmaster. (Note: -split 0 doesn't work. You have to use 1 or more.) To prevent splitting (for instance, if you've put something like send: -split 5 in your MH profile), use -split -1 at the What now? prompt.
To experiment, send a long file to yourself. If your system has /usr/dict/words, the dictionary file, it's a good choice. Mine has about 200k characters, which send splits into five messages. Be sure that your file isn't much longer than mine -- unless you want a lot of partial messages! Here's how to send the file with send -split:
% comp To: jerry cc: Subject: Testing send -split ------- #text/plain [words] /usr/dict/words #text/plain [signature] /home/ehuser/.signature CTRL-D -------- What now? e mhn (on nmh, just mime) What now? s -split 1 Sending as 5 Partial Messages pausing 1 seconds before sending part 2... pausing 1 seconds before sending part 3... pausing 1 seconds before sending part 4...The first and second partial messages you sent will look like this:
% show -noshowproc 131 | more (Message inbox:131) From: Jerry Peek <firstname.lastname@example.org> To: jerry Subject: Testing send -split MIME-Version: 1.0 Content-Type: message/partial; id="<email@example.com>"; number=1; total=5 Content-Description: part 1 of 5 Date: Wed, 12 Oct 1994 21:06:04 -0700 Message-ID: <firstname.lastname@example.org> Content-Type: multipart/mixed; boundary="----- =_aaaaaaaaaa0" Content-ID: <email@example.com> Message-ID: <firstname.lastname@example.org> ------- =_aaaaaaaaaa0 Content-Type: text/plain; charset="us-ascii" Content-ID: <email@example.com> Content-Description: words 10th 1st 2nd ...omitted... crease create creating % show -noshow 132 | head -15 (Message inbox:132) From: Jerry Peek <firstname.lastname@example.org> To: jerry Subject: Testing send -split MIME-Version: 1.0 Content-Type: message/partial; id="<email@example.com>"; number=2; total=5 Content-Description: part 2 of 5 Date: Wed, 12 Oct 1994 21:06:23 -0700 Message-ID: <firstname.lastname@example.org> creature creche credent credential ...omitted...The last message (in this example, the fifth message) has the closing part boundary.
It's a good idea to use the mhn -check or mhbuild -check switch when you compose a split message. See the Section Adding an Integrity Check.
When a MIME message is encoded, mhn and mhbuild can add a Content-MD5: header field with a message checksum. The recipient can compare the message checksum to the message body. If the checksum doesn't match, the message body has probably been changed. The checksum method takes into account some of the common ways that gateways munge messages: it ignores trailing whitespace on lines, for example.
To add a checksum, use the -check switch with mhn. That is:
What now? edit mhn -check (in MH) What now? mime -check (in nmh)RFC 1864 documents the Content-MD5: field.
MIME encoding is designed to get the data safely through almost every known mail transport system and gateway. MIME encoding is also designed to make messages as easy for humans to read as possible. The Content-Transfer-Encoding: field tells the recipient how a message was encoded -- and how to decode it. The Section MIME Encoding has more information.
mhn doesn't let you choose the encoding it uses. It checks the message text and content type and then picks an appropriate encoding. (If you're using nmh, the mhbuild -ebcdicsafe switch lets you tweak the "quoted-printable" transfer encoding. Some common punctuation characters are encoded to get the message through mail gateways that use EBCDIC character encoding.) After you have experience with MIME, you might not always agree with the mhn or mhbuild encoding.
For example, I needed to mail a troff source file. A lot of the lines in troff source start with a dot (.) -- and leading dots cause some mailers to truncate files. But mhn chose plain 7bit encoding, which copies the text into the draft as-is. I wanted base64 encoding. So, after mhn encoded my message (edit mhn), I edited the draft (with edit vi, though any plain-text editor would do). I changed the Content-Transfer-Encoding: field value to base64. I deleted the 7bit-encoded body part -- and replaced it with the output from the mimencode(1) utility, being careful to keep the blank lines in the message body as they were. This is the vi command to do it:
:r !mimencode /my/troff/file/pathnamemimencode is part of the Metamail package; if you don't have it, see the Section Metamail.
The next release of MH will probably encode text messages (except text/plain) with leading dots as quoted-printable. The dots will be encoded as =2E.
NOTE: There are a few cases where a particular encoding is required, and there are other cases where the encoding is recommended. Before you change the default encoding, read RFC 2045 (The Section RFCs and Internet Drafts expains where to get it).Here are the steps that the MH 6.8.3 version of mhn uses as it picks an encoding. (Thanks to Marshall T. Rose for this info.)
nmh users: I'll bet the list above will change. Check the "Transfer Encodings" section of the mhbuild(1) manual page for the latest info.
This file is from the third edition of the book MH & xmh: Email for Users & Programmers, ISBN 1-56592-093-7, by Jerry Peek. It is freely available; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation. For more information, see COPYING.
Copyright © 1991, 1992, 1995 O'Reilly Media, Inc.
Copyright © 1996, 1997, 1998, 1999, 2000, 2002, 2004 Jerry Peek
Copyright © 2006 Bill Wohler
Last modified: 2006-05-31 15:13:43 -0700