LCOV - code coverage report
Current view: directory - netwerk/protocol/ftp - nsFtpConnectionThread.cpp (source / functions) Found Hit Coverage
Test: app.info Lines: 1162 174 15.0 %
Date: 2012-06-02 Functions: 68 22 32.4 %

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

Generated by: LCOV version 1.7