aboutsummaryrefslogtreecommitdiff
path: root/Src/Components/wac_downloadManager/wac_downloadManager.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Src/Components/wac_downloadManager/wac_downloadManager.cpp')
-rw-r--r--Src/Components/wac_downloadManager/wac_downloadManager.cpp872
1 files changed, 872 insertions, 0 deletions
diff --git a/Src/Components/wac_downloadManager/wac_downloadManager.cpp b/Src/Components/wac_downloadManager/wac_downloadManager.cpp
new file mode 100644
index 00000000..aa3e7ae9
--- /dev/null
+++ b/Src/Components/wac_downloadManager/wac_downloadManager.cpp
@@ -0,0 +1,872 @@
+#include <string.h>
+#include <strsafe.h>
+
+#include <iostream>
+#include <cstdio>
+
+#include <QtCore/qglobal.h>
+
+#include <QWebEngineProfile>
+#include <QtWebEngineWidgets/QtWebEngineWidgets>
+#include <QWebEnginePage>
+#include <QWebEngineSettings>
+
+#include "api__wac_downloadManager.h"
+
+#include "wac_downloadManager.h"
+#include "wac_download_http_receiver_api.h"
+
+#include "..\wac_network\wac_network_http_receiver_api.h"
+
+#include "api/service/waservicefactory.h"
+
+#include "../nu/threadname.h"
+#include "../nu/AutoChar.h"
+#include "../nu/threadpool/timerhandle.hpp"
+
+#include "..\WAT\WAT.h"
+
+#include "..\Winamp\buildType.h"
+
+static const GUID internetConfigGroupGUID =
+{
+ 0xc0a565dc, 0xcfe, 0x405a, { 0xa2, 0x7c, 0x46, 0x8b, 0xc, 0x8a, 0x3a, 0x5c }
+};
+
+#define DOWNLOAD_TIMEOUT_MS 60000 // 60 second timeout
+#define DOWNLOAD_SLEEP_MS 50
+#define DOWNLOAD_BUFFER_SIZE 1310720 // gives a maximum download rate of 25 mb/sec per file
+
+
+/**********************************************************************************
+ **********************************************************************************/
+
+
+/**********************************************************************************
+ * PUBLIC *
+ **********************************************************************************/
+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 )
+{
+ _http = p_http;
+
+ strcpy_s( this->_url, 1024, p_url );
+
+ _flags = p_flags;
+ _callback = p_callback;
+
+ if ( _callback )
+ _callback->AddRef();
+
+ _hFile = INVALID_HANDLE_VALUE;
+ _filepath[ 0 ] = 0;
+ _fileext = 0;
+
+ int download_method = ( api_downloadManager::DOWNLOADEX_MASK_DOWNLOADMETHOD & _flags );
+ switch ( download_method )
+ {
+ case api_downloadManager::DOWNLOADEX_TEMPFILE:
+ {
+ wchar_t temppath[ MAX_PATH - 14 ] = { 0 }; // MAX_PATH-14 'cause MSDN said so
+ GetTempPathW( MAX_PATH - 14, temppath );
+ GetTempFileNameW( temppath, L"wdl", 0, _filepath );
+ _hFile = CreateFileW( _filepath, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, 0, CREATE_ALWAYS, 0, 0 );
+ }
+ break;
+ case api_downloadManager::DOWNLOADEX_CALLBACK:
+ if ( _callback )
+ _callback->GetLocation( _filepath, MAX_PATH );
+ break;
+ }
+
+
+
+ _source[ 0 ] = 0;
+ _title[ 0 ] = 0;
+
+ if ( _flags & api_downloadManager::DOWNLOADEX_CALLBACK )
+ {
+ if ( _callback )
+ {
+ _callback->GetSource( _source, 1024 );
+ _callback->GetTitle( _title, 1024 );
+ }
+ }
+
+ _connectionStart = _lastDownloadTick = GetTickCount();
+ _last_status = HTTP_RECEIVER_STATUS_ERROR;
+ _pending = ( _flags & api_downloadManager::DOWNLOADEX_PENDING ) > 0;
+}
+
+wa::Components::WAC_DownloadData::~WAC_DownloadData()
+{
+ ServiceRelease( _http, httpreceiverGUID2 );
+
+ _http = NULL;
+
+ if ( _fileext )
+ delete _fileext;
+
+ int download_method = ( api_downloadManager::DOWNLOADEX_MASK_DOWNLOADMETHOD & _flags );
+ if ( download_method == api_downloadManager::DOWNLOADEX_TEMPFILE && _filepath[ 0 ] )
+ DeleteFileW( _filepath );
+
+ if ( _callback )
+ _callback->Release();
+
+ _callback = NULL;
+}
+
+
+void wa::Components::WAC_DownloadData::Retain()
+{
+ this->_refCount.fetch_add( 1 );
+}
+
+void wa::Components::WAC_DownloadData::Release()
+{
+ if ( this->_refCount.fetch_sub( 1 ) == 0 )
+ delete this;
+}
+
+
+void wa::Components::WAC_DownloadData::Close( ifc_downloadManagerCallback **callbackCopy )
+{
+ if ( _hFile != INVALID_HANDLE_VALUE )
+ CloseHandle( _hFile );
+
+ _hFile = INVALID_HANDLE_VALUE;
+
+ if ( callbackCopy != NULL )
+ {
+ *callbackCopy = _callback;
+ if ( _callback != NULL )
+ _callback->AddRef();
+ }
+ else if ( _callback != NULL )
+ {
+ _callback->Release();
+ _callback = NULL;
+ }
+
+ // don't want to close http here, because someone might still want to get the headers out of it
+}
+
+bool wa::Components::WAC_DownloadData::getExtention()
+{
+ if ( _fileext && *_fileext )
+ return _fileext;
+
+ char l_header_name_content_type[] = "Content-Type";
+
+ char *l_content_type = _http->getheader( l_header_name_content_type );
+ if ( l_content_type && *l_content_type )
+ {
+ if ( _CONTENT_TYPES_EXTENSIONS.count( l_content_type ) == 1 )
+ _fileext = _strdup( _CONTENT_TYPES_EXTENSIONS.find( l_content_type )->second.c_str() );
+ }
+
+ return _fileext;
+}
+
+
+
+
+/**********************************************************************************
+ * PRIVATE *
+ **********************************************************************************/
+
+
+
+/**********************************************************************************
+ **********************************************************************************/
+
+/**********************************************************************************
+ * PUBLIC *
+ **********************************************************************************/
+wa::Components::WAC_DownloadManager::WAC_DownloadManager( QObject *parent ) : QNetworkAccessManager( parent )
+{
+ this->setObjectName( "DownloadManagerService" );
+
+ this->init();
+}
+
+wa::Components::WAC_DownloadManager::~WAC_DownloadManager()
+{
+ disconnect( _connection_authentication_required );
+}
+
+
+DownloadToken wa::Components::WAC_DownloadManager::Download( const char *p_url, ifc_downloadManagerCallback *p_callback )
+{
+ return DownloadEx( p_url, p_callback, api_downloadManager::DOWNLOADEX_TEMPFILE );
+}
+
+DownloadToken wa::Components::WAC_DownloadManager::DownloadEx( const char *p_url, ifc_downloadManagerCallback *p_callback, int p_flags )
+{
+
+
+
+ return DownloadToken();
+}
+
+
+
+
+
+/**********************************************************************************
+ * PRIVATE *
+ **********************************************************************************/
+void wa::Components::WAC_DownloadManager::init()
+{
+ QString l_winamp_user_agent = QString( "%1 Winamp/%2" ).arg( QWebEngineProfile::defaultProfile()->httpUserAgent(), STR_WINAMP_PRODUCTVER ).replace( ",", "." );
+
+ QWebEngineProfile::defaultProfile()->setHttpUserAgent( l_winamp_user_agent );
+
+
+ _connection_authentication_required = connect( this, &QNetworkAccessManager::authenticationRequired, this, &wa::Components::WAC_DownloadManager::on_s_authentication_required );
+
+
+}
+
+
+QNetworkReply *wa::Components::WAC_DownloadManager::createRequest( Operation p_operation, const QNetworkRequest &p_request, QIODevice *p_outgoing_data )
+{
+ return QNetworkAccessManager::createRequest( p_operation, p_request, p_outgoing_data );
+}
+
+
+
+/**********************************************************************************
+ * PRIVATE SLOTS *
+ **********************************************************************************/
+void wa::Components::WAC_DownloadManager::on_s_authentication_required( QNetworkReply *p_reply, QAuthenticator *p_authenticator )
+{
+ Q_UNUSED( p_reply );
+ Q_UNUSED( p_authenticator );
+}
+
+
+
+
+
+/**********************************************************************************
+ **********************************************************************************/
+DownloadData::DownloadData( api_httpreceiver *p_http, const char *p_url, int p_flags, ifc_downloadManagerCallback *p_callback )
+{
+ flags = p_flags;
+ http = p_http;
+ callback = p_callback;
+
+ if ( callback )
+ callback->AddRef();
+
+ hFile = INVALID_HANDLE_VALUE;
+ filepath[ 0 ] = 0;
+ fileext = 0;
+
+ int download_method = ( api_downloadManager::DOWNLOADEX_MASK_DOWNLOADMETHOD & flags );
+ switch ( download_method )
+ {
+ case api_downloadManager::DOWNLOADEX_TEMPFILE:
+ {
+ wchar_t temppath[ MAX_PATH - 14 ] = { 0 }; // MAX_PATH-14 'cause MSDN said so
+ GetTempPathW( MAX_PATH - 14, temppath );
+ GetTempFileNameW( temppath, L"wdl", 0, filepath );
+ hFile = CreateFileW( filepath, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, 0, CREATE_ALWAYS, 0, 0 );
+ }
+ break;
+ case api_downloadManager::DOWNLOADEX_CALLBACK:
+ if ( callback )
+ callback->GetLocation( filepath, MAX_PATH );
+ break;
+ }
+
+ strcpy_s( this->url, 1024, p_url );
+ source[ 0 ] = 0;
+ title[ 0 ] = 0;
+ if ( flags & api_downloadManager::DOWNLOADEX_CALLBACK )
+ {
+ if ( callback )
+ {
+ callback->GetSource( source, 1024 );
+ callback->GetTitle( title, 1024 );
+ }
+ }
+
+ connectionStart = lastDownloadTick = GetTickCount();
+ last_status = HTTPRECEIVER_STATUS_ERROR;
+ pending = ( flags & api_downloadManager::DOWNLOADEX_PENDING ) > 0;
+}
+
+DownloadData::~DownloadData()
+{
+ ServiceRelease( http, httpreceiverGUID );
+
+ http = NULL;
+
+ if ( fileext )
+ delete fileext;
+
+ int download_method = ( api_downloadManager::DOWNLOADEX_MASK_DOWNLOADMETHOD & flags );
+ if ( download_method == api_downloadManager::DOWNLOADEX_TEMPFILE && filepath[ 0 ] )
+ DeleteFileW( filepath );
+
+ if ( callback )
+ callback->Release();
+
+ callback = NULL;
+}
+
+
+void DownloadData::Retain()
+{
+ this->_refCount.fetch_add( 1 );
+}
+
+void DownloadData::Release()
+{
+ if ( this->_refCount.fetch_sub( 1 ) == 0 )
+ delete this;
+}
+
+
+void DownloadData::Close( ifc_downloadManagerCallback **callbackCopy )
+{
+ if ( hFile != INVALID_HANDLE_VALUE )
+ CloseHandle( hFile );
+
+ hFile = INVALID_HANDLE_VALUE;
+
+ if ( callbackCopy != NULL )
+ {
+ *callbackCopy = callback;
+ if ( callback != NULL )
+ callback->AddRef();
+ }
+ else if ( callback != NULL )
+ {
+ callback->Release();
+ callback = NULL;
+ }
+
+ // don't want to close http here, because someone might still want to get the headers out of it
+}
+
+bool DownloadData::getExtention()
+{
+ if ( fileext && *fileext )
+ return fileext;
+
+ char l_header_name_content_type[] = "Content-Type";
+ char *l_content_type = http->getheader( l_header_name_content_type );
+ if ( l_content_type && *l_content_type )
+ {
+ if ( _CONTENT_TYPES_EXTENSIONS.count( l_content_type ) == 1 )
+ fileext = _strdup( _CONTENT_TYPES_EXTENSIONS.find( l_content_type )->second.c_str() );
+ }
+
+ return fileext;
+}
+
+
+
+/**********************************************************************************
+ **********************************************************************************/
+
+ /**********************************************************************************
+ * PUBLIC *
+ **********************************************************************************/
+DownloadManager::DownloadManager()
+{
+ download_thread = NULL;
+ killswitch = CreateEvent( NULL, TRUE, FALSE, NULL );
+
+ InitializeCriticalSection( &downloadsCS );
+}
+
+
+void DownloadManager::Kill()
+{
+ SetEvent( killswitch );
+ if ( download_thread )
+ {
+ WaitForSingleObject( download_thread, 3000 );
+ CloseHandle( download_thread );
+ }
+
+ DeleteCriticalSection( &downloadsCS );
+ CloseHandle( killswitch );
+}
+
+
+static void SetUserAgent( api_httpreceiver *p_http )
+{
+ char agent[ 256 ] = { 0 };
+ StringCchPrintfA( agent, 256, "User-Agent: %S/%S", WASABI_API_APP->main_getAppName(), WASABI_API_APP->main_getVersionNumString() );
+ p_http->addheader( agent );
+
+ //QString l_winamp_user_agent = QString( "User-Agent: %1 Winamp/%2" ).arg( QWebEngineProfile::defaultProfile()->httpUserAgent(), STR_WINAMP_PRODUCTVER ).replace( ",", "." );
+
+ //http->addheader( l_winamp_user_agent.toStdString().c_str() );
+}
+
+
+DownloadToken DownloadManager::Download( const char *url, ifc_downloadManagerCallback *callback )
+{
+ return DownloadEx( url, callback, api_downloadManager::DOWNLOADEX_TEMPFILE );
+}
+
+DownloadToken DownloadManager::DownloadEx( const char *url, ifc_downloadManagerCallback *callback, int flags )
+{
+ if ( InitDownloadThread() )
+ {
+ api_httpreceiver *http = NULL;
+
+ ServiceBuild( http, httpreceiverGUID );
+
+ if ( http )
+ {
+ DownloadData *downloadData = new DownloadData( http, url, flags, callback );
+
+ int use_proxy = 1;
+ bool proxy80 = AGAVE_API_CONFIG->GetBool( internetConfigGroupGUID, L"proxy80", false );
+
+ if ( proxy80 && strstr( url, ":" ) && ( !strstr( url, ":80/" ) && strstr( url, ":80" ) != ( url + strlen( url ) - 3 ) ) )
+ use_proxy = 0;
+
+ const wchar_t *proxy = use_proxy ? AGAVE_API_CONFIG->GetString( internetConfigGroupGUID, L"proxy", 0 ) : 0;
+
+ http->open( API_DNS_AUTODNS, DOWNLOAD_BUFFER_SIZE, ( proxy && proxy[ 0 ] ) ? (const char *)AutoChar( proxy ) : NULL );
+
+ SetUserAgent( http );
+
+ if ( callback )
+ callback->OnInit( downloadData );
+
+ if ( downloadData->flags & api_downloadManager::DOWNLOADEX_UI )
+ {
+ for ( ifc_downloadManagerCallback *l_status : status_callbacks )
+ l_status->OnInit( downloadData );
+ }
+
+ //only call http->connect when it is not pending download request
+ if ( !( flags & DOWNLOADEX_PENDING ) )
+ http->connect( url, 1 );
+
+ //http->run(); // let's get this party started
+ EnterCriticalSection( &downloadsCS );
+ downloads.push_back( downloadData );
+ LeaveCriticalSection( &downloadsCS );
+
+ return downloadData;
+ }
+ }
+
+ return 0;
+}
+
+
+void DownloadManager::ResumePendingDownload( DownloadToken p_token )
+{
+ if ( !p_token )
+ return;
+
+ DownloadData *data = (DownloadData *)p_token;
+ if ( data->pending )
+ {
+ data->pending = false;
+ data->connectionStart = data->lastDownloadTick = GetTickCount();
+
+ data->http->connect( data->url );
+ }
+}
+
+void DownloadManager::CancelDownload( DownloadToken p_token )
+{
+ if ( !p_token )
+ return;
+
+ DownloadData *data = (DownloadData *)p_token;
+ EnterCriticalSection( &downloadsCS );
+
+ if ( downloads.end() != std::find( downloads.begin(), downloads.end(), data ) )
+ {
+ ifc_downloadManagerCallback *callback;
+ data->Close( &callback );
+
+ //downloads.eraseObject(p_data);
+ auto it = std::find( downloads.begin(), downloads.end(), data );
+ if ( it != downloads.end() )
+ {
+ downloads.erase( it );
+ }
+
+ LeaveCriticalSection( &downloadsCS );
+
+ if ( callback )
+ {
+ callback->OnCancel( p_token );
+ if ( data->flags & api_downloadManager::DOWNLOADEX_UI )
+ {
+ for ( ifc_downloadManagerCallback *l_status : status_callbacks )
+ l_status->OnCancel( p_token );
+ }
+
+ callback->Release();
+ }
+
+ data->Release();
+ }
+ else
+ LeaveCriticalSection( &downloadsCS );
+}
+
+void DownloadManager::RetainDownload( DownloadToken p_token )
+{
+ if ( !p_token )
+ return;
+
+ DownloadData *data = (DownloadData *)p_token;
+ if ( data )
+ data->Retain();
+}
+
+void DownloadManager::ReleaseDownload( DownloadToken p_token )
+{
+ if ( !p_token )
+ return;
+
+ DownloadData *data = (DownloadData *)p_token;
+ if ( data )
+ data->Release();
+}
+
+
+void DownloadManager::RegisterStatusCallback( ifc_downloadManagerCallback *callback )
+{
+ EnterCriticalSection( &downloadsCS );
+ status_callbacks.push_back( callback );
+ LeaveCriticalSection( &downloadsCS );
+}
+
+void DownloadManager::UnregisterStatusCallback( ifc_downloadManagerCallback *callback )
+{
+ EnterCriticalSection( &downloadsCS );
+
+ auto it = std::find( status_callbacks.begin(), status_callbacks.end(), callback );
+ if ( it != status_callbacks.end() )
+ status_callbacks.erase( it );
+
+ LeaveCriticalSection( &downloadsCS );
+}
+
+
+bool DownloadManager::IsPending( DownloadToken token )
+{
+ DownloadData *data = (DownloadData *)token;
+ if ( data )
+ return data->pending;
+ else
+ return FALSE;
+}
+
+
+/**********************************************************************************
+ * PRIVATE *
+ **********************************************************************************/
+bool DownloadManager::DownloadThreadTick()
+{
+ unsigned int i = 0;
+ char *downloadBuffer = (char *)malloc( DOWNLOAD_BUFFER_SIZE );
+
+ while ( WaitForSingleObject( killswitch, 0 ) != WAIT_OBJECT_0 )
+ {
+ EnterCriticalSection( &downloadsCS );
+ if ( downloads.empty() )
+ {
+ // TODO: might be nice to dynamically increase the sleep time if this happens
+ // (maybe to INFINITE and have Download() wake us?)
+ LeaveCriticalSection( &downloadsCS );
+
+ free( downloadBuffer );
+
+ return true;
+ }
+
+ if ( i >= downloads.size() )
+ {
+ LeaveCriticalSection( &downloadsCS );
+ free( downloadBuffer );
+
+ return true;
+ }
+
+ DownloadData *thisDownload = downloads[ i ];
+ if ( thisDownload->pending )
+ {
+ LeaveCriticalSection( &downloadsCS );
+ }
+ else
+ {
+ thisDownload->Retain();
+ LeaveCriticalSection( &downloadsCS );
+
+
+ INT tick = Tick( thisDownload, downloadBuffer, DOWNLOAD_BUFFER_SIZE );
+ switch ( tick )
+ {
+ case TICK_NODATA:
+ // do nothing
+ break;
+
+ case TICK_CONNECTING:
+ break;
+
+ case TICK_CONNECTED:
+ if ( thisDownload->callback )
+ thisDownload->callback->OnConnect( thisDownload );
+ if ( thisDownload->flags & api_downloadManager::DOWNLOADEX_UI )
+ {
+ for ( ifc_downloadManagerCallback *l_status : status_callbacks )
+ l_status->OnConnect( thisDownload );
+ }
+ break;
+
+ case TICK_SUCCESS:
+ if ( thisDownload->callback )
+ thisDownload->callback->OnTick( thisDownload );
+ if ( thisDownload->flags & api_downloadManager::DOWNLOADEX_UI )
+ {
+ for ( ifc_downloadManagerCallback *l_status : status_callbacks )
+ l_status->OnTick( thisDownload );
+ }
+
+ // TODO: send out update l_callback
+ break;
+
+ case TICK_FINISHED:
+ case TICK_FAILURE:
+ case TICK_TIMEOUT:
+ case TICK_CANT_CONNECT:
+ case TICK_WRITE_ERROR:
+ FinishDownload( thisDownload, tick );
+ break;
+ }
+ thisDownload->Release();
+ }
+
+ i++;
+ }
+
+ free( downloadBuffer );
+
+ return false; // we only get here when killswitch is set
+}
+
+int DownloadManager::DownloadTickThreadPoolFunc( HANDLE handle, void *user_data, intptr_t id )
+{
+ DownloadManager *dlmgr = (DownloadManager *)user_data;
+ if ( dlmgr->DownloadThreadTick() )
+ {
+ TimerHandle t( handle );
+ t.Wait( DOWNLOAD_SLEEP_MS );
+ }
+ else
+ {
+ WASABI_API_THREADPOOL->RemoveHandle( 0, handle );
+ SetEvent( dlmgr->download_thread );
+ }
+
+ return 0;
+}
+
+bool DownloadManager::InitDownloadThread()
+{
+ if ( download_thread == NULL )
+ {
+ download_thread = CreateEvent( 0, FALSE, FALSE, 0 );
+ TimerHandle t;
+ WASABI_API_THREADPOOL->AddHandle( 0, t, DownloadTickThreadPoolFunc, this, 0, api_threadpool::FLAG_LONG_EXECUTION );
+ t.Wait( DOWNLOAD_SLEEP_MS );
+ }
+
+ return ( download_thread != NULL );
+}
+
+void DownloadManager::FinishDownload( DownloadData *p_data, int code )
+{
+ if ( p_data == NULL )
+ return;
+
+ ifc_downloadManagerCallback *l_callback = NULL;
+
+ EnterCriticalSection( &downloadsCS );
+ p_data->Close( &l_callback );
+ LeaveCriticalSection( &downloadsCS );
+
+ if ( l_callback != NULL )
+ {
+ if ( code == TICK_FINISHED )
+ {
+ l_callback->OnFinish( p_data );
+ if ( p_data->flags & api_downloadManager::DOWNLOADEX_UI )
+ {
+ for ( ifc_downloadManagerCallback *l_data : status_callbacks )
+ l_data->OnFinish( p_data );
+ }
+ }
+ else
+ {
+ l_callback->OnError( p_data, code );
+ if ( p_data->flags & api_downloadManager::DOWNLOADEX_UI )
+ {
+ for ( ifc_downloadManagerCallback *l_data : status_callbacks )
+ l_data->OnError( p_data, code );
+ }
+ }
+
+ l_callback->Release();
+ }
+
+ EnterCriticalSection( &downloadsCS );
+
+ auto it = std::find( downloads.begin(), downloads.end(), p_data );
+ if ( it != downloads.end() )
+ downloads.erase( it );
+
+ LeaveCriticalSection( &downloadsCS );
+
+ p_data->Release();
+}
+
+int DownloadManager::Tick( DownloadData *thisDownload, void *buffer, int bufferSize )
+{
+ if ( !thisDownload )
+ return api_downloadManager::TICK_FAILURE;
+
+ int state = thisDownload->http->run();
+ if ( state == HTTPRECEIVER_RUN_ERROR || thisDownload == NULL )
+ return api_downloadManager::TICK_FAILURE;
+
+ if ( !thisDownload->fileext )
+ thisDownload->getExtention();
+
+
+ int downloaded = thisDownload->http->get_bytes( buffer, bufferSize );
+ if ( downloaded )
+ {
+ switch ( thisDownload->flags & DOWNLOADEX_MASK_DOWNLOADMETHOD )
+ {
+ case api_downloadManager::DOWNLOADEX_BUFFER:
+ {
+ thisDownload->buffer.reserve( thisDownload->http->content_length() );
+ thisDownload->buffer.add( buffer, downloaded );
+ }
+ break;
+ case api_downloadManager::DOWNLOADEX_TEMPFILE:
+ {
+ DWORD written = 0;
+ WriteFile( thisDownload->hFile, buffer, downloaded, &written, NULL );
+ if ( written != downloaded )
+ return api_downloadManager::TICK_WRITE_ERROR;
+ }
+ break;
+ case api_downloadManager::DOWNLOADEX_CALLBACK:
+ {
+ if ( thisDownload->flags & api_downloadManager::DOWNLOADEX_UI )
+ {
+ for ( ifc_downloadManagerCallback *l_status : status_callbacks )
+ l_status->OnData( thisDownload, buffer, downloaded );
+ }
+
+ if ( thisDownload->callback )
+ thisDownload->callback->OnData( thisDownload, buffer, downloaded );
+ }
+ }
+
+ thisDownload->lastDownloadTick = GetTickCount();
+ thisDownload->bytesDownloaded += downloaded;
+
+ return api_downloadManager::TICK_SUCCESS;
+ }
+ else // nothing in the buffer
+ {
+ if ( state == HTTPRECEIVER_RUN_CONNECTION_CLOSED ) // see if the connection is closed
+ {
+ return api_downloadManager::TICK_FINISHED; // yay we're done
+ }
+
+ if ( GetTickCount() - thisDownload->lastDownloadTick > DOWNLOAD_TIMEOUT_MS ) // check for timeout
+ return api_downloadManager::TICK_TIMEOUT;
+
+ switch ( thisDownload->http->get_status() )
+ {
+ case HTTPRECEIVER_STATUS_CONNECTING:
+ if ( thisDownload->last_status != HTTPRECEIVER_STATUS_CONNECTING )
+ {
+ thisDownload->last_status = HTTPRECEIVER_STATUS_CONNECTING;
+ return api_downloadManager::TICK_CONNECTING;
+ }
+ else
+ {
+ return api_downloadManager::TICK_NODATA;
+ }
+ case HTTPRECEIVER_STATUS_READING_HEADERS:
+ if ( thisDownload->last_status != HTTPRECEIVER_STATUS_READING_HEADERS )
+ {
+ thisDownload->last_status = HTTPRECEIVER_STATUS_READING_HEADERS;
+ return api_downloadManager::TICK_CONNECTED;
+ }
+ else
+ {
+ return api_downloadManager::TICK_NODATA;
+ }
+ }
+
+ if ( !thisDownload->replyCode )
+ thisDownload->replyCode = thisDownload->http->getreplycode();
+
+ switch ( thisDownload->replyCode )
+ {
+ case 0:
+ case 100:
+ case 200:
+ case 201:
+ case 202:
+ case 203:
+ case 204:
+ case 205:
+ case 206:
+ return api_downloadManager::TICK_NODATA;
+ default:
+ return api_downloadManager::TICK_CANT_CONNECT;
+ }
+ }
+}
+
+
+
+
+
+#define CBCLASS DownloadManager
+START_DISPATCH;
+CB( API_DOWNLOADMANAGER_DOWNLOAD, Download )
+CB( API_DOWNLOADMANAGER_DOWNLOADEX, DownloadEx )
+CB( API_DOWNLOADMANAGER_GETRECEIVER, GetReceiver )
+CB( API_DOWNLOADMANAGER_GETLOCATION, GetLocation )
+VCB( API_DOWNLOADMANAGER_SETLOCATION, SetLocation )
+CB( API_DOWNLOADMANAGER_GETEXTENTION, GetExtention )
+CB( API_DOWNLOADMANAGER_GETURL, GetUrl )
+CB( API_DOWNLOADMANAGER_GETBYTESDOWNLOADED, GetBytesDownloaded );
+CB( API_DOWNLOADMANAGER_GETBUFFER, GetBuffer );
+VCB( API_DOWNLOADMANAGER_RESUMEPENDINGDOWNLOAD, ResumePendingDownload );
+VCB( API_DOWNLOADMANAGER_CANCELDOWNLOAD, CancelDownload );
+VCB( API_DOWNLOADMANAGER_RETAINDOWNLOAD, RetainDownload );
+VCB( API_DOWNLOADMANAGER_RELEASEDOWNLOAD, ReleaseDownload );
+VCB( API_DOWNLOADMANAGER_REGISTERSTATUSCALLBACK, RegisterStatusCallback );
+VCB( API_DOWNLOADMANAGER_UNREGISTERSTATUSCALLBACK, UnregisterStatusCallback );
+CB( API_DOWNLOADMANAGER_GETSOURCE, GetSource );
+CB( API_DOWNLOADMANAGER_GETTITLE, GetTitle );
+CB( API_DOWNLOADMANAGER_ISPENDING, IsPending );
+END_DISPATCH;
+#undef CBCLASS