/src/mozilla-central/netwerk/protocol/ftp/nsFtpControlConnection.cpp
Line | Count | Source (jump to first uncovered line) |
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 | 0 | #define LOG(args) MOZ_LOG(gFTPLog, mozilla::LogLevel::Debug, args) |
23 | 0 | #define LOG_INFO(args) MOZ_LOG(gFTPLog, mozilla::LogLevel::Info, args) |
24 | | |
25 | | // |
26 | | // nsFtpControlConnection implementation ... |
27 | | // |
28 | | |
29 | | NS_IMPL_ISUPPORTS(nsFtpControlConnection, nsIInputStreamCallback) |
30 | | |
31 | | NS_IMETHODIMP |
32 | | nsFtpControlConnection::OnInputStreamReady(nsIAsyncInputStream *stream) |
33 | 0 | { |
34 | 0 | char data[4096]; |
35 | 0 |
|
36 | 0 | // Consume data whether we have a listener or not. |
37 | 0 | 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 | 0 |
|
43 | 0 | uint32_t n; |
44 | 0 | rv = stream->Read(data, avail, &n); |
45 | 0 | if (NS_SUCCEEDED(rv)) |
46 | 0 | avail = n; |
47 | 0 | } |
48 | 0 |
|
49 | 0 | // It's important that we null out mListener before calling one of its |
50 | 0 | // methods as it may call WaitData, which would queue up another read. |
51 | 0 |
|
52 | 0 | RefPtr<nsFtpControlConnectionListener> listener; |
53 | 0 | listener.swap(mListener); |
54 | 0 |
|
55 | 0 | if (!listener) |
56 | 0 | return NS_OK; |
57 | 0 | |
58 | 0 | if (NS_FAILED(rv)) { |
59 | 0 | listener->OnControlError(rv); |
60 | 0 | } else { |
61 | 0 | listener->OnControlDataAvailable(data, avail); |
62 | 0 | } |
63 | 0 |
|
64 | 0 | return NS_OK; |
65 | 0 | } |
66 | | |
67 | | nsFtpControlConnection::nsFtpControlConnection(const nsACString& host, |
68 | | uint32_t port) |
69 | | : mServerType(0) |
70 | | , mSuspendedWrite(0) |
71 | | , mSessionId(gFtpHandler->GetSessionId()) |
72 | | , mUseUTF8(false) |
73 | | , mHost(host) |
74 | | , mPort(port) |
75 | 0 | { |
76 | 0 | LOG_INFO(("FTP:CC created @%p", this)); |
77 | 0 | } |
78 | | |
79 | | nsFtpControlConnection::~nsFtpControlConnection() |
80 | 0 | { |
81 | 0 | LOG_INFO(("FTP:CC destroyed @%p", this)); |
82 | 0 | } |
83 | | |
84 | | bool |
85 | | nsFtpControlConnection::IsAlive() |
86 | 0 | { |
87 | 0 | if (!mSocket) |
88 | 0 | return false; |
89 | 0 | |
90 | 0 | bool isAlive = false; |
91 | 0 | mSocket->IsAlive(&isAlive); |
92 | 0 | return isAlive; |
93 | 0 | } |
94 | | nsresult |
95 | | nsFtpControlConnection::Connect(nsIProxyInfo* proxyInfo, |
96 | | nsITransportEventSink* eventSink) |
97 | 0 | { |
98 | 0 | if (mSocket) |
99 | 0 | return NS_OK; |
100 | 0 | |
101 | 0 | // build our own |
102 | 0 | nsresult rv; |
103 | 0 | nsCOMPtr<nsISocketTransportService> sts = |
104 | 0 | do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &rv); |
105 | 0 | if (NS_FAILED(rv)) |
106 | 0 | return rv; |
107 | 0 | |
108 | 0 | rv = sts->CreateTransport(nullptr, 0, mHost, mPort, proxyInfo, |
109 | 0 | getter_AddRefs(mSocket)); // the command transport |
110 | 0 | if (NS_FAILED(rv)) |
111 | 0 | return rv; |
112 | 0 | |
113 | 0 | mSocket->SetQoSBits(gFtpHandler->GetControlQoSBits()); |
114 | 0 |
|
115 | 0 | // proxy transport events back to current thread |
116 | 0 | if (eventSink) |
117 | 0 | mSocket->SetEventSink(eventSink, GetCurrentThreadEventTarget()); |
118 | 0 |
|
119 | 0 | // open buffered, blocking output stream to socket. so long as commands |
120 | 0 | // do not exceed 1024 bytes in length, the writing thread (the main thread) |
121 | 0 | // will not block. this should be OK. |
122 | 0 | rv = mSocket->OpenOutputStream(nsITransport::OPEN_BLOCKING, 1024, 1, |
123 | 0 | getter_AddRefs(mSocketOutput)); |
124 | 0 | if (NS_FAILED(rv)) |
125 | 0 | return rv; |
126 | 0 | |
127 | 0 | // open buffered, non-blocking/asynchronous input stream to socket. |
128 | 0 | nsCOMPtr<nsIInputStream> inStream; |
129 | 0 | rv = mSocket->OpenInputStream(0, |
130 | 0 | nsIOService::gDefaultSegmentSize, |
131 | 0 | nsIOService::gDefaultSegmentCount, |
132 | 0 | getter_AddRefs(inStream)); |
133 | 0 | if (NS_SUCCEEDED(rv)) |
134 | 0 | mSocketInput = do_QueryInterface(inStream); |
135 | 0 |
|
136 | 0 | return rv; |
137 | 0 | } |
138 | | |
139 | | nsresult |
140 | | nsFtpControlConnection::WaitData(nsFtpControlConnectionListener *listener) |
141 | 0 | { |
142 | 0 | LOG(("FTP:(%p) wait data [listener=%p]\n", this, listener)); |
143 | 0 |
|
144 | 0 | // If listener is null, then simply disconnect the listener. Otherwise, |
145 | 0 | // ensure that we are listening. |
146 | 0 | if (!listener) { |
147 | 0 | mListener = nullptr; |
148 | 0 | return NS_OK; |
149 | 0 | } |
150 | 0 | |
151 | 0 | NS_ENSURE_STATE(mSocketInput); |
152 | 0 |
|
153 | 0 | mListener = listener; |
154 | 0 | return mSocketInput->AsyncWait(this, 0, 0, GetCurrentThreadEventTarget()); |
155 | 0 | } |
156 | | |
157 | | nsresult |
158 | | nsFtpControlConnection::Disconnect(nsresult status) |
159 | 0 | { |
160 | 0 | if (!mSocket) |
161 | 0 | return NS_OK; // already disconnected |
162 | 0 | |
163 | 0 | LOG_INFO(("FTP:(%p) CC disconnecting (%" PRIx32 ")", this, |
164 | 0 | static_cast<uint32_t>(status))); |
165 | 0 |
|
166 | 0 | if (NS_FAILED(status)) { |
167 | 0 | // break cyclic reference! |
168 | 0 | mSocket->Close(status); |
169 | 0 | mSocket = nullptr; |
170 | 0 | mSocketInput->AsyncWait(nullptr, 0, 0, nullptr); // clear any observer |
171 | 0 | mSocketInput = nullptr; |
172 | 0 | mSocketOutput = nullptr; |
173 | 0 | } |
174 | 0 |
|
175 | 0 | return NS_OK; |
176 | 0 | } |
177 | | |
178 | | nsresult |
179 | | nsFtpControlConnection::Write(const nsACString& command) |
180 | 0 | { |
181 | 0 | NS_ENSURE_STATE(mSocketOutput); |
182 | 0 |
|
183 | 0 | uint32_t len = command.Length(); |
184 | 0 | uint32_t cnt; |
185 | 0 | nsresult rv = mSocketOutput->Write(command.Data(), len, &cnt); |
186 | 0 |
|
187 | 0 | if (NS_FAILED(rv)) |
188 | 0 | return rv; |
189 | 0 | |
190 | 0 | if (len != cnt) |
191 | 0 | return NS_ERROR_FAILURE; |
192 | 0 | |
193 | 0 | return NS_OK; |
194 | 0 | } |