/// <summary> /// <para>Creates an array of commands, that will be sent to the server</para> /// </summary> protected override PipelineEntry[] BuildCommandsList(WebRequest req) { bool resetLoggedInState = false; FtpWebRequest request = (FtpWebRequest)req; if (NetEventSource.IsEnabled) { NetEventSource.Info(this); } _responseUri = request.RequestUri; ArrayList commandList = new ArrayList(); if (request.EnableSsl && !UsingSecureStream) { commandList.Add(new PipelineEntry(FormatFtpCommand("AUTH", "TLS"))); // According to RFC we need to re-authorize with USER/PASS after we re-authenticate. resetLoggedInState = true; } if (resetLoggedInState) { _loginDirectory = null; _establishedServerDirectory = null; _requestedServerDirectory = null; _currentTypeSetting = string.Empty; if (_loginState == FtpLoginState.LoggedIn) { _loginState = FtpLoginState.LoggedInButNeedsRelogin; } } if (_loginState != FtpLoginState.LoggedIn) { Credentials = request.Credentials.GetCredential(request.RequestUri, "basic"); _welcomeMessage = new StringBuilder(); _exitMessage = new StringBuilder(); string domainUserName = string.Empty; string password = string.Empty; if (Credentials != null) { domainUserName = Credentials.UserName; string domain = Credentials.Domain; if (!string.IsNullOrEmpty(domain)) { domainUserName = domain + "\\" + domainUserName; } password = Credentials.Password; } if (domainUserName.Length == 0 && password.Length == 0) { domainUserName = "******"; password = "******"; } commandList.Add(new PipelineEntry(FormatFtpCommand("USER", domainUserName))); commandList.Add(new PipelineEntry(FormatFtpCommand("PASS", password), PipelineEntryFlags.DontLogParameter)); // If SSL, always configure data channel encryption after authentication to maximum RFC compatibility. The RFC allows for // PBSZ/PROT commands to come either before or after the USER/PASS, but some servers require USER/PASS immediately after // the AUTH TLS command. if (request.EnableSsl && !UsingSecureStream) { commandList.Add(new PipelineEntry(FormatFtpCommand("PBSZ", "0"))); commandList.Add(new PipelineEntry(FormatFtpCommand("PROT", "P"))); } commandList.Add(new PipelineEntry(FormatFtpCommand("OPTS", "utf8 on"))); commandList.Add(new PipelineEntry(FormatFtpCommand("PWD", null))); } GetPathOption getPathOption = GetPathOption.Normal; if (request.MethodInfo.HasFlag(FtpMethodFlags.DoesNotTakeParameter)) { getPathOption = GetPathOption.AssumeNoFilename; } else if (request.MethodInfo.HasFlag(FtpMethodFlags.ParameterIsDirectory)) { getPathOption = GetPathOption.AssumeFilename; } string requestPath; string requestDirectory; string requestFilename; GetPathInfo(getPathOption, request.RequestUri, out requestPath, out requestDirectory, out requestFilename); if (requestFilename.Length == 0 && request.MethodInfo.HasFlag(FtpMethodFlags.TakesParameter)) { throw new WebException(SR.net_ftp_invalid_uri); } // We optimize for having the current working directory staying at the login directory. This ensure that // our relative paths work right and reduces unnecessary CWD commands. // Usually, we don't change the working directory except for some FTP commands. If necessary, // we need to reset our working directory back to the login directory. if (_establishedServerDirectory != null && _loginDirectory != null && _establishedServerDirectory != _loginDirectory) { commandList.Add(new PipelineEntry(FormatFtpCommand("CWD", _loginDirectory), PipelineEntryFlags.UserCommand)); _requestedServerDirectory = _loginDirectory; } // For most commands, we don't need to navigate to the directory since we pass in the full // path as part of the FTP protocol command. However, some commands require it. if (request.MethodInfo.HasFlag(FtpMethodFlags.MustChangeWorkingDirectoryToPath) && requestDirectory.Length > 0) { commandList.Add(new PipelineEntry(FormatFtpCommand("CWD", requestDirectory), PipelineEntryFlags.UserCommand)); _requestedServerDirectory = requestDirectory; } if (!request.MethodInfo.IsCommandOnly) { string requestedTypeSetting = request.UseBinary ? "I" : "A"; if (_currentTypeSetting != requestedTypeSetting) { commandList.Add(new PipelineEntry(FormatFtpCommand("TYPE", requestedTypeSetting))); _currentTypeSetting = requestedTypeSetting; } if (request.UsePassive) { string passiveCommand = (ServerAddress.AddressFamily == AddressFamily.InterNetwork) ? "PASV" : "EPSV"; commandList.Add(new PipelineEntry(FormatFtpCommand(passiveCommand, null), PipelineEntryFlags.CreateDataConnection)); } else { string portCommand = (ServerAddress.AddressFamily == AddressFamily.InterNetwork) ? "PORT" : "EPRT"; CreateFtpListenerSocket(request); commandList.Add(new PipelineEntry(FormatFtpCommand(portCommand, GetPortCommandLine(request)))); } if (request.ContentOffset > 0) { // REST command must always be the last sent before the main file command is sent. commandList.Add(new PipelineEntry(FormatFtpCommand("REST", request.ContentOffset.ToString(CultureInfo.InvariantCulture)))); } } PipelineEntryFlags flags = PipelineEntryFlags.UserCommand; if (!request.MethodInfo.IsCommandOnly) { flags |= PipelineEntryFlags.GiveDataStream; if (!request.UsePassive) { flags |= PipelineEntryFlags.CreateDataConnection; } } if (request.MethodInfo.Operation == FtpOperation.Rename) { string baseDir = (requestDirectory == string.Empty) ? string.Empty : requestDirectory + "/"; commandList.Add(new PipelineEntry(FormatFtpCommand("RNFR", baseDir + requestFilename), flags)); string renameTo; if (!string.IsNullOrEmpty(request.RenameTo) && request.RenameTo.StartsWith("/", StringComparison.OrdinalIgnoreCase)) { renameTo = request.RenameTo; // Absolute path } else { renameTo = baseDir + request.RenameTo; // Relative path } commandList.Add(new PipelineEntry(FormatFtpCommand("RNTO", renameTo), flags)); } else if (request.MethodInfo.HasFlag(FtpMethodFlags.DoesNotTakeParameter)) { commandList.Add(new PipelineEntry(FormatFtpCommand(request.Method, string.Empty), flags)); } else if (request.MethodInfo.HasFlag(FtpMethodFlags.MustChangeWorkingDirectoryToPath)) { commandList.Add(new PipelineEntry(FormatFtpCommand(request.Method, requestFilename), flags)); } else { commandList.Add(new PipelineEntry(FormatFtpCommand(request.Method, requestPath), flags)); } commandList.Add(new PipelineEntry(FormatFtpCommand("QUIT", null))); return((PipelineEntry[])commandList.ToArray(typeof(PipelineEntry))); }
/// <summary> /// <para>Creates an array of commands, that will be sent to the server</para> /// </summary> protected override PipelineEntry[] BuildCommandsList(WebRequest req) { bool resetLoggedInState = false; FtpWebRequest request = (FtpWebRequest)req; if (NetEventSource.IsEnabled) NetEventSource.Info(this); _responseUri = request.RequestUri; ArrayList commandList = new ArrayList(); if (request.EnableSsl && !UsingSecureStream) { commandList.Add(new PipelineEntry(FormatFtpCommand("AUTH", "TLS"))); // According to RFC we need to re-authorize with USER/PASS after we re-authenticate. resetLoggedInState = true; } if (resetLoggedInState) { _loginDirectory = null; _establishedServerDirectory = null; _requestedServerDirectory = null; _currentTypeSetting = string.Empty; if (_loginState == FtpLoginState.LoggedIn) _loginState = FtpLoginState.LoggedInButNeedsRelogin; } if (_loginState != FtpLoginState.LoggedIn) { Credentials = request.Credentials.GetCredential(request.RequestUri, "basic"); _welcomeMessage = new StringBuilder(); _exitMessage = new StringBuilder(); string domainUserName = string.Empty; string password = string.Empty; if (Credentials != null) { domainUserName = Credentials.UserName; string domain = Credentials.Domain; if (!string.IsNullOrEmpty(domain)) { domainUserName = domain + "\\" + domainUserName; } password = Credentials.Password; } if (domainUserName.Length == 0 && password.Length == 0) { domainUserName = "******"; password = "******"; } commandList.Add(new PipelineEntry(FormatFtpCommand("USER", domainUserName))); commandList.Add(new PipelineEntry(FormatFtpCommand("PASS", password), PipelineEntryFlags.DontLogParameter)); // If SSL, always configure data channel encryption after authentication to maximum RFC compatibility. The RFC allows for // PBSZ/PROT commands to come either before or after the USER/PASS, but some servers require USER/PASS immediately after // the AUTH TLS command. if (request.EnableSsl && !UsingSecureStream) { commandList.Add(new PipelineEntry(FormatFtpCommand("PBSZ", "0"))); commandList.Add(new PipelineEntry(FormatFtpCommand("PROT", "P"))); } commandList.Add(new PipelineEntry(FormatFtpCommand("OPTS", "utf8 on"))); commandList.Add(new PipelineEntry(FormatFtpCommand("PWD", null))); } GetPathOption getPathOption = GetPathOption.Normal; if (request.MethodInfo.HasFlag(FtpMethodFlags.DoesNotTakeParameter)) { getPathOption = GetPathOption.AssumeNoFilename; } else if (request.MethodInfo.HasFlag(FtpMethodFlags.ParameterIsDirectory)) { getPathOption = GetPathOption.AssumeFilename; } string requestPath; string requestDirectory; string requestFilename; GetPathInfo(getPathOption, request.RequestUri, out requestPath, out requestDirectory, out requestFilename); if (requestFilename.Length == 0 && request.MethodInfo.HasFlag(FtpMethodFlags.TakesParameter)) throw new WebException(SR.net_ftp_invalid_uri); // We optimize for having the current working directory staying at the login directory. This ensure that // our relative paths work right and reduces unnecessary CWD commands. // Usually, we don't change the working directory except for some FTP commands. If necessary, // we need to reset our working directory back to the login directory. if (_establishedServerDirectory != null && _loginDirectory != null && _establishedServerDirectory != _loginDirectory) { commandList.Add(new PipelineEntry(FormatFtpCommand("CWD", _loginDirectory), PipelineEntryFlags.UserCommand)); _requestedServerDirectory = _loginDirectory; } // For most commands, we don't need to navigate to the directory since we pass in the full // path as part of the FTP protocol command. However, some commands require it. if (request.MethodInfo.HasFlag(FtpMethodFlags.MustChangeWorkingDirectoryToPath) && requestDirectory.Length > 0) { commandList.Add(new PipelineEntry(FormatFtpCommand("CWD", requestDirectory), PipelineEntryFlags.UserCommand)); _requestedServerDirectory = requestDirectory; } if (!request.MethodInfo.IsCommandOnly) { string requestedTypeSetting = request.UseBinary ? "I" : "A"; if (_currentTypeSetting != requestedTypeSetting) { commandList.Add(new PipelineEntry(FormatFtpCommand("TYPE", requestedTypeSetting))); _currentTypeSetting = requestedTypeSetting; } if (request.UsePassive) { string passiveCommand = (ServerAddress.AddressFamily == AddressFamily.InterNetwork) ? "PASV" : "EPSV"; commandList.Add(new PipelineEntry(FormatFtpCommand(passiveCommand, null), PipelineEntryFlags.CreateDataConnection)); } else { string portCommand = (ServerAddress.AddressFamily == AddressFamily.InterNetwork) ? "PORT" : "EPRT"; CreateFtpListenerSocket(request); commandList.Add(new PipelineEntry(FormatFtpCommand(portCommand, GetPortCommandLine(request)))); } if (request.ContentOffset > 0) { // REST command must always be the last sent before the main file command is sent. commandList.Add(new PipelineEntry(FormatFtpCommand("REST", request.ContentOffset.ToString(CultureInfo.InvariantCulture)))); } } PipelineEntryFlags flags = PipelineEntryFlags.UserCommand; if (!request.MethodInfo.IsCommandOnly) { flags |= PipelineEntryFlags.GiveDataStream; if (!request.UsePassive) flags |= PipelineEntryFlags.CreateDataConnection; } if (request.MethodInfo.Operation == FtpOperation.Rename) { string baseDir = (requestDirectory == string.Empty) ? string.Empty : requestDirectory + "/"; commandList.Add(new PipelineEntry(FormatFtpCommand("RNFR", baseDir + requestFilename), flags)); string renameTo; if (!string.IsNullOrEmpty(request.RenameTo) && request.RenameTo.StartsWith("/", StringComparison.OrdinalIgnoreCase)) { renameTo = request.RenameTo; // Absolute path } else { renameTo = baseDir + request.RenameTo; // Relative path } commandList.Add(new PipelineEntry(FormatFtpCommand("RNTO", renameTo), flags)); } else if (request.MethodInfo.HasFlag(FtpMethodFlags.DoesNotTakeParameter)) { commandList.Add(new PipelineEntry(FormatFtpCommand(request.Method, string.Empty), flags)); } else if (request.MethodInfo.HasFlag(FtpMethodFlags.MustChangeWorkingDirectoryToPath)) { commandList.Add(new PipelineEntry(FormatFtpCommand(request.Method, requestFilename), flags)); } else { commandList.Add(new PipelineEntry(FormatFtpCommand(request.Method, requestPath), flags)); } commandList.Add(new PipelineEntry(FormatFtpCommand("QUIT", null))); return (PipelineEntry[])commandList.ToArray(typeof(PipelineEntry)); }
// This is called by underlying base class code, each time a new response is received from the wire or a protocol stage is resumed. // This function controls the setting up of a data socket/connection, and of saving off the server responses. protected override PipelineInstruction PipelineCallback(PipelineEntry entry, ResponseDescription response, bool timeout, ref Stream stream) { if (NetEventSource.IsEnabled) { NetEventSource.Info(this, $"Command:{entry?.Command} Description:{response?.StatusDescription}"); } // null response is not expected if (response == null) { return(PipelineInstruction.Abort); } FtpStatusCode status = (FtpStatusCode)response.Status; // // Update global "current status" for FtpWebRequest // if (status != FtpStatusCode.ClosingControl) { // A 221 status won't be reflected on the user FTP response // Anything else will (by design?) StatusCode = status; StatusLine = response.StatusDescription; } // If the status code is outside the range defined in RFC (1xx to 5xx) throw if (response.InvalidStatusCode) { throw new WebException(SR.net_InvalidStatusCode, WebExceptionStatus.ProtocolError); } // Update the banner message if any, this is a little hack because the "entry" param is null if (_index == -1) { if (status == FtpStatusCode.SendUserCommand) { _bannerMessage = new StringBuilder(); _bannerMessage.Append(StatusLine); return(PipelineInstruction.Advance); } else if (status == FtpStatusCode.ServiceTemporarilyNotAvailable) { return(PipelineInstruction.Reread); } else { throw GenerateException(status, response.StatusDescription, null); } } // // Check for the result of our attempt to use UTF8 // if (entry.Command == "OPTS utf8 on\r\n") { if (response.PositiveCompletion) { Encoding = Encoding.UTF8; } else { Encoding = Encoding.Default; } return(PipelineInstruction.Advance); } // If we are already logged in and the server returns 530 then // the server does not support re-issuing a USER command, // tear down the connection and start all over again if (entry.Command.IndexOf("USER") != -1) { // The server may not require a password for this user, so bypass the password command if (status == FtpStatusCode.LoggedInProceed) { _loginState = FtpLoginState.LoggedIn; _index++; } } // // Throw on an error with possible recovery option // if (response.TransientFailure || response.PermanentFailure) { if (status == FtpStatusCode.ServiceNotAvailable) { MarkAsRecoverableFailure(); } throw GenerateException(status, response.StatusDescription, null); } if (_loginState != FtpLoginState.LoggedIn && entry.Command.IndexOf("PASS") != -1) { // Note the fact that we logged in if (status == FtpStatusCode.NeedLoginAccount || status == FtpStatusCode.LoggedInProceed) { _loginState = FtpLoginState.LoggedIn; } else { throw GenerateException(status, response.StatusDescription, null); } } // // Parse special cases // if (entry.HasFlag(PipelineEntryFlags.CreateDataConnection) && (response.PositiveCompletion || response.PositiveIntermediate)) { bool isSocketReady; PipelineInstruction result = QueueOrCreateDataConection(entry, response, timeout, ref stream, out isSocketReady); if (!isSocketReady) { return(result); } // otherwise we have a stream to create } // // This is part of the above case and it's all about giving data stream back // if (status == FtpStatusCode.OpeningData || status == FtpStatusCode.DataAlreadyOpen) { if (_dataSocket == null) { return(PipelineInstruction.Abort); } if (!entry.HasFlag(PipelineEntryFlags.GiveDataStream)) { _abortReason = SR.Format(SR.net_ftp_invalid_status_response, status, entry.Command); return(PipelineInstruction.Abort); } // Parse out the Content length, if we can TryUpdateContentLength(response.StatusDescription); // Parse out the file name, when it is returned and use it for our ResponseUri FtpWebRequest request = (FtpWebRequest)_request; if (request.MethodInfo.ShouldParseForResponseUri) { TryUpdateResponseUri(response.StatusDescription, request); } return(QueueOrCreateFtpDataStream(ref stream)); } // // Parse responses by status code exclusivelly // // Update welcome message if (status == FtpStatusCode.LoggedInProceed) { _welcomeMessage.Append(StatusLine); } // OR set the user response ExitMessage else if (status == FtpStatusCode.ClosingControl) { _exitMessage.Append(response.StatusDescription); // And close the control stream socket on "QUIT" CloseSocket(); } // OR set us up for SSL/TLS, after this we'll be writing securely else if (status == FtpStatusCode.ServerWantsSecureSession) { // If NetworkStream is a TlsStream, then this must be in the async callback // from completing the SSL handshake. // So just let the pipeline continue. if (!(NetworkStream is TlsStream)) { FtpWebRequest request = (FtpWebRequest)_request; TlsStream tlsStream = new TlsStream(NetworkStream, Socket, request.RequestUri.Host, request.ClientCertificates); if (_isAsync) { tlsStream.BeginAuthenticateAsClient(ar => { try { tlsStream.EndAuthenticateAsClient(ar); NetworkStream = tlsStream; this.ContinueCommandPipeline(); } catch (Exception e) { this.CloseSocket(); this.InvokeRequestCallback(e); } }, null); return(PipelineInstruction.Pause); } else { tlsStream.AuthenticateAsClient(); NetworkStream = tlsStream; } } } // OR parse out the file size or file time, usually a result of sending SIZE/MDTM commands else if (status == FtpStatusCode.FileStatus) { FtpWebRequest request = (FtpWebRequest)_request; if (entry.Command.StartsWith("SIZE ")) { _contentLength = GetContentLengthFrom213Response(response.StatusDescription); } else if (entry.Command.StartsWith("MDTM ")) { _lastModified = GetLastModifiedFrom213Response(response.StatusDescription); } } // OR parse out our login directory else if (status == FtpStatusCode.PathnameCreated) { if (entry.Command == "PWD\r\n" && !entry.HasFlag(PipelineEntryFlags.UserCommand)) { _loginDirectory = GetLoginDirectory(response.StatusDescription); } } // Asserting we have some positive response else { // We only use CWD to reset ourselves back to the login directory. if (entry.Command.IndexOf("CWD") != -1) { _establishedServerDirectory = _requestedServerDirectory; } } // Intermediate responses require rereading if (response.PositiveIntermediate || (!UsingSecureStream && entry.Command == "AUTH TLS\r\n")) { return(PipelineInstruction.Reread); } return(PipelineInstruction.Advance); }
protected override CommandStream.PipelineEntry[] BuildCommandsList(WebRequest req) { string str3; string str4; string str5; bool flag = false; FtpWebRequest request = (FtpWebRequest)req; this.m_ResponseUri = request.RequestUri; ArrayList list = new ArrayList(); if (request.EnableSsl && !base.UsingSecureStream) { list.Add(new CommandStream.PipelineEntry(this.FormatFtpCommand("AUTH", "TLS"))); flag = true; } if (flag) { this.m_LoginDirectory = null; this.m_EstablishedServerDirectory = null; this.m_RequestedServerDirectory = null; this.m_CurrentTypeSetting = string.Empty; if (this.m_LoginState == FtpLoginState.LoggedIn) { this.m_LoginState = FtpLoginState.LoggedInButNeedsRelogin; } } if (this.m_LoginState != FtpLoginState.LoggedIn) { this.Credentials = request.Credentials.GetCredential(request.RequestUri, "basic"); this.m_WelcomeMessage = new StringBuilder(); this.m_ExitMessage = new StringBuilder(); string parameter = string.Empty; string password = string.Empty; if (this.Credentials != null) { parameter = this.Credentials.InternalGetDomainUserName(); password = this.Credentials.InternalGetPassword(); } if ((parameter.Length == 0) && (password.Length == 0)) { parameter = "anonymous"; password = "******"; } list.Add(new CommandStream.PipelineEntry(this.FormatFtpCommand("USER", parameter))); list.Add(new CommandStream.PipelineEntry(this.FormatFtpCommand("PASS", password), CommandStream.PipelineEntryFlags.DontLogParameter)); if (request.EnableSsl && !base.UsingSecureStream) { list.Add(new CommandStream.PipelineEntry(this.FormatFtpCommand("PBSZ", "0"))); list.Add(new CommandStream.PipelineEntry(this.FormatFtpCommand("PROT", "P"))); } list.Add(new CommandStream.PipelineEntry(this.FormatFtpCommand("OPTS", "utf8 on"))); list.Add(new CommandStream.PipelineEntry(this.FormatFtpCommand("PWD", null))); } GetPathOption normal = GetPathOption.Normal; if (request.MethodInfo.HasFlag(FtpMethodFlags.DoesNotTakeParameter)) { normal = GetPathOption.AssumeNoFilename; } else if (request.MethodInfo.HasFlag(FtpMethodFlags.ParameterIsDirectory)) { normal = GetPathOption.AssumeFilename; } GetPathInfo(normal, request.RequestUri, out str3, out str4, out str5); if ((str5.Length == 0) && request.MethodInfo.HasFlag(FtpMethodFlags.TakesParameter)) { throw new WebException(SR.GetString("net_ftp_invalid_uri")); } if (((this.m_EstablishedServerDirectory != null) && (this.m_LoginDirectory != null)) && (this.m_EstablishedServerDirectory != this.m_LoginDirectory)) { list.Add(new CommandStream.PipelineEntry(this.FormatFtpCommand("CWD", this.m_LoginDirectory), CommandStream.PipelineEntryFlags.UserCommand)); this.m_RequestedServerDirectory = this.m_LoginDirectory; } if (request.MethodInfo.HasFlag(FtpMethodFlags.MustChangeWorkingDirectoryToPath) && (str4.Length > 0)) { list.Add(new CommandStream.PipelineEntry(this.FormatFtpCommand("CWD", str4), CommandStream.PipelineEntryFlags.UserCommand)); this.m_RequestedServerDirectory = str4; } if (((request.CacheProtocol != null) && (request.CacheProtocol.ProtocolStatus == CacheValidationStatus.DoNotTakeFromCache)) && (request.MethodInfo.Operation == FtpOperation.DownloadFile)) { list.Add(new CommandStream.PipelineEntry(this.FormatFtpCommand("MDTM", str3))); } if (!request.MethodInfo.IsCommandOnly) { if ((request.CacheProtocol == null) || (request.CacheProtocol.ProtocolStatus != CacheValidationStatus.Continue)) { string str6 = request.UseBinary ? "I" : "A"; if (this.m_CurrentTypeSetting != str6) { list.Add(new CommandStream.PipelineEntry(this.FormatFtpCommand("TYPE", str6))); this.m_CurrentTypeSetting = str6; } if (request.UsePassive) { string command = (base.ServerAddress.AddressFamily == AddressFamily.InterNetwork) ? "PASV" : "EPSV"; list.Add(new CommandStream.PipelineEntry(this.FormatFtpCommand(command, null), CommandStream.PipelineEntryFlags.CreateDataConnection)); } else { string str8 = (base.ServerAddress.AddressFamily == AddressFamily.InterNetwork) ? "PORT" : "EPRT"; this.CreateFtpListenerSocket(request); list.Add(new CommandStream.PipelineEntry(this.FormatFtpCommand(str8, this.GetPortCommandLine(request)))); } if ((request.CacheProtocol != null) && (request.CacheProtocol.ProtocolStatus == CacheValidationStatus.CombineCachedAndServerResponse)) { if (request.CacheProtocol.Validator.CacheEntry.StreamSize > 0L) { list.Add(new CommandStream.PipelineEntry(this.FormatFtpCommand("REST", request.CacheProtocol.Validator.CacheEntry.StreamSize.ToString(CultureInfo.InvariantCulture)))); } } else if (request.ContentOffset > 0L) { list.Add(new CommandStream.PipelineEntry(this.FormatFtpCommand("REST", request.ContentOffset.ToString(CultureInfo.InvariantCulture)))); } } else { list.Add(new CommandStream.PipelineEntry(this.FormatFtpCommand("SIZE", str3))); list.Add(new CommandStream.PipelineEntry(this.FormatFtpCommand("MDTM", str3))); } } if ((request.CacheProtocol == null) || (request.CacheProtocol.ProtocolStatus != CacheValidationStatus.Continue)) { CommandStream.PipelineEntryFlags userCommand = CommandStream.PipelineEntryFlags.UserCommand; if (!request.MethodInfo.IsCommandOnly) { userCommand |= CommandStream.PipelineEntryFlags.GiveDataStream; if (!request.UsePassive) { userCommand |= CommandStream.PipelineEntryFlags.CreateDataConnection; } } if (request.MethodInfo.Operation == FtpOperation.Rename) { list.Add(new CommandStream.PipelineEntry(this.FormatFtpCommand("RNFR", str4 + "/" + str5), userCommand)); list.Add(new CommandStream.PipelineEntry(this.FormatFtpCommand("RNTO", str4 + "/" + request.RenameTo), userCommand)); } else if (request.MethodInfo.HasFlag(FtpMethodFlags.DoesNotTakeParameter)) { list.Add(new CommandStream.PipelineEntry(this.FormatFtpCommand(request.Method, string.Empty), userCommand)); } else if (request.MethodInfo.HasFlag(FtpMethodFlags.MustChangeWorkingDirectoryToPath)) { list.Add(new CommandStream.PipelineEntry(this.FormatFtpCommand(request.Method, str5), userCommand)); } else { list.Add(new CommandStream.PipelineEntry(this.FormatFtpCommand(request.Method, str3), userCommand)); } if (!request.KeepAlive) { list.Add(new CommandStream.PipelineEntry(this.FormatFtpCommand("QUIT", null))); } } return((CommandStream.PipelineEntry[])list.ToArray(typeof(CommandStream.PipelineEntry))); }
// This is called by underlying base class code, each time a new response is received from the wire or a protocol stage is resumed. // This function controls the setting up of a data socket/connection, and of saving off the server responses. protected override PipelineInstruction PipelineCallback(PipelineEntry entry, ResponseDescription response, bool timeout, ref Stream stream) { if (NetEventSource.IsEnabled) NetEventSource.Info(this, $"Command:{entry?.Command} Description:{response?.StatusDescription}"); // null response is not expected if (response == null) return PipelineInstruction.Abort; FtpStatusCode status = (FtpStatusCode)response.Status; // // Update global "current status" for FtpWebRequest // if (status != FtpStatusCode.ClosingControl) { // A 221 status won't be reflected on the user FTP response // Anything else will (by design?) StatusCode = status; StatusLine = response.StatusDescription; } // If the status code is outside the range defined in RFC (1xx to 5xx) throw if (response.InvalidStatusCode) throw new WebException(SR.net_InvalidStatusCode, WebExceptionStatus.ProtocolError); // Update the banner message if any, this is a little hack because the "entry" param is null if (_index == -1) { if (status == FtpStatusCode.SendUserCommand) { _bannerMessage = new StringBuilder(); _bannerMessage.Append(StatusLine); return PipelineInstruction.Advance; } else if (status == FtpStatusCode.ServiceTemporarilyNotAvailable) { return PipelineInstruction.Reread; } else throw GenerateException(status, response.StatusDescription, null); } // // Check for the result of our attempt to use UTF8 // if (entry.Command == "OPTS utf8 on\r\n") { if (response.PositiveCompletion) { Encoding = Encoding.UTF8; } else { Encoding = Encoding.Default; } return PipelineInstruction.Advance; } // If we are already logged in and the server returns 530 then // the server does not support re-issuing a USER command, // tear down the connection and start all over again if (entry.Command.IndexOf("USER") != -1) { // The server may not require a password for this user, so bypass the password command if (status == FtpStatusCode.LoggedInProceed) { _loginState = FtpLoginState.LoggedIn; _index++; } } // // Throw on an error with possible recovery option // if (response.TransientFailure || response.PermanentFailure) { if (status == FtpStatusCode.ServiceNotAvailable) { MarkAsRecoverableFailure(); } throw GenerateException(status, response.StatusDescription, null); } if (_loginState != FtpLoginState.LoggedIn && entry.Command.IndexOf("PASS") != -1) { // Note the fact that we logged in if (status == FtpStatusCode.NeedLoginAccount || status == FtpStatusCode.LoggedInProceed) _loginState = FtpLoginState.LoggedIn; else throw GenerateException(status, response.StatusDescription, null); } // // Parse special cases // if (entry.HasFlag(PipelineEntryFlags.CreateDataConnection) && (response.PositiveCompletion || response.PositiveIntermediate)) { bool isSocketReady; PipelineInstruction result = QueueOrCreateDataConection(entry, response, timeout, ref stream, out isSocketReady); if (!isSocketReady) return result; // otheriwse we have a stream to create } // // This is part of the above case and it's all about giving data stream back // if (status == FtpStatusCode.OpeningData || status == FtpStatusCode.DataAlreadyOpen) { if (_dataSocket == null) { return PipelineInstruction.Abort; } if (!entry.HasFlag(PipelineEntryFlags.GiveDataStream)) { _abortReason = SR.Format(SR.net_ftp_invalid_status_response, status, entry.Command); return PipelineInstruction.Abort; } // Parse out the Content length, if we can TryUpdateContentLength(response.StatusDescription); // Parse out the file name, when it is returned and use it for our ResponseUri FtpWebRequest request = (FtpWebRequest)_request; if (request.MethodInfo.ShouldParseForResponseUri) { TryUpdateResponseUri(response.StatusDescription, request); } return QueueOrCreateFtpDataStream(ref stream); } // // Parse responses by status code exclusivelly // // Update welcome message if (status == FtpStatusCode.LoggedInProceed) { _welcomeMessage.Append(StatusLine); } // OR set the user response ExitMessage else if (status == FtpStatusCode.ClosingControl) { _exitMessage.Append(response.StatusDescription); // And close the control stream socket on "QUIT" CloseSocket(); } // OR set us up for SSL/TLS, after this we'll be writing securely else if (status == FtpStatusCode.ServerWantsSecureSession) { // If NetworkStream is a TlsStream, then this must be in the async callback // from completing the SSL handshake. // So just let the pipeline continue. if (!(NetworkStream is TlsStream)) { FtpWebRequest request = (FtpWebRequest)_request; TlsStream tlsStream = new TlsStream(NetworkStream, Socket, request.RequestUri.Host, request.ClientCertificates); if (_isAsync) { tlsStream.BeginAuthenticateAsClient(ar => { try { tlsStream.EndAuthenticateAsClient(ar); NetworkStream = tlsStream; this.ContinueCommandPipeline(); } catch (Exception e) { this.CloseSocket(); this.InvokeRequestCallback(e); } }, null); return PipelineInstruction.Pause; } else { tlsStream.AuthenticateAsClient(); NetworkStream = tlsStream; } } } // OR parse out the file size or file time, usually a result of sending SIZE/MDTM commands else if (status == FtpStatusCode.FileStatus) { FtpWebRequest request = (FtpWebRequest)_request; if (entry.Command.StartsWith("SIZE ")) { _contentLength = GetContentLengthFrom213Response(response.StatusDescription); } else if (entry.Command.StartsWith("MDTM ")) { _lastModified = GetLastModifiedFrom213Response(response.StatusDescription); } } // OR parse out our login directory else if (status == FtpStatusCode.PathnameCreated) { if (entry.Command == "PWD\r\n" && !entry.HasFlag(PipelineEntryFlags.UserCommand)) { _loginDirectory = GetLoginDirectory(response.StatusDescription); } } // Asserting we have some positive response else { // We only use CWD to reset ourselves back to the login directory. if (entry.Command.IndexOf("CWD") != -1) { _establishedServerDirectory = _requestedServerDirectory; } } // Intermediate responses require rereading if (response.PositiveIntermediate || (!UsingSecureStream && entry.Command == "AUTH TLS\r\n")) { return PipelineInstruction.Reread; } return PipelineInstruction.Advance; }
/// <summary> /// <para>Creates an array of commands, that will be sent to the server</para> /// </summary> protected override PipelineEntry [] BuildCommandsList(WebRequest req) { bool resetLoggedInState = false; FtpWebRequest request = (FtpWebRequest) req; GlobalLog.Print("FtpControlStream#" + ValidationHelper.HashString(this) + "BuildCommandsList"); m_ResponseUri = request.RequestUri; ArrayList commandList = new ArrayList(); #if DEBUG // the Credentials.IsEqualTo method is only compiled in DEBUG so the assert must be restricted to DEBUG // as well // While some FTP servers support it, in general, the RFC's don't allow re-issuing the USER command to // change the authentication context of an existing logged in connection. We prevent re-using existing // connections if the credentials are different from the previous FtpWebRequest. Let's make sure that // our connection pooling code is working correctly. Debug.Assert(Credentials == null || Credentials.IsEqualTo(request.Credentials.GetCredential(request.RequestUri, "basic")), "Should not be re-using an existing connection with different credentials"); #endif if (request.EnableSsl && !UsingSecureStream) { commandList.Add(new PipelineEntry(FormatFtpCommand("AUTH", "TLS"))); // According to RFC we need to re-authorize with USER/PASS after we re-authenticate. resetLoggedInState = true; } if (resetLoggedInState) { m_LoginDirectory = null; m_EstablishedServerDirectory = null; m_RequestedServerDirectory = null; m_CurrentTypeSetting = string.Empty; if (m_LoginState == FtpLoginState.LoggedIn) m_LoginState = FtpLoginState.LoggedInButNeedsRelogin; } if (m_LoginState != FtpLoginState.LoggedIn) { Credentials = request.Credentials.GetCredential(request.RequestUri, "basic"); m_WelcomeMessage = new StringBuilder(); m_ExitMessage = new StringBuilder(); string domainUserName = string.Empty; string password = string.Empty; if (Credentials != null) { domainUserName = Credentials.InternalGetDomainUserName(); password = Credentials.InternalGetPassword(); } if (domainUserName.Length == 0 && password.Length == 0) { domainUserName = "******"; password = "******"; } commandList.Add(new PipelineEntry(FormatFtpCommand("USER", domainUserName))); commandList.Add(new PipelineEntry(FormatFtpCommand("PASS", password), PipelineEntryFlags.DontLogParameter)); // If SSL, always configure data channel encryption after authentication to maximum RFC compatibility. The RFC allows for // PBSZ/PROT commands to come either before or after the USER/PASS, but some servers require USER/PASS immediately after // the AUTH TLS command. if (request.EnableSsl && !UsingSecureStream) { commandList.Add(new PipelineEntry(FormatFtpCommand("PBSZ", "0"))); commandList.Add(new PipelineEntry(FormatFtpCommand("PROT", "P"))); } commandList.Add(new PipelineEntry(FormatFtpCommand("OPTS", "utf8 on"))); commandList.Add(new PipelineEntry(FormatFtpCommand("PWD", null))); } GetPathOption getPathOption = GetPathOption.Normal; if (request.MethodInfo.HasFlag(FtpMethodFlags.DoesNotTakeParameter)) { getPathOption = GetPathOption.AssumeNoFilename; } else if (request.MethodInfo.HasFlag(FtpMethodFlags.ParameterIsDirectory)) { getPathOption = GetPathOption.AssumeFilename; } string requestPath; string requestDirectory; string requestFilename; GetPathInfo(getPathOption, request.RequestUri, out requestPath, out requestDirectory, out requestFilename); if (requestFilename.Length == 0 && request.MethodInfo.HasFlag(FtpMethodFlags.TakesParameter)) throw new WebException(SR.GetString(SR.net_ftp_invalid_uri)); // We optimize for having the current working directory staying at the login directory. This ensure that // our relative paths work right and reduces unnecessary CWD commands. // Usually, we don't change the working directory except for some FTP commands. If necessary, // we need to reset our working directory back to the login directory. if (m_EstablishedServerDirectory != null && m_LoginDirectory != null && m_EstablishedServerDirectory != m_LoginDirectory) { commandList.Add(new PipelineEntry(FormatFtpCommand("CWD", m_LoginDirectory), PipelineEntryFlags.UserCommand)); m_RequestedServerDirectory = m_LoginDirectory; } // For most commands, we don't need to navigate to the directory since we pass in the full // path as part of the FTP protocol command. However, some commands require it. if (request.MethodInfo.HasFlag(FtpMethodFlags.MustChangeWorkingDirectoryToPath) && requestDirectory.Length > 0) { commandList.Add(new PipelineEntry(FormatFtpCommand("CWD", requestDirectory), PipelineEntryFlags.UserCommand)); m_RequestedServerDirectory = requestDirectory; } if (request.CacheProtocol != null && request.CacheProtocol.ProtocolStatus == CacheValidationStatus.DoNotTakeFromCache && request.MethodInfo.Operation == FtpOperation.DownloadFile) commandList.Add(new PipelineEntry(FormatFtpCommand("MDTM", requestPath))); if (!request.MethodInfo.IsCommandOnly) { // This is why having a protocol logic on the connection is a bad idea if (request.CacheProtocol == null || request.CacheProtocol.ProtocolStatus != CacheValidationStatus.Continue) { string requestedTypeSetting = request.UseBinary ? "I" : "A"; if (m_CurrentTypeSetting != requestedTypeSetting) { commandList.Add(new PipelineEntry(FormatFtpCommand("TYPE", requestedTypeSetting))); m_CurrentTypeSetting = requestedTypeSetting; } if (request.UsePassive) { string passiveCommand = (ServerAddress.AddressFamily == AddressFamily.InterNetwork) ? "PASV" : "EPSV"; commandList.Add(new PipelineEntry(FormatFtpCommand(passiveCommand, null), PipelineEntryFlags.CreateDataConnection)); } else { string portCommand = (ServerAddress.AddressFamily == AddressFamily.InterNetwork) ? "PORT" : "EPRT"; CreateFtpListenerSocket(request); commandList.Add(new PipelineEntry(FormatFtpCommand(portCommand, GetPortCommandLine(request)))); } if (request.CacheProtocol != null && request.CacheProtocol.ProtocolStatus == CacheValidationStatus.CombineCachedAndServerResponse) { // Combining partial cache with the reminder using "REST" if (request.CacheProtocol.Validator.CacheEntry.StreamSize > 0) commandList.Add(new PipelineEntry(FormatFtpCommand("REST", request.CacheProtocol.Validator.CacheEntry.StreamSize.ToString(CultureInfo.InvariantCulture)))); } else if (request.ContentOffset > 0) { // REST command must always be the last sent before the main file command is sent. commandList.Add(new PipelineEntry(FormatFtpCommand("REST", request.ContentOffset.ToString(CultureInfo.InvariantCulture)))); } } else { // revalidating GetFileSize = "SIZE" GetDateTimeStamp = "MDTM" commandList.Add(new PipelineEntry(FormatFtpCommand("SIZE", requestPath))); commandList.Add(new PipelineEntry(FormatFtpCommand("MDTM", requestPath))); } } // // Suppress the data file if this is a revalidation request // if (request.CacheProtocol == null || request.CacheProtocol.ProtocolStatus != CacheValidationStatus.Continue) { PipelineEntryFlags flags = PipelineEntryFlags.UserCommand; if (!request.MethodInfo.IsCommandOnly) { flags |= PipelineEntryFlags.GiveDataStream; if (!request.UsePassive) flags |= PipelineEntryFlags.CreateDataConnection; } if (request.MethodInfo.Operation == FtpOperation.Rename) { string baseDir = (requestDirectory == string.Empty) ? string.Empty : requestDirectory + "/"; commandList.Add(new PipelineEntry(FormatFtpCommand("RNFR", baseDir + requestFilename), flags)); string renameTo; if (!string.IsNullOrEmpty(request.RenameTo) && request.RenameTo.StartsWith("/", StringComparison.OrdinalIgnoreCase)) { renameTo = request.RenameTo; // Absolute path } else { renameTo = baseDir + request.RenameTo; // Relative path } commandList.Add(new PipelineEntry(FormatFtpCommand("RNTO", renameTo), flags)); } else if (request.MethodInfo.HasFlag(FtpMethodFlags.DoesNotTakeParameter)) { commandList.Add(new PipelineEntry(FormatFtpCommand(request.Method, string.Empty), flags)); } else if (request.MethodInfo.HasFlag(FtpMethodFlags.MustChangeWorkingDirectoryToPath)) { commandList.Add(new PipelineEntry(FormatFtpCommand(request.Method, requestFilename), flags)); } else { commandList.Add(new PipelineEntry(FormatFtpCommand(request.Method, requestPath), flags)); } if (!request.KeepAlive) { commandList.Add(new PipelineEntry(FormatFtpCommand("QUIT", null))); } } return (PipelineEntry []) commandList.ToArray(typeof(PipelineEntry)); }
protected override CommandStream.PipelineInstruction PipelineCallback(CommandStream.PipelineEntry entry, ResponseDescription response, bool timeout, ref Stream stream) { if (response == null) { return(CommandStream.PipelineInstruction.Abort); } FtpStatusCode status = (FtpStatusCode)response.Status; if (status != FtpStatusCode.ClosingControl) { this.StatusCode = status; this.StatusLine = response.StatusDescription; } if (response.InvalidStatusCode) { throw new WebException(SR.GetString("net_InvalidStatusCode"), WebExceptionStatus.ProtocolError); } if (base.m_Index == -1) { switch (status) { case FtpStatusCode.SendUserCommand: this.m_BannerMessage = new StringBuilder(); this.m_BannerMessage.Append(this.StatusLine); return(CommandStream.PipelineInstruction.Advance); case FtpStatusCode.ServiceTemporarilyNotAvailable: return(CommandStream.PipelineInstruction.Reread); } throw base.GenerateException(status, response.StatusDescription, null); } if (entry.Command == "OPTS utf8 on\r\n") { if (response.PositiveCompletion) { base.Encoding = Encoding.UTF8; } else { base.Encoding = Encoding.Default; } return(CommandStream.PipelineInstruction.Advance); } if (entry.Command.IndexOf("USER") != -1) { if (status == FtpStatusCode.LoggedInProceed) { this.m_LoginState = FtpLoginState.LoggedIn; base.m_Index++; } else if ((status == FtpStatusCode.NotLoggedIn) && (this.m_LoginState != FtpLoginState.NotLoggedIn)) { this.m_LoginState = FtpLoginState.ReloginFailed; throw ExceptionHelper.IsolatedException; } } if (response.TransientFailure || response.PermanentFailure) { if (status == FtpStatusCode.ServiceNotAvailable) { base.MarkAsRecoverableFailure(); } throw base.GenerateException(status, response.StatusDescription, null); } if ((this.m_LoginState != FtpLoginState.LoggedIn) && (entry.Command.IndexOf("PASS") != -1)) { switch (status) { case FtpStatusCode.NeedLoginAccount: case FtpStatusCode.LoggedInProceed: this.m_LoginState = FtpLoginState.LoggedIn; goto Label_017A; } throw base.GenerateException(status, response.StatusDescription, null); } Label_017A: if (entry.HasFlag(CommandStream.PipelineEntryFlags.CreateDataConnection) && (response.PositiveCompletion || response.PositiveIntermediate)) { bool flag; CommandStream.PipelineInstruction instruction = this.QueueOrCreateDataConection(entry, response, timeout, ref stream, out flag); if (!flag) { return(instruction); } } switch (status) { case FtpStatusCode.OpeningData: case FtpStatusCode.DataAlreadyOpen: { if (this.m_DataSocket == null) { return(CommandStream.PipelineInstruction.Abort); } if (!entry.HasFlag(CommandStream.PipelineEntryFlags.GiveDataStream)) { base.m_AbortReason = SR.GetString("net_ftp_invalid_status_response", new object[] { status, entry.Command }); return(CommandStream.PipelineInstruction.Abort); } this.TryUpdateContentLength(response.StatusDescription); FtpWebRequest request = (FtpWebRequest)base.m_Request; if (request.MethodInfo.ShouldParseForResponseUri) { this.TryUpdateResponseUri(response.StatusDescription, request); } return(this.QueueOrCreateFtpDataStream(ref stream)); } case FtpStatusCode.LoggedInProceed: this.m_WelcomeMessage.Append(this.StatusLine); break; case FtpStatusCode.ClosingControl: this.m_ExitMessage.Append(response.StatusDescription); base.CloseSocket(); break; case FtpStatusCode.ServerWantsSecureSession: { FtpWebRequest initiatingRequest = (FtpWebRequest)base.m_Request; TlsStream stream2 = new TlsStream(initiatingRequest.RequestUri.Host, base.NetworkStream, initiatingRequest.ClientCertificates, base.Pool.ServicePoint, initiatingRequest, base.m_Async ? initiatingRequest.GetWritingContext().ContextCopy : null); base.NetworkStream = stream2; break; } case FtpStatusCode.FileStatus: { FtpWebRequest request1 = (FtpWebRequest)base.m_Request; if (entry.Command.StartsWith("SIZE ")) { this.m_ContentLength = this.GetContentLengthFrom213Response(response.StatusDescription); } else if (entry.Command.StartsWith("MDTM ")) { this.m_LastModified = this.GetLastModifiedFrom213Response(response.StatusDescription); } break; } default: if (status == FtpStatusCode.PathnameCreated) { if ((entry.Command == "PWD\r\n") && !entry.HasFlag(CommandStream.PipelineEntryFlags.UserCommand)) { this.m_LoginDirectory = this.GetLoginDirectory(response.StatusDescription); } } else if (entry.Command.IndexOf("CWD") != -1) { this.m_EstablishedServerDirectory = this.m_RequestedServerDirectory; } break; } if (!response.PositiveIntermediate && (base.UsingSecureStream || !(entry.Command == "AUTH TLS\r\n"))) { return(CommandStream.PipelineInstruction.Advance); } return(CommandStream.PipelineInstruction.Reread); }
// // This is called by underlying base class code, each time a new response is received from the wire or a protocol stage is resumed. // This function controls the seting up of a data socket/connection, and of saving off the server responses // protected override PipelineInstruction PipelineCallback(PipelineEntry entry, ResponseDescription response, bool timeout, ref Stream stream) { GlobalLog.Print("FtpControlStream#" + ValidationHelper.HashString(this) + ">" + (entry == null? "null" : entry.Command)); GlobalLog.Print("FtpControlStream#" + ValidationHelper.HashString(this) + ">" + ((response == null) ? "null" : response.StatusDescription)); // null response is not expected if (response == null) return PipelineInstruction.Abort; FtpStatusCode status = (FtpStatusCode) response.Status; // // Update global "current status" for FtpWebRequest // if (status != FtpStatusCode.ClosingControl) { // A 221 status won't be reflected on the user FTP response // Anything else will (by design?) StatusCode = status; StatusLine = response.StatusDescription; } // If the status code is outside the range defined in RFC (1xx to 5xx) throw if (response.InvalidStatusCode) throw new WebException(SR.GetString(SR.net_InvalidStatusCode), WebExceptionStatus.ProtocolError); // Update the banner message if any, this is a little hack because the "entry" param is null if (m_Index == -1) { if (status == FtpStatusCode.SendUserCommand) { m_BannerMessage = new StringBuilder(); m_BannerMessage.Append(StatusLine); return PipelineInstruction.Advance; } else if (status == FtpStatusCode.ServiceTemporarilyNotAvailable) { return PipelineInstruction.Reread; } else throw GenerateException(status,response.StatusDescription, null); } // // Check for the result of our attempt to use UTF8 // Condsider: optimize this for speed (avoid string compare) as that is the only command that may fail // if (entry.Command == "OPTS utf8 on\r\n") { if (response.PositiveCompletion) { Encoding = Encoding.UTF8; } else { Encoding = Encoding.Default; } return PipelineInstruction.Advance; } // If we are already logged in and the server returns 530 then // the server does not support re-issuing a USER command, // tear down the connection and start all over again if (entry.Command.IndexOf("USER") != -1) { // The server may not require a password for this user, so bypass the password command if (status == FtpStatusCode.LoggedInProceed) { m_LoginState = FtpLoginState.LoggedIn; m_Index++; } // The server does not like re-login // (We are logged in already but want to re-login under a different user) else if (status == FtpStatusCode.NotLoggedIn && m_LoginState != FtpLoginState.NotLoggedIn) { m_LoginState = FtpLoginState.ReloginFailed; throw ExceptionHelper.IsolatedException; } } // // Throw on an error with possible recovery option // if (response.TransientFailure || response.PermanentFailure) { if (status == FtpStatusCode.ServiceNotAvailable) { MarkAsRecoverableFailure(); } throw GenerateException(status,response.StatusDescription, null); } if (m_LoginState != FtpLoginState.LoggedIn && entry.Command.IndexOf("PASS") != -1) { // Note the fact that we logged in if (status == FtpStatusCode.NeedLoginAccount || status == FtpStatusCode.LoggedInProceed) m_LoginState = FtpLoginState.LoggedIn; else throw GenerateException(status,response.StatusDescription, null); } // // Parse special cases // if (entry.HasFlag(PipelineEntryFlags.CreateDataConnection) && (response.PositiveCompletion || response.PositiveIntermediate)) { bool isSocketReady; PipelineInstruction result = QueueOrCreateDataConection(entry, response, timeout, ref stream, out isSocketReady); if (!isSocketReady) return result; // otheriwse we have a stream to create } // // This is part of the above case and it's all about giving data stream back // if (status == FtpStatusCode.OpeningData || status == FtpStatusCode.DataAlreadyOpen) { if (m_DataSocket == null) { // a better diagnostic? return PipelineInstruction.Abort; } if (!entry.HasFlag(PipelineEntryFlags.GiveDataStream)) { m_AbortReason = SR.GetString(SR.net_ftp_invalid_status_response, status, entry.Command); return PipelineInstruction.Abort; } // Parse out the Content length, if we can TryUpdateContentLength(response.StatusDescription); // Parse out the file name, when it is returned and use it for our ResponseUri FtpWebRequest request = (FtpWebRequest) m_Request; if (request.MethodInfo.ShouldParseForResponseUri) { TryUpdateResponseUri(response.StatusDescription, request); } return QueueOrCreateFtpDataStream(ref stream); } // // Parse responses by status code exclusivelly // //Update welcome message if (status == FtpStatusCode.LoggedInProceed) { m_WelcomeMessage.Append(StatusLine); } // OR set the user response ExitMessage else if (status == FtpStatusCode.ClosingControl) { m_ExitMessage.Append(response.StatusDescription); // And close the control stream socket on "QUIT" CloseSocket(); } #if !FEATURE_PAL // OR set us up for SSL/TLS, after this we'll be writing securely else if (status == FtpStatusCode.ServerWantsSecureSession) { FtpWebRequest request = (FtpWebRequest) m_Request; TlsStream tlsStream = new TlsStream(request.RequestUri.Host, NetworkStream, request.ClientCertificates, Pool.ServicePoint, request, m_Async ? request.GetWritingContext().ContextCopy : null); NetworkStream = tlsStream; } #endif // !FEATURE_PAL // OR parse out the file size or file time, usually a result of sending SIZE/MDTM commands else if (status == FtpStatusCode.FileStatus) { FtpWebRequest request = (FtpWebRequest) m_Request; if (entry.Command.StartsWith("SIZE ")) { m_ContentLength = GetContentLengthFrom213Response(response.StatusDescription); } else if (entry.Command.StartsWith("MDTM ")) { m_LastModified = GetLastModifiedFrom213Response(response.StatusDescription); } } // OR parse out our login directory else if (status == FtpStatusCode.PathnameCreated) { if (entry.Command == "PWD\r\n" && !entry.HasFlag(PipelineEntryFlags.UserCommand)) { m_LoginDirectory = GetLoginDirectory(response.StatusDescription); } } // Asserting we have some positive response else { // We only use CWD to reset ourselves back to the login directory. if (entry.Command.IndexOf("CWD") != -1) { m_EstablishedServerDirectory = m_RequestedServerDirectory; } } // Intermediate responses require rereading if (response.PositiveIntermediate || (!UsingSecureStream && entry.Command == "AUTH TLS\r\n")) { return PipelineInstruction.Reread; } return PipelineInstruction.Advance; }
/// <summary> /// <para>Creates an array of commands, that will be sent to the server</para> /// </summary> protected override PipelineEntry [] BuildCommandsList(WebRequest req) { FtpWebRequest request = (FtpWebRequest) req; GlobalLog.Print("FtpControlStream#" + ValidationHelper.HashString(this) + "BuildCommandsList"); m_ResponseUri = request.RequestUri; ArrayList commandList = new ArrayList(); if ((m_LastRequestWasUnknownMethod && !request.MethodInfo.IsUnknownMethod) || Credentials == null || !Credentials.IsEqualTo(request.Credentials.GetCredential(request.RequestUri, "basic"))) { m_PreviousServerPath = null; m_NewServerPath = null; m_LoginDirectory = null; if (m_LoginState == FtpLoginState.LoggedIn) m_LoginState = FtpLoginState.LoggedInButNeedsRelogin; } m_LastRequestWasUnknownMethod = request.MethodInfo.IsUnknownMethod; if (request.EnableSsl && !UsingSecureStream) { commandList.Add(new PipelineEntry(FormatFtpCommand("AUTH", "TLS"))); commandList.Add(new PipelineEntry(FormatFtpCommand("PBSZ", "0"))); commandList.Add(new PipelineEntry(FormatFtpCommand("PROT", "P"))); // According to RFC we need to re-authorize with USER/PASS after we re-authenticate. if (m_LoginState == FtpLoginState.LoggedIn) m_LoginState = FtpLoginState.LoggedInButNeedsRelogin; } if (m_LoginState != FtpLoginState.LoggedIn) { Credentials = request.Credentials.GetCredential(request.RequestUri, "basic"); m_WelcomeMessage = new StringBuilder(); m_ExitMessage = new StringBuilder(); string domainUserName = string.Empty; string password = string.Empty; if (Credentials != null) { domainUserName = Credentials.InternalGetDomainUserName(); password = Credentials.InternalGetPassword(); } if (domainUserName.Length == 0 && password.Length == 0) { domainUserName = "******"; password = "******"; } commandList.Add(new PipelineEntry(FormatFtpCommand("USER", domainUserName))); commandList.Add(new PipelineEntry(FormatFtpCommand("PASS", password), PipelineEntryFlags.DontLogParameter)); commandList.Add(new PipelineEntry(FormatFtpCommand("OPTS", "utf8 on"))); commandList.Add(new PipelineEntry(FormatFtpCommand("PWD", null))); } GetPathOption getPathOption = GetPathOption.Normal; if (request.MethodInfo.HasFlag(FtpMethodFlags.DoesNotTakeParameter)) { getPathOption = GetPathOption.AssumeNoFilename; } else if (request.MethodInfo.HasFlag(FtpMethodFlags.ParameterIsDirectory)) { getPathOption = GetPathOption.AssumeFilename; } string requestPath = null; string requestFilename = null; GetPathAndFilename(getPathOption, request.RequestUri, ref requestPath, ref requestFilename, ref m_IsRootPath); if (requestFilename.Length == 0 && request.MethodInfo.HasFlag(FtpMethodFlags.TakesParameter)) throw new WebException(SR.GetString(SR.net_ftp_invalid_uri)); string newServerPath = requestPath; if (m_PreviousServerPath != newServerPath) { if (!m_IsRootPath && m_LoginState == FtpLoginState.LoggedIn && m_LoginDirectory != null) { newServerPath = m_LoginDirectory+newServerPath; } m_NewServerPath = newServerPath; commandList.Add(new PipelineEntry(FormatFtpCommand("CWD", newServerPath), PipelineEntryFlags.UserCommand)); } if (request.CacheProtocol != null && request.CacheProtocol.ProtocolStatus == CacheValidationStatus.DoNotTakeFromCache && request.MethodInfo.Operation == FtpOperation.DownloadFile) commandList.Add(new PipelineEntry(FormatFtpCommand("MDTM", requestFilename))); if (!request.MethodInfo.IsCommandOnly) { // This is why having a protocol logic on the connection is a bad idea if (request.CacheProtocol == null || request.CacheProtocol.ProtocolStatus != CacheValidationStatus.Continue) { if (request.UseBinary) { commandList.Add(new PipelineEntry(FormatFtpCommand("TYPE", "I"))); } else { commandList.Add(new PipelineEntry(FormatFtpCommand("TYPE", "A"))); } if (request.UsePassive) { string passiveCommand = (ServerAddress.AddressFamily == AddressFamily.InterNetwork) ? "PASV" : "EPSV"; commandList.Add(new PipelineEntry(FormatFtpCommand(passiveCommand, null), PipelineEntryFlags.CreateDataConnection)); } else { string portCommand = (ServerAddress.AddressFamily == AddressFamily.InterNetwork) ? "PORT" : "EPRT"; CreateFtpListenerSocket(request); commandList.Add(new PipelineEntry(FormatFtpCommand(portCommand, GetPortCommandLine(request)))); } if (request.CacheProtocol != null && request.CacheProtocol.ProtocolStatus == CacheValidationStatus.CombineCachedAndServerResponse) { // Combining partial cache with the reminder using "REST" if (request.CacheProtocol.Validator.CacheEntry.StreamSize > 0) commandList.Add(new PipelineEntry(FormatFtpCommand("REST", request.CacheProtocol.Validator.CacheEntry.StreamSize.ToString(CultureInfo.InvariantCulture)))); } else if (request.ContentOffset > 0) { // REST command must always be the last sent before the main file command is sent. commandList.Add(new PipelineEntry(FormatFtpCommand("REST", request.ContentOffset.ToString(CultureInfo.InvariantCulture)))); } } else { // revalidating GetFileSize = "SIZE" GetDateTimeStamp = "MDTM" commandList.Add(new PipelineEntry(FormatFtpCommand("SIZE", requestFilename))); commandList.Add(new PipelineEntry(FormatFtpCommand("MDTM", requestFilename))); } } // // Suppress the data file if this is a revalidation request // if (request.CacheProtocol == null || request.CacheProtocol.ProtocolStatus != CacheValidationStatus.Continue) { PipelineEntryFlags flags = PipelineEntryFlags.UserCommand; if (!request.MethodInfo.IsCommandOnly) { flags |= PipelineEntryFlags.GiveDataStream; if (!request.UsePassive) flags |= PipelineEntryFlags.CreateDataConnection; } if (request.MethodInfo.Operation == FtpOperation.Rename) { commandList.Add(new PipelineEntry(FormatFtpCommand("RNFR", requestFilename), flags)); commandList.Add(new PipelineEntry(FormatFtpCommand("RNTO", request.RenameTo), flags)); } else { commandList.Add(new PipelineEntry(FormatFtpCommand(request.Method, requestFilename), flags)); } if (!request.KeepAlive) { commandList.Add(new PipelineEntry(FormatFtpCommand("QUIT", null))); } } return (PipelineEntry []) commandList.ToArray(typeof(PipelineEntry)); }
// // This is called by underlying base class code, each time a new response is received from the wire or a protocol stage is resumed. // This function controls the seting up of a data socket/connection, and of saving off the server responses // protected override PipelineInstruction PipelineCallback(PipelineEntry entry, ResponseDescription response, bool timeout, ref Stream stream) { GlobalLog.Print("FtpControlStream#" + ValidationHelper.HashString(this) + ">" + (entry == null? "null" : entry.Command)); GlobalLog.Print("FtpControlStream#" + ValidationHelper.HashString(this) + ">" + ((response == null) ? "null" : response.StatusDescription)); // null response is not expected if (response == null) return PipelineInstruction.Abort; FtpStatusCode status = (FtpStatusCode) response.Status; // // Update global "current status" for FtpWebRequest // if (status != FtpStatusCode.ClosingControl) { // A 221 status won't be reflected on the user FTP response // Anything else will (by design?) StatusCode = status; StatusLine = response.StatusDescription; } // If the status code is outside the range defined in RFC (1xx to 5xx) throw if (response.InvalidStatusCode) throw new WebException(SR.GetString(SR.net_InvalidStatusCode), WebExceptionStatus.ProtocolError); if (m_Index == -1) { if (status == FtpStatusCode.SendUserCommand) { m_BannerMessage = new StringBuilder(); m_BannerMessage.Append(StatusLine); return PipelineInstruction.Advance; } else if (status == FtpStatusCode.ServiceTemporarilyNotAvailable) { return PipelineInstruction.Reread; } else throw GenerateException(status,response.StatusDescription, null); } // // Check for the result of our attempt to use UTF8 // Condsider: optimize this for speed (avoid string compare) as that is the only command that may fail // if (entry.Command == "OPTS utf8 on\r\n") { if (response.PositiveCompletion) { Encoding = Encoding.UTF8; } else { Encoding = Encoding.Default; } return PipelineInstruction.Advance; } // If we are already logged in and the server returns 530 then // the server does not support re-issuing a USER command, // tear down the connection and start all over again if (entry.Command.IndexOf("USER") != -1) { // The server may not require a password for this user, so bypass the password command if (status == FtpStatusCode.LoggedInProceed) { m_LoginState = FtpLoginState.LoggedIn; m_Index++; } // The server does not like re-login // (We are logged in already but want to re-login under a different user) else if (status == FtpStatusCode.NotLoggedIn && m_LoginState != FtpLoginState.NotLoggedIn) { m_LoginState = FtpLoginState.ReloginFailed; throw ExceptionHelper.IsolatedException; } } // // Throw on an error with possibe recovery option // if (response.TransientFailure || response.PermanentFailure) { if (status == FtpStatusCode.ServiceNotAvailable) { MarkAsRecoverableFailure(); } throw GenerateException(status,response.StatusDescription, null); } if (m_LoginState != FtpLoginState.LoggedIn && entry.Command.IndexOf("PASS") != -1) { // Note the fact that we logged in if (status == FtpStatusCode.NeedLoginAccount || status == FtpStatusCode.LoggedInProceed) m_LoginState = FtpLoginState.LoggedIn; else throw GenerateException(status,response.StatusDescription, null); } // // Parse special cases // if (entry.HasFlag(PipelineEntryFlags.CreateDataConnection) && (response.PositiveCompletion || response.PositiveIntermediate)) { bool isSocketReady; PipelineInstruction result = QueueOrCreateDataConection(entry, response, timeout, ref stream, out isSocketReady); if (!isSocketReady) return result; // otheriwse we have a stream to create } // // This is part of the above case and it's all about giving data stream back // if (status == FtpStatusCode.OpeningData || status == FtpStatusCode.DataAlreadyOpen) { if (m_DataSocket == null) { // a better diagnostic? return PipelineInstruction.Abort; } if (!entry.HasFlag(PipelineEntryFlags.GiveDataStream)) { m_AbortReason = SR.GetString(SR.net_ftp_invalid_status_response, status, entry.Command); return PipelineInstruction.Abort; } // Parse out the Content length, if we can TryUpdateContentLength(response.StatusDescription); // Parse out the file name, when it is returned and use it for our ResponseUri if (status == FtpStatusCode.OpeningData) { FtpWebRequest request = (FtpWebRequest) m_Request; if (request.MethodInfo.ShouldParseForResponseUri) { TryUpdateResponseUri(response.StatusDescription, request); } } return QueueOrCreateFtpDataStream(ref stream); } // // Parse responses by status code exclusivelly // //Update our command list if we have an alias if (status == FtpStatusCode.LoggedInProceed) { if(StatusLine.ToLower(CultureInfo.InvariantCulture).IndexOf("alias") > 0){ //find start of alias //skip first status code int i = StatusLine.IndexOf("230-",3); if(i > 0) { i+=4; //eat white space while(i<StatusLine.Length && StatusLine[i] == ' '){i++;} //not eol if (i <StatusLine.Length) { //get end of alias int j = StatusLine.IndexOf(' ',i); if(j<0) j=StatusLine.Length; m_Alias = StatusLine.Substring(i,j-i); if (!m_IsRootPath) { //update command list for (i=0;i<m_Commands.Length;i++){ if(m_Commands[i].Command.IndexOf("CWD") == 0) { string path = m_Alias+m_NewServerPath; m_Commands[i] = new PipelineEntry(FormatFtpCommand("CWD", path)); break; } } } } } } m_WelcomeMessage.Append(StatusLine); } // OR set the user response ExitMessage else if (status == FtpStatusCode.ClosingControl) { m_ExitMessage.Append(response.StatusDescription); // And close the control stream socket on "QUIT" CloseSocket(); } // OR parse out the file size or file time, usually a result of sending SIZE/MDTM commands else if (status == FtpStatusCode.FileStatus) { FtpWebRequest request = (FtpWebRequest) m_Request; if (entry.Command.StartsWith("SIZE ")) { m_ContentLength = GetContentLengthFrom213Response(response.StatusDescription); } else if (entry.Command.StartsWith("MDTM ")) { m_LastModified = GetLastModifiedFrom213Response(response.StatusDescription); } } // OR parse out our login directory else if (status == FtpStatusCode.PathnameCreated) { if (entry.Command == "PWD\r\n" && !entry.HasFlag(PipelineEntryFlags.UserCommand)) { m_LoginDirectory = GetLoginDirectory(response.StatusDescription); if (!m_IsRootPath && m_LoginDirectory != "\\" && m_LoginDirectory != "/" && m_Alias == null) { //update command list for (int i=0;i<m_Commands.Length;i++){ if(m_Commands[i].Command.IndexOf("CWD") == 0) { string path = m_LoginDirectory+m_NewServerPath; m_Commands[i] = new PipelineEntry(FormatFtpCommand("CWD", path)); break; } } } } } // Asserting we have some positive response else { // OR update the current path (optimize this pls) if (entry.Command.IndexOf("CWD") != -1) { m_PreviousServerPath = m_NewServerPath; } } // Intermidate responses require rereading if (response.PositiveIntermediate || (!UsingSecureStream && entry.Command == "AUTH TLS\r\n")) { return PipelineInstruction.Reread; } return PipelineInstruction.Advance; }
protected override CommandStream.PipelineEntry[] BuildCommandsList(WebRequest req) { string str3; string str4; string str5; bool flag = false; FtpWebRequest request = (FtpWebRequest) req; this.m_ResponseUri = request.RequestUri; ArrayList list = new ArrayList(); if (request.EnableSsl && !base.UsingSecureStream) { list.Add(new CommandStream.PipelineEntry(this.FormatFtpCommand("AUTH", "TLS"))); flag = true; } if (flag) { this.m_LoginDirectory = null; this.m_EstablishedServerDirectory = null; this.m_RequestedServerDirectory = null; this.m_CurrentTypeSetting = string.Empty; if (this.m_LoginState == FtpLoginState.LoggedIn) { this.m_LoginState = FtpLoginState.LoggedInButNeedsRelogin; } } if (this.m_LoginState != FtpLoginState.LoggedIn) { this.Credentials = request.Credentials.GetCredential(request.RequestUri, "basic"); this.m_WelcomeMessage = new StringBuilder(); this.m_ExitMessage = new StringBuilder(); string parameter = string.Empty; string password = string.Empty; if (this.Credentials != null) { parameter = this.Credentials.InternalGetDomainUserName(); password = this.Credentials.InternalGetPassword(); } if ((parameter.Length == 0) && (password.Length == 0)) { parameter = "anonymous"; password = "******"; } list.Add(new CommandStream.PipelineEntry(this.FormatFtpCommand("USER", parameter))); list.Add(new CommandStream.PipelineEntry(this.FormatFtpCommand("PASS", password), CommandStream.PipelineEntryFlags.DontLogParameter)); if (request.EnableSsl && !base.UsingSecureStream) { list.Add(new CommandStream.PipelineEntry(this.FormatFtpCommand("PBSZ", "0"))); list.Add(new CommandStream.PipelineEntry(this.FormatFtpCommand("PROT", "P"))); } list.Add(new CommandStream.PipelineEntry(this.FormatFtpCommand("OPTS", "utf8 on"))); list.Add(new CommandStream.PipelineEntry(this.FormatFtpCommand("PWD", null))); } GetPathOption normal = GetPathOption.Normal; if (request.MethodInfo.HasFlag(FtpMethodFlags.DoesNotTakeParameter)) { normal = GetPathOption.AssumeNoFilename; } else if (request.MethodInfo.HasFlag(FtpMethodFlags.ParameterIsDirectory)) { normal = GetPathOption.AssumeFilename; } GetPathInfo(normal, request.RequestUri, out str3, out str4, out str5); if ((str5.Length == 0) && request.MethodInfo.HasFlag(FtpMethodFlags.TakesParameter)) { throw new WebException(SR.GetString("net_ftp_invalid_uri")); } if (((this.m_EstablishedServerDirectory != null) && (this.m_LoginDirectory != null)) && (this.m_EstablishedServerDirectory != this.m_LoginDirectory)) { list.Add(new CommandStream.PipelineEntry(this.FormatFtpCommand("CWD", this.m_LoginDirectory), CommandStream.PipelineEntryFlags.UserCommand)); this.m_RequestedServerDirectory = this.m_LoginDirectory; } if (request.MethodInfo.HasFlag(FtpMethodFlags.MustChangeWorkingDirectoryToPath) && (str4.Length > 0)) { list.Add(new CommandStream.PipelineEntry(this.FormatFtpCommand("CWD", str4), CommandStream.PipelineEntryFlags.UserCommand)); this.m_RequestedServerDirectory = str4; } if (((request.CacheProtocol != null) && (request.CacheProtocol.ProtocolStatus == CacheValidationStatus.DoNotTakeFromCache)) && (request.MethodInfo.Operation == FtpOperation.DownloadFile)) { list.Add(new CommandStream.PipelineEntry(this.FormatFtpCommand("MDTM", str3))); } if (!request.MethodInfo.IsCommandOnly) { if ((request.CacheProtocol == null) || (request.CacheProtocol.ProtocolStatus != CacheValidationStatus.Continue)) { string str6 = request.UseBinary ? "I" : "A"; if (this.m_CurrentTypeSetting != str6) { list.Add(new CommandStream.PipelineEntry(this.FormatFtpCommand("TYPE", str6))); this.m_CurrentTypeSetting = str6; } if (request.UsePassive) { string command = (base.ServerAddress.AddressFamily == AddressFamily.InterNetwork) ? "PASV" : "EPSV"; list.Add(new CommandStream.PipelineEntry(this.FormatFtpCommand(command, null), CommandStream.PipelineEntryFlags.CreateDataConnection)); } else { string str8 = (base.ServerAddress.AddressFamily == AddressFamily.InterNetwork) ? "PORT" : "EPRT"; this.CreateFtpListenerSocket(request); list.Add(new CommandStream.PipelineEntry(this.FormatFtpCommand(str8, this.GetPortCommandLine(request)))); } if ((request.CacheProtocol != null) && (request.CacheProtocol.ProtocolStatus == CacheValidationStatus.CombineCachedAndServerResponse)) { if (request.CacheProtocol.Validator.CacheEntry.StreamSize > 0L) { list.Add(new CommandStream.PipelineEntry(this.FormatFtpCommand("REST", request.CacheProtocol.Validator.CacheEntry.StreamSize.ToString(CultureInfo.InvariantCulture)))); } } else if (request.ContentOffset > 0L) { list.Add(new CommandStream.PipelineEntry(this.FormatFtpCommand("REST", request.ContentOffset.ToString(CultureInfo.InvariantCulture)))); } } else { list.Add(new CommandStream.PipelineEntry(this.FormatFtpCommand("SIZE", str3))); list.Add(new CommandStream.PipelineEntry(this.FormatFtpCommand("MDTM", str3))); } } if ((request.CacheProtocol == null) || (request.CacheProtocol.ProtocolStatus != CacheValidationStatus.Continue)) { CommandStream.PipelineEntryFlags userCommand = CommandStream.PipelineEntryFlags.UserCommand; if (!request.MethodInfo.IsCommandOnly) { userCommand |= CommandStream.PipelineEntryFlags.GiveDataStream; if (!request.UsePassive) { userCommand |= CommandStream.PipelineEntryFlags.CreateDataConnection; } } if (request.MethodInfo.Operation == FtpOperation.Rename) { list.Add(new CommandStream.PipelineEntry(this.FormatFtpCommand("RNFR", str4 + "/" + str5), userCommand)); list.Add(new CommandStream.PipelineEntry(this.FormatFtpCommand("RNTO", str4 + "/" + request.RenameTo), userCommand)); } else if (request.MethodInfo.HasFlag(FtpMethodFlags.DoesNotTakeParameter)) { list.Add(new CommandStream.PipelineEntry(this.FormatFtpCommand(request.Method, string.Empty), userCommand)); } else if (request.MethodInfo.HasFlag(FtpMethodFlags.MustChangeWorkingDirectoryToPath)) { list.Add(new CommandStream.PipelineEntry(this.FormatFtpCommand(request.Method, str5), userCommand)); } else { list.Add(new CommandStream.PipelineEntry(this.FormatFtpCommand(request.Method, str3), userCommand)); } if (!request.KeepAlive) { list.Add(new CommandStream.PipelineEntry(this.FormatFtpCommand("QUIT", null))); } } return (CommandStream.PipelineEntry[]) list.ToArray(typeof(CommandStream.PipelineEntry)); }
protected override CommandStream.PipelineInstruction PipelineCallback(CommandStream.PipelineEntry entry, ResponseDescription response, bool timeout, ref Stream stream) { if (response == null) { return CommandStream.PipelineInstruction.Abort; } FtpStatusCode status = (FtpStatusCode) response.Status; if (status != FtpStatusCode.ClosingControl) { this.StatusCode = status; this.StatusLine = response.StatusDescription; } if (response.InvalidStatusCode) { throw new WebException(SR.GetString("net_InvalidStatusCode"), WebExceptionStatus.ProtocolError); } if (base.m_Index == -1) { switch (status) { case FtpStatusCode.SendUserCommand: this.m_BannerMessage = new StringBuilder(); this.m_BannerMessage.Append(this.StatusLine); return CommandStream.PipelineInstruction.Advance; case FtpStatusCode.ServiceTemporarilyNotAvailable: return CommandStream.PipelineInstruction.Reread; } throw base.GenerateException(status, response.StatusDescription, null); } if (entry.Command == "OPTS utf8 on\r\n") { if (response.PositiveCompletion) { base.Encoding = Encoding.UTF8; } else { base.Encoding = Encoding.Default; } return CommandStream.PipelineInstruction.Advance; } if (entry.Command.IndexOf("USER") != -1) { if (status == FtpStatusCode.LoggedInProceed) { this.m_LoginState = FtpLoginState.LoggedIn; base.m_Index++; } else if ((status == FtpStatusCode.NotLoggedIn) && (this.m_LoginState != FtpLoginState.NotLoggedIn)) { this.m_LoginState = FtpLoginState.ReloginFailed; throw ExceptionHelper.IsolatedException; } } if (response.TransientFailure || response.PermanentFailure) { if (status == FtpStatusCode.ServiceNotAvailable) { base.MarkAsRecoverableFailure(); } throw base.GenerateException(status, response.StatusDescription, null); } if ((this.m_LoginState != FtpLoginState.LoggedIn) && (entry.Command.IndexOf("PASS") != -1)) { switch (status) { case FtpStatusCode.NeedLoginAccount: case FtpStatusCode.LoggedInProceed: this.m_LoginState = FtpLoginState.LoggedIn; goto Label_017A; } throw base.GenerateException(status, response.StatusDescription, null); } Label_017A: if (entry.HasFlag(CommandStream.PipelineEntryFlags.CreateDataConnection) && (response.PositiveCompletion || response.PositiveIntermediate)) { bool flag; CommandStream.PipelineInstruction instruction = this.QueueOrCreateDataConection(entry, response, timeout, ref stream, out flag); if (!flag) { return instruction; } } switch (status) { case FtpStatusCode.OpeningData: case FtpStatusCode.DataAlreadyOpen: { if (this.m_DataSocket == null) { return CommandStream.PipelineInstruction.Abort; } if (!entry.HasFlag(CommandStream.PipelineEntryFlags.GiveDataStream)) { base.m_AbortReason = SR.GetString("net_ftp_invalid_status_response", new object[] { status, entry.Command }); return CommandStream.PipelineInstruction.Abort; } this.TryUpdateContentLength(response.StatusDescription); FtpWebRequest request = (FtpWebRequest) base.m_Request; if (request.MethodInfo.ShouldParseForResponseUri) { this.TryUpdateResponseUri(response.StatusDescription, request); } return this.QueueOrCreateFtpDataStream(ref stream); } case FtpStatusCode.LoggedInProceed: this.m_WelcomeMessage.Append(this.StatusLine); break; case FtpStatusCode.ClosingControl: this.m_ExitMessage.Append(response.StatusDescription); base.CloseSocket(); break; case FtpStatusCode.ServerWantsSecureSession: { FtpWebRequest initiatingRequest = (FtpWebRequest) base.m_Request; TlsStream stream2 = new TlsStream(initiatingRequest.RequestUri.Host, base.NetworkStream, initiatingRequest.ClientCertificates, base.Pool.ServicePoint, initiatingRequest, base.m_Async ? initiatingRequest.GetWritingContext().ContextCopy : null); base.NetworkStream = stream2; break; } case FtpStatusCode.FileStatus: { FtpWebRequest request1 = (FtpWebRequest) base.m_Request; if (entry.Command.StartsWith("SIZE ")) { this.m_ContentLength = this.GetContentLengthFrom213Response(response.StatusDescription); } else if (entry.Command.StartsWith("MDTM ")) { this.m_LastModified = this.GetLastModifiedFrom213Response(response.StatusDescription); } break; } default: if (status == FtpStatusCode.PathnameCreated) { if ((entry.Command == "PWD\r\n") && !entry.HasFlag(CommandStream.PipelineEntryFlags.UserCommand)) { this.m_LoginDirectory = this.GetLoginDirectory(response.StatusDescription); } } else if (entry.Command.IndexOf("CWD") != -1) { this.m_EstablishedServerDirectory = this.m_RequestedServerDirectory; } break; } if (!response.PositiveIntermediate && (base.UsingSecureStream || !(entry.Command == "AUTH TLS\r\n"))) { return CommandStream.PipelineInstruction.Advance; } return CommandStream.PipelineInstruction.Reread; }