Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/netwerk/protocol/ftp/FTPChannelParent.cpp
Line
Count
Source (jump to first uncovered line)
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
/* This Source Code Form is subject to the terms of the Mozilla Public
5
 * License, v. 2.0. If a copy of the MPL was not distributed with this
6
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7
8
#include "mozilla/net/FTPChannelParent.h"
9
#include "nsStringStream.h"
10
#include "mozilla/net/ChannelEventQueue.h"
11
#include "mozilla/dom/TabParent.h"
12
#include "nsFTPChannel.h"
13
#include "nsNetCID.h"
14
#include "nsNetUtil.h"
15
#include "nsQueryObject.h"
16
#include "nsFtpProtocolHandler.h"
17
#include "nsIAuthPrompt.h"
18
#include "nsIAuthPromptProvider.h"
19
#include "nsIEncodedChannel.h"
20
#include "nsIHttpChannelInternal.h"
21
#include "nsIForcePendingChannel.h"
22
#include "mozilla/ipc/IPCStreamUtils.h"
23
#include "mozilla/ipc/URIUtils.h"
24
#include "mozilla/Unused.h"
25
#include "SerializedLoadContext.h"
26
#include "nsIContentPolicy.h"
27
#include "mozilla/ipc/BackgroundUtils.h"
28
#include "mozilla/LoadInfo.h"
29
#include "mozilla/dom/ContentParent.h"
30
31
using namespace mozilla::dom;
32
using namespace mozilla::ipc;
33
34
#undef LOG
35
0
#define LOG(args) MOZ_LOG(gFTPLog, mozilla::LogLevel::Debug, args)
36
37
namespace mozilla {
38
namespace net {
39
40
FTPChannelParent::FTPChannelParent(const PBrowserOrId& aIframeEmbedding,
41
                                   nsILoadContext* aLoadContext,
42
                                   PBOverrideStatus aOverrideStatus)
43
  : mIPCClosed(false)
44
  , mLoadContext(aLoadContext)
45
  , mPBOverride(aOverrideStatus)
46
  , mStatus(NS_OK)
47
  , mDivertingFromChild(false)
48
  , mDivertedOnStartRequest(false)
49
  , mSuspendedForDiversion(false)
50
  , mUseUTF8(false)
51
0
{
52
0
  nsIProtocolHandler* handler;
53
0
  CallGetService(NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX "ftp", &handler);
54
0
  MOZ_ASSERT(handler, "no ftp handler");
55
0
56
0
  if (aIframeEmbedding.type() == PBrowserOrId::TPBrowserParent) {
57
0
    mTabParent = static_cast<dom::TabParent*>(aIframeEmbedding.get_PBrowserParent());
58
0
  }
59
0
60
0
  mEventQ = new ChannelEventQueue(static_cast<nsIParentChannel*>(this));
61
0
}
62
63
FTPChannelParent::~FTPChannelParent()
64
0
{
65
0
  gFtpHandler->Release();
66
0
}
67
68
void
69
FTPChannelParent::ActorDestroy(ActorDestroyReason why)
70
0
{
71
0
  // We may still have refcount>0 if the channel hasn't called OnStopRequest
72
0
  // yet, but we must not send any more msgs to child.
73
0
  mIPCClosed = true;
74
0
}
75
76
//-----------------------------------------------------------------------------
77
// FTPChannelParent::nsISupports
78
//-----------------------------------------------------------------------------
79
80
NS_IMPL_ISUPPORTS(FTPChannelParent,
81
                  nsIStreamListener,
82
                  nsIParentChannel,
83
                  nsIInterfaceRequestor,
84
                  nsIRequestObserver,
85
                  nsIChannelEventSink,
86
                  nsIFTPChannelParentInternal)
87
88
//-----------------------------------------------------------------------------
89
// FTPChannelParent::PFTPChannelParent
90
//-----------------------------------------------------------------------------
91
92
//-----------------------------------------------------------------------------
93
// FTPChannelParent methods
94
//-----------------------------------------------------------------------------
95
96
bool
97
FTPChannelParent::Init(const FTPChannelCreationArgs& aArgs)
98
0
{
99
0
  switch (aArgs.type()) {
100
0
  case FTPChannelCreationArgs::TFTPChannelOpenArgs:
101
0
  {
102
0
    const FTPChannelOpenArgs& a = aArgs.get_FTPChannelOpenArgs();
103
0
    return DoAsyncOpen(a.uri(), a.startPos(), a.entityID(), a.uploadStream(),
104
0
                       a.loadInfo(), a.loadFlags());
105
0
  }
106
0
  case FTPChannelCreationArgs::TFTPChannelConnectArgs:
107
0
  {
108
0
    const FTPChannelConnectArgs& cArgs = aArgs.get_FTPChannelConnectArgs();
109
0
    return ConnectChannel(cArgs.channelId());
110
0
  }
111
0
  default:
112
0
    MOZ_ASSERT_UNREACHABLE("unknown open type");
113
0
    return false;
114
0
  }
115
0
}
116
117
bool
118
FTPChannelParent::DoAsyncOpen(const URIParams& aURI,
119
                              const uint64_t& aStartPos,
120
                              const nsCString& aEntityID,
121
                              const OptionalIPCStream& aUploadStream,
122
                              const OptionalLoadInfoArgs& aLoadInfoArgs,
123
                              const uint32_t& aLoadFlags)
124
0
{
125
0
  nsresult rv;
126
0
127
0
  nsCOMPtr<nsIURI> uri = DeserializeURI(aURI);
128
0
  if (!uri)
129
0
      return false;
130
0
131
#ifdef DEBUG
132
  LOG(("FTPChannelParent DoAsyncOpen [this=%p uri=%s]\n",
133
       this, uri->GetSpecOrDefault().get()));
134
#endif
135
136
0
  nsCOMPtr<nsIIOService> ios(do_GetIOService(&rv));
137
0
  if (NS_FAILED(rv)) {
138
0
    return SendFailedAsyncOpen(rv);
139
0
  }
140
0
141
0
  nsCOMPtr<nsILoadInfo> loadInfo;
142
0
  rv = mozilla::ipc::LoadInfoArgsToLoadInfo(aLoadInfoArgs,
143
0
                                            getter_AddRefs(loadInfo));
144
0
  if (NS_FAILED(rv)) {
145
0
    return SendFailedAsyncOpen(rv);
146
0
  }
147
0
148
0
  OriginAttributes attrs;
149
0
  rv = loadInfo->GetOriginAttributes(&attrs);
150
0
  if (NS_FAILED(rv)) {
151
0
    return SendFailedAsyncOpen(rv);
152
0
  }
153
0
154
0
  nsCOMPtr<nsIChannel> chan;
155
0
  rv = NS_NewChannelInternal(getter_AddRefs(chan), uri, loadInfo,
156
0
                             nullptr, nullptr, nullptr,
157
0
                             aLoadFlags, ios);
158
0
159
0
  if (NS_FAILED(rv))
160
0
    return SendFailedAsyncOpen(rv);
161
0
162
0
  mChannel = chan;
163
0
164
0
  // later on mChannel may become an HTTP channel (we'll be redirected to one
165
0
  // if we're using a proxy), but for now this is safe
166
0
  nsFtpChannel* ftpChan = static_cast<nsFtpChannel*>(mChannel.get());
167
0
168
0
  if (mPBOverride != kPBOverride_Unset) {
169
0
    ftpChan->SetPrivate(mPBOverride == kPBOverride_Private ? true : false);
170
0
  }
171
0
  rv = ftpChan->SetNotificationCallbacks(this);
172
0
  if (NS_FAILED(rv))
173
0
    return SendFailedAsyncOpen(rv);
174
0
175
0
  nsCOMPtr<nsIInputStream> upload = DeserializeIPCStream(aUploadStream);
176
0
  if (upload) {
177
0
    // contentType and contentLength are ignored
178
0
    rv = ftpChan->SetUploadStream(upload, EmptyCString(), 0);
179
0
    if (NS_FAILED(rv))
180
0
      return SendFailedAsyncOpen(rv);
181
0
  }
182
0
183
0
  rv = ftpChan->ResumeAt(aStartPos, aEntityID);
184
0
  if (NS_FAILED(rv))
185
0
    return SendFailedAsyncOpen(rv);
186
0
187
0
  if (loadInfo && loadInfo->GetEnforceSecurity()) {
188
0
    rv = ftpChan->AsyncOpen2(this);
189
0
  }
190
0
  else {
191
0
    rv = ftpChan->AsyncOpen(this, nullptr);
192
0
  }
193
0
194
0
  if (NS_FAILED(rv))
195
0
    return SendFailedAsyncOpen(rv);
196
0
197
0
  return true;
198
0
}
199
200
bool
201
FTPChannelParent::ConnectChannel(const uint32_t& channelId)
202
0
{
203
0
  nsresult rv;
204
0
205
0
  LOG(("Looking for a registered channel [this=%p, id=%d]", this, channelId));
206
0
207
0
  nsCOMPtr<nsIChannel> channel;
208
0
  rv = NS_LinkRedirectChannels(channelId, this, getter_AddRefs(channel));
209
0
  if (NS_SUCCEEDED(rv))
210
0
    mChannel = channel;
211
0
212
0
  LOG(("  found channel %p, rv=%08" PRIx32, mChannel.get(), static_cast<uint32_t>(rv)));
213
0
214
0
  return true;
215
0
}
216
217
mozilla::ipc::IPCResult
218
FTPChannelParent::RecvCancel(const nsresult& status)
219
0
{
220
0
  if (mChannel)
221
0
    mChannel->Cancel(status);
222
0
223
0
  return IPC_OK();
224
0
}
225
226
mozilla::ipc::IPCResult
227
FTPChannelParent::RecvSuspend()
228
0
{
229
0
  if (mChannel) {
230
0
    mChannel->Suspend();
231
0
  }
232
0
  return IPC_OK();
233
0
}
234
235
mozilla::ipc::IPCResult
236
FTPChannelParent::RecvResume()
237
0
{
238
0
  if (mChannel) {
239
0
    mChannel->Resume();
240
0
  }
241
0
  return IPC_OK();
242
0
}
243
244
class FTPDivertDataAvailableEvent : public MainThreadChannelEvent
245
{
246
public:
247
  FTPDivertDataAvailableEvent(FTPChannelParent* aParent,
248
                              const nsCString& data,
249
                              const uint64_t& offset,
250
                              const uint32_t& count)
251
  : mParent(aParent)
252
  , mData(data)
253
  , mOffset(offset)
254
  , mCount(count)
255
0
  {
256
0
  }
257
258
  void Run() override
259
0
  {
260
0
    mParent->DivertOnDataAvailable(mData, mOffset, mCount);
261
0
  }
262
263
private:
264
  FTPChannelParent* mParent;
265
  nsCString mData;
266
  uint64_t mOffset;
267
  uint32_t mCount;
268
};
269
270
mozilla::ipc::IPCResult
271
FTPChannelParent::RecvDivertOnDataAvailable(const nsCString& data,
272
                                            const uint64_t& offset,
273
                                            const uint32_t& count)
274
0
{
275
0
  if (NS_WARN_IF(!mDivertingFromChild)) {
276
0
    MOZ_ASSERT(mDivertingFromChild,
277
0
               "Cannot RecvDivertOnDataAvailable if diverting is not set!");
278
0
    FailDiversion(NS_ERROR_UNEXPECTED);
279
0
    return IPC_FAIL_NO_REASON(this);
280
0
  }
281
0
282
0
  // Drop OnDataAvailables if the parent was canceled already.
283
0
  if (NS_FAILED(mStatus)) {
284
0
    return IPC_OK();
285
0
  }
286
0
287
0
  mEventQ->RunOrEnqueue(new FTPDivertDataAvailableEvent(this, data, offset,
288
0
                                                        count));
289
0
  return IPC_OK();
290
0
}
291
292
void
293
FTPChannelParent::DivertOnDataAvailable(const nsCString& data,
294
                                        const uint64_t& offset,
295
                                        const uint32_t& count)
296
0
{
297
0
  LOG(("FTPChannelParent::DivertOnDataAvailable [this=%p]\n", this));
298
0
299
0
  if (NS_WARN_IF(!mDivertingFromChild)) {
300
0
    MOZ_ASSERT(mDivertingFromChild,
301
0
               "Cannot DivertOnDataAvailable if diverting is not set!");
302
0
    FailDiversion(NS_ERROR_UNEXPECTED);
303
0
    return;
304
0
  }
305
0
306
0
  // Drop OnDataAvailables if the parent was canceled already.
307
0
  if (NS_FAILED(mStatus)) {
308
0
    return;
309
0
  }
310
0
311
0
  nsCOMPtr<nsIInputStream> stringStream;
312
0
  nsresult rv = NS_NewByteInputStream(getter_AddRefs(stringStream), data.get(),
313
0
                                      count, NS_ASSIGNMENT_DEPEND);
314
0
  if (NS_FAILED(rv)) {
315
0
    if (mChannel) {
316
0
      mChannel->Cancel(rv);
317
0
    }
318
0
    mStatus = rv;
319
0
    return;
320
0
  }
321
0
322
0
  AutoEventEnqueuer ensureSerialDispatch(mEventQ);
323
0
324
0
  rv = OnDataAvailable(mChannel, nullptr, stringStream, offset, count);
325
0
326
0
  stringStream->Close();
327
0
  if (NS_FAILED(rv)) {
328
0
    if (mChannel) {
329
0
      mChannel->Cancel(rv);
330
0
    }
331
0
    mStatus = rv;
332
0
  }
333
0
}
334
335
class FTPDivertStopRequestEvent : public MainThreadChannelEvent
336
{
337
public:
338
  FTPDivertStopRequestEvent(FTPChannelParent* aParent,
339
                            const nsresult& statusCode)
340
  : mParent(aParent)
341
  , mStatusCode(statusCode)
342
0
  {
343
0
  }
344
345
  void Run() override
346
0
  {
347
0
    mParent->DivertOnStopRequest(mStatusCode);
348
0
  }
349
350
private:
351
  FTPChannelParent* mParent;
352
  nsresult mStatusCode;
353
};
354
355
mozilla::ipc::IPCResult
356
FTPChannelParent::RecvDivertOnStopRequest(const nsresult& statusCode)
357
0
{
358
0
  if (NS_WARN_IF(!mDivertingFromChild)) {
359
0
    MOZ_ASSERT(mDivertingFromChild,
360
0
               "Cannot RecvDivertOnStopRequest if diverting is not set!");
361
0
    FailDiversion(NS_ERROR_UNEXPECTED);
362
0
    return IPC_FAIL_NO_REASON(this);
363
0
  }
364
0
365
0
  mEventQ->RunOrEnqueue(new FTPDivertStopRequestEvent(this, statusCode));
366
0
  return IPC_OK();
367
0
}
368
369
void
370
FTPChannelParent::DivertOnStopRequest(const nsresult& statusCode)
371
0
{
372
0
  LOG(("FTPChannelParent::DivertOnStopRequest [this=%p]\n", this));
373
0
374
0
  if (NS_WARN_IF(!mDivertingFromChild)) {
375
0
    MOZ_ASSERT(mDivertingFromChild,
376
0
               "Cannot DivertOnStopRequest if diverting is not set!");
377
0
    FailDiversion(NS_ERROR_UNEXPECTED);
378
0
    return;
379
0
  }
380
0
381
0
  // Honor the channel's status even if the underlying transaction completed.
382
0
  nsresult status = NS_FAILED(mStatus) ? mStatus : statusCode;
383
0
384
0
  // Reset fake pending status in case OnStopRequest has already been called.
385
0
  if (mChannel) {
386
0
    nsCOMPtr<nsIForcePendingChannel> forcePendingIChan = do_QueryInterface(mChannel);
387
0
    if (forcePendingIChan) {
388
0
      forcePendingIChan->ForcePending(false);
389
0
    }
390
0
  }
391
0
392
0
  AutoEventEnqueuer ensureSerialDispatch(mEventQ);
393
0
  OnStopRequest(mChannel, nullptr, status);
394
0
}
395
396
class FTPDivertCompleteEvent : public MainThreadChannelEvent
397
{
398
public:
399
  explicit FTPDivertCompleteEvent(FTPChannelParent* aParent)
400
  : mParent(aParent)
401
0
  {
402
0
  }
403
404
  void Run() override
405
0
  {
406
0
    mParent->DivertComplete();
407
0
  }
408
409
private:
410
  FTPChannelParent* mParent;
411
};
412
413
mozilla::ipc::IPCResult
414
FTPChannelParent::RecvDivertComplete()
415
0
{
416
0
  if (NS_WARN_IF(!mDivertingFromChild)) {
417
0
    MOZ_ASSERT(mDivertingFromChild,
418
0
               "Cannot RecvDivertComplete if diverting is not set!");
419
0
    FailDiversion(NS_ERROR_UNEXPECTED);
420
0
    return IPC_FAIL_NO_REASON(this);
421
0
  }
422
0
423
0
  mEventQ->RunOrEnqueue(new FTPDivertCompleteEvent(this));
424
0
  return IPC_OK();
425
0
}
426
427
void
428
FTPChannelParent::DivertComplete()
429
0
{
430
0
  LOG(("FTPChannelParent::DivertComplete [this=%p]\n", this));
431
0
432
0
  if (NS_WARN_IF(!mDivertingFromChild)) {
433
0
    MOZ_ASSERT(mDivertingFromChild,
434
0
               "Cannot DivertComplete if diverting is not set!");
435
0
    FailDiversion(NS_ERROR_UNEXPECTED);
436
0
    return;
437
0
  }
438
0
439
0
  nsresult rv = ResumeForDiversion();
440
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
441
0
    FailDiversion(NS_ERROR_UNEXPECTED);
442
0
  }
443
0
}
444
445
//-----------------------------------------------------------------------------
446
// FTPChannelParent::nsIRequestObserver
447
//-----------------------------------------------------------------------------
448
449
NS_IMETHODIMP
450
FTPChannelParent::OnStartRequest(nsIRequest* aRequest, nsISupports* aContext)
451
0
{
452
0
  LOG(("FTPChannelParent::OnStartRequest [this=%p]\n", this));
453
0
454
0
  if (mDivertingFromChild) {
455
0
    MOZ_RELEASE_ASSERT(mDivertToListener,
456
0
                       "Cannot divert if listener is unset!");
457
0
    return mDivertToListener->OnStartRequest(aRequest, aContext);
458
0
  }
459
0
460
0
  nsCOMPtr<nsIChannel> chan = do_QueryInterface(aRequest);
461
0
  MOZ_ASSERT(chan);
462
0
  NS_ENSURE_TRUE(chan, NS_ERROR_UNEXPECTED);
463
0
464
0
  // Send down any permissions which are relevant to this URL if we are
465
0
  // performing a document load.
466
0
  if (!mIPCClosed) {
467
0
    PContentParent* pcp = Manager()->Manager();
468
0
    MOZ_ASSERT(pcp, "We should have a manager if our IPC isn't closed");
469
0
    DebugOnly<nsresult> rv =
470
0
      static_cast<ContentParent*>(pcp)->AboutToLoadHttpFtpWyciwygDocumentForChild(chan);
471
0
    MOZ_ASSERT(NS_SUCCEEDED(rv));
472
0
  }
473
0
474
0
  int64_t contentLength;
475
0
  chan->GetContentLength(&contentLength);
476
0
  nsCString contentType;
477
0
  chan->GetContentType(contentType);
478
0
479
0
  nsCString entityID;
480
0
  nsCOMPtr<nsIResumableChannel> resChan = do_QueryInterface(aRequest);
481
0
  MOZ_ASSERT(resChan); // both FTP and HTTP should implement nsIResumableChannel
482
0
  if (resChan) {
483
0
    resChan->GetEntityID(entityID);
484
0
  }
485
0
486
0
  PRTime lastModified = 0;
487
0
  nsCOMPtr<nsIFTPChannel> ftpChan = do_QueryInterface(aRequest);
488
0
  if (ftpChan) {
489
0
    ftpChan->GetLastModifiedTime(&lastModified);
490
0
  }
491
0
  nsCOMPtr<nsIHttpChannelInternal> httpChan = do_QueryInterface(aRequest);
492
0
  if (httpChan) {
493
0
    Unused << httpChan->GetLastModifiedTime(&lastModified);
494
0
  }
495
0
496
0
  URIParams uriparam;
497
0
  nsCOMPtr<nsIURI> uri;
498
0
  chan->GetURI(getter_AddRefs(uri));
499
0
  SerializeURI(uri, uriparam);
500
0
501
0
  if (mIPCClosed || !SendOnStartRequest(mStatus, contentLength, contentType,
502
0
                                        lastModified, entityID, uriparam)) {
503
0
    return NS_ERROR_UNEXPECTED;
504
0
  }
505
0
506
0
  return NS_OK;
507
0
}
508
509
NS_IMETHODIMP
510
FTPChannelParent::OnStopRequest(nsIRequest* aRequest,
511
                                nsISupports* aContext,
512
                                nsresult aStatusCode)
513
0
{
514
0
  LOG(("FTPChannelParent::OnStopRequest: [this=%p status=%" PRIu32 "]\n",
515
0
       this, static_cast<uint32_t>(aStatusCode)));
516
0
517
0
  if (mDivertingFromChild) {
518
0
    MOZ_RELEASE_ASSERT(mDivertToListener,
519
0
                       "Cannot divert if listener is unset!");
520
0
    return mDivertToListener->OnStopRequest(aRequest, aContext, aStatusCode);
521
0
  }
522
0
523
0
  if (mIPCClosed || !SendOnStopRequest(aStatusCode, mErrorMsg, mUseUTF8)) {
524
0
    return NS_ERROR_UNEXPECTED;
525
0
  }
526
0
527
0
  return NS_OK;
528
0
}
529
530
//-----------------------------------------------------------------------------
531
// FTPChannelParent::nsIStreamListener
532
//-----------------------------------------------------------------------------
533
534
NS_IMETHODIMP
535
FTPChannelParent::OnDataAvailable(nsIRequest* aRequest,
536
                                  nsISupports* aContext,
537
                                  nsIInputStream* aInputStream,
538
                                  uint64_t aOffset,
539
                                  uint32_t aCount)
540
0
{
541
0
  LOG(("FTPChannelParent::OnDataAvailable [this=%p]\n", this));
542
0
543
0
  if (mDivertingFromChild) {
544
0
    MOZ_RELEASE_ASSERT(mDivertToListener,
545
0
                       "Cannot divert if listener is unset!");
546
0
    return mDivertToListener->OnDataAvailable(aRequest, aContext, aInputStream,
547
0
                                              aOffset, aCount);
548
0
  }
549
0
550
0
  nsCString data;
551
0
  nsresult rv = NS_ReadInputStreamToString(aInputStream, data, aCount);
552
0
  if (NS_FAILED(rv))
553
0
    return rv;
554
0
555
0
  if (mIPCClosed || !SendOnDataAvailable(mStatus, data, aOffset, aCount))
556
0
    return NS_ERROR_UNEXPECTED;
557
0
558
0
  return NS_OK;
559
0
}
560
561
//-----------------------------------------------------------------------------
562
// FTPChannelParent::nsIParentChannel
563
//-----------------------------------------------------------------------------
564
565
NS_IMETHODIMP
566
FTPChannelParent::SetParentListener(HttpChannelParentListener* aListener)
567
0
{
568
0
  // Do not need ptr to HttpChannelParentListener.
569
0
  return NS_OK;
570
0
}
571
572
NS_IMETHODIMP
573
FTPChannelParent::NotifyTrackingProtectionDisabled()
574
0
{
575
0
  // One day, this should probably be filled in.
576
0
  return NS_OK;
577
0
}
578
579
NS_IMETHODIMP
580
FTPChannelParent::NotifyTrackingCookieBlocked(uint32_t aRejectedReason)
581
0
{
582
0
  // One day, this should probably be filled in.
583
0
  return NS_OK;
584
0
}
585
586
NS_IMETHODIMP
587
FTPChannelParent::NotifyTrackingResource(bool aIsThirdParty)
588
0
{
589
0
  // One day, this should probably be filled in.
590
0
  return NS_OK;
591
0
}
592
593
NS_IMETHODIMP
594
FTPChannelParent::SetClassifierMatchedInfo(const nsACString& aList,
595
                                           const nsACString& aProvider,
596
                                           const nsACString& aFullHash)
597
0
{
598
0
  // One day, this should probably be filled in.
599
0
  return NS_OK;
600
0
}
601
602
NS_IMETHODIMP
603
FTPChannelParent::Delete()
604
0
{
605
0
  if (mIPCClosed || !SendDeleteSelf())
606
0
    return NS_ERROR_UNEXPECTED;
607
0
608
0
  return NS_OK;
609
0
}
610
611
//-----------------------------------------------------------------------------
612
// FTPChannelParent::nsIInterfaceRequestor
613
//-----------------------------------------------------------------------------
614
615
NS_IMETHODIMP
616
FTPChannelParent::GetInterface(const nsIID& uuid, void** result)
617
0
{
618
0
  if (uuid.Equals(NS_GET_IID(nsIAuthPromptProvider)) ||
619
0
      uuid.Equals(NS_GET_IID(nsISecureBrowserUI))) {
620
0
    if (mTabParent) {
621
0
      return mTabParent->QueryInterface(uuid, result);
622
0
    }
623
0
  } else if (uuid.Equals(NS_GET_IID(nsIAuthPrompt)) ||
624
0
             uuid.Equals(NS_GET_IID(nsIAuthPrompt2))) {
625
0
    nsCOMPtr<nsIAuthPromptProvider> provider(do_QueryObject(mTabParent));
626
0
    if (provider) {
627
0
      return provider->GetAuthPrompt(nsIAuthPromptProvider::PROMPT_NORMAL,
628
0
                                     uuid,
629
0
                                     result);
630
0
    }
631
0
  }
632
0
633
0
  // Only support nsILoadContext if child channel's callbacks did too
634
0
  if (uuid.Equals(NS_GET_IID(nsILoadContext)) && mLoadContext) {
635
0
    nsCOMPtr<nsILoadContext> copy = mLoadContext;
636
0
    copy.forget(result);
637
0
    return NS_OK;
638
0
  }
639
0
640
0
  return QueryInterface(uuid, result);
641
0
}
642
643
nsresult
644
FTPChannelParent::ResumeChannelInternalIfPossible()
645
0
{
646
0
  nsCOMPtr<nsIChannelWithDivertableParentListener> chan =
647
0
    do_QueryInterface(mChannel);
648
0
  if (chan) {
649
0
    return chan->ResumeInternal();
650
0
  }
651
0
  return mChannel->Resume();
652
0
}
653
654
//-----------------------------------------------------------------------------
655
// FTPChannelParent::ADivertableParentChannel
656
//-----------------------------------------------------------------------------
657
nsresult
658
FTPChannelParent::SuspendForDiversion()
659
0
{
660
0
  MOZ_ASSERT(mChannel);
661
0
  if (NS_WARN_IF(mDivertingFromChild)) {
662
0
    MOZ_ASSERT(!mDivertingFromChild, "Already suspended for diversion!");
663
0
    return NS_ERROR_UNEXPECTED;
664
0
  }
665
0
666
0
  // MessageDiversionStarted call will suspend mEventQ as many times as the
667
0
  // channel has been suspended, so that channel and this queue are in sync.
668
0
  nsCOMPtr<nsIChannelWithDivertableParentListener> chan =
669
0
    do_QueryInterface(mChannel);
670
0
  if (chan) {
671
0
    chan->MessageDiversionStarted(this);
672
0
  }
673
0
674
0
  // We need to suspend only nsHttp/FTPChannel (i.e. we should not suspend
675
0
  // mEventQ). Therefore we call mChannel->SuspendInternal() and not
676
0
  // mChannel->Suspend().
677
0
  // We are suspending only nsHttp/FTPChannel here because we want to stop
678
0
  // OnDataAvailable until diversion is over. At the same time we should
679
0
  // send the diverted OnDataAvailable-s to the listeners and not queue them
680
0
  // in mEventQ.
681
0
  // Try suspending the channel. Allow it to fail, since OnStopRequest may have
682
0
  // been called and thus the channel may not be pending.
683
0
  nsresult rv;
684
0
  if (chan) {
685
0
    rv = chan->SuspendInternal();
686
0
  } else {
687
0
    rv = mChannel->Suspend();
688
0
  }
689
0
690
0
  MOZ_ASSERT(NS_SUCCEEDED(rv) || rv == NS_ERROR_NOT_AVAILABLE);
691
0
  mSuspendedForDiversion = NS_SUCCEEDED(rv);
692
0
693
0
  // Once this is set, no more OnStart/OnData/OnStop callbacks should be sent
694
0
  // to the child.
695
0
  mDivertingFromChild = true;
696
0
697
0
  return NS_OK;
698
0
}
699
700
/* private, supporting function for ADivertableParentChannel */
701
nsresult
702
FTPChannelParent::ResumeForDiversion()
703
0
{
704
0
  MOZ_ASSERT(mChannel);
705
0
  MOZ_ASSERT(mDivertToListener);
706
0
  if (NS_WARN_IF(!mDivertingFromChild)) {
707
0
    MOZ_ASSERT(mDivertingFromChild,
708
0
               "Cannot ResumeForDiversion if not diverting!");
709
0
    return NS_ERROR_UNEXPECTED;
710
0
  }
711
0
712
0
  nsCOMPtr<nsIChannelWithDivertableParentListener> chan =
713
0
    do_QueryInterface(mChannel);
714
0
  if (chan) {
715
0
    chan->MessageDiversionStop();
716
0
  }
717
0
718
0
  if (mSuspendedForDiversion) {
719
0
    nsresult rv = ResumeChannelInternalIfPossible();
720
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
721
0
      FailDiversion(NS_ERROR_UNEXPECTED, true);
722
0
      return rv;
723
0
    }
724
0
    mSuspendedForDiversion = false;
725
0
  }
