tags:

views:

3106

answers:

7

I am trying to send an email using c# using the following code.

      MailMessage mail = new MailMessage();
      mail.From = new MailAddress(fromAddress, friendlyName);
      mail.To.Add(toAddress);
      mail.CC.Add(ccAddress);

      //set the content
      mail.Subject = emailSubject;
      mail.Body = emailHeader + "\n" + emailBody;

      //send the message
      SmtpClient smtp = new SmtpClient(ServerAddress);
      smtp.Credentials = CredentialCache.DefaultNetworkCredentials;
      mail.IsBodyHtml = true;
      smtp.Send(mail);

Now the "toAddress" string that my function recieves might contain a single address, or it might have many, comma delimited addresses.

Now the problem is that, in case of multiple comma delimited addresses, one or two of them might be of the wrong email address format.

So when I try to send an email using this code, I get the exception:

"The specified string is not in the form required for an e-mail address."

Is there any way to validate the comma delimited email addresses? I had read somewhere that the only way to validate an email address is to send an email to it, because the regular expressions to validate an email addreess can be surprisingly huge.

Also, I have no control over the design, or on how that address string comes to my function,I can't add the email validation in the UI, so I am helpless there...

My problem is that the email will not be delivered to ALL the addresses in the comma delimited string, even though only SOME of the addresses are of the wrong format.

Is there any way to properly validate email addresses in .NET? Is there a way to weed out the bad email addresses and send the mail to only the good ones?

+6  A: 

You could just split the email string on the comma and validate each email individual using a simple (or huge) email regex. Or try creating a MailAddress object it supports some basic validation of the address to.

Fredrik Leijon
you got seconds ahead of me with the response so I didn't re-post it... +1
AlexDrenea
+1. I think the MailAddress constructor is more than sufficient for his current purposes (a quick check before sending for the first time).
Matthew Flaschen
You should also note that emails are usually delimited with semicolons, not commas.
Kobi
Kobi, it used to be... Now only commas are accepted. http://rstew.blogspot.com/2007/06/specified-string-is-not-in-form.html
ashwnacharya
Oh. I'm outdated. But semicolons are still used commonly, aren't they? Outlook still use them, if that's an indication... Thanks @ashwnacharya.
Kobi
@Kobi - yeah, Outlook still uses them, as well as OWA (Outlook Web Access). It gets me every time I send an email to 2 people and use a comma, only to get the error "John.Smith,Jane.Doe is not a valid email"
Jared Harley
+2  A: 

Currently we are using following function and it is working quite well for us :)

    public static bool IsValidEmail(string email)
    {
        // source: http://thedailywtf.com/Articles/Validating_Email_Addresses.aspx
        Regex rx = new Regex(
        @"^[-!#$%&'*+/0-9=?A-Z^_a-z{|}~](\.?[-!#$%&'*+/0-9=?A-Z^_a-z{|}~])*@[a-zA-Z](-?[a-zA-Z0-9])*(\.[a-zA-Z](-?[a-zA-Z0-9])*)+$");
        return rx.IsMatch(email);
    }

Please use this:

