Notepad3/grepWinNP3/sktoolslib_mod/DownloadFile.cpp
2020-09-19 02:24:33 +02:00

182 lines
7.2 KiB
C++

// sktoolslib - common files for SK tools
// Copyright (C) 2014, 2017, 2020 - Stefan Kueng
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software Foundation,
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
//
#include "stdafx.h"
#include "DownloadFile.h"
#include "DebugOutput.h"
#pragma comment(lib, "wininet.lib")
CDownloadFile::CDownloadFile(LPCWSTR useragent, CProgressDlg* pProgress)
: m_pProgress(pProgress)
{
hOpenHandle = InternetOpen(useragent, INTERNET_OPEN_TYPE_PRECONFIG, nullptr, nullptr, 0);
}
CDownloadFile::~CDownloadFile(void)
{
if (hOpenHandle)
InternetCloseHandle(hOpenHandle);
}
bool CDownloadFile::DownloadFile(const std::wstring& url, const std::wstring& dest) const
{
wchar_t hostname[INTERNET_MAX_HOST_NAME_LENGTH] = {0};
wchar_t urlpath[INTERNET_MAX_PATH_LENGTH] = {0};
URL_COMPONENTS urlComponents = {0};
urlComponents.dwStructSize = sizeof(urlComponents);
urlComponents.lpszHostName = hostname;
urlComponents.dwHostNameLength = INTERNET_MAX_HOST_NAME_LENGTH;
urlComponents.lpszUrlPath = urlpath;
urlComponents.dwUrlPathLength = INTERNET_MAX_PATH_LENGTH;
if (!InternetCrackUrl(url.c_str(), (DWORD)url.size(), 0, &urlComponents))
return GetLastError() == 0;
DeleteUrlCacheEntry(url.c_str());
bool isHttps = urlComponents.nScheme == INTERNET_SCHEME_HTTPS;
HINTERNET hConnectHandle = InternetConnect(hOpenHandle, hostname, urlComponents.nPort, nullptr, nullptr, isHttps ? INTERNET_SCHEME_HTTP : urlComponents.nScheme, 0, 0);
if (!hConnectHandle)
{
DWORD err = GetLastError();
CTraceToOutputDebugString::Instance()(TEXT(__FUNCTION__) L": Download of %s failed on InternetConnect: %d\n", url.c_str(), err);
return err == 0;
}
HINTERNET hResourceHandle = HttpOpenRequest(hConnectHandle, nullptr, urlpath, nullptr, nullptr, nullptr, INTERNET_FLAG_KEEP_CONNECTION | (isHttps ? INTERNET_FLAG_SECURE : 0), 0);
if (!hResourceHandle)
{
DWORD err = GetLastError();
CTraceToOutputDebugString::Instance()(TEXT(__FUNCTION__) L": Download of %s failed on HttpOpenRequest: %d\n", url.c_str(), err);
InternetCloseHandle(hConnectHandle);
return err == 0;
}
{
DWORD dwError = 0;
bool httpsendrequest = false;
do
{
httpsendrequest = !!HttpSendRequest(hResourceHandle, nullptr, 0, nullptr, 0);
dwError = InternetErrorDlg(nullptr, hResourceHandle, ERROR_SUCCESS, FLAGS_ERROR_UI_FILTER_FOR_ERRORS | FLAGS_ERROR_UI_FLAGS_CHANGE_OPTIONS | FLAGS_ERROR_UI_FLAGS_GENERATE_DATA, nullptr);
} while (dwError == ERROR_INTERNET_FORCE_RETRY);
if (!httpsendrequest)
{
DWORD err = GetLastError();
CTraceToOutputDebugString::Instance()(TEXT(__FUNCTION__) L": Download of %s failed: %d, %d\n", url.c_str(), httpsendrequest, err);
InternetCloseHandle(hResourceHandle);
InternetCloseHandle(hConnectHandle);
return err == 0;
}
}
DWORD contentLength = 0;
{
DWORD length = sizeof(contentLength);
HttpQueryInfo(hResourceHandle, HTTP_QUERY_CONTENT_LENGTH | HTTP_QUERY_FLAG_NUMBER, (LPVOID)&contentLength, &length, nullptr);
}
{
DWORD statusCode = 0;
DWORD length = sizeof(statusCode);
if (!HttpQueryInfo(hResourceHandle, HTTP_QUERY_STATUS_CODE | HTTP_QUERY_FLAG_NUMBER, (LPVOID)&statusCode, &length, nullptr) || statusCode != 200)
{
CTraceToOutputDebugString::Instance()(TEXT(__FUNCTION__) L": Download of %s returned %d\n", url.c_str(), statusCode);
InternetCloseHandle(hResourceHandle);
InternetCloseHandle(hConnectHandle);
if (statusCode == 404)
return false;
else if (statusCode == 403)
return false;
return false; // INET_E_DOWNLOAD_FAILURE;
}
}
HANDLE hDestFile = CreateFile(dest.c_str(), GENERIC_WRITE | GENERIC_READ, FILE_SHARE_DELETE | FILE_SHARE_READ, nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr);
if (hDestFile == INVALID_HANDLE_VALUE)
{
InternetCloseHandle(hResourceHandle);
InternetCloseHandle(hConnectHandle);
return false;
}
DWORD downloadedSum = 0; // sum of bytes downloaded so far
do
{
DWORD size; // size of the data available
if (!InternetQueryDataAvailable(hResourceHandle, &size, 0, 0))
{
DWORD err = GetLastError();
CTraceToOutputDebugString::Instance()(TEXT(__FUNCTION__) L": Download of %s failed on InternetQueryDataAvailable: %d\n", url.c_str(), err);
InternetCloseHandle(hResourceHandle);
InternetCloseHandle(hConnectHandle);
return err == 0;
}
DWORD downloaded; // size of the downloaded data
auto Data = std::make_unique<wchar_t[]>(size + 1);
if (!InternetReadFile(hResourceHandle, (LPVOID)Data.get(), size, &downloaded))
{
DWORD err = GetLastError();
CTraceToOutputDebugString::Instance()(TEXT(__FUNCTION__) L": Download of %s failed on InternetReadFile: %d\n", url.c_str(), err);
InternetCloseHandle(hResourceHandle);
InternetCloseHandle(hConnectHandle);
return err == 0;
}
if (downloaded == 0)
{
break;
}
Data[downloaded] = '\0';
DWORD dwWritten = 0;
WriteFile(hDestFile, Data.get(), downloaded, &dwWritten, nullptr);
downloadedSum += downloaded;
if (contentLength == 0) // got no content-length from web server
{
if (m_pProgress)
m_pProgress->SetProgress(0, 0);
}
else
{
if (downloadedSum > contentLength)
downloadedSum = contentLength - 1;
if (m_pProgress)
{
m_pProgress->SetProgress(downloadedSum, contentLength + 1);
if (m_pProgress->HasUserCancelled())
{
downloadedSum = 0;
break;
}
}
}
} while (true);
CloseHandle(hDestFile);
InternetCloseHandle(hResourceHandle);
InternetCloseHandle(hConnectHandle);
if (downloadedSum == 0)
{
CTraceToOutputDebugString::Instance()(TEXT(__FUNCTION__) L": Download size of %s was zero or user canceled.\n", url.c_str());
return false; // INET_E_DOWNLOAD_FAILURE;
}
return true;
}