Welcome to the Axosoft Community, Sign in | Register | Help
in Search

Hamid Shojaee

Random Thoughts about Axosoft and technology

Free C# SMTP SendMail Source Code

One of the features of Axosoft's flagship product, OnTime, is the ability to send out email notifications when a defect or feature has been created, changed or deleted.  This feature seemed pretty straight forward and we were excited to find the SMTP class in the System.Web namespace of the .NET framework, but after implementing email notifications using the .NET Framework's version of the SMTP sendmail, we realized many of our test clients were getting CDO related error messages.  These error messages were driving us nuts as there is little information out there on how to fix such the CDO error issue for applications that are being deployed on a wide number of systems.

 

So we decided to create our own simple SMTP sendmail that addressed our needs.  The following source code is fairly straight forward.  It includes two main classes: MailMessage and Smtp.  To send an email, create a MailMessage object, set its properties and use the Smtp class to send it.  Here is a short example of how you would use the Smtp & MailMessage classes:

 

MailMessage msg = new MailMessage();

msg.EmailFrom = "test@axosoft.com";

msg.AddEmailTo("to@axosoft.com");

msg.EmailMessageType = MessageType.HTML;

msg.EmailMessage = "any html text";

msg.EmailSubject = "Test Message";

                       

Smtp smtp = new Smtp();

smtp.SmtpServer = "smtpserver.domainname.com";

smtp.SmtpUser = "user_if_required";

smtp.SmtpPassword = "password_if_user_is_required";

smtp.SendEmail(msg);

 

If you have any questions, comments or suggestions for improving the code, please don't hesitate to contact me.

 

Also, please note that this Smtp control does not support attachments.  But it should be fairly easy to add an attachment feature.

 

Let me know if you get some use out of this.  Email me at hamids at axosoft.com.

 

Credits:   Many thanks to Dan Suceava who researched and wrote the initial code based on the rfc821 specs.

 

You can also download the SMTP Source here.

 


 

/*

 * Copyright 2003-2004, Axosoft, LLC

 * You are free to distribute this code royalty-free as you wish, however, we only ask

 * that you leave this copyright notice at the top of the source file giving Axosoft

 * credit for this source code.  If you find any bugs or want to contribute your

 * enhancements, please send them to support@axosoft.com.

 *

 * Visit Axosoft at http://www.axosoft.com

 */

 

using System;

using System.Collections;

using System.Net;

using System.Net.Sockets;

using System.Text;

 

namespace Axosoft.Common.Utilities

{

      public class SmtpException : ApplicationException

      {

            public SmtpException(string message) : base(message)

            {

            }

      }

 

      ///

      /// Indicates the type of message to be sent

      ///

      public enum MessageType

      {

            ///

            /// The message is plain text

            ///

            Text = 0,

            ///

            /// The message is HTML

            ///

            HTML = 1

      }

 

      ///

      /// A mail message that can be sent using the Smtp class

      ///

      public class MailMessage

      {

            private string _emailFrom = "";

            public string EmailFrom

            {

                  get { return _emailFrom; }

                  set { _emailFrom = value; }

            }

 

            private string _emailSubject = "";

            public string EmailSubject

            {

                  get { return _emailSubject; }

                  set { _emailSubject = value; }

            }

 

            private ArrayList _emailTo = null;

            public ArrayList EmailTo

            {

                  get { return _emailTo; }

            }

            public void AddEmailTo(string email)

            {

                  if(_emailTo == null)

                        _emailTo = new ArrayList();

                  _emailTo.Add(email);

            }

 

            private string _emailMessage = "";

            public string EmailMessage

            {

                  get { return _emailMessage; }

                  set { _emailMessage = value; }

            }

 

            private MessageType _emailMessageType = MessageType.Text;

            public MessageType EmailMessageType

            {

                  get { return _emailMessageType; }

                  set { _emailMessageType = value; }

            }

      }

 

      ///

      /// This class allows sending of e-mails through Smtp

      /// For help on SMTP, look up http://www.faqs.org/rfcs/rfc821.html

      ///

      public class Smtp

      {

            #region Class properties

            private string _serverSmtp = "";

