../_images/th-email.png

7.1. Sending E-mail

7.1.1. E-mail Protocols

  • Sending messages – SMTP
  • Message Content – MIME
  • Retrieving Messages – IMAP, POP, POP3

7.1.2. E-mail messages

  • 7-bit ASCII (128 characters)

    • Works for English text only messages
    • Attachments require special handling
  • Variable headers: To, From, Subject, Date and Message-ID are the normal minimum. Others are allowed and may be added by servers that handle the message.

  • MIME – introduced in 1992

MIME stands for Multipurpose Internet Mail Extensions and in comparison to networking and E-mail itself, it is relatively new. MIME defines the mechanisms to encode binary or Unicode data into ASCII and embed the data in an E-mail message so that it can so that it can be sent using SMTP.

7.1.2.1. Pre-MIME Solution to Attachments

Before MIME was widely available, most people either did not send binary attachments in E-mail messages, or they used a couple tools outside of their E-mail system to handle the attachments. uuencode (uu = UNIX to UNIX) is a program that could be used to convert binary data in a file, to a much larger file on ASCII data. As the name implies, it came from UNIX, but was also available for other systems such as MS-DOS. One would encode a file as following:

$ uuencode some_file > encoded_file

If you would examine the encoded file, you would find a line similar to the following:

begin 0664 some_file

In this example, we are saying the file’s name was some_file and its file permissions in UNIX octal notation, were 0664. After the begin line would be a large number of lines contained what appeared to be random characters. At the end of the file, there would be a line, which just said: end.

This encoded file could then be read into an E-mail message and sent to someone else. The recipient should save the whole E-mail message to a temporary file and then run use the uudecode command as follows:

$ uudecode tempfile

The result of the above command would be a new file in the recipient’s current working directory named some_file with file permissions of 0664 (-rw-rw-r–), just as exists on the sender’s computer.

7.1.3. MIME advantages

  • Multiple attachments

    • Divide the message into parts
    • Each part has headers including the type of data, filename, encoding used and data encoded to 7-bit ASCII
  • Content types

  • Styled text with different fonts and colors

  • Interactive multimedia

  • Multi-language support for non-Roman languages like Japanese, Chinese, Arabic, Hebrew, etc...

7.1.4. Building simple messages in Python

Note, discussion of smtplib module follows later, here we discuss just the generation of the e-mail message.

Most of the e-mail tools are contained in submodules of the email module, rather than the email module.

class email.LazyImporter

Each of the MIME e-mail classes for building e-mail messages are subclasses of this class. Note, you do not directly use this class, but you will use some methods inherited from this class (for example, email.MIMEText.as_string()). This class defines a dictionary like object, thus the dictionary facilities are used to set the mail headers of the message. See the example below.

email.MIMEText.MIMEText(message)

Constructor for a simple text MIME e-mail object. The returned object may be used as a dictionary to add the mail headers.

Parameters:message (string) – The content of a text only e-mail message
Return type:Dictionary like e-mail object.
email.MIMEText.as_string()

Inherited function from email.LazyImporter. Returns an e-mail message string as specified by RFC 2822.

Return type:string
>>> from email.MIMEText import MIMEText
>>> msg = MIMEText("hi")
>>> msg.as_string()
'Content-Type: text/plain; charset="us-ascii"\nMIME-Version:
1.0\nContent-Transfer-Encoding: 7bit\n\nhi'
email.Utils.formatdate(timeval=None, localtime=False, usegmt=False)

Returns a date string as specified by RFC 2822, e.g.:

Fri, 09 Nov 2001 01:08:47 -0000
Parameters:
  • timeval – The optional timeval is a floating point time value as accepted by time.gmtime() and time.localtime(), otherwise the current time is used.
  • localtime – If the optional localtime is set the returned time is relative to the local timezone, instead of UTC.
Return type:

string

email.Utils.make_msgid()

Returns a string suitable for RFC 2822 compliant Message-ID.

from email.MIMEText import MIMEText
from email import Utils
import smtplib

message = """Hello,
    This is a test message.

        -- Tim"""

msg = MIMEText(message)
msg['To']       = 'you@home'
msg['From']     = 'me@school'
msg['Subject']  = 'Testing Python e-mail'
msg['Date']     = Utils.formatdate(localtime = 1)
msg['Message-ID'] = Utils.make_msgid()

server = 'localhost:8025'
s = smtplib.SMTP(server)
s.sendmail(msg['From'], [msg['To']], msg.as_string())
s.close()

7.1.5. Multipart MIME messages

email.MIMEMultipart.MIMEMultipart()

