2 // System.Net.FtpWebRequest.cs
5 // Carlos Alberto Cortez (calberto.cortez@gmail.com)
7 // (c) Copyright 2006 Novell, Inc. (http://www.novell.com)
11 #if MONO_SECURITY_ALIAS
12 extern alias MonoSecurity;
13 using MSI = MonoSecurity::Mono.Security.Interface;
15 using MSI = Mono.Security.Interface;
21 using System.Net.Sockets;
23 using System.Threading;
24 using System.Net.Cache;
25 using System.Security.Cryptography.X509Certificates;
27 using System.Net.Security;
28 using System.Security.Authentication;
29 using Mono.Net.Security;
33 public sealed class FtpWebRequest : WebRequest
36 string file_name; // By now, used for upload
37 ServicePoint servicePoint;
38 Stream origDataStream;
41 StreamReader controlReader;
42 NetworkCredential credentials;
43 IPHostEntry hostEntry;
44 IPEndPoint localEndPoint;
47 int rwTimeout = 300000;
50 bool enableSsl = false;
51 bool usePassive = true;
52 bool keepAlive = false;
53 string method = WebRequestMethods.Ftp.DownloadFile;
55 object locker = new object ();
57 RequestState requestState = RequestState.Before;
58 FtpAsyncResult asyncResult;
59 FtpWebResponse ftpResponse;
63 const string ChangeDir = "CWD";
64 const string UserCommand = "USER";
65 const string PasswordCommand = "PASS";
66 const string TypeCommand = "TYPE";
67 const string PassiveCommand = "PASV";
68 const string PortCommand = "PORT";
69 const string AbortCommand = "ABOR";
70 const string AuthCommand = "AUTH";
71 const string RestCommand = "REST";
72 const string RenameFromCommand = "RNFR";
73 const string RenameToCommand = "RNTO";
74 const string QuitCommand = "QUIT";
75 const string EOL = "\r\n"; // Special end of line
91 static readonly string [] supportedCommands = new string [] {
92 WebRequestMethods.Ftp.AppendFile, // APPE
93 WebRequestMethods.Ftp.DeleteFile, // DELE
94 WebRequestMethods.Ftp.ListDirectoryDetails, // LIST
95 WebRequestMethods.Ftp.GetDateTimestamp, // MDTM
96 WebRequestMethods.Ftp.MakeDirectory, // MKD
97 WebRequestMethods.Ftp.ListDirectory, // NLST
98 WebRequestMethods.Ftp.PrintWorkingDirectory, // PWD
99 WebRequestMethods.Ftp.Rename, // RENAME
100 WebRequestMethods.Ftp.DownloadFile, // RETR
101 WebRequestMethods.Ftp.RemoveDirectory, // RMD
102 WebRequestMethods.Ftp.GetFileSize, // SIZE
103 WebRequestMethods.Ftp.UploadFile, // STOR
104 WebRequestMethods.Ftp.UploadFileWithUniqueName // STUR
107 Encoding dataEncoding = Encoding.UTF8;
109 internal FtpWebRequest (Uri uri)
111 this.requestUri = uri;
112 this.proxy = GlobalProxySelection.Select;
115 static Exception GetMustImplement ()
117 return new NotImplementedException ();
121 public X509CertificateCollection ClientCertificates
124 throw GetMustImplement ();
127 throw GetMustImplement ();
132 public override string ConnectionGroupName
135 throw GetMustImplement ();
138 throw GetMustImplement ();
142 public override string ContentType {
144 throw new NotSupportedException ();
147 throw new NotSupportedException ();
151 public override long ContentLength {
160 public long ContentOffset {
165 CheckRequestStarted ();
167 throw new ArgumentOutOfRangeException ();
173 public override ICredentials Credentials {
178 CheckRequestStarted ();
180 throw new ArgumentNullException ();
181 if (!(value is NetworkCredential))
182 throw new ArgumentException ();
184 credentials = value as NetworkCredential;
190 public static new RequestCachePolicy DefaultCachePolicy
193 throw GetMustImplement ();
196 throw GetMustImplement ();
201 public bool EnableSsl {
206 CheckRequestStarted ();
212 public override WebHeaderCollection Headers
215 throw GetMustImplement ();
218 throw GetMustImplement ();
222 [MonoTODO ("We don't support KeepAlive = true")]
223 public bool KeepAlive {
228 CheckRequestStarted ();
233 public override string Method {
238 CheckRequestStarted ();
240 throw new ArgumentNullException ("Method string cannot be null");
242 if (value.Length == 0 || Array.BinarySearch (supportedCommands, value) < 0)
243 throw new ArgumentException ("Method not supported", "value");
249 public override bool PreAuthenticate {
251 throw new NotSupportedException ();
254 throw new NotSupportedException ();
258 public override IWebProxy Proxy {
263 CheckRequestStarted ();
268 public int ReadWriteTimeout {
273 CheckRequestStarted ();
276 throw new ArgumentOutOfRangeException ();
282 public string RenameTo {
287 CheckRequestStarted ();
288 if (value == null || value.Length == 0)
289 throw new ArgumentException ("RenameTo value can't be null or empty", "RenameTo");
295 public override Uri RequestUri {
301 public ServicePoint ServicePoint {
303 return GetServicePoint ();
307 public bool UsePassive {
312 CheckRequestStarted ();
318 public override bool UseDefaultCredentials
321 throw GetMustImplement ();
324 throw GetMustImplement ();
328 public bool UseBinary {
332 CheckRequestStarted ();
337 public override int Timeout {
342 CheckRequestStarted ();
345 throw new ArgumentOutOfRangeException ();
353 return binary ? "I" : "A";
368 requestState = value;
373 public override void Abort () {
375 if (State == RequestState.TransferInProgress) {
376 /*FtpStatus status = */
377 SendCommand (false, AbortCommand);
380 if (!InFinalState ()) {
381 State = RequestState.Aborted;
382 ftpResponse = new FtpWebResponse (this, requestUri, method, FtpStatusCode.FileActionAborted, "Aborted by request");
387 public override IAsyncResult BeginGetResponse (AsyncCallback callback, object state) {
388 if (asyncResult != null && !asyncResult.IsCompleted) {
389 throw new InvalidOperationException ("Cannot re-call BeginGetRequestStream/BeginGetResponse while a previous call is still in progress");
394 asyncResult = new FtpAsyncResult (callback, state);
398 asyncResult.SetCompleted (true, ftpResponse);
400 if (State == RequestState.Before)
401 State = RequestState.Scheduled;
403 Thread thread = new Thread (ProcessRequest);
411 public override WebResponse EndGetResponse (IAsyncResult asyncResult) {
412 if (asyncResult == null)
413 throw new ArgumentNullException ("AsyncResult cannot be null!");
415 if (!(asyncResult is FtpAsyncResult) || asyncResult != this.asyncResult)
416 throw new ArgumentException ("AsyncResult is from another request!");
418 FtpAsyncResult asyncFtpResult = (FtpAsyncResult) asyncResult;
419 if (!asyncFtpResult.WaitUntilComplete (timeout, false)) {
421 throw new WebException ("Transfer timed out.", WebExceptionStatus.Timeout);
428 if (asyncFtpResult.GotException)
429 throw asyncFtpResult.Exception;
431 return asyncFtpResult.Response;
434 public override WebResponse GetResponse () {
435 IAsyncResult asyncResult = BeginGetResponse (null, null);
436 return EndGetResponse (asyncResult);
439 public override IAsyncResult BeginGetRequestStream (AsyncCallback callback, object state) {
440 if (method != WebRequestMethods.Ftp.UploadFile && method != WebRequestMethods.Ftp.UploadFileWithUniqueName &&
441 method != WebRequestMethods.Ftp.AppendFile)
442 throw new ProtocolViolationException ();
447 if (State != RequestState.Before)
448 throw new InvalidOperationException ("Cannot re-call BeginGetRequestStream/BeginGetResponse while a previous call is still in progress");
450 State = RequestState.Scheduled;
453 asyncResult = new FtpAsyncResult (callback, state);
454 Thread thread = new Thread (ProcessRequest);
460 public override Stream EndGetRequestStream (IAsyncResult asyncResult) {
461 if (asyncResult == null)
462 throw new ArgumentNullException ("asyncResult");
464 if (!(asyncResult is FtpAsyncResult))
465 throw new ArgumentException ("asyncResult");
467 if (State == RequestState.Aborted) {
468 throw new WebException ("Request aborted", WebExceptionStatus.RequestCanceled);
471 if (asyncResult != this.asyncResult)
472 throw new ArgumentException ("AsyncResult is from another request!");
474 FtpAsyncResult res = (FtpAsyncResult) asyncResult;
476 if (!res.WaitUntilComplete (timeout, false)) {
478 throw new WebException ("Request timed out");
481 if (res.GotException)
487 public override Stream GetRequestStream () {
488 IAsyncResult asyncResult = BeginGetRequestStream (null, null);
489 return EndGetRequestStream (asyncResult);
492 ServicePoint GetServicePoint ()
494 if (servicePoint == null)
495 servicePoint = ServicePointManager.FindServicePoint (requestUri, proxy);
500 // Probably move some code of command connection here
504 hostEntry = GetServicePoint ().HostEntry;
506 if (hostEntry == null) {
507 ftpResponse.UpdateStatus (new FtpStatus(FtpStatusCode.ActionAbortedLocalProcessingError, "Cannot resolve server name"));
508 throw new WebException ("The remote server name could not be resolved: " + requestUri,
509 null, WebExceptionStatus.NameResolutionFailure, ftpResponse);
513 void ProcessRequest () {
515 if (State == RequestState.Scheduled) {
516 ftpResponse = new FtpWebResponse (this, requestUri, method, keepAlive);
520 //State = RequestState.Finished;
521 //finalResponse = ftpResponse;
522 asyncResult.SetCompleted (false, ftpResponse);
524 catch (Exception e) {
525 if (!GetServicePoint ().UsesProxy)
526 State = RequestState.Error;
527 SetCompleteWithError (e);
532 FtpStatus status = GetResponseStatus ();
534 ftpResponse.UpdateStatus (status);
536 if (ftpResponse.IsFinal ()) {
537 State = RequestState.Finished;
541 asyncResult.SetCompleted (false, ftpResponse);
548 FtpStatus status = SendCommand (TypeCommand, DataType);
549 if ((int) status.StatusCode < 200 || (int) status.StatusCode >= 300)
550 throw CreateExceptionFromResponse (status);
554 string GetRemoteFolderPath (Uri uri)
557 string local_path = Uri.UnescapeDataString (uri.LocalPath);
558 if (initial_path == null || initial_path == "/") {
561 if (local_path [0] == '/')
562 local_path = local_path.Substring (1);
564 UriBuilder initialBuilder = new UriBuilder () {
569 Uri initial = initialBuilder.Uri;
570 result = new Uri (initial, local_path).LocalPath;
573 int last = result.LastIndexOf ('/');
577 return result.Substring (0, last + 1);
580 void CWDAndSetFileName (Uri uri)
582 string remote_folder = GetRemoteFolderPath (uri);
584 if (remote_folder != null) {
585 status = SendCommand (ChangeDir, remote_folder);
586 if ((int) status.StatusCode < 200 || (int) status.StatusCode >= 300)
587 throw CreateExceptionFromResponse (status);
589 int last = uri.LocalPath.LastIndexOf ('/');
591 file_name = Uri.UnescapeDataString (uri.LocalPath.Substring (last + 1));
596 void ProcessMethod ()
598 ServicePoint sp = GetServicePoint ();
600 if (method != WebRequestMethods.Ftp.DownloadFile)
601 throw new NotSupportedException ("FTP+proxy only supports RETR");
603 HttpWebRequest req = (HttpWebRequest) WebRequest.Create (proxy.GetProxy (requestUri));
604 req.Address = requestUri;
605 requestState = RequestState.Finished;
606 WebResponse response = req.GetResponse ();
607 ftpResponse.Stream = new FtpDataStream (this, response.GetResponseStream (), true);
608 ftpResponse.StatusCode = FtpStatusCode.CommandOK;
611 State = RequestState.Connecting;
615 OpenControlConnection ();
616 CWDAndSetFileName (requestUri);
620 // Open data connection and receive data
621 case WebRequestMethods.Ftp.DownloadFile:
622 case WebRequestMethods.Ftp.ListDirectory:
623 case WebRequestMethods.Ftp.ListDirectoryDetails:
626 // Open data connection and send data
627 case WebRequestMethods.Ftp.AppendFile:
628 case WebRequestMethods.Ftp.UploadFile:
629 case WebRequestMethods.Ftp.UploadFileWithUniqueName:
632 // Get info from control connection
633 case WebRequestMethods.Ftp.GetFileSize:
634 case WebRequestMethods.Ftp.GetDateTimestamp:
635 case WebRequestMethods.Ftp.PrintWorkingDirectory:
636 case WebRequestMethods.Ftp.MakeDirectory:
637 case WebRequestMethods.Ftp.Rename:
638 case WebRequestMethods.Ftp.DeleteFile:
639 ProcessSimpleMethod ();
641 default: // What to do here?
642 throw new Exception (String.Format ("Support for command {0} not implemented yet", method));
648 private void CloseControlConnection () {
649 if (controlStream != null) {
650 SendCommand (QuitCommand);
651 controlStream.Close ();
652 controlStream = null;
656 internal void CloseDataConnection () {
657 if(origDataStream != null) {
658 origDataStream.Close ();
659 origDataStream = null;
663 private void CloseConnection () {
664 CloseControlConnection ();
665 CloseDataConnection ();
668 void ProcessSimpleMethod ()
670 State = RequestState.TransferInProgress;
674 if (method == WebRequestMethods.Ftp.PrintWorkingDirectory)
677 if (method == WebRequestMethods.Ftp.Rename)
678 method = RenameFromCommand;
680 status = SendCommand (method, file_name);
682 ftpResponse.Stream = Stream.Null;
684 string desc = status.StatusDescription;
687 case WebRequestMethods.Ftp.GetFileSize: {
688 if (status.StatusCode != FtpStatusCode.FileStatus)
689 throw CreateExceptionFromResponse (status);
693 for (i = 4, len = 0; i < desc.Length && Char.IsDigit (desc [i]); i++, len++)
697 throw new WebException ("Bad format for server response in " + method);
699 if (!Int64.TryParse (desc.Substring (4, len), out size))
700 throw new WebException ("Bad format for server response in " + method);
702 ftpResponse.contentLength = size;
705 case WebRequestMethods.Ftp.GetDateTimestamp:
706 if (status.StatusCode != FtpStatusCode.FileStatus)
707 throw CreateExceptionFromResponse (status);
708 ftpResponse.LastModified = DateTime.ParseExact (desc.Substring (4), "yyyyMMddHHmmss", null);
710 case WebRequestMethods.Ftp.MakeDirectory:
711 if (status.StatusCode != FtpStatusCode.PathnameCreated)
712 throw CreateExceptionFromResponse (status);
715 method = WebRequestMethods.Ftp.PrintWorkingDirectory;
717 if (status.StatusCode != FtpStatusCode.FileActionOK)
718 throw CreateExceptionFromResponse (status);
720 status = SendCommand (method);
722 if (status.StatusCode != FtpStatusCode.PathnameCreated)
723 throw CreateExceptionFromResponse (status);
725 case RenameFromCommand:
726 method = WebRequestMethods.Ftp.Rename;
727 if (status.StatusCode != FtpStatusCode.FileCommandPending)
728 throw CreateExceptionFromResponse (status);
729 // Pass an empty string if RenameTo wasn't specified
730 status = SendCommand (RenameToCommand, renameTo != null ? renameTo : String.Empty);
731 if (status.StatusCode != FtpStatusCode.FileActionOK)
732 throw CreateExceptionFromResponse (status);
734 case WebRequestMethods.Ftp.DeleteFile:
735 if (status.StatusCode != FtpStatusCode.FileActionOK) {
736 throw CreateExceptionFromResponse (status);
741 State = RequestState.Finished;
746 State = RequestState.OpeningData;
748 OpenDataConnection ();
750 State = RequestState.TransferInProgress;
751 requestStream = new FtpDataStream (this, dataStream, false);
752 asyncResult.Stream = requestStream;
757 State = RequestState.OpeningData;
759 OpenDataConnection ();
761 State = RequestState.TransferInProgress;
762 ftpResponse.Stream = new FtpDataStream (this, dataStream, true);
765 void CheckRequestStarted ()
767 if (State != RequestState.Before)
768 throw new InvalidOperationException ("There is a request currently in progress");
771 void OpenControlConnection ()
773 Exception exception = null;
775 foreach (IPAddress address in hostEntry.AddressList) {
776 sock = new Socket (address.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
778 IPEndPoint remote = new IPEndPoint (address, requestUri.Port);
780 if (!ServicePoint.CallEndPointDelegate (sock, remote)) {
785 sock.Connect (remote);
786 localEndPoint = (IPEndPoint) sock.LocalEndPoint;
788 } catch (SocketException exc) {
796 // Couldn't connect to any address
798 throw new WebException ("Unable to connect to remote server", exception,
799 WebExceptionStatus.UnknownError, ftpResponse);
801 controlStream = new NetworkStream (sock);
802 controlReader = new StreamReader (controlStream, Encoding.ASCII);
804 State = RequestState.Authenticating;
807 FtpStatus status = SendCommand ("OPTS", "utf8", "on");
808 if ((int)status.StatusCode < 200 || (int)status.StatusCode > 300)
809 dataEncoding = Encoding.Default;
811 dataEncoding = Encoding.UTF8;
813 status = SendCommand (WebRequestMethods.Ftp.PrintWorkingDirectory);
814 initial_path = GetInitialPath (status);
817 static string GetInitialPath (FtpStatus status)
819 int s = (int) status.StatusCode;
820 if (s < 200 || s > 300 || status.StatusDescription.Length <= 4)
821 throw new WebException ("Error getting current directory: " + status.StatusDescription, null,
822 WebExceptionStatus.UnknownError, null);
824 string msg = status.StatusDescription.Substring (4);
825 if (msg [0] == '"') {
826 int next_quote = msg.IndexOf ('\"', 1);
827 if (next_quote == -1)
828 throw new WebException ("Error getting current directory: PWD -> " + status.StatusDescription, null,
829 WebExceptionStatus.UnknownError, null);
831 msg = msg.Substring (1, next_quote - 1);
834 if (!msg.EndsWith ("/"))
839 // Probably we could do better having here a regex
840 Socket SetupPassiveConnection (string statusDescription)
842 // Current response string
843 string response = statusDescription;
844 if (response.Length < 4)
845 throw new WebException ("Cannot open passive data connection");
847 // Look for first digit after code
849 for (i = 3; i < response.Length && !Char.IsDigit (response [i]); i++)
851 if (i >= response.Length)
852 throw new WebException ("Cannot open passive data connection");
855 string [] digits = response.Substring (i).Split (new char [] {','}, 6);
856 if (digits.Length != 6)
857 throw new WebException ("Cannot open passive data connection");
859 // Clean non-digits at the end of last element
861 for (j = digits [5].Length - 1; j >= 0 && !Char.IsDigit (digits [5][j]); j--)
864 throw new WebException ("Cannot open passive data connection");
866 digits [5] = digits [5].Substring (0, j + 1);
870 ip = IPAddress.Parse (String.Join (".", digits, 0, 4));
871 } catch (FormatException) {
872 throw new WebException ("Cannot open passive data connection");
877 if (!Int32.TryParse (digits [4], out p1) || !Int32.TryParse (digits [5], out p2))
878 throw new WebException ("Cannot open passive data connection");
880 port = (p1 << 8) + p2; // p1 * 256 + p2
881 //port = p1 * 256 + p2;
882 if (port < IPEndPoint.MinPort || port > IPEndPoint.MaxPort)
883 throw new WebException ("Cannot open passive data connection");
885 IPEndPoint ep = new IPEndPoint (ip, port);
886 Socket sock = new Socket (ep.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
889 } catch (SocketException) {
891 throw new WebException ("Cannot open passive data connection");
897 Exception CreateExceptionFromResponse (FtpStatus status)
899 FtpWebResponse ftpResponse = new FtpWebResponse (this, requestUri, method, status);
901 WebException exc = new WebException ("Server returned an error: " + status.StatusDescription,
902 null, WebExceptionStatus.ProtocolError, ftpResponse);
906 // Here we could also get a server error, so be cautious
907 internal void SetTransferCompleted ()
912 State = RequestState.Finished;
913 FtpStatus status = GetResponseStatus ();
914 ftpResponse.UpdateStatus (status);
919 internal void OperationCompleted ()
925 void SetCompleteWithError (Exception exc)
927 if (asyncResult != null) {
928 asyncResult.SetCompleted (false, exc);
932 Socket InitDataConnection ()
937 status = SendCommand (PassiveCommand);
938 if (status.StatusCode != FtpStatusCode.EnteringPassive) {
939 throw CreateExceptionFromResponse (status);
942 return SetupPassiveConnection (status.StatusDescription);
945 // Open a socket to listen the server's connection
946 Socket sock = new Socket (AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
948 sock.Bind (new IPEndPoint (localEndPoint.Address, 0));
949 sock.Listen (1); // We only expect a connection from server
951 } catch (SocketException e) {
954 throw new WebException ("Couldn't open listening socket on client", e);
957 IPEndPoint ep = (IPEndPoint) sock.LocalEndPoint;
958 string ipString = ep.Address.ToString ().Replace ('.', ',');
959 int h1 = ep.Port >> 8; // ep.Port / 256
960 int h2 = ep.Port % 256;
962 string portParam = ipString + "," + h1 + "," + h2;
963 status = SendCommand (PortCommand, portParam);
965 if (status.StatusCode != FtpStatusCode.CommandOK) {
967 throw (CreateExceptionFromResponse (status));
973 void OpenDataConnection ()
977 Socket s = InitDataConnection ();
979 // Handle content offset
981 status = SendCommand (RestCommand, offset.ToString ());
982 if (status.StatusCode != FtpStatusCode.FileCommandPending)
983 throw CreateExceptionFromResponse (status);
986 if (method != WebRequestMethods.Ftp.ListDirectory && method != WebRequestMethods.Ftp.ListDirectoryDetails &&
987 method != WebRequestMethods.Ftp.UploadFileWithUniqueName) {
988 status = SendCommand (method, file_name);
990 status = SendCommand (method);
993 if (status.StatusCode != FtpStatusCode.OpeningData && status.StatusCode != FtpStatusCode.DataAlreadyOpen)
994 throw CreateExceptionFromResponse (status);
997 origDataStream = new NetworkStream (s, true);
998 dataStream = origDataStream;
1000 ChangeToSSLSocket (ref dataStream);
1004 // Active connection (use Socket.Blocking to true)
1005 Socket incoming = null;
1007 incoming = s.Accept ();
1009 catch (SocketException) {
1011 if (incoming != null)
1014 throw new ProtocolViolationException ("Server commited a protocol violation.");
1018 origDataStream = new NetworkStream (incoming, true);
1019 dataStream = origDataStream;
1021 ChangeToSSLSocket (ref dataStream);
1024 ftpResponse.UpdateStatus (status);
1027 void Authenticate ()
1029 string username = null;
1030 string password = null;
1031 string domain = null;
1033 if (credentials != null) {
1034 username = credentials.UserName;
1035 password = credentials.Password;
1036 domain = credentials.Domain;
1039 if (username == null)
1040 username = "anonymous";
1041 if (password == null)
1042 password = "@anonymous";
1043 if (!string.IsNullOrEmpty (domain))
1044 username = domain + '\\' + username;
1046 // Connect to server and get banner message
1047 FtpStatus status = GetResponseStatus ();
1048 ftpResponse.BannerMessage = status.StatusDescription;
1051 InitiateSecureConnection (ref controlStream);
1052 controlReader = new StreamReader (controlStream, Encoding.ASCII);
1053 status = SendCommand ("PBSZ", "0");
1054 int st = (int) status.StatusCode;
1055 if (st < 200 || st >= 300)
1056 throw CreateExceptionFromResponse (status);
1057 // TODO: what if "PROT P" is denied by the server? What does MS do?
1058 status = SendCommand ("PROT", "P");
1059 st = (int) status.StatusCode;
1060 if (st < 200 || st >= 300)
1061 throw CreateExceptionFromResponse (status);
1063 status = new FtpStatus (FtpStatusCode.SendUserCommand, "");
1066 if (status.StatusCode != FtpStatusCode.SendUserCommand)
1067 throw CreateExceptionFromResponse (status);
1069 status = SendCommand (UserCommand, username);
1071 switch (status.StatusCode) {
1072 case FtpStatusCode.SendPasswordCommand:
1073 status = SendCommand (PasswordCommand, password);
1074 if (status.StatusCode != FtpStatusCode.LoggedInProceed)
1075 throw CreateExceptionFromResponse (status);
1077 case FtpStatusCode.LoggedInProceed:
1080 throw CreateExceptionFromResponse (status);
1083 ftpResponse.WelcomeMessage = status.StatusDescription;
1084 ftpResponse.UpdateStatus (status);
1087 FtpStatus SendCommand (string command, params string [] parameters) {
1088 return SendCommand (true, command, parameters);
1091 FtpStatus SendCommand (bool waitResponse, string command, params string [] parameters)
1094 string commandString = command;
1095 if (parameters.Length > 0)
1096 commandString += " " + String.Join (" ", parameters);
1098 commandString += EOL;
1099 cmd = dataEncoding.GetBytes (commandString);
1101 controlStream.Write (cmd, 0, cmd.Length);
1102 } catch (IOException) {
1103 //controlStream.Close ();
1104 return new FtpStatus(FtpStatusCode.ServiceNotAvailable, "Write failed");
1110 FtpStatus result = GetResponseStatus ();
1111 if (ftpResponse != null)
1112 ftpResponse.UpdateStatus (result);
1116 internal static FtpStatus ServiceNotAvailable ()
1118 return new FtpStatus (FtpStatusCode.ServiceNotAvailable, Locale.GetText ("Invalid response from server"));
1121 internal FtpStatus GetResponseStatus ()
1124 string response = null;
1127 response = controlReader.ReadLine ();
1128 } catch (IOException) {
1131 if (response == null || response.Length < 3)
1132 return ServiceNotAvailable ();
1135 if (!Int32.TryParse (response.Substring (0, 3), out code))
1136 return ServiceNotAvailable ();
1138 if (response.Length > 3 && response [3] == '-'){
1140 string find = code.ToString() + ' ';
1144 line = controlReader.ReadLine();
1145 } catch (IOException) {
1148 return ServiceNotAvailable ();
1150 response += Environment.NewLine + line;
1152 if (line.StartsWith(find, StringComparison.Ordinal))
1156 return new FtpStatus ((FtpStatusCode) code, response);
1160 private void InitiateSecureConnection (ref Stream stream) {
1161 FtpStatus status = SendCommand (AuthCommand, "TLS");
1162 if (status.StatusCode != FtpStatusCode.ServerWantsSecureSession)
1163 throw CreateExceptionFromResponse (status);
1165 ChangeToSSLSocket (ref stream);
1168 internal bool ChangeToSSLSocket (ref Stream stream) {
1170 var provider = MonoTlsProviderFactory.GetProviderInternal ();
1171 var settings = MSI.MonoTlsSettings.CopyDefaultSettings ();
1172 settings.UseServicePointManagerCallback = true;
1173 var sslStream = provider.CreateSslStream (stream, true, settings);
1174 sslStream.AuthenticateAsClient (requestUri.Host, null, SslProtocols.Default, false);
1175 stream = sslStream.AuthenticatedStream;
1178 throw new NotImplementedException ();
1182 bool InFinalState () {
1183 return (State == RequestState.Aborted || State == RequestState.Error || State == RequestState.Finished);
1186 bool InProgress () {
1187 return (State != RequestState.Before && !InFinalState ());
1190 internal void CheckIfAborted () {
1191 if (State == RequestState.Aborted)
1192 throw new WebException ("Request aborted", WebExceptionStatus.RequestCanceled);
1195 void CheckFinalState () {
1196 if (InFinalState ())
1197 throw new InvalidOperationException ("Cannot change final state");