nsFtpConnectionThread.cpp 64 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147
  1. /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
  2. /* vim:set tw=80 ts=4 sts=4 sw=4 et cin: */
  3. /* This Source Code Form is subject to the terms of the Mozilla Public
  4. * License, v. 2.0. If a copy of the MPL was not distributed with this
  5. * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
  6. #include <ctype.h>
  7. #include "prprf.h"
  8. #include "mozilla/Logging.h"
  9. #include "prtime.h"
  10. #include "nsIOService.h"
  11. #include "nsFTPChannel.h"
  12. #include "nsFtpConnectionThread.h"
  13. #include "nsFtpControlConnection.h"
  14. #include "nsFtpProtocolHandler.h"
  15. #include "netCore.h"
  16. #include "nsCRT.h"
  17. #include "nsEscape.h"
  18. #include "nsMimeTypes.h"
  19. #include "nsNetCID.h"
  20. #include "nsNetUtil.h"
  21. #include "nsIAsyncStreamCopier.h"
  22. #include "nsThreadUtils.h"
  23. #include "nsStreamUtils.h"
  24. #include "nsIURL.h"
  25. #include "nsISocketTransport.h"
  26. #include "nsIStreamListenerTee.h"
  27. #include "nsIPrefService.h"
  28. #include "nsIPrefBranch.h"
  29. #include "nsAuthInformationHolder.h"
  30. #include "nsIProtocolProxyService.h"
  31. #include "nsICancelable.h"
  32. #include "nsIOutputStream.h"
  33. #include "nsIPrompt.h"
  34. #include "nsIProtocolHandler.h"
  35. #include "nsIProxyInfo.h"
  36. #include "nsIRunnable.h"
  37. #include "nsISocketTransportService.h"
  38. #include "nsIURI.h"
  39. #include "nsILoadInfo.h"
  40. #include "nsNullPrincipal.h"
  41. #include "nsIAuthPrompt2.h"
  42. #include "nsIFTPChannelParentInternal.h"
  43. using namespace mozilla;
  44. using namespace mozilla::net;
  45. extern LazyLogModule gFTPLog;
  46. #define LOG(args) MOZ_LOG(gFTPLog, mozilla::LogLevel::Debug, args)
  47. #define LOG_INFO(args) MOZ_LOG(gFTPLog, mozilla::LogLevel::Info, args)
  48. // remove FTP parameters (starting with ";") from the path
  49. static void
  50. removeParamsFromPath(nsCString& path)
  51. {
  52. int32_t index = path.FindChar(';');
  53. if (index >= 0) {
  54. path.SetLength(index);
  55. }
  56. }
  57. NS_IMPL_ISUPPORTS_INHERITED(nsFtpState,
  58. nsBaseContentStream,
  59. nsIInputStreamCallback,
  60. nsITransportEventSink,
  61. nsIRequestObserver,
  62. nsIProtocolProxyCallback)
  63. nsFtpState::nsFtpState()
  64. : nsBaseContentStream(true)
  65. , mState(FTP_INIT)
  66. , mNextState(FTP_S_USER)
  67. , mKeepRunning(true)
  68. , mReceivedControlData(false)
  69. , mTryingCachedControl(false)
  70. , mRETRFailed(false)
  71. , mFileSize(kJS_MAX_SAFE_UINTEGER)
  72. , mServerType(FTP_GENERIC_TYPE)
  73. , mAction(GET)
  74. , mAnonymous(true)
  75. , mRetryPass(false)
  76. , mStorReplyReceived(false)
  77. , mRlist1xxReceived(false)
  78. , mRstor1xxReceived(false)
  79. , mRretr1xxReceived(false)
  80. , mInternalError(NS_OK)
  81. , mReconnectAndLoginAgain(false)
  82. , mCacheConnection(true)
  83. , mPort(21)
  84. , mAddressChecked(false)
  85. , mServerIsIPv6(false)
  86. , mUseUTF8(false)
  87. , mControlStatus(NS_OK)
  88. , mDeferredCallbackPending(false)
  89. {
  90. LOG_INFO(("FTP:(%x) nsFtpState created", this));
  91. // make sure handler stays around
  92. NS_ADDREF(gFtpHandler);
  93. }
  94. nsFtpState::~nsFtpState()
  95. {
  96. LOG_INFO(("FTP:(%x) nsFtpState destroyed", this));
  97. if (mProxyRequest)
  98. mProxyRequest->Cancel(NS_ERROR_FAILURE);
  99. // release reference to handler
  100. nsFtpProtocolHandler *handler = gFtpHandler;
  101. NS_RELEASE(handler);
  102. }
  103. // nsIInputStreamCallback implementation
  104. NS_IMETHODIMP
  105. nsFtpState::OnInputStreamReady(nsIAsyncInputStream *aInStream)
  106. {
  107. LOG(("FTP:(%p) data stream ready\n", this));
  108. // We are receiving a notification from our data stream, so just forward it
  109. // on to our stream callback.
  110. if (HasPendingCallback())
  111. DispatchCallbackSync();
  112. return NS_OK;
  113. }
  114. void
  115. nsFtpState::OnControlDataAvailable(const char *aData, uint32_t aDataLen)
  116. {
  117. LOG(("FTP:(%p) control data available [%u]\n", this, aDataLen));
  118. mControlConnection->WaitData(this); // queue up another call
  119. if (!mReceivedControlData) {
  120. // parameter can be null cause the channel fills them in.
  121. OnTransportStatus(nullptr, NS_NET_STATUS_BEGIN_FTP_TRANSACTION, 0, 0);
  122. mReceivedControlData = true;
  123. }
  124. // Sometimes we can get two responses in the same packet, eg from LIST.
  125. // So we need to parse the response line by line
  126. nsCString buffer = mControlReadCarryOverBuf;
  127. // Clear the carryover buf - if we still don't have a line, then it will
  128. // be reappended below
  129. mControlReadCarryOverBuf.Truncate();
  130. buffer.Append(aData, aDataLen);
  131. const char* currLine = buffer.get();
  132. while (*currLine && mKeepRunning) {
  133. int32_t eolLength = strcspn(currLine, CRLF);
  134. int32_t currLineLength = strlen(currLine);
  135. // if currLine is empty or only contains CR or LF, then bail. we can
  136. // sometimes get an ODA event with the full response line + CR without
  137. // the trailing LF. the trailing LF might come in the next ODA event.
  138. // because we are happy enough to process a response line ending only
  139. // in CR, we need to take care to discard the extra LF (bug 191220).
  140. if (eolLength == 0 && currLineLength <= 1)
  141. break;
  142. if (eolLength == currLineLength) {
  143. mControlReadCarryOverBuf.Assign(currLine);
  144. break;
  145. }
  146. // Append the current segment, including the LF
  147. nsAutoCString line;
  148. int32_t crlfLength = 0;
  149. if ((currLineLength > eolLength) &&
  150. (currLine[eolLength] == nsCRT::CR) &&
  151. (currLine[eolLength+1] == nsCRT::LF)) {
  152. crlfLength = 2; // CR +LF
  153. } else {
  154. crlfLength = 1; // + LF or CR
  155. }
  156. line.Assign(currLine, eolLength + crlfLength);
  157. // Does this start with a response code?
  158. bool startNum = (line.Length() >= 3 &&
  159. isdigit(line[0]) &&
  160. isdigit(line[1]) &&
  161. isdigit(line[2]));
  162. if (mResponseMsg.IsEmpty()) {
  163. // If we get here, then we know that we have a complete line, and
  164. // that it is the first one
  165. NS_ASSERTION(line.Length() > 4 && startNum,
  166. "Read buffer doesn't include response code");
  167. mResponseCode = atoi(PromiseFlatCString(Substring(line,0,3)).get());
  168. }
  169. mResponseMsg.Append(line);
  170. // This is the last line if its 3 numbers followed by a space
  171. if (startNum && line[3] == ' ') {
  172. // yup. last line, let's move on.
  173. if (mState == mNextState) {
  174. NS_ERROR("ftp read state mixup");
  175. mInternalError = NS_ERROR_FAILURE;
  176. mState = FTP_ERROR;
  177. } else {
  178. mState = mNextState;
  179. }
  180. nsCOMPtr<nsIFTPEventSink> ftpSink;
  181. mChannel->GetFTPEventSink(ftpSink);
  182. if (ftpSink)
  183. ftpSink->OnFTPControlLog(true, mResponseMsg.get());
  184. nsresult rv = Process();
  185. mResponseMsg.Truncate();
  186. if (NS_FAILED(rv)) {
  187. CloseWithStatus(rv);
  188. return;
  189. }
  190. }
  191. currLine = currLine + eolLength + crlfLength;
  192. }
  193. }
  194. void
  195. nsFtpState::OnControlError(nsresult status)
  196. {
  197. NS_ASSERTION(NS_FAILED(status), "expecting error condition");
  198. LOG(("FTP:(%p) CC(%p) error [%x was-cached=%u]\n",
  199. this, mControlConnection.get(), status, mTryingCachedControl));
  200. mControlStatus = status;
  201. if (mReconnectAndLoginAgain && NS_SUCCEEDED(mInternalError)) {
  202. mReconnectAndLoginAgain = false;
  203. mAnonymous = false;
  204. mControlStatus = NS_OK;
  205. Connect();
  206. } else if (mTryingCachedControl && NS_SUCCEEDED(mInternalError)) {
  207. mTryingCachedControl = false;
  208. Connect();
  209. } else {
  210. CloseWithStatus(status);
  211. }
  212. }
  213. nsresult
  214. nsFtpState::EstablishControlConnection()
  215. {
  216. NS_ASSERTION(!mControlConnection, "we already have a control connection");
  217. nsresult rv;
  218. LOG(("FTP:(%x) trying cached control\n", this));
  219. // Look to see if we can use a cached control connection:
  220. RefPtr<nsFtpControlConnection> connection;
  221. // Don't use cached control if anonymous (bug #473371)
  222. if (!mChannel->HasLoadFlag(nsIRequest::LOAD_ANONYMOUS))
  223. gFtpHandler->RemoveConnection(mChannel->URI(), getter_AddRefs(connection));
  224. if (connection) {
  225. mControlConnection.swap(connection);
  226. if (mControlConnection->IsAlive())
  227. {
  228. // set stream listener of the control connection to be us.
  229. mControlConnection->WaitData(this);
  230. // read cached variables into us.
  231. mServerType = mControlConnection->mServerType;
  232. mPassword = mControlConnection->mPassword;
  233. mPwd = mControlConnection->mPwd;
  234. mUseUTF8 = mControlConnection->mUseUTF8;
  235. mTryingCachedControl = true;
  236. // we have to set charset to connection if server supports utf-8
  237. if (mUseUTF8)
  238. mChannel->SetContentCharset(NS_LITERAL_CSTRING("UTF-8"));
  239. // we're already connected to this server, skip login.
  240. mState = FTP_S_PASV;
  241. mResponseCode = 530; // assume the control connection was dropped.
  242. mControlStatus = NS_OK;
  243. mReceivedControlData = false; // For this request, we have not.
  244. // if we succeed, return. Otherwise, we need to create a transport
  245. rv = mControlConnection->Connect(mChannel->ProxyInfo(), this);
  246. if (NS_SUCCEEDED(rv))
  247. return rv;
  248. }
  249. LOG(("FTP:(%p) cached CC(%p) is unusable\n", this,
  250. mControlConnection.get()));
  251. mControlConnection->WaitData(nullptr);
  252. mControlConnection = nullptr;
  253. }
  254. LOG(("FTP:(%p) creating CC\n", this));
  255. mState = FTP_READ_BUF;
  256. mNextState = FTP_S_USER;
  257. nsAutoCString host;
  258. rv = mChannel->URI()->GetAsciiHost(host);
  259. if (NS_FAILED(rv))
  260. return rv;
  261. mControlConnection = new nsFtpControlConnection(host, mPort);
  262. if (!mControlConnection)
  263. return NS_ERROR_OUT_OF_MEMORY;
  264. rv = mControlConnection->Connect(mChannel->ProxyInfo(), this);
  265. if (NS_FAILED(rv)) {
  266. LOG(("FTP:(%p) CC(%p) failed to connect [rv=%x]\n", this,
  267. mControlConnection.get(), rv));
  268. mControlConnection = nullptr;
  269. return rv;
  270. }
  271. return mControlConnection->WaitData(this);
  272. }
  273. void
  274. nsFtpState::MoveToNextState(FTP_STATE nextState)
  275. {
  276. if (NS_FAILED(mInternalError)) {
  277. mState = FTP_ERROR;
  278. LOG(("FTP:(%x) FAILED (%x)\n", this, mInternalError));
  279. } else {
  280. mState = FTP_READ_BUF;
  281. mNextState = nextState;
  282. }
  283. }
  284. nsresult
  285. nsFtpState::Process()
  286. {
  287. nsresult rv = NS_OK;
  288. bool processingRead = true;
  289. while (mKeepRunning && processingRead) {
  290. switch (mState) {
  291. case FTP_COMMAND_CONNECT:
  292. KillControlConnection();
  293. LOG(("FTP:(%p) establishing CC", this));
  294. mInternalError = EstablishControlConnection(); // sets mState
  295. if (NS_FAILED(mInternalError)) {
  296. mState = FTP_ERROR;
  297. LOG(("FTP:(%p) FAILED\n", this));
  298. } else {
  299. LOG(("FTP:(%p) SUCCEEDED\n", this));
  300. }
  301. break;
  302. case FTP_READ_BUF:
  303. LOG(("FTP:(%p) Waiting for CC(%p)\n", this,
  304. mControlConnection.get()));
  305. processingRead = false;
  306. break;
  307. case FTP_ERROR: // xx needs more work to handle dropped control connection cases
  308. if ((mTryingCachedControl && mResponseCode == 530 &&
  309. mInternalError == NS_ERROR_FTP_PASV) ||
  310. (mResponseCode == 425 &&
  311. mInternalError == NS_ERROR_FTP_PASV)) {
  312. // The user was logged out during an pasv operation
  313. // we want to restart this request with a new control
  314. // channel.
  315. mState = FTP_COMMAND_CONNECT;
  316. } else if (mResponseCode == 421 &&
  317. mInternalError != NS_ERROR_FTP_LOGIN) {
  318. // The command channel dropped for some reason.
  319. // Fire it back up, unless we were trying to login
  320. // in which case the server might just be telling us
  321. // that the max number of users has been reached...
  322. mState = FTP_COMMAND_CONNECT;
  323. } else if (mAnonymous &&
  324. mInternalError == NS_ERROR_FTP_LOGIN) {
  325. // If the login was anonymous, and it failed, try again with a username
  326. // Don't reuse old control connection, see #386167
  327. mAnonymous = false;
  328. mState = FTP_COMMAND_CONNECT;
  329. } else {
  330. LOG(("FTP:(%x) FTP_ERROR - calling StopProcessing\n", this));
  331. rv = StopProcessing();
  332. NS_ASSERTION(NS_SUCCEEDED(rv), "StopProcessing failed.");
  333. processingRead = false;
  334. }
  335. break;
  336. case FTP_COMPLETE:
  337. LOG(("FTP:(%x) COMPLETE\n", this));
  338. rv = StopProcessing();
  339. NS_ASSERTION(NS_SUCCEEDED(rv), "StopProcessing failed.");
  340. processingRead = false;
  341. break;
  342. // USER
  343. case FTP_S_USER:
  344. rv = S_user();
  345. if (NS_FAILED(rv))
  346. mInternalError = NS_ERROR_FTP_LOGIN;
  347. MoveToNextState(FTP_R_USER);
  348. break;
  349. case FTP_R_USER:
  350. mState = R_user();
  351. if (FTP_ERROR == mState)
  352. mInternalError = NS_ERROR_FTP_LOGIN;
  353. break;
  354. // PASS
  355. case FTP_S_PASS:
  356. rv = S_pass();
  357. if (NS_FAILED(rv))
  358. mInternalError = NS_ERROR_FTP_LOGIN;
  359. MoveToNextState(FTP_R_PASS);
  360. break;
  361. case FTP_R_PASS:
  362. mState = R_pass();
  363. if (FTP_ERROR == mState)
  364. mInternalError = NS_ERROR_FTP_LOGIN;
  365. break;
  366. // ACCT
  367. case FTP_S_ACCT:
  368. rv = S_acct();
  369. if (NS_FAILED(rv))
  370. mInternalError = NS_ERROR_FTP_LOGIN;
  371. MoveToNextState(FTP_R_ACCT);
  372. break;
  373. case FTP_R_ACCT:
  374. mState = R_acct();
  375. if (FTP_ERROR == mState)
  376. mInternalError = NS_ERROR_FTP_LOGIN;
  377. break;
  378. // SYST
  379. case FTP_S_SYST:
  380. rv = S_syst();
  381. if (NS_FAILED(rv))
  382. mInternalError = NS_ERROR_FTP_LOGIN;
  383. MoveToNextState(FTP_R_SYST);
  384. break;
  385. case FTP_R_SYST:
  386. mState = R_syst();
  387. if (FTP_ERROR == mState)
  388. mInternalError = NS_ERROR_FTP_LOGIN;
  389. break;
  390. // TYPE
  391. case FTP_S_TYPE:
  392. rv = S_type();
  393. if (NS_FAILED(rv))
  394. mInternalError = rv;
  395. MoveToNextState(FTP_R_TYPE);
  396. break;
  397. case FTP_R_TYPE:
  398. mState = R_type();
  399. if (FTP_ERROR == mState)
  400. mInternalError = NS_ERROR_FAILURE;
  401. break;
  402. // CWD
  403. case FTP_S_CWD:
  404. rv = S_cwd();
  405. if (NS_FAILED(rv))
  406. mInternalError = NS_ERROR_FTP_CWD;
  407. MoveToNextState(FTP_R_CWD);
  408. break;
  409. case FTP_R_CWD:
  410. mState = R_cwd();
  411. if (FTP_ERROR == mState)
  412. mInternalError = NS_ERROR_FTP_CWD;
  413. break;
  414. // LIST
  415. case FTP_S_LIST:
  416. rv = S_list();
  417. if (rv == NS_ERROR_NOT_RESUMABLE) {
  418. mInternalError = rv;
  419. } else if (NS_FAILED(rv)) {
  420. mInternalError = NS_ERROR_FTP_CWD;
  421. }
  422. MoveToNextState(FTP_R_LIST);
  423. break;
  424. case FTP_R_LIST:
  425. mState = R_list();
  426. if (FTP_ERROR == mState)
  427. mInternalError = NS_ERROR_FAILURE;
  428. break;
  429. // SIZE
  430. case FTP_S_SIZE:
  431. rv = S_size();
  432. if (NS_FAILED(rv))
  433. mInternalError = rv;
  434. MoveToNextState(FTP_R_SIZE);
  435. break;
  436. case FTP_R_SIZE:
  437. mState = R_size();
  438. if (FTP_ERROR == mState)
  439. mInternalError = NS_ERROR_FAILURE;
  440. break;
  441. // REST
  442. case FTP_S_REST:
  443. rv = S_rest();
  444. if (NS_FAILED(rv))
  445. mInternalError = rv;
  446. MoveToNextState(FTP_R_REST);
  447. break;
  448. case FTP_R_REST:
  449. mState = R_rest();
  450. if (FTP_ERROR == mState)
  451. mInternalError = NS_ERROR_FAILURE;
  452. break;
  453. // MDTM
  454. case FTP_S_MDTM:
  455. rv = S_mdtm();
  456. if (NS_FAILED(rv))
  457. mInternalError = rv;
  458. MoveToNextState(FTP_R_MDTM);
  459. break;
  460. case FTP_R_MDTM:
  461. mState = R_mdtm();
  462. // Don't want to overwrite a more explicit status code
  463. if (FTP_ERROR == mState && NS_SUCCEEDED(mInternalError))
  464. mInternalError = NS_ERROR_FAILURE;
  465. break;
  466. // RETR
  467. case FTP_S_RETR:
  468. rv = S_retr();
  469. if (NS_FAILED(rv))
  470. mInternalError = rv;
  471. MoveToNextState(FTP_R_RETR);
  472. break;
  473. case FTP_R_RETR:
  474. mState = R_retr();
  475. if (FTP_ERROR == mState)
  476. mInternalError = NS_ERROR_FAILURE;
  477. break;
  478. // STOR
  479. case FTP_S_STOR:
  480. rv = S_stor();
  481. if (NS_FAILED(rv))
  482. mInternalError = rv;
  483. MoveToNextState(FTP_R_STOR);
  484. break;
  485. case FTP_R_STOR:
  486. mState = R_stor();
  487. if (FTP_ERROR == mState)
  488. mInternalError = NS_ERROR_FAILURE;
  489. break;
  490. // PASV
  491. case FTP_S_PASV:
  492. rv = S_pasv();
  493. if (NS_FAILED(rv))
  494. mInternalError = NS_ERROR_FTP_PASV;
  495. MoveToNextState(FTP_R_PASV);
  496. break;
  497. case FTP_R_PASV:
  498. mState = R_pasv();
  499. if (FTP_ERROR == mState)
  500. mInternalError = NS_ERROR_FTP_PASV;
  501. break;
  502. // PWD
  503. case FTP_S_PWD:
  504. rv = S_pwd();
  505. if (NS_FAILED(rv))
  506. mInternalError = NS_ERROR_FTP_PWD;
  507. MoveToNextState(FTP_R_PWD);
  508. break;
  509. case FTP_R_PWD:
  510. mState = R_pwd();
  511. if (FTP_ERROR == mState)
  512. mInternalError = NS_ERROR_FTP_PWD;
  513. break;
  514. // FEAT for RFC2640 support
  515. case FTP_S_FEAT:
  516. rv = S_feat();
  517. if (NS_FAILED(rv))
  518. mInternalError = rv;
  519. MoveToNextState(FTP_R_FEAT);
  520. break;
  521. case FTP_R_FEAT:
  522. mState = R_feat();
  523. // Don't want to overwrite a more explicit status code
  524. if (FTP_ERROR == mState && NS_SUCCEEDED(mInternalError))
  525. mInternalError = NS_ERROR_FAILURE;
  526. break;
  527. // OPTS for some non-RFC2640-compliant servers support
  528. case FTP_S_OPTS:
  529. rv = S_opts();
  530. if (NS_FAILED(rv))
  531. mInternalError = rv;
  532. MoveToNextState(FTP_R_OPTS);
  533. break;
  534. case FTP_R_OPTS:
  535. mState = R_opts();
  536. // Don't want to overwrite a more explicit status code
  537. if (FTP_ERROR == mState && NS_SUCCEEDED(mInternalError))
  538. mInternalError = NS_ERROR_FAILURE;
  539. break;
  540. default:
  541. ;
  542. }
  543. }
  544. return rv;
  545. }
  546. ///////////////////////////////////
  547. // STATE METHODS
  548. ///////////////////////////////////
  549. nsresult
  550. nsFtpState::S_user() {
  551. // some servers on connect send us a 421 or 521. (84525) (141784)
  552. if ((mResponseCode == 421) || (mResponseCode == 521))
  553. return NS_ERROR_FAILURE;
  554. nsresult rv;
  555. nsAutoCString usernameStr("USER ");
  556. mResponseMsg = "";
  557. if (mAnonymous) {
  558. mReconnectAndLoginAgain = true;
  559. usernameStr.AppendLiteral("anonymous");
  560. } else {
  561. mReconnectAndLoginAgain = false;
  562. if (mUsername.IsEmpty()) {
  563. // No prompt for anonymous requests (bug #473371)
  564. if (mChannel->HasLoadFlag(nsIRequest::LOAD_ANONYMOUS))
  565. return NS_ERROR_FAILURE;
  566. nsCOMPtr<nsIAuthPrompt2> prompter;
  567. NS_QueryAuthPrompt2(static_cast<nsIChannel*>(mChannel),
  568. getter_AddRefs(prompter));
  569. if (!prompter)
  570. return NS_ERROR_NOT_INITIALIZED;
  571. RefPtr<nsAuthInformationHolder> info =
  572. new nsAuthInformationHolder(nsIAuthInformation::AUTH_HOST,
  573. EmptyString(),
  574. EmptyCString());
  575. bool retval;
  576. rv = prompter->PromptAuth(mChannel, nsIAuthPrompt2::LEVEL_NONE,
  577. info, &retval);
  578. // if the user canceled or didn't supply a username we want to fail
  579. if (NS_FAILED(rv) || !retval || info->User().IsEmpty())
  580. return NS_ERROR_FAILURE;
  581. mUsername = info->User();
  582. mPassword = info->Password();
  583. }
  584. // XXX Is UTF-8 the best choice?
  585. AppendUTF16toUTF8(mUsername, usernameStr);
  586. }
  587. usernameStr.Append(CRLF);
  588. return SendFTPCommand(usernameStr);
  589. }
  590. FTP_STATE
  591. nsFtpState::R_user() {
  592. mReconnectAndLoginAgain = false;
  593. if (mResponseCode/100 == 3) {
  594. // send off the password
  595. return FTP_S_PASS;
  596. }
  597. if (mResponseCode/100 == 2) {
  598. // no password required, we're already logged in
  599. return FTP_S_SYST;
  600. }
  601. if (mResponseCode/100 == 5) {
  602. // problem logging in. typically this means the server
  603. // has reached it's user limit.
  604. return FTP_ERROR;
  605. }
  606. // LOGIN FAILED
  607. return FTP_ERROR;
  608. }
  609. nsresult
  610. nsFtpState::S_pass() {
  611. nsresult rv;
  612. nsAutoCString passwordStr("PASS ");
  613. mResponseMsg = "";
  614. if (mAnonymous) {
  615. if (!mPassword.IsEmpty()) {
  616. // XXX Is UTF-8 the best choice?
  617. AppendUTF16toUTF8(mPassword, passwordStr);
  618. } else {
  619. nsXPIDLCString anonPassword;
  620. bool useRealEmail = false;
  621. nsCOMPtr<nsIPrefBranch> prefs =
  622. do_GetService(NS_PREFSERVICE_CONTRACTID);
  623. if (prefs) {
  624. rv = prefs->GetBoolPref("advanced.mailftp", &useRealEmail);
  625. if (NS_SUCCEEDED(rv) && useRealEmail) {
  626. prefs->GetCharPref("network.ftp.anonymous_password",
  627. getter_Copies(anonPassword));
  628. }
  629. }
  630. if (!anonPassword.IsEmpty()) {
  631. passwordStr.AppendASCII(anonPassword);
  632. } else {
  633. // We need to default to a valid email address - bug 101027
  634. // example.com is reserved (rfc2606), so use that
  635. passwordStr.AppendLiteral("mozilla@example.com");
  636. }
  637. }
  638. } else {
  639. if (mPassword.IsEmpty() || mRetryPass) {
  640. // No prompt for anonymous requests (bug #473371)
  641. if (mChannel->HasLoadFlag(nsIRequest::LOAD_ANONYMOUS))
  642. return NS_ERROR_FAILURE;
  643. nsCOMPtr<nsIAuthPrompt2> prompter;
  644. NS_QueryAuthPrompt2(static_cast<nsIChannel*>(mChannel),
  645. getter_AddRefs(prompter));
  646. if (!prompter)
  647. return NS_ERROR_NOT_INITIALIZED;
  648. RefPtr<nsAuthInformationHolder> info =
  649. new nsAuthInformationHolder(nsIAuthInformation::AUTH_HOST |
  650. nsIAuthInformation::ONLY_PASSWORD,
  651. EmptyString(),
  652. EmptyCString());
  653. info->SetUserInternal(mUsername);
  654. bool retval;
  655. rv = prompter->PromptAuth(mChannel, nsIAuthPrompt2::LEVEL_NONE,
  656. info, &retval);
  657. // we want to fail if the user canceled. Note here that if they want
  658. // a blank password, we will pass it along.
  659. if (NS_FAILED(rv) || !retval)
  660. return NS_ERROR_FAILURE;
  661. mPassword = info->Password();
  662. }
  663. // XXX Is UTF-8 the best choice?
  664. AppendUTF16toUTF8(mPassword, passwordStr);
  665. }
  666. passwordStr.Append(CRLF);
  667. return SendFTPCommand(passwordStr);
  668. }
  669. FTP_STATE
  670. nsFtpState::R_pass() {
  671. if (mResponseCode/100 == 3) {
  672. // send account info
  673. return FTP_S_ACCT;
  674. }
  675. if (mResponseCode/100 == 2) {
  676. // logged in
  677. return FTP_S_SYST;
  678. }
  679. if (mResponseCode == 503) {
  680. // start over w/ the user command.
  681. // note: the password was successful, and it's stored in mPassword
  682. mRetryPass = false;
  683. return FTP_S_USER;
  684. }
  685. if (mResponseCode/100 == 5 || mResponseCode==421) {
  686. // There is no difference between a too-many-users error,
  687. // a wrong-password error, or any other sort of error
  688. if (!mAnonymous)
  689. mRetryPass = true;
  690. return FTP_ERROR;
  691. }
  692. // unexpected response code
  693. return FTP_ERROR;
  694. }
  695. nsresult
  696. nsFtpState::S_pwd() {
  697. return SendFTPCommand(NS_LITERAL_CSTRING("PWD" CRLF));
  698. }
  699. FTP_STATE
  700. nsFtpState::R_pwd() {
  701. // Error response to PWD command isn't fatal, but don't cache the connection
  702. // if CWD command is sent since correct mPwd is needed for further requests.
  703. if (mResponseCode/100 != 2)
  704. return FTP_S_TYPE;
  705. nsAutoCString respStr(mResponseMsg);
  706. int32_t pos = respStr.FindChar('"');
  707. if (pos > -1) {
  708. respStr.Cut(0, pos+1);
  709. pos = respStr.FindChar('"');
  710. if (pos > -1) {
  711. respStr.Truncate(pos);
  712. if (mServerType == FTP_VMS_TYPE)
  713. ConvertDirspecFromVMS(respStr);
  714. if (respStr.IsEmpty() || respStr.Last() != '/')
  715. respStr.Append('/');
  716. mPwd = respStr;
  717. }
  718. }
  719. return FTP_S_TYPE;
  720. }
  721. nsresult
  722. nsFtpState::S_syst() {
  723. return SendFTPCommand(NS_LITERAL_CSTRING("SYST" CRLF));
  724. }
  725. FTP_STATE
  726. nsFtpState::R_syst() {
  727. if (mResponseCode/100 == 2) {
  728. if (( mResponseMsg.Find("L8") > -1) ||
  729. ( mResponseMsg.Find("UNIX") > -1) ||
  730. ( mResponseMsg.Find("BSD") > -1) ||
  731. ( mResponseMsg.Find("MACOS Peter's Server") > -1) ||
  732. ( mResponseMsg.Find("MACOS WebSTAR FTP") > -1) ||
  733. ( mResponseMsg.Find("MVS") > -1) ||
  734. ( mResponseMsg.Find("OS/390") > -1) ||
  735. ( mResponseMsg.Find("OS/400") > -1)) {
  736. mServerType = FTP_UNIX_TYPE;
  737. } else if (( mResponseMsg.Find("WIN32", true) > -1) ||
  738. ( mResponseMsg.Find("windows", true) > -1)) {
  739. mServerType = FTP_NT_TYPE;
  740. } else if (mResponseMsg.Find("OS/2", true) > -1) {
  741. mServerType = FTP_OS2_TYPE;
  742. } else if (mResponseMsg.Find("VMS", true) > -1) {
  743. mServerType = FTP_VMS_TYPE;
  744. } else {
  745. NS_ERROR("Server type list format unrecognized.");
  746. // clear mResponseMsg, which is displayed to the user.
  747. mResponseMsg = "";
  748. return FTP_ERROR;
  749. }
  750. return FTP_S_FEAT;
  751. }
  752. if (mResponseCode/100 == 5) {
  753. // server didn't like the SYST command. Probably (500, 501, 502)
  754. // No clue. We will just hope it is UNIX type server.
  755. mServerType = FTP_UNIX_TYPE;
  756. return FTP_S_FEAT;
  757. }
  758. return FTP_ERROR;
  759. }
  760. nsresult
  761. nsFtpState::S_acct() {
  762. return SendFTPCommand(NS_LITERAL_CSTRING("ACCT noaccount" CRLF));
  763. }
  764. FTP_STATE
  765. nsFtpState::R_acct() {
  766. if (mResponseCode/100 == 2)
  767. return FTP_S_SYST;
  768. return FTP_ERROR;
  769. }
  770. nsresult
  771. nsFtpState::S_type() {
  772. return SendFTPCommand(NS_LITERAL_CSTRING("TYPE I" CRLF));
  773. }
  774. FTP_STATE
  775. nsFtpState::R_type() {
  776. if (mResponseCode/100 != 2)
  777. return FTP_ERROR;
  778. return FTP_S_PASV;
  779. }
  780. nsresult
  781. nsFtpState::S_cwd() {
  782. // Don't cache the connection if PWD command failed
  783. if (mPwd.IsEmpty())
  784. mCacheConnection = false;
  785. nsAutoCString cwdStr;
  786. if (mAction != PUT)
  787. cwdStr = mPath;
  788. if (cwdStr.IsEmpty() || cwdStr.First() != '/')
  789. cwdStr.Insert(mPwd,0);
  790. if (mServerType == FTP_VMS_TYPE)
  791. ConvertDirspecToVMS(cwdStr);
  792. cwdStr.Insert("CWD ",0);
  793. cwdStr.Append(CRLF);
  794. return SendFTPCommand(cwdStr);
  795. }
  796. FTP_STATE
  797. nsFtpState::R_cwd() {
  798. if (mResponseCode/100 == 2) {
  799. if (mAction == PUT)
  800. return FTP_S_STOR;
  801. return FTP_S_LIST;
  802. }
  803. return FTP_ERROR;
  804. }
  805. nsresult
  806. nsFtpState::S_size() {
  807. nsAutoCString sizeBuf(mPath);
  808. if (sizeBuf.IsEmpty() || sizeBuf.First() != '/')
  809. sizeBuf.Insert(mPwd,0);
  810. if (mServerType == FTP_VMS_TYPE)
  811. ConvertFilespecToVMS(sizeBuf);
  812. sizeBuf.Insert("SIZE ",0);
  813. sizeBuf.Append(CRLF);
  814. return SendFTPCommand(sizeBuf);
  815. }
  816. FTP_STATE
  817. nsFtpState::R_size() {
  818. if (mResponseCode/100 == 2) {
  819. PR_sscanf(mResponseMsg.get() + 4, "%llu", &mFileSize);
  820. mChannel->SetContentLength(mFileSize);
  821. }
  822. // We may want to be able to resume this
  823. return FTP_S_MDTM;
  824. }
  825. nsresult
  826. nsFtpState::S_mdtm() {
  827. nsAutoCString mdtmBuf(mPath);
  828. if (mdtmBuf.IsEmpty() || mdtmBuf.First() != '/')
  829. mdtmBuf.Insert(mPwd,0);
  830. if (mServerType == FTP_VMS_TYPE)
  831. ConvertFilespecToVMS(mdtmBuf);
  832. mdtmBuf.Insert("MDTM ",0);
  833. mdtmBuf.Append(CRLF);
  834. return SendFTPCommand(mdtmBuf);
  835. }
  836. FTP_STATE
  837. nsFtpState::R_mdtm() {
  838. if (mResponseCode == 213) {
  839. mResponseMsg.Cut(0,4);
  840. mResponseMsg.Trim(" \t\r\n");
  841. // yyyymmddhhmmss
  842. if (mResponseMsg.Length() != 14) {
  843. NS_ASSERTION(mResponseMsg.Length() == 14, "Unknown MDTM response");
  844. } else {
  845. mModTime = mResponseMsg;
  846. // Save lastModified time for downloaded files.
  847. nsAutoCString timeString;
  848. nsresult error;
  849. PRExplodedTime exTime;
  850. mResponseMsg.Mid(timeString, 0, 4);
  851. exTime.tm_year = timeString.ToInteger(&error);
  852. mResponseMsg.Mid(timeString, 4, 2);
  853. exTime.tm_month = timeString.ToInteger(&error) - 1; //january = 0
  854. mResponseMsg.Mid(timeString, 6, 2);
  855. exTime.tm_mday = timeString.ToInteger(&error);
  856. mResponseMsg.Mid(timeString, 8, 2);
  857. exTime.tm_hour = timeString.ToInteger(&error);
  858. mResponseMsg.Mid(timeString, 10, 2);
  859. exTime.tm_min = timeString.ToInteger(&error);
  860. mResponseMsg.Mid(timeString, 12, 2);
  861. exTime.tm_sec = timeString.ToInteger(&error);
  862. exTime.tm_usec = 0;
  863. exTime.tm_params.tp_gmt_offset = 0;
  864. exTime.tm_params.tp_dst_offset = 0;
  865. PR_NormalizeTime(&exTime, PR_GMTParameters);
  866. exTime.tm_params = PR_LocalTimeParameters(&exTime);
  867. PRTime time = PR_ImplodeTime(&exTime);
  868. (void)mChannel->SetLastModifiedTime(time);
  869. }
  870. }
  871. nsCString entityID;
  872. entityID.Truncate();
  873. entityID.AppendInt(int64_t(mFileSize));
  874. entityID.Append('/');
  875. entityID.Append(mModTime);
  876. mChannel->SetEntityID(entityID);
  877. // We weren't asked to resume
  878. if (!mChannel->ResumeRequested())
  879. return FTP_S_RETR;
  880. //if (our entityID == supplied one (if any))
  881. if (mSuppliedEntityID.IsEmpty() || entityID.Equals(mSuppliedEntityID))
  882. return FTP_S_REST;
  883. mInternalError = NS_ERROR_ENTITY_CHANGED;
  884. mResponseMsg.Truncate();
  885. return FTP_ERROR;
  886. }
  887. nsresult
  888. nsFtpState::SetContentType()
  889. {
  890. // FTP directory URLs don't always end in a slash. Make sure they do.
  891. // This check needs to be here rather than a more obvious place
  892. // (e.g. LIST command processing) so that it ensures the terminating
  893. // slash is appended for the new request case.
  894. if (!mPath.IsEmpty() && mPath.Last() != '/') {
  895. nsCOMPtr<nsIURL> url = (do_QueryInterface(mChannel->URI()));
  896. nsAutoCString filePath;
  897. if(NS_SUCCEEDED(url->GetFilePath(filePath))) {
  898. filePath.Append('/');
  899. url->SetFilePath(filePath);
  900. }
  901. }
  902. return mChannel->SetContentType(
  903. NS_LITERAL_CSTRING(APPLICATION_HTTP_INDEX_FORMAT));
  904. }
  905. nsresult
  906. nsFtpState::S_list() {
  907. nsresult rv = SetContentType();
  908. if (NS_FAILED(rv))
  909. // XXX Invalid cast of FTP_STATE to nsresult -- FTP_ERROR has
  910. // value < 0x80000000 and will pass NS_SUCCEEDED() (bug 778109)
  911. return (nsresult)FTP_ERROR;
  912. rv = mChannel->PushStreamConverter("text/ftp-dir",
  913. APPLICATION_HTTP_INDEX_FORMAT);
  914. if (NS_FAILED(rv)) {
  915. // clear mResponseMsg which is displayed to the user.
  916. // TODO: we should probably set this to something meaningful.
  917. mResponseMsg = "";
  918. return rv;
  919. }
  920. // dir listings aren't resumable
  921. NS_ENSURE_TRUE(!mChannel->ResumeRequested(), NS_ERROR_NOT_RESUMABLE);
  922. mChannel->SetEntityID(EmptyCString());
  923. const char *listString;
  924. if (mServerType == FTP_VMS_TYPE) {
  925. listString = "LIST *.*;0" CRLF;
  926. } else {
  927. listString = "LIST" CRLF;
  928. }
  929. return SendFTPCommand(nsDependentCString(listString));
  930. }
  931. FTP_STATE
  932. nsFtpState::R_list() {
  933. if (mResponseCode/100 == 1) {
  934. mRlist1xxReceived = true;
  935. // OK, time to start reading from the data connection.
  936. if (mDataStream && HasPendingCallback())
  937. mDataStream->AsyncWait(this, 0, 0, CallbackTarget());
  938. return FTP_READ_BUF;
  939. }
  940. if (mResponseCode/100 == 2 && mRlist1xxReceived) {
  941. //(DONE)
  942. mNextState = FTP_COMPLETE;
  943. mRlist1xxReceived = false;
  944. return FTP_COMPLETE;
  945. }
  946. return FTP_ERROR;
  947. }
  948. nsresult
  949. nsFtpState::S_retr() {
  950. nsAutoCString retrStr(mPath);
  951. if (retrStr.IsEmpty() || retrStr.First() != '/')
  952. retrStr.Insert(mPwd,0);
  953. if (mServerType == FTP_VMS_TYPE)
  954. ConvertFilespecToVMS(retrStr);
  955. retrStr.Insert("RETR ",0);
  956. retrStr.Append(CRLF);
  957. return SendFTPCommand(retrStr);
  958. }
  959. FTP_STATE
  960. nsFtpState::R_retr() {
  961. if (mResponseCode/100 == 2 && mRretr1xxReceived) {
  962. //(DONE)
  963. mNextState = FTP_COMPLETE;
  964. mRretr1xxReceived = false;
  965. return FTP_COMPLETE;
  966. }
  967. if (mResponseCode/100 == 1) {
  968. mRretr1xxReceived = true;
  969. if (mDataStream && HasPendingCallback())
  970. mDataStream->AsyncWait(this, 0, 0, CallbackTarget());
  971. return FTP_READ_BUF;
  972. }
  973. // These error codes are related to problems with the connection.
  974. // If we encounter any at this point, do not try CWD and abort.
  975. if (mResponseCode == 421 || mResponseCode == 425 || mResponseCode == 426)
  976. return FTP_ERROR;
  977. if (mResponseCode/100 == 5) {
  978. mRETRFailed = true;
  979. return FTP_S_PASV;
  980. }
  981. return FTP_S_CWD;
  982. }
  983. nsresult
  984. nsFtpState::S_rest() {
  985. nsAutoCString restString("REST ");
  986. // The int64_t cast is needed to avoid ambiguity
  987. restString.AppendInt(int64_t(mChannel->StartPos()), 10);
  988. restString.Append(CRLF);
  989. return SendFTPCommand(restString);
  990. }
  991. FTP_STATE
  992. nsFtpState::R_rest() {
  993. if (mResponseCode/100 == 4) {
  994. // If REST fails, then we can't resume
  995. mChannel->SetEntityID(EmptyCString());
  996. mInternalError = NS_ERROR_NOT_RESUMABLE;
  997. mResponseMsg.Truncate();
  998. return FTP_ERROR;
  999. }
  1000. return FTP_S_RETR;
  1001. }
  1002. nsresult
  1003. nsFtpState::S_stor() {
  1004. NS_ENSURE_STATE(mChannel->UploadStream());
  1005. NS_ASSERTION(mAction == PUT, "Wrong state to be here");
  1006. nsCOMPtr<nsIURL> url = do_QueryInterface(mChannel->URI());
  1007. NS_ASSERTION(url, "I thought you were a nsStandardURL");
  1008. nsAutoCString storStr;
  1009. url->GetFilePath(storStr);
  1010. NS_ASSERTION(!storStr.IsEmpty(), "What does it mean to store a empty path");
  1011. // kill the first slash since we want to be relative to CWD.
  1012. if (storStr.First() == '/')
  1013. storStr.Cut(0,1);
  1014. if (mServerType == FTP_VMS_TYPE)
  1015. ConvertFilespecToVMS(storStr);
  1016. NS_UnescapeURL(storStr);
  1017. storStr.Insert("STOR ",0);
  1018. storStr.Append(CRLF);
  1019. return SendFTPCommand(storStr);
  1020. }
  1021. FTP_STATE
  1022. nsFtpState::R_stor() {
  1023. if (mResponseCode/100 == 2 && mRstor1xxReceived) {
  1024. //(DONE)
  1025. mNextState = FTP_COMPLETE;
  1026. mStorReplyReceived = true;
  1027. // Call Close() if it was not called in nsFtpState::OnStoprequest()
  1028. if (!mUploadRequest && !IsClosed())
  1029. Close();
  1030. mRstor1xxReceived = false;
  1031. return FTP_COMPLETE;
  1032. }
  1033. if (mResponseCode/100 == 1) {
  1034. mRstor1xxReceived = true;
  1035. LOG(("FTP:(%x) writing on DT\n", this));
  1036. return FTP_READ_BUF;
  1037. }
  1038. mStorReplyReceived = true;
  1039. return FTP_ERROR;
  1040. }
  1041. nsresult
  1042. nsFtpState::S_pasv() {
  1043. if (!mAddressChecked) {
  1044. // Find socket address
  1045. mAddressChecked = true;
  1046. mServerAddress.raw.family = AF_INET;
  1047. mServerAddress.inet.ip = htonl(INADDR_ANY);
  1048. mServerAddress.inet.port = htons(0);
  1049. nsITransport *controlSocket = mControlConnection->Transport();
  1050. if (!controlSocket)
  1051. // XXX Invalid cast of FTP_STATE to nsresult -- FTP_ERROR has
  1052. // value < 0x80000000 and will pass NS_SUCCEEDED() (bug 778109)
  1053. return (nsresult)FTP_ERROR;
  1054. nsCOMPtr<nsISocketTransport> sTrans = do_QueryInterface(controlSocket);
  1055. if (sTrans) {
  1056. nsresult rv = sTrans->GetPeerAddr(&mServerAddress);
  1057. if (NS_SUCCEEDED(rv)) {
  1058. if (!IsIPAddrAny(&mServerAddress))
  1059. mServerIsIPv6 = (mServerAddress.raw.family == AF_INET6) &&
  1060. !IsIPAddrV4Mapped(&mServerAddress);
  1061. else {
  1062. /*
  1063. * In case of SOCKS5 remote DNS resolution, we do
  1064. * not know the remote IP address. Still, if it is
  1065. * an IPV6 host, then the external address of the
  1066. * socks server should also be IPv6, and this is the
  1067. * self address of the transport.
  1068. */
  1069. NetAddr selfAddress;
  1070. rv = sTrans->GetSelfAddr(&selfAddress);
  1071. if (NS_SUCCEEDED(rv))
  1072. mServerIsIPv6 = (selfAddress.raw.family == AF_INET6) &&
  1073. !IsIPAddrV4Mapped(&selfAddress);
  1074. }
  1075. }
  1076. }
  1077. }
  1078. const char *string;
  1079. if (mServerIsIPv6) {
  1080. string = "EPSV" CRLF;
  1081. } else {
  1082. string = "PASV" CRLF;
  1083. }
  1084. return SendFTPCommand(nsDependentCString(string));
  1085. }
  1086. FTP_STATE
  1087. nsFtpState::R_pasv() {
  1088. if (mResponseCode/100 != 2)
  1089. return FTP_ERROR;
  1090. nsresult rv;
  1091. int32_t port;
  1092. nsAutoCString responseCopy(mResponseMsg);
  1093. char *response = responseCopy.BeginWriting();
  1094. char *ptr = response;
  1095. // Make sure to ignore the address in the PASV response (bug 370559)
  1096. if (mServerIsIPv6) {
  1097. // The returned string is of the form
  1098. // text (|||ppp|)
  1099. // Where '|' can be any single character
  1100. char delim;
  1101. while (*ptr && *ptr != '(')
  1102. ptr++;
  1103. if (*ptr++ != '(')
  1104. return FTP_ERROR;
  1105. delim = *ptr++;
  1106. if (!delim || *ptr++ != delim ||
  1107. *ptr++ != delim ||
  1108. *ptr < '0' || *ptr > '9')
  1109. return FTP_ERROR;
  1110. port = 0;
  1111. do {
  1112. port = port * 10 + *ptr++ - '0';
  1113. } while (*ptr >= '0' && *ptr <= '9');
  1114. if (*ptr++ != delim || *ptr != ')')
  1115. return FTP_ERROR;
  1116. } else {
  1117. // The returned address string can be of the form
  1118. // (xxx,xxx,xxx,xxx,ppp,ppp) or
  1119. // xxx,xxx,xxx,xxx,ppp,ppp (without parens)
  1120. int32_t h0, h1, h2, h3, p0, p1;
  1121. int32_t fields = 0;
  1122. // First try with parens
  1123. while (*ptr && *ptr != '(')
  1124. ++ptr;
  1125. if (*ptr) {
  1126. ++ptr;
  1127. fields = PR_sscanf(ptr,
  1128. "%ld,%ld,%ld,%ld,%ld,%ld",
  1129. &h0, &h1, &h2, &h3, &p0, &p1);
  1130. }
  1131. if (!*ptr || fields < 6) {
  1132. // OK, lets try w/o parens
  1133. ptr = response;
  1134. while (*ptr && *ptr != ',')
  1135. ++ptr;
  1136. if (*ptr) {
  1137. // backup to the start of the digits
  1138. do {
  1139. ptr--;
  1140. } while ((ptr >=response) && (*ptr >= '0') && (*ptr <= '9'));
  1141. ptr++; // get back onto the numbers
  1142. fields = PR_sscanf(ptr,
  1143. "%ld,%ld,%ld,%ld,%ld,%ld",
  1144. &h0, &h1, &h2, &h3, &p0, &p1);
  1145. }
  1146. }
  1147. NS_ASSERTION(fields == 6, "Can't parse PASV response");
  1148. if (fields < 6)
  1149. return FTP_ERROR;
  1150. port = ((int32_t) (p0<<8)) + p1;
  1151. }
  1152. bool newDataConn = true;
  1153. if (mDataTransport) {
  1154. // Reuse this connection only if its still alive, and the port
  1155. // is the same
  1156. nsCOMPtr<nsISocketTransport> strans = do_QueryInterface(mDataTransport);
  1157. if (strans) {
  1158. int32_t oldPort;
  1159. nsresult rv = strans->GetPort(&oldPort);
  1160. if (NS_SUCCEEDED(rv)) {
  1161. if (oldPort == port) {
  1162. bool isAlive;
  1163. if (NS_SUCCEEDED(strans->IsAlive(&isAlive)) && isAlive)
  1164. newDataConn = false;
  1165. }
  1166. }
  1167. }
  1168. if (newDataConn) {
  1169. mDataTransport->Close(NS_ERROR_ABORT);
  1170. mDataTransport = nullptr;
  1171. mDataStream = nullptr;
  1172. }
  1173. }
  1174. if (newDataConn) {
  1175. // now we know where to connect our data channel
  1176. nsCOMPtr<nsISocketTransportService> sts =
  1177. do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID);
  1178. if (!sts)
  1179. return FTP_ERROR;
  1180. nsCOMPtr<nsISocketTransport> strans;
  1181. nsAutoCString host;
  1182. if (!IsIPAddrAny(&mServerAddress)) {
  1183. char buf[kIPv6CStrBufSize];
  1184. NetAddrToString(&mServerAddress, buf, sizeof(buf));
  1185. host.Assign(buf);
  1186. } else {
  1187. /*
  1188. * In case of SOCKS5 remote DNS resolving, the peer address
  1189. * fetched previously will be invalid (0.0.0.0): it is unknown
  1190. * to us. But we can pass on the original hostname to the
  1191. * connect for the data connection.
  1192. */
  1193. rv = mChannel->URI()->GetAsciiHost(host);
  1194. if (NS_FAILED(rv))
  1195. return FTP_ERROR;
  1196. }
  1197. rv = sts->CreateTransport(nullptr, 0, host,
  1198. port, mChannel->ProxyInfo(),
  1199. getter_AddRefs(strans)); // the data socket
  1200. if (NS_FAILED(rv))
  1201. return FTP_ERROR;
  1202. mDataTransport = strans;
  1203. strans->SetQoSBits(gFtpHandler->GetDataQoSBits());
  1204. LOG(("FTP:(%x) created DT (%s:%x)\n", this, host.get(), port));
  1205. // hook ourself up as a proxy for status notifications
  1206. rv = mDataTransport->SetEventSink(this, NS_GetCurrentThread());
  1207. NS_ENSURE_SUCCESS(rv, FTP_ERROR);
  1208. if (mAction == PUT) {
  1209. NS_ASSERTION(!mRETRFailed, "Failed before uploading");
  1210. // nsIUploadChannel requires the upload stream to support ReadSegments.
  1211. // therefore, we can open an unbuffered socket output stream.
  1212. nsCOMPtr<nsIOutputStream> output;
  1213. rv = mDataTransport->OpenOutputStream(nsITransport::OPEN_UNBUFFERED,
  1214. 0, 0, getter_AddRefs(output));
  1215. if (NS_FAILED(rv))
  1216. return FTP_ERROR;
  1217. // perform the data copy on the socket transport thread. we do this
  1218. // because "output" is a socket output stream, so the result is that
  1219. // all work will be done on the socket transport thread.
  1220. nsCOMPtr<nsIEventTarget> stEventTarget =
  1221. do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID);
  1222. if (!stEventTarget)
  1223. return FTP_ERROR;
  1224. nsCOMPtr<nsIAsyncStreamCopier> copier;
  1225. rv = NS_NewAsyncStreamCopier(getter_AddRefs(copier),
  1226. mChannel->UploadStream(),
  1227. output,
  1228. stEventTarget,
  1229. true, // upload stream is buffered
  1230. false); // output is NOT buffered
  1231. if (NS_FAILED(rv))
  1232. return FTP_ERROR;
  1233. rv = copier->AsyncCopy(this, nullptr);
  1234. if (NS_FAILED(rv))
  1235. return FTP_ERROR;
  1236. // hold a reference to the copier so we can cancel it if necessary.
  1237. mUploadRequest = copier;
  1238. // update the current working directory before sending the STOR
  1239. // command. this is needed since we might be reusing a control
  1240. // connection.
  1241. return FTP_S_CWD;
  1242. }
  1243. //
  1244. // else, we are reading from the data connection...
  1245. //
  1246. // open a buffered, asynchronous socket input stream
  1247. nsCOMPtr<nsIInputStream> input;
  1248. rv = mDataTransport->OpenInputStream(0,
  1249. nsIOService::gDefaultSegmentSize,
  1250. nsIOService::gDefaultSegmentCount,
  1251. getter_AddRefs(input));
  1252. NS_ENSURE_SUCCESS(rv, FTP_ERROR);
  1253. mDataStream = do_QueryInterface(input);
  1254. }
  1255. if (mRETRFailed || mPath.IsEmpty() || mPath.Last() == '/')
  1256. return FTP_S_CWD;
  1257. return FTP_S_SIZE;
  1258. }
  1259. nsresult
  1260. nsFtpState::S_feat() {
  1261. return SendFTPCommand(NS_LITERAL_CSTRING("FEAT" CRLF));
  1262. }
  1263. FTP_STATE
  1264. nsFtpState::R_feat() {
  1265. if (mResponseCode/100 == 2) {
  1266. if (mResponseMsg.Find(NS_LITERAL_CSTRING(CRLF " UTF8" CRLF), true) > -1) {
  1267. // This FTP server supports UTF-8 encoding
  1268. mChannel->SetContentCharset(NS_LITERAL_CSTRING("UTF-8"));
  1269. mUseUTF8 = true;
  1270. return FTP_S_OPTS;
  1271. }
  1272. }
  1273. mUseUTF8 = false;
  1274. return FTP_S_PWD;
  1275. }
  1276. nsresult
  1277. nsFtpState::S_opts() {
  1278. // This command is for compatibility of old FTP spec (IETF Draft)
  1279. return SendFTPCommand(NS_LITERAL_CSTRING("OPTS UTF8 ON" CRLF));
  1280. }
  1281. FTP_STATE
  1282. nsFtpState::R_opts() {
  1283. // Ignore error code because "OPTS UTF8 ON" is for compatibility of
  1284. // FTP server using IETF draft
  1285. return FTP_S_PWD;
  1286. }
  1287. ////////////////////////////////////////////////////////////////////////////////
  1288. // nsIRequest methods:
  1289. nsresult
  1290. nsFtpState::Init(nsFtpChannel *channel)
  1291. {
  1292. // parameter validation
  1293. NS_ASSERTION(channel, "FTP: needs a channel");
  1294. mChannel = channel; // a straight ref ptr to the channel
  1295. // initialize counter for network metering
  1296. mCountRecv = 0;
  1297. mKeepRunning = true;
  1298. mSuppliedEntityID = channel->EntityID();
  1299. if (channel->UploadStream())
  1300. mAction = PUT;
  1301. nsresult rv;
  1302. nsCOMPtr<nsIURL> url = do_QueryInterface(mChannel->URI());
  1303. nsAutoCString host;
  1304. if (url) {
  1305. rv = url->GetAsciiHost(host);
  1306. } else {
  1307. rv = mChannel->URI()->GetAsciiHost(host);
  1308. }
  1309. if (NS_FAILED(rv) || host.IsEmpty()) {
  1310. return NS_ERROR_MALFORMED_URI;
  1311. }
  1312. nsAutoCString path;
  1313. if (url) {
  1314. rv = url->GetFilePath(path);
  1315. } else {
  1316. rv = mChannel->URI()->GetPath(path);
  1317. }
  1318. if (NS_FAILED(rv))
  1319. return rv;
  1320. removeParamsFromPath(path);
  1321. // FTP parameters such as type=i are ignored
  1322. if (url) {
  1323. url->SetFilePath(path);
  1324. } else {
  1325. mChannel->URI()->SetPath(path);
  1326. }
  1327. // Skip leading slash
  1328. char *fwdPtr = path.BeginWriting();
  1329. if (!fwdPtr)
  1330. return NS_ERROR_OUT_OF_MEMORY;
  1331. if (*fwdPtr == '/')
  1332. fwdPtr++;
  1333. if (*fwdPtr != '\0') {
  1334. // now unescape it... %xx reduced inline to resulting character
  1335. int32_t len = NS_UnescapeURL(fwdPtr);
  1336. mPath.Assign(fwdPtr, len);
  1337. #ifdef DEBUG
  1338. if (mPath.FindCharInSet(CRLF) >= 0)
  1339. NS_ERROR("NewURI() should've prevented this!!!");
  1340. #endif
  1341. }
  1342. // pull any username and/or password out of the uri
  1343. nsAutoCString uname;
  1344. rv = mChannel->URI()->GetUsername(uname);
  1345. if (NS_FAILED(rv))
  1346. return rv;
  1347. if (!uname.IsEmpty() && !uname.EqualsLiteral("anonymous")) {
  1348. mAnonymous = false;
  1349. CopyUTF8toUTF16(NS_UnescapeURL(uname), mUsername);
  1350. // return an error if we find a CR or LF in the username
  1351. if (uname.FindCharInSet(CRLF) >= 0)
  1352. return NS_ERROR_MALFORMED_URI;
  1353. }
  1354. nsAutoCString password;
  1355. rv = mChannel->URI()->GetPassword(password);
  1356. if (NS_FAILED(rv))
  1357. return rv;
  1358. CopyUTF8toUTF16(NS_UnescapeURL(password), mPassword);
  1359. // return an error if we find a CR or LF in the password
  1360. if (mPassword.FindCharInSet(CRLF) >= 0)
  1361. return NS_ERROR_MALFORMED_URI;
  1362. int32_t port;
  1363. rv = mChannel->URI()->GetPort(&port);
  1364. if (NS_FAILED(rv))
  1365. return rv;
  1366. if (port > 0)
  1367. mPort = port;
  1368. // Lookup Proxy information asynchronously if it isn't already set
  1369. // on the channel and if we aren't configured explicitly to go directly
  1370. nsCOMPtr<nsIProtocolProxyService> pps =
  1371. do_GetService(NS_PROTOCOLPROXYSERVICE_CONTRACTID);
  1372. if (pps && !mChannel->ProxyInfo()) {
  1373. pps->AsyncResolve(static_cast<nsIChannel*>(mChannel), 0, this,
  1374. getter_AddRefs(mProxyRequest));
  1375. }
  1376. return NS_OK;
  1377. }
  1378. void
  1379. nsFtpState::Connect()
  1380. {
  1381. mState = FTP_COMMAND_CONNECT;
  1382. mNextState = FTP_S_USER;
  1383. nsresult rv = Process();
  1384. // check for errors.
  1385. if (NS_FAILED(rv)) {
  1386. LOG(("FTP:Process() failed: %x\n", rv));
  1387. mInternalError = NS_ERROR_FAILURE;
  1388. mState = FTP_ERROR;
  1389. CloseWithStatus(mInternalError);
  1390. }
  1391. }
  1392. void
  1393. nsFtpState::KillControlConnection()
  1394. {
  1395. mControlReadCarryOverBuf.Truncate(0);
  1396. mAddressChecked = false;
  1397. mServerIsIPv6 = false;
  1398. // if everything went okay, save the connection.
  1399. // FIX: need a better way to determine if we can cache the connections.
  1400. // there are some errors which do not mean that we need to kill the connection
  1401. // e.g. fnf.
  1402. if (!mControlConnection)
  1403. return;
  1404. // kill the reference to ourselves in the control connection.
  1405. mControlConnection->WaitData(nullptr);
  1406. if (NS_SUCCEEDED(mInternalError) &&
  1407. NS_SUCCEEDED(mControlStatus) &&
  1408. mControlConnection->IsAlive() &&
  1409. mCacheConnection) {
  1410. LOG_INFO(("FTP:(%p) caching CC(%p)", this, mControlConnection.get()));
  1411. // Store connection persistent data
  1412. mControlConnection->mServerType = mServerType;
  1413. mControlConnection->mPassword = mPassword;
  1414. mControlConnection->mPwd = mPwd;
  1415. mControlConnection->mUseUTF8 = mUseUTF8;
  1416. nsresult rv = NS_OK;
  1417. // Don't cache controlconnection if anonymous (bug #473371)
  1418. if (!mChannel->HasLoadFlag(nsIRequest::LOAD_ANONYMOUS))
  1419. rv = gFtpHandler->InsertConnection(mChannel->URI(),
  1420. mControlConnection);
  1421. // Can't cache it? Kill it then.
  1422. mControlConnection->Disconnect(rv);
  1423. } else {
  1424. mControlConnection->Disconnect(NS_BINDING_ABORTED);
  1425. }
  1426. mControlConnection = nullptr;
  1427. }
  1428. nsresult
  1429. nsFtpState::StopProcessing()
  1430. {
  1431. // Only do this function once.
  1432. if (!mKeepRunning)
  1433. return NS_OK;
  1434. mKeepRunning = false;
  1435. LOG_INFO(("FTP:(%x) nsFtpState stopping", this));
  1436. if (NS_FAILED(mInternalError) && !mResponseMsg.IsEmpty()) {
  1437. NS_ERROR("FTP: bad control status.");
  1438. // check to see if the control status is bad; forward the error message.
  1439. nsCOMPtr<nsIFTPChannelParentInternal> ftpChanP;
  1440. mChannel->GetCallback(ftpChanP);
  1441. if (ftpChanP) {
  1442. ftpChanP->SetErrorMsg(mResponseMsg.get(), mUseUTF8);
  1443. }
  1444. }
  1445. nsresult broadcastErrorCode = mControlStatus;
  1446. if (NS_SUCCEEDED(broadcastErrorCode))
  1447. broadcastErrorCode = mInternalError;
  1448. mInternalError = broadcastErrorCode;
  1449. KillControlConnection();
  1450. // XXX This can fire before we are done loading data. Is that a problem?
  1451. OnTransportStatus(nullptr, NS_NET_STATUS_END_FTP_TRANSACTION, 0, 0);
  1452. if (NS_FAILED(broadcastErrorCode))
  1453. CloseWithStatus(broadcastErrorCode);
  1454. return NS_OK;
  1455. }
  1456. nsresult
  1457. nsFtpState::SendFTPCommand(const nsCSubstring& command)
  1458. {
  1459. NS_ASSERTION(mControlConnection, "null control connection");
  1460. // we don't want to log the password:
  1461. nsAutoCString logcmd(command);
  1462. if (StringBeginsWith(command, NS_LITERAL_CSTRING("PASS ")))
  1463. logcmd = "PASS xxxxx";
  1464. LOG(("FTP:(%x) writing \"%s\"\n", this, logcmd.get()));
  1465. nsCOMPtr<nsIFTPEventSink> ftpSink;
  1466. mChannel->GetFTPEventSink(ftpSink);
  1467. if (ftpSink)
  1468. ftpSink->OnFTPControlLog(false, logcmd.get());
  1469. if (mControlConnection)
  1470. return mControlConnection->Write(command);
  1471. return NS_ERROR_FAILURE;
  1472. }
  1473. // Convert a unix-style filespec to VMS format
  1474. // /foo/fred/barney/file.txt -> foo:[fred.barney]file.txt
  1475. // /foo/file.txt -> foo:[000000]file.txt
  1476. void
  1477. nsFtpState::ConvertFilespecToVMS(nsCString& fileString)
  1478. {
  1479. int ntok=1;
  1480. char *t, *nextToken;
  1481. nsAutoCString fileStringCopy;
  1482. // Get a writeable copy we can strtok with.
  1483. fileStringCopy = fileString;
  1484. t = nsCRT::strtok(fileStringCopy.BeginWriting(), "/", &nextToken);
  1485. if (t)
  1486. while (nsCRT::strtok(nextToken, "/", &nextToken))
  1487. ntok++; // count number of terms (tokens)
  1488. LOG(("FTP:(%x) ConvertFilespecToVMS ntok: %d\n", this, ntok));
  1489. LOG(("FTP:(%x) ConvertFilespecToVMS from: \"%s\"\n", this, fileString.get()));
  1490. if (fileString.First() == '/') {
  1491. // absolute filespec
  1492. // / -> []
  1493. // /a -> a (doesn't really make much sense)
  1494. // /a/b -> a:[000000]b
  1495. // /a/b/c -> a:[b]c
  1496. // /a/b/c/d -> a:[b.c]d
  1497. if (ntok == 1) {
  1498. if (fileString.Length() == 1) {
  1499. // Just a slash
  1500. fileString.Truncate();
  1501. fileString.AppendLiteral("[]");
  1502. } else {
  1503. // just copy the name part (drop the leading slash)
  1504. fileStringCopy = fileString;
  1505. fileString = Substring(fileStringCopy, 1,
  1506. fileStringCopy.Length()-1);
  1507. }
  1508. } else {
  1509. // Get another copy since the last one was written to.
  1510. fileStringCopy = fileString;
  1511. fileString.Truncate();
  1512. fileString.Append(nsCRT::strtok(fileStringCopy.BeginWriting(),
  1513. "/", &nextToken));
  1514. fileString.AppendLiteral(":[");
  1515. if (ntok > 2) {
  1516. for (int i=2; i<ntok; i++) {
  1517. if (i > 2) fileString.Append('.');
  1518. fileString.Append(nsCRT::strtok(nextToken,
  1519. "/", &nextToken));
  1520. }
  1521. } else {
  1522. fileString.AppendLiteral("000000");
  1523. }
  1524. fileString.Append(']');
  1525. fileString.Append(nsCRT::strtok(nextToken, "/", &nextToken));
  1526. }
  1527. } else {
  1528. // relative filespec
  1529. // a -> a
  1530. // a/b -> [.a]b
  1531. // a/b/c -> [.a.b]c
  1532. if (ntok == 1) {
  1533. // no slashes, just use the name as is
  1534. } else {
  1535. // Get another copy since the last one was written to.
  1536. fileStringCopy = fileString;
  1537. fileString.Truncate();
  1538. fileString.AppendLiteral("[.");
  1539. fileString.Append(nsCRT::strtok(fileStringCopy.BeginWriting(),
  1540. "/", &nextToken));
  1541. if (ntok > 2) {
  1542. for (int i=2; i<ntok; i++) {
  1543. fileString.Append('.');
  1544. fileString.Append(nsCRT::strtok(nextToken,
  1545. "/", &nextToken));
  1546. }
  1547. }
  1548. fileString.Append(']');
  1549. fileString.Append(nsCRT::strtok(nextToken, "/", &nextToken));
  1550. }
  1551. }
  1552. LOG(("FTP:(%x) ConvertFilespecToVMS to: \"%s\"\n", this, fileString.get()));
  1553. }
  1554. // Convert a unix-style dirspec to VMS format
  1555. // /foo/fred/barney/rubble -> foo:[fred.barney.rubble]
  1556. // /foo/fred -> foo:[fred]
  1557. // /foo -> foo:[000000]
  1558. // (null) -> (null)
  1559. void
  1560. nsFtpState::ConvertDirspecToVMS(nsCString& dirSpec)
  1561. {
  1562. LOG(("FTP:(%x) ConvertDirspecToVMS from: \"%s\"\n", this, dirSpec.get()));
  1563. if (!dirSpec.IsEmpty()) {
  1564. if (dirSpec.Last() != '/')
  1565. dirSpec.Append('/');
  1566. // we can use the filespec routine if we make it look like a file name
  1567. dirSpec.Append('x');
  1568. ConvertFilespecToVMS(dirSpec);
  1569. dirSpec.Truncate(dirSpec.Length()-1);
  1570. }
  1571. LOG(("FTP:(%x) ConvertDirspecToVMS to: \"%s\"\n", this, dirSpec.get()));
  1572. }
  1573. // Convert an absolute VMS style dirspec to UNIX format
  1574. void
  1575. nsFtpState::ConvertDirspecFromVMS(nsCString& dirSpec)
  1576. {
  1577. LOG(("FTP:(%x) ConvertDirspecFromVMS from: \"%s\"\n", this, dirSpec.get()));
  1578. if (dirSpec.IsEmpty()) {
  1579. dirSpec.Insert('.', 0);
  1580. } else {
  1581. dirSpec.Insert('/', 0);
  1582. dirSpec.ReplaceSubstring(":[", "/");
  1583. dirSpec.ReplaceChar('.', '/');
  1584. dirSpec.ReplaceChar(']', '/');
  1585. }
  1586. LOG(("FTP:(%x) ConvertDirspecFromVMS to: \"%s\"\n", this, dirSpec.get()));
  1587. }
  1588. //-----------------------------------------------------------------------------
  1589. NS_IMETHODIMP
  1590. nsFtpState::OnTransportStatus(nsITransport *transport, nsresult status,
  1591. int64_t progress, int64_t progressMax)
  1592. {
  1593. // Mix signals from both the control and data connections.
  1594. // Ignore data transfer events on the control connection.
  1595. if (mControlConnection && transport == mControlConnection->Transport()) {
  1596. switch (status) {
  1597. case NS_NET_STATUS_RESOLVING_HOST:
  1598. case NS_NET_STATUS_RESOLVED_HOST:
  1599. case NS_NET_STATUS_CONNECTING_TO:
  1600. case NS_NET_STATUS_CONNECTED_TO:
  1601. case NS_NET_STATUS_TLS_HANDSHAKE_STARTING:
  1602. case NS_NET_STATUS_TLS_HANDSHAKE_ENDED:
  1603. break;
  1604. default:
  1605. return NS_OK;
  1606. }
  1607. }
  1608. // Ignore the progressMax value from the socket. We know the true size of
  1609. // the file based on the response from our SIZE request. Additionally, only
  1610. // report the max progress based on where we started/resumed.
  1611. mChannel->OnTransportStatus(nullptr, status, progress,
  1612. mFileSize - mChannel->StartPos());
  1613. return NS_OK;
  1614. }
  1615. //-----------------------------------------------------------------------------
  1616. NS_IMETHODIMP
  1617. nsFtpState::OnStartRequest(nsIRequest *request, nsISupports *context)
  1618. {
  1619. mStorReplyReceived = false;
  1620. return NS_OK;
  1621. }
  1622. NS_IMETHODIMP
  1623. nsFtpState::OnStopRequest(nsIRequest *request, nsISupports *context,
  1624. nsresult status)
  1625. {
  1626. mUploadRequest = nullptr;
  1627. // Close() will be called when reply to STOR command is received
  1628. // see bug #389394
  1629. if (!mStorReplyReceived)
  1630. return NS_OK;
  1631. // We're done uploading. Let our consumer know that we're done.
  1632. Close();
  1633. return NS_OK;
  1634. }
  1635. //-----------------------------------------------------------------------------
  1636. NS_IMETHODIMP
  1637. nsFtpState::Available(uint64_t *result)
  1638. {
  1639. if (mDataStream)
  1640. return mDataStream->Available(result);
  1641. return nsBaseContentStream::Available(result);
  1642. }
  1643. NS_IMETHODIMP
  1644. nsFtpState::ReadSegments(nsWriteSegmentFun writer, void *closure,
  1645. uint32_t count, uint32_t *result)
  1646. {
  1647. // Insert a thunk here so that the input stream passed to the writer is this
  1648. // input stream instead of mDataStream.
  1649. if (mDataStream) {
  1650. nsWriteSegmentThunk thunk = { this, writer, closure };
  1651. nsresult rv;
  1652. rv = mDataStream->ReadSegments(NS_WriteSegmentThunk, &thunk, count,
  1653. result);
  1654. if (NS_SUCCEEDED(rv)) {
  1655. CountRecvBytes(*result);
  1656. }
  1657. return rv;
  1658. }
  1659. return nsBaseContentStream::ReadSegments(writer, closure, count, result);
  1660. }
  1661. nsresult
  1662. nsFtpState::SaveNetworkStats(bool enforce)
  1663. {
  1664. return NS_ERROR_NOT_IMPLEMENTED;
  1665. }
  1666. NS_IMETHODIMP
  1667. nsFtpState::CloseWithStatus(nsresult status)
  1668. {
  1669. LOG(("FTP:(%p) close [%x]\n", this, status));
  1670. // Shutdown the control connection processing if we are being closed with an
  1671. // error. Note: This method may be called several times.
  1672. if (!IsClosed() && status != NS_BASE_STREAM_CLOSED && NS_FAILED(status)) {
  1673. if (NS_SUCCEEDED(mInternalError))
  1674. mInternalError = status;
  1675. StopProcessing();
  1676. }
  1677. if (mUploadRequest) {
  1678. mUploadRequest->Cancel(NS_ERROR_ABORT);
  1679. mUploadRequest = nullptr;
  1680. }
  1681. if (mDataTransport) {
  1682. // Save the network stats before data transport is closing.
  1683. SaveNetworkStats(true);
  1684. // Shutdown the data transport.
  1685. mDataTransport->Close(NS_ERROR_ABORT);
  1686. mDataTransport = nullptr;
  1687. }
  1688. mDataStream = nullptr;
  1689. return nsBaseContentStream::CloseWithStatus(status);
  1690. }
  1691. static nsresult
  1692. CreateHTTPProxiedChannel(nsIChannel *channel, nsIProxyInfo *pi, nsIChannel **newChannel)
  1693. {
  1694. nsresult rv;
  1695. nsCOMPtr<nsIIOService> ioService = do_GetIOService(&rv);
  1696. if (NS_FAILED(rv))
  1697. return rv;
  1698. nsCOMPtr<nsIProtocolHandler> handler;
  1699. rv = ioService->GetProtocolHandler("http", getter_AddRefs(handler));
  1700. if (NS_FAILED(rv))
  1701. return rv;
  1702. nsCOMPtr<nsIProxiedProtocolHandler> pph = do_QueryInterface(handler, &rv);
  1703. if (NS_FAILED(rv))
  1704. return rv;
  1705. nsCOMPtr<nsIURI> uri;
  1706. channel->GetURI(getter_AddRefs(uri));
  1707. nsCOMPtr<nsILoadInfo> loadInfo;
  1708. channel->GetLoadInfo(getter_AddRefs(loadInfo));
  1709. return pph->NewProxiedChannel2(uri, pi, 0, nullptr, loadInfo, newChannel);
  1710. }
  1711. NS_IMETHODIMP
  1712. nsFtpState::OnProxyAvailable(nsICancelable *request, nsIChannel *channel,
  1713. nsIProxyInfo *pi, nsresult status)
  1714. {
  1715. mProxyRequest = nullptr;
  1716. // failed status code just implies DIRECT processing
  1717. if (NS_SUCCEEDED(status)) {
  1718. nsAutoCString type;
  1719. if (pi && NS_SUCCEEDED(pi->GetType(type)) && type.EqualsLiteral("http")) {
  1720. // Proxy the FTP url via HTTP
  1721. // This would have been easier to just return a HTTP channel directly
  1722. // from nsIIOService::NewChannelFromURI(), but the proxy type cannot
  1723. // be reliabliy determined synchronously without jank due to pac, etc..
  1724. LOG(("FTP:(%p) Configured to use a HTTP proxy channel\n", this));
  1725. nsCOMPtr<nsIChannel> newChannel;
  1726. if (NS_SUCCEEDED(CreateHTTPProxiedChannel(channel, pi,
  1727. getter_AddRefs(newChannel))) &&
  1728. NS_SUCCEEDED(mChannel->Redirect(newChannel,
  1729. nsIChannelEventSink::REDIRECT_INTERNAL,
  1730. true))) {
  1731. LOG(("FTP:(%p) Redirected to use a HTTP proxy channel\n", this));
  1732. return NS_OK;
  1733. }
  1734. }
  1735. else if (pi) {
  1736. // Proxy using the FTP protocol routed through a socks proxy
  1737. LOG(("FTP:(%p) Configured to use a SOCKS proxy channel\n", this));
  1738. mChannel->SetProxyInfo(pi);
  1739. }
  1740. }
  1741. if (mDeferredCallbackPending) {
  1742. mDeferredCallbackPending = false;
  1743. OnCallbackPending();
  1744. }
  1745. return NS_OK;
  1746. }
  1747. void
  1748. nsFtpState::OnCallbackPending()
  1749. {
  1750. if (mState == FTP_INIT) {
  1751. if (mProxyRequest) {
  1752. mDeferredCallbackPending = true;
  1753. return;
  1754. }
  1755. Connect();
  1756. } else if (mDataStream) {
  1757. mDataStream->AsyncWait(this, 0, 0, CallbackTarget());
  1758. }
  1759. }