FTPSession.cpp 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869
  1. /*-------------------------------------------------------------------------
  2. * clintlib\FTPSession.cpp
  3. *
  4. * The implementation of IFTPSession
  5. *
  6. * See clintlib\FTPSession.h for descriptions of all public functions.
  7. *
  8. * Owner:
  9. *
  10. * Copyright 1986-2000 Microsoft Corporation, All Rights Reserved
  11. *-----------------------------------------------------------------------*/
  12. #include "pch.h"
  13. template<class TInterfaceClass>
  14. class CInternetSessionImpl :
  15. public TInterfaceClass
  16. {
  17. public:
  18. CInternetSessionImpl(IInternetSessionSink * pSink) : // pSink can be NULL
  19. m_buffer(NULL),
  20. m_hFile(NULL),
  21. m_pszFileList(NULL),
  22. m_pszFileListData(NULL),
  23. m_nLastErrorCode(0),
  24. m_nLastErrorCodeInThread(0),
  25. m_cBytesRead(0),
  26. m_bAbortDownload(false),
  27. m_hFTPSession(NULL),
  28. m_hFileConnection(NULL),
  29. m_pUpdateSink(pSink)
  30. {
  31. m_szLastError[0] = 0;
  32. m_szErrorInThread[0] = 0;
  33. m_eventResumeDownload = CreateEvent(NULL, FALSE, FALSE, NULL);
  34. m_eventKillDownload = CreateEvent(NULL, TRUE, FALSE, NULL);
  35. m_eventProgress = CreateEvent(NULL, FALSE, FALSE, NULL);
  36. m_eventDownloadTerminated = CreateEvent(NULL, TRUE, FALSE, NULL);
  37. m_eventFileCompleted = CreateEvent(NULL, FALSE, FALSE, NULL);
  38. debugf("Creating download thread.\n");
  39. DWORD dum;
  40. m_threadDownload = CreateThread(NULL, 0, DownloadThread, (void*)this, 0, &dum);
  41. if (m_pUpdateSink && m_threadDownload == NULL)
  42. debugf("Failed to create thread.\n");
  43. }
  44. virtual ~CInternetSessionImpl()
  45. {
  46. KillDownload();
  47. Disconnect();
  48. CloseHandle(m_eventResumeDownload);
  49. CloseHandle(m_eventKillDownload);
  50. CloseHandle(m_eventProgress);
  51. CloseHandle(m_eventDownloadTerminated);
  52. CloseHandle(m_eventFileCompleted);
  53. CloseHandle(m_threadDownload);
  54. if (m_pszFileList)
  55. {
  56. char * * psz = m_pszFileListData + 1; // first one is 0xFFFFFFFF, so skip it
  57. while (psz && *psz)
  58. free(*(psz++));
  59. delete[] m_pszFileListData;
  60. }
  61. }
  62. virtual int GetFileListIncrement() = 0;
  63. void SetSink(IInternetSessionSink * pUpdateSink)
  64. {
  65. m_pUpdateSink = pUpdateSink;
  66. }
  67. bool InitiateDownload(const char * const * pszFileList,
  68. const char * szDestFolder,
  69. bool bDisconnectWhenDone,
  70. int nMaxBufferSize)
  71. {
  72. //
  73. // Prepare a memory buffer for the filelist, and copy pszFileList to m_pszFileList
  74. //
  75. {
  76. // iterate once to find number of entries
  77. char * * psz = const_cast<char * *>(pszFileList);
  78. int i = 0;
  79. while (psz && *psz)
  80. {
  81. i++;
  82. psz++;
  83. }
  84. m_pszFileListData = new char*[i+2];
  85. m_pszFileListData[0] = (char*)-1; // means not started yet;
  86. // StartNextFile() increments first thing; so (char*)-1 is not downloaded, (I chose -1 because it's different than NULL)
  87. // iterate again and allocate memory for each entry
  88. psz = const_cast<char * *>(pszFileList);
  89. i = 1;
  90. while (psz && *psz)
  91. {
  92. m_pszFileListData[i] = _strdup(*psz);
  93. i++;
  94. psz++;
  95. }
  96. m_pszFileListData[i] = NULL;
  97. m_pszFileList = m_pszFileListData;
  98. }
  99. strcpy(m_szDestFolder, szDestFolder);
  100. if (m_szDestFolder[0] == '\0' || // prevent access of not our memory
  101. m_szDestFolder[strlen(m_szDestFolder)-1] != '\\')
  102. {
  103. strcat(m_szDestFolder, "\\");
  104. }
  105. m_bAutoDisconnect = bDisconnectWhenDone;
  106. m_nBufferSize = nMaxBufferSize;
  107. if(m_buffer == NULL)
  108. m_buffer = (char*)::VirtualAlloc(NULL, m_nBufferSize, MEM_COMMIT, PAGE_READWRITE);
  109. assert(m_buffer);
  110. m_cTotalBytesRead = 0;
  111. ResetEvent(m_eventResumeDownload);
  112. ResetEvent(m_eventKillDownload);
  113. ResetEvent(m_eventProgress);
  114. ResetEvent(m_eventDownloadTerminated);
  115. ResetEvent(m_eventFileCompleted);
  116. return true;
  117. }
  118. bool Disconnect()
  119. {
  120. FinishCurrentFile(false);
  121. if (m_hInternetSession)
  122. {
  123. if (!InternetCloseHandle(m_hInternetSession))
  124. {
  125. DoError("Disconnect Failed");
  126. return false;
  127. }
  128. m_hInternetSession = NULL;
  129. }
  130. if (m_buffer)
  131. {
  132. ::VirtualFree((void*)m_buffer, 0, MEM_RELEASE);
  133. m_buffer = NULL;
  134. }
  135. return true;
  136. }
  137. const char* GetDownloadPath()
  138. {
  139. return m_szDestFolder;
  140. }
  141. const char* GetLastErrorMessage()
  142. {
  143. if (m_szLastError[0] != '\0')
  144. {
  145. return m_szLastError;
  146. }
  147. else return NULL; // no error has occured
  148. }
  149. void Abort(bool bAutoDisconnect)
  150. {
  151. KillDownload();
  152. m_bAbortDownload = true;
  153. if (bAutoDisconnect)
  154. Disconnect();
  155. }
  156. bool ContinueDownload()
  157. {
  158. if (m_szLastError[0] != '\0') // if aborted or previous error
  159. return false;
  160. // if error within download thread
  161. if (WaitForSingleObject(m_eventKillDownload, 0) == WAIT_OBJECT_0)
  162. {
  163. if (m_szErrorInThread[0] != 0)
  164. {
  165. SetLastError(m_nLastErrorCodeInThread);
  166. DoError(m_szErrorInThread);
  167. }
  168. else
  169. if (m_bAutoDisconnect)
  170. Disconnect();
  171. return false; // signal that we are done
  172. }
  173. // Note: *m_pszFileList is 0xFFFFFFFF if download thread hasn't started downloading yet
  174. if (*m_pszFileList != NULL && !m_bAbortDownload) // if not done
  175. {
  176. if (WaitForSingleObject(m_eventProgress, 0) == WAIT_OBJECT_0)
  177. {
  178. //
  179. // Flush buffer as if needed
  180. //
  181. if (m_cBytesRead == m_nBufferSize)
  182. {
  183. if (!FlushDownloadBuffer())
  184. return false;
  185. }
  186. // If progress updates are wanted
  187. if(m_pUpdateSink)
  188. {
  189. // Fire update
  190. m_pUpdateSink->OnProgress(m_cTotalBytesRead, *m_pszFileList, m_cCurrentFileBytesRead);
  191. }
  192. SetEvent(m_eventResumeDownload);
  193. }
  194. else if(WaitForSingleObject(m_eventFileCompleted, 0) == WAIT_OBJECT_0)
  195. {
  196. FinishCurrentFile(true);
  197. SetEvent(m_eventResumeDownload);
  198. }
  199. return true; // signal NOT yet done with entire transfer
  200. }
  201. else // if done
  202. {
  203. //
  204. // Close up
  205. //
  206. // signal complete transfer
  207. if (m_pUpdateSink)
  208. m_pUpdateSink->OnTransferFinished();
  209. if (m_bAutoDisconnect)
  210. Disconnect();
  211. return false;
  212. }
  213. }
  214. protected:
  215. enum DOWNLOAD_RESULT
  216. {
  217. DOWNLOAD_ERROR,
  218. DOWNLOAD_PROGRESS,
  219. FILE_COMPLETED,
  220. };
  221. static DWORD WINAPI DownloadThread(LPVOID pThreadParameter)
  222. {
  223. CInternetSessionImpl * pSession = (CInternetSessionImpl *) pThreadParameter;
  224. HANDLE pHandles[] = { pSession->m_eventKillDownload, pSession->m_eventResumeDownload };
  225. //
  226. // Wait for file download to resume or for abort
  227. //
  228. while (WaitForMultipleObjects(2, pHandles, FALSE, INFINITE) != WAIT_OBJECT_0)
  229. {
  230. if (pSession->m_hFile == NULL)
  231. {
  232. if (!pSession->StartNextFile())
  233. {
  234. SetEvent(pSession->m_eventKillDownload);
  235. break;
  236. }
  237. }
  238. if (pSession->m_hFile != NULL)
  239. {
  240. // DOWNLOAD_RESULT result = pSession->DownloadFileBlock();
  241. DOWNLOAD_RESULT result = DOWNLOAD_ERROR; // default to error, until we know better
  242. __try
  243. {
  244. result = pSession->DownloadFileBlock();
  245. }
  246. __except(1)
  247. {
  248. result = DOWNLOAD_ERROR;
  249. }
  250. if (result == DOWNLOAD_PROGRESS)
  251. {
  252. SetEvent(pSession->m_eventProgress);
  253. }
  254. else
  255. if (result == FILE_COMPLETED)
  256. {
  257. SetEvent(pSession->m_eventFileCompleted);
  258. }
  259. else
  260. if (result == DOWNLOAD_ERROR)
  261. {
  262. SetEvent(pSession->m_eventKillDownload);
  263. break;
  264. }
  265. }
  266. }
  267. debugf("Download thread exiting...\n");
  268. SetEvent(pSession->m_eventDownloadTerminated);
  269. ExitThread(0);
  270. return 0;
  271. }
  272. virtual bool StartNextFile()
  273. {
  274. m_cBytesRead = 0;
  275. m_cCurrentFileBytesRead = 0;
  276. ++m_pszFileList;
  277. unsigned cTries = 0;
  278. if (*m_pszFileList)
  279. {
  280. //
  281. // Open file for download
  282. //
  283. while (!(m_hFileConnection = FtpOpenFile(m_hFTPSession, *m_pszFileList, GENERIC_READ, FTP_TRANSFER_TYPE_BINARY | INTERNET_FLAG_RELOAD, 0)))
  284. {
  285. //
  286. // Okay, it failed. On NT5, this seems to sometimes happen for no apparent reason, so lets try a
  287. // few more things just to be sure.
  288. //
  289. cTries++;
  290. debugf("Failed to open file via FTP for download, try #%d; error code: %d\n", cTries, GetLastError());
  291. Sleep(500);
  292. if(cTries >= 10) // okay, it must be more than a glich--must be an error, like the file not existing
  293. {
  294. DoErrorInThread("Failed to open file (%s) for download.", *m_pszFileList);
  295. return false;
  296. }
  297. }
  298. return OpenDownloadFile();
  299. }
  300. return true;
  301. }
  302. bool FinishCurrentFile(bool bCompleted) // finish as in close, without any downloading
  303. {
  304. if (m_hFileConnection)
  305. {
  306. if (!InternetCloseHandle(m_hFileConnection))
  307. {
  308. DoError("InternetCloseHandle() Failed for download file");
  309. return false;
  310. }
  311. m_hFileConnection = NULL;
  312. }
  313. //
  314. // If done, write file and exit
  315. //
  316. if (!FlushDownloadBuffer())
  317. return false;
  318. if (!CloseDownloadFile(bCompleted))
  319. return false;
  320. return true;
  321. }
  322. DOWNLOAD_RESULT DownloadFileBlock()
  323. {
  324. unsigned long cBytesAvail, cBytesJustRead;
  325. //
  326. // Find out is data is available for receiving
  327. //
  328. if (!InternetQueryDataAvailable(m_hFileConnection, &cBytesAvail, 0, 0))
  329. {
  330. DoErrorInThread("InternetQueryDataAvailable() Failed.");
  331. return DOWNLOAD_ERROR;
  332. }
  333. // cBytesAvail = min(cBytesAvail, nMaxBlockSize);
  334. if (cBytesAvail == 0)
  335. {
  336. return FILE_COMPLETED;
  337. //return FinishCurrentFile();
  338. }
  339. //
  340. // Get the available data
  341. //
  342. unsigned long cBytesAttempted = min(cBytesAvail, m_nBufferSize-m_cBytesRead);
  343. if (!InternetReadFile((void*)m_hFileConnection, (void*)(m_buffer+m_cBytesRead), cBytesAttempted, &cBytesJustRead))
  344. {
  345. DoErrorInThread("InternetReadFile() Failed.");
  346. return DOWNLOAD_ERROR;
  347. }
  348. if (cBytesJustRead == 0) // testing proves this additional check is needed.
  349. {
  350. return FILE_COMPLETED;
  351. // return FinishCurrentFile();
  352. }
  353. /*
  354. INTERNET_BUFFERS buffer;
  355. buffer.lpvBuffer = m_buffer+m_cBytesRead;
  356. buffer.dwBufferLength = m_nBufferSize-m_cBytesRead;
  357. buffer.dwBufferTotal = buffer.dwBufferLength;
  358. buffer.dwStructSize = sizeof(INTERNET_BUFFERS);
  359. buffer.lpcszHeader = NULL;
  360. buffer.dwHeadersLength = 0;
  361. buffer.dwHeadersTotal = 0;
  362. buffer.Next = NULL;
  363. buffer.dwOffsetLow = 0;
  364. buffer.dwOffsetHigh = 0;
  365. if (!InternetReadFileEx(m_hFileConnection, &buffer, IRF_SYNC | IRF_NO_WAIT, 0))
  366. {
  367. DoError("FTP Read Failed.");
  368. return false;
  369. }
  370. unsigned cBytesJustRead = buffer.dwBufferTotal;
  371. */
  372. m_cBytesRead += cBytesJustRead;
  373. m_cCurrentFileBytesRead += cBytesJustRead;
  374. m_cTotalBytesRead += cBytesJustRead;
  375. return DOWNLOAD_PROGRESS;
  376. }
  377. bool OpenDownloadFile()
  378. {
  379. //
  380. // Make Path
  381. //
  382. char szFilename[MAX_PATH+20];
  383. strcpy(szFilename, m_szDestFolder);
  384. strcat(szFilename, *m_pszFileList);
  385. m_hFile = CreateFile(szFilename,
  386. GENERIC_WRITE,
  387. FILE_SHARE_READ,
  388. NULL,
  389. CREATE_ALWAYS,
  390. FILE_ATTRIBUTE_TEMPORARY, // don't write to disk right away (for better performance).
  391. NULL);
  392. if (m_hFile == INVALID_HANDLE_VALUE)
  393. {
  394. DoErrorInThread("Failed create file (%s) on local drive.", szFilename);
  395. return false;
  396. }
  397. return true;
  398. }
  399. bool FlushDownloadBuffer()
  400. {
  401. unsigned long cBytesWritten;
  402. if (m_cBytesRead != 0)
  403. {
  404. if(m_pUpdateSink)
  405. {
  406. if(m_pUpdateSink->OnDataReceived((void*)m_buffer, m_cBytesRead) == false)
  407. {
  408. // after looking at the data, user has decided to
  409. // skip this file.
  410. // NOTE: This hasn't been tested.
  411. ::InternetCloseHandle(m_hFileConnection);
  412. m_hFileConnection = NULL;
  413. ::CloseHandle(m_hFile);
  414. m_hFile = NULL;
  415. }
  416. }
  417. if (!WriteFile(m_hFile, (void*)m_buffer, m_cBytesRead, &cBytesWritten, NULL))
  418. {
  419. DoError("Failed to write the file (%s) to local drive : ", *m_pszFileList);
  420. return false;
  421. }
  422. m_cBytesRead = 0;
  423. }
  424. return true;
  425. }
  426. bool CloseDownloadFile(bool bCompleted)
  427. {
  428. if (m_hFile == NULL)
  429. return true;
  430. if (!::CloseHandle(m_hFile))
  431. DoError("Failed to close file %s", *m_pszFileList);
  432. m_hFile = NULL;
  433. //
  434. // If progress updates are wanted
  435. //
  436. if(m_pUpdateSink && bCompleted)
  437. {
  438. // Fire update
  439. if(!m_pUpdateSink->OnFileCompleted(*m_pszFileList))
  440. {
  441. m_pszFileList-= GetFileListIncrement();
  442. m_cTotalBytesRead -= m_cCurrentFileBytesRead;
  443. }
  444. }
  445. return true;
  446. }
  447. /*-------------------------------------------------------------------------
  448. * FormatErrorMessage()
  449. *-------------------------------------------------------------------------
  450. * Paramters:
  451. * dwErrorCode: take a dwErrorCode and print what it means as text
  452. *
  453. */
  454. void FormatErrorMessage(char *szBuffer, DWORD dwErrorCode)
  455. {
  456. sprintf(szBuffer,"(%d) ", dwErrorCode);
  457. FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM |
  458. FORMAT_MESSAGE_IGNORE_INSERTS,
  459. NULL,
  460. dwErrorCode,
  461. MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
  462. szBuffer + strlen(szBuffer),
  463. 128,
  464. NULL
  465. );
  466. strcat(m_szLastError, " ");
  467. unsigned long dummy, size = sizeof(m_szLastError) - strlen(szBuffer) - 2;
  468. InternetGetLastResponseInfo(&dummy, szBuffer + strlen(szBuffer), &size);
  469. }
  470. /*-------------------------------------------------------------------------
  471. * DoError()
  472. *-------------------------------------------------------------------------
  473. */
  474. void DoError(char * szFormat, ...)
  475. {
  476. if (m_szLastError[0] != 0) // don't erase over first error which can cause other errors
  477. return;
  478. m_nLastErrorCode = GetLastError();
  479. char szMsg[sizeof(m_szLastError) - 50];
  480. va_list pArg;
  481. va_start(pArg, szFormat);
  482. _vsnprintf(szMsg, sizeof(szMsg), szFormat, pArg);
  483. va_end(pArg);
  484. strcpy(m_szLastError, szMsg);
  485. FormatErrorMessage(m_szLastError + strlen(m_szLastError), m_nLastErrorCode);
  486. CloseDownloadFile(false);
  487. // by now GetLastError() has been cleared
  488. SetLastError(m_nLastErrorCode); // preserve this for external users
  489. if(m_pUpdateSink)
  490. m_pUpdateSink->OnError(m_szLastError);
  491. }
  492. void DoErrorInThread(char * szFormat, ...)
  493. {
  494. if (m_szErrorInThread[0] != 0) // don't erase over first error which can cause other errors
  495. return;
  496. m_nLastErrorCodeInThread = GetLastError();
  497. char szMsg[sizeof(m_szErrorInThread) - 50];
  498. va_list pArg;
  499. va_start(pArg, szFormat);
  500. _vsnprintf(szMsg, sizeof(szMsg), szFormat, pArg);
  501. va_end(pArg);
  502. strcpy(m_szErrorInThread, szMsg);
  503. }
  504. void KillDownload()
  505. {
  506. if (m_threadDownload)
  507. {
  508. SetEvent(m_eventKillDownload); // tell thread to exit
  509. int nAwaker = WaitForSingleObject(m_eventDownloadTerminated, 5000); // wait up to 5 seconds
  510. if (nAwaker == WAIT_TIMEOUT)
  511. {
  512. // Ideally, the thread would have it's memory instead of using the InternetSession's memory.
  513. // Then we wouldn't need to Terminate it like this.
  514. TerminateThread(m_threadDownload, 0);
  515. }
  516. CloseHandle(m_threadDownload);
  517. m_threadDownload = NULL;
  518. }
  519. }
  520. protected:
  521. char * * m_pszFileList;
  522. char * * m_pszFileListData;
  523. char m_szDestFolder[MAX_PATH];
  524. volatile HINTERNET m_hInternetSession;
  525. volatile HINTERNET m_hFTPSession;
  526. volatile HINTERNET m_hFileConnection;
  527. volatile HANDLE m_hFile;
  528. // events for controlling download thread
  529. HANDLE m_eventResumeDownload; // ready to resume downloading because either we are starting a new file or a progress sink has finished being fired
  530. HANDLE m_eventKillDownload; // download was killed by either error or abort
  531. HANDLE m_eventDownloadTerminated; // download thread was terminated
  532. HANDLE m_eventProgress; // download progress was made, so time to fire an update event to the sink
  533. HANDLE m_eventFileCompleted; // file was completed
  534. HANDLE m_threadDownload;
  535. volatile char * m_buffer;
  536. volatile unsigned m_cBytesRead; // bytes read into m_buffer for current file
  537. volatile unsigned m_nBufferSize; // alloc-ed size of m_buffer for current file
  538. char m_szLastError[1024];
  539. char m_szErrorInThread[MAX_PATH+100];
  540. int m_nLastErrorCode;
  541. int m_nLastErrorCodeInThread;
  542. bool m_bAutoDisconnect; // if true, auto disconnects after D/L
  543. bool m_bAbortDownload; // if true, this class quits downloading ASAP
  544. volatile unsigned long m_cTotalBytesRead; // bytes read for all files
  545. volatile unsigned long m_cCurrentFileBytesRead; // bytes read for current file
  546. IInternetSessionSink * m_pUpdateSink;
  547. };
  548. class CFTPSessionImpl:
  549. public CInternetSessionImpl<IFTPSession>
  550. {
  551. public:
  552. CFTPSessionImpl(IFTPSessionUpdateSink * pSink) :
  553. CInternetSessionImpl<IFTPSession>(pSink)
  554. {
  555. }
  556. virtual ~CFTPSessionImpl()
  557. {
  558. }
  559. virtual bool ConnectToSite(const char * szFTPSite, const char * szDirectory, const char * szUsername, const char * szPassword)
  560. {
  561. m_szLastError[0] = '\0';
  562. m_hInternetSession = ::InternetOpen(
  563. "Microsoft Internet Explorer", // agent
  564. INTERNET_OPEN_TYPE_PROXY, // access
  565. "ftp-gw", // proxy server
  566. NULL, // defaults
  567. 0); // synchronous
  568. //
  569. // Connect to remote FTP server.
  570. //
  571. m_hFTPSession = ::InternetConnect(
  572. m_hInternetSession, // Handle from a previous
  573. // call to InternetOpen.
  574. szFTPSite, // Server we want to connect to
  575. INTERNET_INVALID_PORT_NUMBER, // Use appropriate port
  576. szUsername, // Username, can be NULL
  577. szPassword, // Password, can be NULL
  578. INTERNET_SERVICE_FTP, // Flag to use FTP services
  579. 0, // Flags (see SDK docs)
  580. (DWORD) this); // Context for this connection
  581. if(m_hFTPSession== NULL)
  582. {
  583. DoError("Failed to log onto FTP site (%s) : ", szFTPSite);
  584. return false;
  585. }
  586. if (!FtpSetCurrentDirectory(m_hFTPSession, szDirectory))
  587. {
  588. DoError("Failed to enter the proper FTP directory (%s) : ", szDirectory);
  589. return false;
  590. }
  591. return true;
  592. }
  593. virtual int GetFileListIncrement()
  594. {
  595. return 1;
  596. }
  597. virtual bool InitiateDownload(const char * const * pszFileList,
  598. const char * szDestFolder,
  599. bool bDisconnectWhenDone = true,
  600. int nMaxBufferSize = 1024*1024)
  601. {
  602. bool bRet = CInternetSessionImpl<IFTPSession>::InitiateDownload(pszFileList, szDestFolder, false, nMaxBufferSize);
  603. // If progress updates are wanted
  604. if(m_pUpdateSink && *pszFileList != NULL)
  605. // Fire starting point
  606. m_pUpdateSink->OnProgress(0, *(m_pszFileList+1), 0);
  607. SetEvent(m_eventResumeDownload); // startup the download thread
  608. return bRet;
  609. }
  610. };
  611. class CHTTPSessionImpl :
  612. public CInternetSessionImpl<IHTTPSession>
  613. {
  614. public:
  615. CHTTPSessionImpl(IHTTPSessionSink * pSink) :
  616. CInternetSessionImpl<IHTTPSession>(pSink)
  617. {
  618. m_hInternetSession = ::InternetOpen(
  619. "Microsoft Internet Explorer", // agent
  620. INTERNET_OPEN_TYPE_PRECONFIG, //INTERNET_OPEN_TYPE_PROXY, // access
  621. NULL,//"ftp-gw", // proxy server
  622. NULL, // defaults
  623. 0); // synchronous
  624. if (m_hInternetSession == NULL)
  625. DoError("Failed to initialize HTTP stuff.");
  626. }
  627. virtual ~CHTTPSessionImpl()
  628. {
  629. }
  630. bool ConstructionSuccess()
  631. {
  632. return m_hInternetSession != NULL;
  633. }
  634. virtual int GetFileListIncrement()
  635. {
  636. return 2;
  637. }
  638. virtual bool StartNextFile()
  639. {
  640. m_cBytesRead = 0;
  641. m_cCurrentFileBytesRead = 0;
  642. ++m_pszFileList;
  643. unsigned cTries = 0;
  644. if (*m_pszFileList)
  645. {
  646. //
  647. // Open file for download
  648. //
  649. while (!(m_hFileConnection = InternetOpenUrl(m_hInternetSession, *m_pszFileList, NULL, 0, INTERNET_FLAG_RELOAD | INTERNET_FLAG_NO_CACHE_WRITE, 0)))
  650. {
  651. cTries++;
  652. debugf("Failed to open URL(%s) for download, try #%d\n", *m_pszFileList, cTries);
  653. Sleep(500);
  654. if(cTries >= 5)
  655. {
  656. DoErrorInThread("Failed to open file for download.");
  657. return false;
  658. }
  659. }
  660. ++m_pszFileList;
  661. if (*m_pszFileList == NULL) // todo, verify the memory is ours
  662. {
  663. DoErrorInThread("FileList has bad format");
  664. return false;
  665. }
  666. return OpenDownloadFile();
  667. }
  668. return true;
  669. }
  670. virtual bool InitiateDownload(const char * const * pszFileList,
  671. const char * szDestFolder,
  672. int nMaxBufferSize = 1024*1024)
  673. {
  674. bool bRet = CInternetSessionImpl<IHTTPSession>::InitiateDownload(pszFileList, szDestFolder, false, nMaxBufferSize);
  675. if(m_pUpdateSink && *(m_pszFileList+2) != NULL)
  676. // Fire starting point
  677. m_pUpdateSink->OnProgress(0, *(m_pszFileList+2), 0);
  678. SetEvent(m_eventResumeDownload); // startup the download thread
  679. return bRet;
  680. }
  681. };
  682. IHTTPSession * CreateHTTPSession(IHTTPSessionSink * pUpdateSink /*= NULL*/)
  683. {
  684. CHTTPSessionImpl * pNew = new CHTTPSessionImpl(pUpdateSink);
  685. if (pNew && pNew->ConstructionSuccess())
  686. return pNew;
  687. else
  688. return NULL;
  689. }
  690. IFTPSession * CreateFTPSession(IFTPSessionUpdateSink * pUpdateSink /*= NULL*/)
  691. {
  692. CFTPSessionImpl * pNew = new CFTPSessionImpl(pUpdateSink);
  693. return pNew;
  694. }