mirror of
https://github.com/pbatard/rufus.git
synced 2024-08-14 23:57:05 +00:00
311 lines
11 KiB
C
311 lines
11 KiB
C
/*
|
|
* Rufus: The Reliable USB Formatting Utility
|
|
* Networking functionality (web file download, etc.)
|
|
* Copyright (c) 2012 Pete Batard <pete@akeo.ie>
|
|
*
|
|
* 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 3 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, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
/* Memory leaks detection - define _CRTDBG_MAP_ALLOC as preprocessor macro */
|
|
#ifdef _CRTDBG_MAP_ALLOC
|
|
#include <stdlib.h>
|
|
#include <crtdbg.h>
|
|
#endif
|
|
|
|
#include <windows.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
|
|
#include "msapi_utf8.h"
|
|
#include "rufus.h"
|
|
#include "resource.h"
|
|
// winhttp.h is not available for WDK and MinGW32, so we have to use a replacement
|
|
#include "net.h"
|
|
|
|
/*
|
|
* FormatMessage does not handle WinHTTP
|
|
*/
|
|
const char* WinHTTPErrorString(void)
|
|
{
|
|
static char err_string[34];
|
|
DWORD error_code;
|
|
|
|
error_code = GetLastError();
|
|
|
|
if ((error_code < WINHTTP_ERROR_BASE) || (error_code > WINHTTP_ERROR_LAST))
|
|
return WindowsErrorString();
|
|
|
|
switch(error_code) {
|
|
case ERROR_WINHTTP_OUT_OF_HANDLES:
|
|
return "No more handles could be generated at this time.";
|
|
case ERROR_WINHTTP_TIMEOUT:
|
|
return "The request has timed out.";
|
|
case ERROR_WINHTTP_INTERNAL_ERROR:
|
|
return "An internal error has occurred.";
|
|
case ERROR_WINHTTP_INVALID_URL:
|
|
return "The URL is invalid.";
|
|
case ERROR_WINHTTP_UNRECOGNIZED_SCHEME:
|
|
return "The URL scheme could not be recognized or is not supported.";
|
|
case ERROR_WINHTTP_NAME_NOT_RESOLVED:
|
|
return "The server name could not be resolved.";
|
|
case ERROR_WINHTTP_INVALID_OPTION:
|
|
return "The request specified an invalid option value.";
|
|
case ERROR_WINHTTP_OPTION_NOT_SETTABLE:
|
|
return "The request option cannot be set, only queried.";
|
|
case ERROR_WINHTTP_SHUTDOWN:
|
|
return "The Win32 HTTP function support is being shut down or unloaded.";
|
|
case ERROR_WINHTTP_LOGIN_FAILURE:
|
|
return "The request to connect and log on to the server failed.";
|
|
case ERROR_WINHTTP_OPERATION_CANCELLED:
|
|
return "The operation was canceled";
|
|
case ERROR_WINHTTP_INCORRECT_HANDLE_TYPE:
|
|
return "The type of handle supplied is incorrect for this operation.";
|
|
case ERROR_WINHTTP_INCORRECT_HANDLE_STATE:
|
|
return "The requested operation cannot be carried out because the handle supplied is not in the correct state.";
|
|
case ERROR_WINHTTP_CANNOT_CONNECT:
|
|
return "The attempt to connect to the server failed.";
|
|
case ERROR_WINHTTP_CONNECTION_ERROR:
|
|
return "The connection with the server has been terminated.";
|
|
case ERROR_WINHTTP_RESEND_REQUEST:
|
|
return "The Win32 HTTP function needs to redo the request.";
|
|
case ERROR_WINHTTP_SECURE_CERT_DATE_INVALID:
|
|
return "SSL certificate date indicates that the certificate is expired.";
|
|
case ERROR_WINHTTP_SECURE_CERT_CN_INVALID:
|
|
return "SSL certificate common name (host name field) is incorrect.";
|
|
case ERROR_WINHTTP_CLIENT_AUTH_CERT_NEEDED:
|
|
return "Client Authentication certificate needed";
|
|
case ERROR_WINHTTP_SECURE_INVALID_CA:
|
|
return "SSL certificate has been issued by an invalid Certification Authority.";
|
|
case ERROR_WINHTTP_SECURE_CERT_REV_FAILED:
|
|
return "SSL certificate revocation check failed.";
|
|
case ERROR_WINHTTP_CANNOT_CALL_BEFORE_OPEN:
|
|
return "Cannot use this call before WinHttpOpen";
|
|
case ERROR_WINHTTP_CANNOT_CALL_BEFORE_SEND:
|
|
return "Cannot use this call before WinHttpSend";
|
|
case ERROR_WINHTTP_CANNOT_CALL_AFTER_SEND:
|
|
return "Cannot use this call after WinHttpSend";
|
|
case ERROR_WINHTTP_CANNOT_CALL_AFTER_OPEN:
|
|
return "Cannot use this call after WinHttpOpen";
|
|
case ERROR_WINHTTP_HEADER_NOT_FOUND:
|
|
return "HTTP header was not found.";
|
|
case ERROR_WINHTTP_INVALID_SERVER_RESPONSE:
|
|
return "Invalid HTTP server response.";
|
|
case ERROR_WINHTTP_INVALID_HEADER:
|
|
return "Invalid HTTP header.";
|
|
case ERROR_WINHTTP_INVALID_QUERY_REQUEST:
|
|
return "Invalid HTTP query request.";
|
|
case ERROR_WINHTTP_HEADER_ALREADY_EXISTS:
|
|
return "HTTP header already exists.";
|
|
case ERROR_WINHTTP_REDIRECT_FAILED:
|
|
return "HTTP redirect failed.";
|
|
case ERROR_WINHTTP_SECURE_CHANNEL_ERROR:
|
|
return "Unnable to establish secure HTTP channel.";
|
|
case ERROR_WINHTTP_BAD_AUTO_PROXY_SCRIPT:
|
|
return "Bad auto proxy script.";
|
|
case ERROR_WINHTTP_UNABLE_TO_DOWNLOAD_SCRIPT:
|
|
return "Unable to download script.";
|
|
case ERROR_WINHTTP_SECURE_INVALID_CERT:
|
|
return "SSL certificate is invalid.";
|
|
case ERROR_WINHTTP_SECURE_CERT_REVOKED:
|
|
return "SSL certificate has been revoked.";
|
|
case ERROR_WINHTTP_NOT_INITIALIZED:
|
|
return "WinHTTP has not be initialized.";
|
|
case ERROR_WINHTTP_SECURE_FAILURE:
|
|
return "SSL failure.";
|
|
case ERROR_WINHTTP_AUTO_PROXY_SERVICE_ERROR:
|
|
return "Auto proxy service error.";
|
|
case ERROR_WINHTTP_SECURE_CERT_WRONG_USAGE:
|
|
return "Wrong SSL certificate usage.";
|
|
case ERROR_WINHTTP_AUTODETECTION_FAILED:
|
|
return "HTTP autodetection failed.";
|
|
case ERROR_WINHTTP_HEADER_COUNT_EXCEEDED:
|
|
return "HTTP header count exceeded.";
|
|
case ERROR_WINHTTP_HEADER_SIZE_OVERFLOW:
|
|
return "HTTP header size overflow.";
|
|
case ERROR_WINHTTP_CHUNKED_ENCODING_HEADER_SIZE_OVERFLOW:
|
|
return "Chunked encoding HTTP header size overflow.";
|
|
case ERROR_WINHTTP_RESPONSE_DRAIN_OVERFLOW:
|
|
return "Response drain overflow.";
|
|
case ERROR_WINHTTP_CLIENT_CERT_NO_PRIVATE_KEY:
|
|
return "Certificate does not contain a private key.";
|
|
case ERROR_WINHTTP_CLIENT_CERT_NO_ACCESS_PRIVATE_KEY:
|
|
return "Unable to access client certificate's private key.";
|
|
default:
|
|
safe_sprintf(err_string, sizeof(err_string), "WinHTTP unknown error 0x%08X", error_code);
|
|
return err_string;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Download a file from an URL
|
|
* Mostly taken from http://msdn.microsoft.com/en-us/library/aa384270.aspx
|
|
*/
|
|
BOOL DownloadFile(const char* url, const char* file)
|
|
{
|
|
BOOL r=FALSE;
|
|
DWORD dwSize, dwDownloaded, dwTotalSize, dwReadSize, dwTotalSizeSize = sizeof(dwTotalSize);
|
|
FILE* fd = NULL;
|
|
LONG progress_style;
|
|
unsigned char* buf = NULL;
|
|
wchar_t wAgent[64], *wUrl = NULL, wHostName[64], wUrlPath[128];
|
|
HINTERNET hSession=NULL, hConnect=NULL, hRequest=NULL;
|
|
URL_COMPONENTSW UrlParts = {sizeof(URL_COMPONENTSW), NULL, 1, (INTERNET_SCHEME)0,
|
|
wHostName, ARRAYSIZE(wHostName), INTERNET_DEFAULT_PORT, NULL, 1, wUrlPath, ARRAYSIZE(wUrlPath), NULL, 1};
|
|
|
|
PF_DECL(WinHttpCrackUrl);
|
|
PF_DECL(WinHttpOpen);
|
|
PF_DECL(WinHttpConnect);
|
|
PF_DECL(WinHttpOpenRequest);
|
|
PF_DECL(WinHttpSendRequest);
|
|
PF_DECL(WinHttpReceiveResponse);
|
|
PF_DECL(WinHttpQueryHeaders);
|
|
PF_DECL(WinHttpQueryDataAvailable);
|
|
PF_DECL(WinHttpReadData);
|
|
PF_DECL(WinHttpCloseHandle);
|
|
|
|
PF_INIT_OR_OUT(WinHttpCrackUrl, winhttp);
|
|
PF_INIT_OR_OUT(WinHttpOpen, winhttp);
|
|
PF_INIT_OR_OUT(WinHttpConnect, winhttp);
|
|
PF_INIT_OR_OUT(WinHttpOpenRequest, winhttp);
|
|
PF_INIT_OR_OUT(WinHttpSendRequest, winhttp);
|
|
PF_INIT_OR_OUT(WinHttpReceiveResponse, winhttp);
|
|
PF_INIT_OR_OUT(WinHttpQueryHeaders, winhttp);
|
|
PF_INIT_OR_OUT(WinHttpQueryDataAvailable, winhttp);
|
|
PF_INIT_OR_OUT(WinHttpReadData, winhttp);
|
|
PF_INIT_OR_OUT(WinHttpCloseHandle, winhttp);
|
|
|
|
wUrl = utf8_to_wchar(url);
|
|
if (wUrl == NULL) goto out;
|
|
|
|
// We reuse the ISO progress dialog for download progress
|
|
SetWindowTextU(hISOProgressDlg, "Downloading file...");
|
|
SetWindowTextU(hISOFileName, url);
|
|
progress_style = GetWindowLong(hISOProgressBar, GWL_STYLE);
|
|
SetWindowLong(hISOProgressBar, GWL_STYLE, progress_style & (~PBS_MARQUEE));
|
|
SendMessage(hISOProgressBar, PBM_SETPOS, 0, 0);
|
|
ShowWindow(hISOProgressDlg, SW_SHOW);
|
|
UpdateWindow(hISOProgressDlg);
|
|
|
|
PrintStatus(0, FALSE, "Downloading %s: Connecting...\n", file);
|
|
uprintf("Downloading %s from %s\n", file, url);
|
|
|
|
if (!pfWinHttpCrackUrl(wUrl, 0, 0, &UrlParts)) {
|
|
uprintf("Unable to decode URL: %s\n", WinHTTPErrorString());
|
|
goto out;
|
|
}
|
|
|
|
_snwprintf(wAgent, ARRAYSIZE(wAgent), L"Rufus/%d.%d.%d.%d", rufus_version[0], rufus_version[1], rufus_version[2], rufus_version[3]);
|
|
// Use WinHttpOpen to obtain a session handle.
|
|
hSession = pfWinHttpOpen(wAgent, WINHTTP_ACCESS_TYPE_DEFAULT_PROXY, WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, 0);
|
|
if (!hSession) {
|
|
uprintf("Could not open HTTP session: %s\n", WinHTTPErrorString());
|
|
goto out;
|
|
}
|
|
|
|
// Specify an HTTP server.
|
|
hConnect = pfWinHttpConnect(hSession, UrlParts.lpszHostName, UrlParts.nPort, 0);
|
|
if (!hConnect) {
|
|
uprintf("Could not connect to HTTP server: %s\n", WinHTTPErrorString());
|
|
goto out;
|
|
}
|
|
|
|
// Create an HTTP request handle.
|
|
hRequest = pfWinHttpOpenRequest(hConnect, L"GET", UrlParts.lpszUrlPath,
|
|
NULL, WINHTTP_NO_REFERER, WINHTTP_DEFAULT_ACCEPT_TYPES,
|
|
(UrlParts.nScheme == INTERNET_SCHEME_HTTPS)?WINHTTP_FLAG_SECURE:0);
|
|
if (!hRequest) {
|
|
uprintf("Could not create server request: %s\n", WinHTTPErrorString());
|
|
goto out;
|
|
}
|
|
|
|
if (!pfWinHttpSendRequest(hRequest, WINHTTP_NO_ADDITIONAL_HEADERS, 0, WINHTTP_NO_REQUEST_DATA, 0, 0, 0 )) {
|
|
uprintf("Could not send server request: %s\n", WinHTTPErrorString());
|
|
goto out;
|
|
}
|
|
|
|
if (!pfWinHttpReceiveResponse(hRequest, NULL)) {
|
|
uprintf("Failure to receive server response: %s\n", WinHTTPErrorString());
|
|
goto out;
|
|
}
|
|
|
|
if (!pfWinHttpQueryHeaders(hRequest, WINHTTP_QUERY_CONTENT_LENGTH|WINHTTP_QUERY_FLAG_NUMBER,
|
|
WINHTTP_HEADER_NAME_BY_INDEX, &dwTotalSize, &dwTotalSizeSize, WINHTTP_NO_HEADER_INDEX)) {
|
|
uprintf("Could not retreive file length: %s\n", WinHTTPErrorString());
|
|
goto out;
|
|
}
|
|
uprintf("File length: %d bytes\n", dwTotalSize);
|
|
|
|
fd = fopen(file, "wb");
|
|
if (fd == NULL) {
|
|
uprintf("Unable to create file %s\n", file);
|
|
goto out;
|
|
}
|
|
|
|
// Keep checking for data until there is nothing left.
|
|
dwReadSize = 0;
|
|
while(1) {
|
|
if (IS_ERROR(FormatStatus))
|
|
goto out;
|
|
|
|
dwSize = 0;
|
|
if (!pfWinHttpQueryDataAvailable(hRequest, &dwSize))
|
|
uprintf("Error in WinHttpQueryDataAvailable: %s\n", WinHTTPErrorString());
|
|
if (dwSize <= 0)
|
|
break;
|
|
|
|
// Allocate space for the buffer.
|
|
buf = (unsigned char*)malloc(dwSize+1);
|
|
if (buf == NULL) {
|
|
uprintf("Could not allocate buffer for download.\n");
|
|
goto out;
|
|
}
|
|
if (!pfWinHttpReadData(hRequest, (LPVOID)buf, dwSize, &dwDownloaded)) {
|
|
uprintf("Error in WinHttpReadData: %s\n", WinHTTPErrorString());
|
|
goto out;
|
|
}
|
|
if (dwDownloaded != dwSize) {
|
|
uprintf("Error: expected %d bytes by received %d\n", dwSize, dwDownloaded);
|
|
goto out;
|
|
}
|
|
dwReadSize += dwDownloaded;
|
|
SendMessage(hISOProgressBar, PBM_SETPOS, (WPARAM)(MAX_PROGRESS*((1.0f*dwReadSize)/(1.0f*dwTotalSize))), 0);
|
|
PrintStatus(0, FALSE, "Downloading %s: %0.1f%%\n", file, (100.0f*dwReadSize)/(1.0f*dwTotalSize));
|
|
if (fwrite(buf, 1, dwSize, fd) != dwSize) {
|
|
uprintf("Error writing file %s\n", file);
|
|
goto out;
|
|
}
|
|
safe_free(buf);
|
|
}
|
|
r = (dwReadSize == dwTotalSize);
|
|
if (r)
|
|
uprintf("Successfully downloaded %s\n", file);
|
|
|
|
out:
|
|
ShowWindow(hISOProgressDlg, SW_HIDE);
|
|
safe_free(wUrl);
|
|
safe_free(buf);
|
|
if (fd != NULL) fclose(fd);
|
|
if (!r) {
|
|
_unlink(file);
|
|
PrintStatus(0, FALSE, "Failed to download file.");
|
|
MessageBoxA(hMainDialog, IS_ERROR(FormatStatus)?StrError(FormatStatus):WinHTTPErrorString(),
|
|
"File download", MB_OK|MB_ICONERROR);
|
|
}
|
|
if (hRequest) pfWinHttpCloseHandle(hRequest);
|
|
if (hConnect) pfWinHttpCloseHandle(hConnect);
|
|
if (hSession) pfWinHttpCloseHandle(hSession);
|
|
|
|
return r;
|
|
}
|