Merge pull request #2171 from lambdageek/dev/fix-marshal
[mono.git] / mcs / class / System / System.Net / FtpWebRequest.cs
index bb765dd660c494f9c6f25fad8cfc33a129e2ae32..bb900979a48ff614732e30717f06f15925b1981f 100644 (file)
@@ -6,7 +6,15 @@
 //
 // (c) Copyright 2006 Novell, Inc. (http://www.novell.com)
 //
-#if NET_2_0
+
+#if SECURITY_DEP
+#if MONO_SECURITY_ALIAS
+extern alias MonoSecurity;
+using MSI = MonoSecurity::Mono.Security.Interface;
+#else
+using MSI = Mono.Security.Interface;
+#endif
+#endif
 
 using System;
 using System.IO;
@@ -16,7 +24,9 @@ using System.Threading;
 using System.Net.Cache;
 using System.Security.Cryptography.X509Certificates;
 using System.Net;
-
+using System.Net.Security;
+using System.Security.Authentication;
+using Mono.Net.Security;
 
 namespace System.Net
 {
@@ -25,8 +35,9 @@ namespace System.Net
                Uri requestUri;
                string file_name; // By now, used for upload
                ServicePoint servicePoint;
-               Socket dataSocket;
-               NetworkStream controlStream;
+               Stream origDataStream;
+               Stream dataStream;
+               Stream controlStream;
                StreamReader controlReader;
                NetworkCredential credentials;
                IPHostEntry hostEntry;
@@ -93,6 +104,8 @@ namespace System.Net
                        WebRequestMethods.Ftp.UploadFileWithUniqueName // STUR
                        };
 
+               Encoding dataEncoding = Encoding.UTF8;
+
                internal FtpWebRequest (Uri uri) 
                {
                        this.requestUri = uri;
@@ -172,6 +185,7 @@ namespace System.Net
                        }
                }
 
+#if !NET_2_1
                [MonoTODO]
                public static new RequestCachePolicy DefaultCachePolicy
                {
@@ -182,6 +196,7 @@ namespace System.Net
                                throw GetMustImplement ();
                        }
                }
+#endif
 
                public bool EnableSsl {
                        get {
@@ -246,9 +261,6 @@ namespace System.Net
                        }
                        set {
                                CheckRequestStarted ();
-                               if (value == null)
-                                       throw new ArgumentNullException ();
-
                                proxy = value;
                        }
                }
@@ -510,7 +522,8 @@ namespace System.Net
                                        asyncResult.SetCompleted (false, ftpResponse);
                                }
                                catch (Exception e) {
-                                       State = RequestState.Error;
+                                       if (!GetServicePoint ().UsesProxy)
+                                               State = RequestState.Error;
                                        SetCompleteWithError (e);
                                }
                        }
@@ -548,7 +561,12 @@ namespace System.Net
                                if (local_path [0] == '/')
                                        local_path = local_path.Substring (1);
 
-                               Uri initial = new Uri ("ftp://dummy-host" + initial_path);
+                               UriBuilder initialBuilder = new UriBuilder () {
+                                       Scheme  = "ftp",
+                                       Host    = "dummy-host",
+                                       Path    = initial_path,
+                               };
+                               Uri initial = initialBuilder.Uri;
                                result = new Uri (initial, local_path).LocalPath;
                        }
 
@@ -577,6 +595,19 @@ namespace System.Net
 
                void ProcessMethod ()
                {
+                       ServicePoint sp = GetServicePoint ();
+                       if (sp.UsesProxy) {
+                               if (method != WebRequestMethods.Ftp.DownloadFile)
+                                       throw new NotSupportedException ("FTP+proxy only supports RETR");
+
+                               HttpWebRequest req = (HttpWebRequest) WebRequest.Create (proxy.GetProxy (requestUri));
+                               req.Address = requestUri;
+                               requestState = RequestState.Finished;
+                               WebResponse response = req.GetResponse ();
+                               ftpResponse.Stream = new FtpDataStream (this, response.GetResponseStream (), true);
+                               ftpResponse.StatusCode = FtpStatusCode.CommandOK;
+                               return;
+                       }
                        State = RequestState.Connecting;
 
                        ResolveHost ();
@@ -615,13 +646,18 @@ namespace System.Net
                }
 
                private void CloseControlConnection () {
-                       SendCommand (QuitCommand);
-                       controlStream.Close ();
+                       if (controlStream != null) {
+                               SendCommand (QuitCommand);
+                               controlStream.Close ();
+                               controlStream = null;
+                       }
                }
 