Constructor for a email.MIMEMultipart object

email.MIMEMultipart.attach(attachment)

Attach a MIME e-mail object to the message

email.MIMEMultipart.as_string()

Returns a string representation of the message. See email.MIMEText.as_string()

7.1.5.1. Procedure to Create Multipart MIME messages

  • Start with a MIMEMultipart object

  • Create MIMEText objects for the message body and any text attachments; attach to the message

  • For binary file attachments, create other MIME objects, encode the data for each; attach to the message.

    • MIMEBase – generic binary file
    • MIMEAudio audio file
    • MIMEImage graphic image file
  • See example code from The Text Book (mime_gen_basic.py)

7.1.5.2. MIME Encoders

encode_quopri

Encodes the payload into quoted-printable form and sets the Content-Transfer-Encoding header to quoted-printable7.2. Use this encoding when most of your payload is normal printable data, but it contains a few unprintable characters.

encode_base64

Encodes the payload into base64 form and sets the Content-Transfer-Encoding header to base64. Use this encoding when most of your payload is unprintable data since it is a more compact form than quoted-printable. The drawback of base64 encoding is that it renders the text non-readable.

encode_7or8bit

This doesn’t actually modify the message’s payload, but it does set the Content-Transfer-Encoding header to either 7bit or 8bit as appropriate, based on the payload data.

7.1.6. MIMEAlternatives

  • Like email.MIMEMultipart, except the e-mail client program is to display one part or the other (text or HTML), but not both. Multiple parts of the message are used to hold the same content in different formats.

  • No Content-Disposition header is inserted

    • Content-Disposition header is used to give the reader program some clues of what to do with the file, such as a default file name for saving it.
    • Mime Alternatives are for viewing, so file handling is not needed.

7.1.7. Parsing E-mail messages

For the Network Programming Class, the students may skip this section of The Text Book. We are more concerned with sending messages, which is more common for non-interactive programs to do than retrieving and parsing messages.

7.1.8. SMTP

  • Simple Mail Transport Protocol

  • Unlike HTTP, SMTP is a connection oriented protocol

    • Multiple messages exchanged between client and server to set who

      the message is to and from and to send the data.

    • Normal conversation is:

      HELO {domain_name}
      MAIL From: {address}
      RCPT To: {address1, address2, ...}
      DATA {-- Send the message as a string --}
      *Blank line followed by a single period to indicate end of data*
      QUIT
  • Always send data as a string

  • See smtpSocket.py for a demonstration of sending a simple SMTP message between client and server using sockets directly.

  • See demo video of running SMTPSocket.py with Fake mail

class smtplib.SMTP(server)

Make a connection to a SMTP server. Returns an object, which can be used to send a message.

Parameters:server (string) – The server to connect to. If the SMTP server is not listening on port 25, put a colon after the server name and then list the port number after the colon.
Return type:object
smtplib.sendmail(from, to, message)

Send an e-mail message

Download the following example: smtplibTest.py

import smtplib

message = """Hi everyone,
            -- Tim"""
msg = MIMEText(message)
msg['To'] = 'you@home'
msg['From'] = 'me@school'
msg['Subject'] = 'Testing Python e-mail'
msg['Date'] = Utils.formatdate(localtime = 1)
msg['Message-ID'] = Utils.make_msgid()

s = smtplib.SMTP('localhost:8025')
s.sendmail(msg['From'], [msg['To']], msg.as_string())
s.close()

7.1.9. Fake mail Server

  • Download it from here (fakemail.py)

  • Creates a SMTP server that you can send messages to for testing much better for debugging than sending to a real server.

  • Recommend to run from Command Prompt:

    C:\tmp\> python fakemail.py
  • Use Control-c (if that does not work in Windows, use Control-Break) to stop the fakemail server. Be patient, the Windows command prompt is very slow to send signals to processes. It is immediate in Unix.

  • Defaults to listen on port 8025 (Real SMTP port is 25)

  • Saves messages to files in same directory

  • Change file name extension to ‘.eml’ to view the message with your e-mail client.

7.1.10. ESMTP

  • Extended Simple Mail Transfer Protocol
  • Newer version of SMTP
  • Supported by some servers
  • Allows clients to get information, such as maximum allowed message size, from the server prior to sending the message
  • Allows for encryption of the message. Note in Python 3, The smtplib module supports SMTP over SSL thanks to the addition of the SMTP_SSL class. This class supports an interface identical to the existing SMTP class.
  • Allows for a login before accepting the message
  • Protocol session begins with EHLO instead of HELO, which can be used to test if the server uses ESMTP or SMTP.