Do and Run

A couple of quick extension methods for your amusement that I use a lot.

 

   1:          /// <summary>

   2:          /// Runs action against the specified source.
   3:          /// </summary>
   4:          /// <typeparam name="T">The type of the element.</typeparam>
   5:          /// <param name="source">The source.</param>
   6:          /// <param name="action">The action.</param>
   7:          public static void Run<T>(this IEnumerable<T> source, Function<T> action)
   8:          {
   9:              if (source == null)
  10:              {
  11:                  throw new ArgumentNullException("source");
  12:              }
  13:   
  14:              if (action == null)
  15:              {
  16:                  throw new ArgumentNullException("action");
  17:              }
  18:   
  19:              foreach (var element in source)
  20:              {
  21:                  action(element);
  22:              }
  23:          }
  24:   
  25:          /// <summary>
  26:          /// Does action against the specified source and returns the source.
  27:          /// </summary>
  28:          /// <typeparam name="T">The type of the element.</typeparam>
  29:          /// <param name="source">The source.</param>
  30:          /// <param name="action">The action.</param>
  31:          /// <returns>The source after the action is executed.</returns>
  32:          public static IEnumerable<T> Do<T>(this IEnumerable<T> source, Function<T> action)
  33:          {
  34:              if (source == null)
  35:              {
  36:                  throw new ArgumentNullException("source");
  37:              }
  38:   
  39:              if (action == null)
  40:              {
  41:                  throw new ArgumentNullException("action");
  42:              }
  43:   
  44:              return source.Select(
  45:                  element =>
  46:                  {
  47:                      action(element);
  48:                      return element;
  49:                  });
  50:          }


A better direct SMTP mailer

Wayne posted an interesting little SMTP snippet recently and I had been poking around at that, so I thought I'd share this... It is an adaptation of several articles scattered around through the years, his code, and several other things I wrote a while ago. Enjoy...

namespace SMTP
{
    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Diagnostics;
    using System.IO;
    using System.Net;
    using System.Net.Mail;
    using System.Net.Sockets;
    using System.Runtime.InteropServices;
    using System.Text;
    using System.Text.RegularExpressions;
    using System.Threading;