            public string SmtpServer

            {

                  get { return _serverSmtp; }

                  set { _serverSmtp = value; }

            }

 

            private int _portSmtp = 25;

            public int SmtpPort

            {

                  get { return _portSmtp; }

                  set { _portSmtp = value; }

            }

 

            private string _userSmtp = "";

            public string SmtpUser

            {

                  get { return _userSmtp; }

                  set { _userSmtp = value; }

            }

 

            private string _passwordSmtp = "";

            public string SmtpPassword

            {

                  get { return _passwordSmtp; }

                  set { _passwordSmtp = value; }

            }

 

            #endregion

 

            public Smtp()

            {

            }

 

            #region Public methods

            ///

            /// Sends the e-mail based on the properties set for this object

            ///

            public void SendEmail(MailMessage msg)

            {

                  int code;

 

                  if(_serverSmtp == "" || msg.EmailFrom == "" || msg.EmailSubject == "" || msg.EmailTo == null)

                  {

                        throw new SmtpException("Invalid Smtp or email parameters.");

                  }

 

                  // open a connection to the Smtp server

                  using(TcpClient smtpSocket = new TcpClient(_serverSmtp, _portSmtp))

                  using(NetworkStream ns = smtpSocket.GetStream())

                  {

                        // get response from Smtp server

                        code = GetSmtpResponse(ReadBuffer(ns));

                        if(code != 220)

                        {

                              throw new SmtpException("Error connecting to Smtp server. (" + code.ToString() + ")");

                        }

 

                        // EHLO

                        WriteBuffer(ns, "ehlo\r\n");

                        // get response from Smtp server

                        string buffer = ReadBuffer(ns);

                        code = GetSmtpResponse(buffer);

                        if(code != 250)

                        {

                              throw new SmtpException("Error initiating communication with Smtp server. (" + code.ToString() + ")");

                        }

                        // check for AUTH=LOGIN

                        if(buffer.IndexOf("AUTH=LOGIN") >= 0)

                        {

                              // AUTH LOGIN

                              WriteBuffer(ns, "auth login\r\n");

                              // get response from Smtp server

                              code = GetSmtpResponse(ReadBuffer(ns));

                              if(code != 334)

                              {

                                    //throw new SmtpException("Error initiating Auth=Login. (" + code.ToString() + ")");

                              }

 

                              // username:

                              WriteBuffer(ns, System.Convert.ToBase64String(ASCIIEncoding.ASCII.GetBytes(_userSmtp)) + "\r\n");

                              // get response from Smtp server

                              code = GetSmtpResponse(ReadBuffer(ns));

                              if(code != 334)

                              {

                                    //throw new SmtpException("Error setting Auth user name. (" + code.ToString() + ")");

                              }

 

                              // password:

                              WriteBuffer(ns, System.Convert.ToBase64String(ASCIIEncoding.ASCII.GetBytes(_passwordSmtp)) + "\r\n");

                              // get response from Smtp server

                              code = GetSmtpResponse(ReadBuffer(ns));

                              if(code != 235)

                              {

                                    //throw new SmtpException("Error setting Auth password. (" + code.ToString() + ")");

                              }

                        }

 

                        // MAIL FROM:

                        WriteBuffer(ns, "mail from: <" + msg.EmailFrom + ">\r\n");

                        // get response from Smtp server

                        code = GetSmtpResponse(ReadBuffer(ns));

                        if(code != 250)

                        {

                              throw new SmtpException("Error setting sender email address. (" + code.ToString() + ")");

                        }

 

                        // RCPT TO:

                        foreach(string sEmailTo in msg.EmailTo)

                        {

                              WriteBuffer(ns, "rcpt to:<" + sEmailTo + ">\r\n");

                              // get response from Smtp server

                              code = GetSmtpResponse(ReadBuffer(ns));

                              if(code != 250 && code != 251)

                              {

                                    throw new SmtpException("Error setting receipient email address. (" + code.ToString() + ")");

                              }

                        }

 

                        // DATA

                        WriteBuffer(ns, "data\r\n");

                        // get response from Smtp server

                        code = GetSmtpResponse(ReadBuffer(ns));

                        if(code != 354)

                        {

                              throw new SmtpException("Error starting email body. (" + code.ToString() + ")");

                        }

 

                        // Repeat the from and to addresses in the data section

                        WriteBuffer(ns, "from:<" + msg.EmailFrom + ">\r\n");

                        foreach(string sEmailTo in msg.EmailTo)

                        {

                              WriteBuffer(ns, "to:<" + sEmailTo + ">\r\n");

                        }

 

                        WriteBuffer(ns, "Subject:" + msg.EmailSubject + "\r\n");

                        switch(msg.EmailMessageType)

                        {

                              case MessageType.Text:

                                    // send text message

                                    WriteBuffer(ns, "\r\n" + msg.EmailMessage + "\r\n.\r\n");

                                    break;

 

                              case MessageType.HTML:

                                    // send HTML message

                                    WriteBuffer(ns, "MIME-Version: 1.0\r\n");

                                    WriteBuffer(ns, "Content-type: text/html\r\n");

                                    WriteBuffer(ns, "\r\n" + msg.EmailMessage + "\r\n.\r\n");

                                    break;

                        }

                        // get response from Smtp server

                        code = GetSmtpResponse(ReadBuffer(ns));

                        if(code != 250)

                        {

                              throw new SmtpException("Error setting email body. (" + code.ToString() + ")");

                        }

 

                        // QUIT

                        WriteBuffer(ns, "quit\r\n");

                  }

            }