726
0
727
0
  // Delete() will tear down IPDL, but ref from underlying nsFTPChannel will
728
0
  // keep us alive if there's more data to be delivered to listener.
729
0
  if (NS_WARN_IF(NS_FAILED(Delete()))) {
730
0
    FailDiversion(NS_ERROR_UNEXPECTED);
731
0
    return NS_ERROR_UNEXPECTED;
732
0
  }
733
0
  return NS_OK;
734
0
}
735
736
nsresult
737
FTPChannelParent::SuspendMessageDiversion()
738
0
{
739
0
  // This only need to suspend message queue.
740
0
  mEventQ->Suspend();
741
0
  return NS_OK;
742
0
}
743
744
nsresult
745
FTPChannelParent::ResumeMessageDiversion()
746
0
{
747
0
  // This only need to resumes message queue.
748
0
  mEventQ->Resume();
749
0
  return NS_OK;
750
0
}
751
752
nsresult
753
FTPChannelParent::CancelDiversion()
754
0
{
755
0
  // Only HTTP channels can have child-process-sourced-data that's long-lived
756
0
  // so this isn't currently relevant for FTP channels and there is nothing to
757
0
  // do.
758
0
  return NS_OK;
759
0
}
760
761
void
762
FTPChannelParent::DivertTo(nsIStreamListener *aListener)
763
0
{
764
0
  MOZ_ASSERT(aListener);
765
0
  if (NS_WARN_IF(!mDivertingFromChild)) {
766
0
    MOZ_ASSERT(mDivertingFromChild,
767
0
               "Cannot DivertTo new listener if diverting is not set!");
768
0
    return;
769
0
  }
770
0
771
0
  if (NS_WARN_IF(mIPCClosed || !SendFlushedForDiversion())) {
772
0
    FailDiversion(NS_ERROR_UNEXPECTED);
773
0
    return;
774
0
  }
775
0
776
0
  mDivertToListener = aListener;
777
0
778
0
  // Call OnStartRequest and SendDivertMessages asynchronously to avoid
779
0
  // reentering client context.
780
0
  NS_DispatchToCurrentThread(
781
0
    NewRunnableMethod("net::FTPChannelParent::StartDiversion",
782
0
                      this,
783
0
                      &FTPChannelParent::StartDiversion));
784
0
}
785
786
void
787
FTPChannelParent::StartDiversion()
788
0
{
789
0
  if (NS_WARN_IF(!mDivertingFromChild)) {
790
0
    MOZ_ASSERT(mDivertingFromChild,
791
0
               "Cannot StartDiversion if diverting is not set!");
792
0
    return;
793
0
  }
794
0
795
0
  // Fake pending status in case OnStopRequest has already been called.
796
0
  if (mChannel) {
797
0
    nsCOMPtr<nsIForcePendingChannel> forcePendingIChan = do_QueryInterface(mChannel);
798
0
    if (forcePendingIChan) {
799
0
      forcePendingIChan->ForcePending(true);
800
0
    }
801
0
  }
802
0
803
0
  {
804
0
    AutoEventEnqueuer ensureSerialDispatch(mEventQ);
805
0
    // Call OnStartRequest for the "DivertTo" listener.
806
0
    nsresult rv = OnStartRequest(mChannel, nullptr);
807
0
    if (NS_FAILED(rv)) {
808
0
      if (mChannel) {
809
0
        mChannel->Cancel(rv);
810
0
      }
811
0
      mStatus = rv;
812
0
      return;
813
0
    }
814
0
  }
815
0
816
0
  // After OnStartRequest has been called, tell FTPChannelChild to divert the
817
0
  // OnDataAvailables and OnStopRequest to this FTPChannelParent.
818
0
  if (NS_WARN_IF(mIPCClosed || !SendDivertMessages())) {
819
0
    FailDiversion(NS_ERROR_UNEXPECTED);
820
0
    return;
821
0
  }
822
0
}
823
824
class FTPFailDiversionEvent : public Runnable
825
{
826
public:
827
  FTPFailDiversionEvent(FTPChannelParent* aChannelParent,
828
                        nsresult aErrorCode,
829
                        bool aSkipResume)
830
    : Runnable("net::FTPFailDiversionEvent")
831
    , mChannelParent(aChannelParent)
832
    , mErrorCode(aErrorCode)
833
    , mSkipResume(aSkipResume)
834
0
  {
835
0
    MOZ_RELEASE_ASSERT(aChannelParent);
836
0
    MOZ_RELEASE_ASSERT(NS_FAILED(aErrorCode));
837
0
  }
838
  NS_IMETHOD Run() override
839
0
  {
840
0
    mChannelParent->NotifyDiversionFailed(mErrorCode, mSkipResume);
841
0
    return NS_OK;
842
0
  }
843
private:
844
  RefPtr<FTPChannelParent> mChannelParent;
845
  nsresult mErrorCode;
846
  bool mSkipResume;
847
};
848
849
void
850
FTPChannelParent::FailDiversion(nsresult aErrorCode,
851
                                            bool aSkipResume)
