Blame netwerk/protocol/ftp/nsFtpControlConnection.cpp

Packit f0b94e
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
Packit f0b94e
/* This Source Code Form is subject to the terms of the Mozilla Public
Packit f0b94e
 * License, v. 2.0. If a copy of the MPL was not distributed with this
Packit f0b94e
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
Packit f0b94e
Packit f0b94e
#include "nsIOService.h"
Packit f0b94e
#include "nsFtpControlConnection.h"
Packit f0b94e
#include "nsFtpProtocolHandler.h"
Packit f0b94e
#include "mozilla/Logging.h"
Packit f0b94e
#include "nsIInputStream.h"
Packit f0b94e
#include "nsISocketTransportService.h"
Packit f0b94e
#include "nsISocketTransport.h"
Packit f0b94e
#include "nsThreadUtils.h"
Packit f0b94e
#include "nsIOutputStream.h"
Packit f0b94e
#include "nsNetCID.h"
Packit f0b94e
#include <algorithm>
Packit f0b94e
Packit f0b94e
using namespace mozilla;
Packit f0b94e
using namespace mozilla::net;
Packit f0b94e
Packit f0b94e
extern LazyLogModule gFTPLog;
Packit f0b94e
#define LOG(args) MOZ_LOG(gFTPLog, mozilla::LogLevel::Debug, args)
Packit f0b94e
#define LOG_INFO(args) MOZ_LOG(gFTPLog, mozilla::LogLevel::Info, args)
Packit f0b94e
Packit f0b94e
//
Packit f0b94e
// nsFtpControlConnection implementation ...
Packit f0b94e
//
Packit f0b94e
Packit f0b94e
NS_IMPL_ISUPPORTS(nsFtpControlConnection, nsIInputStreamCallback)
Packit f0b94e
Packit f0b94e
NS_IMETHODIMP
Packit f0b94e
nsFtpControlConnection::OnInputStreamReady(nsIAsyncInputStream* stream) {
Packit f0b94e
  char data[4096];
Packit f0b94e
Packit f0b94e
  // Consume data whether we have a listener or not.
Packit f0b94e
  uint64_t avail64;
Packit f0b94e
  uint32_t avail = 0;
Packit f0b94e
  nsresult rv = stream->Available(&avail64);
Packit f0b94e
  if (NS_SUCCEEDED(rv)) {
Packit f0b94e
    avail = (uint32_t)std::min(avail64, (uint64_t)sizeof(data));
Packit f0b94e
Packit f0b94e
    uint32_t n;
Packit f0b94e
    rv = stream->Read(data, avail, &n);
Packit f0b94e
    if (NS_SUCCEEDED(rv)) avail = n;
Packit f0b94e
  }
Packit f0b94e
Packit f0b94e
  // It's important that we null out mListener before calling one of its
Packit f0b94e
  // methods as it may call WaitData, which would queue up another read.
Packit f0b94e
Packit f0b94e
  RefPtr<nsFtpControlConnectionListener> listener;
Packit f0b94e
  listener.swap(mListener);
Packit f0b94e
Packit f0b94e
  if (!listener) return NS_OK;
Packit f0b94e
Packit f0b94e
  if (NS_FAILED(rv)) {
Packit f0b94e
    listener->OnControlError(rv);
Packit f0b94e
  } else {
Packit f0b94e
    listener->OnControlDataAvailable(data, avail);
Packit f0b94e
  }
Packit f0b94e
Packit f0b94e
  return NS_OK;
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
nsFtpControlConnection::nsFtpControlConnection(const nsACString& host,
Packit f0b94e
                                               uint32_t port)
Packit f0b94e
    : mServerType(0),
Packit f0b94e
      mSessionId(gFtpHandler->GetSessionId()),
Packit f0b94e
      mUseUTF8(false),
Packit f0b94e
      mHost(host),
Packit f0b94e
      mPort(port) {
Packit f0b94e
  LOG_INFO(("FTP:CC created @%p", this));
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
nsFtpControlConnection::~nsFtpControlConnection() {
Packit f0b94e
  LOG_INFO(("FTP:CC destroyed @%p", this));
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
bool nsFtpControlConnection::IsAlive() {
Packit f0b94e
  if (!mSocket) return false;
Packit f0b94e
Packit f0b94e
  bool isAlive = false;
Packit f0b94e
  mSocket->IsAlive(&isAlive);
Packit f0b94e
  return isAlive;
Packit f0b94e
}
Packit f0b94e
nsresult nsFtpControlConnection::Connect(nsIProxyInfo* proxyInfo,
Packit f0b94e
                                         nsITransportEventSink* eventSink) {
Packit f0b94e
  if (mSocket) return NS_OK;
Packit f0b94e
Packit f0b94e
  // build our own
Packit f0b94e
  nsresult rv;
Packit f0b94e
  nsCOMPtr<nsISocketTransportService> sts =
Packit f0b94e
      do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &rv;;
Packit f0b94e
  if (NS_FAILED(rv)) return rv;
Packit f0b94e
Packit f0b94e
  rv = sts->CreateTransport(nullptr, 0, mHost, mPort, proxyInfo,
Packit f0b94e
                            getter_AddRefs(mSocket));  // the command transport
Packit f0b94e
  if (NS_FAILED(rv)) return rv;
Packit f0b94e
Packit f0b94e
  mSocket->SetQoSBits(gFtpHandler->GetControlQoSBits());
Packit f0b94e
Packit f0b94e
  // proxy transport events back to current thread
Packit f0b94e
  if (eventSink)
Packit f0b94e
    mSocket->SetEventSink(eventSink, GetCurrentThreadEventTarget());
Packit f0b94e
Packit f0b94e
  // open buffered, blocking output stream to socket.  so long as commands
Packit f0b94e
  // do not exceed 1024 bytes in length, the writing thread (the main thread)
Packit f0b94e
  // will not block.  this should be OK.
Packit f0b94e
  rv = mSocket->OpenOutputStream(nsITransport::OPEN_BLOCKING, 1024, 1,
Packit f0b94e
                                 getter_AddRefs(mSocketOutput));
Packit f0b94e
  if (NS_FAILED(rv)) return rv;
Packit f0b94e
Packit f0b94e
  // open buffered, non-blocking/asynchronous input stream to socket.
Packit f0b94e
  nsCOMPtr<nsIInputStream> inStream;
Packit f0b94e
  rv = mSocket->OpenInputStream(0, nsIOService::gDefaultSegmentSize,
Packit f0b94e
                                nsIOService::gDefaultSegmentCount,
Packit f0b94e
                                getter_AddRefs(inStream));
Packit f0b94e
  if (NS_SUCCEEDED(rv)) mSocketInput = do_QueryInterface(inStream);
Packit f0b94e
Packit f0b94e
  return rv;
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
nsresult nsFtpControlConnection::WaitData(
Packit f0b94e
    nsFtpControlConnectionListener* listener) {
Packit f0b94e
  LOG(("FTP:(%p) wait data [listener=%p]\n", this, listener));
Packit f0b94e
Packit f0b94e
  // If listener is null, then simply disconnect the listener.  Otherwise,
Packit f0b94e
  // ensure that we are listening.
Packit f0b94e
  if (!listener) {
Packit f0b94e
    mListener = nullptr;
Packit f0b94e
    return NS_OK;
Packit f0b94e
  }
Packit f0b94e
Packit f0b94e
  NS_ENSURE_STATE(mSocketInput);
Packit f0b94e
Packit f0b94e
  mListener = listener;
Packit f0b94e
  return mSocketInput->AsyncWait(this, 0, 0, GetCurrentThreadEventTarget());
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
nsresult nsFtpControlConnection::Disconnect(nsresult status) {
Packit f0b94e
  if (!mSocket) return NS_OK;  // already disconnected
Packit f0b94e
Packit f0b94e
  LOG_INFO(("FTP:(%p) CC disconnecting (%" PRIx32 ")", this,
Packit f0b94e
            static_cast<uint32_t>(status)));
Packit f0b94e
Packit f0b94e
  if (NS_FAILED(status)) {
Packit f0b94e
    // break cyclic reference!
Packit f0b94e
    mSocket->Close(status);
Packit f0b94e
    mSocket = nullptr;
Packit f0b94e
    mSocketInput->AsyncWait(nullptr, 0, 0, nullptr);  // clear any observer
Packit f0b94e
    mSocketInput = nullptr;
Packit f0b94e
    mSocketOutput = nullptr;
Packit f0b94e
  }
Packit f0b94e
Packit f0b94e
  return NS_OK;
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
nsresult nsFtpControlConnection::Write(const nsACString& command) {
Packit f0b94e
  NS_ENSURE_STATE(mSocketOutput);
Packit f0b94e
Packit f0b94e
  uint32_t len = command.Length();
Packit f0b94e
  uint32_t cnt;
Packit f0b94e
  nsresult rv = mSocketOutput->Write(command.Data(), len, &cnt);
Packit f0b94e
Packit f0b94e
  if (NS_FAILED(rv)) return rv;
Packit f0b94e
Packit f0b94e
  if (len != cnt) return NS_ERROR_FAILURE;
Packit f0b94e
Packit f0b94e
  return NS_OK;
Packit f0b94e
}