            #endregion

 

            #region Private methods

            ///

            /// Looks for an Smtp response code inside a repsonse string

            ///

            /// The response string to be searched

            /// The int value of the Smtp reponse code

            private int GetSmtpResponse(string sResponse)

            {

                  int response = 0;

                  int iSpace = sResponse.IndexOf(" ");

                  int iDash = sResponse.IndexOf("-");

                  if(iDash > 0 && iDash < iSpace)

                        iSpace = sResponse.IndexOf("-");

 

                  try

                  {

                        if(iSpace > 0)

                              response = int.Parse(sResponse.Substring(0, iSpace));

                  }

                  catch(Exception)

                  {

                        // error - ignore it

                  }

 

                  return response;

            }

 

            ///

            /// Write a string to the network stream

            ///

            /// The network stream on which to write

            /// The string to write to the stream

            private void WriteBuffer(NetworkStream ns, string sBuffer)

            {

                  try

                  {

                        byte[] buffer = Encoding.ASCII.GetBytes(sBuffer);

                        ns.Write(buffer, 0, buffer.Length);

                  }

                  catch(System.IO.IOException)

                  {

                        // error writing to stream

                        throw new SmtpException("Error sending data to Smtp server.");

                  }

            }

 

            ///

            /// Reads a response from the network stream

            ///

            /// The network stream from which to read

            /// A string representing the reponse read

            private string ReadBuffer(NetworkStream ns)

            {

                  byte[] buffer = new byte[1024];

                  int i=0;

                  int b;

                  int timeout = System.Environment.TickCount;

 

                  try

                  {

                        // wait for data to show up on the stream

                        while(!ns.DataAvailable && ((System.Environment.TickCount - timeout) < 20000))

                        {

                              System.Threading.Thread.Sleep(100);

                        }

                        if(!ns.DataAvailable)

                              throw new SmtpException("No response received from Smtp server.");

 

                        // read while there's data on the stream

                        while(i < buffer.Length && ns.DataAvailable)

                        {

                              b = ns.ReadByte();

                              buffer[i++] = (byte)b;

                        }

                  }

                  catch(System.IO.IOException)

                  {

                        // error reading from stream

                        throw new SmtpException("Error receiving data from Smtp server.");

                  }

 

                  return Encoding.ASCII.GetString(buffer);

            }

            #endregion

      }

}

 

Published Wednesday, February 18, 2004 10:34 AM by Hamid
Filed under:

Comments

 

Anonymous said:

Living in sweden i noticed the following problem :
An ASCII encoding provides for 7 bit characters and therefore only supports the first 128 unicode characters. All characters outside that range will display an unknown symbol - typically a "?" (0x3f) or "|" (0x7f) symbol.
What I wanted to achieve was an ANSI encoding. To get an ANSI encoding you need to specify a code page which prescribes the characters from 128 on up. Code page 1252 seems to be the one with the swedish specific characters.

