a575963da9
Former-commit-id: da6be194a6b1221998fc28233f2503bd61dd9d14
300 lines
9.5 KiB
C#
300 lines
9.5 KiB
C#
//
|
|
// System.Web.Mail.SmtpClient.cs
|
|
//
|
|
// Author(s):
|
|
// Per Arneng <pt99par@student.bth.se>
|
|
// Sanjay Gupta <gsanjay@novell.com>
|
|
// (C) 2004, Novell, Inc. (http://www.novell.com)
|
|
//
|
|
|
|
//
|
|
// Permission is hereby granted, free of charge, to any person obtaining
|
|
// a copy of this software and associated documentation files (the
|
|
// "Software"), to deal in the Software without restriction, including
|
|
// without limitation the rights to use, copy, modify, merge, publish,
|
|
// distribute, sublicense, and/or sell copies of the Software, and to
|
|
// permit persons to whom the Software is furnished to do so, subject to
|
|
// the following conditions:
|
|
//
|
|
// The above copyright notice and this permission notice shall be
|
|
// included in all copies or substantial portions of the Software.
|
|
//
|
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
//
|
|
|
|
using System.Net;
|
|
using System.IO;
|
|
using System.Text;
|
|
using System.Collections;
|
|
using System.Net.Sockets;
|
|
using System.Security.Permissions;
|
|
using System.Reflection;
|
|
|
|
namespace System.Web.Mail {
|
|
|
|
/// represents a conntection to a smtp server
|
|
internal class SmtpClient
|
|
{
|
|
string server;
|
|
TcpClient tcpConnection;
|
|
SmtpStream smtp;
|
|
string username;
|
|
string password;
|
|
int port = 25;
|
|
bool usessl = false;
|
|
short authenticate = 1;
|
|
|
|
//Initialise the variables and connect
|
|
public SmtpClient (string server)
|
|
{
|
|
this.server = server;
|
|
}
|
|
|
|
// make the actual connection
|
|
// and HELO handshaking
|
|
void Connect ()
|
|
{
|
|
tcpConnection = new TcpClient (server, port);
|
|
|
|
NetworkStream stream = tcpConnection.GetStream ();
|
|
smtp = new SmtpStream (stream);
|
|
}
|
|
|
|
void ChangeToSSLSocket ()
|
|
{
|
|
#if TARGET_JVM
|
|
java.lang.Class c = vmw.common.TypeUtils.ToClass (smtp.Stream);
|
|
java.lang.reflect.Method m = c.getMethod ("ChangeToSSLSocket", null);
|
|
m.invoke (smtp.Stream, new object[]{});
|
|
#else
|
|
// Load Mono.Security.dll
|
|
Assembly a;
|
|
try {
|
|
a = Assembly.Load (Consts.AssemblyMono_Security);
|
|
} catch (System.IO.FileNotFoundException) {
|
|
throw new SmtpException ("Cannot load Mono.Security.dll");
|
|
}
|
|
Type tSslClientStream = a.GetType ("Mono.Security.Protocol.Tls.SslClientStream");
|
|
object[] consArgs = new object[4];
|
|
consArgs[0] = smtp.Stream;
|
|
consArgs[1] = server;
|
|
consArgs[2] = true;
|
|
Type tSecurityProtocolType = a.GetType ("Mono.Security.Protocol.Tls.SecurityProtocolType");
|
|
int nSsl3Val = (int) Enum.Parse (tSecurityProtocolType, "Ssl3");
|
|
int nTlsVal = (int) Enum.Parse (tSecurityProtocolType, "Tls");
|
|
consArgs[3] = Enum.ToObject (tSecurityProtocolType, nSsl3Val | nTlsVal);
|
|
|
|
object objSslClientStream = Activator.CreateInstance (tSslClientStream, consArgs);
|
|
|
|
if (objSslClientStream != null)
|
|
smtp = new SmtpStream ((Stream)objSslClientStream);
|
|
#endif
|
|
}
|
|
|
|
void ReadFields (MailMessageWrapper msg)
|
|
{
|
|
string tmp;
|
|
username = msg.Fields.Data ["http://schemas.microsoft.com/cdo/configuration/sendusername"];
|
|
password = msg.Fields.Data ["http://schemas.microsoft.com/cdo/configuration/sendpassword"];
|
|
tmp = msg.Fields.Data ["http://schemas.microsoft.com/cdo/configuration/smtpauthenticate"];
|
|
if (tmp != null)
|
|
authenticate = short.Parse (tmp);
|
|
tmp = msg.Fields.Data ["http://schemas.microsoft.com/cdo/configuration/smtpusessl"];
|
|
if (tmp != null)
|
|
usessl = bool.Parse (tmp);
|
|
tmp = msg.Fields.Data ["http://schemas.microsoft.com/cdo/configuration/smtpserverport"];
|
|
if (tmp != null)
|
|
port = int.Parse (tmp);
|
|
}
|
|
|
|
void StartSend (MailMessageWrapper msg)
|
|
{
|
|
ReadFields (msg);
|
|
Connect ();
|
|
|
|
// read the server greeting
|
|
smtp.ReadResponse ();
|
|
smtp.CheckForStatusCode (220);
|
|
|
|
if (usessl || (username != null && password != null && authenticate != 1))
|
|
{
|
|
smtp.WriteEhlo (Dns.GetHostName ());
|
|
|
|
if (usessl) {
|
|
bool isSSL = smtp.WriteStartTLS ();
|
|
if (isSSL)
|
|
ChangeToSSLSocket ();
|
|
}
|
|
|
|
if (username != null && password != null && authenticate != 1) {
|
|
smtp.WriteAuthLogin ();
|
|
if (smtp.LastResponse.StatusCode == 334) {
|
|
smtp.WriteLine (Convert.ToBase64String (Encoding.ASCII.GetBytes (username)));
|
|
smtp.ReadResponse ();
|
|
smtp.CheckForStatusCode (334);
|
|
smtp.WriteLine (Convert.ToBase64String (Encoding.ASCII.GetBytes (password)));
|
|
smtp.ReadResponse ();
|
|
smtp.CheckForStatusCode (235);
|
|
}
|
|
}
|
|
} else {
|
|
smtp.WriteHelo (Dns.GetHostName ());
|
|
}
|
|
}
|
|
|
|
public void Send (MailMessageWrapper msg)
|
|
{
|
|
if (msg.From == null)
|
|
throw new SmtpException ("From property must be set.");
|
|
|
|
if (msg.To == null)
|
|
if (msg.To.Count < 1)
|
|
throw new SmtpException ("Atleast one recipient must be set.");
|
|
|
|
StartSend (msg);
|
|
// start with a reset incase old data
|
|
// is present at the server in this session
|
|
smtp.WriteRset ();
|
|
|
|
// write the mail from command
|
|
smtp.WriteMailFrom (msg.From.Address);
|
|
|
|
// write the rcpt to command for the To addresses
|
|
foreach (MailAddress addr in msg.To)
|
|
smtp.WriteRcptTo (addr.Address);
|
|
|
|
// write the rcpt to command for the Cc addresses
|
|
foreach (MailAddress addr in msg.Cc)
|
|
smtp.WriteRcptTo (addr.Address);
|
|
|
|
// write the rcpt to command for the Bcc addresses
|
|
foreach (MailAddress addr in msg.Bcc)
|
|
smtp.WriteRcptTo (addr.Address);
|
|
|
|
// write the data command and then
|
|
// send the email
|
|
smtp.WriteData ();
|
|
|
|
if (msg.Attachments.Count == 0)
|
|
SendSinglepartMail (msg);
|
|
else
|
|
SendMultipartMail (msg);
|
|
|
|
// write the data end tag "."
|
|
smtp.WriteDataEndTag ();
|
|
}
|
|
|
|
// sends a single part mail to the server
|
|
void SendSinglepartMail (MailMessageWrapper msg)
|
|
{
|
|
// write the header
|
|
smtp.WriteHeader (msg.Header);
|
|
|
|
// send the mail body
|
|
smtp.WriteBytes (msg.BodyEncoding.GetBytes (msg.Body));
|
|
}
|
|
|
|
// SECURITY-FIXME: lower assertion with imperative asserts
|
|
[FileIOPermission (SecurityAction.Assert, Unrestricted = true)]
|
|
// sends a multipart mail to the server
|
|
void SendMultipartMail (MailMessageWrapper msg)
|
|
{
|
|
// generate the boundary between attachments
|
|
string boundary = MailUtil.GenerateBoundary ();
|
|
|
|
// set the Content-Type header to multipart/mixed
|
|
string bodyContentType = msg.Header.ContentType;
|
|
|
|
msg.Header.ContentType = String.Concat ("multipart/mixed;\r\n boundary=", boundary);
|
|
|
|
// write the header
|
|
smtp.WriteHeader (msg.Header);
|
|
|
|
// write the first part text part
|
|
// before the attachments
|
|
smtp.WriteBoundary (boundary);
|
|
|
|
MailHeader partHeader = new MailHeader ();
|
|
partHeader.ContentType = bodyContentType;
|
|
|
|
// Add all the custom headers to body part as specified in
|
|
//Fields property of MailMessageWrapper
|
|
|
|
//Remove fields specific for authenticating to SMTP server.
|
|
//Need to incorporate AUTH command in SmtpStream to handle
|
|
//Authorization info. Its a temporary fix for Bug no 68829.
|
|
//Will dig some more on SMTP AUTH command, and then implement
|
|
//Authorization. - Sanjay
|
|
|
|
if (msg.Fields.Data ["http://schemas.microsoft.com/cdo/configuration/smtpauthenticate"] != null)
|
|
msg.Fields.Data.Remove ("http://schemas.microsoft.com/cdo/configuration/smtpauthenticate");
|
|
if (msg.Fields.Data ["http://schemas.microsoft.com/cdo/configuration/sendusername"] != null)
|
|
msg.Fields.Data.Remove ("http://schemas.microsoft.com/cdo/configuration/sendusername");
|
|
if (msg.Fields.Data ["http://schemas.microsoft.com/cdo/configuration/sendpassword"] != null)
|
|
msg.Fields.Data.Remove ("http://schemas.microsoft.com/cdo/configuration/sendpassword");
|
|
partHeader.Data.Add (msg.Fields.Data);
|
|
|
|
smtp.WriteHeader (partHeader);
|
|
|
|
// FIXME: probably need to use QP or Base64 on everything higher
|
|
// then 8-bit .. like utf-16
|
|
smtp.WriteBytes (msg.BodyEncoding.GetBytes (msg.Body) );
|
|
|
|
smtp.WriteBoundary (boundary);
|
|
|
|
// now start to write the attachments
|
|
|
|
for (int i=0; i< msg.Attachments.Count ; i++) {
|
|
MailAttachment a = (MailAttachment)msg.Attachments[ i ];
|
|
FileInfo fileInfo = new FileInfo (a.Filename);
|
|
MailHeader aHeader = new MailHeader ();
|
|
|
|
aHeader.ContentType =
|
|
String.Concat (MimeTypes.GetMimeType (fileInfo.Name), "; name=\"", fileInfo.Name, "\"");
|
|
|
|
aHeader.ContentDisposition = String.Concat ("attachment; filename=\"", fileInfo.Name, "\"");
|
|
aHeader.ContentTransferEncoding = a.Encoding.ToString();
|
|
smtp.WriteHeader (aHeader);
|
|
|
|
// perform the actual writing of the file.
|
|
// read from the file stream and write to the tcp stream
|
|
FileStream ins = fileInfo.OpenRead ();
|
|
|
|
// create an apropriate encoder
|
|
IAttachmentEncoder encoder;
|
|
if (a.Encoding == MailEncoding.UUEncode)
|
|
encoder = new UUAttachmentEncoder (644, fileInfo.Name );
|
|
else
|
|
encoder = new Base64AttachmentEncoder ();
|
|
|
|
encoder.EncodeStream (ins, smtp.Stream);
|
|
|
|
ins.Close ();
|
|
smtp.WriteLine ("");
|
|
|
|
// if it is the last attachment write
|
|
// the final boundary otherwise write
|
|
// a normal one.
|
|
if (i < (msg.Attachments.Count - 1))
|
|
smtp.WriteBoundary (boundary);
|
|
else
|
|
smtp.WriteFinalBoundary (boundary);
|
|
}
|
|
}
|
|
|
|
// send quit command and
|
|
// closes the connection
|
|
public void Close()
|
|
{
|
|
smtp.WriteQuit();
|
|
tcpConnection.Close();
|
|
}
|
|
}
|
|
}
|