Note: This is not the complete source code--just the main source file.
You can download the full source (with include files) from our
sample code archive by clicking on the diskette icons.
// HexillionEmailValidator sample control
// version 1.0.0.0
//
// Demonstrates how to create a custom email validator control.
// It does client-side validation with a JavaScript regular
// expression and server-side validation with the HexValidEmail
// component.
//
// HexGadgets (components) required:
// - HexValidEmail COM
// Info: http://www.HexGadgets.com/
// Download: http://www.hexillion.com/download/HexGadgets.exe
//
// History:
// 2003-01-23 1.0.0.0 Created
//
// Copyright 2003 Hexillion Technologies. All rights reserved.
//
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY
// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT
// LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND/OR
// FITNESS FOR A PARTICULAR PURPOSE.
using System;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.ComponentModel;
using System.Text;
using Hexillion.HexValidEmail.Interop;
namespace Hexillion.Samples.Interop
{
/// <summary>
/// An ASP.NET validator server control that validates email addresses using both
/// client-side and advanced server-side tests.
/// </summary>
/// <remarks>
/// <note>This control requires the <see href="http://www.hexillion.com/hg/HexValidEmail/">HexValidEmail</see>
/// component (COM version), which you can download as part of the
/// <see href="http://www.HexGadgets.com/">HexGadgets installation package</see>.
/// It also needs the HexValidEmail interop assembly, which is included with the
/// HexillionEmailValidator assembly.</note>
/// <note>To put the HexillionEmailValidator control in your Visual Studio .NET toolbox,
/// choose Customize Toolbox from the Tools menu. In the dialog, click on the .NET
/// Framework Components tab, then click Browse. Use the file dialog to find and select
/// HexillionEmailValidator.dll. After you close the Customize Toolbox dialog, the
/// validator control should be in your toolbox.</note>
/// <para>Use this control to validate email addresses entered into a text field. It does
/// both client-side validation (if enabled) and server-side validation. The server-side
/// validation uses <see href="http://www.hexillion.com/hg/HexValidEmail/">HexValidEmail</see>
/// to check the syntax, domain, and username.</para>
/// <para>The only property that you <i>must</i> set is <see cref="BaseValidator.ControlToValidate"/>.
/// However, we strongly urge you to also set the <see cref="FromDomain"/> and
/// <see cref="FromEmail"/> properties to <see href="http://www.hexillion.com/docs/guides/HexValidEmail/concepts/polite_usage.htm">be polite</see>
/// to mail server administrators.</para>
/// </remarks>
/// <include file='Examples.xml' path='documentation/class[@name="HexillionEmailValidator"]/*' />
[
DefaultProperty("ControlToValidate"),
ToolboxData("<{0}:HexillionEmailValidator runat=\"server\"></{0}:HexillionEmailValidator>")
]
public class HexillionEmailValidator : BaseValidator
{
public HexillionEmailValidator()
{
// Create a HexValidEmail.Connection object
_conn = new Connection();
_initialError = _conn.Error;
// Error message is client-side message initially
ErrorMessage = _clientSideMessage;
}
/// <summary>
/// Adds an attribute to the validator tag to hook it in to the client-side form validation.
/// </summary>
protected override void AddAttributesToRender( HtmlTextWriter writer )
{
base.AddAttributesToRender( writer );
if( RenderUplevel )
writer.AddAttribute("evaluationfunction", "HexillionEmailValidatorValidate");
}
/// <summary>
/// Places JavaScript code on the page for client-side validation.
/// </summary>
protected override void OnPreRender( EventArgs e )
{
base.OnPreRender( e );
// Register our client-side script block
if( RenderUplevel )
Page.RegisterClientScriptBlock( "HexillionEmailValidator",
string.Format( _clientScript,
_regex,
ControlToValidate,
ID,
_clientSideMessage ) );
}
/// <summary>
/// Gets or sets the highest level of validation to attempt.
/// </summary>
/// <value><b>1</b> for syntax, <b>2</b> for domain (DNS), <b>3</b> for username (SMTP)</value>
[
Description("The highest level of validation to attempt: 1 for syntax, 2 for domain (DNS), 3 for username (SMTP)" ),
DefaultValue(3),
Category("Behavior")
]
public int TryLevel
{
get { return (int)_levelTry; }
set
{
if( value < 1 || value > 3 )
throw new ArgumentException( "TryLevel must have a value from 1 to 3." );
_levelTry = (HexValidEmailLevel)value;
}
}
/// <summary>Gets or sets the lowest confidence rating required for the validator to pass the input.</summary>
/// <value>An integer from <b>1</b> to <b>3</b>. This should usually be set to <b>1</b>, the default value.</value>
/// <remarks>Only a rating of <b>0</b> indicates a bad address. Thus, this property should usually be
/// set to <b>1</b> to pass all addresses that are not confirmed bad.</remarks>
[
Description("The lowest confidence rating required for the validator to pass the input. This should usually be set to 1." ),
DefaultValue(1),
Category("Behavior")
]
public int RequiredRating
{
get { return (int)_levelRequired; }
set
{
if( value < 1 || value > 3 )
throw new ArgumentException( "RequiredRating must have a value from 1 to 3." );
_levelRequired = (HexValidEmailLevel)value;
}
}
/// <summary>
/// Gets or sets text to place after the server-side error message.
/// </summary>
/// <remarks>After a postback, the server-side validation determines the error message
/// to be displayed. This property gives you a chance to place text or HTML at the
/// end of that message.</remarks>
[
Description("Text to place after the server-side error message."),
DefaultValue(""),
Category("Appearance")
]
public string AppendedMessage
{
get { return _appendedMessage; }
set { _appendedMessage = value; }
}
/// <summary>
/// Gets or sets text to place before the server-side error message.
/// </summary>
/// <remarks>After a postback, the server-side validation determines the error message
/// to be displayed. This property gives you a chance to place text or HTML in front of
/// that message.</remarks>
[
Description("Text to place before the server-side error message."),
DefaultValue(""),
Category("Appearance")
]
public string PrependedMessage
{
get { return _prependedMessage; }
set { _prependedMessage = value; }
}
/// <summary>
/// Gets or sets the error message to displayed if the client-side validation
/// rejects the input.
/// </summary>
[
Description("Error message for client-side validation."),
DefaultValue("The email address is invalid"),
Category("Appearance")
]
public string ClientSideErrorMessage
{
get { return _clientSideMessage; }
set { _clientSideMessage = value; }
}
/// <summary>
/// Gets or sets the regular expression for validating the email address on the client side.
/// </summary>
/// <remarks>
/// Notes about the default regular expression:
/// <list type="bullet">
/// <item><description>
/// It is liberal within legal syntax. If you look at <see href="http://www.rfc-editor.org/rfc/rfc2822.txt">RFC 2822</see>,
/// there are a lot more legal characters than you might expect.
/// This regex allows them in the local part, regardless of whether
/// they are common. Likewise, the regex passes just about any
/// legal domain name, regardless of whether it's likely.
/// The general philosophy is to let most legal stuff
/// slide through the client-side validation and let the server-side
/// validations check for realism.
/// </description></item>
/// <item><description>
/// It is strict when syntax is illegal. Dash characters ("-"), for
/// example, are not allowed on the ends of the domain labels.
/// Also, domains cannot contain underscores ("_")... at least not
/// domains that conform to the old host name rules, which are what
/// domains in email addresses must follow these days.
/// </description></item>
/// <item><description>
/// It does require at least a second-level domain and conformance
/// conformance to host name standards (<see href="http://www.rfc-editor.org/rfc/rfc2822.txt">RFC 952</see>
/// and <see href="http://www.rfc-editor.org/rfc/rfc1123.txt">RFC 1123</see>).
/// </description></item>
/// <item><description>
/// It allows only IPv4 domain literals.
/// </description></item>
/// <item><description>
/// It does not allow display names, quoted literals, comments, or
/// whitespace. It basically allows a limited version of the
/// addr-spec form described in RFC 2822.
/// </description></item>
/// </list>
/// </remarks>
[
Description("Regular expression for validating the email address on the client side."),
DefaultValue(_defaultRegex),
Category("Behavior")
]
public string ClientSideRegex
{
get { return _regex; }
set { _regex = value; }
}
/// <summary>
/// Gets or sets the maximum time to wait for DNS validation (milliseconds).
/// </summary>
/// <value>An integer from 50 to 60000 representing milliseconds. The default is 4000.</value>
[
Description("Maximum time to wait for DNS validation (milliseconds)."),
DefaultValue(4000),
Category("Behavior")
]
public int DnsTimeout
{
get { return _dnsTimeout; }
set
{
if( value < 50 || value > 60000 )
throw new ArgumentException( "DnsTimeout must have a value from 50 to 60000 (milliseconds). We suggest 4000." );
_dnsTimeout = value;
}
}
/// <summary>
/// Gets or sets the maximum time to wait for SMTP validation (milliseconds).
/// </summary>
/// <value>An integer from 50 to 120000 representing milliseconds. The default is 10000.</value>
[
Description("Maximum time to wait for SMTP validation (milliseconds)."),
DefaultValue(10000),
Category("Behavior")
]
public int SmtpTimeout
{
get { return _smtpTimeout; }
set
{
if( value < 50 || value > 120000 )
throw new ArgumentException( "DnsTimeout must have a value from 50 to 120000 (milliseconds). We suggest 10000." );
_smtpTimeout = value;
}
}
/// <summary>
/// Gets or sets the domain name to use as identification during SMTP validation.
/// </summary>
/// <value>A domain name string that will be sent with the SMTP HELO command.
/// Set this to the domain name of your machine.</value>
/// <remarks>See <see href="http://www.hexillion.com/docs/guides/HexValidEmail/concepts/polite_usage.htm">
/// http://www.hexillion.com/docs/guides/HexValidEmail/concepts/polite_usage.htm</see> for more details.</remarks>
[
Description("The domain name to use as identification during SMTP validation. Set this to the domain name of your machine." ),
DefaultValue("hexillion.com"),
Category("Behavior")
]
public string FromDomain
{
get { return _conn.FromDomain; }
set
{
if( null == value )
throw new ArgumentNullException();
_conn.FromDomain = value;
}
}
/// <summary>
/// Gets or sets the email address to use as contact information during SMTP validation.
/// </summary>
/// <value>An email address string that will be sent with the SMTP MAIL FROM command.
/// Set this to the email address of a technical contact person at your domain.</value>
/// <remarks>See <see href="http://www.hexillion.com/docs/guides/HexValidEmail/concepts/polite_usage.htm">
/// http://www.hexillion.com/docs/guides/HexValidEmail/concepts/polite_usage.htm</see> for more details.</remarks>
[
Description("The email address to use as contact information during SMTP validation. Set this to the email address of a technical contact person at your domain." ),
DefaultValue("HexValidEmail@hexillion.com"),
Category("Behavior")
]
public string FromEmail
{
get { return _conn.FromEmail; }
set
{
if( null == value )
throw new ArgumentNullException();
_conn.FromEmail = value;
}
}
/// <summary>
/// Validates the referenced email address field.
/// </summary>
/// <returns><b>true</b> if the email address passes, <b>false</b> if it fails.</returns>
protected override bool EvaluateIsValid()
{
HexValidEmailLevel rating;
// Set the timeouts
_conn.Timeouts.Item(HexValidEmailTimeout.hexVeTimeoutDnsTotal).Value = _dnsTimeout;
_conn.Timeouts.Item(HexValidEmailTimeout.hexVeTimeoutSmtpTotal).Value = _smtpTimeout;
// Do the validation
rating = (HexValidEmailLevel)_conn.Validate( GetControlValidationValue( ControlToValidate ),
_levelTry );
StringBuilder sb = new StringBuilder();
sb.Append( _prependedMessage );
sb.Append( GetErrorString( _conn.Error ) );
sb.Append( _appendedMessage );
ErrorMessage = sb.ToString();
// Establish whether the address passed
bool passed = (rating >= _levelRequired);
// Set visibility of the error message
Style["visibility"] = passed ? "hidden" : "visible";
return passed;
}
private string GetErrorString(int error)
{
switch( (HexValidEmailErrors)error )
{
case HexValidEmailErrors.hexVeErrTimedOut:
return "Timed out";
case HexValidEmailErrors.hexVeErrConnectionRefused:
return "Connection refused";
case HexValidEmailErrors.hexVeErrConnectionReset:
return "Connection reset";
case HexValidEmailErrors.hexVeErrHostUnreachable:
return "Host unreachable";
case HexValidEmailErrors.hexVeErrAddressNotAvailable:
return "Address not available";
case HexValidEmailErrors.hexVeErrNetworkDown:
return "Network down";
case HexValidEmailErrors.hexVeErrNetworkUnreachable:
return "Network unreachable";
case HexValidEmailErrors.hexVeErrConnectionAborted:
return "Connection aborted";
case HexValidEmailErrors.hexVeErrHostNotFound:
return "Host not found";
case HexValidEmailErrors.hexVeErrTryAgain:
return "Try again";
case HexValidEmailErrors.hexVeErrNoRecovery:
return "No recovery";
case HexValidEmailErrors.hexVeErrNoData:
return "No data";
case HexValidEmailErrors.hexVeErrUnexpected:
return "Unexpected error";
case HexValidEmailErrors.hexVeErrAddrTooLong:
return "The email address is too long";
case HexValidEmailErrors.hexVeErrExtraTextPresent:
return "The email address has extra text that is not allowed";
case HexValidEmailErrors.hexVeErrIllegalChar:
return "The email address contains an illegal character";
case HexValidEmailErrors.hexVeErrUnbalancedParenthesis:
return "The email address contains an unbalanced parenthesis";
case HexValidEmailErrors.hexVeErrUnbalancedSquareBracket:
return "The email address contains an unbalanced square bracket";
case HexValidEmailErrors.hexVeErrUnbalancedAngleBracket:
return "The email address contains an unbalanced angle bracket";
case HexValidEmailErrors.hexVeErrQuotationMarksNotClosed:
return "Quotation marks in the email address are not closed";
case HexValidEmailErrors.hexVeErrDomainLiteralPresent:
return "The email address contains a domain literal, which is not allowed";
case HexValidEmailErrors.hexVeErrMisplacedDomainLiteral:
return "The email address contains a misplaced domain literal";
case HexValidEmailErrors.hexVeErrMisplacedQuotedString:
return "The email address contains a misplaced quoted string";
case HexValidEmailErrors.hexVeErrNoLocalPart:
return "The email address does not include a local part (username)";
case HexValidEmailErrors.hexVeErrNoDomain:
return "The email address does not include a domain";
case HexValidEmailErrors.hexVeErrInvalidDomain:
return "The domain in the email address is invalid";
case HexValidEmailErrors.hexVeErrInvalidDomainLiteral:
return "The email address contains an invalid domain literal";
case HexValidEmailErrors.hexVeErrNoDnsServerConfigured:
return "No DNS servers configured";
case HexValidEmailErrors.hexVeErrDomainDoesNotExist:
return "The email address domain does not exist";
case HexValidEmailErrors.hexVeErrNoMxForDomain:
return "The email address domain does not have a mail exchanger";
case HexValidEmailErrors.hexVeErrCouldNotVerifyRecipient:
return "Could not verify the local part (username) due to a problem communicating with the mail server";
case HexValidEmailErrors.hexVeErrRecipientRejected:
return "The domain's mail server rejected the email address";
case HexValidEmailErrors.hexVeErrNoAddrSpecified:
return "No email address was specified";
case HexValidEmailErrors.hexVeErrSuccess:
return "None";
case HexValidEmailErrors.hexVeErrLicenseFileNotFound:
return "License file not found";
case HexValidEmailErrors.hexVeErrCouldNotOpenLicenseFile:
return "Could not open license file";
case HexValidEmailErrors.hexVeErrCorruptLicenseFile:
return "License file is corrupt";
case HexValidEmailErrors.hexVeErrWrongProductLicense:
return "License is for wrong product";
case HexValidEmailErrors.hexVeErrWrongVersionLicense:
return "License is for wrong version";
case HexValidEmailErrors.hexVeErrUnlicensedProcessors:
return "Running on machine with unlicensed processors";
default:
return "Unknown error";
}
}
private HexValidEmailLevel _levelTry = HexValidEmailLevel.hexVeLevelSmtp;
private HexValidEmailLevel _levelRequired = HexValidEmailLevel.hexVeLevelSyntax;
private int _dnsTimeout = 4000;
private int _smtpTimeout = 10000;
private int _initialError;
private Connection _conn;
private string _appendedMessage = String.Empty;
private string _prependedMessage = String.Empty;
private string _clientSideMessage = "The email address is invalid";
private string _regex = _defaultRegex;
// See the ClientSideRegex property for comments about this expression.
private const string _defaultRegex = @"^(?:[\w\!\#\$\%\&\'\*\+\-\/\=\?\^\`\{\|\}\~]+\.)*[\w\!\#\$\%\&\'\*\+\-\/\=\?\^\`\{\|\}\~]+@(?:(?:(?:[a-zA-Z0-9](?:[a-zA-Z0-9\-](?!\.)){0,61}[a-zA-Z0-9]?\.)+[a-zA-Z0-9](?:[a-zA-Z0-9\-](?!$)){0,61}[a-zA-Z0-9]?)|(?:\[(?:(?:[01]?\d{1,2}|2[0-4]\d|25[0-5])\.){3}(?:[01]?\d{1,2}|2[0-4]\d|25[0-5])\]))$";
private const string _clientScript = @"
<script language=""JavaScript""><!--
// Client-side script for the HexillionEmailValidator sample control.
// http://www.Hexillion.com/samples/#HexillionEmailValidator
function HEVGetElement( id )
{{
if( document.all )
return document.all[id];
else
return document.getElementById( id );
}}
function HexillionEmailValidatorValidate()
{{
var re = /{0}/i;
if( !re.test( HEVGetElement( '{1}' ).value ) )
{{
// Reset to the client-side message in case there's
// an old server-side message still in place
HEVGetElement( '{2}' ).firstChild.nodeValue = '{3}';
return false;
}}
return true;
}}
//--></script>
";
}
}