Click here to Skip to main content
15,881,172 members
Articles / Programming Languages / C#
Article

Command Line Emailer

Rate me:
Please Sign up or sign in to vote.
4.93/5 (34 votes)
24 Sep 20034 min read 153.4K   1.5K   78   37
A console application for sending email from the command line

Image 1

Introduction

This article describes a .NET console application to send email using a command line. It shows methods for parsing command line arguments, loading and parsing a simple parameter file, and using the System.Web.Mail namespace to construct and send emails.

Background

I continue to use batch files for a number of administrative tasks. Recently when working with a batch file of NTBACKUP commands, I was interested in having the log file text sent through email and developed this application for that purpose.

Using the code

The application receives all arguments as either command line switches, or as supplied through a parameter file. The following shows the usage of the console command and its available switches.

Usage:
  CommandLineEmailer [/p:parameterFile] [/smtp:smtpserver] [/f:from]
                     [/to:recipients] [/cc:courtesyCopies]
                     [/bcc:blindCourtesyCopies] [/s:subject] [/b:body]
                     [/a:attachment] [/i:insertFile] [/?] 

Switches:
  /p:     file containing message parameters such as the Smtp server, 
          recipients lists, and the message subject.

  /smtp:  smtp server

  /f:     message sender (from)

  /to:    semicolon-delimited list of message recipients

  /cc:    semicolon-delimited list of message courtesy-copy recipients 

  /bcc:   semicolon-delimited list of message blind courtesy-copy recipients

  /s:     message subject

  /b:     message body

  /a:     file attachment
  
  /i:     insert the supplied text file in the body

  /?      display help text

The parameter file that may be supplied with the /p: switch is an ASCII file with lines in the form:

key = value

The following describes the keys that may be used in a parameter file:

smtpserver = the SMTP mail server used to send the message
from       = the message sender
to         = a semicolon-delimited list of message recipients
cc         = courtesy copies (semicolon-delimited list)
bcc        = blind courtesy copies (semicolon-delimited list)
subject    = the message subject
body       = A line of the message body; multiple body= lines
             may appear in the parameter file to include multiple
             lines in the message body
attachment = A full or partial path to a file that will be attached
             to the email message; multiple attachment= lines
             may appear in the parameter file to attach multiple files
insertfile = A full or partial path to a text file whose contents will
             be inserted into the body of the email message; multiple
             insertfile= lines may appear in the parameter file
This shows the text of an example parameter file:
smtpserver = myserver.com
from       = me@myserver.com
to         = recipient1@myserver.com; recipient2@myserver.com
cc         = cc1@myserver.com
subject    = Test Message
body       = This should appear as the first line of the body.
body       = This should appear as the second line of the body.
body       = This should appear as the third line of the body.
body       =
body       = The text of file "myprocess.log" follows:
body       = --------------------------------------
insertfile = myprocess.log
body       = --------------------------------------
body       =
body       = Finally, some attachments:
attachment = c:\files\FirstAttachment.txt
attachment = c:\files\SecondAttachment.txt
If the above parameter file is saved as myParms.txt, then the CommandLineEmailer program may be executed with the following at the command line or in a batch file:
CommandLineEmailer /p:myParms.txt
As an alternative, all required values may be supplied as command line switches. Only one line of body text may be added this way.
CommandLineEmailer /smtp:myserver.com /from:me@myserver.com
                   /to:recipient@myserver.com
                   /subject:Test Message /b:This is the message body.
A combination of switches and a parameter file may be also used. For example, with this parameter file defining the "smtpserver" and "from" values:
smtpserver = myserver.com
from       = me@myserver.com
values for "to", "subject", and "insertfile" may be supplied on the command line:
CommandLineEmailer /p:myParms.txt /to:recipient@myserver.com
                   /subject:Log Report /i:myprocess.log

Describing the code

Three code samples from CommandLineEmailer are described here to show approaches for command line parsing, line by line text file parsing, and email sending.

CommandLineParser Class

This class shows one approach to parsing the command line. For this application, all arguments are supplied through switches. The class constructor is defined with the command line as a string argument, which is converted to an array of strings using the .Split('/') method. Each individual string in the array is inspected to see if a colon exists, intended to separate the switch from its supplied value. If the colon exists, the switch and value are parsed out and added to a local StringDictionary object, with the switch being the key. The StringDictionary class, exposed through the System.Collections.Specialized namespace, represents a collection of string values associated with string keys. If the colon does not exist, the switch and a blank string "" as its associated value are added to the StringDictionary.

C#
public CommandLineParser(string sCommandLine)
{
    // given the command line setup an ArrayList of switch items
    // switches in the form /s:xxx are supported

    _switches = new StringDictionary();

    // create an array of items split by the / symbol
    string[] arr = sCommandLine.Split('/');

    // loop through each string in the array
    foreach (string s in arr)
    {
        string st = s.Trim();
        if (st != "")
        {
            // if the switch has a value it will follow a
            // colon after the switch
            int i = st.IndexOf(":");
            if (i > 0)
            {
                // we have a switch and a value;
                // add to the StringDictionary
                // as a convenience, strip out any double quotes
                string k = st.Substring(0, i);
                string v = st.Substring(i + 1);
                _switches.Add(k.Trim(), v.Trim().Replace("\"", ""));
            }
            else
            {
                // we have a switch without a value;
                // add to the StringDictionary with a blank string
                _switches.Add(st, "");
            }
        }
    }

}

The class also defines HasSwitch and SwitchValue methods, and an AllSwitches property:

C#
private StringDictionary _switches;

public bool HasSwitch(string sSwitch)
{
    // return TRUE if the given switch (without the slash) exists
    return (_switches.ContainsKey(sSwitch));
}

