Click here to Skip to main content
Licence 
First Posted 21 Jan 2003
Views 78,725
Bookmarked 52 times

SMTP Login class

By | 21 Jan 2003 | Article
Basic SMTP Login class

Introduction

If you are like me, you have found many SMTP classes and none have dealt with Auth Login. Those that do deal with Auth Login usually come with a fee. Using some java code that I found and the SMTP RFC , I have hashed together a very basic SMTP class that handles Auth Login.

Using the code

First lets look at how SMTP works. The RFC can be found here: RFC

  1. Connect to Server
  2. Say Helo
  3. Tell the server you wish to authenticate and how
  4. Tell the server your username (64bit encoded)
  5. Tell the server your password (64bit encoded)
  6. Send the email
  7. Disconnect from the server.

Connecting to the server

There are several different methods of authentication available. And the method your server uses may differ from the one I show here. Telneting to the server on port 25, you can type "ehlo" as your greeting and you will receive a list of the valid authentication types back. I do this as my greeting watching for "250 OK" before I continue. On some servers you may not be able to use the "ehlo" command as your primary greeting and should modify the class to use " HELO " method.

The only truly important thing to remember here is that the username and password must be 64bit encoded.

string EncodedUserName = Convert.ToBase64String(Encoder.GetBytes(UserName));
string EncodedPassword = Convert.ToBase64String(Encoder.GetBytes(Password));

Once authenticated to the server you can now begin sending the message. There are several steps in sending a message. First you have to tell the server who is sending the message. On most servers this must be the same person that logged in.

MAIL FROM: <youremail@yourdomain.com> <CRLF> 

Next, the server must be told who is to get the message.

RCPT TO: <rcptemail@theirdomain.com> <CRLF> 

Sending the Message

The server now knows who it is receiving mail from and who it should send the mail to. However it does not know what to send. To tell the server that it is about to receive the message we must send the "DATA" tag

DATA<CRLF> 

The server now knows that it is receiving message data. There are several commands that are valid here. I am only going to cover sending a subject and the actual message.

To send the subject, simply send the "Subject: " command followed by a string.

Subject: <Message Subject><CRLF> 

At this point you are ready to send the message. Simply send it as single string to the server. Not much else to it. Remember to follow the message with a <CRLF> to tell the server your done.

Closing the Message

Now that the server has the message, we have send it on it's merry way. This is done by sending a single period to the server.

.<CRLF> 

Points of Interest

The source code that I have for download also includes my basic server class. I know it's not the best so please don't flame for it :-) Enjoy!!!

History

  • This is a first revision

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

About the Author

Benjamin L. Miller



United States United States

Member



Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board. (secure sign-in)
 
Search this forum  
 FAQ
    Noise  Layout  Per page   
  Refresh
NewsRedesigned code + 3 bug fixes [modified] PinmemberJohnnyUT12:07 30 Jan '08  
Thanks for this simple mail class. Actually a class that really works... in contrast to System.Net.Mail.SmtpClient (awww how i hate this one! D'Oh! | :doh: ).
 
Unfortunately your code design is quite unfavorable, so i decided to redesign the class completely.   Wink | ;) Now it has 180 loc (including very detailed documentation) in opposite of the 420 loc your solution had.
 
Additionally I made 3 fixes:
- added a "TO:"-field in the data section to show the real recipient in the mail client instead of just "undisclosed-recipients:;"
- changed encoding to UTF-8 and specified it in the mail body to show umlaute like äöü and other special characters in the right way on every mail client
- added a name to the EHLO command ("EHLO MailClient") since initiating a connection doesn't work with just "EHLO".
 
Due to the redesign the class is now used in a slightly different way:
 
MailClient smtp = new MailClient();
smtp.Connect("server", 25, "username", "password");
//or smtp.Connect("server", 25); if no authentication is needed
smtp.SendMail("from", "to", "subject", "msg");
...
smtp.SendMail("from", "to", "subject", "msg");
smtp.Close();
 
