LCOV - code coverage report
Current view: top level - netwerk/protocol/ftp - nsFtpConnectionThread.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 0 1099 0.0 %
Date: 2017-07-14 16:53:18 Functions: 0 67 0.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
       2             : /* vim:set tw=80 ts=4 sts=4 sw=4 et cin: */
       3             : /* This Source Code Form is subject to the terms of the Mozilla Public
       4             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       5             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
       6             : 
       7             : #include <ctype.h>
       8             : 
       9             : #include "prprf.h"
      10             : #include "mozilla/Logging.h"
      11             : #include "prtime.h"
      12             : 
      13             : #include "nsIOService.h"
      14             : #include "nsFTPChannel.h"
      15             : #include "nsFtpConnectionThread.h"
      16             : #include "nsFtpControlConnection.h"
      17             : #include "nsFtpProtocolHandler.h"
      18             : #include "netCore.h"
      19             : #include "nsCRT.h"
      20             : #include "nsEscape.h"
      21             : #include "nsMimeTypes.h"
      22             : #include "nsNetCID.h"
      23             : #include "nsNetUtil.h"
      24             : #include "nsIAsyncStreamCopier.h"
      25             : #include "nsThreadUtils.h"
      26             : #include "nsStreamUtils.h"
      27             : #include "nsIURL.h"
      28             : #include "nsISocketTransport.h"
      29             : #include "nsIStreamListenerTee.h"
      30             : #include "nsIPrefService.h"
      31             : #include "nsIPrefBranch.h"
      32             : #include "nsIStringBundle.h"
      33             : #include "nsAuthInformationHolder.h"
      34             : #include "nsIProtocolProxyService.h"
      35             : #include "nsICancelable.h"
      36             : #include "nsIOutputStream.h"
      37             : #include "nsIPrompt.h"
      38             : #include "nsIProtocolHandler.h"
      39             : #include "nsIProxyInfo.h"
      40             : #include "nsIRunnable.h"
      41             : #include "nsISocketTransportService.h"
      42             : #include "nsIURI.h"
      43             : #include "nsILoadInfo.h"
      44             : #include "NullPrincipal.h"
      45             : #include "nsIAuthPrompt2.h"
      46             : #include "nsIFTPChannelParentInternal.h"
      47             : 
      48             : using namespace mozilla;
      49             : using namespace mozilla::net;
      50             : 
      51             : extern LazyLogModule gFTPLog;
      52             : #define LOG(args)         MOZ_LOG(gFTPLog, mozilla::LogLevel::Debug, args)
      53             : #define LOG_INFO(args)  MOZ_LOG(gFTPLog, mozilla::LogLevel::Info, args)
      54             : 
      55             : // remove FTP parameters (starting with ";") from the path
      56             : static void
      57           0 : removeParamsFromPath(nsCString& path)
      58             : {
      59           0 :   int32_t index = path.FindChar(';');
      60           0 :   if (index >= 0) {
      61           0 :     path.SetLength(index);
      62             :   }
      63           0 : }
      64             : 
      65           0 : NS_IMPL_ISUPPORTS_INHERITED(nsFtpState,
      66             :                             nsBaseContentStream,
      67             :                             nsIInputStreamCallback,
      68             :                             nsITransportEventSink,
      69             :                             nsIRequestObserver,
      70             :                             nsIProtocolProxyCallback)
      71             : 
      72           0 : nsFtpState::nsFtpState()
      73             :     : nsBaseContentStream(true)
      74             :     , mState(FTP_INIT)
      75             :     , mNextState(FTP_S_USER)
      76             :     , mKeepRunning(true)
      77             :     , mReceivedControlData(false)
      78             :     , mTryingCachedControl(false)
      79             :     , mRETRFailed(false)
      80             :     , mFileSize(kJS_MAX_SAFE_UINTEGER)
      81             :     , mServerType(FTP_GENERIC_TYPE)
      82             :     , mAction(GET)
      83             :     , mAnonymous(true)
      84             :     , mRetryPass(false)
      85             :     , mStorReplyReceived(false)
      86             :     , mInternalError(NS_OK)
      87             :     , mReconnectAndLoginAgain(false)
      88             :     , mCacheConnection(true)
      89             :     , mPort(21)
      90             :     , mAddressChecked(false)
      91             :     , mServerIsIPv6(false)
      92             :     , mUseUTF8(false)
      93             :     , mControlStatus(NS_OK)
      94           0 :     , mDeferredCallbackPending(false)
      95             : {
      96           0 :     LOG_INFO(("FTP:(%p) nsFtpState created", this));
      97             : 
      98             :     // make sure handler stays around
      99           0 :     NS_ADDREF(gFtpHandler);
     100           0 : }
     101             : 
     102           0 : nsFtpState::~nsFtpState()
     103             : {
     104           0 :     LOG_INFO(("FTP:(%p) nsFtpState destroyed", this));
     105             : 
     106           0 :     if (mProxyRequest)
     107           0 :         mProxyRequest->Cancel(NS_ERROR_FAILURE);
     108             : 
     109             :     // release reference to handler
     110           0 :     nsFtpProtocolHandler *handler = gFtpHandler;
     111           0 :     NS_RELEASE(handler);
     112           0 : }
     113             : 
     114             : // nsIInputStreamCallback implementation
     115             : NS_IMETHODIMP
     116           0 : nsFtpState::OnInputStreamReady(nsIAsyncInputStream *aInStream)
     117             : {
     118           0 :     LOG(("FTP:(%p) data stream ready\n", this));
     119             : 
     120             :     // We are receiving a notification from our data stream, so just forward it
     121             :     // on to our stream callback.
     122           0 :     if (HasPendingCallback())
     123           0 :         DispatchCallbackSync();
     124             : 
     125           0 :     return NS_OK;
     126             : }
     127             : 
     128             : void
     129           0 : nsFtpState::OnControlDataAvailable(const char *aData, uint32_t aDataLen)
     130             : {
     131           0 :     LOG(("FTP:(%p) control data available [%u]\n", this, aDataLen));
     132           0 :     mControlConnection->WaitData(this);  // queue up another call
     133             : 
     134           0 :     if (!mReceivedControlData) {
     135             :         // parameter can be null cause the channel fills them in.
     136           0 :         OnTransportStatus(nullptr, NS_NET_STATUS_BEGIN_FTP_TRANSACTION, 0, 0);
     137           0 :         mReceivedControlData = true;
     138             :     }
     139             : 
     140             :     // Sometimes we can get two responses in the same packet, eg from LIST.
     141             :     // So we need to parse the response line by line
     142             : 
     143           0 :     nsCString buffer = mControlReadCarryOverBuf;
     144             : 
     145             :     // Clear the carryover buf - if we still don't have a line, then it will
     146             :     // be reappended below
     147           0 :     mControlReadCarryOverBuf.Truncate();
     148             : 
     149           0 :     buffer.Append(aData, aDataLen);
     150             : 
     151           0 :     const char* currLine = buffer.get();
     152           0 :     while (*currLine && mKeepRunning) {
     153           0 :         int32_t eolLength = strcspn(currLine, CRLF);
     154           0 :         int32_t currLineLength = strlen(currLine);
     155             : 
     156             :         // if currLine is empty or only contains CR or LF, then bail.  we can
     157             :         // sometimes get an ODA event with the full response line + CR without
     158             :         // the trailing LF.  the trailing LF might come in the next ODA event.
     159             :         // because we are happy enough to process a response line ending only
     160             :         // in CR, we need to take care to discard the extra LF (bug 191220).
     161           0 :         if (eolLength == 0 && currLineLength <= 1)
     162           0 :             break;
     163             : 
     164           0 :         if (eolLength == currLineLength) {
     165           0 :             mControlReadCarryOverBuf.Assign(currLine);
     166           0 :             break;
     167             :         }
     168             : 
     169             :         // Append the current segment, including the LF
     170           0 :         nsAutoCString line;
     171           0 :         int32_t crlfLength = 0;
     172             : 
     173           0 :         if ((currLineLength > eolLength) &&
     174           0 :             (currLine[eolLength] == nsCRT::CR) &&
     175           0 :             (currLine[eolLength+1] == nsCRT::LF)) {
     176           0 :             crlfLength = 2; // CR +LF
     177             :         } else {
     178           0 :             crlfLength = 1; // + LF or CR
     179             :         }
     180             : 
     181           0 :         line.Assign(currLine, eolLength + crlfLength);
     182             : 
     183             :         // Does this start with a response code?
     184           0 :         bool startNum = (line.Length() >= 3 &&
     185           0 :                            isdigit(line[0]) &&
     186           0 :                            isdigit(line[1]) &&
     187           0 :                            isdigit(line[2]));
     188             : 
     189           0 :         if (mResponseMsg.IsEmpty()) {
     190             :             // If we get here, then we know that we have a complete line, and
     191             :             // that it is the first one
     192             : 
     193           0 :             NS_ASSERTION(line.Length() > 4 && startNum,
     194             :                          "Read buffer doesn't include response code");
     195             : 
     196           0 :             mResponseCode = atoi(PromiseFlatCString(Substring(line,0,3)).get());
     197             :         }
     198             : 
     199           0 :         mResponseMsg.Append(line);
     200             : 
     201             :         // This is the last line if its 3 numbers followed by a space
     202           0 :         if (startNum && line[3] == ' ') {
     203             :             // yup. last line, let's move on.
     204           0 :             if (mState == mNextState) {
     205           0 :                 NS_ERROR("ftp read state mixup");
     206           0 :                 mInternalError = NS_ERROR_FAILURE;
     207           0 :                 mState = FTP_ERROR;
     208             :             } else {
     209           0 :                 mState = mNextState;
     210             :             }
     211             : 
     212           0 :             nsCOMPtr<nsIFTPEventSink> ftpSink;
     213           0 :             mChannel->GetFTPEventSink(ftpSink);
     214           0 :             if (ftpSink)
     215           0 :                 ftpSink->OnFTPControlLog(true, mResponseMsg.get());
     216             : 
     217           0 :             nsresult rv = Process();
     218           0 :             mResponseMsg.Truncate();
     219           0 :             if (NS_FAILED(rv)) {
     220           0 :                 CloseWithStatus(rv);
     221           0 :                 return;
     222             :             }
     223             :         }
     224             : 
     225           0 :         currLine = currLine + eolLength + crlfLength;
     226             :     }
     227             : }
     228             : 
     229             : void
     230           0 : nsFtpState::OnControlError(nsresult status)
     231             : {
     232           0 :     NS_ASSERTION(NS_FAILED(status), "expecting error condition");
     233             : 
     234           0 :     LOG(("FTP:(%p) CC(%p) error [%" PRIx32 " was-cached=%u]\n",
     235             :          this, mControlConnection.get(), static_cast<uint32_t>(status),
     236             :          mTryingCachedControl));
     237             : 
     238           0 :     mControlStatus = status;
     239           0 :     if (mReconnectAndLoginAgain && NS_SUCCEEDED(mInternalError)) {
     240           0 :         mReconnectAndLoginAgain = false;
     241           0 :         mAnonymous = false;
     242           0 :         mControlStatus = NS_OK;
     243           0 :         Connect();
     244           0 :     } else if (mTryingCachedControl && NS_SUCCEEDED(mInternalError)) {
     245           0 :         mTryingCachedControl = false;
     246           0 :         Connect();
     247             :     } else {
     248           0 :         CloseWithStatus(status);
     249             :     }
     250           0 : }
     251             : 
     252             : nsresult
     253           0 : nsFtpState::EstablishControlConnection()
     254             : {
     255           0 :     NS_ASSERTION(!mControlConnection, "we already have a control connection");
     256             : 
     257             :     nsresult rv;
     258             : 
     259           0 :     LOG(("FTP:(%p) trying cached control\n", this));
     260             : 
     261             :     // Look to see if we can use a cached control connection:
     262           0 :     RefPtr<nsFtpControlConnection> connection;
     263             :     // Don't use cached control if anonymous (bug #473371)
     264           0 :     if (!mChannel->HasLoadFlag(nsIRequest::LOAD_ANONYMOUS))
     265           0 :         gFtpHandler->RemoveConnection(mChannel->URI(), getter_AddRefs(connection));
     266             : 
     267           0 :     if (connection) {
     268           0 :         mControlConnection.swap(connection);
     269           0 :         if (mControlConnection->IsAlive())
     270             :         {
     271             :             // set stream listener of the control connection to be us.
     272           0 :             mControlConnection->WaitData(this);
     273             : 
     274             :             // read cached variables into us.
     275           0 :             mServerType = mControlConnection->mServerType;
     276           0 :             mPassword   = mControlConnection->mPassword;
     277           0 :             mPwd        = mControlConnection->mPwd;
     278           0 :             mUseUTF8    = mControlConnection->mUseUTF8;
     279           0 :             mTryingCachedControl = true;
     280             : 
     281             :             // we have to set charset to connection if server supports utf-8
     282           0 :             if (mUseUTF8)
     283           0 :                 mChannel->SetContentCharset(NS_LITERAL_CSTRING("UTF-8"));
     284             : 
     285             :             // we're already connected to this server, skip login.
     286           0 :             mState = FTP_S_PASV;
     287           0 :             mResponseCode = 530;  // assume the control connection was dropped.
     288           0 :             mControlStatus = NS_OK;
     289           0 :             mReceivedControlData = false;  // For this request, we have not.
     290             : 
     291             :             // if we succeed, return.  Otherwise, we need to create a transport
     292           0 :             rv = mControlConnection->Connect(mChannel->ProxyInfo(), this);
     293           0 :             if (NS_SUCCEEDED(rv))
     294           0 :                 return rv;
     295             :         }
     296           0 :         LOG(("FTP:(%p) cached CC(%p) is unusable\n", this,
     297             :             mControlConnection.get()));
     298             : 
     299           0 :         mControlConnection->WaitData(nullptr);
     300           0 :         mControlConnection = nullptr;
     301             :     }
     302             : 
     303           0 :     LOG(("FTP:(%p) creating CC\n", this));
     304             : 
     305           0 :     mState = FTP_READ_BUF;
     306           0 :     mNextState = FTP_S_USER;
     307             : 
     308           0 :     nsAutoCString host;
     309           0 :     rv = mChannel->URI()->GetAsciiHost(host);
     310           0 :     if (NS_FAILED(rv))
     311           0 :         return rv;
     312             : 
     313           0 :     mControlConnection = new nsFtpControlConnection(host, mPort);
     314           0 :     if (!mControlConnection)
     315           0 :         return NS_ERROR_OUT_OF_MEMORY;
     316             : 
     317           0 :     rv = mControlConnection->Connect(mChannel->ProxyInfo(), this);
     318           0 :     if (NS_FAILED(rv)) {
     319           0 :         LOG(("FTP:(%p) CC(%p) failed to connect [rv=%" PRIx32 "]\n", this,
     320             :              mControlConnection.get(), static_cast<uint32_t>(rv)));
     321           0 :         mControlConnection = nullptr;
     322           0 :         return rv;
     323             :     }
     324             : 
     325           0 :     return mControlConnection->WaitData(this);
     326             : }
     327             : 
     328             : void
     329           0 : nsFtpState::MoveToNextState(FTP_STATE nextState)
     330             : {
     331           0 :     if (NS_FAILED(mInternalError)) {
     332           0 :         mState = FTP_ERROR;
     333           0 :         LOG(("FTP:(%p) FAILED (%" PRIx32 ")\n", this, static_cast<uint32_t>(mInternalError)));
     334             :     } else {
     335           0 :         mState = FTP_READ_BUF;
     336           0 :         mNextState = nextState;
     337             :     }
     338           0 : }
     339             : 
     340             : nsresult
     341           0 : nsFtpState::Process()
     342             : {
     343           0 :     nsresult    rv = NS_OK;
     344           0 :     bool        processingRead = true;
     345             : 
     346           0 :     while (mKeepRunning && processingRead) {
     347           0 :         switch (mState) {
     348             :           case FTP_COMMAND_CONNECT:
     349           0 :             KillControlConnection();
     350           0 :             LOG(("FTP:(%p) establishing CC", this));
     351           0 :             mInternalError = EstablishControlConnection();  // sets mState
     352           0 :             if (NS_FAILED(mInternalError)) {
     353           0 :                 mState = FTP_ERROR;
     354           0 :                 LOG(("FTP:(%p) FAILED\n", this));
     355             :             } else {
     356           0 :                 LOG(("FTP:(%p) SUCCEEDED\n", this));
     357             :             }
     358           0 :             break;
     359             : 
     360             :           case FTP_READ_BUF:
     361           0 :             LOG(("FTP:(%p) Waiting for CC(%p)\n", this,
     362             :                 mControlConnection.get()));
     363           0 :             processingRead = false;
     364           0 :             break;
     365             : 
     366             :           case FTP_ERROR: // xx needs more work to handle dropped control connection cases
     367           0 :             if ((mTryingCachedControl && mResponseCode == 530 &&
     368           0 :                 mInternalError == NS_ERROR_FTP_PASV) ||
     369           0 :                 (mResponseCode == 425 &&
     370           0 :                 mInternalError == NS_ERROR_FTP_PASV)) {
     371             :                 // The user was logged out during an pasv operation
     372             :                 // we want to restart this request with a new control
     373             :                 // channel.
     374           0 :                 mState = FTP_COMMAND_CONNECT;
     375           0 :             } else if (mResponseCode == 421 &&
     376           0 :                        mInternalError != NS_ERROR_FTP_LOGIN) {
     377             :                 // The command channel dropped for some reason.
     378             :                 // Fire it back up, unless we were trying to login
     379             :                 // in which case the server might just be telling us
     380             :                 // that the max number of users has been reached...
     381           0 :                 mState = FTP_COMMAND_CONNECT;
     382           0 :             } else if (mAnonymous &&
     383           0 :                        mInternalError == NS_ERROR_FTP_LOGIN) {
     384             :                 // If the login was anonymous, and it failed, try again with a username
     385             :                 // Don't reuse old control connection, see #386167
     386           0 :                 mAnonymous = false;
     387           0 :                 mState = FTP_COMMAND_CONNECT;
     388             :             } else {
     389           0 :                 LOG(("FTP:(%p) FTP_ERROR - calling StopProcessing\n", this));
     390           0 :                 rv = StopProcessing();
     391           0 :                 NS_ASSERTION(NS_SUCCEEDED(rv), "StopProcessing failed.");
     392           0 :                 processingRead = false;
     393             :             }
     394           0 :             break;
     395             : 
     396             :           case FTP_COMPLETE:
     397           0 :             LOG(("FTP:(%p) COMPLETE\n", this));
     398           0 :             rv = StopProcessing();
     399           0 :             NS_ASSERTION(NS_SUCCEEDED(rv), "StopProcessing failed.");
     400           0 :             processingRead = false;
     401           0 :             break;
     402             : 
     403             : // USER
     404             :           case FTP_S_USER:
     405           0 :             rv = S_user();
     406             : 
     407           0 :             if (NS_FAILED(rv))
     408           0 :                 mInternalError = NS_ERROR_FTP_LOGIN;
     409             : 
     410           0 :             MoveToNextState(FTP_R_USER);
     411           0 :             break;
     412             : 
     413             :           case FTP_R_USER:
     414           0 :             mState = R_user();
     415             : 
     416           0 :             if (FTP_ERROR == mState)
     417           0 :                 mInternalError = NS_ERROR_FTP_LOGIN;
     418             : 
     419           0 :             break;
     420             : // PASS
     421             :           case FTP_S_PASS:
     422           0 :             rv = S_pass();
     423             : 
     424           0 :             if (NS_FAILED(rv))
     425           0 :                 mInternalError = NS_ERROR_FTP_LOGIN;
     426             : 
     427           0 :             MoveToNextState(FTP_R_PASS);
     428           0 :             break;
     429             : 
     430             :           case FTP_R_PASS:
     431           0 :             mState = R_pass();
     432             : 
     433           0 :             if (FTP_ERROR == mState)
     434           0 :                 mInternalError = NS_ERROR_FTP_LOGIN;
     435             : 
     436           0 :             break;
     437             : // ACCT
     438             :           case FTP_S_ACCT:
     439           0 :             rv = S_acct();
     440             : 
     441           0 :             if (NS_FAILED(rv))
     442           0 :                 mInternalError = NS_ERROR_FTP_LOGIN;
     443             : 
     444           0 :             MoveToNextState(FTP_R_ACCT);
     445           0 :             break;
     446             : 
     447             :           case FTP_R_ACCT:
     448           0 :             mState = R_acct();
     449             : 
     450           0 :             if (FTP_ERROR == mState)
     451           0 :                 mInternalError = NS_ERROR_FTP_LOGIN;
     452             : 
     453           0 :             break;
     454             : 
     455             : // SYST
     456             :           case FTP_S_SYST:
     457           0 :             rv = S_syst();
     458             : 
     459           0 :             if (NS_FAILED(rv))
     460           0 :                 mInternalError = NS_ERROR_FTP_LOGIN;
     461             : 
     462           0 :             MoveToNextState(FTP_R_SYST);
     463           0 :             break;
     464             : 
     465             :           case FTP_R_SYST:
     466           0 :             mState = R_syst();
     467             : 
     468           0 :             if (FTP_ERROR == mState)
     469           0 :                 mInternalError = NS_ERROR_FTP_LOGIN;
     470             : 
     471           0 :             break;
     472             : 
     473             : // TYPE
     474             :           case FTP_S_TYPE:
     475           0 :             rv = S_type();
     476             : 
     477           0 :             if (NS_FAILED(rv))
     478           0 :                 mInternalError = rv;
     479             : 
     480           0 :             MoveToNextState(FTP_R_TYPE);
     481           0 :             break;
     482             : 
     483             :           case FTP_R_TYPE:
     484           0 :             mState = R_type();
     485             : 
     486           0 :             if (FTP_ERROR == mState)
     487           0 :                 mInternalError = NS_ERROR_FAILURE;
     488             : 
     489           0 :             break;
     490             : // CWD
     491             :           case FTP_S_CWD:
     492           0 :             rv = S_cwd();
     493             : 
     494           0 :             if (NS_FAILED(rv))
     495           0 :                 mInternalError = NS_ERROR_FTP_CWD;
     496             : 
     497           0 :             MoveToNextState(FTP_R_CWD);
     498           0 :             break;
     499             : 
     500             :           case FTP_R_CWD:
     501           0 :             mState = R_cwd();
     502             : 
     503           0 :             if (FTP_ERROR == mState)
     504           0 :                 mInternalError = NS_ERROR_FTP_CWD;
     505           0 :             break;
     506             : 
     507             : // LIST
     508             :           case FTP_S_LIST:
     509           0 :             rv = S_list();
     510             : 
     511           0 :             if (rv == NS_ERROR_NOT_RESUMABLE) {
     512           0 :                 mInternalError = rv;
     513           0 :             } else if (NS_FAILED(rv)) {
     514           0 :                 mInternalError = NS_ERROR_FTP_CWD;
     515             :             }
     516             : 
     517           0 :             MoveToNextState(FTP_R_LIST);
     518           0 :             break;
     519             : 
     520             :           case FTP_R_LIST:
     521           0 :             mState = R_list();
     522             : 
     523           0 :             if (FTP_ERROR == mState)
     524           0 :                 mInternalError = NS_ERROR_FAILURE;
     525             : 
     526           0 :             break;
     527             : 
     528             : // SIZE
     529             :           case FTP_S_SIZE:
     530           0 :             rv = S_size();
     531             : 
     532           0 :             if (NS_FAILED(rv))
     533           0 :                 mInternalError = rv;
     534             : 
     535           0 :             MoveToNextState(FTP_R_SIZE);
     536           0 :             break;
     537             : 
     538             :           case FTP_R_SIZE:
     539           0 :             mState = R_size();
     540             : 
     541           0 :             if (FTP_ERROR == mState)
     542           0 :                 mInternalError = NS_ERROR_FAILURE;
     543             : 
     544           0 :             break;
     545             : 
     546             : // REST
     547             :           case FTP_S_REST:
     548           0 :             rv = S_rest();
     549             : 
     550           0 :             if (NS_FAILED(rv))
     551           0 :                 mInternalError = rv;
     552             : 
     553           0 :             MoveToNextState(FTP_R_REST);
     554           0 :             break;
     555             : 
     556             :           case FTP_R_REST:
     557           0 :             mState = R_rest();
     558             : 
     559           0 :             if (FTP_ERROR == mState)
     560           0 :                 mInternalError = NS_ERROR_FAILURE;
     561             : 
     562           0 :             break;
     563             : 
     564             : // MDTM
     565             :           case FTP_S_MDTM:
     566           0 :             rv = S_mdtm();
     567           0 :             if (NS_FAILED(rv))
     568           0 :                 mInternalError = rv;
     569           0 :             MoveToNextState(FTP_R_MDTM);
     570           0 :             break;
     571             : 
     572             :           case FTP_R_MDTM:
     573           0 :             mState = R_mdtm();
     574             : 
     575             :             // Don't want to overwrite a more explicit status code
     576           0 :             if (FTP_ERROR == mState && NS_SUCCEEDED(mInternalError))
     577           0 :                 mInternalError = NS_ERROR_FAILURE;
     578             : 
     579           0 :             break;
     580             : 
     581             : // RETR
     582             :           case FTP_S_RETR:
     583           0 :             rv = S_retr();
     584             : 
     585           0 :             if (NS_FAILED(rv))
     586           0 :                 mInternalError = rv;
     587             : 
     588           0 :             MoveToNextState(FTP_R_RETR);
     589           0 :             break;
     590             : 
     591             :           case FTP_R_RETR:
     592             : 
     593           0 :             mState = R_retr();
     594             : 
     595           0 :             if (FTP_ERROR == mState)
     596           0 :                 mInternalError = NS_ERROR_FAILURE;
     597             : 
     598           0 :             break;
     599             : 
     600             : // STOR
     601             :           case FTP_S_STOR:
     602           0 :             rv = S_stor();
     603             : 
     604           0 :             if (NS_FAILED(rv))
     605           0 :                 mInternalError = rv;
     606             : 
     607           0 :             MoveToNextState(FTP_R_STOR);
     608           0 :             break;
     609             : 
     610             :           case FTP_R_STOR:
     611           0 :             mState = R_stor();
     612             : 
     613           0 :             if (FTP_ERROR == mState)
     614           0 :                 mInternalError = NS_ERROR_FAILURE;
     615             : 
     616           0 :             break;
     617             : 
     618             : // PASV
     619             :           case FTP_S_PASV:
     620           0 :             rv = S_pasv();
     621             : 
     622           0 :             if (NS_FAILED(rv))
     623           0 :                 mInternalError = NS_ERROR_FTP_PASV;
     624             : 
     625           0 :             MoveToNextState(FTP_R_PASV);
     626           0 :             break;
     627             : 
     628             :           case FTP_R_PASV:
     629           0 :             mState = R_pasv();
     630             : 
     631           0 :             if (FTP_ERROR == mState)
     632           0 :                 mInternalError = NS_ERROR_FTP_PASV;
     633             : 
     634           0 :             break;
     635             : 
     636             : // PWD
     637             :           case FTP_S_PWD:
     638           0 :             rv = S_pwd();
     639             : 
     640           0 :             if (NS_FAILED(rv))
     641           0 :                 mInternalError = NS_ERROR_FTP_PWD;
     642             : 
     643           0 :             MoveToNextState(FTP_R_PWD);
     644           0 :             break;
     645             : 
     646             :           case FTP_R_PWD:
     647           0 :             mState = R_pwd();
     648             : 
     649           0 :             if (FTP_ERROR == mState)
     650           0 :                 mInternalError = NS_ERROR_FTP_PWD;
     651             : 
     652           0 :             break;
     653             : 
     654             : // FEAT for RFC2640 support
     655             :           case FTP_S_FEAT:
     656           0 :             rv = S_feat();
     657             : 
     658           0 :             if (NS_FAILED(rv))
     659           0 :                 mInternalError = rv;
     660             : 
     661           0 :             MoveToNextState(FTP_R_FEAT);
     662           0 :             break;
     663             : 
     664             :           case FTP_R_FEAT:
     665           0 :             mState = R_feat();
     666             : 
     667             :             // Don't want to overwrite a more explicit status code
     668           0 :             if (FTP_ERROR == mState && NS_SUCCEEDED(mInternalError))
     669           0 :                 mInternalError = NS_ERROR_FAILURE;
     670           0 :             break;
     671             : 
     672             : // OPTS for some non-RFC2640-compliant servers support
     673             :           case FTP_S_OPTS:
     674           0 :             rv = S_opts();
     675             : 
     676           0 :             if (NS_FAILED(rv))
     677           0 :                 mInternalError = rv;
     678             : 
     679           0 :             MoveToNextState(FTP_R_OPTS);
     680           0 :             break;
     681             : 
     682             :           case FTP_R_OPTS:
     683           0 :             mState = R_opts();
     684             : 
     685             :             // Don't want to overwrite a more explicit status code
     686           0 :             if (FTP_ERROR == mState && NS_SUCCEEDED(mInternalError))
     687           0 :                 mInternalError = NS_ERROR_FAILURE;
     688           0 :             break;
     689             : 
     690             :           default:
     691             :             ;
     692             : 
     693             :         }
     694             :     }
     695             : 
     696           0 :     return rv;
     697             : }
     698             : 
     699             : ///////////////////////////////////
     700             : // STATE METHODS
     701             : ///////////////////////////////////
     702             : nsresult
     703           0 : nsFtpState::S_user() {
     704             :     // some servers on connect send us a 421 or 521.  (84525) (141784)
     705           0 :     if ((mResponseCode == 421) || (mResponseCode == 521))
     706           0 :         return NS_ERROR_FAILURE;
     707             : 
     708             :     nsresult rv;
     709           0 :     nsAutoCString usernameStr("USER ");
     710             : 
     711           0 :     mResponseMsg = "";
     712             : 
     713           0 :     if (mAnonymous) {
     714           0 :         mReconnectAndLoginAgain = true;
     715           0 :         usernameStr.AppendLiteral("anonymous");
     716             :     } else {
     717           0 :         mReconnectAndLoginAgain = false;
     718           0 :         if (mUsername.IsEmpty()) {
     719             : 
     720             :             // No prompt for anonymous requests (bug #473371)
     721           0 :             if (mChannel->HasLoadFlag(nsIRequest::LOAD_ANONYMOUS))
     722           0 :               return NS_ERROR_FAILURE;
     723             : 
     724           0 :             nsCOMPtr<nsIAuthPrompt2> prompter;
     725           0 :             NS_QueryAuthPrompt2(static_cast<nsIChannel*>(mChannel),
     726           0 :                                 getter_AddRefs(prompter));
     727           0 :             if (!prompter)
     728           0 :                 return NS_ERROR_NOT_INITIALIZED;
     729             : 
     730             :             RefPtr<nsAuthInformationHolder> info =
     731             :                 new nsAuthInformationHolder(nsIAuthInformation::AUTH_HOST,
     732             :                                             EmptyString(),
     733           0 :                                             EmptyCString());
     734             : 
     735             :             bool retval;
     736           0 :             rv = prompter->PromptAuth(mChannel, nsIAuthPrompt2::LEVEL_NONE,
     737           0 :                                       info, &retval);
     738             : 
     739             :             // if the user canceled or didn't supply a username we want to fail
     740           0 :             if (NS_FAILED(rv) || !retval || info->User().IsEmpty())
     741           0 :                 return NS_ERROR_FAILURE;
     742             : 
     743           0 :             mUsername = info->User();
     744           0 :             mPassword = info->Password();
     745             :         }
     746             :         // XXX Is UTF-8 the best choice?
     747           0 :         AppendUTF16toUTF8(mUsername, usernameStr);
     748             :     }
     749           0 :     usernameStr.Append(CRLF);
     750             : 
     751           0 :     return SendFTPCommand(usernameStr);
     752             : }
     753             : 
     754             : FTP_STATE
     755           0 : nsFtpState::R_user() {
     756           0 :     mReconnectAndLoginAgain = false;
     757           0 :     if (mResponseCode/100 == 3) {
     758             :         // send off the password
     759           0 :         return FTP_S_PASS;
     760             :     }
     761           0 :     if (mResponseCode/100 == 2) {
     762             :         // no password required, we're already logged in
     763           0 :         return FTP_S_SYST;
     764             :     }
     765           0 :     if (mResponseCode/100 == 5) {
     766             :         // problem logging in. typically this means the server
     767             :         // has reached it's user limit.
     768           0 :         return FTP_ERROR;
     769             :     }
     770             :     // LOGIN FAILED
     771           0 :     return FTP_ERROR;
     772             : }
     773             : 
     774             : 
     775             : nsresult
     776           0 : nsFtpState::S_pass() {
     777             :     nsresult rv;
     778           0 :     nsAutoCString passwordStr("PASS ");
     779             : 
     780           0 :     mResponseMsg = "";
     781             : 
     782           0 :     if (mAnonymous) {
     783           0 :         if (!mPassword.IsEmpty()) {
     784             :             // XXX Is UTF-8 the best choice?
     785           0 :             AppendUTF16toUTF8(mPassword, passwordStr);
     786             :         } else {
     787           0 :             nsXPIDLCString anonPassword;
     788           0 :             bool useRealEmail = false;
     789             :             nsCOMPtr<nsIPrefBranch> prefs =
     790           0 :                     do_GetService(NS_PREFSERVICE_CONTRACTID);
     791           0 :             if (prefs) {
     792           0 :                 rv = prefs->GetBoolPref("advanced.mailftp", &useRealEmail);
     793           0 :                 if (NS_SUCCEEDED(rv) && useRealEmail) {
     794           0 :                     prefs->GetCharPref("network.ftp.anonymous_password",
     795           0 :                                        getter_Copies(anonPassword));
     796             :                 }
     797             :             }
     798           0 :             if (!anonPassword.IsEmpty()) {
     799           0 :                 passwordStr.AppendASCII(anonPassword);
     800             :             } else {
     801             :                 // We need to default to a valid email address - bug 101027
     802             :                 // example.com is reserved (rfc2606), so use that
     803           0 :                 passwordStr.AppendLiteral("mozilla@example.com");
     804             :             }
     805             :         }
     806             :     } else {
     807           0 :         if (mPassword.IsEmpty() || mRetryPass) {
     808             : 
     809             :             // No prompt for anonymous requests (bug #473371)
     810           0 :             if (mChannel->HasLoadFlag(nsIRequest::LOAD_ANONYMOUS))
     811           0 :                 return NS_ERROR_FAILURE;
     812             : 
     813           0 :             nsCOMPtr<nsIAuthPrompt2> prompter;
     814           0 :             NS_QueryAuthPrompt2(static_cast<nsIChannel*>(mChannel),
     815           0 :                                 getter_AddRefs(prompter));
     816           0 :             if (!prompter)
     817           0 :                 return NS_ERROR_NOT_INITIALIZED;
     818             : 
     819             :             RefPtr<nsAuthInformationHolder> info =
     820             :                 new nsAuthInformationHolder(nsIAuthInformation::AUTH_HOST |
     821             :                                             nsIAuthInformation::ONLY_PASSWORD,
     822             :                                             EmptyString(),
     823           0 :                                             EmptyCString());
     824             : 
     825           0 :             info->SetUserInternal(mUsername);
     826             : 
     827             :             bool retval;
     828           0 :             rv = prompter->PromptAuth(mChannel, nsIAuthPrompt2::LEVEL_NONE,
     829           0 :                                       info, &retval);
     830             : 
     831             :             // we want to fail if the user canceled. Note here that if they want
     832             :             // a blank password, we will pass it along.
     833           0 :             if (NS_FAILED(rv) || !retval)
     834           0 :                 return NS_ERROR_FAILURE;
     835             : 
     836           0 :             mPassword = info->Password();
     837             :         }
     838             :         // XXX Is UTF-8 the best choice?
     839           0 :         AppendUTF16toUTF8(mPassword, passwordStr);
     840             :     }
     841           0 :     passwordStr.Append(CRLF);
     842             : 
     843           0 :     return SendFTPCommand(passwordStr);
     844             : }
     845             : 
     846             : FTP_STATE
     847           0 : nsFtpState::R_pass() {
     848           0 :     if (mResponseCode/100 == 3) {
     849             :         // send account info
     850           0 :         return FTP_S_ACCT;
     851             :     }
     852           0 :     if (mResponseCode/100 == 2) {
     853             :         // logged in
     854           0 :         return FTP_S_SYST;
     855             :     }
     856           0 :     if (mResponseCode == 503) {
     857             :         // start over w/ the user command.
     858             :         // note: the password was successful, and it's stored in mPassword
     859           0 :         mRetryPass = false;
     860           0 :         return FTP_S_USER;
     861             :     }
     862           0 :     if (mResponseCode/100 == 5 || mResponseCode==421) {
     863             :         // There is no difference between a too-many-users error,
     864             :         // a wrong-password error, or any other sort of error
     865             : 
     866           0 :         if (!mAnonymous)
     867           0 :             mRetryPass = true;
     868             : 
     869           0 :         return FTP_ERROR;
     870             :     }
     871             :     // unexpected response code
     872           0 :     return FTP_ERROR;
     873             : }
     874             : 
     875             : nsresult
     876           0 : nsFtpState::S_pwd() {
     877           0 :     return SendFTPCommand(NS_LITERAL_CSTRING("PWD" CRLF));
     878             : }
     879             : 
     880             : FTP_STATE
     881           0 : nsFtpState::R_pwd() {
     882             :     // Error response to PWD command isn't fatal, but don't cache the connection
     883             :     // if CWD command is sent since correct mPwd is needed for further requests.
     884           0 :     if (mResponseCode/100 != 2)
     885           0 :         return FTP_S_TYPE;
     886             : 
     887           0 :     nsAutoCString respStr(mResponseMsg);
     888           0 :     int32_t pos = respStr.FindChar('"');
     889           0 :     if (pos > -1) {
     890           0 :         respStr.Cut(0, pos+1);
     891           0 :         pos = respStr.FindChar('"');
     892           0 :         if (pos > -1) {
     893           0 :             respStr.Truncate(pos);
     894           0 :             if (mServerType == FTP_VMS_TYPE)
     895           0 :                 ConvertDirspecFromVMS(respStr);
     896           0 :             if (respStr.IsEmpty() || respStr.Last() != '/')
     897           0 :                 respStr.Append('/');
     898           0 :             mPwd = respStr;
     899             :         }
     900             :     }
     901           0 :     return FTP_S_TYPE;
     902             : }
     903             : 
     904             : nsresult
     905           0 : nsFtpState::S_syst() {
     906           0 :     return SendFTPCommand(NS_LITERAL_CSTRING("SYST" CRLF));
     907             : }
     908             : 
     909             : FTP_STATE
     910           0 : nsFtpState::R_syst() {
     911           0 :     if (mResponseCode/100 == 2) {
     912           0 :         if (( mResponseMsg.Find("L8") > -1) ||
     913           0 :             ( mResponseMsg.Find("UNIX") > -1) ||
     914           0 :             ( mResponseMsg.Find("BSD") > -1) ||
     915           0 :             ( mResponseMsg.Find("MACOS Peter's Server") > -1) ||
     916           0 :             ( mResponseMsg.Find("MACOS WebSTAR FTP") > -1) ||
     917           0 :             ( mResponseMsg.Find("MVS") > -1) ||
     918           0 :             ( mResponseMsg.Find("OS/390") > -1) ||
     919           0 :             ( mResponseMsg.Find("OS/400") > -1)) {
     920           0 :             mServerType = FTP_UNIX_TYPE;
     921           0 :         } else if (( mResponseMsg.Find("WIN32", true) > -1) ||
     922           0 :                    ( mResponseMsg.Find("windows", true) > -1)) {
     923           0 :             mServerType = FTP_NT_TYPE;
     924           0 :         } else if (mResponseMsg.Find("OS/2", true) > -1) {
     925           0 :             mServerType = FTP_OS2_TYPE;
     926           0 :         } else if (mResponseMsg.Find("VMS", true) > -1) {
     927           0 :             mServerType = FTP_VMS_TYPE;
     928             :         } else {
     929           0 :             NS_ERROR("Server type list format unrecognized.");
     930             :             // Guessing causes crashes.
     931             :             // (Of course, the parsing code should be more robust...)
     932             :             nsCOMPtr<nsIStringBundleService> bundleService =
     933           0 :                 do_GetService(NS_STRINGBUNDLE_CONTRACTID);
     934           0 :             if (!bundleService)
     935           0 :                 return FTP_ERROR;
     936             : 
     937           0 :             nsCOMPtr<nsIStringBundle> bundle;
     938           0 :             nsresult rv = bundleService->CreateBundle(NECKO_MSGS_URL,
     939           0 :                                                       getter_AddRefs(bundle));
     940           0 :             if (NS_FAILED(rv))
     941           0 :                 return FTP_ERROR;
     942             : 
     943           0 :             char16_t* ucs2Response = ToNewUnicode(mResponseMsg);
     944           0 :             const char16_t *formatStrings[1] = { ucs2Response };
     945             : 
     946           0 :             nsXPIDLString formattedString;
     947           0 :             rv = bundle->FormatStringFromName(u"UnsupportedFTPServer", formatStrings, 1,
     948           0 :                                               getter_Copies(formattedString));
     949           0 :             free(ucs2Response);
     950           0 :             if (NS_FAILED(rv))
     951           0 :                 return FTP_ERROR;
     952             : 
     953             :             // TODO(darin): this code should not be dictating UI like this!
     954           0 :             nsCOMPtr<nsIPrompt> prompter;
     955           0 :             mChannel->GetCallback(prompter);
     956           0 :             if (prompter)
     957           0 :                 prompter->Alert(nullptr, formattedString.get());
     958             : 
     959             :             // since we just alerted the user, clear mResponseMsg,
     960             :             // which is displayed to the user.
     961           0 :             mResponseMsg = "";
     962           0 :             return FTP_ERROR;
     963             :         }
     964             : 
     965           0 :         return FTP_S_FEAT;
     966             :     }
     967             : 
     968           0 :     if (mResponseCode/100 == 5) {
     969             :         // server didn't like the SYST command.  Probably (500, 501, 502)
     970             :         // No clue.  We will just hope it is UNIX type server.
     971           0 :         mServerType = FTP_UNIX_TYPE;
     972             : 
     973           0 :         return FTP_S_FEAT;
     974             :     }
     975           0 :     return FTP_ERROR;
     976             : }
     977             : 
     978             : nsresult
     979           0 : nsFtpState::S_acct() {
     980           0 :     return SendFTPCommand(NS_LITERAL_CSTRING("ACCT noaccount" CRLF));
     981             : }
     982             : 
     983             : FTP_STATE
     984           0 : nsFtpState::R_acct() {
     985           0 :     if (mResponseCode/100 == 2)
     986           0 :         return FTP_S_SYST;
     987             : 
     988           0 :     return FTP_ERROR;
     989             : }
     990             : 
     991             : nsresult
     992           0 : nsFtpState::S_type() {
     993           0 :     return SendFTPCommand(NS_LITERAL_CSTRING("TYPE I" CRLF));
     994             : }
     995             : 
     996             : FTP_STATE
     997           0 : nsFtpState::R_type() {
     998           0 :     if (mResponseCode/100 != 2)
     999           0 :         return FTP_ERROR;
    1000             : 
    1001           0 :     return FTP_S_PASV;
    1002             : }
    1003             : 
    1004             : nsresult
    1005           0 : nsFtpState::S_cwd() {
    1006             :     // Don't cache the connection if PWD command failed
    1007           0 :     if (mPwd.IsEmpty())
    1008           0 :         mCacheConnection = false;
    1009             : 
    1010           0 :     nsAutoCString cwdStr;
    1011           0 :     if (mAction != PUT)
    1012           0 :         cwdStr = mPath;
    1013           0 :     if (cwdStr.IsEmpty() || cwdStr.First() != '/')
    1014           0 :         cwdStr.Insert(mPwd,0);
    1015           0 :     if (mServerType == FTP_VMS_TYPE)
    1016           0 :         ConvertDirspecToVMS(cwdStr);
    1017           0 :     cwdStr.Insert("CWD ",0);
    1018           0 :     cwdStr.Append(CRLF);
    1019             : 
    1020           0 :     return SendFTPCommand(cwdStr);
    1021             : }
    1022             : 
    1023             : FTP_STATE
    1024           0 : nsFtpState::R_cwd() {
    1025           0 :     if (mResponseCode/100 == 2) {
    1026           0 :         if (mAction == PUT)
    1027           0 :             return FTP_S_STOR;
    1028             : 
    1029           0 :         return FTP_S_LIST;
    1030             :     }
    1031             : 
    1032           0 :     return FTP_ERROR;
    1033             : }
    1034             : 
    1035             : nsresult
    1036           0 : nsFtpState::S_size() {
    1037           0 :     nsAutoCString sizeBuf(mPath);
    1038           0 :     if (sizeBuf.IsEmpty() || sizeBuf.First() != '/')
    1039           0 :         sizeBuf.Insert(mPwd,0);
    1040           0 :     if (mServerType == FTP_VMS_TYPE)
    1041           0 :         ConvertFilespecToVMS(sizeBuf);
    1042           0 :     sizeBuf.Insert("SIZE ",0);
    1043           0 :     sizeBuf.Append(CRLF);
    1044             : 
    1045           0 :     return SendFTPCommand(sizeBuf);
    1046             : }
    1047             : 
    1048             : FTP_STATE
    1049           0 : nsFtpState::R_size() {
    1050           0 :     if (mResponseCode/100 == 2) {
    1051           0 :         PR_sscanf(mResponseMsg.get() + 4, "%llu", &mFileSize);
    1052           0 :         mChannel->SetContentLength(mFileSize);
    1053             :     }
    1054             : 
    1055             :     // We may want to be able to resume this
    1056           0 :     return FTP_S_MDTM;
    1057             : }
    1058             : 
    1059             : nsresult
    1060           0 : nsFtpState::S_mdtm() {
    1061           0 :     nsAutoCString mdtmBuf(mPath);
    1062           0 :     if (mdtmBuf.IsEmpty() || mdtmBuf.First() != '/')
    1063           0 :         mdtmBuf.Insert(mPwd,0);
    1064           0 :     if (mServerType == FTP_VMS_TYPE)
    1065           0 :         ConvertFilespecToVMS(mdtmBuf);
    1066           0 :     mdtmBuf.Insert("MDTM ",0);
    1067           0 :     mdtmBuf.Append(CRLF);
    1068             : 
    1069           0 :     return SendFTPCommand(mdtmBuf);
    1070             : }
    1071             : 
    1072             : FTP_STATE
    1073           0 : nsFtpState::R_mdtm() {
    1074           0 :     if (mResponseCode == 213) {
    1075           0 :         mResponseMsg.Cut(0,4);
    1076           0 :         mResponseMsg.Trim(" \t\r\n");
    1077             :         // yyyymmddhhmmss
    1078           0 :         if (mResponseMsg.Length() != 14) {
    1079           0 :             NS_ASSERTION(mResponseMsg.Length() == 14, "Unknown MDTM response");
    1080             :         } else {
    1081           0 :             mModTime = mResponseMsg;
    1082             : 
    1083             :             // Save lastModified time for downloaded files.
    1084           0 :             nsAutoCString timeString;
    1085             :             nsresult error;
    1086             :             PRExplodedTime exTime;
    1087             : 
    1088           0 :             mResponseMsg.Mid(timeString, 0, 4);
    1089           0 :             exTime.tm_year  = timeString.ToInteger(&error);
    1090           0 :             mResponseMsg.Mid(timeString, 4, 2);
    1091           0 :             exTime.tm_month = timeString.ToInteger(&error) - 1; //january = 0
    1092           0 :             mResponseMsg.Mid(timeString, 6, 2);
    1093           0 :             exTime.tm_mday  = timeString.ToInteger(&error);
    1094           0 :             mResponseMsg.Mid(timeString, 8, 2);
    1095           0 :             exTime.tm_hour  = timeString.ToInteger(&error);
    1096           0 :             mResponseMsg.Mid(timeString, 10, 2);
    1097           0 :             exTime.tm_min   = timeString.ToInteger(&error);
    1098           0 :             mResponseMsg.Mid(timeString, 12, 2);
    1099           0 :             exTime.tm_sec   = timeString.ToInteger(&error);
    1100           0 :             exTime.tm_usec  = 0;
    1101             : 
    1102           0 :             exTime.tm_params.tp_gmt_offset = 0;
    1103           0 :             exTime.tm_params.tp_dst_offset = 0;
    1104             : 
    1105           0 :             PR_NormalizeTime(&exTime, PR_GMTParameters);
    1106           0 :             exTime.tm_params = PR_LocalTimeParameters(&exTime);
    1107             : 
    1108           0 :             PRTime time = PR_ImplodeTime(&exTime);
    1109           0 :             (void)mChannel->SetLastModifiedTime(time);
    1110             :         }
    1111             :     }
    1112             : 
    1113           0 :     nsCString entityID;
    1114           0 :     entityID.Truncate();
    1115           0 :     entityID.AppendInt(int64_t(mFileSize));
    1116           0 :     entityID.Append('/');
    1117           0 :     entityID.Append(mModTime);
    1118           0 :     mChannel->SetEntityID(entityID);
    1119             : 
    1120             :     // We weren't asked to resume
    1121           0 :     if (!mChannel->ResumeRequested())
    1122           0 :         return FTP_S_RETR;
    1123             : 
    1124             :     //if (our entityID == supplied one (if any))
    1125           0 :     if (mSuppliedEntityID.IsEmpty() || entityID.Equals(mSuppliedEntityID))
    1126           0 :         return FTP_S_REST;
    1127             : 
    1128           0 :     mInternalError = NS_ERROR_ENTITY_CHANGED;
    1129           0 :     mResponseMsg.Truncate();
    1130           0 :     return FTP_ERROR;
    1131             : }
    1132             : 
    1133             : nsresult
    1134           0 : nsFtpState::SetContentType()
    1135             : {
    1136             :     // FTP directory URLs don't always end in a slash.  Make sure they do.
    1137             :     // This check needs to be here rather than a more obvious place
    1138             :     // (e.g. LIST command processing) so that it ensures the terminating
    1139             :     // slash is appended for the new request case.
    1140             : 
    1141           0 :     if (!mPath.IsEmpty() && mPath.Last() != '/') {
    1142           0 :         nsCOMPtr<nsIURL> url = (do_QueryInterface(mChannel->URI()));
    1143           0 :         nsAutoCString filePath;
    1144           0 :         if(NS_SUCCEEDED(url->GetFilePath(filePath))) {
    1145           0 :             filePath.Append('/');
    1146           0 :             url->SetFilePath(filePath);
    1147             :         }
    1148             :     }
    1149           0 :     return mChannel->SetContentType(
    1150           0 :         NS_LITERAL_CSTRING(APPLICATION_HTTP_INDEX_FORMAT));
    1151             : }
    1152             : 
    1153             : nsresult
    1154           0 : nsFtpState::S_list() {
    1155           0 :     nsresult rv = SetContentType();
    1156           0 :     if (NS_FAILED(rv))
    1157             :         // XXX Invalid cast of FTP_STATE to nsresult -- FTP_ERROR has
    1158             :         // value < 0x80000000 and will pass NS_SUCCEEDED() (bug 778109)
    1159           0 :         return (nsresult)FTP_ERROR;
    1160             : 
    1161           0 :     rv = mChannel->PushStreamConverter("text/ftp-dir",
    1162           0 :                                        APPLICATION_HTTP_INDEX_FORMAT);
    1163           0 :     if (NS_FAILED(rv)) {
    1164             :         // clear mResponseMsg which is displayed to the user.
    1165             :         // TODO: we should probably set this to something meaningful.
    1166           0 :         mResponseMsg = "";
    1167           0 :         return rv;
    1168             :     }
    1169             : 
    1170             :     // dir listings aren't resumable
    1171           0 :     NS_ENSURE_TRUE(!mChannel->ResumeRequested(), NS_ERROR_NOT_RESUMABLE);
    1172             : 
    1173           0 :     mChannel->SetEntityID(EmptyCString());
    1174             : 
    1175             :     const char *listString;
    1176           0 :     if (mServerType == FTP_VMS_TYPE) {
    1177           0 :         listString = "LIST *.*;0" CRLF;
    1178             :     } else {
    1179           0 :         listString = "LIST" CRLF;
    1180             :     }
    1181             : 
    1182           0 :     return SendFTPCommand(nsDependentCString(listString));
    1183             : }
    1184             : 
    1185             : FTP_STATE
    1186           0 : nsFtpState::R_list() {
    1187           0 :     if (mResponseCode/100 == 1) {
    1188             :         // OK, time to start reading from the data connection.
    1189           0 :         if (mDataStream && HasPendingCallback())
    1190           0 :             mDataStream->AsyncWait(this, 0, 0, CallbackTarget());
    1191           0 :         return FTP_READ_BUF;
    1192             :     }
    1193             : 
    1194           0 :     if (mResponseCode/100 == 2) {
    1195             :         //(DONE)
    1196           0 :         mNextState = FTP_COMPLETE;
    1197           0 :         return FTP_COMPLETE;
    1198             :     }
    1199           0 :     return FTP_ERROR;
    1200             : }
    1201             : 
    1202             : nsresult
    1203           0 : nsFtpState::S_retr() {
    1204           0 :     nsAutoCString retrStr(mPath);
    1205           0 :     if (retrStr.IsEmpty() || retrStr.First() != '/')
    1206           0 :         retrStr.Insert(mPwd,0);
    1207           0 :     if (mServerType == FTP_VMS_TYPE)
    1208           0 :         ConvertFilespecToVMS(retrStr);
    1209           0 :     retrStr.Insert("RETR ",0);
    1210           0 :     retrStr.Append(CRLF);
    1211           0 :     return SendFTPCommand(retrStr);
    1212             : }
    1213             : 
    1214             : FTP_STATE
    1215           0 : nsFtpState::R_retr() {
    1216           0 :     if (mResponseCode/100 == 2) {
    1217             :         //(DONE)
    1218           0 :         mNextState = FTP_COMPLETE;
    1219           0 :         return FTP_COMPLETE;
    1220             :     }
    1221             : 
    1222           0 :     if (mResponseCode/100 == 1) {
    1223           0 :         if (mDataStream && HasPendingCallback())
    1224           0 :             mDataStream->AsyncWait(this, 0, 0, CallbackTarget());
    1225           0 :         return FTP_READ_BUF;
    1226             :     }
    1227             : 
    1228             :     // These error codes are related to problems with the connection.
    1229             :     // If we encounter any at this point, do not try CWD and abort.
    1230           0 :     if (mResponseCode == 421 || mResponseCode == 425 || mResponseCode == 426)
    1231           0 :         return FTP_ERROR;
    1232             : 
    1233           0 :     if (mResponseCode/100 == 5) {
    1234           0 :         mRETRFailed = true;
    1235           0 :         return FTP_S_PASV;
    1236             :     }
    1237             : 
    1238           0 :     return FTP_S_CWD;
    1239             : }
    1240             : 
    1241             : 
    1242             : nsresult
    1243           0 : nsFtpState::S_rest() {
    1244             : 
    1245           0 :     nsAutoCString restString("REST ");
    1246             :     // The int64_t cast is needed to avoid ambiguity
    1247           0 :     restString.AppendInt(int64_t(mChannel->StartPos()), 10);
    1248           0 :     restString.Append(CRLF);
    1249             : 
    1250           0 :     return SendFTPCommand(restString);
    1251             : }
    1252             : 
    1253             : FTP_STATE
    1254           0 : nsFtpState::R_rest() {
    1255           0 :     if (mResponseCode/100 == 4) {
    1256             :         // If REST fails, then we can't resume
    1257           0 :         mChannel->SetEntityID(EmptyCString());
    1258             : 
    1259           0 :         mInternalError = NS_ERROR_NOT_RESUMABLE;
    1260           0 :         mResponseMsg.Truncate();
    1261             : 
    1262           0 :         return FTP_ERROR;
    1263             :     }
    1264             : 
    1265           0 :     return FTP_S_RETR;
    1266             : }
    1267             : 
    1268             : nsresult
    1269           0 : nsFtpState::S_stor() {
    1270           0 :     NS_ENSURE_STATE(mChannel->UploadStream());
    1271             : 
    1272           0 :     NS_ASSERTION(mAction == PUT, "Wrong state to be here");
    1273             : 
    1274           0 :     nsCOMPtr<nsIURL> url = do_QueryInterface(mChannel->URI());
    1275           0 :     NS_ASSERTION(url, "I thought you were a nsStandardURL");
    1276             : 
    1277           0 :     nsAutoCString storStr;
    1278           0 :     url->GetFilePath(storStr);
    1279           0 :     NS_ASSERTION(!storStr.IsEmpty(), "What does it mean to store a empty path");
    1280             : 
    1281             :     // kill the first slash since we want to be relative to CWD.
    1282           0 :     if (storStr.First() == '/')
    1283           0 :         storStr.Cut(0,1);
    1284             : 
    1285           0 :     if (mServerType == FTP_VMS_TYPE)
    1286           0 :         ConvertFilespecToVMS(storStr);
    1287             : 
    1288           0 :     NS_UnescapeURL(storStr);
    1289           0 :     storStr.Insert("STOR ",0);
    1290           0 :     storStr.Append(CRLF);
    1291             : 
    1292           0 :     return SendFTPCommand(storStr);
    1293             : }
    1294             : 
    1295             : FTP_STATE
    1296           0 : nsFtpState::R_stor() {
    1297           0 :     if (mResponseCode/100 == 2) {
    1298             :         //(DONE)
    1299           0 :         mNextState = FTP_COMPLETE;
    1300           0 :         mStorReplyReceived = true;
    1301             : 
    1302             :         // Call Close() if it was not called in nsFtpState::OnStoprequest()
    1303           0 :         if (!mUploadRequest && !IsClosed())
    1304           0 :             Close();
    1305             : 
    1306           0 :         return FTP_COMPLETE;
    1307             :     }
    1308             : 
    1309           0 :     if (mResponseCode/100 == 1) {
    1310           0 :         LOG(("FTP:(%p) writing on DT\n", this));
    1311           0 :         return FTP_READ_BUF;
    1312             :     }
    1313             : 
    1314           0 :    mStorReplyReceived = true;
    1315           0 :    return FTP_ERROR;
    1316             : }
    1317             : 
    1318             : 
    1319             : nsresult
    1320           0 : nsFtpState::S_pasv() {
    1321           0 :     if (!mAddressChecked) {
    1322             :         // Find socket address
    1323           0 :         mAddressChecked = true;
    1324           0 :         mServerAddress.raw.family = AF_INET;
    1325           0 :         mServerAddress.inet.ip = htonl(INADDR_ANY);
    1326           0 :         mServerAddress.inet.port = htons(0);
    1327             : 
    1328           0 :         nsITransport *controlSocket = mControlConnection->Transport();
    1329           0 :         if (!controlSocket)
    1330             :             // XXX Invalid cast of FTP_STATE to nsresult -- FTP_ERROR has
    1331             :             // value < 0x80000000 and will pass NS_SUCCEEDED() (bug 778109)
    1332           0 :             return (nsresult)FTP_ERROR;
    1333             : 
    1334           0 :         nsCOMPtr<nsISocketTransport> sTrans = do_QueryInterface(controlSocket);
    1335           0 :         if (sTrans) {
    1336           0 :             nsresult rv = sTrans->GetPeerAddr(&mServerAddress);
    1337           0 :             if (NS_SUCCEEDED(rv)) {
    1338           0 :                 if (!IsIPAddrAny(&mServerAddress))
    1339           0 :                     mServerIsIPv6 = (mServerAddress.raw.family == AF_INET6) &&
    1340           0 :                                     !IsIPAddrV4Mapped(&mServerAddress);
    1341             :                 else {
    1342             :                     /*
    1343             :                      * In case of SOCKS5 remote DNS resolution, we do
    1344             :                      * not know the remote IP address. Still, if it is
    1345             :                      * an IPV6 host, then the external address of the
    1346             :                      * socks server should also be IPv6, and this is the
    1347             :                      * self address of the transport.
    1348             :                      */
    1349             :                     NetAddr selfAddress;
    1350           0 :                     rv = sTrans->GetSelfAddr(&selfAddress);
    1351           0 :                     if (NS_SUCCEEDED(rv))
    1352           0 :                         mServerIsIPv6 = (selfAddress.raw.family == AF_INET6) &&
    1353           0 :                                         !IsIPAddrV4Mapped(&selfAddress);
    1354             :                 }
    1355             :             }
    1356             :         }
    1357             :     }
    1358             : 
    1359             :     const char *string;
    1360           0 :     if (mServerIsIPv6) {
    1361           0 :         string = "EPSV" CRLF;
    1362             :     } else {
    1363           0 :         string = "PASV" CRLF;
    1364             :     }
    1365             : 
    1366           0 :     return SendFTPCommand(nsDependentCString(string));
    1367             : 
    1368             : }
    1369             : 
    1370             : FTP_STATE
    1371           0 : nsFtpState::R_pasv() {
    1372           0 :     if (mResponseCode/100 != 2)
    1373           0 :         return FTP_ERROR;
    1374             : 
    1375             :     nsresult rv;
    1376             :     int32_t port;
    1377             : 
    1378           0 :     nsAutoCString responseCopy(mResponseMsg);
    1379           0 :     char *response = responseCopy.BeginWriting();
    1380             : 
    1381           0 :     char *ptr = response;
    1382             : 
    1383             :     // Make sure to ignore the address in the PASV response (bug 370559)
    1384             : 
    1385           0 :     if (mServerIsIPv6) {
    1386             :         // The returned string is of the form
    1387             :         // text (|||ppp|)
    1388             :         // Where '|' can be any single character
    1389             :         char delim;
    1390           0 :         while (*ptr && *ptr != '(')
    1391           0 :             ptr++;
    1392           0 :         if (*ptr++ != '(')
    1393           0 :             return FTP_ERROR;
    1394           0 :         delim = *ptr++;
    1395           0 :         if (!delim || *ptr++ != delim ||
    1396           0 :                       *ptr++ != delim ||
    1397           0 :                       *ptr < '0' || *ptr > '9')
    1398           0 :             return FTP_ERROR;
    1399           0 :         port = 0;
    1400           0 :         do {
    1401           0 :             port = port * 10 + *ptr++ - '0';
    1402           0 :         } while (*ptr >= '0' && *ptr <= '9');
    1403           0 :         if (*ptr++ != delim || *ptr != ')')
    1404           0 :             return FTP_ERROR;
    1405             :     } else {
    1406             :         // The returned address string can be of the form
    1407             :         // (xxx,xxx,xxx,xxx,ppp,ppp) or
    1408             :         //  xxx,xxx,xxx,xxx,ppp,ppp (without parens)
    1409             :         int32_t h0, h1, h2, h3, p0, p1;
    1410             : 
    1411           0 :         int32_t fields = 0;
    1412             :         // First try with parens
    1413           0 :         while (*ptr && *ptr != '(')
    1414           0 :             ++ptr;
    1415           0 :         if (*ptr) {
    1416           0 :             ++ptr;
    1417             :             fields = PR_sscanf(ptr,
    1418             :                                "%ld,%ld,%ld,%ld,%ld,%ld",
    1419           0 :                                &h0, &h1, &h2, &h3, &p0, &p1);
    1420             :         }
    1421           0 :         if (!*ptr || fields < 6) {
    1422             :             // OK, lets try w/o parens
    1423           0 :             ptr = response;
    1424           0 :             while (*ptr && *ptr != ',')
    1425           0 :                 ++ptr;
    1426           0 :             if (*ptr) {
    1427             :                 // backup to the start of the digits
    1428           0 :                 do {
    1429           0 :                     ptr--;
    1430           0 :                 } while ((ptr >=response) && (*ptr >= '0') && (*ptr <= '9'));
    1431           0 :                 ptr++; // get back onto the numbers
    1432             :                 fields = PR_sscanf(ptr,
    1433             :                                    "%ld,%ld,%ld,%ld,%ld,%ld",
    1434           0 :                                    &h0, &h1, &h2, &h3, &p0, &p1);
    1435             :             }
    1436             :         }
    1437             : 
    1438           0 :         NS_ASSERTION(fields == 6, "Can't parse PASV response");
    1439           0 :         if (fields < 6)
    1440           0 :             return FTP_ERROR;
    1441             : 
    1442           0 :         port = ((int32_t) (p0<<8)) + p1;
    1443             :     }
    1444             : 
    1445           0 :     bool newDataConn = true;
    1446           0 :     if (mDataTransport) {
    1447             :         // Reuse this connection only if its still alive, and the port
    1448             :         // is the same
    1449           0 :         nsCOMPtr<nsISocketTransport> strans = do_QueryInterface(mDataTransport);
    1450           0 :         if (strans) {
    1451             :             int32_t oldPort;
    1452           0 :             nsresult rv = strans->GetPort(&oldPort);
    1453           0 :             if (NS_SUCCEEDED(rv)) {
    1454           0 :                 if (oldPort == port) {
    1455             :                     bool isAlive;
    1456           0 :                     if (NS_SUCCEEDED(strans->IsAlive(&isAlive)) && isAlive)
    1457           0 :                         newDataConn = false;
    1458             :                 }
    1459             :             }
    1460             :         }
    1461             : 
    1462           0 :         if (newDataConn) {
    1463           0 :             mDataTransport->Close(NS_ERROR_ABORT);
    1464           0 :             mDataTransport = nullptr;
    1465           0 :             mDataStream = nullptr;
    1466             :         }
    1467             :     }
    1468             : 
    1469           0 :     if (newDataConn) {
    1470             :         // now we know where to connect our data channel
    1471             :         nsCOMPtr<nsISocketTransportService> sts =
    1472           0 :             do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID);
    1473           0 :         if (!sts)
    1474           0 :             return FTP_ERROR;
    1475             : 
    1476           0 :         nsCOMPtr<nsISocketTransport> strans;
    1477             : 
    1478           0 :         nsAutoCString host;
    1479           0 :         if (!IsIPAddrAny(&mServerAddress)) {
    1480             :             char buf[kIPv6CStrBufSize];
    1481           0 :             NetAddrToString(&mServerAddress, buf, sizeof(buf));
    1482           0 :             host.Assign(buf);
    1483             :         } else {
    1484             :             /*
    1485             :              * In case of SOCKS5 remote DNS resolving, the peer address
    1486             :              * fetched previously will be invalid (0.0.0.0): it is unknown
    1487             :              * to us. But we can pass on the original hostname to the
    1488             :              * connect for the data connection.
    1489             :              */
    1490           0 :             rv = mChannel->URI()->GetAsciiHost(host);
    1491           0 :             if (NS_FAILED(rv))
    1492           0 :                 return FTP_ERROR;
    1493             :         }
    1494             : 
    1495           0 :         rv =  sts->CreateTransport(nullptr, 0, host,
    1496             :                                    port, mChannel->ProxyInfo(),
    1497           0 :                                    getter_AddRefs(strans)); // the data socket
    1498           0 :         if (NS_FAILED(rv))
    1499           0 :             return FTP_ERROR;
    1500           0 :         mDataTransport = strans;
    1501             : 
    1502           0 :         strans->SetQoSBits(gFtpHandler->GetDataQoSBits());
    1503             : 
    1504           0 :         LOG(("FTP:(%p) created DT (%s:%x)\n", this, host.get(), port));
    1505             : 
    1506             :         // hook ourself up as a proxy for status notifications
    1507           0 :         rv = mDataTransport->SetEventSink(this, GetCurrentThreadEventTarget());
    1508           0 :         NS_ENSURE_SUCCESS(rv, FTP_ERROR);
    1509             : 
    1510           0 :         if (mAction == PUT) {
    1511           0 :             NS_ASSERTION(!mRETRFailed, "Failed before uploading");
    1512             : 
    1513             :             // nsIUploadChannel requires the upload stream to support ReadSegments.
    1514             :             // therefore, we can open an unbuffered socket output stream.
    1515           0 :             nsCOMPtr<nsIOutputStream> output;
    1516           0 :             rv = mDataTransport->OpenOutputStream(nsITransport::OPEN_UNBUFFERED,
    1517           0 :                                                   0, 0, getter_AddRefs(output));
    1518           0 :             if (NS_FAILED(rv))
    1519           0 :                 return FTP_ERROR;
    1520             : 
    1521             :             // perform the data copy on the socket transport thread.  we do this
    1522             :             // because "output" is a socket output stream, so the result is that
    1523             :             // all work will be done on the socket transport thread.
    1524             :             nsCOMPtr<nsIEventTarget> stEventTarget =
    1525           0 :                 do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID);
    1526           0 :             if (!stEventTarget)
    1527           0 :                 return FTP_ERROR;
    1528             : 
    1529           0 :             nsCOMPtr<nsIAsyncStreamCopier> copier;
    1530           0 :             rv = NS_NewAsyncStreamCopier(getter_AddRefs(copier),
    1531             :                                          mChannel->UploadStream(),
    1532             :                                          output,
    1533             :                                          stEventTarget,
    1534             :                                          true,   // upload stream is buffered
    1535           0 :                                          false); // output is NOT buffered
    1536           0 :             if (NS_FAILED(rv))
    1537           0 :                 return FTP_ERROR;
    1538             : 
    1539           0 :             rv = copier->AsyncCopy(this, nullptr);
    1540           0 :             if (NS_FAILED(rv))
    1541           0 :                 return FTP_ERROR;
    1542             : 
    1543             :             // hold a reference to the copier so we can cancel it if necessary.
    1544           0 :             mUploadRequest = copier;
    1545             : 
    1546             :             // update the current working directory before sending the STOR
    1547             :             // command.  this is needed since we might be reusing a control
    1548             :             // connection.
    1549           0 :             return FTP_S_CWD;
    1550             :         }
    1551             : 
    1552             :         //
    1553             :         // else, we are reading from the data connection...
    1554             :         //
    1555             : 
    1556             :         // open a buffered, asynchronous socket input stream
    1557           0 :         nsCOMPtr<nsIInputStream> input;
    1558           0 :         rv = mDataTransport->OpenInputStream(0,
    1559             :                                              nsIOService::gDefaultSegmentSize,
    1560             :                                              nsIOService::gDefaultSegmentCount,
    1561           0 :                                              getter_AddRefs(input));
    1562           0 :         NS_ENSURE_SUCCESS(rv, FTP_ERROR);
    1563           0 :         mDataStream = do_QueryInterface(input);
    1564             :     }
    1565             : 
    1566           0 :     if (mRETRFailed || mPath.IsEmpty() || mPath.Last() == '/')
    1567           0 :         return FTP_S_CWD;
    1568           0 :     return FTP_S_SIZE;
    1569             : }
    1570             : 
    1571             : nsresult
    1572           0 : nsFtpState::S_feat() {
    1573           0 :     return SendFTPCommand(NS_LITERAL_CSTRING("FEAT" CRLF));
    1574             : }
    1575             : 
    1576             : FTP_STATE
    1577           0 : nsFtpState::R_feat() {
    1578           0 :     if (mResponseCode/100 == 2) {
    1579           0 :         if (mResponseMsg.Find(NS_LITERAL_CSTRING(CRLF " UTF8" CRLF), true) > -1) {
    1580             :             // This FTP server supports UTF-8 encoding
    1581           0 :             mChannel->SetContentCharset(NS_LITERAL_CSTRING("UTF-8"));
    1582           0 :             mUseUTF8 = true;
    1583           0 :             return FTP_S_OPTS;
    1584             :         }
    1585             :     }
    1586             : 
    1587           0 :     mUseUTF8 = false;
    1588           0 :     return FTP_S_PWD;
    1589             : }
    1590             : 
    1591             : nsresult
    1592           0 : nsFtpState::S_opts() {
    1593             :      // This command is for compatibility of old FTP spec (IETF Draft)
    1594           0 :     return SendFTPCommand(NS_LITERAL_CSTRING("OPTS UTF8 ON" CRLF));
    1595             : }
    1596             : 
    1597             : FTP_STATE
    1598           0 : nsFtpState::R_opts() {
    1599             :     // Ignore error code because "OPTS UTF8 ON" is for compatibility of
    1600             :     // FTP server using IETF draft
    1601           0 :     return FTP_S_PWD;
    1602             : }
    1603             : 
    1604             : ////////////////////////////////////////////////////////////////////////////////
    1605             : // nsIRequest methods:
    1606             : 
    1607             : nsresult
    1608           0 : nsFtpState::Init(nsFtpChannel *channel)
    1609             : {
    1610             :     // parameter validation
    1611           0 :     NS_ASSERTION(channel, "FTP: needs a channel");
    1612             : 
    1613           0 :     mChannel = channel; // a straight ref ptr to the channel
    1614             : 
    1615           0 :     mKeepRunning = true;
    1616           0 :     mSuppliedEntityID = channel->EntityID();
    1617             : 
    1618           0 :     if (channel->UploadStream())
    1619           0 :         mAction = PUT;
    1620             : 
    1621             :     nsresult rv;
    1622           0 :     nsCOMPtr<nsIURL> url = do_QueryInterface(mChannel->URI());
    1623             : 
    1624           0 :     nsAutoCString host;
    1625           0 :     if (url) {
    1626           0 :         rv = url->GetAsciiHost(host);
    1627             :     } else {
    1628           0 :         rv = mChannel->URI()->GetAsciiHost(host);
    1629             :     }
    1630           0 :     if (NS_FAILED(rv) || host.IsEmpty()) {
    1631           0 :         return NS_ERROR_MALFORMED_URI;
    1632             :     }
    1633             : 
    1634           0 :     nsAutoCString path;
    1635           0 :     if (url) {
    1636           0 :         rv = url->GetFilePath(path);
    1637             :     } else {
    1638           0 :         rv = mChannel->URI()->GetPath(path);
    1639             :     }
    1640           0 :     if (NS_FAILED(rv))
    1641           0 :         return rv;
    1642             : 
    1643           0 :     removeParamsFromPath(path);
    1644             : 
    1645             :     // FTP parameters such as type=i are ignored
    1646           0 :     if (url) {
    1647           0 :         url->SetFilePath(path);
    1648             :     } else {
    1649           0 :         mChannel->URI()->SetPath(path);
    1650             :     }
    1651             : 
    1652             :     // Skip leading slash
    1653           0 :     char *fwdPtr = path.BeginWriting();
    1654           0 :     if (!fwdPtr)
    1655           0 :         return NS_ERROR_OUT_OF_MEMORY;
    1656           0 :     if (*fwdPtr == '/')
    1657           0 :         fwdPtr++;
    1658           0 :     if (*fwdPtr != '\0') {
    1659             :         // now unescape it... %xx reduced inline to resulting character
    1660           0 :         int32_t len = NS_UnescapeURL(fwdPtr);
    1661           0 :         mPath.Assign(fwdPtr, len);
    1662             : 
    1663             : #ifdef DEBUG
    1664           0 :         if (mPath.FindCharInSet(CRLF) >= 0)
    1665           0 :             NS_ERROR("NewURI() should've prevented this!!!");
    1666             : #endif
    1667             :     }
    1668             : 
    1669             :     // pull any username and/or password out of the uri
    1670           0 :     nsAutoCString uname;
    1671           0 :     rv = mChannel->URI()->GetUsername(uname);
    1672           0 :     if (NS_FAILED(rv))
    1673           0 :         return rv;
    1674             : 
    1675           0 :     if (!uname.IsEmpty() && !uname.EqualsLiteral("anonymous")) {
    1676           0 :         mAnonymous = false;
    1677           0 :         CopyUTF8toUTF16(NS_UnescapeURL(uname), mUsername);
    1678             : 
    1679             :         // return an error if we find a CR or LF in the username
    1680           0 :         if (uname.FindCharInSet(CRLF) >= 0)
    1681           0 :             return NS_ERROR_MALFORMED_URI;
    1682             :     }
    1683             : 
    1684           0 :     nsAutoCString password;
    1685           0 :     rv = mChannel->URI()->GetPassword(password);
    1686           0 :     if (NS_FAILED(rv))
    1687           0 :         return rv;
    1688             : 
    1689           0 :     CopyUTF8toUTF16(NS_UnescapeURL(password), mPassword);
    1690             : 
    1691             :     // return an error if we find a CR or LF in the password
    1692           0 :     if (mPassword.FindCharInSet(CRLF) >= 0)
    1693           0 :         return NS_ERROR_MALFORMED_URI;
    1694             : 
    1695             :     int32_t port;
    1696           0 :     rv = mChannel->URI()->GetPort(&port);
    1697           0 :     if (NS_FAILED(rv))
    1698           0 :         return rv;
    1699             : 
    1700           0 :     if (port > 0)
    1701           0 :         mPort = port;
    1702             : 
    1703             :     // Lookup Proxy information asynchronously if it isn't already set
    1704             :     // on the channel and if we aren't configured explicitly to go directly
    1705             :     nsCOMPtr<nsIProtocolProxyService> pps =
    1706           0 :         do_GetService(NS_PROTOCOLPROXYSERVICE_CONTRACTID);
    1707             : 
    1708           0 :     if (pps && !mChannel->ProxyInfo()) {
    1709           0 :         pps->AsyncResolve(static_cast<nsIChannel*>(mChannel), 0, this, nullptr,
    1710           0 :                           getter_AddRefs(mProxyRequest));
    1711             :     }
    1712             : 
    1713           0 :     return NS_OK;
    1714             : }
    1715             : 
    1716             : void
    1717           0 : nsFtpState::Connect()
    1718             : {
    1719           0 :     mState = FTP_COMMAND_CONNECT;
    1720           0 :     mNextState = FTP_S_USER;
    1721             : 
    1722           0 :     nsresult rv = Process();
    1723             : 
    1724             :     // check for errors.
    1725           0 :     if (NS_FAILED(rv)) {
    1726           0 :         LOG(("FTP:Process() failed: %" PRIx32 "\n", static_cast<uint32_t>(rv)));
    1727           0 :         mInternalError = NS_ERROR_FAILURE;
    1728           0 :         mState = FTP_ERROR;
    1729           0 :         CloseWithStatus(mInternalError);
    1730             :     }
    1731           0 : }
    1732             : 
    1733             : void
    1734           0 : nsFtpState::KillControlConnection()
    1735             : {
    1736           0 :     mControlReadCarryOverBuf.Truncate(0);
    1737             : 
    1738           0 :     mAddressChecked = false;
    1739           0 :     mServerIsIPv6 = false;
    1740             : 
    1741             :     // if everything went okay, save the connection.
    1742             :     // FIX: need a better way to determine if we can cache the connections.
    1743             :     //      there are some errors which do not mean that we need to kill the connection
    1744             :     //      e.g. fnf.
    1745             : 
    1746           0 :     if (!mControlConnection)
    1747           0 :         return;
    1748             : 
    1749             :     // kill the reference to ourselves in the control connection.
    1750           0 :     mControlConnection->WaitData(nullptr);
    1751             : 
    1752           0 :     if (NS_SUCCEEDED(mInternalError) &&
    1753           0 :         NS_SUCCEEDED(mControlStatus) &&
    1754           0 :         mControlConnection->IsAlive() &&
    1755           0 :         mCacheConnection) {
    1756             : 
    1757           0 :         LOG_INFO(("FTP:(%p) caching CC(%p)", this, mControlConnection.get()));
    1758             : 
    1759             :         // Store connection persistent data
    1760           0 :         mControlConnection->mServerType = mServerType;
    1761           0 :         mControlConnection->mPassword = mPassword;
    1762           0 :         mControlConnection->mPwd = mPwd;
    1763           0 :         mControlConnection->mUseUTF8 = mUseUTF8;
    1764             : 
    1765           0 :         nsresult rv = NS_OK;
    1766             :         // Don't cache controlconnection if anonymous (bug #473371)
    1767           0 :         if (!mChannel->HasLoadFlag(nsIRequest::LOAD_ANONYMOUS))
    1768           0 :             rv = gFtpHandler->InsertConnection(mChannel->URI(),
    1769           0 :                                                mControlConnection);
    1770             :         // Can't cache it?  Kill it then.
    1771           0 :         mControlConnection->Disconnect(rv);
    1772             :     } else {
    1773           0 :         mControlConnection->Disconnect(NS_BINDING_ABORTED);
    1774             :     }
    1775             : 
    1776           0 :     mControlConnection = nullptr;
    1777             : }
    1778             : 
    1779             : class nsFtpAsyncAlert : public Runnable
    1780             : {
    1781             : public:
    1782           0 :   nsFtpAsyncAlert(nsIPrompt* aPrompter, nsString aResponseMsg)
    1783           0 :     : mozilla::Runnable("nsFtpAsyncAlert")
    1784             :     , mPrompter(aPrompter)
    1785           0 :     , mResponseMsg(aResponseMsg)
    1786             :   {
    1787           0 :     }
    1788             : protected:
    1789           0 :     virtual ~nsFtpAsyncAlert()
    1790           0 :     {
    1791           0 :     }
    1792             : public:
    1793           0 :     NS_IMETHOD Run() override
    1794             :     {
    1795           0 :         if (mPrompter) {
    1796           0 :             mPrompter->Alert(nullptr, mResponseMsg.get());
    1797             :         }
    1798           0 :         return NS_OK;
    1799             :     }
    1800             : private:
    1801             :     nsCOMPtr<nsIPrompt> mPrompter;
    1802             :     nsString mResponseMsg;
    1803             : };
    1804             : 
    1805             : 
    1806             : nsresult
    1807           0 : nsFtpState::StopProcessing()
    1808             : {
    1809             :     // Only do this function once.
    1810           0 :     if (!mKeepRunning)
    1811           0 :         return NS_OK;
    1812           0 :     mKeepRunning = false;
    1813             : 
    1814           0 :     LOG_INFO(("FTP:(%p) nsFtpState stopping", this));
    1815             : 
    1816           0 :     if (NS_FAILED(mInternalError) && !mResponseMsg.IsEmpty()) {
    1817             :         // check to see if the control status is bad.
    1818             :         // web shell wont throw an alert.  we better:
    1819             : 
    1820             :         // XXX(darin): this code should not be dictating UI like this!
    1821           0 :         nsCOMPtr<nsIPrompt> prompter;
    1822           0 :         mChannel->GetCallback(prompter);
    1823           0 :         if (prompter) {
    1824           0 :             nsCOMPtr<nsIRunnable> alertEvent;
    1825           0 :             if (mUseUTF8) {
    1826             :                 alertEvent = new nsFtpAsyncAlert(prompter,
    1827           0 :                     NS_ConvertUTF8toUTF16(mResponseMsg));
    1828             :             } else {
    1829             :                 alertEvent = new nsFtpAsyncAlert(prompter,
    1830           0 :                     NS_ConvertASCIItoUTF16(mResponseMsg));
    1831             :             }
    1832           0 :             NS_DispatchToMainThread(alertEvent);
    1833             :         }
    1834           0 :         nsCOMPtr<nsIFTPChannelParentInternal> ftpChanP;
    1835           0 :         mChannel->GetCallback(ftpChanP);
    1836           0 :         if (ftpChanP) {
    1837           0 :           ftpChanP->SetErrorMsg(mResponseMsg.get(), mUseUTF8);
    1838             :         }
    1839             :     }
    1840             : 
    1841           0 :     nsresult broadcastErrorCode = mControlStatus;
    1842           0 :     if (NS_SUCCEEDED(broadcastErrorCode))
    1843           0 :         broadcastErrorCode = mInternalError;
    1844             : 
    1845           0 :     mInternalError = broadcastErrorCode;
    1846             : 
    1847           0 :     KillControlConnection();
    1848             : 
    1849             :     // XXX This can fire before we are done loading data.  Is that a problem?
    1850           0 :     OnTransportStatus(nullptr, NS_NET_STATUS_END_FTP_TRANSACTION, 0, 0);
    1851             : 
    1852           0 :     if (NS_FAILED(broadcastErrorCode))
    1853           0 :         CloseWithStatus(broadcastErrorCode);
    1854             : 
    1855           0 :     return NS_OK;
    1856             : }
    1857             : 
    1858             : nsresult
    1859           0 : nsFtpState::SendFTPCommand(const nsACString& command)
    1860             : {
    1861           0 :     NS_ASSERTION(mControlConnection, "null control connection");
    1862             : 
    1863             :     // we don't want to log the password:
    1864           0 :     nsAutoCString logcmd(command);
    1865           0 :     if (StringBeginsWith(command, NS_LITERAL_CSTRING("PASS ")))
    1866           0 :         logcmd = "PASS xxxxx";
    1867             : 
    1868           0 :     LOG(("FTP:(%p) writing \"%s\"\n", this, logcmd.get()));
    1869             : 
    1870           0 :     nsCOMPtr<nsIFTPEventSink> ftpSink;
    1871           0 :     mChannel->GetFTPEventSink(ftpSink);
    1872           0 :     if (ftpSink)
    1873           0 :         ftpSink->OnFTPControlLog(false, logcmd.get());
    1874             : 
    1875           0 :     if (mControlConnection)
    1876           0 :         return mControlConnection->Write(command);
    1877             : 
    1878           0 :     return NS_ERROR_FAILURE;
    1879             : }
    1880             : 
    1881             : // Convert a unix-style filespec to VMS format
    1882             : // /foo/fred/barney/file.txt -> foo:[fred.barney]file.txt
    1883             : // /foo/file.txt -> foo:[000000]file.txt
    1884             : void
    1885           0 : nsFtpState::ConvertFilespecToVMS(nsCString& fileString)
    1886             : {
    1887           0 :     int ntok=1;
    1888             :     char *t, *nextToken;
    1889           0 :     nsAutoCString fileStringCopy;
    1890             : 
    1891             :     // Get a writeable copy we can strtok with.
    1892           0 :     fileStringCopy = fileString;
    1893           0 :     t = nsCRT::strtok(fileStringCopy.BeginWriting(), "/", &nextToken);
    1894           0 :     if (t)
    1895           0 :         while (nsCRT::strtok(nextToken, "/", &nextToken))
    1896           0 :             ntok++; // count number of terms (tokens)
    1897           0 :     LOG(("FTP:(%p) ConvertFilespecToVMS ntok: %d\n", this, ntok));
    1898           0 :     LOG(("FTP:(%p) ConvertFilespecToVMS from: \"%s\"\n", this, fileString.get()));
    1899             : 
    1900           0 :     if (fileString.First() == '/') {
    1901             :         // absolute filespec
    1902             :         //   /        -> []
    1903             :         //   /a       -> a (doesn't really make much sense)
    1904             :         //   /a/b     -> a:[000000]b
    1905             :         //   /a/b/c   -> a:[b]c
    1906             :         //   /a/b/c/d -> a:[b.c]d
    1907           0 :         if (ntok == 1) {
    1908           0 :             if (fileString.Length() == 1) {
    1909             :                 // Just a slash
    1910           0 :                 fileString.Truncate();
    1911           0 :                 fileString.AppendLiteral("[]");
    1912             :             } else {
    1913             :                 // just copy the name part (drop the leading slash)
    1914           0 :                 fileStringCopy = fileString;
    1915           0 :                 fileString = Substring(fileStringCopy, 1,
    1916           0 :                                        fileStringCopy.Length()-1);
    1917             :             }
    1918             :         } else {
    1919             :             // Get another copy since the last one was written to.
    1920           0 :             fileStringCopy = fileString;
    1921           0 :             fileString.Truncate();
    1922           0 :             fileString.Append(nsCRT::strtok(fileStringCopy.BeginWriting(),
    1923           0 :                               "/", &nextToken));
    1924           0 :             fileString.AppendLiteral(":[");
    1925           0 :             if (ntok > 2) {
    1926           0 :                 for (int i=2; i<ntok; i++) {
    1927           0 :                     if (i > 2) fileString.Append('.');
    1928           0 :                     fileString.Append(nsCRT::strtok(nextToken,
    1929           0 :                                       "/", &nextToken));
    1930             :                 }
    1931             :             } else {
    1932           0 :                 fileString.AppendLiteral("000000");
    1933             :             }
    1934           0 :             fileString.Append(']');
    1935           0 :             fileString.Append(nsCRT::strtok(nextToken, "/", &nextToken));
    1936             :         }
    1937             :     } else {
    1938             :        // relative filespec
    1939             :         //   a       -> a
    1940             :         //   a/b     -> [.a]b
    1941             :         //   a/b/c   -> [.a.b]c
    1942           0 :         if (ntok == 1) {
    1943             :             // no slashes, just use the name as is
    1944             :         } else {
    1945             :             // Get another copy since the last one was written to.
    1946           0 :             fileStringCopy = fileString;
    1947           0 :             fileString.Truncate();
    1948           0 :             fileString.AppendLiteral("[.");
    1949           0 :             fileString.Append(nsCRT::strtok(fileStringCopy.BeginWriting(),
    1950           0 :                               "/", &nextToken));
    1951           0 :             if (ntok > 2) {
    1952           0 :                 for (int i=2; i<ntok; i++) {
    1953           0 :                     fileString.Append('.');
    1954           0 :                     fileString.Append(nsCRT::strtok(nextToken,
    1955           0 :                                       "/", &nextToken));
    1956             :                 }
    1957             :             }
    1958           0 :             fileString.Append(']');
    1959           0 :             fileString.Append(nsCRT::strtok(nextToken, "/", &nextToken));
    1960             :         }
    1961             :     }
    1962           0 :     LOG(("FTP:(%p) ConvertFilespecToVMS   to: \"%s\"\n", this, fileString.get()));
    1963           0 : }
    1964             : 
    1965             : // Convert a unix-style dirspec to VMS format
    1966             : // /foo/fred/barney/rubble -> foo:[fred.barney.rubble]
    1967             : // /foo/fred -> foo:[fred]
    1968             : // /foo -> foo:[000000]
    1969             : // (null) -> (null)
    1970             : void
    1971           0 : nsFtpState::ConvertDirspecToVMS(nsCString& dirSpec)
    1972             : {
    1973           0 :     LOG(("FTP:(%p) ConvertDirspecToVMS from: \"%s\"\n", this, dirSpec.get()));
    1974           0 :     if (!dirSpec.IsEmpty()) {
    1975           0 :         if (dirSpec.Last() != '/')
    1976           0 :             dirSpec.Append('/');
    1977             :         // we can use the filespec routine if we make it look like a file name
    1978           0 :         dirSpec.Append('x');
    1979           0 :         ConvertFilespecToVMS(dirSpec);
    1980           0 :         dirSpec.Truncate(dirSpec.Length()-1);
    1981             :     }
    1982           0 :     LOG(("FTP:(%p) ConvertDirspecToVMS   to: \"%s\"\n", this, dirSpec.get()));
    1983           0 : }
    1984             : 
    1985             : // Convert an absolute VMS style dirspec to UNIX format
    1986             : void
    1987           0 : nsFtpState::ConvertDirspecFromVMS(nsCString& dirSpec)
    1988             : {
    1989           0 :     LOG(("FTP:(%p) ConvertDirspecFromVMS from: \"%s\"\n", this, dirSpec.get()));
    1990           0 :     if (dirSpec.IsEmpty()) {
    1991           0 :         dirSpec.Insert('.', 0);
    1992             :     } else {
    1993           0 :         dirSpec.Insert('/', 0);
    1994           0 :         dirSpec.ReplaceSubstring(":[", "/");
    1995           0 :         dirSpec.ReplaceChar('.', '/');
    1996           0 :         dirSpec.ReplaceChar(']', '/');
    1997             :     }
    1998           0 :     LOG(("FTP:(%p) ConvertDirspecFromVMS   to: \"%s\"\n", this, dirSpec.get()));
    1999           0 : }
    2000             : 
    2001             : //-----------------------------------------------------------------------------
    2002             : 
    2003             : NS_IMETHODIMP
    2004           0 : nsFtpState::OnTransportStatus(nsITransport *transport, nsresult status,
    2005             :                               int64_t progress, int64_t progressMax)
    2006             : {
    2007             :     // Mix signals from both the control and data connections.
    2008             : 
    2009             :     // Ignore data transfer events on the control connection.
    2010           0 :     if (mControlConnection && transport == mControlConnection->Transport()) {
    2011           0 :         switch (status) {
    2012             :         case NS_NET_STATUS_RESOLVING_HOST:
    2013             :         case NS_NET_STATUS_RESOLVED_HOST:
    2014             :         case NS_NET_STATUS_CONNECTING_TO:
    2015             :         case NS_NET_STATUS_CONNECTED_TO:
    2016             :         case NS_NET_STATUS_TLS_HANDSHAKE_STARTING:
    2017             :         case NS_NET_STATUS_TLS_HANDSHAKE_ENDED:
    2018           0 :             break;
    2019             :         default:
    2020           0 :             return NS_OK;
    2021             :         }
    2022             :     }
    2023             : 
    2024             :     // Ignore the progressMax value from the socket.  We know the true size of
    2025             :     // the file based on the response from our SIZE request. Additionally, only
    2026             :     // report the max progress based on where we started/resumed.
    2027           0 :     mChannel->OnTransportStatus(nullptr, status, progress,
    2028           0 :                                 mFileSize - mChannel->StartPos());
    2029           0 :     return NS_OK;
    2030             : }
    2031             : 
    2032             : //-----------------------------------------------------------------------------
    2033             : 
    2034             : NS_IMETHODIMP
    2035           0 : nsFtpState::OnStartRequest(nsIRequest *request, nsISupports *context)
    2036             : {
    2037           0 :     mStorReplyReceived = false;
    2038           0 :     return NS_OK;
    2039             : }
    2040             : 
    2041             : NS_IMETHODIMP
    2042           0 : nsFtpState::OnStopRequest(nsIRequest *request, nsISupports *context,
    2043             :                           nsresult status)
    2044             : {
    2045           0 :     mUploadRequest = nullptr;
    2046             : 
    2047             :     // Close() will be called when reply to STOR command is received
    2048             :     // see bug #389394
    2049           0 :     if (!mStorReplyReceived)
    2050           0 :       return NS_OK;
    2051             : 
    2052             :     // We're done uploading.  Let our consumer know that we're done.
    2053           0 :     Close();
    2054           0 :     return NS_OK;
    2055             : }
    2056             : 
    2057             : //-----------------------------------------------------------------------------
    2058             : 
    2059             : NS_IMETHODIMP
    2060           0 : nsFtpState::Available(uint64_t *result)
    2061             : {
    2062           0 :     if (mDataStream)
    2063           0 :         return mDataStream->Available(result);
    2064             : 
    2065           0 :     return nsBaseContentStream::Available(result);
    2066             : }
    2067             : 
    2068             : NS_IMETHODIMP
    2069           0 : nsFtpState::ReadSegments(nsWriteSegmentFun writer, void *closure,
    2070             :                          uint32_t count, uint32_t *result)
    2071             : {
    2072             :     // Insert a thunk here so that the input stream passed to the writer is this
    2073             :     // input stream instead of mDataStream.
    2074             : 
    2075           0 :     if (mDataStream) {
    2076           0 :         nsWriteSegmentThunk thunk = { this, writer, closure };
    2077             :         nsresult rv;
    2078           0 :         rv = mDataStream->ReadSegments(NS_WriteSegmentThunk, &thunk, count,
    2079           0 :                                        result);
    2080           0 :         return rv;
    2081             :     }
    2082             : 
    2083           0 :     return nsBaseContentStream::ReadSegments(writer, closure, count, result);
    2084             : }
    2085             : 
    2086             : NS_IMETHODIMP
    2087           0 : nsFtpState::CloseWithStatus(nsresult status)
    2088             : {
    2089           0 :     LOG(("FTP:(%p) close [%" PRIx32 "]\n", this, static_cast<uint32_t>(status)));
    2090             : 
    2091             :     // Shutdown the control connection processing if we are being closed with an
    2092             :     // error.  Note: This method may be called several times.
    2093           0 :     if (!IsClosed() && status != NS_BASE_STREAM_CLOSED && NS_FAILED(status)) {
    2094           0 :         if (NS_SUCCEEDED(mInternalError))
    2095           0 :             mInternalError = status;
    2096           0 :         StopProcessing();
    2097             :     }
    2098             : 
    2099           0 :     if (mUploadRequest) {
    2100           0 :         mUploadRequest->Cancel(NS_ERROR_ABORT);
    2101           0 :         mUploadRequest = nullptr;
    2102             :     }
    2103             : 
    2104           0 :     if (mDataTransport) {
    2105             :         // Shutdown the data transport.
    2106           0 :         mDataTransport->Close(NS_ERROR_ABORT);
    2107           0 :         mDataTransport = nullptr;
    2108             :     }
    2109             : 
    2110           0 :     mDataStream = nullptr;
    2111             : 
    2112           0 :     return nsBaseContentStream::CloseWithStatus(status);
    2113             : }
    2114             : 
    2115             : static nsresult
    2116           0 : CreateHTTPProxiedChannel(nsIChannel *channel, nsIProxyInfo *pi, nsIChannel **newChannel)
    2117             : {
    2118             :     nsresult rv;
    2119           0 :     nsCOMPtr<nsIIOService> ioService = do_GetIOService(&rv);
    2120           0 :     if (NS_FAILED(rv))
    2121           0 :         return rv;
    2122             : 
    2123           0 :     nsCOMPtr<nsIProtocolHandler> handler;
    2124           0 :     rv = ioService->GetProtocolHandler("http", getter_AddRefs(handler));
    2125           0 :     if (NS_FAILED(rv))
    2126           0 :         return rv;
    2127             : 
    2128           0 :     nsCOMPtr<nsIProxiedProtocolHandler> pph = do_QueryInterface(handler, &rv);
    2129           0 :     if (NS_FAILED(rv))
    2130           0 :         return rv;
    2131             : 
    2132           0 :     nsCOMPtr<nsIURI> uri;
    2133           0 :     channel->GetURI(getter_AddRefs(uri));
    2134             : 
    2135           0 :     nsCOMPtr<nsILoadInfo> loadInfo;
    2136           0 :     channel->GetLoadInfo(getter_AddRefs(loadInfo));
    2137             : 
    2138           0 :     return pph->NewProxiedChannel2(uri, pi, 0, nullptr, loadInfo, newChannel);
    2139             : }
    2140             : 
    2141             : NS_IMETHODIMP
    2142           0 : nsFtpState::OnProxyAvailable(nsICancelable *request, nsIChannel *channel,
    2143             :                              nsIProxyInfo *pi, nsresult status)
    2144             : {
    2145           0 :   mProxyRequest = nullptr;
    2146             : 
    2147             :   // failed status code just implies DIRECT processing
    2148             : 
    2149           0 :   if (NS_SUCCEEDED(status)) {
    2150           0 :     nsAutoCString type;
    2151           0 :     if (pi && NS_SUCCEEDED(pi->GetType(type)) && type.EqualsLiteral("http")) {
    2152             :         // Proxy the FTP url via HTTP
    2153             :         // This would have been easier to just return a HTTP channel directly
    2154             :         // from nsIIOService::NewChannelFromURI(), but the proxy type cannot
    2155             :         // be reliabliy determined synchronously without jank due to pac, etc..
    2156           0 :         LOG(("FTP:(%p) Configured to use a HTTP proxy channel\n", this));
    2157             : 
    2158           0 :         nsCOMPtr<nsIChannel> newChannel;
    2159           0 :         if (NS_SUCCEEDED(CreateHTTPProxiedChannel(channel, pi,
    2160           0 :                                                   getter_AddRefs(newChannel))) &&
    2161           0 :             NS_SUCCEEDED(mChannel->Redirect(newChannel,
    2162             :                                             nsIChannelEventSink::REDIRECT_INTERNAL,
    2163             :                                             true))) {
    2164           0 :             LOG(("FTP:(%p) Redirected to use a HTTP proxy channel\n", this));
    2165           0 :             return NS_OK;
    2166             :         }
    2167             :     }
    2168           0 :     else if (pi) {
    2169             :         // Proxy using the FTP protocol routed through a socks proxy
    2170           0 :         LOG(("FTP:(%p) Configured to use a SOCKS proxy channel\n", this));
    2171           0 :         mChannel->SetProxyInfo(pi);
    2172             :     }
    2173             :   }
    2174             : 
    2175           0 :   if (mDeferredCallbackPending) {
    2176           0 :       mDeferredCallbackPending = false;
    2177           0 :       OnCallbackPending();
    2178             :   }
    2179           0 :   return NS_OK;
    2180             : }
    2181             : 
    2182             : void
    2183           0 : nsFtpState::OnCallbackPending()
    2184             : {
    2185           0 :     if (mState == FTP_INIT) {
    2186           0 :         if (mProxyRequest) {
    2187           0 :             mDeferredCallbackPending = true;
    2188           0 :             return;
    2189             :         }
    2190           0 :         Connect();
    2191           0 :     } else if (mDataStream) {
    2192           0 :         mDataStream->AsyncWait(this, 0, 0, CallbackTarget());
    2193             :     }
    2194             : }
    2195             : 

Generated by: LCOV version 1.13