So I changed this (in the WriteBuffer section) :
byte[] buffer = Encoding.ASCII.GetBytes(sBuffer);
To this:
byte[] buffer = Encoding.GetEncoding(1252).GetBytes(sBuffer);

I also changed the last row from this :
return Encoding.ASCII.GetString(buffer);
to this :
return Encoding.GetEncoding(1252).GetString(buffer);

Im sure there must be some way of using Encoding.Default here but I didnt have time to explore that possibility.
In conclusion I believe that changing the 1252 to whatever will work for people around the globe.


Originally posted by:
Peter
February 24, 2004 5:56 AM
 

Anonymous said:

erm hi,

do u know how to code using mircosoft.net for smtp mail protocal? i got alot of problem doing it.. hope u really can help. my email is benzai05@yahoo.com

Originally posted by:
ben
April 6, 2004 6:41 AM
 

Hamid said:

Ben, I'm not sure what you mean. The source code for the SMTP send mail is above.
April 6, 2004 8:24 AM
 

Anonymous said:

Hi,
I am unable to send a message using above code. it throws an unhandled exception. at this moment debugger shows to be on following line
if(buffer.IndexOf("AUTH=LOGIN") >= 0)
and it jumps to else branch.
I examined the contents of buffer and foun the following string: "501 Syntax: EHLO hostname". can you advice?


Originally posted by:
~Default
May 26, 2004 12:21 AM
 

Anonymous said:

Thanx Peter,

I just coded a SMTP server in C#. And i had this ASCII encoding problem.

I did my test using MS Outlook. IT changes the enconding depending on what you wrote in tour subject or body mail. I mean that if you write words with accents (after 128 first char) the encoding change.

Choose File


Originally posted by:
Choose File
June 5, 2004 3:36 AM
 

Anonymous said:

You should add the ability to attach files to the email.
Thanks,
Seth

Originally posted by:
Seth O'Neal
June 18, 2004 7:34 AM
 

Anonymous said:

Greate Stuff! Compliment!

Originally posted by:
recode
July 1, 2004 12:03 AM
 

Anonymous said:

My mail provider after ehlo repilies:
501: syntax EHLO HOSTNAME

so i suggest to add following code after the EHLO response:

else if (code == 501)
{
WriteBuffer(ns, "ehlo " + Dns.GetHostName() + "\r\n");
buffer = ReadBuffer(ns);
code = GetSmtpResponse(buffer);
}

and then check code again.


Originally posted by:
Tomasz Kubacki
July 9, 2004 1:27 AM
 

Anonymous said:

sorry of cource not "else if" but "if"

Originally posted by:
Tomasz Kubacki
July 9, 2004 1:28 AM
 

Anonymous said:

Hi It would be great if you could add the attachment functionality ! The code works great! Thanks

Originally posted by:
Yumna
July 20, 2004 5:28 AM
 

Anonymous said:

I am getting the "Error Starting Email body (530)".
I can't figure out what the problem is...Could you please enlighten me?

Thx

Originally posted by:
Riz
August 6, 2004 5:51 AM
 

Anonymous said:

try to uncomment exception throws, this may help You to understand what happens




Originally posted by:
j23tom
August 12, 2004 11:30 AM
 

Anonymous said:

Great code! Works great!

I am trying to modify this so that the From Email and to Email work the same way as it does using the system.web class

In the System.Web class I can specify my From and To with the following syntax:

Firstname LastName <Emailaddress>

and it will send. The code above only seems to work with an e-mail address only.

I am looking through the specs seeing if it's just a syntax thing but not getting anywhere.

Anyone know how to do this?



Originally posted by:
Ralph
November 23, 2004 9:14 AM
 

TrackBack said:



Originally posted by:
RedWolves2 Weblog
November 30, 2004 3:15 PM
 

Anonymous said:

Hello, I geting this error

An established connection was aborted by the software in your host machine

any help ? please

TIA

Originally posted by:
MajorTom
December 12, 2004 9:15 AM
 

