author Ehsan Akhgari <>
Sat, 05 Jun 2010 21:23:26 -0400
changeset 43173 ac1ed3f6b2e71637e562866867c9ac571d2cb283
parent 43144 2d90590dabe63ad9d6323376e6a1138add81e1cb
parent 43113 d8dc49d5bd609668b3c4fadd6c1df12d5da20547
child 69727 f1d79c22fd712766d2e030f4677b546e1d286741
permissions -rw-r--r--
Merge resolving the bad rename changeset landed as part of bug 542222

/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* ***** BEGIN LICENSE BLOCK *****
 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
 * The contents of this file are subject to the Mozilla Public License Version
 * 1.1 (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 * The Original Code is code.
 * The Initial Developer of the Original Code is
 * Netscape Communications Corporation.
 * Portions created by the Initial Developer are Copyright (C) 1998
 * the Initial Developer. All Rights Reserved.
 * Contributor(s):
 * Alternatively, the contents of this file may be used under the terms of
 * either the GNU General Public License Version 2 or later (the "GPL"), or
 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 * in which case the provisions of the GPL or the LGPL are applicable instead
 * of those above. If you wish to allow use of your version of this file only
 * under the terms of either the GPL or the LGPL, and not to allow others to
 * use your version of this file under the terms of the MPL, indicate your
 * decision by deleting the provisions above and replace them with the notice
 * and other provisions required by the GPL or the LGPL. If you do not delete
 * the provisions above, a recipient may use your version of this file under
 * the terms of any one of the MPL, the GPL or the LGPL.
 * ***** END LICENSE BLOCK ***** */

#include "nsIOService.h"
#include "nsFTPChannel.h"
#include "nsFtpControlConnection.h"
#include "nsFtpProtocolHandler.h"
#include "prlog.h"
#include "nsIPipe.h"
#include "nsIInputStream.h"
#include "nsISocketTransportService.h"
#include "nsISocketTransport.h"
#include "nsNetUtil.h"
#include "nsThreadUtils.h"
#include "nsCRT.h"

#if defined(PR_LOGGING)
extern PRLogModuleInfo* gFTPLog;
#define LOG(args)         PR_LOG(gFTPLog, PR_LOG_DEBUG, args)
#define LOG_ALWAYS(args)  PR_LOG(gFTPLog, PR_LOG_ALWAYS, args)

// nsFtpControlConnection implementation ...

NS_IMPL_ISUPPORTS1(nsFtpControlConnection, nsIInputStreamCallback)

nsFtpControlConnection::OnInputStreamReady(nsIAsyncInputStream *stream)
    char data[4096];

    // Consume data whether we have a listener or not.
    PRUint32 avail;
    nsresult rv = stream->Available(&avail);
    if (NS_SUCCEEDED(rv)) {
        if (avail > sizeof(data))
            avail = sizeof(data);

        PRUint32 n;
        rv = stream->Read(data, avail, &n);
        if (NS_SUCCEEDED(rv) && n != avail)
            avail = n;

    // It's important that we null out mListener before calling one of its
    // methods as it may call WaitData, which would queue up another read.

    nsRefPtr<nsFtpControlConnectionListener> listener;

    if (!listener)
        return NS_OK;

    if (NS_FAILED(rv)) {
    } else {
        listener->OnControlDataAvailable(data, avail);

    return NS_OK;

nsFtpControlConnection::nsFtpControlConnection(const nsCSubstring& host,
                                               PRUint32 port)
    : mServerType(0), mSessionId(gFtpHandler->GetSessionId()), mHost(host)
    , mPort(port)
    LOG_ALWAYS(("FTP:CC created @%p", this));

    LOG_ALWAYS(("FTP:CC destroyed @%p", this));

    if (!mSocket) 
        return PR_FALSE;

    PRBool isAlive = PR_FALSE;
    return isAlive;
nsFtpControlConnection::Connect(nsIProxyInfo* proxyInfo,
                                nsITransportEventSink* eventSink)
    if (mSocket)
        return NS_OK;

    // build our own
    nsresult rv;
    nsCOMPtr<nsISocketTransportService> sts =
    if (NS_FAILED(rv))
        return rv;

    rv = sts->CreateTransport(nsnull, 0, mHost, mPort, proxyInfo,
                              getter_AddRefs(mSocket)); // the command transport
    if (NS_FAILED(rv))
        return rv;


    // proxy transport events back to current thread
    if (eventSink)
        mSocket->SetEventSink(eventSink, NS_GetCurrentThread());

    // open buffered, blocking output stream to socket.  so long as commands
    // do not exceed 1024 bytes in length, the writing thread (the main thread)
    // will not block.  this should be OK.
    rv = mSocket->OpenOutputStream(nsITransport::OPEN_BLOCKING, 1024, 1,
    if (NS_FAILED(rv))
        return rv;

    // open buffered, non-blocking/asynchronous input stream to socket.
    nsCOMPtr<nsIInputStream> inStream;
    rv = mSocket->OpenInputStream(0,
    if (NS_SUCCEEDED(rv))
        mSocketInput = do_QueryInterface(inStream);
    return rv;

nsFtpControlConnection::WaitData(nsFtpControlConnectionListener *listener)
    LOG(("FTP:(%p) wait data [listener=%p]\n", this, listener));

    // If listener is null, then simply disconnect the listener.  Otherwise,
    // ensure that we are listening.
    if (!listener) {
        mListener = nsnull;
        return NS_OK;


    mListener = listener;
    return mSocketInput->AsyncWait(this, 0, 0, NS_GetCurrentThread());

nsFtpControlConnection::Disconnect(nsresult status)
    if (!mSocket)
        return NS_OK;  // already disconnected
    LOG_ALWAYS(("FTP:(%p) CC disconnecting (%x)", this, status));

    if (NS_FAILED(status)) {
        // break cyclic reference!
        mSocket = 0;
        mSocketInput->AsyncWait(nsnull, 0, 0, nsnull);  // clear any observer
        mSocketInput = nsnull;
        mSocketOutput = nsnull;

    return NS_OK;

nsFtpControlConnection::Write(const nsCSubstring& command)

    PRUint32 len = command.Length();
    PRUint32 cnt;
    nsresult rv = mSocketOutput->Write(command.Data(), len, &cnt);

    if (NS_FAILED(rv))
        return rv;

    if (len != cnt)
        return NS_ERROR_FAILURE;
    return NS_OK;