nsFtpProtocolHandler.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427
  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. *
  7. * This Original Code has been modified by IBM Corporation.
  8. * Modifications made by IBM described herein are
  9. * Copyright (c) International Business Machines
  10. * Corporation, 2000
  11. *
  12. * Modifications to Mozilla code or documentation
  13. * identified per MPL Section 3.3
  14. *
  15. * Date Modified by Description of modification
  16. * 03/27/2000 IBM Corp. Added PR_CALLBACK for Optlink
  17. * use in OS2
  18. */
  19. #include "mozilla/net/NeckoChild.h"
  20. #include "mozilla/net/FTPChannelChild.h"
  21. using namespace mozilla;
  22. using namespace mozilla::net;
  23. #include "nsFtpProtocolHandler.h"
  24. #include "nsFTPChannel.h"
  25. #include "nsIStandardURL.h"
  26. #include "mozilla/Logging.h"
  27. #include "nsIPrefService.h"
  28. #include "nsIPrefBranch.h"
  29. #include "nsIObserverService.h"
  30. #include "nsEscape.h"
  31. #include "nsAlgorithm.h"
  32. //-----------------------------------------------------------------------------
  33. //
  34. // Log module for FTP Protocol logging...
  35. //
  36. // To enable logging (see prlog.h for full details):
  37. //
  38. // set MOZ_LOG=nsFtp:5
  39. // set MOZ_LOG_FILE=ftp.log
  40. //
  41. // This enables LogLevel::Debug level information and places all output in
  42. // the file ftp.log.
  43. //
  44. LazyLogModule gFTPLog("nsFtp");
  45. #undef LOG
  46. #define LOG(args) MOZ_LOG(gFTPLog, mozilla::LogLevel::Debug, args)
  47. //-----------------------------------------------------------------------------
  48. #define IDLE_TIMEOUT_PREF "network.ftp.idleConnectionTimeout"
  49. #define IDLE_CONNECTION_LIMIT 8 /* TODO pref me */
  50. #define QOS_DATA_PREF "network.ftp.data.qos"
  51. #define QOS_CONTROL_PREF "network.ftp.control.qos"
  52. nsFtpProtocolHandler *gFtpHandler = nullptr;
  53. //-----------------------------------------------------------------------------
  54. nsFtpProtocolHandler::nsFtpProtocolHandler()
  55. : mIdleTimeout(-1)
  56. , mSessionId(0)
  57. , mControlQoSBits(0x00)
  58. , mDataQoSBits(0x00)
  59. {
  60. LOG(("FTP:creating handler @%x\n", this));
  61. gFtpHandler = this;
  62. }
  63. nsFtpProtocolHandler::~nsFtpProtocolHandler()
  64. {
  65. LOG(("FTP:destroying handler @%x\n", this));
  66. NS_ASSERTION(mRootConnectionList.Length() == 0, "why wasn't Observe called?");
  67. gFtpHandler = nullptr;
  68. }
  69. NS_IMPL_ISUPPORTS(nsFtpProtocolHandler,
  70. nsIProtocolHandler,
  71. nsIProxiedProtocolHandler,
  72. nsIObserver,
  73. nsISupportsWeakReference)
  74. nsresult
  75. nsFtpProtocolHandler::Init()
  76. {
  77. if (IsNeckoChild())
  78. NeckoChild::InitNeckoChild();
  79. if (mIdleTimeout == -1) {
  80. nsresult rv;
  81. nsCOMPtr<nsIPrefBranch> branch = do_GetService(NS_PREFSERVICE_CONTRACTID, &rv);
  82. if (NS_FAILED(rv)) return rv;
  83. rv = branch->GetIntPref(IDLE_TIMEOUT_PREF, &mIdleTimeout);
  84. if (NS_FAILED(rv))
  85. mIdleTimeout = 5*60; // 5 minute default
  86. rv = branch->AddObserver(IDLE_TIMEOUT_PREF, this, true);
  87. if (NS_FAILED(rv)) return rv;
  88. int32_t val;
  89. rv = branch->GetIntPref(QOS_DATA_PREF, &val);
  90. if (NS_SUCCEEDED(rv))
  91. mDataQoSBits = (uint8_t) clamped(val, 0, 0xff);
  92. rv = branch->AddObserver(QOS_DATA_PREF, this, true);
  93. if (NS_FAILED(rv)) return rv;
  94. rv = branch->GetIntPref(QOS_CONTROL_PREF, &val);
  95. if (NS_SUCCEEDED(rv))
  96. mControlQoSBits = (uint8_t) clamped(val, 0, 0xff);
  97. rv = branch->AddObserver(QOS_CONTROL_PREF, this, true);
  98. if (NS_FAILED(rv)) return rv;
  99. }
  100. nsCOMPtr<nsIObserverService> observerService =
  101. mozilla::services::GetObserverService();
  102. if (observerService) {
  103. observerService->AddObserver(this,
  104. "network:offline-about-to-go-offline",
  105. true);
  106. observerService->AddObserver(this,
  107. "net:clear-active-logins",
  108. true);
  109. }
  110. return NS_OK;
  111. }
  112. //-----------------------------------------------------------------------------
  113. // nsIProtocolHandler methods:
  114. NS_IMETHODIMP
  115. nsFtpProtocolHandler::GetScheme(nsACString &result)
  116. {
  117. result.AssignLiteral("ftp");
  118. return NS_OK;
  119. }
  120. NS_IMETHODIMP
  121. nsFtpProtocolHandler::GetDefaultPort(int32_t *result)
  122. {
  123. *result = 21;
  124. return NS_OK;
  125. }
  126. NS_IMETHODIMP
  127. nsFtpProtocolHandler::GetProtocolFlags(uint32_t *result)
  128. {
  129. *result = URI_STD | ALLOWS_PROXY | ALLOWS_PROXY_HTTP |
  130. URI_LOADABLE_BY_ANYONE;
  131. return NS_OK;
  132. }
  133. NS_IMETHODIMP
  134. nsFtpProtocolHandler::NewURI(const nsACString &aSpec,
  135. const char *aCharset,
  136. nsIURI *aBaseURI,
  137. nsIURI **result)
  138. {
  139. nsAutoCString spec(aSpec);
  140. spec.Trim(" \t\n\r"); // Match NS_IsAsciiWhitespace instead of HTML5
  141. char *fwdPtr = spec.BeginWriting();
  142. // now unescape it... %xx reduced inline to resulting character
  143. int32_t len = NS_UnescapeURL(fwdPtr);
  144. // NS_UnescapeURL() modified spec's buffer, truncate to ensure
  145. // spec knows its new length.
  146. spec.Truncate(len);
  147. // return an error if we find a NUL, CR, or LF in the path
  148. if (spec.FindCharInSet(CRLF) >= 0 || spec.FindChar('\0') >= 0)
  149. return NS_ERROR_MALFORMED_URI;
  150. nsresult rv;
  151. nsCOMPtr<nsIStandardURL> url = do_CreateInstance(NS_STANDARDURL_CONTRACTID, &rv);
  152. if (NS_FAILED(rv)) return rv;
  153. rv = url->Init(nsIStandardURL::URLTYPE_AUTHORITY, 21, aSpec, aCharset, aBaseURI);
  154. if (NS_FAILED(rv)) return rv;
  155. return CallQueryInterface(url, result);
  156. }
  157. NS_IMETHODIMP
  158. nsFtpProtocolHandler::NewChannel2(nsIURI* url,
  159. nsILoadInfo* aLoadInfo,
  160. nsIChannel** result)
  161. {
  162. return NewProxiedChannel2(url, nullptr, 0, nullptr, aLoadInfo, result);
  163. }
  164. NS_IMETHODIMP
  165. nsFtpProtocolHandler::NewChannel(nsIURI* url, nsIChannel* *result)
  166. {
  167. return NewChannel2(url, nullptr, result);
  168. }
  169. NS_IMETHODIMP
  170. nsFtpProtocolHandler::NewProxiedChannel2(nsIURI* uri, nsIProxyInfo* proxyInfo,
  171. uint32_t proxyResolveFlags,
  172. nsIURI *proxyURI,
  173. nsILoadInfo* aLoadInfo,
  174. nsIChannel* *result)
  175. {
  176. NS_ENSURE_ARG_POINTER(uri);
  177. RefPtr<nsBaseChannel> channel;
  178. if (IsNeckoChild())
  179. channel = new FTPChannelChild(uri);
  180. else
  181. channel = new nsFtpChannel(uri, proxyInfo);
  182. nsresult rv = channel->Init();
  183. if (NS_FAILED(rv)) {
  184. return rv;
  185. }
  186. // set the loadInfo on the new channel
  187. rv = channel->SetLoadInfo(aLoadInfo);
  188. if (NS_FAILED(rv)) {
  189. return rv;
  190. }
  191. channel.forget(result);
  192. return rv;
  193. }
  194. NS_IMETHODIMP
  195. nsFtpProtocolHandler::NewProxiedChannel(nsIURI* uri, nsIProxyInfo* proxyInfo,
  196. uint32_t proxyResolveFlags,
  197. nsIURI *proxyURI,
  198. nsIChannel* *result)
  199. {
  200. return NewProxiedChannel2(uri, proxyInfo, proxyResolveFlags,
  201. proxyURI, nullptr /*loadinfo*/,
  202. result);
  203. }
  204. NS_IMETHODIMP
  205. nsFtpProtocolHandler::AllowPort(int32_t port, const char *scheme, bool *_retval)
  206. {
  207. *_retval = (port == 21 || port == 22);
  208. return NS_OK;
  209. }
  210. // connection cache methods
  211. void
  212. nsFtpProtocolHandler::Timeout(nsITimer *aTimer, void *aClosure)
  213. {
  214. LOG(("FTP:timeout reached for %p\n", aClosure));
  215. bool found = gFtpHandler->mRootConnectionList.RemoveElement(aClosure);
  216. if (!found) {
  217. NS_ERROR("timerStruct not found");
  218. return;
  219. }
  220. timerStruct* s = (timerStruct*)aClosure;
  221. delete s;
  222. }
  223. nsresult
  224. nsFtpProtocolHandler::RemoveConnection(nsIURI *aKey, nsFtpControlConnection* *_retval)
  225. {
  226. NS_ASSERTION(_retval, "null pointer");
  227. NS_ASSERTION(aKey, "null pointer");
  228. *_retval = nullptr;
  229. nsAutoCString spec;
  230. aKey->GetPrePath(spec);
  231. LOG(("FTP:removing connection for %s\n", spec.get()));
  232. timerStruct* ts = nullptr;
  233. uint32_t i;
  234. bool found = false;
  235. for (i=0;i<mRootConnectionList.Length();++i) {
  236. ts = mRootConnectionList[i];
  237. if (strcmp(spec.get(), ts->key) == 0) {
  238. found = true;
  239. mRootConnectionList.RemoveElementAt(i);
  240. break;
  241. }
  242. }
  243. if (!found)
  244. return NS_ERROR_FAILURE;
  245. // swap connection ownership
  246. ts->conn.forget(_retval);
  247. delete ts;
  248. return NS_OK;
  249. }
  250. nsresult
  251. nsFtpProtocolHandler::InsertConnection(nsIURI *aKey, nsFtpControlConnection *aConn)
  252. {
  253. NS_ASSERTION(aConn, "null pointer");
  254. NS_ASSERTION(aKey, "null pointer");
  255. if (aConn->mSessionId != mSessionId)
  256. return NS_ERROR_FAILURE;
  257. nsAutoCString spec;
  258. aKey->GetPrePath(spec);
  259. LOG(("FTP:inserting connection for %s\n", spec.get()));
  260. nsresult rv;
  261. nsCOMPtr<nsITimer> timer = do_CreateInstance("@mozilla.org/timer;1", &rv);
  262. if (NS_FAILED(rv)) return rv;
  263. timerStruct* ts = new timerStruct();
  264. if (!ts)
  265. return NS_ERROR_OUT_OF_MEMORY;
  266. rv = timer->InitWithFuncCallback(nsFtpProtocolHandler::Timeout,
  267. ts,
  268. mIdleTimeout*1000,
  269. nsITimer::TYPE_REPEATING_SLACK);
  270. if (NS_FAILED(rv)) {
  271. delete ts;
  272. return rv;
  273. }
  274. ts->key = ToNewCString(spec);
  275. if (!ts->key) {
  276. delete ts;
  277. return NS_ERROR_OUT_OF_MEMORY;
  278. }
  279. // ts->conn is a RefPtr
  280. ts->conn = aConn;
  281. ts->timer = timer;
  282. //
  283. // limit number of idle connections. if limit is reached, then prune
  284. // eldest connection with matching key. if none matching, then prune
  285. // eldest connection.
  286. //
  287. if (mRootConnectionList.Length() == IDLE_CONNECTION_LIMIT) {
  288. uint32_t i;
  289. for (i=0;i<mRootConnectionList.Length();++i) {
  290. timerStruct *candidate = mRootConnectionList[i];
  291. if (strcmp(candidate->key, ts->key) == 0) {
  292. mRootConnectionList.RemoveElementAt(i);
  293. delete candidate;
  294. break;
  295. }
  296. }
  297. if (mRootConnectionList.Length() == IDLE_CONNECTION_LIMIT) {
  298. timerStruct *eldest = mRootConnectionList[0];
  299. mRootConnectionList.RemoveElementAt(0);
  300. delete eldest;
  301. }
  302. }
  303. mRootConnectionList.AppendElement(ts);
  304. return NS_OK;
  305. }
  306. //-----------------------------------------------------------------------------
  307. // nsIObserver
  308. NS_IMETHODIMP
  309. nsFtpProtocolHandler::Observe(nsISupports *aSubject,
  310. const char *aTopic,
  311. const char16_t *aData)
  312. {
  313. LOG(("FTP:observing [%s]\n", aTopic));
  314. if (!strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID)) {
  315. nsCOMPtr<nsIPrefBranch> branch = do_QueryInterface(aSubject);
  316. if (!branch) {
  317. NS_ERROR("no prefbranch");
  318. return NS_ERROR_UNEXPECTED;
  319. }
  320. int32_t val;
  321. nsresult rv = branch->GetIntPref(IDLE_TIMEOUT_PREF, &val);
  322. if (NS_SUCCEEDED(rv))
  323. mIdleTimeout = val;
  324. rv = branch->GetIntPref(QOS_DATA_PREF, &val);
  325. if (NS_SUCCEEDED(rv))
  326. mDataQoSBits = (uint8_t) clamped(val, 0, 0xff);
  327. rv = branch->GetIntPref(QOS_CONTROL_PREF, &val);
  328. if (NS_SUCCEEDED(rv))
  329. mControlQoSBits = (uint8_t) clamped(val, 0, 0xff);
  330. } else if (!strcmp(aTopic, "network:offline-about-to-go-offline")) {
  331. ClearAllConnections();
  332. } else if (!strcmp(aTopic, "net:clear-active-logins")) {
  333. ClearAllConnections();
  334. mSessionId++;
  335. } else {
  336. NS_NOTREACHED("unexpected topic");
  337. }
  338. return NS_OK;
  339. }
  340. void
  341. nsFtpProtocolHandler::ClearAllConnections()
  342. {
  343. uint32_t i;
  344. for (i=0;i<mRootConnectionList.Length();++i)
  345. delete mRootConnectionList[i];
  346. mRootConnectionList.Clear();
  347. }