Anonymous said:

Why does everybody use accessors for every variable. Surely after all the years that mail has been around and "SUPRISE, SUPRISE" the port number has been static (25). Why have Set and Get for _portSmtp? Get MAYBE, but Set???

Originally posted by:
TheGetSetBoy
December 17, 2004 1:51 AM
 

Anonymous said:

I know I use accessors so I'm free to change the internal representation of a variable w/o effecting the code that uses it.

As for port 25. That is the _standard_. However, I know I run a SMPT server on a non-standard port in my office (in addition to the normal one) so I can send mail via MY smtp servers even when I'm on a network that forces me to use their server by blocking port 25. Cable modems, DSL and AOL are three regular offenders of blocking port 25.

-Walden

Originally posted by:
Walden
December 18, 2004 10:09 AM
 

Anonymous said:

if(_serverSmtp == "" || msg.EmailFrom == "" || msg.EmailSubject == "" || msg.EmailTo == null)

{

throw new SmtpException("Invalid Smtp or email parameters.");

}

This line is not quite correct either, in that if you are running more than one virtual SMTP server on a windows box, you can specify the IP address of the virtual machine. If you want to have more than one virtual server on a windows box, it requires you to have more than one network card on it.

e.g. _serverSmtp == "10.10.x.x" can be valid.

-AndyA

Originally posted by:
Andy
December 20, 2004 8:03 AM
 

Anonymous said:

Is it possible to set the message as Hyperlink?
Is it possible to send mail via Yahoo?


Originally posted by:
Sagiv
December 28, 2004 12:05 AM
 

Anonymous said:

WriteBuffer(ns, "ehlo\r\n");
// get response from Smtp server
string buffer = ReadBuffer(ns);
code = GetSmtpResponse(buffer);
if(code != 250)
{

throw new SmtpException("Error initiating communication with Smtp server. (" + code.ToString() + ")");

}

I have an error here, the code = 501, buffer = "501 Syntactically invalid EHLO arguments(s)\r\n...."

What can I do?
thanks
Nhan



Originally posted by:
Nhan
January 6, 2005 4:57 PM
 

Anonymous said:

WriteBuffer(ns, "rcpt to:<" + sEmailTo + ">\r\n");
// get response from Smtp server
buffer = ReadBuffer(ns);
code = GetSmtpResponse(buffer);
if(code != 250 && code != 251)
{
throw new SmtpException("Error setting receipient email address. (" + code.ToString() + ")");
}

I am getting error, buffer: "554 <le.nhan@freenet.de>: Relay access denied\r\n\".
I have tried with another email adress for recipient, but it doesn't help.

How I can do?

Originally posted by:
Nhan
January 6, 2005 5:36 PM
 

Anonymous said:

I got the same "relaying denied" message. It turned out i was not doing the auth step because of the

if(b.IndexOf("AUTH=LOGIN") >= 0)

changed to

if(b.IndexOf("LOGIN") >= 0)

works great..

My server was returning "AUTH PLAIN LOGIN"


Originally posted by:
todd
January 10, 2005 8:02 AM
 

Anonymous said:

