nsFtpControlConnection.cpp 5.2 KB

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