The main advantage is, that it's possible to send more than one mail per connection which leads to less time and resource usage as well as mail server stress.
 
Hope anyone might need this class! Again, thanks for the idea behind, Benjamin!   Smile | :)
 

using System;
using System.Text;
using System.Net.Sockets;
using System.Threading;
 
namespace YAQv2.Tools
{
      public class MailClient
      {
            /// <summary>
            /// Tcp connection used for communication to the server.
            /// </summary>
            private TcpClient _client;
 
            /// <summary>
            /// Network stream used for sending/reading data to/from the server.
            /// </summary>
            private NetworkStream _netStream;
 
            /// <summary>
            /// Encoder used for translating text into bytes and vice versa.
            /// </summary>
            private static UTF8Encoding _encoder = new UTF8Encoding();
 
            /// <summary>
            /// Connects to the mail server. Needs to be called before SendMail().
            /// </summary>
            /// <param name="server">Server name to connect to.</param>
            /// <param name="port">Port to connect to.</param>
            internal void Connect(string server, int port)
            {
                  Connect(server, port, null, null);
            }
 
            /// <summary>
            /// Connects to the mail server including authentication. Needs to be called before SendMail().
            /// </summary>
            /// <param name="server">Server name to connect to.</param>
            /// <param name="port">Port to connect to.</param>
            /// <param name="username">The username of the account sending the message.</param>
            /// <param name="password">The password of the account sending the message.</param>
            internal void Connect(string server, int port, string username, string password)
            {
                  //create new connection
                  _client = new TcpClient();
 
                  try
                  {
                        _client.Connect(server, port);
                  }
                  catch (SocketException se)
                  {
                        throw new Exception("Could not connect to the mail server " + server + ":" + port, se);
                  }
 
                  //set the stream to the mail client
                  _netStream = _client.GetStream();
 
                  //check for established connection
                  if (!ReadData().StartsWith("220"))
                        throw new Exception("220: Error connecting to SMTP server.");
 
                  //initialize connection
                  SendData("EHLO MailClient");                                                            //send greeting (ehlo)
                  if (!ReadData().StartsWith("250"))                                                   //check for OK message
                        throw new Exception("250: Error talking to SMTP server.");
 
                  //skip authentication process?
                  if (username != null && password != null)
                  {
                        SendData("AUTH LOGIN");                                                            //send authentication command
                        if (!ReadData().StartsWith("334 VXNlcm5hbWU6"))                        //check for user name challenge
                              throw new Exception("334: Error initializing authentication method of SMTP Server.");
 
                        SendData(Convert.ToBase64String(_encoder.GetBytes(username)));   //send username (64bit encoded)
                        if (!ReadData().StartsWith("334 UGFzc3dvcmQ6"))                        //check for password challenge
                              throw new Exception("334: Error sending username to SMTP server.");
 
                        SendData(Convert.ToBase64String(_encoder.GetBytes(password)));   //send password (64bit encoded)
                        if (!ReadData().StartsWith("235"))                                             //check for authentication successful
                              throw new Exception("235: Error authenticating to SMTP server.");
                  }
            }
 
            /// <summary>
            /// Used to send mail according to SMTP AUTH.
            /// </summary>
            /// <param name="from">Email address of person sending the mail.</param>
            /// <param name="to">Email of recipient.</param>
            /// <param name="subject">This is the email subject.</param>
            /// <param name="message">The actual message to be sent.</param>
            internal void SendMail(string from, string to, string subject, string message)
            {
                  //if connection hasn't been established
                  if (_client == null || !_client.Connected)
                        return;
 
                  //send email
                  SendData("MAIL FROM:" + from);                                                         //send author of the mail
                  if (!ReadData().StartsWith("250"))                                                   //check for OK message
                        throw new Exception("250: Error with orgin address: " + from);
 
                  SendData("RCPT TO: " + to);                                                            //send recipients
                  if (!ReadData().StartsWith("250"))                                                   //check for OK message
                        throw new Exception("250: Error with destination address: " + to);
 
                  SendData("DATA");                                                                           //beginn body transmission
                  if (!ReadData().StartsWith("354"))                                                   //check for waiting for "." message
                        throw new Exception("354: Error while trying to send subject & body.");
 
                  SendData("Subject:" + subject);                                                      //send subject
                  SendData("To:" + to);                                                                     //send recipients
                  SendData("Content-Type: text/plain; charset=UTF-8");                        //set character encoding;
                  SendData(message);                                                                           //send the message itself
                  SendData(".");                                                                                 //end transmission
                  if (!ReadData().StartsWith("250"))                                                   //check for OK message
                        throw new Exception("250: Error while attempting to close message on server.");
            }
 
