Line data Source code
1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : /* This Source Code Form is subject to the terms of the Mozilla Public
3 : * License, v. 2.0. If a copy of the MPL was not distributed with this
4 : * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 :
6 : #include "nsIOService.h"
7 : #include "nsFtpControlConnection.h"
8 : #include "nsFtpProtocolHandler.h"
9 : #include "mozilla/Logging.h"
10 : #include "nsIInputStream.h"
11 : #include "nsISocketTransportService.h"
12 : #include "nsISocketTransport.h"
13 : #include "nsThreadUtils.h"
14 : #include "nsIOutputStream.h"
15 : #include "nsNetCID.h"
16 : #include <algorithm>
17 :
18 : using namespace mozilla;
19 : using namespace mozilla::net;
20 :
21 : extern LazyLogModule gFTPLog;
22 : #define LOG(args) MOZ_LOG(gFTPLog, mozilla::LogLevel::Debug, args)
23 : #define LOG_INFO(args) MOZ_LOG(gFTPLog, mozilla::LogLevel::Info, args)
24 :
25 : //
26 : // nsFtpControlConnection implementation ...
27 : //
28 :
29 0 : NS_IMPL_ISUPPORTS(nsFtpControlConnection, nsIInputStreamCallback)
30 :
31 : NS_IMETHODIMP
32 0 : nsFtpControlConnection::OnInputStreamReady(nsIAsyncInputStream *stream)
33 : {
34 : char data[4096];
35 :
36 : // Consume data whether we have a listener or not.
37 : uint64_t avail64;
38 0 : uint32_t avail = 0;
39 0 : nsresult rv = stream->Available(&avail64);
40 0 : if (NS_SUCCEEDED(rv)) {
41 0 : avail = (uint32_t)std::min(avail64, (uint64_t)sizeof(data));
42 :
43 : uint32_t n;
44 0 : rv = stream->Read(data, avail, &n);
45 0 : if (NS_SUCCEEDED(rv))
46 0 : avail = n;
47 : }
48 :
49 : // It's important that we null out mListener before calling one of its
50 : // methods as it may call WaitData, which would queue up another read.
51 :
52 0 : RefPtr<nsFtpControlConnectionListener> listener;
53 0 : listener.swap(mListener);
54 :
55 0 : if (!listener)
56 0 : return NS_OK;
57 :
58 0 : if (NS_FAILED(rv)) {
59 0 : listener->OnControlError(rv);
60 : } else {
61 0 : listener->OnControlDataAvailable(data, avail);
62 : }
63 :
64 0 : return NS_OK;
65 : }
66 :
67 0 : nsFtpControlConnection::nsFtpControlConnection(const nsACString& host,
68 0 : uint32_t port)
69 0 : : mServerType(0), mSessionId(gFtpHandler->GetSessionId())
70 0 : , mUseUTF8(false), mHost(host), mPort(port)
71 : {
72 0 : LOG_INFO(("FTP:CC created @%p", this));
73 0 : }
74 :
75 0 : nsFtpControlConnection::~nsFtpControlConnection()
76 : {
77 0 : LOG_INFO(("FTP:CC destroyed @%p", this));
78 0 : }
79 :
80 : bool
81 0 : nsFtpControlConnection::IsAlive()
82 : {
83 0 : if (!mSocket)
84 0 : return false;
85 :
86 0 : bool isAlive = false;
87 0 : mSocket->IsAlive(&isAlive);
88 0 : return isAlive;
89 : }
90 : nsresult
91 0 : nsFtpControlConnection::Connect(nsIProxyInfo* proxyInfo,
92 : nsITransportEventSink* eventSink)
93 : {
94 0 : if (mSocket)
95 0 : return NS_OK;
96 :
97 : // build our own
98 : nsresult rv;
99 : nsCOMPtr<nsISocketTransportService> sts =
100 0 : do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &rv);
101 0 : if (NS_FAILED(rv))
102 0 : return rv;
103 :
104 0 : rv = sts->CreateTransport(nullptr, 0, mHost, mPort, proxyInfo,
105 0 : getter_AddRefs(mSocket)); // the command transport
106 0 : if (NS_FAILED(rv))
107 0 : return rv;
108 :
109 0 : mSocket->SetQoSBits(gFtpHandler->GetControlQoSBits());
110 :
111 : // proxy transport events back to current thread
112 0 : if (eventSink)
113 0 : mSocket->SetEventSink(eventSink, GetCurrentThreadEventTarget());
114 :
115 : // open buffered, blocking output stream to socket. so long as commands
116 : // do not exceed 1024 bytes in length, the writing thread (the main thread)
117 : // will not block. this should be OK.
118 0 : rv = mSocket->OpenOutputStream(nsITransport::OPEN_BLOCKING, 1024, 1,
119 0 : getter_AddRefs(mSocketOutput));
120 0 : if (NS_FAILED(rv))
121 0 : return rv;
122 :
123 : // open buffered, non-blocking/asynchronous input stream to socket.
124 0 : nsCOMPtr<nsIInputStream> inStream;
125 0 : rv = mSocket->OpenInputStream(0,
126 : nsIOService::gDefaultSegmentSize,
127 : nsIOService::gDefaultSegmentCount,
128 0 : getter_AddRefs(inStream));
129 0 : if (NS_SUCCEEDED(rv))
130 0 : mSocketInput = do_QueryInterface(inStream);
131 :
132 0 : return rv;
133 : }
134 :
135 : nsresult
136 0 : nsFtpControlConnection::WaitData(nsFtpControlConnectionListener *listener)
137 : {
138 0 : LOG(("FTP:(%p) wait data [listener=%p]\n", this, listener));
139 :
140 : // If listener is null, then simply disconnect the listener. Otherwise,
141 : // ensure that we are listening.
142 0 : if (!listener) {
143 0 : mListener = nullptr;
144 0 : return NS_OK;
145 : }
146 :
147 0 : NS_ENSURE_STATE(mSocketInput);
148 :
149 0 : mListener = listener;
150 0 : return mSocketInput->AsyncWait(this, 0, 0, GetCurrentThreadEventTarget());
151 : }
152 :
153 : nsresult
154 0 : nsFtpControlConnection::Disconnect(nsresult status)
155 : {
156 0 : if (!mSocket)
157 0 : return NS_OK; // already disconnected
158 :
159 0 : LOG_INFO(("FTP:(%p) CC disconnecting (%" PRIx32 ")", this,
160 : static_cast<uint32_t>(status)));
161 :
162 0 : if (NS_FAILED(status)) {
163 : // break cyclic reference!
164 0 : mSocket->Close(status);
165 0 : mSocket = nullptr;
166 0 : mSocketInput->AsyncWait(nullptr, 0, 0, nullptr); // clear any observer
167 0 : mSocketInput = nullptr;
168 0 : mSocketOutput = nullptr;
169 : }
170 :
171 0 : return NS_OK;
172 : }
173 :
174 : nsresult
175 0 : nsFtpControlConnection::Write(const nsACString& command)
176 : {
177 0 : NS_ENSURE_STATE(mSocketOutput);
178 :
179 0 : uint32_t len = command.Length();
180 : uint32_t cnt;
181 0 : nsresult rv = mSocketOutput->Write(command.Data(), len, &cnt);
182 :
183 0 : if (NS_FAILED(rv))
184 0 : return rv;
185 :
186 0 : if (len != cnt)
187 0 : return NS_ERROR_FAILURE;
188 :
189 0 : return NS_OK;
190 : }
|