netwerk/protocol/ftp/nsFtpControlConnection.cpp

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

mercurial