            /// <summary>
            /// Disconnects from the mail server. Needs to be called after SendMail();
            /// </summary>
            internal void Disconnect()
            {
                  if (_netStream != null)
                        _netStream.Close();
                  if (_client != null)
                        _client.Close();
            }
 
            /// <summary>
            /// Sends the passed data to the mail server.
            /// </summary>
            /// <param name="data">Data to be sent.</param>
            private void SendData(string data)
            {
                  //convert characters to bytes
                  data += "\r\n";
                  byte[] rawData = _encoder.GetBytes(data);
 
                  //send data
                  _netStream.Write(rawData, 0, rawData.Length);
            }
 
            /// <summary>
            /// Reads data received from the mail server.
            /// </summary>
            /// <returns>Received data.</returns>
            private string ReadData()
            {
                  //check for data
                  if (!WaitForResponse())
                        throw new Exception("Server did not respond.");
 
                  //read data
                  int bytesRead;
                  string data = String.Empty;
                  byte[] rawData = new byte[1024];
 
                  while (_netStream.DataAvailable && (bytesRead = _netStream.Read(rawData, 0, rawData.Length)) > 0)
                        //convert bytes to characters
                        data += _encoder.GetString(rawData);
 
                  return data;
            }
 
            /// <summary>
            /// Waits until the mail server responds, but at most 10 seconds.
            /// </summary>
            /// <returns>Whether the mail server responded or not.</returns>
            private bool WaitForResponse()
            {
                  DateTime maxTime = DateTime.Now.AddSeconds(10);
                  while (!_netStream.DataAvailable && maxTime > DateTime.Now)
                        Thread.Sleep(100);
 
                  return _netStream.DataAvailable;
            }
      }
}
GeneralSMTP Login class FIX... PinmembertheBuck9:55 9 Nov '04  
GeneralAuth login Pinmembertrongnd20:53 30 May '04  
GeneralRe: Auth login PinmemberBenjamin L. Miller20:17 10 Jun '05  
GeneralAUTH LOGIN PinmemberAlex Rovner5:36 29 Apr '04  
GeneralRe: AUTH LOGIN PinsussAnonymous15:36 23 Jan '05  
GeneralServer Timeout Note Pinmemberyfisaqt19:09 31 Mar '04  
GeneralThank you Pinmembershivpal19:31 14 Oct '03  
QuestionBusy waiting? Pinmemberstevex2:04 18 May '03  
GeneralAdding attachments Pinmemberrkendall17:19 24 Apr '03  
QuestionCram MD5? Pinmemberkalme6:33 29 Jan '03  
AnswerRe: Cram MD5? PinmemberBenjamin L. Miller7:02 30 Jan '03  
GeneralAnother SMTP library with the sockets PinmemberSebastien Curutchet20:21 22 Jan '03  
GeneralRe: Another SMTP library with the sockets PinmemberGriffonRL2:40 23 Jan '03  

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

Permalink | Advertise | Privacy | Mobile
Web04 | 2.5.120529.1 | Last Updated 22 Jan 2003
Article Copyright 2003 by Benjamin L. Miller
Everything else Copyright © CodeProject, 1999-2012
Terms of Use
Layout: fixed | fluid