852
0
{
853
0
  MOZ_RELEASE_ASSERT(NS_FAILED(aErrorCode));
854
0
  MOZ_RELEASE_ASSERT(mDivertingFromChild);
855
0
  MOZ_RELEASE_ASSERT(mDivertToListener);
856
0
  MOZ_RELEASE_ASSERT(mChannel);
857
0
858
0
  NS_DispatchToCurrentThread(
859
0
    new FTPFailDiversionEvent(this, aErrorCode, aSkipResume));
860
0
}
861
862
void
863
FTPChannelParent::NotifyDiversionFailed(nsresult aErrorCode,
864
                                        bool aSkipResume)
865
0
{
866
0
  MOZ_RELEASE_ASSERT(NS_FAILED(aErrorCode));
867
0
  MOZ_RELEASE_ASSERT(mDivertingFromChild);
868
0
  MOZ_RELEASE_ASSERT(mDivertToListener);
869
0
  MOZ_RELEASE_ASSERT(mChannel);
870
0
871
0
  mChannel->Cancel(aErrorCode);
872
0
  nsCOMPtr<nsIForcePendingChannel> forcePendingIChan = do_QueryInterface(mChannel);
873
0
  if (forcePendingIChan) {
874
0
    forcePendingIChan->ForcePending(false);
875
0
  }
876
0
877
0
  bool isPending = false;
878
0
  nsresult rv = mChannel->IsPending(&isPending);
879
0
  MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv));
