1 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* vim: set sw=2 ts=8 et tw=80 : */
3 :
4 : /* ***** BEGIN LICENSE BLOCK *****
5 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
6 : *
7 : * The contents of this file are subject to the Mozilla Public License Version
8 : * 1.1 (the "License"); you may not use this file except in compliance with
9 : * the License. You may obtain a copy of the License at
10 : * http://www.mozilla.org/MPL/
11 : *
12 : * Software distributed under the License is distributed on an "AS IS" basis,
13 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
14 : * for the specific language governing rights and limitations under the
15 : * License.
16 : *
17 : * The Original Code is mozilla.org code.
18 : *
19 : * The Initial Developer of the Original Code is
20 : * The Mozilla Foundation
21 : * Portions created by the Initial Developer are Copyright (C) 2010
22 : * the Initial Developer. All Rights Reserved.
23 : *
24 : * Contributor(s):
25 : * Alon Zakai <azakai@mozilla.com>
26 : * Josh Matthews <josh@joshmatthews.net>
27 : *
28 : * Alternatively, the contents of this file may be used under the terms of
29 : * either the GNU General Public License Version 2 or later (the "GPL"), or
30 : * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
31 : * in which case the provisions of the GPL or the LGPL are applicable instead
32 : * of those above. If you wish to allow use of your version of this file only
33 : * under the terms of either the GPL or the LGPL, and not to allow others to
34 : * use your version of this file under the terms of the MPL, indicate your
35 : * decision by deleting the provisions above and replace them with the notice
36 : * and other provisions required by the GPL or the LGPL. If you do not delete
37 : * the provisions above, a recipient may use your version of this file under
38 : * the terms of any one of the MPL, the GPL or the LGPL.
39 : *
40 : * ***** END LICENSE BLOCK ***** */
41 :
42 : #include "mozilla/net/NeckoChild.h"
43 : #include "mozilla/net/FTPChannelChild.h"
44 : #include "nsFtpProtocolHandler.h"
45 :
46 : #include "nsStringStream.h"
47 : #include "nsMimeTypes.h"
48 : #include "nsNetUtil.h"
49 : #include "nsIURIFixup.h"
50 : #include "nsCDefaultURIFixup.h"
51 :
52 : #undef LOG
53 : #define LOG(args) PR_LOG(gFTPLog, PR_LOG_DEBUG, args)
54 :
55 : namespace mozilla {
56 : namespace net {
57 :
58 0 : FTPChannelChild::FTPChannelChild(nsIURI* uri)
59 : : mIPCOpen(false)
60 : , mEventQ(static_cast<nsIFTPChannel*>(this))
61 : , mCanceled(false)
62 : , mSuspendCount(0)
63 : , mIsPending(false)
64 : , mWasOpened(false)
65 : , mLastModifiedTime(0)
66 0 : , mStartPos(0)
67 : {
68 0 : LOG(("Creating FTPChannelChild @%x\n", this));
69 : // grab a reference to the handler to ensure that it doesn't go away.
70 0 : NS_ADDREF(gFtpHandler);
71 0 : SetURI(uri);
72 0 : }
73 :
74 0 : FTPChannelChild::~FTPChannelChild()
75 : {
76 0 : LOG(("Destroying FTPChannelChild @%x\n", this));
77 0 : gFtpHandler->Release();
78 0 : }
79 :
80 : void
81 0 : FTPChannelChild::AddIPDLReference()
82 : {
83 0 : NS_ABORT_IF_FALSE(!mIPCOpen, "Attempt to retain more than one IPDL reference");
84 0 : mIPCOpen = true;
85 0 : AddRef();
86 0 : }
87 :
88 : void
89 0 : FTPChannelChild::ReleaseIPDLReference()
90 : {
91 0 : NS_ABORT_IF_FALSE(mIPCOpen, "Attempt to release nonexistent IPDL reference");
92 0 : mIPCOpen = false;
93 0 : Release();
94 0 : }
95 :
96 : //-----------------------------------------------------------------------------
97 : // FTPChannelChild::nsISupports
98 : //-----------------------------------------------------------------------------
99 :
100 0 : NS_IMPL_ISUPPORTS_INHERITED5(FTPChannelChild,
101 : nsBaseChannel,
102 : nsIFTPChannel,
103 : nsIUploadChannel,
104 : nsIResumableChannel,
105 : nsIProxiedChannel,
106 : nsIChildChannel)
107 :
108 : //-----------------------------------------------------------------------------
109 :
110 : NS_IMETHODIMP
111 0 : FTPChannelChild::GetLastModifiedTime(PRTime* lastModifiedTime)
112 : {
113 0 : *lastModifiedTime = mLastModifiedTime;
114 0 : return NS_OK;
115 : }
116 :
117 : NS_IMETHODIMP
118 0 : FTPChannelChild::SetLastModifiedTime(PRTime lastModifiedTime)
119 : {
120 0 : return NS_ERROR_NOT_AVAILABLE;
121 : }
122 :
123 : NS_IMETHODIMP
124 0 : FTPChannelChild::ResumeAt(PRUint64 aStartPos, const nsACString& aEntityID)
125 : {
126 0 : NS_ENSURE_TRUE(!mIsPending, NS_ERROR_IN_PROGRESS);
127 0 : mStartPos = aStartPos;
128 0 : mEntityID = aEntityID;
129 0 : return NS_OK;
130 : }
131 :
132 : NS_IMETHODIMP
133 0 : FTPChannelChild::GetEntityID(nsACString& entityID)
134 : {
135 0 : entityID = mEntityID;
136 0 : return NS_OK;
137 : }
138 :
139 : NS_IMETHODIMP
140 0 : FTPChannelChild::GetProxyInfo(nsIProxyInfo** aProxyInfo)
141 : {
142 0 : DROP_DEAD();
143 : }
144 :
145 : NS_IMETHODIMP
146 0 : FTPChannelChild::SetUploadStream(nsIInputStream* stream,
147 : const nsACString& contentType,
148 : PRInt32 contentLength)
149 : {
150 0 : NS_ENSURE_TRUE(!mIsPending, NS_ERROR_IN_PROGRESS);
151 0 : mUploadStream = stream;
152 : // NOTE: contentLength is intentionally ignored here.
153 0 : return NS_OK;
154 : }
155 :
156 : NS_IMETHODIMP
157 0 : FTPChannelChild::GetUploadStream(nsIInputStream** stream)
158 : {
159 0 : NS_ENSURE_ARG_POINTER(stream);
160 0 : *stream = mUploadStream;
161 0 : NS_IF_ADDREF(*stream);
162 0 : return NS_OK;
163 : }
164 :
165 : //-----------------------------------------------------------------------------
166 :
167 : NS_IMETHODIMP
168 0 : FTPChannelChild::AsyncOpen(::nsIStreamListener* listener, nsISupports* aContext)
169 : {
170 0 : LOG(("FTPChannelChild::AsyncOpen [this=%x]\n", this));
171 :
172 0 : NS_ENSURE_TRUE((gNeckoChild), NS_ERROR_FAILURE);
173 0 : NS_ENSURE_ARG_POINTER(listener);
174 0 : NS_ENSURE_TRUE(!mIsPending, NS_ERROR_IN_PROGRESS);
175 0 : NS_ENSURE_TRUE(!mWasOpened, NS_ERROR_ALREADY_OPENED);
176 :
177 : // Port checked in parent, but duplicate here so we can return with error
178 : // immediately, as we've done since before e10s.
179 : nsresult rv;
180 0 : rv = NS_CheckPortSafety(nsBaseChannel::URI()); // Need to disambiguate,
181 : // because in the child ipdl,
182 : // a typedef URI is defined...
183 0 : if (NS_FAILED(rv))
184 0 : return rv;
185 :
186 : // FIXME: like bug 558623, merge constructor+SendAsyncOpen into 1 IPC msg
187 0 : gNeckoChild->SendPFTPChannelConstructor(this);
188 0 : mListener = listener;
189 0 : mListenerContext = aContext;
190 :
191 : // add ourselves to the load group.
192 0 : if (mLoadGroup)
193 0 : mLoadGroup->AddRequest(this, nsnull);
194 :
195 : SendAsyncOpen(nsBaseChannel::URI(), mStartPos, mEntityID,
196 0 : IPC::InputStream(mUploadStream));
197 :
198 : // The socket transport layer in the chrome process now has a logical ref to
199 : // us until OnStopRequest is called.
200 0 : AddIPDLReference();
201 :
202 0 : mIsPending = true;
203 0 : mWasOpened = true;
204 :
205 0 : return rv;
206 : }
207 :
208 : NS_IMETHODIMP
209 0 : FTPChannelChild::IsPending(bool* result)
210 : {
211 0 : *result = mIsPending;
212 0 : return NS_OK;
213 : }
214 :
215 : nsresult
216 0 : FTPChannelChild::OpenContentStream(bool async,
217 : nsIInputStream** stream,
218 : nsIChannel** channel)
219 : {
220 0 : NS_RUNTIMEABORT("FTPChannel*Child* should never have OpenContentStream called!");
221 0 : return NS_OK;
222 : }
223 :
224 : //-----------------------------------------------------------------------------
225 : // FTPChannelChild::PFTPChannelChild
226 : //-----------------------------------------------------------------------------
227 :
228 : class FTPStartRequestEvent : public ChannelEvent
229 0 : {
230 : public:
231 0 : FTPStartRequestEvent(FTPChannelChild* aChild, const PRInt32& aContentLength,
232 : const nsCString& aContentType, const PRTime& aLastModified,
233 : const nsCString& aEntityID, const IPC::URI& aURI)
234 : : mChild(aChild), mContentLength(aContentLength), mContentType(aContentType),
235 0 : mLastModified(aLastModified), mEntityID(aEntityID), mURI(aURI) {}
236 0 : void Run() { mChild->DoOnStartRequest(mContentLength, mContentType,
237 0 : mLastModified, mEntityID, mURI); }
238 : private:
239 : FTPChannelChild* mChild;
240 : PRInt32 mContentLength;
241 : nsCString mContentType;
242 : PRTime mLastModified;
243 : nsCString mEntityID;
244 : IPC::URI mURI;
245 : };
246 :
247 : bool
248 0 : FTPChannelChild::RecvOnStartRequest(const PRInt32& aContentLength,
249 : const nsCString& aContentType,
250 : const PRTime& aLastModified,
251 : const nsCString& aEntityID,
252 : const IPC::URI& aURI)
253 : {
254 0 : if (mEventQ.ShouldEnqueue()) {
255 : mEventQ.Enqueue(new FTPStartRequestEvent(this, aContentLength, aContentType,
256 0 : aLastModified, aEntityID, aURI));
257 : } else {
258 : DoOnStartRequest(aContentLength, aContentType, aLastModified,
259 0 : aEntityID, aURI);
260 : }
261 0 : return true;
262 : }
263 :
264 : void
265 0 : FTPChannelChild::DoOnStartRequest(const PRInt32& aContentLength,
266 : const nsCString& aContentType,
267 : const PRTime& aLastModified,
268 : const nsCString& aEntityID,
269 : const IPC::URI& aURI)
270 : {
271 0 : LOG(("FTPChannelChild::RecvOnStartRequest [this=%x]\n", this));
272 :
273 0 : SetContentLength(aContentLength);
274 0 : SetContentType(aContentType);
275 0 : mLastModifiedTime = aLastModified;
276 0 : mEntityID = aEntityID;
277 :
278 0 : nsCString spec;
279 0 : nsCOMPtr<nsIURI> uri(aURI);
280 0 : uri->GetSpec(spec);
281 0 : nsBaseChannel::URI()->SetSpec(spec);
282 :
283 0 : AutoEventEnqueuer ensureSerialDispatch(mEventQ);
284 0 : nsresult rv = mListener->OnStartRequest(this, mListenerContext);
285 0 : if (NS_FAILED(rv))
286 0 : Cancel(rv);
287 0 : }
288 :
289 : class FTPDataAvailableEvent : public ChannelEvent
290 0 : {
291 : public:
292 0 : FTPDataAvailableEvent(FTPChannelChild* aChild, const nsCString& aData,
293 : const PRUint32& aOffset, const PRUint32& aCount)
294 0 : : mChild(aChild), mData(aData), mOffset(aOffset), mCount(aCount) {}
295 0 : void Run() { mChild->DoOnDataAvailable(mData, mOffset, mCount); }
296 : private:
297 : FTPChannelChild* mChild;
298 : nsCString mData;
299 : PRUint32 mOffset, mCount;
300 : };
301 :
302 : bool
303 0 : FTPChannelChild::RecvOnDataAvailable(const nsCString& data,
304 : const PRUint32& offset,
305 : const PRUint32& count)
306 : {
307 0 : if (mEventQ.ShouldEnqueue()) {
308 0 : mEventQ.Enqueue(new FTPDataAvailableEvent(this, data, offset, count));
309 : } else {
310 0 : DoOnDataAvailable(data, offset, count);
311 : }
312 0 : return true;
313 : }
314 :
315 : void
316 0 : FTPChannelChild::DoOnDataAvailable(const nsCString& data,
317 : const PRUint32& offset,
318 : const PRUint32& count)
319 : {
320 0 : LOG(("FTPChannelChild::RecvOnDataAvailable [this=%x]\n", this));
321 :
322 0 : if (mCanceled)
323 0 : return;
324 :
325 : // NOTE: the OnDataAvailable contract requires the client to read all the data
326 : // in the inputstream. This code relies on that ('data' will go away after
327 : // this function). Apparently the previous, non-e10s behavior was to actually
328 : // support only reading part of the data, allowing later calls to read the
329 : // rest.
330 0 : nsCOMPtr<nsIInputStream> stringStream;
331 0 : nsresult rv = NS_NewByteInputStream(getter_AddRefs(stringStream),
332 : data.get(),
333 : count,
334 0 : NS_ASSIGNMENT_DEPEND);
335 0 : if (NS_FAILED(rv)) {
336 0 : Cancel(rv);
337 : return;
338 : }
339 :
340 0 : AutoEventEnqueuer ensureSerialDispatch(mEventQ);
341 0 : rv = mListener->OnDataAvailable(this, mListenerContext,
342 0 : stringStream, offset, count);
343 0 : if (NS_FAILED(rv))
344 0 : Cancel(rv);
345 0 : stringStream->Close();
346 : }
347 :
348 : class FTPStopRequestEvent : public ChannelEvent
349 0 : {
350 : public:
351 0 : FTPStopRequestEvent(FTPChannelChild* aChild, const nsresult& aStatusCode)
352 0 : : mChild(aChild), mStatusCode(aStatusCode) {}
353 0 : void Run() { mChild->DoOnStopRequest(mStatusCode); }
354 : private:
355 : FTPChannelChild* mChild;
356 : nsresult mStatusCode;
357 : };
358 :
359 : bool
360 0 : FTPChannelChild::RecvOnStopRequest(const nsresult& statusCode)
361 : {
362 0 : if (mEventQ.ShouldEnqueue()) {
363 0 : mEventQ.Enqueue(new FTPStopRequestEvent(this, statusCode));
364 : } else {
365 0 : DoOnStopRequest(statusCode);
366 : }
367 0 : return true;
368 : }
369 :
370 : void
371 0 : FTPChannelChild::DoOnStopRequest(const nsresult& statusCode)
372 : {
373 0 : LOG(("FTPChannelChild::RecvOnStopRequest [this=%x status=%u]\n",
374 : this, statusCode));
375 :
376 0 : if (!mCanceled)
377 0 : mStatus = statusCode;
378 :
379 : { // Ensure that all queued ipdl events are dispatched before
380 : // we initiate protocol deletion below.
381 0 : mIsPending = false;
382 0 : AutoEventEnqueuer ensureSerialDispatch(mEventQ);
383 0 : (void)mListener->OnStopRequest(this, mListenerContext, statusCode);
384 0 : mListener = nsnull;
385 0 : mListenerContext = nsnull;
386 :
387 0 : if (mLoadGroup)
388 0 : mLoadGroup->RemoveRequest(this, nsnull, statusCode);
389 : }
390 :
391 : // This calls NeckoChild::DeallocPFTPChannel(), which deletes |this| if IPDL
392 : // holds the last reference. Don't rely on |this| existing after here!
393 0 : Send__delete__(this);
394 0 : }
395 :
396 : class FTPFailedAsyncOpenEvent : public ChannelEvent
397 0 : {
398 : public:
399 0 : FTPFailedAsyncOpenEvent(FTPChannelChild* aChild, nsresult aStatus)
400 0 : : mChild(aChild), mStatus(aStatus) {}
401 0 : void Run() { mChild->DoFailedAsyncOpen(mStatus); }
402 : private:
403 : FTPChannelChild* mChild;
404 : nsresult mStatus;
405 : };
406 :
407 : bool
408 0 : FTPChannelChild::RecvFailedAsyncOpen(const nsresult& statusCode)
409 : {
410 0 : if (mEventQ.ShouldEnqueue()) {
411 0 : mEventQ.Enqueue(new FTPFailedAsyncOpenEvent(this, statusCode));
412 : } else {
413 0 : DoFailedAsyncOpen(statusCode);
414 : }
415 0 : return true;
416 : }
417 :
418 : void
419 0 : FTPChannelChild::DoFailedAsyncOpen(const nsresult& statusCode)
420 : {
421 0 : mStatus = statusCode;
422 :
423 0 : if (mLoadGroup)
424 0 : mLoadGroup->RemoveRequest(this, nsnull, statusCode);
425 :
426 0 : if (mListener) {
427 0 : mListener->OnStartRequest(this, mListenerContext);
428 0 : mIsPending = false;
429 0 : mListener->OnStopRequest(this, mListenerContext, statusCode);
430 : } else {
431 0 : mIsPending = false;
432 : }
433 :
434 0 : mListener = nsnull;
435 0 : mListenerContext = nsnull;
436 :
437 0 : if (mIPCOpen)
438 0 : Send__delete__(this);
439 0 : }
440 :
441 : class FTPDeleteSelfEvent : public ChannelEvent
442 0 : {
443 : public:
444 0 : FTPDeleteSelfEvent(FTPChannelChild* aChild)
445 0 : : mChild(aChild) {}
446 0 : void Run() { mChild->DoDeleteSelf(); }
447 : private:
448 : FTPChannelChild* mChild;
449 : };
450 :
451 : bool
452 0 : FTPChannelChild::RecvDeleteSelf()
453 : {
454 0 : if (mEventQ.ShouldEnqueue()) {
455 0 : mEventQ.Enqueue(new FTPDeleteSelfEvent(this));
456 : } else {
457 0 : DoDeleteSelf();
458 : }
459 0 : return true;
460 : }
461 :
462 : void
463 0 : FTPChannelChild::DoDeleteSelf()
464 : {
465 0 : if (mIPCOpen)
466 0 : Send__delete__(this);
467 0 : }
468 :
469 : NS_IMETHODIMP
470 0 : FTPChannelChild::Cancel(nsresult status)
471 : {
472 0 : if (mCanceled)
473 0 : return NS_OK;
474 :
475 0 : mCanceled = true;
476 0 : mStatus = status;
477 0 : if (mIPCOpen)
478 0 : SendCancel(status);
479 0 : return NS_OK;
480 : }
481 :
482 : NS_IMETHODIMP
483 0 : FTPChannelChild::Suspend()
484 : {
485 0 : NS_ENSURE_TRUE(mIPCOpen, NS_ERROR_NOT_AVAILABLE);
486 0 : if (!mSuspendCount++) {
487 0 : SendSuspend();
488 0 : mEventQ.Suspend();
489 : }
490 0 : return NS_OK;
491 : }
492 :
493 : nsresult
494 0 : FTPChannelChild::AsyncCall(void (FTPChannelChild::*funcPtr)(),
495 : nsRunnableMethod<FTPChannelChild> **retval)
496 : {
497 : nsresult rv;
498 :
499 0 : nsRefPtr<nsRunnableMethod<FTPChannelChild> > event = NS_NewRunnableMethod(this, funcPtr);
500 0 : rv = NS_DispatchToCurrentThread(event);
501 0 : if (NS_SUCCEEDED(rv) && retval) {
502 0 : *retval = event;
503 : }
504 :
505 0 : return rv;
506 : }
507 :
508 : void
509 0 : FTPChannelChild::CompleteResume()
510 : {
511 0 : mEventQ.Resume();
512 0 : }
513 :
514 : NS_IMETHODIMP
515 0 : FTPChannelChild::Resume()
516 : {
517 0 : NS_ENSURE_TRUE(mIPCOpen, NS_ERROR_NOT_AVAILABLE);
518 :
519 0 : if (!--mSuspendCount) {
520 0 : SendResume();
521 0 : AsyncCall(&FTPChannelChild::CompleteResume);
522 : }
523 0 : return NS_OK;
524 : }
525 :
526 : //-----------------------------------------------------------------------------
527 : // FTPChannelChild::nsIChildChannel
528 : //-----------------------------------------------------------------------------
529 :
530 : NS_IMETHODIMP
531 0 : FTPChannelChild::ConnectParent(PRUint32 id)
532 : {
533 : // The socket transport in the chrome process now holds a logical ref to us
534 : // until OnStopRequest, or we do a redirect, or we hit an IPDL error.
535 0 : AddIPDLReference();
536 :
537 0 : if (!gNeckoChild->SendPFTPChannelConstructor(this))
538 0 : return NS_ERROR_FAILURE;
539 :
540 0 : if (!SendConnectChannel(id))
541 0 : return NS_ERROR_FAILURE;
542 :
543 0 : return NS_OK;
544 : }
545 :
546 : NS_IMETHODIMP
547 0 : FTPChannelChild::CompleteRedirectSetup(nsIStreamListener *listener,
548 : nsISupports *aContext)
549 : {
550 0 : LOG(("FTPChannelChild::CompleteRedirectSetup [this=%x]\n", this));
551 :
552 0 : NS_ENSURE_TRUE(!mIsPending, NS_ERROR_IN_PROGRESS);
553 0 : NS_ENSURE_TRUE(!mWasOpened, NS_ERROR_ALREADY_OPENED);
554 :
555 0 : mIsPending = true;
556 0 : mWasOpened = true;
557 0 : mListener = listener;
558 0 : mListenerContext = aContext;
559 :
560 : // add ourselves to the load group.
561 0 : if (mLoadGroup)
562 0 : mLoadGroup->AddRequest(this, nsnull);
563 :
564 : // We already have an open IPDL connection to the parent. If on-modify-request
565 : // listeners or load group observers canceled us, let the parent handle it
566 : // and send it back to us naturally.
567 0 : return NS_OK;
568 : }
569 :
570 : } // namespace net
571 : } // namespace mozilla
572 :
|