Article Author: Mitch Gordon
Introduction
The POP3 protocol is a simple, plain-text protocol that allows you to easily obtain emails from a POP3 server. With a few dozen lines of code, you can connect to the server, authenticate yourself, check to see if there are any emails available and retrieve them as needed. The task gets more difficult, however, when you realize that all the server returns to you is a string. How do you go about decomposing this string back into the text and attachments that it contains?
By going back to the specifications that outline how email and MIME are supposed to work, you can learn how all the content is arranged in the email you receive and, more importantly, how to go about extracting and decoding it. RFC822 is the original internet mail specification submitted in 1982. In it, you will find much information that is still relevant, mainly pertaining to the headers at the beginning of the email. RFC822 has since been obsoleted by RFC1939. In 1992, RFC1341 was submitted; this details how to embed multimedia content in an email by specifying a Multipurpose Internet Mail Extensions (MIME) type for each piece of content the email contains. MIME describes the format of the content and its type so that programs that access the content will know how to interpret it. This RFC has since been superceded by RFCs2045-2049. In this article and in the sample code, I’ll show you how to use the POP3 protocol to obtain email from a POP3 server and how to parse it.
System Requirements
To run the code for this sample you should have
- IIS running on Windows 2000 or later
- The .NET Framework version 1.1
- VS.NET 2003
- An accessible POP3 server and a valid email account.
Installing and Compiling the Sample Code
The sample download for this article contains a C# solution containing two projects, POP3 and POP3Web. The POP3Web project is a web application that allows you to log into your email server and check your mail. It uses the classes in the POP3 project to retrieve and parse the emails which you can then view in the browser.
To install the sample, create a virtual directory called POP3Web and point it at the folder by the same name in the sample package. Be sure to grant "Scripts Only" execute permissions on the virtual directory. In most cases, this will be the default.
The POP3Web Application
As stated earlier, this web application allows you to log into a mail server and retrieve your emails. Start by accessing the default.aspx page and entering a server name, user name, and password for your email server.

Figure 1. The login panel of the default page.
After you click the Log In button, the application will connect to the mail server and retrieve the emails from the inbox, displaying each in a table.

Figure 2. The inbox panel of the default page.
This figure has been reduced in size to fit in the text. To view the full image Click here
Clicking the Check Email button will refresh the list of emails from the server. The subject of each item in the table is a hyperlink to the details page which will display the particulars of the email.

