Navigation |
>> Home |
Programming the Sending of Emails yourself
Written by: Christoph Wille There are (almost) as many email components as there are grains of sand on a beach, and many of them even are free. Certainly (almost) everyone has used them, be it AspMail, CDONTS, SA-SmtpMail or one of many others. Something that (almost) no one has done up to now is programming his or her own email component. In today's article we will concern ourselves with the basics and send a very simple email message. In future articles we will build on the source code presented today and will finally have a fully functional component for HTML emails and attachments. Prerequisite for using the source code in this article is an installation of the Microsoft .NET Framework SDK on a Web server. Besides that, presuppose that the reader is familiar with the C# programming language to some degree. A minimalist EmailFirst we have to figure out how the SMTP (Simple Mail Transport Protocol) Protocol works as it is the foundation for the sending of emails. The beauty of it is that all the commands and replies to them from the SMTP server are clear text (which on the other hand might be considered to be a security issue...). The following 'script' shows the basic sequence of SMTP commands necessary for sending an email: HELO local computername MAIL FROM: sender's address RCPT TO: recipient's address DATA mail header and body text . To actually send an email, you have to telnet onto port 25 (the SMTP port, telnet servername 25) on the Email server and type in the example email: HELO SHAGPAD MAIL FROM: christophw@alphasierrapapa.com RCPT TO: christophw@dev.alfasierrapapa.com DATA Subject: Hi Chris This is a part of the body text . The parameters HELO, MAIL FROM and RCPT TO are unspectacular and were 'previsible'. The DATA parameter, however, is interesting: the header (Subject) and body text are separated by a blank line. This example sports only one header (Subject), further examples will insert the missing headers. The dot (which has to be on a separate line) finally terminates the email and the server reports the status of the operation. Apropos status reports: each of the commands shown returns a status message from the server - these can come in very handy! Implementing the minimalist Email in C#We will now transport this process 1:1 into C# using the System.Net classes of the .NET Framework as helpers. Basically, it all now is a mattter of an instance of the TcpClient class and its Stream for connecting to the SMTP Server and for exchanging commands and status messages. The file email-simple.aspx is part of the download for today's article. <% @Page Language="C#" %> <% @Import Namespace="System.IO" %> <% @Import Namespace="System.Net" %> <% @Import Namespace="System.Net.Sockets" %> <% @Import Namespace="System.Text" %> <script language="C#" runat="server"> bool WriteToStream(ref NetworkStream nwstream, string strLine) { string strString2Send = strLine + "\r\n"; Byte[] arr2Send = Encoding.ASCII.GetBytes(strString2Send.ToCharArray()); try { nwstream.Write(arr2Send, 0, arr2Send.Length); } catch { return false; } return true; } bool ReadFromStream(ref NetworkStream nwstream, out string strMessage) { byte[] readBuffer = new byte[255]; int nLength = nwstream.Read(readBuffer, 0, readBuffer.Length); strMessage = Encoding.ASCII.GetString(readBuffer, 0, nLength); return (3 <= readBuffer[0]); // 2 success, 3 informational } </script> <% Response.Buffer = false; string strEmailServer = "fx2.dev.alfasierrapapa.com"; string strSendTo = "christophw@fx2.dev.alfasierrapapa.com"; string strMailFrom = "christophw@alphasierrapapa.com"; string strSubject = "My Email Test"; TcpClient tcpc = new TcpClient(); try { tcpc.Connect(strEmailServer, 25); } catch (SocketException socketEx) { Response.Write("Connection Error: " + socketEx.ToString()); Response.End(); } NetworkStream nwstream = tcpc.GetStream(); string strResponse; WriteToStream(ref nwstream, "HELO myhost"); ReadFromStream(ref nwstream, out strResponse); Response.Write(strResponse + "<br>"); WriteToStream(ref nwstream, "MAIL FROM: " + strMailFrom); ReadFromStream(ref nwstream, out strResponse); Response.Write(strResponse + "<br>"); WriteToStream(ref nwstream, "RCPT TO: " + strSendTo); ReadFromStream(ref nwstream, out strResponse); Response.Write(strResponse + "<br>"); WriteToStream(ref nwstream, "DATA"); ReadFromStream(ref nwstream, out strResponse); Response.Write(strResponse + "<br>"); WriteToStream(ref nwstream, "Subject: " + strSubject); ReadFromStream(ref nwstream, out strResponse); Response.Write(strResponse + "<br>"); WriteToStream(ref nwstream, "."); ReadFromStream(ref nwstream, out strResponse); Response.Write(strResponse + "<br>"); %> Concentrating on the lower part of the listing (the blocks of code three-liners), we see that this really looks no different from creating an Email using Telnet. The funcionality for sending the commands and for querying the status messages is hidden in the methods WriteToStream and ReadFromStream, respectively. The WriteToStream method is responsible for sending the commands. This happens by appending a CR/LF pair to the string, converting that into a byte array and the subsequent sending to the SMTP Server via the Write method of the NetworkStream object. The return value is true/false, dependent on whether the command could successfully be sent: bool WriteToStream(ref NetworkStream nwstream, string strLine) { string strString2Send = strLine + "\r\n"; Byte[] arr2Send = Encoding.ASCII.GetBytes(strString2Send.ToCharArray()); try { nwstream.Write(arr2Send, 0, arr2Send.Length); } catch { return false; } return true; } The ReadFromStream method basically works much the same way, differing only in reading a byte array and returning that as a String to the caller in consequence. As every status message of the SMTP Server is initiated by a three digit status code, I use that to compute true/false: 2xx are success messages, 3xx are successes with additional information: bool ReadFromStream(ref NetworkStream nwstream, out string strMessage) { byte[] readBuffer = new byte[255]; int nLength = nwstream.Read(readBuffer, 0, readBuffer.Length); strMessage = Encoding.ASCII.GetString(readBuffer, 0, nLength); return (3 <= readBuffer[0]); // 2 success, 3 informational } Even with both methods returning boolean results, I ignore them for simplicity's sake in today's example. Instead, I will output the status messages of the SMTP Server - something one wouldn't do in a production environment: According to the status code, sending the Email was a success - Outlook Express shares that opinion: However, the result looks less than appealing, as Outlook expects special headers for sender and recipient - Eudora isn't as nitpicking as far as this goes, but less popular. Thus we must program a little additional something into it to make the Outlookians think favourable of us. Eye candy for OutlookThis little something are the headers To and From. These simply are included in the Header section (email-better.aspx): WriteToStream(ref nwstream, "DATA"); ReadFromStream(ref nwstream, out strResponse); Response.Write(strResponse + "<br>"); WriteToStream(ref nwstream, "From: " + strMailFrom); WriteToStream(ref nwstream, "Subject: " + strSubject); WriteToStream(ref nwstream, "To: " + strSendTo); WriteToStream(ref nwstream, ""); WriteToStream(ref nwstream, "Hello Christoph!"); WriteToStream(ref nwstream, "\r\n."); ReadFromStream(ref nwstream, out strResponse); Response.Write(strResponse + "<br>"); This time around, Outlook Express also is 'friendly' with us and displays all desired information: ConclusionToday, we have just sent a simple email message, but in the course of this, we got acquainted with all the important SMTP commands. What we will add in the next articles will be recipients of copies, blind copies, content type, additional headers, HTML Email and attachments. Stay tuned! Downloading the CodeClick here to start the Download.
©2000-2004 AspHeute.com |