60
5/21/2018 Send(SMTP)andRetrieve(POP3)EmailwithEaseunderVB.NET-slidepdf.com http://slidepdf.com/reader/full/send-smtp-and-retrieve-pop3-email-with-ease-under-vbnet-561ac  Page –1– Send (SMTP) and Retrieve (POP3) Email with Ease under VB.NET By David Ross Goben Copyright © 2011 by David Ross Goben All rights reserved. Last Update: Monday, July 16, 2012 This is a sample excerpt from the free PDF e-book,  Enhancing Visual Basic .NET Far Beyond the Scope of Visual Basic 6.0 , by David Ross Goben. Download this e-book, and its free companion,  Navigating Your Way Through Visual Basic 6.0 Upgrades to Visual Basic  .NET , also by David Ross Goben, at www.slideshare.net/davidrossgoben . They are also available on Google Docs at https://docs.google.com/leaf?id=0B_Dj_dKazINlN2JlY2EwMmEtNGUyMy00NzQzLTliN2QtMDhlZTc5NDUzY2E5&sort=name&layout=list&num =50. The Google site also has a VBNetEmail.zip source file package featuring all VB.NET classes and utilities. Table of Contents Send (SMTP) and Retrieve (POP3) Email with Ease under VB.NET...................................................................................2 Adding the VB6 MAPISession and MAPIMessage Controls t o VB.NET............................................................................................................2 PART ONE ▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬ ▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬ ▬▬▬▬▬▬ Sending Email under VB.NET using Native Methods ................................................................................................................................3 Quick and Dirty Email Senders ..........................................................................................................................................................3 TCP Ports, SSL Authentication, and Creating Credentials ...............................................................................................................5 An Email Sender with a Lot of Muscle ............................................................................................................................................... 6 Sending Email Messages as HTML .................................................................................................................................................. 8 Sending Alternate Message Views ..................................................................................................................................................10 Sending Alternate Message Views with Different Context Types and Transfer Encoding ............................................................13 Typical Email Server Specifications ..........................................................................................................................................................17 PART TWO ▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬ ▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬ ▬▬▬▬▬▬▬▬ Encoding and Decoding Email Data .........................................................................................................................................................18 Allowing Users to Specify Content-Type and C ontent-Transfer-Encoding Options.......................................................................19 Determining if Text can be Encoded As Quoted-Printable, Base64, or 7Bit ..................................................................................21 Converting 8-Bit HTML Data to 7-Bit without Loss of Integrity .......................................................................................................21 Converting 8-Bit Text Data to 7-Bit without D ata Loss....................................................................................................................22 Decoding Quoted-Printable Text......................................................................................................................................................23 Translating Base64 Data Back to Its Original Format .....................................................................................................................23 Translating BinHex Data Back to its Original Format ......................................................................................................................26 PART THREE ▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬ ▬▬▬▬▬▬▬▬ Receiving Email under VB.NET using Native Methods............................................................................................................................27 Connecting to a POP3 Server..........................................................................................................................................................30 Checking for a POP3 Server Response ..........................................................................................................................................31 Checking for Being Connected to a POP3 Server ..........................................................................................................................31 Getting a Response from the POP3 Server ....................................................................................................................................31 Submitting a Request to the POP3 Server ......................................................................................................................................33 Disconnecting from the POP3 Server..............................................................................................................................................33 Getting Email Statistics from the POP3 Server ...............................................................................................................................34 Getting an Email Reference List from the POP3 Server.................................................................................................................34 Get an Email H eader from the POP3 Server...................................................................................................................................35 Retrieve an Email fr om the POP3 Server ........................................................................................................................................36 Deleting an Email from the POP3 Server ........................................................................................................................................36 Reset (Undo) All Deletes from the POP3 Server ............................................................................................................................37 Send a ‘Keep-Alive’ NOOP Command to the POP3 Server ...........................................................................................................37 Disposing of Resources ...................................................................................................................................................................37 Using the Completed POP3 Class...................................................................................................................................................38 PART FOUR ▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬ ▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬ ▬▬▬▬▬▬  Email Data Blocks Made Easy ..................................................................................................................................................................39 Easily Extracting the Component Parts from an Email File .............................................................................................................42 Compiling Everything into an Email C lass Library....................................................................................................................................44 Building the VBNetMail Class Library ...............................................................................................................................................46 Accessing your New VBNetEmail Class Library DLL from another Project ....................................................................................46 The Complete SMTP.VB File ....................................................................................................................................................................47 The Complete POP3.VB File .....................................................................................................................................................................47 The Complete Utiliti es.VB File...................................................................................................................................................................53 Conclusion................................................................................................................................................................. 58 About the Author .............................................................................................................................................................. 59

Send (SMTP) and Retrieve (POP3) Email with Ease under VB.NET

Embed Size (px)

DESCRIPTION

This sample excerpt show you how to easily process, encode, and decode email. It also shows you how to easily perform complex outbound SMTP email, and introduces an inbound POP3 email class that is missing in VB.NET.

