Example #1
0
        /// <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)));
        }
Example #2
0
        /// <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));
        }
Example #3
0
        //    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)));
        }
Example #5
0
        //    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;
        }
Example #6
0
        /// <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);
        }
Example #8
0
        //
        //    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;
        }