880
0
881
0
  // Resume only we suspended earlier.
882
0
  if (mSuspendedForDiversion) {
883
0
    ResumeChannelInternalIfPossible();
884
0
  }
885
0
  // Channel has already sent OnStartRequest to the child, so ensure that we
886
0
  // call it here if it hasn't already been called.
887
0
  if (!mDivertedOnStartRequest) {
888
0
    nsCOMPtr<nsIForcePendingChannel> forcePendingIChan = do_QueryInterface(mChannel);
889
0
    if (forcePendingIChan) {
890
0
      forcePendingIChan->ForcePending(true);
891
0
    }
892
0
    mDivertToListener->OnStartRequest(mChannel, nullptr);
893
0
894
0
    if (forcePendingIChan) {
895
0
      forcePendingIChan->ForcePending(false);
896
0
    }
897
0
  }
898
0
  // If the channel is pending, it will call OnStopRequest itself; otherwise, do
899
0
  // it here.
900
0
  if (!isPending) {
901
0
    mDivertToListener->OnStopRequest(mChannel, nullptr, aErrorCode);
902
0
  }
903
0
  mDivertToListener = nullptr;
904
0
  mChannel = nullptr;
905
0
906
0
  if (!mIPCClosed) {
907
0
    Unused << SendDeleteSelf();
908
0
  }