-               private void CloseDataConnection () {
-                       if(dataSocket != null)
-                               dataSocket.Close ();
+               internal void CloseDataConnection () {
+                       if(origDataStream != null) {
+                               origDataStream.Close ();
+                               origDataStream = null;
+                       }
                }
 
                private void CloseConnection () {
@@ -712,7 +748,7 @@ namespace System.Net
                        OpenDataConnection ();
 
                        State = RequestState.TransferInProgress;
-                       requestStream = new FtpDataStream (this, dataSocket, false);
+                       requestStream = new FtpDataStream (this, dataStream, false);
                        asyncResult.Stream = requestStream;
                }
 
@@ -723,7 +759,7 @@ namespace System.Net
                        OpenDataConnection ();
 
                        State = RequestState.TransferInProgress;
-                       ftpResponse.Stream = new FtpDataStream (this, dataSocket, true);
+                       ftpResponse.Stream = new FtpDataStream (this, dataStream, true);
                }
 
                void CheckRequestStarted ()
@@ -769,7 +805,11 @@ namespace System.Net
 
                        Authenticate ();
                        FtpStatus status = SendCommand ("OPTS", "utf8", "on");
-                       // ignore status for OPTS
+                       if ((int)status.StatusCode < 200 || (int)status.StatusCode > 300)
+                               dataEncoding = Encoding.Default;
+                       else
+                               dataEncoding = Encoding.UTF8;
+
                        status = SendCommand (WebRequestMethods.Ftp.PrintWorkingDirectory);
                        initial_path = GetInitialPath (status);
                }
@@ -954,7 +994,10 @@ namespace System.Net
                                throw CreateExceptionFromResponse (status);
 
                        if (usePassive) {
-                               dataSocket = s;
+                               origDataStream = new NetworkStream (s, true);
+                               dataStream = origDataStream;
+                               if (EnableSsl)
+                                       ChangeToSSLSocket (ref dataStream);
                        }
                        else {
 
@@ -972,12 +1015,10 @@ namespace System.Net
                                }
 
                                s.Close ();
-                               dataSocket = incoming;
-                       }
-
-                       if (EnableSsl) {
-                               InitiateSecureConnection (ref controlStream);
-                               controlReader = new StreamReader (controlStream, Encoding.ASCII);
+                               origDataStream = new NetworkStream (incoming, true);
+                               dataStream = origDataStream;
+                               if (EnableSsl)
+                                       ChangeToSSLSocket (ref dataStream);
                        }
 
                        ftpResponse.UpdateStatus (status);
@@ -1009,6 +1050,17 @@ namespace System.Net
                        if (EnableSsl) {
                                InitiateSecureConnection (ref controlStream);
                                controlReader = new StreamReader (controlStream, Encoding.ASCII);
+                               status = SendCommand ("PBSZ", "0");
+                               int st = (int) status.StatusCode;
+                               if (st < 200 || st >= 300)
+                                       throw CreateExceptionFromResponse (status);
+                               // TODO: what if "PROT P" is denied by the server? What does MS do?
+                               status = SendCommand ("PROT", "P");
+                               st = (int) status.StatusCode;
+                               if (st < 200 || st >= 300)
+                                       throw CreateExceptionFromResponse (status);
+
+                               status = new FtpStatus (FtpStatusCode.SendUserCommand, "");
                        }
                        
                        if (status.StatusCode != FtpStatusCode.SendUserCommand)
@@ -1044,7 +1096,7 @@ namespace System.Net
                                commandString += " " + String.Join (" ", parameters);
 
                        commandString += EOL;
-                       cmd = Encoding.ASCII.GetBytes (commandString);
+                       cmd = dataEncoding.GetBytes (commandString);
                        try {
                                controlStream.Write (cmd, 0, cmd.Length);
                        } catch (IOException) {
@@ -1087,6 +1139,7 @@ namespace System.Net
                                        string line = null;
                                        string find = code.ToString() + ' ';
                                        while (true){
+                                               line = null;
                                                try {
                                                        line = controlReader.ReadLine();
                                                } catch (IOException) {
@@ -1104,19 +1157,22 @@ namespace System.Net
                        }
                }
 
-               private void InitiateSecureConnection (ref NetworkStream stream) {
+               private void InitiateSecureConnection (ref Stream stream) {
                        FtpStatus status = SendCommand (AuthCommand, "TLS");
-
-                       if (status.StatusCode != FtpStatusCode.ServerWantsSecureSession) {
+                       if (status.StatusCode != FtpStatusCode.ServerWantsSecureSession)
                                throw CreateExceptionFromResponse (status);
-                       }
 
                        ChangeToSSLSocket (ref stream);
                }
 
-               internal static bool ChangeToSSLSocket (ref NetworkStream stream) {
-#if TARGET_JVM
-                       stream.ChangeToSSLSocket ();
+               internal bool ChangeToSSLSocket (ref Stream stream) {
+#if SECURITY_DEP
+                       var provider = MonoTlsProviderFactory.GetProviderInternal ();
+                       var settings = new MSI.MonoTlsSettings ();
+                       settings.UseServicePointManagerCallback = true;
+                       var sslStream = provider.CreateSslStream (stream, true, settings);
+                       sslStream.AuthenticateAsClient (requestUri.Host, null, SslProtocols.Default, false);
+                       stream = sslStream.AuthenticatedStream;
                        return true;
 #else
                        throw new NotImplementedException ();
@@ -1143,5 +1199,4 @@ namespace System.Net
        }
 }
 
-#endif