#if NET_2_0 || NETSTANDARD2_0
#region Apache License
//
// Licensed to the Apache Software Foundation (ASF) under one or more
// contributor license agreements. See the NOTICE file distributed with
// this work for additional information regarding copyright ownership.
// The ASF licenses this file to you under the Apache License, Version 2.0
// (the "License"); you may not use this file except in compliance with
// the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
#endregion
// .NET Compact Framework 1.0 has no support for System.Web.Mail
// SSCLI 1.0 has no support for System.Web.Mail
#if !NETCF && !SSCLI
using System;
using System.IO;
using System.Text;
#if NET_2_0 || MONO_2_0 || NETSTANDARD2_0
using System.Net.Mail;
#else
using System.Web.Mail;
#endif
using log4net.Core;
namespace log4net.Appender
{
///
/// Send an e-mail when a specific logging event occurs, typically on errors
/// or fatal errors.
///
///
///
/// The number of logging events delivered in this e-mail depend on
/// the value of option. The
/// keeps only the last
/// logging events in its
/// cyclic buffer. This keeps memory requirements at a reasonable level while
/// still delivering useful application context.
///
///
/// Authentication and setting the server Port are only available on the MS .NET 1.1 runtime.
/// For these features to be enabled you need to ensure that you are using a version of
/// the log4net assembly that is built against the MS .NET 1.1 framework and that you are
/// running the your application on the MS .NET 1.1 runtime. On all other platforms only sending
/// unauthenticated messages to a server listening on port 25 (the default) is supported.
///
///
/// Authentication is supported by setting the property to
/// either or .
/// If using authentication then the
/// and properties must also be set.
///
///
/// To set the SMTP server port use the property. The default port is 25.
///
///
/// Nicko Cadell
/// Gert Driesen
public class SmtpAppender : BufferingAppenderSkeleton
{
#region Public Instance Constructors
///
/// Default constructor
///
///
///
/// Default constructor
///
///
public SmtpAppender()
{
}
#endregion // Public Instance Constructors
#region Public Instance Properties
///
/// Gets or sets a comma- or semicolon-delimited list of recipient e-mail addresses (use semicolon on .NET 1.1 and comma for later versions).
///
///
///
/// For .NET 1.1 (System.Web.Mail): A semicolon-delimited list of e-mail addresses.
///
///
/// For .NET 2.0 (System.Net.Mail): A comma-delimited list of e-mail addresses.
///
///
///
///
/// For .NET 1.1 (System.Web.Mail): A semicolon-delimited list of e-mail addresses.
///
///
/// For .NET 2.0 (System.Net.Mail): A comma-delimited list of e-mail addresses.
///
///
public string To
{
get { return m_to; }
set { m_to = MaybeTrimSeparators(value); }
}
///
/// Gets or sets a comma- or semicolon-delimited list of recipient e-mail addresses
/// that will be carbon copied (use semicolon on .NET 1.1 and comma for later versions).
///
///
///
/// For .NET 1.1 (System.Web.Mail): A semicolon-delimited list of e-mail addresses.
///
///
/// For .NET 2.0 (System.Net.Mail): A comma-delimited list of e-mail addresses.
///
///
///
///
/// For .NET 1.1 (System.Web.Mail): A semicolon-delimited list of e-mail addresses.
///
///
/// For .NET 2.0 (System.Net.Mail): A comma-delimited list of e-mail addresses.
///
///
public string Cc
{
get { return m_cc; }
set { m_cc = MaybeTrimSeparators(value); }
}
///
/// Gets or sets a semicolon-delimited list of recipient e-mail addresses
/// that will be blind carbon copied.
///
///
/// A semicolon-delimited list of e-mail addresses.
///
///
///
/// A semicolon-delimited list of recipient e-mail addresses.
///
///
public string Bcc
{
get { return m_bcc; }
set { m_bcc = MaybeTrimSeparators(value); }
}
///
/// Gets or sets the e-mail address of the sender.
///
///
/// The e-mail address of the sender.
///
///
///
/// The e-mail address of the sender.
///
///
public string From
{
get { return m_from; }
set { m_from = value; }
}
///
/// Gets or sets the subject line of the e-mail message.
///
///
/// The subject line of the e-mail message.
///
///
///
/// The subject line of the e-mail message.
///
///
public string Subject
{
get { return m_subject; }
set { m_subject = value; }
}
///
/// Gets or sets the name of the SMTP relay mail server to use to send
/// the e-mail messages.
///
///
/// The name of the e-mail relay server. If SmtpServer is not set, the
/// name of the local SMTP server is used.
///
///
///
/// The name of the e-mail relay server. If SmtpServer is not set, the
/// name of the local SMTP server is used.
///
///
public string SmtpHost
{
get { return m_smtpHost; }
set { m_smtpHost = value; }
}
///
/// Obsolete
///
///
/// Use the BufferingAppenderSkeleton Fix methods instead
///
///
///
/// Obsolete property.
///
///
[Obsolete("Use the BufferingAppenderSkeleton Fix methods")]
public bool LocationInfo
{
get { return false; }
set { ; }
}
///
/// The mode to use to authentication with the SMTP server
///
///
/// Authentication is only available on the MS .NET 1.1 runtime.
///
/// Valid Authentication mode values are: ,
/// , and .
/// The default value is . When using
/// you must specify the
/// and to use to authenticate.
/// When using the Windows credentials for the current
/// thread, if impersonating, or the process will be used to authenticate.
///
///
public SmtpAuthentication Authentication
{
get { return m_authentication; }
set { m_authentication = value; }
}
///
/// The username to use to authenticate with the SMTP server
///
///
/// Authentication is only available on the MS .NET 1.1 runtime.
///
/// A and must be specified when
/// is set to ,
/// otherwise the username will be ignored.
///
///
public string Username
{
get { return m_username; }
set { m_username = value; }
}
///
/// The password to use to authenticate with the SMTP server
///
///
/// Authentication is only available on the MS .NET 1.1 runtime.
///
/// A and must be specified when
/// is set to ,
/// otherwise the password will be ignored.
///
///
public string Password
{
get { return m_password; }
set { m_password = value; }
}
///
/// The port on which the SMTP server is listening
///
///
/// Server Port is only available on the MS .NET 1.1 runtime.
///
/// The port on which the SMTP server is listening. The default
/// port is 25. The Port can only be changed when running on
/// the MS .NET 1.1 runtime.
///
///
public int Port
{
get { return m_port; }
set { m_port = value; }
}
///
/// Gets or sets the priority of the e-mail message
///
///
/// One of the values.
///
///
///
/// Sets the priority of the e-mails generated by this
/// appender. The default priority is .
///
///
/// If you are using this appender to report errors then
/// you may want to set the priority to .
///
///
public MailPriority Priority
{
get { return m_mailPriority; }
set { m_mailPriority = value; }
}
#if NET_2_0 || MONO_2_0 || NETSTANDARD2_0
///
/// Enable or disable use of SSL when sending e-mail message
///
///
/// This is available on MS .NET 2.0 runtime and higher
///
public bool EnableSsl
{
get { return m_enableSsl; }
set { m_enableSsl = value; }
}
///
/// Gets or sets the reply-to e-mail address.
///
///
/// This is available on MS .NET 2.0 runtime and higher
///
public string ReplyTo
{
get { return m_replyTo; }
set { m_replyTo = value; }
}
#endif
///
/// Gets or sets the subject encoding to be used.
///
///
/// The default encoding is the operating system's current ANSI codepage.
///
public Encoding SubjectEncoding
{
get { return m_subjectEncoding; }
set { m_subjectEncoding = value; }
}
///
/// Gets or sets the body encoding to be used.
///
///
/// The default encoding is the operating system's current ANSI codepage.
///
public Encoding BodyEncoding
{
get { return m_bodyEncoding; }
set { m_bodyEncoding = value; }
}
#endregion // Public Instance Properties
#region Override implementation of BufferingAppenderSkeleton
///
/// Sends the contents of the cyclic buffer as an e-mail message.
///
/// The logging events to send.
protected override void SendBuffer(LoggingEvent[] events)
{
// Note: this code already owns the monitor for this
// appender. This frees us from needing to synchronize again.
try
{
using StringWriter writer = new StringWriter(System.Globalization.CultureInfo.InvariantCulture);
string t = Layout.Header;
if (t != null)
{
writer.Write(t);
}
for(int i = 0; i < events.Length; i++)
{
// Render the event and append the text to the buffer
RenderLoggingEvent(writer, events[i]);
}
t = Layout.Footer;
if (t != null)
{
writer.Write(t);
}
SendEmail(writer.ToString());
}
catch(Exception e)
{
ErrorHandler.Error("Error occurred while sending e-mail notification.", e);
}
}
#endregion // Override implementation of BufferingAppenderSkeleton
#region Override implementation of AppenderSkeleton
///
/// This appender requires a to be set.
///
/// true
///
///
/// This appender requires a to be set.
///
///
protected override bool RequiresLayout
{
get { return true; }
}
#endregion // Override implementation of AppenderSkeleton
#region Protected Methods
///
/// Send the email message
///
/// the body text to include in the mail
protected virtual void SendEmail(string messageBody)
{
#if NET_2_0 || MONO_2_0 || NETSTANDARD2_0
// .NET 2.0 has a new API for SMTP email System.Net.Mail
// This API supports credentials and multiple hosts correctly.
// The old API is deprecated.
// Create and configure the smtp client
#if NET_4_0 || MONO_4_0 || NETSTANDARD2_0
using SmtpClient smtpClient = new SmtpClient();
#else
SmtpClient smtpClient = new SmtpClient();
#endif
if (!String.IsNullOrEmpty(m_smtpHost))
{
smtpClient.Host = m_smtpHost;
}
smtpClient.Port = m_port;
smtpClient.DeliveryMethod = SmtpDeliveryMethod.Network;
smtpClient.EnableSsl = m_enableSsl;
if (m_authentication == SmtpAuthentication.Basic)
{
// Perform basic authentication
smtpClient.Credentials = new System.Net.NetworkCredential(m_username, m_password);
}
else if (m_authentication == SmtpAuthentication.Ntlm)
{
// Perform integrated authentication (NTLM)
smtpClient.Credentials = System.Net.CredentialCache.DefaultNetworkCredentials;
}
using (MailMessage mailMessage = new MailMessage())
{
mailMessage.Body = messageBody;
mailMessage.BodyEncoding = m_bodyEncoding;
mailMessage.From = new MailAddress(m_from);
mailMessage.To.Add(m_to);
if (!String.IsNullOrEmpty(m_cc))
{
mailMessage.CC.Add(m_cc);
}
if (!String.IsNullOrEmpty(m_bcc))
{
mailMessage.Bcc.Add(m_bcc);
}
if (!String.IsNullOrEmpty(m_replyTo))
{
// .NET 4.0 warning CS0618: 'System.Net.Mail.MailMessage.ReplyTo' is obsolete:
// 'ReplyTo is obsoleted for this type. Please use ReplyToList instead which can accept multiple addresses. http://go.microsoft.com/fwlink/?linkid=14202'
#if !NET_4_0 && !MONO_4_0 && !NETSTANDARD2_0
mailMessage.ReplyTo = new MailAddress(m_replyTo);
#else
mailMessage.ReplyToList.Add(new MailAddress(m_replyTo));
#endif
}
mailMessage.Subject = m_subject;
mailMessage.SubjectEncoding = m_subjectEncoding;
mailMessage.Priority = m_mailPriority;
// TODO: Consider using SendAsync to send the message without blocking. This would be a change in
// behaviour compared to .NET 1.x. We would need a SendCompletedCallback to log errors.
smtpClient.Send(mailMessage);
}
#else
// .NET 1.x uses the System.Web.Mail API for sending Mail
MailMessage mailMessage = new MailMessage();
mailMessage.Body = messageBody;
mailMessage.BodyEncoding = m_bodyEncoding;
mailMessage.From = m_from;
mailMessage.To = m_to;
if (m_cc != null && m_cc.Length > 0)
{
mailMessage.Cc = m_cc;
}
if (m_bcc != null && m_bcc.Length > 0)
{
mailMessage.Bcc = m_bcc;
}
mailMessage.Subject = m_subject;
#if !MONO && !NET_1_0 && !NET_1_1 && !CLI_1_0
mailMessage.SubjectEncoding = m_subjectEncoding;
#endif
mailMessage.Priority = m_mailPriority;
#if NET_1_1
// The Fields property on the MailMessage allows the CDO properties to be set directly.
// This property is only available on .NET Framework 1.1 and the implementation must understand
// the CDO properties. For details of the fields available in CDO see:
//
// http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cdosys/html/_cdosys_configuration_coclass.asp
//
try
{
if (m_authentication == SmtpAuthentication.Basic)
{
// Perform basic authentication
mailMessage.Fields.Add("http://schemas.microsoft.com/cdo/configuration/smtpauthenticate", 1);
mailMessage.Fields.Add("http://schemas.microsoft.com/cdo/configuration/sendusername", m_username);
mailMessage.Fields.Add("http://schemas.microsoft.com/cdo/configuration/sendpassword", m_password);
}
else if (m_authentication == SmtpAuthentication.Ntlm)
{
// Perform integrated authentication (NTLM)
mailMessage.Fields.Add("http://schemas.microsoft.com/cdo/configuration/smtpauthenticate", 2);
}
// Set the port if not the default value
if (m_port != 25)
{
mailMessage.Fields.Add("http://schemas.microsoft.com/cdo/configuration/smtpserverport", m_port);
}
}
catch(MissingMethodException missingMethodException)
{
// If we were compiled against .NET 1.1 but are running against .NET 1.0 then
// we will get a MissingMethodException when accessing the MailMessage.Fields property.
ErrorHandler.Error("SmtpAppender: Authentication and server Port are only supported when running on the MS .NET 1.1 framework", missingMethodException);
}
#else
if (m_authentication != SmtpAuthentication.None)
{
ErrorHandler.Error("SmtpAppender: Authentication is only supported on the MS .NET 1.1 or MS .NET 2.0 builds of log4net");
}
if (m_port != 25)
{
ErrorHandler.Error("SmtpAppender: Server Port is only supported on the MS .NET 1.1 or MS .NET 2.0 builds of log4net");
}
#endif // if NET_1_1
if (m_smtpHost != null && m_smtpHost.Length > 0)
{
SmtpMail.SmtpServer = m_smtpHost;
}
SmtpMail.Send(mailMessage);
#endif // if NET_2_0
}
#endregion // Protected Methods
#region Private Instance Fields
private string m_to;
private string m_cc;
private string m_bcc;
private string m_from;
private string m_subject;
private string m_smtpHost;
private Encoding m_subjectEncoding = Encoding.UTF8;
private Encoding m_bodyEncoding = Encoding.UTF8;
// authentication fields
private SmtpAuthentication m_authentication = SmtpAuthentication.None;
private string m_username;
private string m_password;
// server port, default port 25
private int m_port = 25;
private MailPriority m_mailPriority = MailPriority.Normal;
#if NET_2_0 || MONO_2_0 || NETSTANDARD2_0
private bool m_enableSsl = false;
private string m_replyTo;
#endif
#endregion // Private Instance Fields
#region SmtpAuthentication Enum
///
/// Values for the property.
///
///
///
/// SMTP authentication modes.
///
///
public enum SmtpAuthentication
{
///
/// No authentication
///
None,
///
/// Basic authentication.
///
///
/// Requires a username and password to be supplied
///
Basic,
///
/// Integrated authentication
///
///
/// Uses the Windows credentials from the current thread or process to authenticate.
///
Ntlm
}
#endregion // SmtpAuthentication Enum
private static readonly char[] ADDRESS_DELIMITERS = new char[] { ',', ';' };
///
/// trims leading and trailing commas or semicolons
///
private static string MaybeTrimSeparators(string s) {
#if NET_2_0 || MONO_2_0 || NETSTANDARD2_0
return string.IsNullOrEmpty(s) ? s : s.Trim(ADDRESS_DELIMITERS);
#else
return s != null && s.Length > 0 ? s : s.Trim(ADDRESS_DELIMITERS);
#endif
}
}
}
#endif // !NETCF && !SSCLI
#endif // NET_2_0