    /// <summary>
    /// provides methods to send email via smtp direct to mail server
    /// </summary>
    public class SmtpDirect
    {
        /// <summary>
        /// Determines whether the specified input email is email.
        /// </summary>
        /// <param name="inputEmail">The input email.</param>
        /// <param name="fromEmail">From email.</param>
        /// <param name="tryLookup">if set to <c>true</c> [try lookup].</param>
        /// <param name="tryConnect">if set to <c>true</c> [try connect].</param>
        /// <returns>
        ///     <c>true</c> if the specified input email is email; otherwise, <c>false</c>.
        /// </returns>
        public static bool IsEmail(string inputEmail, string fromEmail, bool tryLookup, bool tryConnect)
        {
            // WLF: Check for null or empty input

            if (string.IsNullOrEmpty(inputEmail))
            {
                return false;
            }

            // WLF: RegEx check

            const string strRegex = @"^(([a-zA-Z0-9_\-\+/\^]+)([\.]?)([a-zA-Z0-9_\-\+/\^]+))+@((\[[0-9]{1,3}" +
                                    @"\.[0-9]{1,3}\.[0-9]{1,3}\.)|(([a-zA-Z0-9\-]+\" +
                                    @".)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\]?)$";
            var re = new Regex(strRegex);

            if (!re.IsMatch(inputEmail))
            {
                return false;
            }

            // WWB: Check To Make Sure The Email Address Parses

            MailAddress mailAddress;
            try
            {
                mailAddress = new MailAddress(inputEmail);
            }
            catch
            {
                return false;
            }

            if (tryLookup)
            {
                // WWB: Check To Make Sure There Is a SMTP Server To Recieve The Email Address

                if (DnsMx.GetMXRecords(mailAddress.Host).Length == 0)
                {
                    return false;
                }
            }

            if (tryConnect)
            {
                // WLF: Try to connect to the SMTP server

                try
                {
                    var mxRecords = DnsMx.GetMXRecords(mailAddress.Host);
                    if (mxRecords.Length == 0)
                    {
                        return false;
                    }

                    var ipHostEntry = Dns.GetHostEntry(mxRecords[0]);
                    var endPoint = new IPEndPoint(ipHostEntry.AddressList[0], 25);
                    var s = new Socket(endPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
                    s.Connect(endPoint);

                    // WLF: Attempting to connect

                    if (!CheckResponse(s, SmtpResponse.ConnectSuccess))
                    {
                        s.Close();
                        return false;
                    }

                    // WLF: HELO server

                    Senddata(s, string.Format("HELO {0}\r\n", Dns.GetHostName()));
                    if (!CheckResponse(s, SmtpResponse.GenericSuccess))
                    {
                        s.Close();
                        return false;
                    }

                    // WLF: Say who it is from 

                    Senddata(s, string.Format("MAIL From: {0}\r\n", fromEmail));
                    if (!CheckResponse(s, SmtpResponse.GenericSuccess))
                    {
                        s.Close();
                        return false;
                    }

                    // WLF: Try to send 

                    Senddata(s, string.Format("RCPT TO: {0}\r\n", inputEmail));
                    if (!CheckResponse(s, SmtpResponse.GenericSuccess))
                    {
                        s.Close();
                        return false;
                    }
                }
                catch
                {
                    return false;
                }
            }

            // WWB: Success

            return true;
        }

        /// <summary>
        /// Sends the mail directly to the receiver's server.
        /// </summary>
        /// <param name="message">The message.</param>
        /// <param name="errors">The errors.</param>
        /// <returns></returns>
        public static bool SendDirect(MailMessage message, out List<string> errors)
        {
            errors = new List<string>();
            var deliveryAddresses = new List<MailAddress>();

            // WLF: Add To's
            foreach (var address in message.To)
            {
                deliveryAddresses.Add(address);
            }

            // WLF: Add CC's
            foreach (var address in message.CC)
            {
                deliveryAddresses.Add(address);
            }

            foreach (var to in deliveryAddresses)
            {
                var mxRecords = DnsMx.GetMXRecords(to.Host);
                if (mxRecords.Length == 0)
                {
                    errors.Add(to.Host);
                }
                else
                {
                    var ipHostEntry = Dns.GetHostEntry(mxRecords[0]);
                    var endPoint = new IPEndPoint(ipHostEntry.AddressList[0], 25);
                    var s = new Socket(endPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
                    s.Connect(endPoint);

                    if (!CheckResponse(s, SmtpResponse.ConnectSuccess))
                    {
                        s.Close();
                        return false;
                    }

                    Senddata(s, string.Format("HELO {0}\r\n", Dns.GetHostName()));
                    if (!CheckResponse(s, SmtpResponse.GenericSuccess))
                    {
                        s.Close();
                        return false;
                    }

                    Senddata(s, string.Format("MAIL From: {0}\r\n", message.From));
                    if (!CheckResponse(s, SmtpResponse.GenericSuccess))
                    {
                        s.Close();
                        return false;
                    }

                    Senddata(s, string.Format("RCPT TO: {0}\r\n", to.Address));
                    if (!CheckResponse(s, SmtpResponse.GenericSuccess))
                    {
                        s.Close();
                        return false;
                    }

                    #region Build message body

                    var header = GetHeader(message);

                    var msgBody = message.Body;
                    if (!msgBody.EndsWith("\r\n"))
                        msgBody += "\r\n";

                    #region Handle attachments

                    if (message.Attachments.Count > 0)
                    {
                        header.Append("MIME-Version: 1.0\r\n");
                        header.Append("Content-Type: multipart/mixed; boundary=unique-boundary-1\r\n");
                        header.Append("\r\n");
                        header.Append("This is a multi-part message in MIME format.\r\n");
                        var sb = new StringBuilder();
                        sb.Append("--unique-boundary-1\r\n");
                        sb.Append("Content-Type: text/plain\r\n");
                        sb.Append("Content-Transfer-Encoding: 7Bit\r\n");
                        sb.Append("\r\n");
                        sb.Append(msgBody + "\r\n");
                        sb.Append("\r\n");

                        foreach (var a in message.Attachments)
                        {
                            if (a == null) continue;

                            var f = new FileInfo(a.Name);
                            sb.Append("--unique-boundary-1\r\n");
                            sb.Append("Content-Type: application/octet-stream; file=" + f.Name + "\r\n");
                            sb.Append("Content-Transfer-Encoding: base64\r\n");
                            sb.Append("Content-Disposition: attachment; filename=" + f.Name + "\r\n");
                            sb.Append("\r\n");
                            var fs = f.OpenRead();
                            var binaryData = new Byte[fs.Length];
                            long bytesRead = fs.Read(binaryData, 0, (int) fs.Length);
                            Trace.WriteLine(string.Format("Bytes read: {0}", bytesRead));
                            fs.Close();
                            var base64String = Convert.ToBase64String(binaryData, 0, binaryData.Length);

                            for (var i = 0; i < base64String.Length;)
                            {
                                var nextchunk = 100;
                                if (base64String.Length - (i + nextchunk) < 0)
                                    nextchunk = base64String.Length - i;
                                sb.Append(base64String.Substring(i, nextchunk));
                                sb.Append("\r\n");
                                i += nextchunk;
                            }
                            sb.Append("\r\n");
                        }

                        msgBody = sb.ToString();
                    }

                    #endregion

                    #endregion

                    #region Send message body

                    Senddata(s, ("DATA\r\n"));
                    if (!CheckResponse(s, SmtpResponse.DataSuccess))
                    {
                        s.Close();
                        return false;
                    }
                    header.Append("\r\n");
                    header.Append(msgBody);
                    header.Append(".\r\n");
                    header.Append("\r\n");
                    header.Append("\r\n");
                    Senddata(s, header.ToString());
                    if (!CheckResponse(s, SmtpResponse.GenericSuccess))
                    {
                        s.Close();
                        return false;
                    }

                    Senddata(s, "QUIT\r\n");
                    CheckResponse(s, SmtpResponse.QuitSuccess);
                    s.Close();

                    #endregion
                }
            }

            return errors.Count == 0;
        }

        /// <summary>
        /// Sends the specified message.
        /// </summary>
        /// <param name="message">The message.</param>
        /// <param name="outboundSmtpServer">The outbound SMTP server.</param>
        /// <returns></returns>
        public static bool Send(MailMessage message, string outboundSmtpServer)
        {
            var ipHostEntry = Dns.GetHostEntry(outboundSmtpServer);
            var endPoint = new IPEndPoint(ipHostEntry.AddressList[0], 25);
            var s = new Socket(endPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
            s.Connect(endPoint);

            if (!CheckResponse(s, SmtpResponse.ConnectSuccess))
            {
                s.Close();
                return false;
            }

            Senddata(s, string.Format("HELO {0}\r\n", Dns.GetHostName()));
            if (!CheckResponse(s, SmtpResponse.GenericSuccess))
            {
                s.Close();
                return false;
            }

            Senddata(s, string.Format("MAIL From: {0}\r\n", message.From));
            if (!CheckResponse(s, SmtpResponse.GenericSuccess))
            {
                s.Close();
                return false;
            }

            foreach (var to in message.To)
            {
                Senddata(s, string.Format("RCPT TO: {0}\r\n", to.Address));
                if (!CheckResponse(s, SmtpResponse.GenericSuccess))
                {
                    s.Close();
                    return false;
                }
            }

            foreach (var cc in message.CC)
            {
                Senddata(s, string.Format("RCPT TO: {0}\r\n", cc.Address));
                if (!CheckResponse(s, SmtpResponse.GenericSuccess))
                {
                    s.Close();
                    return false;
                }
            }

            var header = GetHeader(message);

            var msgBody = message.Body;
            if (!msgBody.EndsWith("\r\n"))
                msgBody += "\r\n";

            #region Handle attachments

            if (message.Attachments.Count > 0)
            {
                header.Append("MIME-Version: 1.0\r\n");
                header.Append("Content-Type: multipart/mixed; boundary=unique-boundary-1\r\n");
                header.Append("\r\n");
                header.Append("This is a multi-part message in MIME format.\r\n");
                var sb = new StringBuilder();
                sb.Append("--unique-boundary-1\r\n");
                sb.Append("Content-Type: text/plain\r\n");
                sb.Append("Content-Transfer-Encoding: 7Bit\r\n");
                sb.Append("\r\n");
                sb.Append(msgBody + "\r\n");
                sb.Append("\r\n");

                foreach (var a in message.Attachments)
                {
                    if (a == null) continue;

                    var f = new FileInfo(a.Name);
                    sb.Append("--unique-boundary-1\r\n");
                    sb.Append("Content-Type: application/octet-stream; file=" + f.Name + "\r\n");
                    sb.Append("Content-Transfer-Encoding: base64\r\n");
                    sb.Append("Content-Disposition: attachment; filename=" + f.Name + "\r\n");
                    sb.Append("\r\n");
                    var fs = f.OpenRead();
                    var binaryData = new Byte[fs.Length];
                    long bytesRead = fs.Read(binaryData, 0, (int) fs.Length);
                    Trace.WriteLine(string.Format("Bytes read: {0}", bytesRead));
                    fs.Close();
                    var base64String = Convert.ToBase64String(binaryData, 0, binaryData.Length);

                    for (var i = 0; i < base64String.Length;)
                    {
                        var nextchunk = 100;
                        if (base64String.Length - (i + nextchunk) < 0)
                            nextchunk = base64String.Length - i;
                        sb.Append(base64String.Substring(i, nextchunk));
                        sb.Append("\r\n");
                        i += nextchunk;
                    }
                    sb.Append("\r\n");
                }

                msgBody = sb.ToString();
            }

            #endregion

            Senddata(s, ("DATA\r\n"));
            if (!CheckResponse(s, SmtpResponse.DataSuccess))
            {
                s.Close();
                return false;
            }
            header.Append("\r\n");
            header.Append(msgBody);
            header.Append(".\r\n");
            header.Append("\r\n");
            header.Append("\r\n");
            Senddata(s, header.ToString());
            if (!CheckResponse(s, SmtpResponse.GenericSuccess))
            {
                s.Close();
                return false;
            }

            Senddata(s, "QUIT\r\n");
            CheckResponse(s, SmtpResponse.QuitSuccess);
            s.Close();
            return true;
        }

        /// <summary>
        /// Gets the header.
        /// </summary>
        /// <param name="message">The message.</param>
        /// <returns></returns>
        private static StringBuilder GetHeader(MailMessage message)
        {
            var header = new StringBuilder();

            header.Append("From: " + message.From + "\r\n");

            var tos = string.Empty;
            foreach (var to in message.To)
            {
                tos = to.Address + ",";
            }
            header.AppendFormat("To: {0}\r\n", tos.TrimEnd(','));

            if (message.CC.Count > 0)
            {
                var ccs = string.Empty;
                foreach (var cc in message.CC)
                {
                    ccs = cc.Address + ",";
                }
                header.AppendFormat("Cc: {0}\r\n", ccs.TrimEnd(','));
            }

            header.AppendFormat("Date: {0}\r\n", DateTime.Now.ToString("ddd, d M y H:m:s z"));
            header.AppendFormat("Subject: {0}\r\n", message.Subject);
            header.Append("X-Mailer: SMTPDirect v1\r\n");
            return header;
        }

        #region Socket Operations

        /// <summary>
        /// Sends data to the specified socket.
        /// </summary>
        /// <param name="socket">The socket.</param>
        /// <param name="message">The message.</param>
        private static void Senddata(Socket socket, string message)
        {
            var messageBytes = Encoding.ASCII.GetBytes(message);
            socket.Send(messageBytes, 0, messageBytes.Length, SocketFlags.None);
        }

        /// <summary>
        /// Checks the response.
        /// </summary>
        /// <param name="socket">The socket.</param>
        /// <param name="responseExpected">The response expected.</param>
        /// <returns></returns>
        private static bool CheckResponse(Socket socket, SmtpResponse responseExpected)
        {
            var bytes = new byte[1024];
            while (socket.Available == 0)
            {
                Thread.Sleep(100);
            }

            socket.Receive(bytes, 0, socket.Available, SocketFlags.None);
            var sResponse = Encoding.ASCII.GetString(bytes);
            var response = Convert.ToInt32(sResponse.Substring(0, 3));
            return response == (int) responseExpected;
        }

        #endregion

        #region DNS

        /// <summary>
        /// DNS MX Lookup
        /// </summary>
        public class DnsMx
        {
            /// <summary>
            /// Executes a DNS Query
            /// </summary>
            /// <param name="pszName">Name of the PSZ.</param>
            /// <param name="wType">Type of the query.</param>
            /// <param name="options">The query options.</param>
            /// <param name="aipServers">The aip servers.</param>
            /// <param name="ppQueryResults">The pp query results.</param>
            /// <param name="pReserved">The p reserved.</param>
            /// <returns></returns>
            [DllImport("dnsapi", EntryPoint = "DnsQuery_W", CharSet = CharSet.Unicode, SetLastError = true,
                ExactSpelling = true)]
            private static extern int DnsQuery([MarshalAs(UnmanagedType.VBByRefStr)] ref string pszName,
                                               QueryTypes wType, QueryOptions options, int aipServers,
                                               ref IntPtr ppQueryResults, int pReserved);

            /// <summary>
            /// Grabs the DNS Record List
            /// </summary>
            /// <param name="pRecordList">The p record list.</param>
            /// <param name="FreeType">Type of the free.</param>
            [DllImport("dnsapi", CharSet = CharSet.Auto, SetLastError = true)]
            private static extern void DnsRecordListFree(IntPtr pRecordList, int FreeType);

            /// <summary>
            /// Gets the MX records.
            /// </summary>
            /// <param name="domain">The domain.</param>
            /// <returns></returns>
            public static string[] GetMXRecords(string domain)
            {
                var ptr1 = IntPtr.Zero;
                IntPtr ptr2;
                MXRecord recMx;

                if (Environment.OSVersion.Platform != PlatformID.Win32NT)
                {
                    throw new NotSupportedException();
                }

                var list1 = new List<string>();
                var num1 = DnsQuery(ref domain, QueryTypes.DnsTypeMX, QueryOptions.DnsQueryBypassCache, 0, ref ptr1, 0);

                if (num1 != 0)
                {
                    throw new Win32Exception(num1);
                }

                for (ptr2 = ptr1; !ptr2.Equals(IntPtr.Zero); ptr2 = recMx.PNext)
                {
                    recMx = (MXRecord) Marshal.PtrToStructure(ptr2, typeof (MXRecord));
                    if (recMx.WType != 15) continue;

                    var text1 = Marshal.PtrToStringAuto(recMx.PNameExchange);
                    list1.Add(text1);
                }

                DnsRecordListFree(ptr1, 0);
                return list1.ToArray();
            }

            #region Nested type: MXRecord

            [StructLayout(LayoutKind.Sequential)]
            private struct MXRecord
            {
                public IntPtr PNext;
                public string PName;
                public short WType;
                public short WDataLength;
                public int Flags;
                public int DwTtl;
                public int DwReserved;
                public IntPtr PNameExchange;
                public short WPreference;
                public short Pad;
            }

            #endregion

            #region Nested type: QueryOptions

            /// <summary>
            /// Query Options
            /// </summary>
            private enum QueryOptions
            {
                //DnsQueryAcceptTruncatedResponse = 1,
                /// <summary>
                /// DNS Query Bypass Cache
                /// </summary>
                DnsQueryBypassCache = 8,
                //DnsQueryDontResetTtlValues = 0x100000,
                //DnsQueryNoHostsFile = 0x40,
                //DnsQueryNoLocalName = 0x20,
                //DnsQueryNoNetbt = 0x80,
                //DnsQueryNoRecursion = 4,
                //DnsQueryNoWireQuery = 0x10,
                //DnsQueryReserved = -16777216,
                //DnsQueryReturnMessage = 0x200,
                //DnsQueryStandard = 0,
                //DnsQueryTreatAsFqdn = 0x1000,
                //DnsQueryUseTcpOnly = 2,
                //DnsQueryWireOnly = 0x100
            }

            #endregion

            #region Nested type: QueryTypes

            /// <summary>
            /// Query Types
            /// </summary>
            private enum QueryTypes
            {
                //DnsTypeA = 1,
                //DnsTypeNs = 2,
                //DnsTypeCname = 5,
                //DnsTypeSoa = 6,
                //DnsTypePtr = 12,
                //DnsTypeHinfo = 13,
                /// <summary>
                /// DNS Type MX
                /// </summary>
                DnsTypeMX = 15,
                //DnsTypeTxt = 16,
                //DnsTypeAaaa = 28
            }

            #endregion
        }

        #endregion

        #region Nested type: SmtpResponse

        /// <summary>
        /// Smtp Response
        /// </summary>
        private enum SmtpResponse
        {
            /// <summary>
            /// Connect Success
            /// </summary>
            ConnectSuccess = 220,
            /// <summary>
            /// Generic Success
            /// </summary>
            GenericSuccess = 250,
            /// <summary>
            /// Data Success
            /// </summary>
            DataSuccess = 354,
            /// <summary>
            /// Quit Success
            /// </summary>
            QuitSuccess = 221
        }

        #endregion
    }
}

    Month List

    Recent Tweets

    Twitter June 8, 16:30
    Traffic in town is nuts today

    Twitter May 27, 21:38
    Wish I'd found this a while ago... http://t.co/CIlE1Ekg9U

    Twitter May 23, 22:02
    I hope they replace the whole bridge #SkagitBridge

    Twitter May 23, 22:01
    Hey, cell phone carriers... So you think our cell coverage is "adequate" in Mount Vernon, huh? Have you seen how many times King 5 dropped

    Twitter May 23, 20:26
    Holy crap... I drove over that thing just a couple days ago... http://t.co/65vA8Vq6Wo

    @wforney

    Widget Twitter not found.

    Root element is missing.X

    VSAchievements

    Visual Studio Achievements
    Bill Forney (182 Points)
     
    Bill's bookshelf: read

    How to Win Friends & Influence People Getting Things Done: The Art of Stress-Free Productivity The Fellowship of the Ring The Dragon Reborn A Game of Thrones Towers of Midnight

    More of Bill's books »
    Book recommendations, book reviews, quotes, book clubs, book trivia, book lists

    Recent Comments

    Comment RSS

    About the author

    William L. Forney was born in Pennsylvania and relocated to Washington State in early 1999. His hobbies include all the usual things: movies, books, music, video games, etc. He writes short sci-fi/fantasy stories which will someday be published in the form of small novels.

    Bill's Photograph

    He loves computers and has worked in several different areas from web, multimedia, video and 3D animation to database and windows development.

    Currently he works at Visible Reality, LLC as the lead developer and improvGroup, LLC as a networking consultant.

    He also works with Padgett & Padgett, PLLC, the accounting firm where he setup shop after moving.

    His other blog can be found here.