Introduction
I was confronted with the issue of sending an e-mail to a list of recipients, none of which being aware of the other potential receivers (if any) of the message. Concretely, I desired to send a holidays greeting message to all of my acquaintances, without manually sending individual messages.
Background
There are three standard solutions to send an e-mail to more than one recipient, none of which is satisfactory for the task I needed accomplished:
- Filling up the To: field of the e-mail sending form - each receiver sees all of the other recipients
- Putting all except one receiver into the CC: (Carbon Copy: ) box - raises again the concern from point 1.
- Placing all except one recipient in the BCC: (Black Carbon Copy: ) field - each receiver sees the recipient in the To: selection, but none of the others residing in the BCC: area
None of these options proves reliable when the user expects an e-mail addressed individually to him / her.
Designing the Solution
The most natural choice is to repeatedly send the e-mail to a single recipient specified in the To: field. This cannot be efficiently done manually, so an automatic solution must be devised.
A specifically-designed application (or script) should:
- read the e-mail configuration of the mail server on which the sender's mail account dwells: SMTP (Simple Mail Transfer Protocol) host name, SMTP port and the availability of SSL (Secure Socket Layer) on the mail server
- retrieve the name of the sender to be viewed later by the receivers, as well as its' mail account name and password
- obtain the message subject and content to be sent
- get the list of names and e-mail addresses to which the message must be sent
- send the e-mails one-by-one
- print a report on the status of the e-mail sends
A sensible option would be to read this data from a configuration file set up by the user of the application prior to running it.
Configuration File
The format the HolidaysMailer
configuration file uses is XML, as shown in the subsequent EmailConfig.xml file:
="1.0" ="utf-8"
<!DOCTYPE EmailConfiguration [
<!ELEMENT EmailConfiguration (SmtpHost, SmtpPort,
FromAddress, FromName, Subject, Content, Addresses)>
<!ATTLIST EmailConfiguration EnableSSL (true|false) #REQUIRED>
<!ELEMENT SmtpHost (#PCDATA)>
<!ELEMENT SmtpPort (#PCDATA)>
<!ELEMENT FromAddress (#PCDATA)>
<!ELEMENT FromName (#PCDATA)>
<!ELEMENT Subject (#PCDATA)>
<!ELEMENT Content (#PCDATA)>
<!ELEMENT Addresses (Person*)>
<!ELEMENT Person (Name, Email)>
<!ELEMENT Name (#PCDATA)>
<!ELEMENT Email (#PCDATA)>
]>
<EmailConfiguration EnableSSL="true">
<SmtpHost>smtp.gmail.com</SmtpHost>
<SmtpPort>587</SmtpPort>
<FromAddress>mock.email@gmail.com</FromAddress>
<FromName>Mihnea Radulescu</FromName>
<Subject>Happy Easter</Subject>
<Content>Happy Easter and kindest wishes to you!
Yours sincerely,
Mihnea Radulescu</Content>
<Addresses>
<Person>
<Name>Mihnea Radulescu</Name>
<Email>mock.email@gmail.com</Email>
</Person>
</Addresses>
</EmailConfiguration>
The first part of the XML file contains the XML DTD schema for the configuration file that the user can guide itself by when writing the proper XML content and that provides the application a means of checking the validity of the XML document. The password for the e-mail account is not stored within the XML file for security reasons.
The second part of the XML features an example of configuration that is tailored for a Google mail account. It reads my e-mail credentials and sends the message to a single person (myself). Setting it up for Yahoo! requires the user to have a Yahoo! Mail Plus (paid) account, since an ordinary Yahoo! account does not offer SMTP functionality.
C# Implementation
.NET is endowed with classes particularly suited for e-mail-related necessities, supplied under the System.Net
and System.Net.Mail
namespaces. For the reading and interpreting of XML content, the System.Xml
namespace is utilized.
The implementation is a Console application relying on two classes: EmailConfiguration
, for the reading of the e-mail configuration from the XML file and EmailSender
, for the bulk (automated) sending of e-mails.
Two relevant code samples from these classes are shown below:
private void ParseXMLDocumentWorker(XmlReader xmlReader)
{
bool validName = false, validEmail = false;
string name = string.Empty, email = string.Empty;
try
{
while (xmlReader.Read())
{
switch (xmlReader.NodeType)
{
case XmlNodeType.Element:
switch (xmlReader.LocalName)
{
case "EmailConfiguration":
string enableSSLString =
xmlReader.GetAttribute("EnableSSL");
if (enableSSLString == "true")
enableSSL = true;
else
enableSSL = false;
break;
case "SmtpHost":
smtpHost = xmlReader.ReadString();
break;
case "SmtpPort":
string smtpPortString = xmlReader.ReadString();
smtpPort = int.Parse(smtpPortString);
break;
case "FromAddress":
fromAddress = xmlReader.ReadString();
break;
case "FromName":
fromName = xmlReader.ReadString();
break;
case "Subject":
subject = xmlReader.ReadString();
break;
case "Content":
content = xmlReader.ReadString();
break;
case "Person":
validName = false;
validEmail = false;
break;
case "Name":
name = xmlReader.ReadString();
validName = true;
break;
case "Email":
email = xmlReader.ReadString();
validEmail = true;
break;
}
break;
case XmlNodeType.EndElement:
switch (xmlReader.LocalName)
{
case "Person":
if ((validName == false) || (validEmail == false))
throw new XmlException
("Invalid or incomplete person details.");
else
{
string newAddress = name + " <" + email + ">";
toAddresses.Add(newAddress);
}
break;
}
break;
}
}
}
catch (XmlException)
{
xmlIsValid = false;
Console.Error.WriteLine("The e-mail configuration file is not a
well-formed XML document.");
}
}
private void SendEmail(string toAddress)
{
try
{
MailAddress mailAddressFrom = new MailAddress(fromAddress, fromName);
MailAddress mailAddressTo = new MailAddress(toAddress);
MailMessage emailMessage = new MailMessage(mailAddressFrom, mailAddressTo);
emailMessage.BodyEncoding = Encoding.UTF8;
emailMessage.IsBodyHtml = false;
emailMessage.Subject = subject;
emailMessage.Body = content;
NetworkCredential emailCredential =
new NetworkCredential(fromAddress, password);
SmtpClient mailClient = new SmtpClient(smtpHost, smtpPort);
mailClient.EnableSsl = enableSSL;
mailClient.UseDefaultCredentials = false;
mailClient.Credentials = emailCredential;
mailClient.Send(emailMessage);
Console.Out.WriteLine("E-mail to " + toAddress + " successfully sent.");
}
catch
{
errorCount++;
Console.Error.WriteLine("Failure sending e-mail to " + toAddress + ".");
}
}
Application Value
The application is useful for sending a message to a group of people, allowing each recipient to believe that the e-mail was sent specifically to him / her. In particular, holidays are instances when such a solution proves its' value, being required to greet a large number of acquaintances.
Source Code and Application Download
The complete source code of the HolidaysMailer
application (a Google Code project) can be accessed here. If one is only interested in the binaries, they can be downloaded from this link.
I would gladly welcome contributions and feedback to this HolidaysMailer open-source (GPL v3) project.
References
- [1] The Microsoft Developer Network (MSDN) pages
Contact
On my website, http://www.mihnearadulescu.com/.
History
- Version 0.1 - Initial submission - 01/03/2010
- Version 0.2 - Code updates - 03/04/2010
- Version 0.3 - Updated content, sources and binaries - 21/09/2011
- Version 0.4 - Removed the actual e-mail address from the article and the source code to avoid spam e-mails - 21/01/2013