//
// (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;
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
{
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;
WebRequestMethods.Ftp.UploadFileWithUniqueName // STUR
};
+ Encoding dataEncoding = Encoding.UTF8;
+
internal FtpWebRequest (Uri uri)
{
this.requestUri = uri;
}
}
+#if !NET_2_1
[MonoTODO]
public static new RequestCachePolicy DefaultCachePolicy
{
throw GetMustImplement ();
}
}
+#endif
public bool EnableSsl {
get {
}
}
+ [MonoTODO ("We don't support KeepAlive = true")]
public bool KeepAlive {
get {
return keepAlive;
}
set {
CheckRequestStarted ();
- keepAlive = value;
+ //keepAlive = value;
}
}
}
set {
CheckRequestStarted ();
- if (value == null)
- throw new ArgumentNullException ();
-
proxy = value;
}
}
if (!InFinalState ()) {
State = RequestState.Aborted;
- ftpResponse = new FtpWebResponse (requestUri, method, FtpStatusCode.FileActionAborted, "Aborted by request");
+ ftpResponse = new FtpWebResponse (this, requestUri, method, FtpStatusCode.FileActionAborted, "Aborted by request");
}
}
}
void ProcessRequest () {
if (State == RequestState.Scheduled) {
- ftpResponse = new FtpWebResponse (requestUri, method, keepAlive);
+ ftpResponse = new FtpWebResponse (this, requestUri, method, keepAlive);
try {
ProcessMethod ();
asyncResult.SetCompleted (false, ftpResponse);
}
catch (Exception e) {
- State = RequestState.Error;
+ if (!GetServicePoint ().UsesProxy)
+ State = RequestState.Error;
SetCompleteWithError (e);
}
}
{
string result;
string local_path = Uri.UnescapeDataString (uri.LocalPath);
- if (initial_path == null) {
+ if (initial_path == null || initial_path == "/") {
result = local_path;
} else {
if (local_path [0] == '/')
local_path = local_path.Substring (1);
- Uri initial = new Uri (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;
}
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 ();
}
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 () {
FtpStatus status;
if (method == WebRequestMethods.Ftp.PrintWorkingDirectory)
- method = ChangeDir;
+ method = "PWD";
if (method == WebRequestMethods.Ftp.Rename)
method = RenameFromCommand;
status = SendCommand (method, file_name);
- ftpResponse.Stream = new EmptyStream ();
+ ftpResponse.Stream = Stream.Null;
string desc = status.StatusDescription;
OpenDataConnection ();
State = RequestState.TransferInProgress;
- requestStream = new FtpDataStream (this, dataSocket, false);
+ requestStream = new FtpDataStream (this, dataStream, false);
asyncResult.Stream = requestStream;
}
{
State = RequestState.OpeningData;
- // Handle content offset
- if (offset > 0) {
- FtpStatus status = SendCommand (RestCommand, offset.ToString ());
-
- if (status.StatusCode != FtpStatusCode.FileCommandPending)
- throw CreateExceptionFromResponse (status);
- }
-
OpenDataConnection ();
State = RequestState.TransferInProgress;
- ftpResponse.Stream = new FtpDataStream (this, dataSocket, true);
+ ftpResponse.Stream = new FtpDataStream (this, dataStream, true);
}
void CheckRequestStarted ()
void OpenControlConnection ()
{
+ Exception exception = null;
Socket sock = null;
foreach (IPAddress address in hostEntry.AddressList) {
sock = new Socket (address.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
sock.Connect (remote);
localEndPoint = (IPEndPoint) sock.LocalEndPoint;
break;
- } catch (SocketException) {
+ } catch (SocketException exc) {
+ exception = exc;
sock.Close ();
sock = null;
}
// Couldn't connect to any address
if (sock == null)
- throw new WebException ("Unable to connect to remote server", null,
+ throw new WebException ("Unable to connect to remote server", exception,
WebExceptionStatus.UnknownError, ftpResponse);
controlStream = new NetworkStream (sock);
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);
}
Exception CreateExceptionFromResponse (FtpStatus status)
{
- FtpWebResponse ftpResponse = new FtpWebResponse (requestUri, method, status);
+ FtpWebResponse ftpResponse = new FtpWebResponse (this, requestUri, method, status);
WebException exc = new WebException ("Server returned an error: " + status.StatusDescription,
null, WebExceptionStatus.ProtocolError, ftpResponse);
CloseConnection ();
}
+ internal void OperationCompleted ()
+ {
+ if(!keepAlive)
+ CloseConnection ();
+ }
+
void SetCompleteWithError (Exception exc)
{
if (asyncResult != null) {
Socket s = InitDataConnection ();
+ // Handle content offset
+ if (offset > 0) {
+ status = SendCommand (RestCommand, offset.ToString ());
+ if (status.StatusCode != FtpStatusCode.FileCommandPending)
+ throw CreateExceptionFromResponse (status);
+ }
+
if (method != WebRequestMethods.Ftp.ListDirectory && method != WebRequestMethods.Ftp.ListDirectoryDetails &&
method != WebRequestMethods.Ftp.UploadFileWithUniqueName) {
status = SendCommand (method, file_name);
throw CreateExceptionFromResponse (status);
if (usePassive) {
- dataSocket = s;
+ origDataStream = new NetworkStream (s, true);
+ dataStream = origDataStream;
+ if (EnableSsl)
+ ChangeToSSLSocket (ref dataStream);
}
else {
}
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);
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)
commandString += " " + String.Join (" ", parameters);
commandString += EOL;
- cmd = Encoding.ASCII.GetBytes (commandString);
+ cmd = dataEncoding.GetBytes (commandString);
try {
controlStream.Write (cmd, 0, cmd.Length);
} catch (IOException) {
if (!Int32.TryParse (response.Substring (0, 3), out code))
return ServiceNotAvailable ();
- if (response [3] == '-'){
+ if (response.Length > 3 && response [3] == '-'){
string line = null;
string find = code.ToString() + ' ';
while (true){
+ line = null;
try {
line = controlReader.ReadLine();
} catch (IOException) {
}
}
- 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 ();
if (InFinalState ())
throw new InvalidOperationException ("Cannot change final state");
}
-
- class EmptyStream : MemoryStream
- {
- internal EmptyStream ()
- : base (new byte [0], false) {
- }
- }
}
}
-#endif