SmtpClient.cs, SmtpStream.cs: Added SMTP over TLS support, refactoring
authorVladimir Krasnov <krasnov@mono-cvs.ximian.com>
Wed, 26 Apr 2006 08:40:11 +0000 (08:40 -0000)
committerVladimir Krasnov <krasnov@mono-cvs.ximian.com>
Wed, 26 Apr 2006 08:40:11 +0000 (08:40 -0000)
svn path=/trunk/mcs/; revision=59914

mcs/class/System.Web/System.Web.Mail/ChangeLog
mcs/class/System.Web/System.Web.Mail/SmtpClient.cs
mcs/class/System.Web/System.Web.Mail/SmtpStream.cs

index 8dcfeecea654e79280b85af259812ecd62118acc..1261cb51ca5a033e9af679ccfaa10534f74f42b5 100644 (file)
@@ -1,3 +1,8 @@
+2006-04-26  Vladimir Krasnov  <vladimirk@mainsoft.com>
+
+       * SmtpClient.cs, SmtpStream.cs: Added SMTP over TLS support, 
+       refactoring
+
 2006-03-23  Vladimir Krasnov  <vladimirk@mainsoft.com>
 
        * SmtpMail.cs: removed TARGET_JVM part from Send method.
index 97a8109200cb793e0e44b976676790d2ae24c2c7..63ce37ae373bd7d290fac753da816be8f2239045 100644 (file)
@@ -34,6 +34,7 @@ using System.Text;
 using System.Collections;
 using System.Net.Sockets;
 using System.Security.Permissions;
+using System.Reflection;
 
 namespace System.Web.Mail {
 
@@ -43,8 +44,13 @@ namespace System.Web.Mail {
        private string server;
        private TcpClient tcpConnection;
        private SmtpStream smtp;
-       
-       //Initialise the variables and connect
+       private string username;
+       private string password;
+       private int port = 25;
+       private bool usessl = false;
+       private short authenticate = 1;         
+        
+    //Initialise the variables and connect
        public SmtpClient( string server ) {
            
            this.server = server;
@@ -54,18 +60,96 @@ namespace System.Web.Mail {
        // make the actual connection
        // and HELO handshaking
        private void Connect() {
-           tcpConnection = new TcpClient( server , 25 );
+           tcpConnection = new TcpClient( server , port );
            
-           Stream stream = tcpConnection.GetStream();
+           NetworkStream stream = tcpConnection.GetStream();
            smtp = new SmtpStream( stream );
-           
-           // read the server greeting
-           smtp.ReadResponse();
-           smtp.CheckForStatusCode( 220 );
-          
-           // write the HELO command to the server
-           smtp.WriteHelo( Dns.GetHostName() );
+       }
                    
+       private 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
+       }
+               
+       private 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);
+       }
+
+       private void StartSend(MailMessageWrapper msg)
+       {
+               ReadFields(msg);
+               
+               // 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 ) {
@@ -78,7 +162,7 @@ namespace System.Web.Mail {
                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();
@@ -166,8 +250,8 @@ namespace System.Web.Mail {
         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");\r
-        if (msg.Fields.Data ["http://schemas.microsoft.com/cdo/configuration/sendpassword"] != null)\r
+            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);
 #endif
index 3228bbb56aaec53c88b93499eba07e3f4cb12cda..3c3d4bfbe30001c8a60f50047a7161eb45ebbd71 100644 (file)
@@ -62,6 +62,31 @@ namespace System.Web.Mail {
        
        }
        
+       public void WriteAuthLogin()
+       {
+               command = "AUTH LOGIN";
+               WriteLine( command );
+               ReadResponse();         
+       }
+
+       public bool WriteStartTLS( ) 
+       { 
+               command = "STARTTLS";
+               WriteLine( command );
+               ReadResponse();
+               return LastResponse.StatusCode == 220;
+
+       }
+
+       public void WriteEhlo( string hostName ) 
+       { 
+               command = "EHLO " + hostName;
+               WriteLine( command );
+               ReadResponse();
+               CheckForStatusCode( 250 );
+       
+       }
+       
        public void WriteHelo( string hostName ) { 
            command = "HELO " + hostName;
            WriteLine( command );
@@ -173,22 +198,39 @@ namespace System.Web.Mail {
        
        // read a line from the server
        public void ReadResponse( ) {
-           string line = null;
-           
-           byte[] buffer = new byte[ 4096 ];
-           
-           int readLength = stream.Read( buffer , 0 , buffer.Length );
-           
-           if( readLength > 0 ) { 
-           
-               line = encoding.GetString( buffer , 0 , readLength );
                
-               line = line.TrimEnd( new Char[] { '\r' , '\n' , ' ' } );
+               byte[] buffer = new byte [512];
+               int position = 0;
+               bool lastLine = false;
+
+               do {
+                       int readLength = stream.Read (buffer , position , buffer.Length - position);
+                       if (readLength > 0) { 
+                               int available = position + readLength - 1;
+                               if (available > 4 && (buffer [available] == '\n' || buffer [available] == '\r'))
+                                       for (int index = available - 3; ; index--) {
+                                               if (index < 0 || buffer [index] == '\n' || buffer [index] == '\r') {
+                                                       lastLine = buffer [index + 4] == ' ';
+                                                       break;
+                                               }
+                                       }
+
+                               // move position
+                               position += readLength;
+
+                               // check if buffer is full
+                               if (position == buffer.Length) {
+                                       byte [] newBuffer = new byte [buffer.Length * 2];
+                                       Array.Copy (buffer, 0, newBuffer, 0, buffer.Length);
+                                       buffer = newBuffer;
+                               }
+                       }
+               } while(!lastLine);
+
+               string line = encoding.GetString (buffer , 0 , position - 1);
                        
-           }
-          
            // parse the line to the lastResponse object
-           lastResponse = SmtpResponse.Parse( line );
+           lastResponse = SmtpResponse.Parse (line);
           
            #if DEBUG
              DebugPrint( line );