summaryrefslogtreecommitdiffstats
path: root/netwerk/protocol/ftp/nsFtpControlConnection.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'netwerk/protocol/ftp/nsFtpControlConnection.cpp')
-rw-r--r--netwerk/protocol/ftp/nsFtpControlConnection.cpp169
1 files changed, 169 insertions, 0 deletions
diff --git a/netwerk/protocol/ftp/nsFtpControlConnection.cpp b/netwerk/protocol/ftp/nsFtpControlConnection.cpp
new file mode 100644
index 0000000000..1d54ed9de2
--- /dev/null
+++ b/netwerk/protocol/ftp/nsFtpControlConnection.cpp
@@ -0,0 +1,169 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsIOService.h"
+#include "nsFtpControlConnection.h"
+#include "nsFtpProtocolHandler.h"
+#include "mozilla/Logging.h"
+#include "nsIInputStream.h"
+#include "nsISocketTransportService.h"
+#include "nsISocketTransport.h"
+#include "nsThreadUtils.h"
+#include "nsNetCID.h"
+#include "nsTArray.h"
+#include <algorithm>
+
+using namespace mozilla;
+using namespace mozilla::net;
+
+extern LazyLogModule gFTPLog;
+#define LOG(args) MOZ_LOG(gFTPLog, mozilla::LogLevel::Debug, args)
+#define LOG_INFO(args) MOZ_LOG(gFTPLog, mozilla::LogLevel::Info, args)
+
+//
+// nsFtpControlConnection implementation ...
+//
+
+NS_IMPL_ISUPPORTS(nsFtpControlConnection, nsIInputStreamCallback)
+
+NS_IMETHODIMP
+nsFtpControlConnection::OnInputStreamReady(nsIAsyncInputStream* stream) {
+ char data[4096];
+
+ // Consume data whether we have a listener or not.
+ uint64_t avail64;
+ uint32_t avail = 0;
+ nsresult rv = stream->Available(&avail64);
+ if (NS_SUCCEEDED(rv)) {
+ avail = (uint32_t)std::min(avail64, (uint64_t)sizeof(data));
+
+ uint32_t n;
+ rv = stream->Read(data, avail, &n);
+ if (NS_SUCCEEDED(rv)) 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.
+
+ RefPtr<nsFtpControlConnectionListener> listener;
+ listener.swap(mListener);
+
+ if (!listener) return NS_OK;
+
+ if (NS_FAILED(rv)) {
+ listener->OnControlError(rv);
+ } else {
+ listener->OnControlDataAvailable(data, avail);
+ }
+
+ return NS_OK;
+}
+
+nsFtpControlConnection::nsFtpControlConnection(const nsACString& host,
+ uint32_t port)
+ : mServerType(0),
+ mSuspendedWrite(0),
+ mSessionId(gFtpHandler->GetSessionId()),
+ mUseUTF8(false),
+ mHost(host),
+ mPort(port) {
+ LOG_INFO(("FTP:CC created @%p", this));
+}
+
+nsFtpControlConnection::~nsFtpControlConnection() {
+ LOG_INFO(("FTP:CC destroyed @%p", this));
+}
+
+bool nsFtpControlConnection::IsAlive() {
+ if (!mSocket) return false;
+
+ bool isAlive = false;
+ mSocket->IsAlive(&isAlive);
+ return isAlive;
+}
+nsresult nsFtpControlConnection::Connect(nsIProxyInfo* proxyInfo,
+ nsITransportEventSink* eventSink) {
+ if (mSocket) return NS_OK;
+
+ // build our own
+ nsresult rv;
+ nsCOMPtr<nsISocketTransportService> sts =
+ do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &rv);
+ if (NS_FAILED(rv)) return rv;
+
+ rv = sts->CreateTransport(nsTArray<nsCString>(), mHost, mPort, proxyInfo,
+ getter_AddRefs(mSocket)); // the command transport
+ if (NS_FAILED(rv)) return rv;
+
+ mSocket->SetQoSBits(gFtpHandler->GetControlQoSBits());
+
+ // proxy transport events back to current thread
+ if (eventSink) mSocket->SetEventSink(eventSink, GetCurrentEventTarget());
+
+ // 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,
+ getter_AddRefs(mSocketOutput));
+ if (NS_FAILED(rv)) return rv;
+
+ // open buffered, non-blocking/asynchronous input stream to socket.
+ nsCOMPtr<nsIInputStream> inStream;
+ rv = mSocket->OpenInputStream(0, nsIOService::gDefaultSegmentSize,
+ nsIOService::gDefaultSegmentCount,
+ getter_AddRefs(inStream));
+ if (NS_SUCCEEDED(rv)) mSocketInput = do_QueryInterface(inStream);
+
+ return rv;
+}
+
+nsresult 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 = nullptr;
+ return NS_OK;
+ }
+
+ NS_ENSURE_STATE(mSocketInput);
+
+ mListener = listener;
+ return mSocketInput->AsyncWait(this, 0, 0, GetCurrentEventTarget());
+}
+
+nsresult nsFtpControlConnection::Disconnect(nsresult status) {
+ if (!mSocket) return NS_OK; // already disconnected
+
+ LOG_INFO(("FTP:(%p) CC disconnecting (%" PRIx32 ")", this,
+ static_cast<uint32_t>(status)));
+
+ if (NS_FAILED(status)) {
+ // break cyclic reference!
+ mSocket->Close(status);
+ mSocket = nullptr;
+ mSocketInput->AsyncWait(nullptr, 0, 0, nullptr); // clear any observer
+ mSocketInput = nullptr;
+ mSocketOutput = nullptr;
+ }
+
+ return NS_OK;
+}
+
+nsresult nsFtpControlConnection::Write(const nsACString& command) {
+ NS_ENSURE_STATE(mSocketOutput);
+
+ uint32_t len = command.Length();
+ uint32_t 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;
+}