If you really want to learn how to format an Internet Message then you should refer its Request For Comments (aka RFC). The one that defines "Multipurpose Internet Mail Extensions - Format of Internet Message Bodies" is RFC2045 issued on November 1996.
The format is somehow very strict and must be followed as-is.
Basically the message contains a header and the body. The header defines the type of the message, the way it's formated, some other fields which differ from one type to another.
The body is formed by different entities. An entity can be for instance just a plain-text like "Hello there!" but also can be an image, an attachment, whatever.
NOTE In the following examples everything enclosed into brackets (eg. {hello}) should be replaced with your real value. Any newline is in reality CRLF (ie. ASCII 13 + ASCII 10). Where you see two CRLF stick to it. It would be the worst moment to show how creative you are.
Basically for an email message that has attachments the header should look like this:
MIME-Version: 1.0
To: {email@domain}
Subject: {email-subject}
X-Priority: {2 (High)}
Content-Type: multipart/mixed; boundary="{mixed-boudary}"
In the above example the {mixed-boudary} may be any unique hash value, like 000008050800060107020705. The others are self-explanatory.
Now, anytime we want to append a new entity to the message (like the message body, an image, an attachment) we have to tell the email agent that a new section is coming, ie. to prefix that entity with the {mixed-boundary} value. We call this "open the boundary". Note that by opening a boundary we don't insert that boundary as was defined initially, we use 2 more minus signs in front, like --{mixed-boudary}. When we close a boundary we proceed likewise, except that we have to use other 2 minus signs at the end, like --{mixed-boudary}--
--{mixed-boudary}
the entity content
--{mixed-boudary}--
Because the email agent should understand what type has the content of our new inserted entity we have to declare that right after the opening of boundary. The declaration is just a header which contains only those parameters/values that are compatible with the entity.
For a HTML body content my entity header would look like:
Content-Type: text/html; charset=utf-8
Content-Transfer-Encoding: 7bit
so the whole body (enclosed into boundaries) will finally look like:
--{mixed-boudary}
Content-Type: text/html; charset=utf-8
Content-Transfer-Encoding: 7bit
<html>
<head><meta http-equiv="content-type" content="text/html; charset=utf-8"></head>
<body bgcolor="#FFFFFF" text="#000000">
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque vel
dapibus arcu. Duis quam dui, ornare non mi nec, luctus faucibus massa. Vivamus
quis purus in erat euismod ullamcorper vitae eget dolor. Aliquam tempor erat
accumsan, consectetur ex et, rhoncus risus.
</body>
</html>
If another entity has to be inserted we proceed exactly like above.
When there is no more data to add to the message we close the mixed-boundary, ie. CRLF + --{mixed-boudary}--.
If for any reason an entity has to be inserted with an alternative representation (eg. a body message is inserted both as plain-text format and also as HTML format), then the entity content has to be declared with content-type multipart/alternative (although the global multipart/mixed header still remains!). Each alternative representation will be enclosed by this new boundary.
A complete example below:
MIME-Version: 1.0
To: {email@domain}
Subject: {email-subject}
X-Priority: {2 (High)}
Content-Type: multipart/mixed; boundary="{mixed-boudary}"
--{mixed-boudary}
Content-Type: multipart/alternative; boundary="{alternative-boudary}"
--{alternative-boudary}
Content-Type: text/plain; charset=utf-8;
Content-Transfer-Encoding: 7bit
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque vel
dapibus arcu. Duis quam dui, ornare non mi nec, luctus faucibus massa. Vivamus
quis purus in erat euismod ullamcorper vitae eget dolor. Aliquam tempor erat
accumsan, consectetur ex et, rhoncus risus.
--{alternative-boudary}
Content-Type: text/html; charset=utf-8;
Content-Transfer-Encoding: 7bit
<html>
<head><meta http-equiv="content-type" content="text/html; charset=utf-8"></head>
<body bgcolor="#FFFFFF" text="#000000">
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque vel
dapibus arcu. Duis quam dui, ornare non mi nec, luctus faucibus massa. Vivamus
quis purus in erat euismod ullamcorper vitae eget dolor. Aliquam tempor erat
accumsan, consectetur ex et, rhoncus risus.
</body>
</html>
--{alternative-boudary}--
--{mixed-boudary}
Content-Type: application/pdf; name="myfile.pdf"
Content-Transfer-Encoding: base64
Content-Disposition: attachment; filename="myfile.pdf"
JVBERi0xLjINOCAwIG9iag08PCAvTGVuZ3RoIDkgMCBSIC9GaWx0ZXIgL0ZsYXRlRGVjb2Rl
ID4+DXN0cmVhbQ1oQ51bbY/cNg7+BfsfhAUO11w3riW/B7gPaZEAAdpcm06RL8EBzoyn68uM
vZ3xZLv//khKsuUxNaMNiiabpUg+pKiHsmxJEcN/UsgiilP4ab2/+XF1I81vszSqclHIOEpj
sdrf/PC2EFVUpmK1vXkZxVKs1uJlJJVYPYrvPra7XVvvxYdIrE7rL83hhVj97+bNyjUoFam7
FnOB+tubGI3FZEkwmhpKXpVRnqJi0PCyjBJ1DjyOYqWBxxXp/1h3X+ov9abZt434pV0feoG/
ars/xU/9/qEZmm7diJ+abmgOr0TGeFNFEuXx5M4B95Idns/QAaJMI1IpKeXi9+ZhaPafm4NQ
cRwzNpK0iirlRvisRBZpVJa+PP51091kkjBWBXrJxUuZRjIXh0Z8FN3MnB5X5st5Kay9355n
--{mixed-boudary}--
TIPS
Use your preferred email client (mine is Thunderbird) and send to
yourself one message plain-text only, one HTML only, one mixed, and
then each of the earlier but with one file attachment enclosed. When
you receive the message just study its source (View -> Message
source).
@Edit: a very well documented case study + PHP example can be found at here