(?:[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*|"(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21\x23-\x5b\x5d-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])*")@(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?|\[(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?|[a-z0-9-]*[a-z0-9]:(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21-\x5a\x53-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])+)\])
TheVillageIdiot
That one validates [email protected] but there are no one letter TLDs.
Jonas Elfström
yep this is no good :(
TheVillageIdiot
+1  A: 

The following will check that the e-mail address is of the correct form (not it that actually exists):

private bool isEmail(string inputEmail)
{
    Regex re = new Regex(@"^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$",
                  RegexOptions.IgnoreCase);
    return re.IsMatch(inputEmail);
}

I've updated this with a simpler expression (including case insensitivity) in order to hopefully make it a bit clearer.

The following is the basics of the code that will verify that the domain actually exists:

private bool isRealDomain(string inputEmail)
{
    bool isReal = false;
    try
    {
        string[] host = (inputEmail.Split('@'));
        string hostname = host[1];

        IPHostEntry IPhst = Dns.GetHostEntry(hostname);
        IPEndPoint endPt = new IPEndPoint(IPhst.AddressList[0], 25);
        Socket s = new Socket(endPt.AddressFamily,
                SocketType.Stream, ProtocolType.Tcp);
        s.Connect(endPt);
        s.Close();
        isReal = true;
    }
    catch (<specific exceptions here>)
    {
    }

    return isReal;
}

There is a lot more you can do, actually trying to connect for example, to verify that the domain will receive the mail. Plus you'll need to make sure you catch the necessary exceptions and handle them correctly.

ChrisF
IMHO you have way too many statements inside of the "try" for you to be ignoring all exceptions with a "catch". Any of those could fail for reasons that have nothing to do with the validity of the email address, and you won't know about it.
John Saunders
[email protected] is a perfectly legal email address but your Regex thinks it's not.
Jonas Elfström
@jonelf - I must admit that I "borrowed" the regex and haven't had time to check it out properly. Thanks for the heads up.
ChrisF
@John Saunders - the code is merely there as an example. I'll remove the try...catch if you feel it gets in the way.
ChrisF
@ChrisF: instead, think of setting a good example. You _know_ how many people find these answers and just copy and paste code. _You_ know it's just an example, and _I_ know it's just an example, but many of them don't read before they copy and paste.
John Saunders
@John - so true. Hopefully with the comments and the <specific exception here> it'll be clearer and I'll update the code later.
ChrisF
@jonelf - I've updated the regex which now copes with "+" in e-mails. Testing it again also showed up an error in some subsequent code so thanks again for pointing it out.
ChrisF
A: 

Yeah, split the emails string on the delimiter and then validate each email address. here's an example, it will fail on the second email address (foo#bar.com). I've used the regular expression from the asp.net regular expression control to validate the addresses.

String email = "[email protected];foo#bar.com";

String expression = @"\w+([-+.']\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*";

Regex regex = new Regex(expression);

String[] emails = email.Split(new Char[] { ';' });

foreach (String s in emails)
{

    Match m = regex.Match(s);

    if (!m.Success)
     {
        // Validation fails.

     }
}
Phil
+4  A: 

This is code we have on production (even added a comma for you). Normally you shouldn't use try/catch for validation, but it works well here. I believe it's better than trying to recode the validator.

string[] allToAddresses = to.Split(";,".ToCharArray(),
                                 StringSplitOptions.RemoveEmptyEntries)
foreach (string toAddress in allToAddresses)
    {
        try
        {
            message.To.Add(toAddress);
        }
        catch (FormatException)
        {
            //do nothing, illformed address. screw it.
        }
    }
Kobi
Are you sure that _every_ exception thrown by Add implies an illformed address? You should only catch the exceptions that actually _do_ mean a bad address. Your code will prevent you from seeing that one exception that says "something is horribly wrong; I hope someone sees this and does something about it; I'm going to die now; goodbye".
John Saunders
I knew that wouldn't be popular - but yes. Look at http://msdn.microsoft.com/en-us/library/ms144695.aspx - two exceptions for null and empty, and a FormatException. I guess you can always get an exotic exception (say, out of memory, or something crazy like that), so I'll change it. Thanks.
Kobi
Also, .NET isn't Java, and Microsoft gets to add exceptions over the course of time. It would be better to fail to catch some exception that actually _does_ imply an invalid email address, than to catch and ignore some exception that implies that something terrible is happening.
John Saunders
A: 

Hello,

why don't using a component to validate the e-mail addresses?

IMHO the best one is EmailVerify.NET (http://www.emailverify.net), which performs syntax checks on the addresses as well as DNS and SMTP connect validations. On top of that, it has an internal disposable address providers list, which is used by the component to flag temporary email addresses. It can even double-validate each address at the target mail server, in order to make sure it isn't a catch-all one!

There's also an online demo page, where you can test it interactively (http://www.emailverify.net/Demo.aspx).

Btw, price is low, less than 50 dollars or so.

Hope this helps.

-- Ryan Ashton

Ryan Ashton
A: 

Adding potential addresses to a MailMessage and checking for exceptions does not work well. E.g. it accepts asdf@asdf, and if you specify "asdf @ asdf " it throws an exception other than FormatException.

Naughton