wac_downloadManager.cpp 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872
  1. #include <string.h>
  2. #include <strsafe.h>
  3. #include <iostream>
  4. #include <cstdio>
  5. #include <QtCore/qglobal.h>
  6. #include <QWebEngineProfile>
  7. #include <QtWebEngineWidgets/QtWebEngineWidgets>
  8. #include <QWebEnginePage>
  9. #include <QWebEngineSettings>
  10. #include "api__wac_downloadManager.h"
  11. #include "wac_downloadManager.h"
  12. #include "wac_download_http_receiver_api.h"
  13. #include "..\wac_network\wac_network_http_receiver_api.h"
  14. #include "api/service/waservicefactory.h"
  15. #include "../nu/threadname.h"
  16. #include "../nu/AutoChar.h"
  17. #include "../nu/threadpool/timerhandle.hpp"
  18. #include "..\WAT\WAT.h"
  19. #include "..\Winamp\buildType.h"
  20. static const GUID internetConfigGroupGUID =
  21. {
  22. 0xc0a565dc, 0xcfe, 0x405a, { 0xa2, 0x7c, 0x46, 0x8b, 0xc, 0x8a, 0x3a, 0x5c }
  23. };
  24. #define DOWNLOAD_TIMEOUT_MS 60000 // 60 second timeout
  25. #define DOWNLOAD_SLEEP_MS 50
  26. #define DOWNLOAD_BUFFER_SIZE 1310720 // gives a maximum download rate of 25 mb/sec per file
  27. /**********************************************************************************
  28. **********************************************************************************/
  29. /**********************************************************************************
  30. * PUBLIC *
  31. **********************************************************************************/
  32. wa::Components::WAC_DownloadData::WAC_DownloadData( api_wac_download_manager_http_receiver *p_http, const char *p_url, int p_flags, ifc_downloadManagerCallback *p_callback )
  33. {
  34. _http = p_http;
  35. strcpy_s( this->_url, 1024, p_url );
  36. _flags = p_flags;
  37. _callback = p_callback;
  38. if ( _callback )
  39. _callback->AddRef();
  40. _hFile = INVALID_HANDLE_VALUE;
  41. _filepath[ 0 ] = 0;
  42. _fileext = 0;
  43. int download_method = ( api_downloadManager::DOWNLOADEX_MASK_DOWNLOADMETHOD & _flags );
  44. switch ( download_method )
  45. {
  46. case api_downloadManager::DOWNLOADEX_TEMPFILE:
  47. {
  48. wchar_t temppath[ MAX_PATH - 14 ] = { 0 }; // MAX_PATH-14 'cause MSDN said so
  49. GetTempPathW( MAX_PATH - 14, temppath );
  50. GetTempFileNameW( temppath, L"wdl", 0, _filepath );
  51. _hFile = CreateFileW( _filepath, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, 0, CREATE_ALWAYS, 0, 0 );
  52. }
  53. break;
  54. case api_downloadManager::DOWNLOADEX_CALLBACK:
  55. if ( _callback )
  56. _callback->GetLocation( _filepath, MAX_PATH );
  57. break;
  58. }
  59. _source[ 0 ] = 0;
  60. _title[ 0 ] = 0;
  61. if ( _flags & api_downloadManager::DOWNLOADEX_CALLBACK )
  62. {
  63. if ( _callback )
  64. {
  65. _callback->GetSource( _source, 1024 );
  66. _callback->GetTitle( _title, 1024 );
  67. }
  68. }
  69. _connectionStart = _lastDownloadTick = GetTickCount();
  70. _last_status = HTTP_RECEIVER_STATUS_ERROR;
  71. _pending = ( _flags & api_downloadManager::DOWNLOADEX_PENDING ) > 0;
  72. }
  73. wa::Components::WAC_DownloadData::~WAC_DownloadData()
  74. {
  75. ServiceRelease( _http, httpreceiverGUID2 );
  76. _http = NULL;
  77. if ( _fileext )
  78. delete _fileext;
  79. int download_method = ( api_downloadManager::DOWNLOADEX_MASK_DOWNLOADMETHOD & _flags );
  80. if ( download_method == api_downloadManager::DOWNLOADEX_TEMPFILE && _filepath[ 0 ] )
  81. DeleteFileW( _filepath );
  82. if ( _callback )
  83. _callback->Release();
  84. _callback = NULL;
  85. }
  86. void wa::Components::WAC_DownloadData::Retain()
  87. {
  88. this->_refCount.fetch_add( 1 );
  89. }
  90. void wa::Components::WAC_DownloadData::Release()
  91. {
  92. if ( this->_refCount.fetch_sub( 1 ) == 0 )
  93. delete this;
  94. }
  95. void wa::Components::WAC_DownloadData::Close( ifc_downloadManagerCallback **callbackCopy )
  96. {
  97. if ( _hFile != INVALID_HANDLE_VALUE )
  98. CloseHandle( _hFile );
  99. _hFile = INVALID_HANDLE_VALUE;
  100. if ( callbackCopy != NULL )
  101. {
  102. *callbackCopy = _callback;
  103. if ( _callback != NULL )
  104. _callback->AddRef();
  105. }
  106. else if ( _callback != NULL )
  107. {
  108. _callback->Release();
  109. _callback = NULL;
  110. }
  111. // don't want to close http here, because someone might still want to get the headers out of it
  112. }
  113. bool wa::Components::WAC_DownloadData::getExtention()
  114. {
  115. if ( _fileext && *_fileext )
  116. return _fileext;
  117. char l_header_name_content_type[] = "Content-Type";
  118. char *l_content_type = _http->getheader( l_header_name_content_type );
  119. if ( l_content_type && *l_content_type )
  120. {
  121. if ( _CONTENT_TYPES_EXTENSIONS.count( l_content_type ) == 1 )
  122. _fileext = _strdup( _CONTENT_TYPES_EXTENSIONS.find( l_content_type )->second.c_str() );
  123. }
  124. return _fileext;
  125. }
  126. /**********************************************************************************
  127. * PRIVATE *
  128. **********************************************************************************/
  129. /**********************************************************************************
  130. **********************************************************************************/
  131. /**********************************************************************************
  132. * PUBLIC *
  133. **********************************************************************************/
  134. wa::Components::WAC_DownloadManager::WAC_DownloadManager( QObject *parent ) : QNetworkAccessManager( parent )
  135. {
  136. this->setObjectName( "DownloadManagerService" );
  137. this->init();
  138. }
  139. wa::Components::WAC_DownloadManager::~WAC_DownloadManager()
  140. {
  141. disconnect( _connection_authentication_required );
  142. }
  143. DownloadToken wa::Components::WAC_DownloadManager::Download( const char *p_url, ifc_downloadManagerCallback *p_callback )
  144. {
  145. return DownloadEx( p_url, p_callback, api_downloadManager::DOWNLOADEX_TEMPFILE );
  146. }
  147. DownloadToken wa::Components::WAC_DownloadManager::DownloadEx( const char *p_url, ifc_downloadManagerCallback *p_callback, int p_flags )
  148. {
  149. return DownloadToken();
  150. }
  151. /**********************************************************************************
  152. * PRIVATE *
  153. **********************************************************************************/
  154. void wa::Components::WAC_DownloadManager::init()
  155. {
  156. QString l_winamp_user_agent = QString( "%1 Winamp/%2" ).arg( QWebEngineProfile::defaultProfile()->httpUserAgent(), STR_WINAMP_PRODUCTVER ).replace( ",", "." );
  157. QWebEngineProfile::defaultProfile()->setHttpUserAgent( l_winamp_user_agent );
  158. _connection_authentication_required = connect( this, &QNetworkAccessManager::authenticationRequired, this, &wa::Components::WAC_DownloadManager::on_s_authentication_required );
  159. }
  160. QNetworkReply *wa::Components::WAC_DownloadManager::createRequest( Operation p_operation, const QNetworkRequest &p_request, QIODevice *p_outgoing_data )
  161. {
  162. return QNetworkAccessManager::createRequest( p_operation, p_request, p_outgoing_data );
  163. }
  164. /**********************************************************************************
  165. * PRIVATE SLOTS *
  166. **********************************************************************************/
  167. void wa::Components::WAC_DownloadManager::on_s_authentication_required( QNetworkReply *p_reply, QAuthenticator *p_authenticator )
  168. {
  169. Q_UNUSED( p_reply );
  170. Q_UNUSED( p_authenticator );
  171. }
  172. /**********************************************************************************
  173. **********************************************************************************/
  174. DownloadData::DownloadData( api_httpreceiver *p_http, const char *p_url, int p_flags, ifc_downloadManagerCallback *p_callback )
  175. {
  176. flags = p_flags;
  177. http = p_http;
  178. callback = p_callback;
  179. if ( callback )
  180. callback->AddRef();
  181. hFile = INVALID_HANDLE_VALUE;
  182. filepath[ 0 ] = 0;
  183. fileext = 0;
  184. int download_method = ( api_downloadManager::DOWNLOADEX_MASK_DOWNLOADMETHOD & flags );
  185. switch ( download_method )
  186. {
  187. case api_downloadManager::DOWNLOADEX_TEMPFILE:
  188. {
  189. wchar_t temppath[ MAX_PATH - 14 ] = { 0 }; // MAX_PATH-14 'cause MSDN said so
  190. GetTempPathW( MAX_PATH - 14, temppath );
  191. GetTempFileNameW( temppath, L"wdl", 0, filepath );
  192. hFile = CreateFileW( filepath, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, 0, CREATE_ALWAYS, 0, 0 );
  193. }
  194. break;
  195. case api_downloadManager::DOWNLOADEX_CALLBACK:
  196. if ( callback )
  197. callback->GetLocation( filepath, MAX_PATH );
  198. break;
  199. }
  200. strcpy_s( this->url, 1024, p_url );
  201. source[ 0 ] = 0;
  202. title[ 0 ] = 0;
  203. if ( flags & api_downloadManager::DOWNLOADEX_CALLBACK )
  204. {
  205. if ( callback )
  206. {
  207. callback->GetSource( source, 1024 );
  208. callback->GetTitle( title, 1024 );
  209. }
  210. }
  211. connectionStart = lastDownloadTick = GetTickCount();
  212. last_status = HTTPRECEIVER_STATUS_ERROR;
  213. pending = ( flags & api_downloadManager::DOWNLOADEX_PENDING ) > 0;
  214. }
  215. DownloadData::~DownloadData()
  216. {
  217. ServiceRelease( http, httpreceiverGUID );
  218. http = NULL;
  219. if ( fileext )
  220. delete fileext;
  221. int download_method = ( api_downloadManager::DOWNLOADEX_MASK_DOWNLOADMETHOD & flags );
  222. if ( download_method == api_downloadManager::DOWNLOADEX_TEMPFILE && filepath[ 0 ] )
  223. DeleteFileW( filepath );
  224. if ( callback )
  225. callback->Release();
  226. callback = NULL;
  227. }
  228. void DownloadData::Retain()
  229. {
  230. this->_refCount.fetch_add( 1 );
  231. }
  232. void DownloadData::Release()
  233. {
  234. if ( this->_refCount.fetch_sub( 1 ) == 0 )
  235. delete this;
  236. }
  237. void DownloadData::Close( ifc_downloadManagerCallback **callbackCopy )
  238. {
  239. if ( hFile != INVALID_HANDLE_VALUE )
  240. CloseHandle( hFile );
  241. hFile = INVALID_HANDLE_VALUE;
  242. if ( callbackCopy != NULL )
  243. {
  244. *callbackCopy = callback;
  245. if ( callback != NULL )
  246. callback->AddRef();
  247. }
  248. else if ( callback != NULL )
  249. {
  250. callback->Release();
  251. callback = NULL;
  252. }
  253. // don't want to close http here, because someone might still want to get the headers out of it
  254. }
  255. bool DownloadData::getExtention()
  256. {
  257. if ( fileext && *fileext )
  258. return fileext;
  259. char l_header_name_content_type[] = "Content-Type";
  260. char *l_content_type = http->getheader( l_header_name_content_type );
  261. if ( l_content_type && *l_content_type )
  262. {
  263. if ( _CONTENT_TYPES_EXTENSIONS.count( l_content_type ) == 1 )
  264. fileext = _strdup( _CONTENT_TYPES_EXTENSIONS.find( l_content_type )->second.c_str() );
  265. }
  266. return fileext;
  267. }
  268. /**********************************************************************************
  269. **********************************************************************************/
  270. /**********************************************************************************
  271. * PUBLIC *
  272. **********************************************************************************/
  273. DownloadManager::DownloadManager()
  274. {
  275. download_thread = NULL;
  276. killswitch = CreateEvent( NULL, TRUE, FALSE, NULL );
  277. InitializeCriticalSection( &downloadsCS );
  278. }
  279. void DownloadManager::Kill()
  280. {
  281. SetEvent( killswitch );
  282. if ( download_thread )
  283. {
  284. WaitForSingleObject( download_thread, 3000 );
  285. CloseHandle( download_thread );
  286. }
  287. DeleteCriticalSection( &downloadsCS );
  288. CloseHandle( killswitch );
  289. }
  290. static void SetUserAgent( api_httpreceiver *p_http )
  291. {
  292. char agent[ 256 ] = { 0 };
  293. StringCchPrintfA( agent, 256, "User-Agent: %S/%S", WASABI_API_APP->main_getAppName(), WASABI_API_APP->main_getVersionNumString() );
  294. p_http->addheader( agent );
  295. //QString l_winamp_user_agent = QString( "User-Agent: %1 Winamp/%2" ).arg( QWebEngineProfile::defaultProfile()->httpUserAgent(), STR_WINAMP_PRODUCTVER ).replace( ",", "." );
  296. //http->addheader( l_winamp_user_agent.toStdString().c_str() );
  297. }
  298. DownloadToken DownloadManager::Download( const char *url, ifc_downloadManagerCallback *callback )
  299. {
  300. return DownloadEx( url, callback, api_downloadManager::DOWNLOADEX_TEMPFILE );
  301. }
  302. DownloadToken DownloadManager::DownloadEx( const char *url, ifc_downloadManagerCallback *callback, int flags )
  303. {
  304. if ( InitDownloadThread() )
  305. {
  306. api_httpreceiver *http = NULL;
  307. ServiceBuild( http, httpreceiverGUID );
  308. if ( http )
  309. {
  310. DownloadData *downloadData = new DownloadData( http, url, flags, callback );
  311. int use_proxy = 1;
  312. bool proxy80 = AGAVE_API_CONFIG->GetBool( internetConfigGroupGUID, L"proxy80", false );
  313. if ( proxy80 && strstr( url, ":" ) && ( !strstr( url, ":80/" ) && strstr( url, ":80" ) != ( url + strlen( url ) - 3 ) ) )
  314. use_proxy = 0;
  315. const wchar_t *proxy = use_proxy ? AGAVE_API_CONFIG->GetString( internetConfigGroupGUID, L"proxy", 0 ) : 0;
  316. http->open( API_DNS_AUTODNS, DOWNLOAD_BUFFER_SIZE, ( proxy && proxy[ 0 ] ) ? (const char *)AutoChar( proxy ) : NULL );
  317. SetUserAgent( http );
  318. if ( callback )
  319. callback->OnInit( downloadData );
  320. if ( downloadData->flags & api_downloadManager::DOWNLOADEX_UI )
  321. {
  322. for ( ifc_downloadManagerCallback *l_status : status_callbacks )
  323. l_status->OnInit( downloadData );
  324. }
  325. //only call http->connect when it is not pending download request
  326. if ( !( flags & DOWNLOADEX_PENDING ) )
  327. http->connect( url, 1 );
  328. //http->run(); // let's get this party started
  329. EnterCriticalSection( &downloadsCS );
  330. downloads.push_back( downloadData );
  331. LeaveCriticalSection( &downloadsCS );
  332. return downloadData;
  333. }
  334. }
  335. return 0;
  336. }
  337. void DownloadManager::ResumePendingDownload( DownloadToken p_token )
  338. {
  339. if ( !p_token )
  340. return;
  341. DownloadData *data = (DownloadData *)p_token;
  342. if ( data->pending )
  343. {
  344. data->pending = false;
  345. data->connectionStart = data->lastDownloadTick = GetTickCount();
  346. data->http->connect( data->url );
  347. }
  348. }
  349. void DownloadManager::CancelDownload( DownloadToken p_token )
  350. {
  351. if ( !p_token )
  352. return;
  353. DownloadData *data = (DownloadData *)p_token;
  354. EnterCriticalSection( &downloadsCS );
  355. if ( downloads.end() != std::find( downloads.begin(), downloads.end(), data ) )
  356. {
  357. ifc_downloadManagerCallback *callback;
  358. data->Close( &callback );
  359. //downloads.eraseObject(p_data);
  360. auto it = std::find( downloads.begin(), downloads.end(), data );
  361. if ( it != downloads.end() )
  362. {
  363. downloads.erase( it );
  364. }
  365. LeaveCriticalSection( &downloadsCS );
  366. if ( callback )
  367. {
  368. callback->OnCancel( p_token );
  369. if ( data->flags & api_downloadManager::DOWNLOADEX_UI )
  370. {
  371. for ( ifc_downloadManagerCallback *l_status : status_callbacks )
  372. l_status->OnCancel( p_token );
  373. }
  374. callback->Release();
  375. }
  376. data->Release();
  377. }
  378. else
  379. LeaveCriticalSection( &downloadsCS );
  380. }
  381. void DownloadManager::RetainDownload( DownloadToken p_token )
  382. {
  383. if ( !p_token )
  384. return;
  385. DownloadData *data = (DownloadData *)p_token;
  386. if ( data )
  387. data->Retain();
  388. }
  389. void DownloadManager::ReleaseDownload( DownloadToken p_token )
  390. {
  391. if ( !p_token )
  392. return;
  393. DownloadData *data = (DownloadData *)p_token;
  394. if ( data )
  395. data->Release();
  396. }
  397. void DownloadManager::RegisterStatusCallback( ifc_downloadManagerCallback *callback )
  398. {
  399. EnterCriticalSection( &downloadsCS );
  400. status_callbacks.push_back( callback );
  401. LeaveCriticalSection( &downloadsCS );
  402. }
  403. void DownloadManager::UnregisterStatusCallback( ifc_downloadManagerCallback *callback )
  404. {
  405. EnterCriticalSection( &downloadsCS );
  406. auto it = std::find( status_callbacks.begin(), status_callbacks.end(), callback );
  407. if ( it != status_callbacks.end() )
  408. status_callbacks.erase( it );
  409. LeaveCriticalSection( &downloadsCS );
  410. }
  411. bool DownloadManager::IsPending( DownloadToken token )
  412. {
  413. DownloadData *data = (DownloadData *)token;
  414. if ( data )
  415. return data->pending;
  416. else
  417. return FALSE;
  418. }
  419. /**********************************************************************************
  420. * PRIVATE *
  421. **********************************************************************************/
  422. bool DownloadManager::DownloadThreadTick()
  423. {
  424. unsigned int i = 0;
  425. char *downloadBuffer = (char *)malloc( DOWNLOAD_BUFFER_SIZE );
  426. while ( WaitForSingleObject( killswitch, 0 ) != WAIT_OBJECT_0 )
  427. {
  428. EnterCriticalSection( &downloadsCS );
  429. if ( downloads.empty() )
  430. {
  431. // TODO: might be nice to dynamically increase the sleep time if this happens
  432. // (maybe to INFINITE and have Download() wake us?)
  433. LeaveCriticalSection( &downloadsCS );
  434. free( downloadBuffer );
  435. return true;
  436. }
  437. if ( i >= downloads.size() )
  438. {
  439. LeaveCriticalSection( &downloadsCS );
  440. free( downloadBuffer );
  441. return true;
  442. }
  443. DownloadData *thisDownload = downloads[ i ];
  444. if ( thisDownload->pending )
  445. {
  446. LeaveCriticalSection( &downloadsCS );
  447. }
  448. else
  449. {
  450. thisDownload->Retain();
  451. LeaveCriticalSection( &downloadsCS );
  452. INT tick = Tick( thisDownload, downloadBuffer, DOWNLOAD_BUFFER_SIZE );
  453. switch ( tick )
  454. {
  455. case TICK_NODATA:
  456. // do nothing
  457. break;
  458. case TICK_CONNECTING:
  459. break;
  460. case TICK_CONNECTED:
  461. if ( thisDownload->callback )
  462. thisDownload->callback->OnConnect( thisDownload );
  463. if ( thisDownload->flags & api_downloadManager::DOWNLOADEX_UI )
  464. {
  465. for ( ifc_downloadManagerCallback *l_status : status_callbacks )
  466. l_status->OnConnect( thisDownload );
  467. }
  468. break;
  469. case TICK_SUCCESS:
  470. if ( thisDownload->callback )
  471. thisDownload->callback->OnTick( thisDownload );
  472. if ( thisDownload->flags & api_downloadManager::DOWNLOADEX_UI )
  473. {
  474. for ( ifc_downloadManagerCallback *l_status : status_callbacks )
  475. l_status->OnTick( thisDownload );
  476. }
  477. // TODO: send out update l_callback
  478. break;
  479. case TICK_FINISHED:
  480. case TICK_FAILURE:
  481. case TICK_TIMEOUT:
  482. case TICK_CANT_CONNECT:
  483. case TICK_WRITE_ERROR:
  484. FinishDownload( thisDownload, tick );
  485. break;
  486. }
  487. thisDownload->Release();
  488. }
  489. i++;
  490. }
  491. free( downloadBuffer );
  492. return false; // we only get here when killswitch is set
  493. }
  494. int DownloadManager::DownloadTickThreadPoolFunc( HANDLE handle, void *user_data, intptr_t id )
  495. {
  496. DownloadManager *dlmgr = (DownloadManager *)user_data;
  497. if ( dlmgr->DownloadThreadTick() )
  498. {
  499. TimerHandle t( handle );
  500. t.Wait( DOWNLOAD_SLEEP_MS );
  501. }
  502. else
  503. {
  504. WASABI_API_THREADPOOL->RemoveHandle( 0, handle );
  505. SetEvent( dlmgr->download_thread );
  506. }
  507. return 0;
  508. }
  509. bool DownloadManager::InitDownloadThread()
  510. {
  511. if ( download_thread == NULL )
  512. {
  513. download_thread = CreateEvent( 0, FALSE, FALSE, 0 );
  514. TimerHandle t;
  515. WASABI_API_THREADPOOL->AddHandle( 0, t, DownloadTickThreadPoolFunc, this, 0, api_threadpool::FLAG_LONG_EXECUTION );
  516. t.Wait( DOWNLOAD_SLEEP_MS );
  517. }
  518. return ( download_thread != NULL );
  519. }
  520. void DownloadManager::FinishDownload( DownloadData *p_data, int code )
  521. {
  522. if ( p_data == NULL )
  523. return;
  524. ifc_downloadManagerCallback *l_callback = NULL;
  525. EnterCriticalSection( &downloadsCS );
  526. p_data->Close( &l_callback );
  527. LeaveCriticalSection( &downloadsCS );
  528. if ( l_callback != NULL )
  529. {
  530. if ( code == TICK_FINISHED )
  531. {
  532. l_callback->OnFinish( p_data );
  533. if ( p_data->flags & api_downloadManager::DOWNLOADEX_UI )
  534. {
  535. for ( ifc_downloadManagerCallback *l_data : status_callbacks )
  536. l_data->OnFinish( p_data );
  537. }
  538. }
  539. else
  540. {
  541. l_callback->OnError( p_data, code );
  542. if ( p_data->flags & api_downloadManager::DOWNLOADEX_UI )
  543. {
  544. for ( ifc_downloadManagerCallback *l_data : status_callbacks )
  545. l_data->OnError( p_data, code );
  546. }
  547. }
  548. l_callback->Release();
  549. }
  550. EnterCriticalSection( &downloadsCS );
  551. auto it = std::find( downloads.begin(), downloads.end(), p_data );
  552. if ( it != downloads.end() )
  553. downloads.erase( it );
  554. LeaveCriticalSection( &downloadsCS );
  555. p_data->Release();
  556. }
  557. int DownloadManager::Tick( DownloadData *thisDownload, void *buffer, int bufferSize )
  558. {
  559. if ( !thisDownload )
  560. return api_downloadManager::TICK_FAILURE;
  561. int state = thisDownload->http->run();
  562. if ( state == HTTPRECEIVER_RUN_ERROR || thisDownload == NULL )
  563. return api_downloadManager::TICK_FAILURE;
  564. if ( !thisDownload->fileext )
  565. thisDownload->getExtention();
  566. int downloaded = thisDownload->http->get_bytes( buffer, bufferSize );
  567. if ( downloaded )
  568. {
  569. switch ( thisDownload->flags & DOWNLOADEX_MASK_DOWNLOADMETHOD )
  570. {
  571. case api_downloadManager::DOWNLOADEX_BUFFER:
  572. {
  573. thisDownload->buffer.reserve( thisDownload->http->content_length() );
  574. thisDownload->buffer.add( buffer, downloaded );
  575. }
  576. break;
  577. case api_downloadManager::DOWNLOADEX_TEMPFILE:
  578. {
  579. DWORD written = 0;
  580. WriteFile( thisDownload->hFile, buffer, downloaded, &written, NULL );
  581. if ( written != downloaded )
  582. return api_downloadManager::TICK_WRITE_ERROR;
  583. }
  584. break;
  585. case api_downloadManager::DOWNLOADEX_CALLBACK:
  586. {
  587. if ( thisDownload->flags & api_downloadManager::DOWNLOADEX_UI )
  588. {
  589. for ( ifc_downloadManagerCallback *l_status : status_callbacks )
  590. l_status->OnData( thisDownload, buffer, downloaded );
  591. }
  592. if ( thisDownload->callback )
  593. thisDownload->callback->OnData( thisDownload, buffer, downloaded );
  594. }
  595. }
  596. thisDownload->lastDownloadTick = GetTickCount();
  597. thisDownload->bytesDownloaded += downloaded;
  598. return api_downloadManager::TICK_SUCCESS;
  599. }
  600. else // nothing in the buffer
  601. {
  602. if ( state == HTTPRECEIVER_RUN_CONNECTION_CLOSED ) // see if the connection is closed
  603. {
  604. return api_downloadManager::TICK_FINISHED; // yay we're done
  605. }
  606. if ( GetTickCount() - thisDownload->lastDownloadTick > DOWNLOAD_TIMEOUT_MS ) // check for timeout
  607. return api_downloadManager::TICK_TIMEOUT;
  608. switch ( thisDownload->http->get_status() )
  609. {
  610. case HTTPRECEIVER_STATUS_CONNECTING:
  611. if ( thisDownload->last_status != HTTPRECEIVER_STATUS_CONNECTING )
  612. {
  613. thisDownload->last_status = HTTPRECEIVER_STATUS_CONNECTING;
  614. return api_downloadManager::TICK_CONNECTING;
  615. }
  616. else
  617. {
  618. return api_downloadManager::TICK_NODATA;
  619. }
  620. case HTTPRECEIVER_STATUS_READING_HEADERS:
  621. if ( thisDownload->last_status != HTTPRECEIVER_STATUS_READING_HEADERS )
  622. {
  623. thisDownload->last_status = HTTPRECEIVER_STATUS_READING_HEADERS;
  624. return api_downloadManager::TICK_CONNECTED;
  625. }
  626. else
  627. {
  628. return api_downloadManager::TICK_NODATA;
  629. }
  630. }
  631. if ( !thisDownload->replyCode )
  632. thisDownload->replyCode = thisDownload->http->getreplycode();
  633. switch ( thisDownload->replyCode )
  634. {
  635. case 0:
  636. case 100:
  637. case 200:
  638. case 201:
  639. case 202:
  640. case 203:
  641. case 204:
  642. case 205:
  643. case 206:
  644. return api_downloadManager::TICK_NODATA;
  645. default:
  646. return api_downloadManager::TICK_CANT_CONNECT;
  647. }
  648. }
  649. }
  650. #define CBCLASS DownloadManager
  651. START_DISPATCH;
  652. CB( API_DOWNLOADMANAGER_DOWNLOAD, Download )
  653. CB( API_DOWNLOADMANAGER_DOWNLOADEX, DownloadEx )
  654. CB( API_DOWNLOADMANAGER_GETRECEIVER, GetReceiver )
  655. CB( API_DOWNLOADMANAGER_GETLOCATION, GetLocation )
  656. VCB( API_DOWNLOADMANAGER_SETLOCATION, SetLocation )
  657. CB( API_DOWNLOADMANAGER_GETEXTENTION, GetExtention )
  658. CB( API_DOWNLOADMANAGER_GETURL, GetUrl )
  659. CB( API_DOWNLOADMANAGER_GETBYTESDOWNLOADED, GetBytesDownloaded );
  660. CB( API_DOWNLOADMANAGER_GETBUFFER, GetBuffer );
  661. VCB( API_DOWNLOADMANAGER_RESUMEPENDINGDOWNLOAD, ResumePendingDownload );
  662. VCB( API_DOWNLOADMANAGER_CANCELDOWNLOAD, CancelDownload );
  663. VCB( API_DOWNLOADMANAGER_RETAINDOWNLOAD, RetainDownload );
  664. VCB( API_DOWNLOADMANAGER_RELEASEDOWNLOAD, ReleaseDownload );
  665. VCB( API_DOWNLOADMANAGER_REGISTERSTATUSCALLBACK, RegisterStatusCallback );
  666. VCB( API_DOWNLOADMANAGER_UNREGISTERSTATUSCALLBACK, UnregisterStatusCallback );
  667. CB( API_DOWNLOADMANAGER_GETSOURCE, GetSource );
  668. CB( API_DOWNLOADMANAGER_GETTITLE, GetTitle );
  669. CB( API_DOWNLOADMANAGER_ISPENDING, IsPending );
  670. END_DISPATCH;
  671. #undef CBCLASS