public string SwitchValue(string sSwitch)
{
    // return the value associated with the given switch,
    // or null if the switch doesn't exist
    if (HasSwitch(sSwitch) )
    {
        return _switches[sSwitch];
    }
    else
    {
        return null;
    }
}

public StringDictionary AllSwitches
{
    get {return _switches;}
}

The class is then used in the application's main() function like this:

C#
static void Main(string[] args)
{

    // parse the command line
    CommandLineParser cp = new CommandLineParser(
        System.Environment.CommandLine);
        .
        .
        .
    // if a parameter file has been supplied, process it first
    if (cp.HasSwitch("p")) ProcessParameterFile(cp.SwitchValue("p"));

    // then process any other switches that may
    // override values or add to values
    if (cp.HasSwitch("smtp")) _smtpserver = cp.SwitchValue("smtp");
    if (cp.HasSwitch("f"))    _from       = cp.SwitchValue("f");
        .
        .
        .
}

This approach to command line parsing is limited in that arguments are expected to be passed as /switches. However, if that is suitable for the application at hand, the class may be used as is.

Line by line text file parsing

Given the parameter files used by this application are typically short, the approach used here is to read the entire file into a string and again use the string's .Split() method to create a string array of lines that are individually parsed. Reading the file into a string is accomplished with the following:

C#
public static string LoadTextFile(string f) {
    FileStream fs = File.OpenRead(f);
    StreamReader sr = new StreamReader(fs);
    String contents = sr.ReadToEnd();
    sr.Close();
    fs.Close();
    return contents;
}
Then the array of individual lines is created with the following function:
C#
public static string[] GetLinesFromString(string contents)
{
    return contents.Split('\n');
}

Each line is then parsed with the following code to set message variables.

C#
public static void ParseLine(string line)
{
    string[] a = line.Split('=');
    string key = (a.Length == 0 ? "" : a[0].Trim());
    string val = (a.Length == 1 ? "" : a[1].Trim());

    switch (key.ToLower())
    {
        case "smtpserver"   : _smtpserver = val;     break;
        case "from"         : _from = val;           break;
        case "to"           : _to = val;             break;
        case "cc"           : _cc = val;             break;
        case "bcc"          : _bcc = val;            break;
        case "subject"      : _subject = val;        break;
        case "body"         : _textblocks.Add(val);  break;
        case "attachment"   : _attachments.Add(val); break;
        case "insertfile"   :
            try
            {
                _textblocks.Add(LoadTextFile(val));
            }
            catch (Exception ex)
            {
                _textblocks.Add("<Error inserting text file " + val +
                      ": " + ex.Message + ">");
            }
            break;

        // everything else is ignored;
    }

}

Most of the message variables are simple strings. Body text (supplied through either 'body=' or 'insertfile=' lines) is handled as an ArrayList of string objects and enumerated prior to sending to construct the message body. This allows multiple body lines to be identified and/or multiple text files to be inserted if desired. The try/catch block in the case "insertfile:" case attempts to load the text file and insert its contents; if there is an error, the message body is still constructed with the error text inserted instead.

Sending the Email

The System.Web.Mail namespace exposes three classes used here for constructing and sending the email. The SmtpMail class is used both to set the mail server (through the SmtpMail.Server property), and to send the message itself (through the SmtpMail.Send method). The MailMessage class represents the message, identifying the sender, recipients, subject, and body text. The MailAttachment class represents a file attachment in a MailMessage.Attachments collection.

The beginning of the SendEmailMessage function in the CommandLineEmailer application sets up the basic message properties with application variables:

C#
public static void SendEmailMessage()
{
    SmtpMail.SmtpServer = _smtpserver;
    MailMessage mm = new MailMessage();
    mm.From = _from;
    mm.To = _to;
    mm.Cc = _cc;
    mm.Bcc = _bcc;
    mm.Subject = _subject;
The body of the email message is constructed using a StringBuilder object by iterating through each string in the _textblocks ArrayList:
C#
// add all textblocks to the body
StringBuilder sb = new StringBuilder();
foreach(string s in _textblocks)
{
    sb.Append(s + System.Environment.NewLine);
}
mm.Body = sb.ToString() + System.Environment.NewLine;
Attachments are then added to the message's Attachments collection. A try/catch block is used to insert error text in the message body if an attachment cannot be added.
C#
// attempt to add any attachments; on an error here,
// add error text to the body
foreach(string s in _attachments)
{
    try
    {
        string fullPath = System.IO.Path.GetFullPath(s);
        mm.Attachments.Add(new MailAttachment(fullPath));
    }
    catch (Exception ex)
    {
        mm.Body += System.Environment.NewLine;
        mm.Body += "<Error attaching file " + s + ": " +
             ex.Message + ">";
        mm.Body += System.Environment.NewLine;
    }
}

Finally, the message is sent through the SmtpServer object:

C#
    SmtpMail.Send(mm);
}

Summary

This article demonstrated how to use the CommandLineEmailer console application for sending mail from a command line or batch file. It also described a method for parsing command line arguments and encapsulating such functionality in a reusable class. An approach for parsing a simple parameter file line by line was explored, as were the SmtpServer, MailMessage, and MailAttachment objects from the System.Web.Mail namespace.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
University of Nevada, Las Vegas
United States United States
With a background in education, music, application development, institutional research, data governance, and business intelligence, I work for the University of Nevada, Las Vegas helping to derive useful information from institutional data. It's an old picture, but one of my favorites.

Comments and Discussions

 
QuestionAttachment Problem Pin
Member 914967222-Sep-12 2:23
Member 914967222-Sep-12 2:23 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.