Great code clears up all CDO problems but I have to have attachments :( Anyone out there modified this code to include attachments?


Pretty Please with sugar on TOP!!!!!!!!

Originally posted by:
Mike
January 12, 2005 4:56 PM
 

Anonymous said:

I Agree this code works really good but ATTACHMENTS ARE A MUST!!!! I would help but not sure how to do it. Hopefully somone can help myself and Mike out.

Looks like there are a few other people that could use it too.



Originally posted by:
Jsmith
January 12, 2005 4:58 PM
 

Anonymous said:

There is a typo in the code sending the helo command:
WriteBuffer(ns, "ehlo\r\n"); should be WriteBuffer(ns, "helo\r\n"); but this is also not correct. The correct syntax for the helo command is helo <domain><crlf> so I changed the line to:

WriteBuffer(ns, "helo " + _serverSmtp + "\r\n");

Now it works with my smtp server.

I could not download the code so I just copied from the web site, maybe the typo is only there?

Hope this helps the people with the "ehlo" problem above.

Regards,
Chris

Originally posted by:
Chris
January 23, 2005 9:45 PM
 

Anonymous said:

got the code and it is working to a point. when ever i send an email it is being delivered straight in to the spam folder in outlook.

does have anyone have any ideas on what needs to be amended so that the email is not classed as spam.

other than that it all seems to work fine

Originally posted by:
keith
February 2, 2005 12:22 AM
 

Anonymous said:

"smtpserver.domainname.com";
please give me example of this

Originally posted by:
fadi
March 23, 2005 2:23 AM
 

Anonymous said:

If You are strategies flush aces buster rock equity bonus cap wheel <a href="http://www.wisewomanguide.com/black-jack.html"> black jack rules</a> stakes ball trips rank face casino?

Originally posted by:
black jack online
June 15, 2005 10:43 PM
 

Anonymous said:

Rate fast <a href="http://www.ncwash.com/pacific-poker-com.html">">http://www.ncwash.com/pacific-poker-com.html"> online pacific poker com</a> cowboys drop edge apple splash http://www.ncwash.com/pacific-poker-com.html over fishhooks cowboys come bet!

Originally posted by:
pacific poker com site
June 16, 2005 5:40 AM
 

Anonymous said:

Get started lowball action dozen action paigow <a href="http://www.rudecomputing.com/free-empire-poker.html"> empire poker free</a> deck rake event earn zero.

Originally posted by:
empire poker free
June 16, 2005 6:14 AM
 

Anonymous said:

Well bluff draw check bingo <a href="http://www.wisewomanguide.com/play-blackjack.html">play blackjack</a> value white club main jacks!

Originally posted by:
play blackjack online free
June 16, 2005 6:43 AM
 

Anonymous said:

Today gambling ring <a href="http://www.rudecomputing.com/empire-poker-com.html"> empire poker com site</a> overplay spikes dime base tilt fold up.

Originally posted by:
view empire poker com
June 16, 2005 7:14 AM
 

Anonymous said:

This greek down outdraw buster limit hard greek soft poker <a href="http://www.wisewomanguide.com/internet-blackjack.html">">http://www.wisewomanguide.com/internet-blackjack.html"> net blackjack</a> championship deuces queens finger dozen chip http://www.wisewomanguide.com/internet-blackjack.html dollar.

Originally posted by:
net blackjack
June 16, 2005 7:15 AM
 

Anonymous said:

Call card hard line spending gross hopper corner tilt <a href="http://www.rudecomputing.com/empire-poker-bonus.html"> empire poker sign up bonus</a> bonus.

Originally posted by:
empire poker freeroll bonus
June 16, 2005 9:44 PM
 

Anonymous said:

In fact pot jacks soft <a href="http://www.wisewomanguide.com/blackjack-strategy.html"> blackjack basic strategy chart</a> buster joint check bank bust jackpot.

Originally posted by:
blackjack strategy
June 16, 2005 10:45 PM
 

Anonymous said:

As the croupier ball cashier paint percentage kind <a href="http://www.leroyhotel.com/texas-holdem-tournament.html">">http://www.leroyhotel.com/texas-holdem-tournament.html"> texas holdem tournament strategy</a> drop glossary set pair broadway handle natural tap pineapple http://www.leroyhotel.com/texas-holdem-tournament.html poker bible credit clubs free foul consecutive?

Originally posted by:
texas holdem tournament strategy
June 17, 2005 4:17 AM
 

Anonymous said:

Preview risk fold hedge <a href="http://www.leroyhotel.com/texas-holdem-game.html"> texas holdem card game</a>!

Originally posted by:
texas holdem computer game
June 17, 2005 5:44 AM
 

payday loan on line said:

At cash advance until pay day budget line cash advance

January 25, 2008 4:26 AM
 

cash loan payday till said:

Would You payday loan fast no fax fax guaranteed loan no payday

February 1, 2008 11:31 AM
 

free christian music ringtones said:

Unhappily ringtones for alltel cell phone loan until payday

February 4, 2008 11:01 AM
 

hour loan online payday said:

Here 2 loan online payday download pc ringtones

February 4, 2008 12:51 PM
New Comments to this post are disabled