Citation preview

  • Page 1

    Send (SMTP) and Retrieve (POP3) Email with Ease under VB.NET

    By David Ross Goben Copyright 2011 by David Ross Goben All rights reserved. Last Update: Monday, July 16, 2012 This is a sample excerpt from the free PDF e-book, Enhancing Visual Basic .NET Far Beyond the Scope of Visual Basic 6.0, by David Ross Goben. Download this e-book, and its free companion, Navigating Your Way Through Visual Basic 6.0 Upgrades to Visual Basic .NET, also by David Ross Goben, at www.slideshare.net/davidrossgoben. They are also available on Google Docs at https://docs.google.com/leaf?id=0B_Dj_dKazINlN2JlY2EwMmEtNGUyMy00NzQzLTliN2QtMDhlZTc5NDUzY2E5&sort=name&layout=list&num=50. The Google site also has a VBNetEmail.zip source file package featuring all VB.NET classes and utilities.

    Table of Contents Send (SMTP) and Retrieve (POP3) Email with Ease under VB.NET................................................................................... 2 Adding the VB6 MAPISession and MAPIMessage Controls to VB.NET............................................................................................................2 PART ONE Sending Email under VB.NET using Native Methods ................................................................................................................................3 Quick and Dirty Email Senders ..........................................................................................................................................................3 TCP Ports, SSL Authentication, and Creating Credentials ...............................................................................................................5 An Email Sender with a Lot of Muscle...............................................................................................................................................6 Sending Email Messages as HTML ..................................................................................................................................................8 Sending Alternate Message Views ..................................................................................................................................................10 Sending Alternate Message Views with Different Context Types and Transfer Encoding ............................................................13 Typical Email Server Specifications ..........................................................................................................................................................17 PART TWO Encoding and Decoding Email Data .........................................................................................................................................................18 Allowing Users to Specify Content-Type and Content-Transfer-Encoding Options.......................................................................19 Determining if Text can be Encoded As Quoted-Printable, Base64, or 7Bit ..................................................................................21 Converting 8-Bit HTML Data to 7-Bit without Loss of Integrity .......................................................................................................21 Converting 8-Bit Text Data to 7-Bit without Data Loss....................................................................................................................22 Decoding Quoted-Printable Text......................................................................................................................................................23 Translating Base64 Data Back to Its Original Format .....................................................................................................................23 Translating BinHex Data Back to its Original Format......................................................................................................................26 PART THREE Receiving Email under VB.NET using Native Methods............................................................................................................................27 Connecting to a POP3 Server..........................................................................................................................................................30 Checking for a POP3 Server Response ..........................................................................................................................................31 Checking for Being Connected to a POP3 Server ..........................................................................................................................31 Getting a Response from the POP3 Server ....................................................................................................................................31 Submitting a Request to the POP3 Server ......................................................................................................................................33 Disconnecting from the POP3 Server ..............................................................................................................................................33 Getting Email Statistics from the POP3 Server ...............................................................................................................................34 Getting an Email Reference List from the POP3 Server.................................................................................................................34 Get an Email Header from the POP3 Server...................................................................................................................................35 Retrieve an Email from the POP3 Server ........................................................................................................................................36 Deleting an Email from the POP3 Server ........................................................................................................................................36 Reset (Undo) All Deletes from the POP3 Server ............................................................................................................................37 Send a Keep-Alive NOOP Command to the POP3 Server ...........................................................................................................37 Disposing of Resources ...................................................................................................................................................................37 Using the Completed POP3 Class...................................................................................................................................................38 PART FOUR Email Data Blocks Made Easy ..................................................................................................................................................................39 Easily Extracting the Component Parts from an Email File .............................................................................................................42 Compiling Everything into an Email Class Library....................................................................................................................................44 Building the VBNetMail Class Library ...............................................................................................................................................46 Accessing your New VBNetEmail Class Library DLL from another Project ....................................................................................46 The Complete SMTP.VB File ....................................................................................................................................................................47 The Complete POP3.VB File.....................................................................................................................................................................47 The Complete Utilities.VB File...................................................................................................................................................................53 Conclusion................................................................................................................................................................. 58 About the Author .............................................................................................................................................................. 59

  • Page 2

    Send (SMTP) and Retrieve (POP3) Email with Ease under VB.NET Brays whisper distantly beneath midnight mists as spent developers, pinned by looming deadlines and their brains threat of total collapse, rasp desperate prayers against an ominous sense of impending doom, working at a fevers pitch to hammer out viable code (which, according to Murphys Law of Looming Deadlines, is an absolute impossibility). Suddenly, in utter horror, they crash into a brick wall; and this after having assured their skeptical client of how superior VB.NET was over that antiquated VB6 the client revered. They realize too late that their strategy for the clients email handler is unworkable: VB.NET does not provide the MAPI controls they thought it did. Another soul-torn howl trebles against the muffled parapets of the valley. Plain and simple, MAPI (Messaging Application Program Interface) is not yet a .NET technology; it is still COM technology (Common Object Model), used by ASP (Active Server Page), IIS (Internet Information Server), and any other COM application that access email. Being COM-based, you should not expect to see the VB.NET Toolbox sport controls such as VB6s MAPISession or MAPMessage. Under the VB6 implementation of MAPI, it used the MAPISession control to (what else?) manage a MAPI session. The MAPIMessage control was used to process email messages, both incoming (POP3; Post Office Protocol Version 3) and outgoing (SMTP; Simple Mail Transfer Protocol). Presently, .NET is set up for outbound email, but it lacks a class supporting inbound email, even though both of these technologies are simple TCP Clients (Transmission Control Protocol). I think it may have something to do with many people wanting to read email using eye-candy apps, such as Outlook or Mail. But this does not remove the need for an inbound class in more controlled environments. Although it is easy to write VB.NET code to support POP3 Inbound Email services, as I will show you, I have found only one other person (I have since found more, but none provide robust solutions), and he works at Microsoft, who has developed any sort of VB.NET code to demonstrate this ability (albeit his solution was just a simple example with very limited capability). But by the time I found his article within the catacombs of MSDN, I had already put the finishing touches on my own full-featured POP3 Inbound Email solution.

    Adding the VB6 MAPISession and MAPIMessage Controls to VB.NET But before diving into these VB.NET solutions, let us first take a look at the kind support that is presently available to developers who are upgrading VB6 MAPI applications to VB.NET. When you upgrade a VB6 MAPI application to VB.NET, you will notice that the upgraded application will still have the VB6 MAPISession and MAPIMessage controls on any form that had them before. This is because their control sources have been copied locally and are referenced internally. Under .NET, a copy of the COM-based MSMAPI32.DLL is converted into a non-COM version (its DLLRegisterServer() entry is disabled) and saved to a project-local file named Interop.MSMAPI32.DLL. But, because both VB6 controls actually accessed this DLL provider through the MSMAPI32.OCX ActiveX interface, another project-local non-COM DLL named AxInterop.MSMAPI32.DLL is internally compiled by .NET that will duplicate both the ActiveX visual Interface construction services for the controls, as well as the function mapping services to the new Interop.MSMAPI32.DLL. Having found these controls on their upgraded applications, many developers also want to add them to other VB.NET projects so they can take advantage of them there, but they cannot seem to find a way to easily access the new DLLs from those new projects. It is doable, but it requires numerous coding hacks. But relax. Why not just add these two VB6 controls to your VB.NET Toolbox and access them directly?

    1. With any form up on the Visual Studio screen so that the IDE toolbox is active, right-click a toolbox category you want to add the MAPI controls to (if you want to add them to their own category, such as to one named COM, right-click any category and select the Add Tab option, then type the name of your category, such as COM, press ENTER, then right-click that tab).

    2. Select the Choose Items option, and wait for the IDE to build a massive control reference list from the computer. 3. Once the Choose Toolbox Items dialog is finally displayed select the COM Components tab. 4. Scroll down and put checkmarks in the check boxes for Microsoft MAPI Messages Control, Version 6.0, and Microsoft MAPI

    Sessions Control, Version 6.0. (Both of these are actually linked to MSMAPI32.OCX, which in turn drilled down to MAPI32.DLL, but they will now both link to a new .NET-compiled axInterop.MSMAPI32.DLL, which then drills down to Interop.MSMAPI32.DLL).

    5. Click the OK button, and you will find these two controls now in your selected Toolbox category list, and you can begin using these controls just exactly as you would had been using them under VB6.

  • Page 3

    NOTE: If you do not find these entries in the Choose Toolbox Items dialog box, then you may not or no longer have the VB6 redistributables on your system, so you will have to minimally install the free Runtime Distribution Pack for Service Pack 6 for Visual Basic 6.0, available from Microsoft (www.microsoft.com/downloads/details.aspx?FamilyId=7B9BA261-7A9C-43E7-9117-F673077FFB3C&displaylang=en). You are allowed to do this even if you no longer own VB6. You should also install the Microsoft Visual Basic 6.0 Service Pack 6 Cumulative Update (www.microsoft.com/download/en/details.aspx?amp;displaylang=en&id=7030).

    PART ONE Sending Email under VB.NET using Native Methods VB.NET has its own Outbound SMTP Email class that supports sending email, and without a need to add the more resource-hungry form controls, as we had to do with VB6. Because this technique is more accessible than supporting inbound email under VB.NET, we will first look at sending email out. Some people think that you simply hit a system-linked Send button and a message they had just typed is automatically launched into the labyrinths of the internet with possibly little or no code from you. Were that it be so easy. But hopefully you will now be able to make your clients think you made it so. Back in the old days of software engineering, say the early 1990s, we processed email through a thing called a Berkeley Socket (circa 1983). This socket simply described the endpoint of a bidirectional inter-process communication flow across an Internet Protocol-based network. It was sometimes a real trick to program for, depending on the platform, but when it functioned correctly, it was a work of art. In a pinch we launched a TelNet client and manually typed the various commands to log on to an email server, send, receive, and read email, and finally disconnect. Those were cryptic and unforgiving days. But looking back to those times, I have to wonder if we were either brilliant geniuses or major drool-monkeys, because we thought back in those younger and smarter years that it was all simple childs play.

    Quick and Dirty Email Senders Nowadays, we have built-in tools to do most of the hard stuff for us, such as the System.Net Namespace. This class library provides the .NET SMTP Outbound Mail class. To use it, in the heading of your form or module, above the class declaration of the file you want to implement it in, I will ask you to add this line: Imports System.Net, System.Text, VB = Microsoft.VisualBasic 'Most the code in this article REQUIRES this Imports line!

    NOTE: Some people import System.NET.Mail just to avoid typing Mail. later in their code, but we will also need access to the System.Net.Mime namespace, and even later the System.Net.Sockets namespace. Note that the third part of the line, the declaration of VB, is not really necessary, but BOY is it handy to have in most every class or module we write. NOTE: MIME (or Mime) is an anagram for Multipurpose Internet Mail Extensions. First, if you want to send a fast note to someone, most servers will allow you to use the following method: '*******************************************************************************

    ' Function Name : BrainDeadSimpleEmailSend ' Purpose : Send super simple email message (works with most SMTP servers) '===============================================================================

    'NOTES: strFrom : Full email address of who is sending the email. ie, David Dingus ' strTo : Full email address of who to send the email to. ie, "Bubba Dingus" ' strSubject: Brief text regarding what the email concerns. ' strBody : text that comprises the message body of the email. ' smtpHost : This is the email host you are using for sending emails, such ' : as "smtp.comcast.net", "authsmtp.juno.com", etc. '*******************************************************************************

    Public Sub BrainDeadSimpleEmailSend(ByVal strFrom As String, _ ByVal strTo As String, _ ByVal strSubject As String, _ ByVal strBody As String, _ ByVal smtpHost As String) Dim smtpEmail As New Mail.SmtpClient(smtpHost) 'create new SMTP client using TCP port 25 smtpEmail.Send(strFrom, strTo, strSubject, strBody) 'send email End Sub

    NOTE: The FROM and TO email addresses can be simple, such as [email protected], or more trendy formats, such as Bernard Shaw Fullo or even Coat Mahatma . If the data contains angle brackets, the Mail object will use only the data contained within them. If there are no angle brackets, then the mail object will surround the data with angle brackets, assuming that the entire text is an email address.

  • Page 4

    NOTE: Yahoo, Gmail, HotMail, and Juno are internet-based services providing both internet and SMTP/POP3 access. Unlike the other three, Yahoo, normally free, requires an additional monthly fee for SMTP/POP3 access. Juno and HotMail provide this service freely to their subscribers. Gmail, a free service, provides it if you set an option in the POP Download section of the Forwarding and POP/IMAP option within its internet account Settings. You will, of course, have to set up SMTP/POP3 accounts and access within your favorite local email application for all four, such as Windows Mail, Outlook Express, or Outlook. The above method actually works for most SMTP servers. For example, Comcast and Juno both support this interface. I use this for quick messages (though they can also be major literary works), like most people post text messages on their cell phones. However, I think it would be a bit difficult to drive down the road with a desktop PC and keyboard in hand, trying to steer while I thumb a quick message. NOTE: Texting while driving is illegal here in Florida, as it should be. In 2010 I witnessed 6 accidents and 1 fatality due to driver texting, primarily by young people, though I must concede that they could have been more distracted by their stereo systems blasting so loudly that it made both their eyes bounce from one side of their head to the other, impairing their vision. The SMTP Host is the address of your email providers SMTP server. SMTP is a TCP/IP (Transmission Control Protocol/Internet Protocol) process used for sending and receiving email. However, because SMTP is limited in its capability to queue messages at its receiving end, it is typically used with one of two other protocols, like POP3 or IMAP (Internet Message Access Protocol). But that is a topic we will cover after we resolve the email sending issues that many thousands of developers are presently having. For a much more robust method that supports most-all servers, including those that use security layers, like Gmail, you might try the following method to send a quick email with no attachments: '*******************************************************************************

    ' Function Name : QuickiEMail ' Purpose : Send a simple email message (but packed with a lot of muscle) '===============================================================================

    'NOTES: strFrom : Full email address of who is sending the email. ie, David Dingus ' strTo : Full email address of who to send the email to. ie, "Bubba Dingus" ' strSubject: Brief text regarding what the email concerns. ' strBody : text that comprises the message body of the email. ' smtpHost : This is the email host you are using for sending emails, such ' : as "smtp.gmail.com", "smtp.comcast.net", "authsmtp.juno.com", etc. ' smtpPort : TCP Communications Port to use. Most servers default to 25, though 465 (SSL) or 587 (TLS) are becoming popular. ' usesSLL : If this value is TRUE, then use SSL/TLS Authentication protocol for secure communications. ' SSLUsername: If usesSLL is True, this is the username to use for creating a credential. Leave blank if the same as strFrom. ' SSLPassword: If usesSLL is True, this is the password to use for creating a credential. If this field and SSLUsername ' : are blank, then default credentials will be used (only works on local, intranet servers). ' SSLDomain : If creating a credential when a specific domain is required, set this parameter, otherwise, leave it blank. '*******************************************************************************

    Public Function QuickiEMail(ByVal strFrom As String, _ ByVal strTo As String, _ ByVal strSubject As String, _ ByVal strBody As String, _ ByVal smtpHost As String, _ Optional ByVal smtpPort As Integer = 25, _ Optional ByVal usesSSL As Boolean = False, _ Optional ByVal SSLUsername As String = vbNullString, _ Optional ByVal SSLPassword As String = vbNullString, _ Optional ByVal SSLDomain As String = vbNullString) As Boolean Try Dim smtpEmail As New Mail.SmtpClient(smtpHost, smtpPort) 'create new SMTP client smtpEmail.EnableSsl = usesSSL 'true if SSL Authentication required If usesSSL Then 'SSL authentication required? If Len(SSLUsername) = 0 AndAlso Len(SSLPassword) = 0 Then 'if both SSLUsername and SSLPassword are blank... smtpEmail.UseDefaultCredentials = True 'use default credentials Else 'otherwise, we must create a new credential If Not CBool(Len(SSLUsername)) Then 'if SSLUsername is blank, use strFrom smtpEmail.Credentials = New NetworkCredential(strFrom, SSLPassword, SSLDomain) Else smtpEmail.Credentials = New NetworkCredential(SSLUsername, SSLPassword, SSLDomain) End If End If End If smtpEmail.Send(strFrom, strTo, strSubject, strBody) 'send email using text/plain content type and QuotedPrintable encoding Catch e As Exception 'if error, report it MsgBox(e.Message, MsgBoxStyle.OkOnly Or MsgBoxStyle.Exclamation, "Mail Send Error") Return False 'return a failure flag End Try Return True 'if no error, then return a success flag End Function

    With the QuickiEMail() method, you can send full, plain text message to one recipient with no attachments, hence; quick and dirty. With this method, you supply it with the full email address for whom the email is from (you, for example), you also provide the full email address of the person you are sending it to, a subject for the email (what it is concerning), the body of the text message, and the SMTP host (such as smtp.comcast.net, authsmtp.juno.com, or smtp.gmail.com).

  • Page 5

    If you must use a TCP Port other than Port 25, such as Gmail requires (using Secure TCP Port 587), then include the needed port number. Also, if you will be using a secure TCP Port, then you will also require SSL Authentication, so include Yes for the Boolean usesSSL flag. By setting usesSSL to True, this will in turn mean that you will need to also supply credentials. Do not panic. This is easy. Just look below.

    TCP Ports, SSL Authentication, and Creating Credentials By default, most Outbound Servers use TCP Port 25, and they use TCP Port 110 for their Inbound Server, providing unencrypted, non-secure email transactions. However, some may differ, such as Gmail, as mentioned above, which uses secure TCP Port 587 for their Outbound Server and TCP Port 995 for their Inbound Server. But this is primarily because Gmail, and some others, requires an SSL (Secure Socket Layer) to process mail. In these cases you will need to set the optional parameter to that TCP Port, such as in the following hard-coded example: Dim smtpEmail As New Mail.SmtpClient("smtp.EngulfNDevour.com", 465) 'Create new SMTP client using Secure TCP Port 465. smtpEmail.EnableSsl = True 'True if SSL authentication required. SmtpEmail.UseDefaultCredentials = True 'Typical for NTLM, negotiate, and Kerberos-based authentication.

    NOTE: Yes, yes, I know; TCP port 587 uses a newer breed of security called TLS (Transport Layer Security). But first, it is still an SSL. Second, SSL technology provides identical security. And third, you are not going to find an EnableTls property in the Mail object. Refer to www.sans.org/reading_room/whitepapers/protocols/ssl-tls-beginners-guide_1029. The above DefaultCredentials property represents the system credentials for the current security context in which the application is running. For a client application, these are usually the Windows credentials (username, password, and domain) of the user running the application (for ASP.NET applications, the default credentials are the user credentials of the logged-in user, or the user being impersonated). NOTE: To examine your default credentials, access the System.Net.CredentialCache.DefaultNetworkCredentials property. However, setting the UseDefaultCredentials property to True will apply only to a Microsoft NT LAN Manager (NTLM) using intranet-based Negotiate authentication and Kerberos-based authentication. All others will have to create a credential. This is no big whoop, as you are about to see. NOTE: To make sure that IIS supports both the Kerberos protocol and the NTLM protocol, you must confirm that the Negotiate security header is set in the NTAuthenticationProviders metabase property. For Negotiate authentication to function correctly, several exchanges must take place on the same connection. Therefore, Negotiate authentication cannot be used if an intervening proxy does not support keep-alive connections. If this is not understood, it should not concern you. NOTE: The centralized account management supported by NT Active Directory Services requires a corresponding authentication protocol for network log-on. Based on RFC 1510 (www.ietf.org/rfc/rfc1510.txt), the Kerberos protocol provides enhanced authentication for the distributed computing environment and standardization to interoperate with other operating systems. FUNNY DIGRESSION: the Term NT stands for New Technology. It was adopted when Microsoft and IBM parted on their joint OS2 venture. IBM, slow about everything (a self-study showed it took them 9 weeks to ship an empty box), they refused to adopt the revolutionary, advanced technology Microsoft was quickly developing without it being time-tested (meaning proven; this is why NASA uses 20-year-old technology), so Microsoft made its own version of OS2 that used it, naming it NT. Now, think about how many times you have read or heard even Microsoft mentioning the term NT Technology? If you are not accessing a local IIS (intranet) SMTP server, you will need to supply a new credential for an SSL SMTP client through a new NetworkCredential Object with a Username and Password provided to it (if Domains differ, then that must be supplied as well), as shown in this hand-coded example: Dim smtpEmail As New Mail.SmtpClient("smtp.gmail.com", 587) 'create new Gmail SMTP client with SSL (actually TLS) for outgoing eMail smtpEmail.EnableSsl = True 'True if SSL/TLS authentication required smtpEmail.Credentials = New NetworkCredential("[email protected]","MomBNipp0neze") 'new credential with Username, Password

    NOTE: You cannot use decorated usernames for creating a network credential. You will not be able to use something like Tukool Firwurds . You would have to provide just the actual email address: [email protected]. Although the above methods work in most domains, highly secure domains may require more than an SSL certificate to reach the outside world. For example, if you are a minion at the Engulf & Devour Credit Corp., you may need to apply code that bypasses massive firewalls and multi-layer proxies, which any high school youth worth their salt can usually break through before Second Period.

  • Page 6

    As indicated, you need to create a NetworkCredential object if you access a server through an SSL/TLS layer, such as Gmail or HotMail, because you will not be able to use default credentials. Compare these examples, demonstrating default access through plain and TLS secure Comcast servers: QuickiEMail("[email protected]", "[email protected]", "Letter to the Editor", "Your paper lines my dog cages.", "smtp.comcast.net") 'plain QuickiEMail("Bob ", "[email protected]", "Ltr 2 Ed", "Yr ppr lns m dg cgz 2.", "smtp.comcast.net", 587, True, "Idjut", "pSSwd#6") 'secure

    NOTE: Gmail, like a few other TLS servers, require the users FULL email address for their certificates (includes @gmail.com). Leaving the SSLUsername field blank, the users email address (as long as it is not decorated) will be used. But even so, just the above QuickiEMail() method supports most emails that people need to transmit, and is, in fact, all the outgoing email support than a great deal of people will ever require.

    An Email Sender with Some Muscle If you may have multiple recipients, multiple optional BCC (Blind Carbon Copy) recipients, multiple optional CC (Carbon Copy) recipients, multiple attachments, or if you want to send the body text as HTML format, or send alternate views of the message body, you will require a method with a whole lot more muscle, like the following SendEmail() method: '*******************************************************************************

    ' Function Name : SendEMail ' Purpose : Send a more complex email message '===============================================================================

    'NOTES: strFrom : Full email address of who is sending the email. ie, "David Dingus " ' strTo : Full email address of who to send the email to. ie, "Bubba Dingus " ' : If multiple recipients, separate each full email address using a semicolon (;) ' strSubject: Brief text regarding what the email concerns. ' strBody : text that comprises the message body of the email. May be raw text or HTML code ' IsHTML : True if the strBody data is HTML, or the type of data that would be contained within an HTML Body block. ' smtpHost : This is the email host you are using for sending emails, such ' : as "smtp.gmail.com", "smtp.comcast.net", "authsmtp.juno.com", etc. ' AltView : A System.Net.Mail.AlternateView object, such as Rich Text or HTML. ' : If need be, set AltView.ContentType.MediaType and AltView.TransferEncoding to properly format the AlternateView. ' : For example: AltView.ContentType.MediaType = Mime.MediaTypeNames.Text.Rtf ' : AltView.TransferEncoding = Mime.TransferEncoding.SevenBit ' StrCC : Send "carbon copies" of email to this or these recipients. ' : If multiple recipients, separate each full email address using a semicolon (;) ' strBcc : Blind Carbon Copy. Hide this or these recipients from view by others. ' : If multiple recipients, separate each full email address using a semicolon (;) ' strAttachments: A single filepath, or a list of filepaths to send to the recipient. ' : If multiple attachments, separate each filepath using a semicolon (;) (C:\my data\win32.txt; c:\jokes.rtf) ' : The contents of the attachments will be encoded and sent. ' : If you wish to send the attachment by specifying content type (MediaType) and content transfer encoding ' : (Encoding), then follow the attachment name with the MediaType and optional encoding (default is ' : application/octet-stream,Base64) by placing them within parentheses, and separated by a comma. For example: ' : C:\My Files\API32.txt (text/plain, SevenBit); C:\telnet.exe (application/octet-stream, Base64) ' : Where: The MediaType is determined from the System.Net.Mime.MediaTypeNames class, which ' : can specify Application, Image, or Text lists. For example, the above content type, ' : "text\plain", was defined by acquiring System.Net.Mime.MediaTypeNames.Text.Plain. ' : The second parameter, Encoding, is determined by the following the values specified by the ' : System.Net.Mime.TrasperEncoding enumeration: ' : QuotedPrintable (acquired by System.Net.Mime.TransferEncoding.QuotedPrintable.ToString) ' : Base64 (acquired by System.Net.Mime.TransferEncoding.Base64.ToString) ' : SevenBit (acquired by System.Net.Mime.TransferEncoding.SevenBit.ToString) ' smtpPort : TCP Communications Port to use. Most servers default to 25. ' usesSLL : If this value is TRUE, then use SSL Authentication protocol for secure communications. ' SSLUsername: If usesSLL is True, this is the username to use for creating a credential. Leave blank if the same as strFrom. ' SSLPassword: If usesSLL is True, this is the password to use for creating a credential. If this field and SSLUsername ' : are blank, then default credentials will be used (only works on local, intranet servers). ' SSLDomain : If creating a credential with a specific domain is required, set this parameter, otherwise, leave it blank. '*******************************************************************************

    Public Function SendEMail(ByVal strFrom As String, _ ByVal strTo As String, _ ByVal strSubject As String, _ ByVal strBody As String, _ ByVal IsHTML As Boolean, _ ByVal smtpHost As String, _ Optional ByVal AltView As Mail.AlternateView = Nothing, _ Optional ByVal strCC As String = vbNullString, _ Optional ByVal strBcc As String = vbNullString, _ Optional ByVal strAttachments As String = vbNullString, _ Optional ByVal smtpPort As Integer = 25, _ Optional ByVal usesSSL As Boolean = False, _ Optional ByVal SSLUsername As String = vbNullString, _ Optional ByVal SSLPassword As String = vbNullString, _ Optional ByVal SSLDomain As String = vbNullString) As Boolean

    Dim Email As New Mail.MailMessage 'create a new mail message With Email .From = New Mail.MailAddress(strFrom) 'add FROM to mail message (must be a Mail Address object) '-------------------------------------------

    Dim Ary() As String = Split(strTo, ";") 'add TO to mail message (possible list of email addresses; separated each with ";") For Idx As Integer = 0 To UBound(Ary) If Len(Trim(Ary(Idx))) 0 Then .To.Add(Trim(Ary(Idx))) 'add each TO recipent (primary recipients) Next '-------------------------------------------

    .Subject = strSubject 'add SUBJECT text line to mail message '-------------------------------------------

    .Body = strBody 'add BODY text of email to mail message.

  • Page 7

    .IsBodyHtml = IsHTML 'indicate if the message body is actually HTML text. '-------------------------------------------

    If AltView IsNot Nothing Then 'if an alternate view of plain text message is defined... .AlternateViews.Add(AltView) 'add the alternate view End If '-------------------------------------------

    If CBool(Len(strCC)) Then 'add CC (Carbon Copy) email addresses to mail message Ary = Split(strCC, ";") '(possible list of email addresses, separated each with ";") For Idx As Integer = 0 To UBound(Ary) If Len(Trim(Ary(Idx))) 0 Then .CC.Add(Trim(Ary(Idx))) 'add each recipent Next End If '-------------------------------------------

    If CBool(Len(strBcc)) Then 'add Bcc (Blind Carbon Copy) email addresses to mail message Ary = Split(strBcc, ";") '(possible list of email addresses; separated each with ";") For Idx As Integer = 0 To UBound(Ary) If Len(Trim(Ary(Idx))) 0 Then .Bcc.Add(Trim(Ary(Idx))) 'add each recipent (hidden recipents) Next End If '-------------------------------------------

    If CBool(Len(strAttachments)) Then 'add any attachments to mail message Ary = Split(strAttachments, ";") '(possible list of file paths, separated each with ";") For Idx As Integer = 0 To UBound(Ary) 'process each attachment Dim attach As String = Trim(Ary(Idx)) 'get attachment data If Len(attach) 0 Then 'if an attachment present... Dim I As Integer = InStr(attach, "(") 'check for formatting instructions If CBool(I) Then 'formatting present? Dim Fmt As String 'yes, so set up format cache Fmt = Mid(attach, I + 1, Len(attach) - I - 1) 'get format data attach = Trim(VB.Left(attach, I - 1)) 'strip format data from the attachment path Dim Atch As New Mail.Attachment(attach) 'create a new attachment Dim fmts() As String = Split(Fmt, ",") 'break formatting up For I = 0 To UBound(fmts) 'process each format specification Fmt = Trim(fmts(I)) 'grab a format instruction If CBool(Len(Fmt)) Then 'data defined? Select Case I 'yes, so determine which type of instruction to process Case 0 'index 0 specified MediaType Atch.ContentType.MediaType = Fmt 'set media type to attachment Case 1 'index 1 specifes Encoding Select Case LCase(Fmt) 'check the encoding types and process accordingly Case "quotedprintable", "quoted-printable" Atch.TransferEncoding = Mime.TransferEncoding.QuotedPrintable Case "sevenbit", "7bit" Atch.TransferEncoding = Mime.TransferEncoding.SevenBit Case Else Atch.TransferEncoding = Mime.TransferEncoding.Base64 End Select End Select End If Next .Attachments.Add(Atch) 'add attachment to email Else .Attachments.Add(New Mail.Attachment(attach)) 'add filepath (if no format specified, encoded in effiecient Base64) End If End If Next End If End With '-----------------------------------------------------------------------

    'now open the email server... Try Dim SmtpEmail As New Mail.SmtpClient(smtpHost, smtpPort) 'create new SMTP client on the SMTP server SmtpEmail.EnableSsl = usesSSL 'true if SSL Authentication required If usesSSL Then 'SSL authentication required? If Len(SSLUsername) = 0 AndAlso Len(SSLPassword) = 0 Then 'if both SSLUsername and SSLPassword are blank... SmtpEmail.UseDefaultCredentials = True 'use default credentials Else 'otherwise, we must create a new credential If Not CBool(Len(SSLUsername)) Then 'if SSLUsername is blank, use strFrom SmtpEmail.Credentials = New NetworkCredential(strFrom, SSLPassword, SSLDomain) Else SmtpEmail.Credentials = New NetworkCredential(SSLUsername, SSLPassword, SSLDomain) End If End If End If SmtpEmail.Send(Email) 'finally, send the email... Catch e As Exception 'if error, report it MsgBox(e.Message, MsgBoxStyle.OkOnly Or MsgBoxStyle.Exclamation, "Mail Error") Return False 'return failure flag End Try Return True 'return success flag End Function

    Notice that in this version we have added more recipients. A lot more. There is a TO list, a Carbon Copy list (CC), and a Blind Carbon Copy list (BCC; not viewable by TO or CC recipients). The CC list is an archaic vestige that just forgot to go away. It was essential back in the days when you could specify only a single email address in the TO field. The BCC was essential for sending copies to other concerned parties, but it was not essential, or the sender did not want the TO or CC viewers to know that these BCC recipients were being sent copies. Some claim there is no use for a BCC list, but I beg to differ. NOTE: Microsoft Mail features BCC, but it does not seem to work (under Vista, anyway).

  • Page 8

    The important thing to notice about these three recipient fields is that you separate each recipient with a semicolon (;). Notice also that in my code that I did a check on each after I split them into an array to ensure that a field was not empty. Most email applications simply slap a semicolon on the tail of each email address, so splitting them into an array may leave any last array element empty. The Attachments, strAttachments, we handle just like TO, CC and BCC. The file paths to the attachments are separated by semicolons. The Mail.Attachments collection object takes care of loading the actual file data. Attachments are appended to the end of the email. As noted in the comments above the method, you can also declare the encoding and display formats for an attachment; otherwise they will default to binary (application/octet-stream) and encoded using the Base64 method, which, as all, converts them to encoded 7-bit text, which all emails must be formatted to for internet processing. An email is actually a series of bytes (in email lingo, these are octets; 8-bits), formatted as 7-bit ASCII text (ergo, the 8th bit is never used). As such, even binary attachments are encoded into blocks of ASCII text, sometimes formatted as ASCII Hexadecimal (hex; 0 through 9, and A through F, allowing for a Base16 numbering system, though this doubles the data size), where each byte is represented by two 7-bit characters, and each character represents a nibble, or 4 bits (computer engineers must always be hungry). However, many servers now support various types of encoding, like Base64, to better transport 8-bit/binary data in the 7-bit-only catacombs of the internet (at a cost of the datas footprint being about 25% larger). For example, my API32.txt file, which, by its extension, is hopefully a text file, looks like gobbledygook at the bottom of my email when converted using the default Base64 encoding. Here is a sampling of its beginning: NOTE: My comments in the SendEmail() method header regarding attachments, where I discuss formatting the attachment to different content types and encoding, we will leave for later, when I actually discovered these solutions. From: [email protected] To: [email protected] Date: 21 Feb 2011 21:16:00 -0500 Subject: Test Content-Type: multipart/mixed; boundary=--boundary_0_5fbcc36e-0097-412e-bf2b-c4dc5bc543d0

    ----boundary_0_5fbcc36e-0097-412e-bf2b-c4dc5bc543d0 Content-Type: text/plain; charset=us-ascii Content-Transfer-Encoding: quoted-printable

    This is just a test ----boundary_0_5fbcc36e-0097-412e-bf2b-c4dc5bc543d0 Content-Type: application/octet-stream; name=API32.txt The Name parameter identifies this as an attachment (and it was handled as a binary stream) Content-Transfer-Encoding: base64 How the file is encoded (I would have rather had this be text/plain, with 7bit encoding)

    JyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0t LS0tLS0tLS0tLS0tLS0tLS0tLS0NCicNCicgICAgIEFQSTMyLlRYVCAtLSBXaW4zMiBBUEkg VHlwZSBEZWNsYXJhdGlvbnMgZm9yIFZpc3VhbCBCYXNpYw0KJw0KJyAgICAgICAgICAgICAg ICAgICAgICAgQ29weXJpZ2h0IChDKSAxOTk2IERlc2F3YXJlDQonDQonICBZb3UgaGF2ZSBh IHJveWFsdHktZnJlZSByaWdodCB0byB1c2UsIG1vZGlmeSwgcmVwcm9kdWNlIGFuZCBkaXN0 ... The encoded definition of several thousand more API declarations, structures, and constants continues from here.

    NOTE: In the back of my mind, when I encountered this during my initial email tests, I was really wondering about this Base64 Content Transfer Encoding. But, we will return to this when we examine alternate views and attachments, where it will make more sense to us. We will also learn how we can fully exploit it and very easily decode it (and also how to avoid it). Two other parameters you may have noticed in the SendEmail() method were IsHTML and AltView. Sending Email Messages as HTML The IsHTML parameter in the SendEMail() method sets the state of the Mail.MailMessage objects Boolean IsBodyHtml flag. If it is set to True, then the SMTP interface will know to set the body text formatting flag to text/html instead of its usual text/plain. It is actually up to email reader software to use that information and determine how to present the data. For example, some plain text readers will simply show the raw data, regardless. However, others will bring up a web interface, such as a WebBrowser control, envelop the text within an HTML body, if it is not already, and present that to the user.

  • Page 9

    This is quite easy to do, but there is a simple test you will need to perform, because some main body HTML is sent without HTML/BODY tags, though most are. The easiest test is to simply check to see if is contained within the message. If not, all you have to do is prepend the text in front of the message, and append the text behind it. And that is all! Consider this test, where String variable Msg is assumed to contain the HTML-formatted text of the message body: If Not CBool(InStr(1, Msg, "", CompareMethod.Text)) Then 'Msg contains an HTML wrapper? Msg = "" & Msg & "" 'no so add one to it End If

    Suppose I sent the following urgent code red email message (note the True for the IsHTML parameter): Dim Msg As String = "This is bold textThis should be underlined" 'some simple HTML text SendEMail("[email protected]", "[email protected]", "Test", Msg, True, "smtp.80micro.com")

    My Gmail account will receive an email with the following at the bottom of the data (sans my notes): From: [email protected] To: [email protected] Date: 21 Feb 2011 21:39:04 -0500 Subject: Test Content-Type: text/html; charset=us-ascii Used second to determine how to display or process the data Content-Transfer-Encoding: quoted-printable Used first to determine how to decode the data

    This is bold textThis should be underlined The HTML formatted text without HTML or BODY tags

    Once I strip out the header data (I will show this later), I end up with a String variable I named Msg that contains the text This is bold textThis should be underlined. I would expose it within my Web Browser interface control, WebBrowser1, using code similar to the following: 'set the web browser to the same location as my usual text display control Me.WebBrowser1.Bounds = Me.RichTextBox1.Bounds Me.RichTextBox1.Visible = False 'hide my plain text/Richtext textbox If Not CBool(InStr(1, Msg, "", CompareMethod.Text)) Then 'Does my Msg contain an HTML wrapper? Msg = "" & Msg & "" 'no so add one to it End If 'set web browser contents to that of my email message body and display it Me.WebBrowser1.DocumentText = Msg Me.WebBrowser1.Visible = True 'expose the web browser

    The two lines of text are dutifully displayed using the HTML formatting I assigned to them. The first line was Bold, and the second line was underlined. The urgent code red message was processed and read in time! The world is once again safe. There is so much more to explore if you truly want to create a full-featured email processor. The Internet Message Format document, RFC 2822 (www.ietf.org/rfc/rfc2822.txt), outlines all the gory details of email formatting.

    I should close this sub-section by saying that the message data on multi-line documents should be further processed to ensure that the data is properly formatted for your web browser, rich text box, or simple text box, such as by decoding special tags that may have been added, such as to represent Unicode text within the simple 7-bit ASCII text format required for email transactions, for example. Data in an email is a series of lines, each terminated by a vbCrLf (Carriage Return and Linefeed codes 13 and 10, respectively). Often these line terminators are not a part of the original text, but are required to limit the line width of the email data. In these cases they are tagged, such as by using a soft return flag, like the equals sign = used by Quoted-Printable encoding. These must be decoded and removed before displaying the text. Also with Quoted-Printable-encoded text, if a space character precedes a vbCrLf, then that space is converted to a special hexadecimal format, =20, which I habitually call Hex-Tags. Also, if there are any 8-bit characters embedded in the message, then you should convert them to Hex-Tags, otherwise the system will automatically encode the data to Base64, regardless of what you really want, such as Quoted-Printable, simply to ensure 100% original data integrity. Fortunately, I will later present very simple functions to allow Quoted-Printable encoding of 8-bit text that will convert any 8-bit codes to 7-bit without losing integrity, as well as decode Quoted-Printable-encoded and Base64-encoded data back to its original form.

  • Page 10

    Sending Alternate Message Views Another thing I want to explore with you is one of the more interesting parameters I have listed for the SendEMail() method, and that is AltView. The AltView parameter is declared as a System.Net.Mail.AlternativeView object. Even though my SendEMail() method presently allows for only one alternative view, which is usually all we ever really need, the .NET SMTP processor will actually allow for as many as you want, in compliance to RFC 2822, as though we would want to spend the rest of our miserable lives toiling over the various formatting of a single email to say Thanks for the $1 on my birthday to Great Aunt Ethel. However, the real reason for this is more mundane: some people simply want to send both a pretty version of their email and a plain text version for those who may want to view them on a cell phone. Technically, most email processors will, by default, display the alternate view, or the first alternate view they can support, leaving the Plain Text version as the last ditch option. Typically, most of us tend to send plain text emails, even if they are in fact formatted by a Rich Text or HTML editor, failing to add emphasis, bolding, italics, or underlining. Phooey! I still remember the thrill I got when I hit a button on my Selectric II Typewriter and it bolded what I typed, or got my first TypeBall we called them golf balls that supported italics. I did not even blink when I had to go though the time and effort of changing the TypeBall just to change fonts. For me, at least, plain text is so monotonous and pass. NOTE: Were I to revert back to the days of no easy bolding, underlining, or italics, I think I would suffer a pulmonary embolism, as may be apparent from reading my document; these features afford us a platform for curt, succinct expression. Most people prefer their emails to be formatted as HTML or as Rich Text, but they also want the option to process their text as Plain Text for those who want to read their email on a cell phone, or who are vision-impaired (Cant see out of one eye, and Im blind as a bat in the other, as Grandpa often said). Most of you are aware that in a VB-written Rich Text editor, using a RichTextBox control, you have easy access to the Plain Text version and the Rich Text version of a document. Its Text property provides the Plain Text version, and its Rtf property provides the Rich Text version. But did you know that accessing plain text from HTML formatting can also be easy? An HTML version can be provided by accessing the DocumentText property of their WebBrowser control, as most of you are already aware. But a Plain Text rendering has always seemed to be an issue. I have seen a number of home-spun HTML editors that provide a Plain Text version of their data by manually stripping out all the HTML Text Tags, plus any special formatting that might be stored within or between them, on top of going through the often arduous task of interpreting all the special HTML Entities in order to provide that simple plain-text version. This adds up to a whole lot of work. NOTE: An HTML Text Tag is a thing starting with , such as or . NOTE: HTML Entities start with an ampersand (&) and end with a semicolon (;), such as < to represent an intentional , (non-breaking space) for a blank space where a space might normally be ignored, plus ;; for an intentional ;. Following is a list of the HTML Reserved Entities: Character Entity Number Entity Name Description " " " quotation mark (?) ' ' ' apostrophe () & & & ampersand (&) < < < less-than ( > > greater-than (>)

    As you can see, there are two versions of tags; one that includes the decimal ASCII code, and one that includes the typical or classic representation. The Entity Number also allows 8- or 16-bit extended characters to be displayed by a 7-bit source.

  • Page 11

    Even though I have seen a number of utilities, both commercial and shareware, that offer this kind of service, you can in fact bypass them and remove better than 75% of that work with this 1-line function: '*******************************************************************************

    ' Function Name : QConvertHTML2Text ' Purpose : Short-Form Convert HTML formatted text to plain text ' :

    ' Returns : Provided a simple HTML source string, it will return a Plain Text ' : string with HTML code removed. '*******************************************************************************

    Public Function QConvertHTML2Text(ByVal HTMLText As String) As String Return RegularExpressions.Regex.Replace(HTMLText.Replace("", " ").Replace(""", """").Replace("'", _ "'"), "]*>", "").Replace(" 10 Then CL -= 7 Dim CR As Integer = Src(Idx + 1) - 48 'do the same for the right hex digit If CR > 10 Then CR -= 7 If Index > UBound(Result) Then ReDim Preserve Result(Index + 255) 'bump by 256 (allow for Index offset) End If Result(Index) = CByte(CL * 16 + CR) 'stuff byte value Index += 1 'bump index Next ReDim Preserve Result(Index - 1) 'set array to final size Return Result 'return the final result End Function

    Like with the DecodeBase64ToBytes() method, mentioned earilier, you can use the returned array to save to a file, or render it to an image, if that is its purpose. However, if you would like to convert to text, if that was its origin, then you can convert the returned Byte array like this: Dim NewStr As String = Encoding.UTF8.GetChars(DecodeBinHex(strData))

  • 27

    PART THREE Receiving Email Under VB.NET using Native Methods This third part of this article is to read email into VB.NET. We are going to do this with ease, because none of the methods I present here are either long or complicated. Once you examine all the methods that we will use to define our email reading class, POP3, you may really start to wonder why Microsoft did not choose to make POP3 processing as easily accessible as SMTP processing. Perhaps they figured, and maybe rightly so, that if anyone can write an email processor that supports the RFC 2045 specification, then they likely had sufficient knowledge to easily handle TCP Clients without an easier front end. There are hundreds of thousands of programmer queries on the internet asking for how access POP3 from VB.NET (and those are just the ones who took time out of their day to say they needed POP3 front end code). Most solutions simply direct you to a few samples of comment-challenged C# or C++ code. Most VB.NET gurus will simply say that POP3 is impossible to do under VB.NET. But because it was impossible, it took me all afternoon to come up with my own VB.NET solution, and it works better than any C# or C++ version I have yet seen. I must admit that after having fully developed this solution, I finally did find a solitary VB.NET article, last updated on 26 March, 2003, on MSDN by Duncan Mackenzie (see Checking My E-mail at http://msdn.microsoft.com/en-us/library/ms973213.aspx), who is the Microsoft Visual Basic .NET Content Strategist for MSDN, which implements a very simple sample solution. His example approach was very rudimentary (I almost said basic no pun intended), slow, it did not provide for SSL certificates, it featured little functionality, and it used a lot of code where I needed only a little just to accomplish the same thing. But I am not knocking him; he was writing this for VB2002, and he was just throwing out an example, not a full implementation. Plus, working with the .NET Framework is much easier now than it was back in the wild and wooly days of ot 3 or even ot 1. My solution was inspired by C# code pieced together by Randy Charles Morin, author of the KBCafe.com blog site (see How to POP3 in C# www.kbcafe.com/articles/HowTo.POP3.CSharp.pdf, circa May 2002, but my current link is to a recently updated version of the article). Randy sifted various C# solutions and tacked together his own. Bill Dean also made a 5 August 2003 C# submission to The Code Project (www.codeproject.com/KB/IP/pop3client.aspx), which was inspired by Agus Kurniawans article on using C# to communicate with a POP3 server, submitted to The Code Project on 19 January, 2002 (www.codeproject.com/KB/IP/popapp.aspx). For all those listed, their code interfaces are very simplistic, and even rougher around the edges than that of Mackenzies work. For example, not one of them could access SSL/TLS accounts, and interpreting email was not addressed (yep, you are going to find a utility here to easily break emails up into its parts). My initial goal was simply to see why those internet gurus, some of whom, based on their writing skills, I can easily imagine sitting there in 3-day-old boxers, scarfing down a bag of M&Ms and flushing the sugar away with Diet Pepsi, were saying that this kind of solution was not possible under VB.NET. After building a VB.NET version familiarly modeled after Randys approach, I then took great pains to significantly optimize the code for both size and speed, and to address limitations he pointed out (which resulted in two complete rewrites of my code), and further enhancing it to accommodate many more functions to conform more fully to the POP3 specifications (see Post Office Protocol Version 3 at www.ietf.org/rfc/rfc1939.txt), to include optional TCP Ports and SSL/TLS support. I finally fully commented the code. By the way, what is it with most developers and their inability to comment their code? Do comments have cooties? NOTE: Cooties was a term first used in a 1917 Service Manual, and is likely to have come from the Malayan word kutu, meaning lice. The trenches of World War I were a veritable breeding ground for every kind of parasitic pest imaginable. We will create a single file, but we will include 3 classes. The file will be a class module. So go ahead and create a class file named POP3.vb. This shall result in the following shell code being generated:

  • 28

    Public Class POP3

    End Class

    We will leave this alone for now and create our other two simple classes below it. The second class, named POP3Message, shall define a message class, which will contain each email message. It will only hold fields, much like its abstract class cousin, a Structure, but we will define it as an object to avoid Boxing as we work with it (Boxing is the process of wrapping a structure within a class shell so that it can be operated on as a reference object, rather than as a value object. This process has a tiny price in time, but is more work than we require). It is declared below: '-------------------------------------------------------------------------------

    ' Class Name : POP3Message ' Purpose : POP3 message data '-------------------------------------------------------------------------------

    Public Class POP3Message Public MailID As Integer = 0 'message number Public ByteCount As Integer = 0 'length of message in bytes Public Retrieved As Boolean = False 'flag indicating if the message has been retrieved Public Message As String = vbNullString 'the text of the message

    Public Overrides Function ToString() As String Return Message End Function End Class

    NOTE: Do not be nervous about adding more than one class to a file. This is perfectly acceptable. In fact, if these other classes were used only within the POP3 class, we could have embedded them within the body of that class, at the same level as its methods. In fact, you might be able to see how a .NET Namespace is structured from this. You will see this concept much more clearly once we put these classes together to create a class library, which we will be able to import into our applications.

    As you can see, the above class object will store the message number as an integer. This is the reference number that the email server assigns to that message, which is normally an incremental index, but we should not make any assumptions about it. It also has a field storing the messages size in bytes. A Boolean flag acts to indicate if we have actually retrieved the message text, or just its references, and then we store the message itself as a string. Because email is transmitted as text, even HTML and Rich Text format, and binary attachments are tacked on as encoded 7-bit ASCII strings, text is usually formatted to certain line widths, and each line is terminated by a Carriage Return and Linefeed (vbCrLf). The email program usually translates this encoding and displays any formatted representation within a text box, rich text box, or web control. Our third class, POP3Exception, is simply an error class, so that we can differentiate POP3 errors from coding errors, so we can detect it in a TryCatch Block, such as by using Catch e As POP3Exception: '-------------------------------------------------------------------------------

    ' Class Name : POP3Exception ' Purpose : process exception ' NOTE : This is a normal exception, but we wrap it to give it an identy ' : that can be associated with our POP3 class '-------------------------------------------------------------------------------

    Public Class POP3Exception Inherits ApplicationException

    Public Sub New(ByVal str As String) MyBase.New(str) End Sub End Class

    It may not look like much, but it holds just as much capability and functionality as any other Exception object, plus we can trap errors that we will know were issued by it. That is the real beauty of inheritance. Now we are ready to construct our main class, POP3. To do everything I want to do in this class, I already know that I will need to import 2 namespaces: 1. I will first import System.Net, from which I can access the Sockets and Security namespaces. The Sockets namespace

    is important because through it I will be able to access a POP3 server though a TC/IP Client class. The Security namespace is important if we will require SSL/TLS authentication, such as Gmail and many others requires.

    2. The second namespace to import is System.Text, because I will need to translate between Unicode strings and Byte arrays. Although it is possible to long-path these namespaces in the code without a compilation cost of even 1 byte, I like to list the namespaces used at the top for self-documentation purposes, to inform reviewers of my use of resources.

  • 29

    I will also need to have local fields that will live as long as the class instance. Among those I will need a Boolean flag that will indicate if we will require SSL authentication or not. We will also need to store two stream objects; one for a standard non-SSL Network Stream, and a possible second SSL Stream. Both of these streams shall be used for POP3 mail server communication. Regardless, the Network Stream is essential for both types, because an SSL piggybacks itself onto a Network Stream. Finally, I want to store the last line read from the server in case we ever want to check it further. Therefore, the main body of my file, with all class bodies, becomes the following: Option Strict On Option Explicit On '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    ' POP3 - Copyright 2011, 2012 by David Ross Goben. '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    'This VB.NET Code was inspired by C# code originally written Randy Charles Morin, 'author of KBCafe.com. '

    ' I have optimized the heck out of the code to speed I/O and program execution, ' forcing a complete rewrite, I have added MANY language and POP3 enhancements, ' cleaned up a lot of clutter, and added Port and SSL support. ' Oh! And I include REAL comments. '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    Imports System.Net, System.Text '-------------------------------------------------------------------------------

    ' Class Name : POP3 ' Purpose : POP3 Interface Class '-------------------------------------------------------------------------------

    Public Class POP3 Inherits Sockets.TcpClient 'this class shall inherit all the functionality of a TC/IP Client

    Dim Stream As Sockets.NetworkStream 'non-SSL stream object Dim UsesSSL As Boolean = False 'True if SLL authentication required Dim SslStream As Security.SslStream 'set to SSL stream supporting SSL authentication if UsesSSL is True Dim SslStreamDisposed As Boolean = False 'true if we disposed of the SSL Stream object Public LastLineRead As String = vbNullString 'copy of the last response line read from the TCP server

    '>>>>>>>>>ANY MORE CODE WILL BE INSERTED HERE HERE

  • 30

    Connecting to a POP3 Server Connecting to the server involves making a connection to the server, typically through generic Port 110, but we must keep it open for other ports in case they are defined (note that email is now trending toward SSL/TLS-encrypted port 995). We then have to submit our email Username and Password to it. We will also want to save the server name off in case we will require later SSL authentication. Connect()is a method that is built into the socket library, so we will have to overload it to add our own customizations on top of it. This is not as scary as it sounds, because we can still access the original method declared for the TCPClient base class that our POP3 class inherited from, which is a good thing, because we will in fact need to invoke it. Connecting to the POP3 Host Server is always the first thing we do. Once we have done that, we will be able to issue all other POP3 commands, until we either remove our connection to the POP3 class, which breaks our connection to the server, or we can explicitly disconnect from the server. We must have this choice in terminating POP3 server communication, as you will learn, in order to provide us with the ability to remove or not removed downloaded emails from the Host Server. We will also require a method to submit queries to the server, which will also mean that we need a resource to check responses from the server (its replies to our queries). Communication with the server is just a long series of our requests and its responses. We will examine each of these parts in their turn. Following is the Connect() method, which references other methods that will be added shortly: '*******************************************************************************

    ' Sub Name : Connect (This is the first the we do with a POP3 object) ' Purpose : Connect to the server using the Server, User Name, Password, ' : and a flag indicating if SSL authentication is required ' :

    ' Returns : Nothing ' :

    ' Typical TelNet I/O: 'telnet mail.domain.net 110 (submit) '+OK POP3 mail.domain.net v2011.83 server ready 'USER myusername (submit) '+OK User name accepted, password please 'PASS mysecretpassword (submit) '+OK Mailbox open, 3 messages (the server locks and opens the appropriate maildrop) '*******************************************************************************

    Public Overloads Sub Connect(ByVal Server As String, _ ByVal Username As String, _ ByVal Password As String, _ Optional ByVal InPort As Integer = 110, _ Optional ByVal UseSSL As Boolean = False)

    If Connected Then Disconnect() ' 'check underlying boolean flag to see if we are presently connected, 'and if so, disconnect that session UsesSSL = UseSSL 'set flag True or False for SSL authentication MyBase.Connect(Server, InPort) 'now connect to the server via our base class Stream = MyBase.GetStream 'before we can check for a response, we first have to set up a non-SSL stream If UsesSSL Then 'do we also need to use SSL authentication? SslStream = _ New Security.SslStream(Stream) 'yes, so build an SSL stream object on top of the non-SSL Network Stream SslStream.AuthenticateAsClient(Server) 'add authentication as a client to the server End If

    If Not CheckResponse() Then Exit Sub ' 'exit if an error was encountered

    If CBool(Len(Username)) Then 'if the username is defined (some servers will reject submissions) Me.Submit("USER " & Username & vbCrLf) 'submit user name If Not CheckResponse() Then Exit Sub ' 'exit if an error was encountered End If

    If CBool(Len(Password)) Then 'if the password is defined (some servers will reject submissions) Me.Submit("PASS " & Password & vbCrLf) 'submit password If Not CheckResponse() Then Exit Sub ' 'exit if an error was encountered End If End Sub

    As you can see, we begin out adventure by initiating communication with the POP3 server. We do this by providing the Connect() method the host server address, our email username (or our whole email address, if your particular server requires it), our email password, a port number if it differs from TCP Port 110, which is becoming common, and True if our POP3 server will require a Secure Socket Layer.

  • 31

    The first thing that Connect() does is check to see if we have a valid POP3 connection. After that, we stow away important references, such as our SSL choice. If we will use SSL, we will set our UsesSSL flag to True. Next, we access the base classs Connect() method and submit the server name we wish to access, such as mail.comcast.net or pop.gmail.com. By default, we submit Port 110 for communication. This is the port most often used by mail servers, though encrypted SSL/TLS connections are growing in popularity. Some servers use a different port, but this is usually because they also use SSL Authentication. Gmail does this. For example, it uses a secure TCP Port 995 for POP3. NOTE: Some servers let anyone and everyone on. In such cases, some few servers sometimes do not even accept usernames and/or passwords and will reject them (or accept any and all responses), or allow you to use a generic response, such as guest or user and password. In those cases, we have to allow for that.

    Checking for a POP3 Server Response We next invoke CheckResponse() to see if a request was accepted. If so, a returned string begins with +OK. Because this test runs rampant throughout a TCP client session, I broken out into a separate function that returns True if it was successful (the response, stored in our public string, LastLineRead, begins with +OK), and False if it was not (an error begins with -ERR). This test follows most other submissions. Here is the CheckResponse() method: '*******************************************************************************

    ' Function Name : CheckResponse ' Purpose : Check the response to a POP3 command ' :

    ' Returns : Boolean flag. True = Success, False = Failure ' :

    ' NOTE : All status responses from the server begin with: ' : +OK (OK; Success, or request granted) ' or : -ERR (NAGATIVE; error) '*******************************************************************************

    Public Function CheckResponse() As Boolean If Not IsConnected() Then Return False ' 'exit if not in TRANSACTION mode LastLineRead = Me.Response 'check response (and save response line) If (Left(LastLineRead, 3) "+OK") Then 'OK? Throw New POP3Exception(LastLineRead) 'no, so throw an exception Return False 'return failure flag End If Return True 'else return success flag End Function

    This method in turn invokes the Response() method. But what is important here is that we get our first look at returned data from the POP3 server, and if we encounter an error, such as the first 3 letters of the response line not being +OK, then we throw our POP3Exception error, transmitting the returned text line to the message pump hidden with the exception object. You may even want to preview what the full responses are by examining the LastLineRead text; the rest of the +OK message can be informative (as are error messages, which all start with -ERR), such as +OK Gpop ready for requests from 12.34.567.89 fahrvergnugen8.0.

    Checking for Being Connected to a POP3 Server The IsConnected() method consolidates a lot of checks to the underlying base classs Boolean Connected property. It throws a POP3ExceptionError if the server is not connected, indicating that it is not in a Transaction state: '*******************************************************************************

    ' Function Name : IsConnected ' Purpose : Return connected to Server state, throw error if not ' :

    ' Returns : Boolean Flag. True if connected to server ' :

    '*******************************************************************************

    Public Function IsConnected() As Boolean If Not Connected Then 'if not connected, throw an exception Throw New POP3Exception("Not Connected to an POP3 Server.") Return False 'return failure flag End If Return True 'Indicate that we are in the TRANSACTION state) End Function

  • 32

    Getting a Response from the POP3 Server The Response() method is where all the really interesting things happen. Here we will finally implement our communication streams to receive real information from the POP3 server. '*******************************************************************************

    ' Function Name : Response ' Purpose : get response from server (read from the mail stream into a buffer) ' :

    ' Returns : string of data from the server ' :

    ' NOTE : If a dataSize value > 1 is supplied, then those number of bytes will be streamed in. ' : Otherwise, the data will be read in a line at a time, and end with the line end code (Linefeed (vbLf) 10 decimal) '*******************************************************************************

    Public Function Response(Optional ByVal dataSize As Integer = 1) As String Dim enc As New ASCIIEncoding 'medium for ASCII representation of Unicode characters Dim ServerBufr() As Byte 'establish buffer Dim Index As Integer = 0 'init server buffer index and character counter If dataSize > 1 Then 'did invoker specify a data length to read? '-------------------------------------------------------

    ReDim ServerBufr(dataSize - 1) 'size to dataSize to read as a single stream block (allow for 0 index) Dim dtsz As Integer = dataSize Dim sz As Integer 'variable to store actual number of bytes read from the stream Do While Index < dataSize 'while we have not read the entire message... If UsesSSL Then 'process through SSL Stream if secure stream sz = SslStream.Read(ServerBufr, Index, dtsz) 'read a server-defined block of data from SSLstream Else 'else process through general TCP Stream sz = Stream.Read(ServerBufr, Index, dtsz) 'read a server-defined block of data from Network Stream End If If sz = 0 Then Return vbNullString ' 'we lost data, so we could not read the string Index += sz 'bump index for data count actually read dtsz -= sz 'drop amount left in buffer Loop Else '------------------------------------------------------ ReDim ServerBufr(255) 'initially dimension buffer to 256 bytes (including 0 offset) Do If UsesSSL Then 'process through SSL Stream if secure stream ServerBufr(Index) = CByte(SslStream.ReadByte) 'read a byte from SSLstream Else 'else process through general TCP Stream ServerBufr(Index) = CByte(Stream.ReadByte) 'read a byte from Network stream End If If ServerBufr(Index) = -1 Then Exit Do ' 'end of stream if -1 encountered Index += 1 'bump our offset index and counter If ServerBufr(Index - 1) = 10 Then Exit Do ' 'done with line if Newline code (10; Linefeed) read in If Index > UBound(ServerBufr) Then 'if the index points past end of buffer... ReDim Preserve ServerBufr(Index + 256) 'then bump buffer another 256 bytes, but keep existing data End If Loop 'loop until line read in End If Return enc.GetString(ServerBufr, 0, Index) 'decode from a byte array into a string and return the string End Function

    Here we read from either the Network Stream (Stream) or the SSL Stream (SslStream), depending on whether we require SSL authentication to communicate with the TCP Server or not. If we do not specify a buffer size, we read the port one byte at a time until we encounter an end of line code (vbLf ; 10). Every C#/C++ example I have ever seen first reads a stream byte into a single-element intermediate buffer array using the stream.Read() method, which requires a destination byte array. However, I have noticed that this can hang until an eventual timeout (usually 30-60 seconds) if the signal drops off, so we end up trying to read beyond the end of the provided stream, which is a more concerning issue when reading one byte at a time. Unlike everyone else, I chose the ReadByte() method instead, which can read to a Byte variable (though I also chose to read it directly into the ServerBufr Byte array instead), but more importantly, it will return a -1 code immediately if we moved beyond the end of the stream or if the connection drops off. If the Stream.ReadByte() method or the SslStream.ReadByte() methods do not report back that they failed to read from their stream, then the index/counter is incremented. We loop until we get an end of a line code (a value of 10, representing a Linefeed character; vbLf, typically following a Carriage Return; vbCr), or if no data was received (it could mean that the signal dropped off). Once it is done reading bytes, the byte array is then translated to a Unicode string (the default string format for VB.NET) and it is returned to the invoker. If a buffer size was supplied, the ServerBufr is immediately dimensioned to that size (-1 is applied for the 0 offset). We then continuously try to fill the buffer full. However, the server actually returns data to us in blocks, not in one big blobby mess of data. For example, the header of an email actually consists of a number of headers, and the body of an email is considered another block, as are attachments and alternate

  • 33

    views. And even then, if the data block is longer than the servers stream buffer, it will break it up into a sequence of sub-blocks. Documentation says that the default maximum stream buffer size is 998 bytes, though it is not uncommon to find buffers that are larger, such as 1400 bytes. This size is implementation-defined, even though our Stream and SslStream objects do have a SetLength property that is supposed to allow you to set the stream buffer size, this is not yet supported: a sad fact the documentation also states.

    Submitting a Request to the POP3 Server The one thing left that we must look at, which is so far unresolved, is the Submit() method: '*******************************************************************************

    ' Sub Name : Submit ' Purpose : Submit a request to the server ' :

    ' Returns : Nothing ' :

    ' NOTE : Command name must be in UPPERCASE, such as "PASS pw1Smorf". ' : "pass pw1Smorf" would not be acceptable, though some few servers do allow for this, we should never assume it. '*******************************************************************************

    Public Sub Submit(ByVal message As String) Dim enc As New ASCIIEncoding 'medium for ASCII representation of Unicode characters Dim WriteBuffer() As Byte = enc.GetBytes(message) 'converts the submitted string into to a sequence of bytes If UsesSSL Then 'using SSL authentication? SslStream.Write(WriteBuffer, 0, WriteBuffer.Length) 'yes, so write SSL buffer using the SslStream object Else Stream.Write(WriteBuffer, 0, WriteBuffer.Length) 'else write to Network buffer using the non-SSL object End If End Sub

    We convert our Unicode request to a Byte array, and then send it to the appropriate stream, depending on whether we use SSL authentication or not. One thing you may not realize is that we can directly access this method and submit a command directly. Just be sure that the command word is uppercase.

    Disconnecting from the POP3 Server A final primary thing that we may need to do, if we want the server to enter its usual UPDATE state, is to disconnect from the POP3 Server. That is accomplished through the Disconnect() method: '*******************************************************************************

    ' Sub Name : Disconnect (This is the last the we do with a POP3 object) ' Purpose : Disconnect from the server and have it enter the UPDATE mode ' :

    ' Returns : Nothing ' :

    ' Typical telNet I/O: 'QUIT (submit) '+OK Sayonara '

    ' NOTE: When the client issues the QUIT command from the TRANSACTION state, ' the POP3 session enters the UPDATE state. (Note that if the client ' issues the QUIT command from the AUTHORIZATION state, the POP3 ' session terminates but does NOT enter the UPDATE state.) '

    ' If a session terminates for some reason other than a client-issued ' QUIT command, the POP3 session does NOT enter the UPDATE state and ' MUST not remove any messages from the maildrop. '

    ' The POP3 server removes all messages marked as deleted from the ' maildrop and replies as to the status of this operation. If there ' is an error, such as a resource shortage, encountered while removing ' messages, the maildrop may result in having some or n