909
0
}
910
911
//-----------------------------------------------------------------------------
912
// FTPChannelParent::nsIChannelEventSink
913
//-----------------------------------------------------------------------------
914
915
NS_IMETHODIMP
916
FTPChannelParent::AsyncOnChannelRedirect(
917
                            nsIChannel *oldChannel,
918
                            nsIChannel *newChannel,
919
                            uint32_t redirectFlags,
920
                            nsIAsyncVerifyRedirectCallback* callback)
921
0
{
922
0
  nsCOMPtr<nsIFTPChannel> ftpChan = do_QueryInterface(newChannel);
923
0
  if (!ftpChan) {
924
0
    // when FTP is set to use HTTP proxying, we wind up getting redirected to an HTTP channel.
925
0
    nsCOMPtr<nsIHttpChannel> httpChan = do_QueryInterface(newChannel);
926
0
    if (!httpChan)
927
0
      return NS_ERROR_UNEXPECTED;
928
0
  }
929
0
  mChannel = newChannel;
930
0
  callback->OnRedirectVerifyCallback(NS_OK);
931
0
  return NS_OK;
932
0
}
933
934
NS_IMETHODIMP
935
FTPChannelParent::SetErrorMsg(const char *aMsg, bool aUseUTF8)
936
0
{
937
0
  mErrorMsg = aMsg;
938
0
  mUseUTF8 = aUseUTF8;
939
0
  return NS_OK;
940
0
}
941
942
//---------------------
943
} // namespace net
944
} // namespace mozilla