Figure 3. Details of the an email as displayed in emaildetail.aspx.
This figure has been reduced in size to fit in the text. To view the full image Click here
Note that just below the date, a list of links to any attachments will be displayed. Clicking any of these links will download the attachment to your local machine from the web server. (The attachment has already been downloaded from the POP3 server to the web server).
The POP3Web application contains two pages, default.aspx which handles logging in and displaying the list of emails in the inbox and emaildetail.aspx which displays the detailed information from any particular email.
The real work, in the sample application, takes place in the POP3 project. This project consists of the following classes.
- Pop3 – Handles all the communication with the server and provides the primary API to calling code.
- Email – Encapsulates all the information about any particular email including attached files or emails.
- EmailParser – Parses the raw string returned from the server and returns a populated Email object.
- EmailPart – When the email is broken up into its MIME parts, this class will be used to store the details about each part.
- Attachment – Contains a binary array representing the attachment and the name of the attached file.
The application uses the Pop3 class’s Connect() method to connect to the server, authenticate the user and retrieve the number of emails in the mailbox. It then calls GetParsedEmail() for each email in the inbox and receives a populated Email object as a return value. Each email is retrieved from the server and parsed with the EmailParser class. An ArrayList containing all the Email objects is stored in Session and a list of them is presented to the user. When the user clicks the subject of a particular email, the index into the ArrayList the for that particular email is passed to the EmailDetails.aspx page which pulls that email from the ArrayList in Session and displays its properties to the user.
Using POP3
Before parsing an email, you first need to obtain it from the POP3 server. To do this, you’ll need to make a connection to the POP3 port on the server. Typically, this will be port 110. The .NET Framework offers several classes you could use to do this, but here we’ll use the Socket class.
IPHostEntry lobj_entry;
lobj_entry = Dns.Resolve(ms_server);
if (lobj_entry.AddressList.Length 0) { throw new Exception("Cannot find DNS listing for supplied server"); } mobj_pop3Socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); mobj_pop3Socket.Connect(new IPEndPoint(lobj_entry.AddressList[0], 110));
Since the user will probably enter a server name instead of an IP address, you need to start by doing a DNS lookup of the name to obtain the server's IP address. Dns.Resolve() returns an Instance of the IPHostEntry class. One of its properties is AddressList which is a collection of IP numbers that the server name maps to. Next, the code creates a new TCP Socket as a stream and connects it to the first IP in the AddressList and port 110.
Having connected to the server, you need a way to communicate with it. For this, define methods to read from and write to the socket stream.
private string ReadStream()
{
StringBuilder lobj_out;
int li_bytesRead;
byte[] lba_readBuffer = new byte[50000];
lobj_out = new StringBuilder();
Thread.Sleep(800);
while(mobj_pop3Socket.Available > 0)
{
li_bytesRead = mobj_pop3Socket.Receive(lba_readBuffer);
lobj_out.Append(
Encoding.ASCII.GetString(lba_readBuffer,0,li_bytesRead) );
Thread.Sleep(250);
}
string ls_out = lobj_out.ToString();
lba_readBuffer = null;
lobj_out = null;
return ls_out;
}
private void WriteStream(string as_text)
{
mobj_pop3Socket.Send(Encoding.ASCII.GetBytes(as_text));
}
The ReadStream() method checks to see if any bytes are available to read and if there is, it continues reading until no more are available. The bytes are read into a byte array which is converted into a string and appended to the final result in a StringBuilder . WriteStream() converts the passed in string into a byte array and writes it out to the socket stream.
POP3 Commands
Below is a list of POP3 commands available. The ones you'll be using in this article arein bold text.
| Command | Purpose |
|---|---|
| QUIT
| Ends the session with the server |
| STAT
| Asks the server for a drop listing that contains the number of emails in the inbox and the total size of the inbox in bytes. |
| LIST
| Returns a scan listing. This command, issued with no parameters, returns one line per message in the inbox. Each line contains the message number and the size of the message. This command can also be followed with a message number in which case a single line is returned containing the same information for that single message. |
| RETR
| Command should be followed by a message number. Returns the message having the number specified. |
| DELE
| Command is followed by a message number and causes that message to be marked for deletion. The message is actually deleted when the session with the server ends with the client issuing a QUIT command. |
| NOOP | The server does nothing. Useful for keeping the server from timing out the connection due to inactivity. |
| RSET | If an messages have been marked for deletion, they are unmarked. |
| TOP | Returns a specified number of lines from the top of a message. Command should be followed by a message number and the number of lines to return. |
| UIDL | Returns the unique id for one or more messages. If the command is issued with no parameters, one line will be returned for each message in the inbox hat has not been marked for deletion. If the command is followed by a message number, one line will be returned containing the unique id for that message only. Each returned line contains the message number and a unique identifier (string) for the message. This value remains the same across sessions and is useful to the client for identifying messages that have already been retrieved. |
| USER
| Command should be followed by the user name for the account being accessed. It's used to specify the account to the server. |
| PASS
| Specifies the password for the account being accessed. Follow the PASS command with the password. |
| APOP | Provides a more secure method for logging into the mail account. When a client connects to the server, the server returns a tagline containing a timestamp value. The client can log into the server by appending this value with a secret value and applying the MD5 algorithm to the string. Follow the APOP command with the account username and the resulting encrypted string to log in. |
Logging In
Next, authenticate the user on the server using the credentials they entered.
string ls_reply;
ls_reply = ReadStream();
if (ls_reply.ToLower().IndexOf("ok") > -1)
{
WriteStream("user " + ms_user + "rn");
ls_reply = ReadStream();
if (ls_reply.ToLower().IndexOf("ok") > -1)
{
WriteStream("pass " + ms_password + "rn");
ls_reply = ReadStream();
if (ls_reply.ToLower().IndexOf("ok") < 0)
{
throw new Exception("Bad Password");
}
}
else
{
throw new Exception("Bad User Name");
}
}
else
{
throw new Exception("On attempt to connect to the server, server
said, "" + ls_reply + """);
}
When you initially connect to the server or send any command to it, the response will always be prefaced with +OK or -ERR indicating success or a problem, respectively. You need to check these responses, at every step, to ensure no problem has occurred. Begin by checking the server's response to the creation of the connection. If all is well, we proceed with sending the user and pass commands to communicate the user's account information to the server.
If all has gone well, we are ready to check the status of the user's inbox by issuing a stat command to the server. The server will reply with the usual status indication followed by two numbers separated by a space. Expect something like +OK 1 453 . The first of these numbers indicates the number of emails in the user's inbox and the second is the total size of those emails in bytes.. You can obtain the number of emails available, read and unread, by parsing out this first number (1 in the previous exmple).
string ls_reply;
string ls_stat;
string [] ls_statDetails;
WriteStream("statrn");
ls_reply = ReadStream();
if (ls_reply.ToLower().IndexOf("ok") < 0)
{
throw new Exception("Error retrieving account status. Message from
server was: " + ls_reply);
}
ls_stat = ls_reply.Substring(4);
ls_statDetails = ls_stat.Split(' ');
mi_numEmails = int.Parse(ls_statDetails[0]);
If emails are available, you can retrieve them using the retr command. You issue this command and the server replies with a status on a line by itself and is followed by the text of the email.
private string ReadEmail(int ai_emailNum)
{
string ls_reply;
string[] lsa_listDetails;
int li_emailSize;
WriteStream ("list " + ai_emailNum.ToString() + "rn");
ls_reply = ReadStream();
ls_reply = ls_reply.Substring(4);
lsa_listDetails = ls_reply.Split(' ');
li_emailSize = int.Parse(lsa_listDetails[1]);
WriteStream("retr " + ai_emailNum.ToString() + "rn");
ls_reply = ReadStream(li_emailSize);
if (ls_reply.Substring(0, 4).ToLower().IndexOf("ok") -1)
{
throw new Exception("Error retrieving email number " +
ai_emailNum.ToString() +". Server said " + ls_reply);
}
ls_reply = ls_reply.Substring(4);
return ls_reply;
}
In the ReadEmail() method, another POP3 command is utilized. The List command allows you to specify a particular email number and retrieve its size. The email’s number is its one-based position in the mailbox. If you send the server the command LIST 1, you would receive a response such as +OK 1 587. The first number is the email’s position and the second is its size . Here, in the sample code the size is passed to an overloaded version of the ReadStream() method which continues to receive information from the server until the specified number of bytes have been received. This is done to insure that the complete email has been received before attempting to parse it.
How an Email is Organized
Let’s start by looking at an email that contains only text.
Received: by Gaatlantms02.gsm1900.org
id <01C56C32.135BE000@Gaatlantms02.gsm1900.org>;
Wed, 8 Jun 2005 09:57:57 -0400
Message-ID: <20050608135747.28168.qmail@web54108.mail.yahoo.com>
From: Mitch Gordon <ghostwrtrone@yahoo.com>
Reply-To: ghostwrtrone@yahoo.com
To: "Gordon, Mitch" <Mitch.Gordon@T-Mobile.com>, mgordon@enterpriseetc.com,
m2w_com@memory2web.com, ghostwrtrone@yahoo.com
Subject: test with multiple tos
Date: Wed, 8 Jun 2005 09:57:47 -0400
MIME-Version: 1.0
Content-Type: text/plain;
charset="iso-8859-1"
test, test, test
.
The entire email must consist only of printable characters. Typically, this will mean that only characters that can be represented by 7 Bits or less can be used. This is due to the structure of the internet, itself, and not a constraint imposed by any RFC, per se. Each line in the message should end in a CRLF pair. Everything up to the first blank line constitutes the header of the email. Here, you’ll find who the email was from, who it was sent to, the subject of the email and the date and time it was sent. These pices of information can be in any order. Since the maximum width of a line in an email should be no longer than 78 characters, some of the lines have been wrapped. An indented line, beginning with a tab character, indicates that it is actually a continuation of the line above it. This rule applies only to the header of the email. You’ll also find the Content-Type in the header. The above email has a Content-Type of text/plain which tells you that it only contains plain text and no multimedia content.
The end of each email message will be marked by a period on a line by itself. If the email actually contains a line with only a single period in it, this should be doubled so the client knows not to treat this as the end of the message.
Any content in the email will be preceded and followed by a single blank line and that’s true of the text in the above example. For an email that contains text marked up with HTML tags, the Content-Type would be text/html . This is how an email client knows to render the content as text marked up with HTML and not render the tags as plain text.
Now, look at an email with an attachment to see how it differs from the above case. In this case, the attachment is a Microsoft Word document and its content type indicates that it is to be opened by an application of type msword.
Received: by Gaatlantms02.gsm1900.org id <01C571BD.5A375320@Gaatlantms02.gsm1900.org>; Wed, 15 Jun 2005 11:17:32 -0400
Message-ID: <20050615151716.70713.qmail@web54109.mail.yahoo.com>
From: Mitch Gordon <ghostwrtrone@yahoo.com>
Reply-To: ghostwrtrone@yahoo.com
To: "Gordon, Mitch" <Mitch.Gordon@T-Mobile.com>
Subject: binary attachment
Date: Wed, 15 Jun 2005 11:17:15 -0400
MIME-Version: 1.0
Content-Type: multipart/mixed; boundary="——_=_NextPart_000_01C571BD.5466C046"
This message is in MIME format. Since your mail reader does not understand
this format, some or all of this message may not be legible.
———_=_NextPart_000_01C571BD.5466C046
Content-Type: text/plain; charset="iso-8859-1"
Hello!
———_=_NextPart_000_01C571BD.5466C046
Content-Type: application/msword; name="SMS Plan.doc"
Content-Transfer-Encoding: base64
Content-Disposition: attachment; filename="SMS Plan.doc"
Content-Description: 628076913-SMS Plan.doc
0M8R4KGxGuEAAAAAAAAAAAAAAAAAAAAAPgADAP7/CQAGAAAAAAAAAAAAAAAFAAAACAIAAAAAAAAA
EAAACgIAAAEAAAD+////AAAAAAMCAAAEAgAABQIAAAYCAAAHAgAA////////////////////////
////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////s
pcEANyAJBAAA+BK/AAAAAAAAEAAAAAAABAAAHgkAAA4AYmpialUWVRYAAAAAAAAAAAAAAAAAAAAA
AAAJBBYAIhYAADd8AAA3fAAAWgMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwwEAAAAAAAD//w8AAAAA
AAAAAAD//w8AAAAAAAAAAAD//w8AAAAAAAAAAAAAAAAAAAAAAGwAAAAAACoBAAAAAAAAKgEAACoB
AAAAAAAAKgEAAAAAAACeAQAAAAAAAJ4BAAAAAAAAngEAABQAAAAAAAAAAAAAALIBAAAAAAAA8gMA
AAAAAADyAwAAAAAAAPIDAAAAAAAA8gMAAAwAAAD+AwAADAAAALIBAAAAAAAA8AgAADYBAAAWBAAA
KAAAAD4EAAAAAAAAPgQAAAAAAAA+BAAAAAAAAD4EAAAAAAAAU
As before, the header starts at the beginning of the string and extends to the first blank line. In this case, the value of the Content-Type is multipart/mixed which indicates that the email consists of more than just text, but contains, instead, multiple parts which could be of any type. The Content-Type line is wrapped and specifies a value for boundary . In a multipart email, each part will be preceded by this boundary value and the last part will be followed by one. If you start at the Content-Type line that specifies the boundary value and start scanning down the example string, you’ll find two lines that have the boundary value in them. The first of these precedes the text part and the second is just before the attachment. The value of the boundary string is determined by the email client that originally sent the email, since that is the point at which the mail is encoded. In this case, the text of the email is the string "Hello!" and the attachment is encoded, so it doesn’t look much like the original file that was attached. In this example, it’s encoded using base64 -the encoding is necessary since the attachment contains binary data whereas emails, during transport, can contain only printable characters. Base64 encoding guarantees only printable characters.
Each part contains a header of its own which tells you its particular Content-Type and how it’s encoded, if applicable. The part that represents the text of the email has a Content-Type of text/plain . Content-description is an optional header value that can contain any text associated with the content. If you extract this part, alone, and what’s between the first and last blank lines of the part, you have the text of the message. Looking at the header for the attachment, you can ascertain that the file is a word document with a name of SMS Plan.doc and it has been encoded with base64 which involves converting the binary data into text consisting of the 26 lowercase ascii characters, 26 upper case characters and the + and / chanracters. Like the text section, you can extract what lies between the first and last blank lines, decode it and have the original file back.
Parsing the Email
Parsing the Header
To parse the header of the email, you extract everything up to the first blank line, unfold the lines so they all begin at column one and extract the name/value pairs we’re interested in. These pairs are specified beginning in column 1 and the name and value are separated by a colon.
string ls_line;
string ls_header;
int li_headerEnd;
StringReader lobj_reader;
//pull out the header section
li_headerEnd = as_text.LastIndexOf("rnrn");
ls_header = as_text.Substring(0, li_headerEnd);
//Unfold the header
ls_header = UnfoldHeader(ls_header);
lobj_reader = new StringReader(ls_header);
//read the header one line at a time, looking for the items we need
ls_line = lobj_reader.ReadLine();
while(true)
{ if (ls_line null) { break; } if (ls_line.ToLower().IndexOf("from:") 0) { aobj_parsedEmail.FromValue = ls_line.Substring(6); ls_line = lobj_reader.ReadLine(); continue; } . . . ls_line = lobj_reader.ReadLine();
}
As each value is obtained, it’s placed in the appropriate property of the email object.
The Content-Type of the email will determine how you proceed
Parsing a Plain Text Email
As you saw earlier, if the Content-Type in the header is text/plain , you know that you can just extract the text and stop parsing. The same is true if the Content-Type is text/html , except that you’ll want to note the fact that the text contains HTML markup so you can display it properly. The Email class contains a Boolean property, IsHtml , to indicate this situation.
There may or may not be a name/value pair in the header with the name Content-Transfer-Encoding . This value indicates that the text has been encoded and specifies what encoding method was used. It is possible that you’ll receive an email that has a Content-Type of text/plain and a Content-Transfer-Encoding value of quoted-printable . You’ll see how to handle decoding, below.
Parsing Multipart Emails
A Content-Type of multipart/mixed indicates that the email contains multiple parts that you’ll need to break out. This will be the case when the mail contains anything other than text. Again, a boundary will be defined and it will precede each part. You can break out the parts, by finding each occurrence of the boundary and extracting what falls between them.
Look at this partial example of a part containing a Microsoft Word document.
———_=_NextPart_000_01C571BD.5466C046
Content-Type: application/msword; name="SMS Plan.doc"
Content-Transfer-Encoding: base64
Content-Disposition: attachment; filename="SMS Plan.doc"
Content-Description: 628076913-SMS Plan.doc
0M8R4KGxGuEAAAAAAAAAAAAAAAAAAAAAPgADAP7/CQAGAAAAAAAAAAAAAAAFAAAACAIAAAAAAAAA
EAAACgIAAAEAAAD+////AAAAAAMCAAAEAgAABQIAAAYCAAAHAgAA////////////////////////
////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////s
pcEANyAJBAA
The Content-Type tells us that this is a file associated with the msword application and its name is "SMS Plan.doc". The Content-Disposition specifies that the part is a file that was attached to the email and the Content-Transfer-Encoding is base64. After extracting a part from the email, you’ll look to these tags for instructions on how to get it back into its original form.
Decoding the Parts
A number of types of encoding are used in emails – including encodings such as Uuencode , Binhex , 8bit , 7bit , Base64 and quoted-printable . Of these, the most common are Base64 and Quoted-Printable. The need to encode parts of the email stems from the fact noted earlier that an email can contain only printable characters. It isn’t possible to send binary information or extended characters without changing it, somehow, into another form. Which encoding is used depends on the content. If the email contains only printable characters then no encoding is necessary.
Quoted-Printable Encoding
Quoted-printable encoding is used when the content is mostly text, but contains characters that are not printable. When the email is being sent, all characters outside the printable range are replaced by an equal sign followed by the hexadecimal representation of the character ~s character code. For example, if a space character is being encoded, it would be replaced by =14. There are width restrictions to deal with, as well. No line in the email may exceed 78 characters. When the content contains a line that exceeds that width, and it’s being encoded with quoted-printable, soft line breaks are inserted into the line to wrap it. This soft line break is indicated by an equal sign at the break point and the rest of the line is wrapped.
To decode a section that has been encoded with quoted-printable, you need to find all the equal signs in the content. If the equals sign is at the end of a line, remove it and the carriage return and line feed that follow it. If it’s followed by a hexadecimal number, replace the construct with the character having the character code specified. For example, the following message
This is a example of a quoted-printable text file.
This might contain some special characters such as:
equal sign =, dollar sign $, or even extended characters
such as the cent sign or foreign characters
When encoded as Quoted-Pintable would look like this
This is a example of a quoted-printable text file. This might contain =
some special characters such as:=20
equal sign =3D, dollar sign $, or even extended characters such as the =
cent sign =A2 or foreign characters =C0=C6=DF
The following code will decode quotable-printed-encoded text.
private string DecodeQPText(string as_text)
{ char[] lca_content; string ls_content; StringBuilder lsb_out; string ls_value; int li_number; ls_content = as_text; lca_content = ls_content.ToCharArray(); lsb_out = new StringBuilder(); for (int i = 0 ; i < ls_content.Length ; i++) { if (lca_content[i] '=') { if (lca_content[i + 1] ‘0’) { ls_value = lca_content[i + 2].ToString(); } else { ls_value = lca_content[i + 1].ToString() + lca_content[i + 2].ToString(); } if (ls_value "rn") { continue; } li_number = Convert.ToInt32(ls_value, 16); if (ls_value.ToLower() Convert.ToString(li_number, 16).ToLower()) { lsb_out.Append((char)li_number); i+=2; } else { lsb_out.Append(lca_content[i]); } } else { lsb_out.Append(lca_content[i]); } } return lsb_out.ToString();
}
In the DecodeQPText() method, the encoded string is converted into a character array. Each character is examined to see if it is an equal sign. If it is, the code examines the two characters to the right to see if they make up a valid hexadecimal value. If so, it converts the hex value to a character and append it to the result.
Base64 Encoding
Base64 encoding is used when the entire content of the part is binary. Each byte is converted into printable characters for transport. The .NET Framework includes support in its Convert class for working with Base64 to encode byte arrays into Base64 strings and back again. You can leverage this functionality to decode the parts that are encoded as Base64.
private Attachment ParseBase64(EmailPart aobj_part)
{ int li_startPos; int li_endPos; string ls_b64; Attachment lobj_attachment; li_startPos = aobj_part.Content.IndexOf("rnrn") + 4; li_endPos = aobj_part.Content.LastIndexOf("rnrn"); if (li_startPos >= 0 && li_endPos >= 0 && li_endPos > li_startPos) { ls_b64 = aobj_part.Content.Substring(li_startPos, li_endPos – li_startPos); lobj_attachment = new Attachment(); lobj_attachment.BinaryContentValue = Convert.FromBase64String(ls_b64); li_startPos = aobj_part.Content.ToLower().IndexOf("filename") + 10; if (li_startPos < 10) { return lobj_attachment; } li_endPos = aobj_part.Content.ToLower().IndexOf("rn", li_startPos) – 1; lobj_attachment.FileNameValue = aobj_part.Content.Substring(li_startPos, li_endPos – li_startPos); return lobj_attachment; } else { throw new Exception("Could not figure out when the binary content started or ended"); } }
After extracting the content from the part, a call is made to the static FromBase64String() method on the System.Convert class to convert the encoded content into a byte array. This array is saved in an Attachment class. The attached file’s name is also harvested from the part so we can use it to save the binary array to disk at a later time, if desired. In fact, the Attachment class includes a Save() method for this purpose.
Further Work
The sample application was constructed to be simple to set up and understand. If using the code in a production environment, you would not want to store an array of Email objects in session state as the sample application does. Instead, I’d recommend you store the content of the email in a database. Attachments can either be decoded and stored in a blob column, or left encoded and stored it in a text column. Either way, the decoded content can be streamed to the browser as the user requests it.
Also, the parser included in the sample code demonstrates basic, standard functionality, but not all email clients follow the standards completely. There are also less common content types the sample will not handle. It’s possible you will encounter quirky exceptions that need to be handled while parsing the emails – and which would obviously need to be dealt with in a production environment.
Conclusion
Parsing emails containing MIME content can be a challenge, but sufficient information is available in the RFC specifications and enough clues exist in the emails themselves to allow you to handle them effectively. Virtually any type of information can be included in an email and using the instructions presented in this article, you will be able to extract it and convert it back into a useful form.

