DragonNest/Client/DnLauncher2/DnAutoUpdate/CHttpDownload.cpp
2024-12-19 09:48:26 +08:00

277 lines
No EOL
6.8 KiB
C++

#include "stdafx.h"
#include "CHttpDownload.h"
#include "MD5Checksum.h"
#include "mmsystem.h"
BOOL ClientDeleteFile( LPCTSTR FineName )
{
DWORD dwNowTime = timeGetTime();
if( ::GetFileAttributes( FineName ) != 0xFFFFFFFF )
{
while( 1 )
{
if( timeGetTime() > dwNowTime + 5000 )
{
return FALSE;
}
DWORD dwAttr = ::GetFileAttributes( FineName );
if( dwAttr & FILE_ATTRIBUTE_READONLY )
{
dwAttr &= ~FILE_ATTRIBUTE_READONLY;
::SetFileAttributes( FineName , dwAttr );
}
if( DeleteFile( FineName ) )
{
return TRUE;
}
}
}
return TRUE;
}
CHttpDownload::CHttpDownload()
: m_hHttpConnection( NULL )
, m_dwTempFileLength( 0 )
, m_dwDownloadFileLength( 0 )
, m_dwTotalFileLength( 0 )
, m_dwTotalBytes( 0 )
, m_fRate( 0.0f )
, m_fPercent( 0.0f )
, m_bExit( FALSE )
{
}
CHttpDownload::~CHttpDownload()
{
Reset();
}
void CHttpDownload::Reset()
{
if( m_hHttpConnection )
InternetCloseHandle( m_hHttpConnection );
m_hHttpConnection = NULL;
m_dwTempFileLength = 0;
m_dwDownloadFileLength = 0;
m_dwTotalFileLength = 0;
m_dwTotalBytes = 0;
m_fRate = 0.0f;
m_fPercent = 0.0f;
m_bExit = FALSE;
}
int CHttpDownload::DownloadFile( HINTERNET hInternetSession, CString strHttpPath, CString strLocalPath )
{
if( hInternetSession == NULL || strHttpPath.GetLength() == 0 || strLocalPath.GetLength() == 0 )
return HTTPDOWNLOAD_ERROR; // argument error
m_strHttpPath = strHttpPath;
m_strLocalPath = strLocalPath + L".tmp";
// 다운 받을 파일 크기 구하기
DWORD dwDownloadFileSize = GetDownloadFileSize( hInternetSession, m_strHttpPath );
if( dwDownloadFileSize == 0 )
return HTTPDOWNLOAD_ERROR;
TRACE( L"Download File Size = %d\n", dwDownloadFileSize );
// 다운 완료된 파일이 있을 경우 유효성 검사
CFile file;
CFileException e;
if( file.Open( strLocalPath, CFile::modeRead, &e ) )
{
if( file.GetLength() == dwDownloadFileSize )
{
m_dwTotalFileLength = dwDownloadFileSize;
m_dwTotalBytes = dwDownloadFileSize;
m_fPercent = 100.0f;
return HTTPDOWNLOAD_ALREADY_DOWNLOAD; // 이미 다 받아진 파일이 있음
}
else
ClientDeleteFile( strLocalPath ); // 받았으나 서버의 파일 정보와 상이할 경우 삭제 후 다시 받음
}
// 기존 다운받던 tmp 파일 체크
FILE* fp;
fp = _wfopen( m_strLocalPath, _T("r") );
UINT uiFileModeFlag = CFile::modeCreate | CFile::modeWrite | CFile::shareDenyWrite;
if( fp != NULL ) // 다운받던 tmp파일이 있을 경우
{
fclose( fp );
uiFileModeFlag |= CFile::modeNoTruncate;
}
if( !file.Open( m_strLocalPath, uiFileModeFlag, &e ) )
return HTTPDOWNLOAD_ERROR; // file open error
file.SeekToEnd();
m_dwTempFileLength = static_cast<DWORD>( file.GetLength() );
if( m_dwTempFileLength < dwDownloadFileSize )
{
// Http 열기
CString strAddHeader;
strAddHeader.Format( L"Range:bytes=%d-\nCache-Control:no-cache\nPragma:no-cache", m_dwTempFileLength );
m_hHttpConnection = InternetOpenUrl( hInternetSession, m_strHttpPath, strAddHeader, strAddHeader.GetLength(),
INTERNET_FLAG_RELOAD | INTERNET_FLAG_DONT_CACHE, 0 );
if( m_hHttpConnection == NULL )
{
file.Close(); // http connection error
return HTTPDOWNLOAD_ERROR;
}
TCHAR szBuffer[MAX_PATH]={0,};
DWORD dwBufferSize = MAX_PATH;
DWORD dwIndex = 0;
// 다운받아야 할 파일 크기 구하기
if( HttpQueryInfo( m_hHttpConnection, HTTP_QUERY_CONTENT_LENGTH, (LPVOID)&szBuffer, &dwBufferSize, &dwIndex ) != FALSE )
{
m_dwDownloadFileLength = (DWORD)_wtoi( szBuffer );
m_dwTotalFileLength = m_dwTempFileLength + m_dwDownloadFileLength;
}
// 다운로드 시작 (이어받기)
if( m_dwDownloadFileLength > 0 )
{
DWORD dwRead, dwSize;
DWORD dwStartTick, dwCurrentTick;
m_dwTotalBytes = m_dwTempFileLength;
char szReadBuf[32768]; // 32KiB
dwStartTick = GetTickCount();
do
{
if( IsExit() )
{
file.Close();
InternetCloseHandle( m_hHttpConnection );
m_hHttpConnection = NULL;
return HTTPDOWNLOAD_TERMINATE;
}
if( !InternetQueryDataAvailable( m_hHttpConnection, &dwSize, 0, 0 ) )
{
// error "data not available!"
break;
}
if( !InternetReadFile( m_hHttpConnection, szReadBuf, dwSize, &dwRead ) )
{
// error "reading file fail!"
break;
}
if( dwRead )
{
dwCurrentTick = GetTickCount();
file.Write( szReadBuf, dwRead );
m_dwTotalBytes += dwRead;
m_fRate = (float)(m_dwTotalBytes / 1000) / (float)((dwCurrentTick - dwStartTick) / 1000);
m_fPercent = (float)m_dwTotalBytes / (float)m_dwTotalFileLength * 100.0f;
TRACE( L"Read : [%09d / %09d] --- [%4.2f KB/s] < %4.0f%% >\n", m_dwTotalBytes, m_dwTotalFileLength, m_fRate, m_fPercent );
}
} while( dwRead != 0 );
}
InternetCloseHandle( m_hHttpConnection );
}
file.Close();
TRACE( L"--- MD5 Check Start... ---\n" );
// 올바르게 다운로드 되었는지 MD5 Check
if( !GetMD5Checksum() )
{
ClientDeleteFile( m_strLocalPath );
return HTTPDOWNLOAD_ERROR; // md5 check error
}
TRACE( L"--- MD5 Check End! ---\n" );
// 정리 tmp -> 원래 파일명
CString strNewLocalPath = m_strLocalPath.Left( m_strLocalPath.GetLength() - 4 );
CFile::Rename( m_strLocalPath, strNewLocalPath );
TRACE( L"--- Download Complete! ---\n" );
return HTTPDOWNLOAD_SUCCESS;
}
DWORD CHttpDownload::GetDownloadFileSize( HINTERNET hInternetSession, CString strHttpPath )
{
if( hInternetSession == NULL )
return 0;
m_hHttpConnection = InternetOpenUrl( hInternetSession, strHttpPath, NULL, 0, INTERNET_FLAG_RELOAD | INTERNET_FLAG_DONT_CACHE, 0 );
if( m_hHttpConnection == NULL )
return 0;
TCHAR szBuffer[MAX_PATH]={0,};
DWORD dwBufferSize = MAX_PATH;
DWORD dwIndex = 0;
DWORD dwFileSize = 0;
// 다운받아야 할 파일 크기 구하기
if( HttpQueryInfo( m_hHttpConnection, HTTP_QUERY_CONTENT_LENGTH, (LPVOID)&szBuffer, &dwBufferSize, &dwIndex ) != FALSE )
dwFileSize = (DWORD)_wtoi( szBuffer );
InternetCloseHandle( m_hHttpConnection );
return dwFileSize;
}
BOOL CHttpDownload::GetMD5Checksum()
{
CString strHttpMD5 = m_strHttpPath + L".MD5";
CString strLocalMD5 = m_strLocalPath + L".MD5";
HRESULT hr = E_FAIL;
for( int i = 0 ; i < RETRY_MAX_COUNT ; i++ )
{
BOOL bResult = DeleteUrlCacheEntry( strHttpMD5 );
hr = ::URLDownloadToFile( NULL, strHttpMD5, strLocalMD5, 0, NULL );
if( hr == S_OK )
break;
}
if( FAILED( hr ) )
{
TRACE( L"MD5 Check File Download Failed!, Error : %ld", hr );
return FALSE;
}
CString strDownloadChecksum;
CFile fileMD5;
CFileException e;
if( !fileMD5.Open( strLocalMD5, CFile::modeRead, &e ) )
return FALSE;
char pBuf[100];
fileMD5.Read( pBuf, 100 );
fileMD5.Close();
ClientDeleteFile( strLocalMD5 );
strDownloadChecksum = pBuf;
int nIndex = strDownloadChecksum.Find('\n');
strDownloadChecksum = strDownloadChecksum.Left( nIndex - 1 );
CString strLocalChecksum = CMD5Checksum::GetMD5( m_strLocalPath );
// MD5 비교
return wcscmp( strLocalChecksum, strDownloadChecksum ) == 0 ? TRUE : FALSE;
}