"Fossies" - the Fresh Open Source Software Archive

Member "rufus-3.13/src/net.c" (20 Nov 2020, 45629 Bytes) of package /linux/misc/rufus-3.13.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "net.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 3.12_vs_3.13.

    1 /*
    2  * Rufus: The Reliable USB Formatting Utility
    3  * Networking functionality (web file download, check for update, etc.)
    4  * Copyright © 2012-2019 Pete Batard <pete@akeo.ie>
    5  *
    6  * This program is free software: you can redistribute it and/or modify
    7  * it under the terms of the GNU General Public License as published by
    8  * the Free Software Foundation, either version 3 of the License, or
    9  * (at your option) any later version.
   10  *
   11  * This program is distributed in the hope that it will be useful,
   12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
   13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   14  * GNU General Public License for more details.
   15  *
   16  * You should have received a copy of the GNU General Public License
   17  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
   18  */
   19 
   20 /* Memory leaks detection - define _CRTDBG_MAP_ALLOC as preprocessor macro */
   21 #ifdef _CRTDBG_MAP_ALLOC
   22 #include <stdlib.h>
   23 #include <crtdbg.h>
   24 #endif
   25 
   26 #include <windows.h>
   27 #include <wininet.h>
   28 #include <stdio.h>
   29 #include <malloc.h>
   30 #include <string.h>
   31 #include <inttypes.h>
   32 #include <assert.h>
   33 
   34 #include "rufus.h"
   35 #include "missing.h"
   36 #include "resource.h"
   37 #include "msapi_utf8.h"
   38 #include "localization.h"
   39 #include "bled/bled.h"
   40 
   41 #include "settings.h"
   42 
   43 /* Maximum download chunk size, in bytes */
   44 #define DOWNLOAD_BUFFER_SIZE    (10*KB)
   45 /* Default delay between update checks (1 day) */
   46 #define DEFAULT_UPDATE_INTERVAL (24*3600)
   47 
   48 DWORD DownloadStatus;
   49 BYTE* fido_script = NULL;
   50 HANDLE update_check_thread = NULL;
   51 
   52 extern loc_cmd* selected_locale;
   53 extern HANDLE dialog_handle;
   54 extern BOOL is_x86_32, close_fido_cookie_prompts;
   55 static DWORD error_code, fido_len = 0;
   56 static BOOL force_update_check = FALSE;
   57 static const char* request_headers = "Accept-Encoding: gzip, deflate";
   58 
   59 /*
   60  * FormatMessage does not handle internet errors
   61  * https://docs.microsoft.com/en-us/windows/desktop/wininet/wininet-errors
   62  */
   63 const char* WinInetErrorString(void)
   64 {
   65     static char error_string[256];
   66     DWORD size = sizeof(error_string);
   67     PF_TYPE_DECL(WINAPI, BOOL, InternetGetLastResponseInfoA, (LPDWORD, LPSTR, LPDWORD));
   68     PF_INIT(InternetGetLastResponseInfoA, WinInet);
   69 
   70     error_code = HRESULT_CODE(GetLastError());
   71 
   72     if ((error_code < INTERNET_ERROR_BASE) || (error_code > INTERNET_ERROR_LAST))
   73         return WindowsErrorString();
   74 
   75     switch(error_code) {
   76     case ERROR_INTERNET_OUT_OF_HANDLES:
   77         return "No more handles could be generated at this time.";
   78     case ERROR_INTERNET_TIMEOUT:
   79         return "The request has timed out.";
   80     case ERROR_INTERNET_INTERNAL_ERROR:
   81         return "An internal error has occurred.";
   82     case ERROR_INTERNET_INVALID_URL:
   83         return "The URL is invalid.";
   84     case ERROR_INTERNET_UNRECOGNIZED_SCHEME:
   85         return "The URL scheme could not be recognized or is not supported.";
   86     case ERROR_INTERNET_NAME_NOT_RESOLVED:
   87         return "The server name could not be resolved.";
   88     case ERROR_INTERNET_PROTOCOL_NOT_FOUND:
   89         return "The requested protocol could not be located.";
   90     case ERROR_INTERNET_INVALID_OPTION:
   91         return "A request specified an invalid option value.";
   92     case ERROR_INTERNET_BAD_OPTION_LENGTH:
   93         return "The length of an option supplied is incorrect for the type of option specified.";
   94     case ERROR_INTERNET_OPTION_NOT_SETTABLE:
   95         return "The request option cannot be set, only queried.";
   96     case ERROR_INTERNET_SHUTDOWN:
   97         return "The Win32 Internet function support is being shut down or unloaded.";
   98     case ERROR_INTERNET_INCORRECT_USER_NAME:
   99         return "The request to connect and log on to an FTP server could not be completed because the supplied user name is incorrect.";
  100     case ERROR_INTERNET_INCORRECT_PASSWORD:
  101         return "The request to connect and log on to an FTP server could not be completed because the supplied password is incorrect.";
  102     case ERROR_INTERNET_LOGIN_FAILURE:
  103         return "The request to connect to and log on to an FTP server failed.";
  104     case ERROR_INTERNET_INVALID_OPERATION:
  105         return "The requested operation is invalid.";
  106     case ERROR_INTERNET_OPERATION_CANCELLED:
  107         return "The operation was cancelled, usually because the handle on which the request was operating was closed before the operation completed.";
  108     case ERROR_INTERNET_INCORRECT_HANDLE_TYPE:
  109         return "The type of handle supplied is incorrect for this operation.";
  110     case ERROR_INTERNET_INCORRECT_HANDLE_STATE:
  111         return "The requested operation cannot be carried out because the handle supplied is not in the correct state.";
  112     case ERROR_INTERNET_NOT_PROXY_REQUEST:
  113         return "The request cannot be made via a proxy.";
  114     case ERROR_INTERNET_REGISTRY_VALUE_NOT_FOUND:
  115         return "A required registry value could not be located.";
  116     case ERROR_INTERNET_BAD_REGISTRY_PARAMETER:
  117         return "A required registry value was located but is an incorrect type or has an invalid value.";
  118     case ERROR_INTERNET_NO_DIRECT_ACCESS:
  119         return "Direct network access cannot be made at this time.";
  120     case ERROR_INTERNET_NO_CONTEXT:
  121         return "An asynchronous request could not be made because a zero context value was supplied.";
  122     case ERROR_INTERNET_NO_CALLBACK:
  123         return "An asynchronous request could not be made because a callback function has not been set.";
  124     case ERROR_INTERNET_REQUEST_PENDING:
  125         return "The required operation could not be completed because one or more requests are pending.";
  126     case ERROR_INTERNET_INCORRECT_FORMAT:
  127         return "The format of the request is invalid.";
  128     case ERROR_INTERNET_ITEM_NOT_FOUND:
  129         return "The requested item could not be located.";
  130     case ERROR_INTERNET_CANNOT_CONNECT:
  131         return "The attempt to connect to the server failed.";
  132     case ERROR_INTERNET_CONNECTION_ABORTED:
  133         return "The connection with the server has been terminated.";
  134     case ERROR_INTERNET_CONNECTION_RESET:
  135         return "The connection with the server has been reset.";
  136     case ERROR_INTERNET_FORCE_RETRY:
  137         return "Calls for the Win32 Internet function to redo the request.";
  138     case ERROR_INTERNET_INVALID_PROXY_REQUEST:
  139         return "The request to the proxy was invalid.";
  140     case ERROR_INTERNET_HANDLE_EXISTS:
  141         return "The request failed because the handle already exists.";
  142     case ERROR_INTERNET_SEC_INVALID_CERT:
  143         return "The SSL certificate is invalid.";
  144     case ERROR_INTERNET_SEC_CERT_DATE_INVALID:
  145         return "SSL certificate date that was received from the server is bad. The certificate is expired.";
  146     case ERROR_INTERNET_SEC_CERT_CN_INVALID:
  147         return "SSL certificate common name (host name field) is incorrect.";
  148     case ERROR_INTERNET_SEC_CERT_ERRORS:
  149         return "The SSL certificate contains errors.";
  150     case ERROR_INTERNET_SEC_CERT_NO_REV:
  151         return "The SSL certificate was not revoked.";
  152     case ERROR_INTERNET_SEC_CERT_REV_FAILED:
  153         return "The revocation check of the SSL certificate failed.";
  154     case ERROR_INTERNET_HTTP_TO_HTTPS_ON_REDIR:
  155         return "The application is moving from a non-SSL to an SSL connection because of a redirect.";
  156     case ERROR_INTERNET_HTTPS_TO_HTTP_ON_REDIR:
  157         return "The application is moving from an SSL to an non-SSL connection because of a redirect.";
  158     case ERROR_INTERNET_MIXED_SECURITY:
  159         return "Some of the content being viewed may have come from unsecured servers.";
  160     case ERROR_INTERNET_CHG_POST_IS_NON_SECURE:
  161         return "The application is posting and attempting to change multiple lines of text on a server that is not secure.";
  162     case ERROR_INTERNET_POST_IS_NON_SECURE:
  163         return "The application is posting data to a server that is not secure.";
  164     case ERROR_FTP_TRANSFER_IN_PROGRESS:
  165         return "The requested operation cannot be made on the FTP session handle because an operation is already in progress.";
  166     case ERROR_FTP_DROPPED:
  167         return "The FTP operation was not completed because the session was aborted.";
  168     case ERROR_GOPHER_PROTOCOL_ERROR:
  169     case ERROR_GOPHER_NOT_FILE:
  170     case ERROR_GOPHER_DATA_ERROR:
  171     case ERROR_GOPHER_END_OF_DATA:
  172     case ERROR_GOPHER_INVALID_LOCATOR:
  173     case ERROR_GOPHER_INCORRECT_LOCATOR_TYPE:
  174     case ERROR_GOPHER_NOT_GOPHER_PLUS:
  175     case ERROR_GOPHER_ATTRIBUTE_NOT_FOUND:
  176     case ERROR_GOPHER_UNKNOWN_LOCATOR:
  177         return "Gopher? Really??? What is this, 1994?";
  178     case ERROR_HTTP_HEADER_NOT_FOUND:
  179         return "The requested header could not be located.";
  180     case ERROR_HTTP_DOWNLEVEL_SERVER:
  181         return "The server did not return any headers.";
  182     case ERROR_HTTP_INVALID_SERVER_RESPONSE:
  183         return "The server response could not be parsed.";
  184     case ERROR_HTTP_INVALID_HEADER:
  185         return "The supplied header is invalid.";
  186     case ERROR_HTTP_INVALID_QUERY_REQUEST:
  187         return "The request made to HttpQueryInfo is invalid.";
  188     case ERROR_HTTP_HEADER_ALREADY_EXISTS:
  189         return "The header could not be added because it already exists.";
  190     case ERROR_HTTP_REDIRECT_FAILED:
  191         return "The redirection failed because either the scheme changed or all attempts made to redirect failed.";
  192     case ERROR_INTERNET_SECURITY_CHANNEL_ERROR:
  193         return "This system's SSL library is too old to be able to access this website.";
  194     case ERROR_INTERNET_CLIENT_AUTH_CERT_NEEDED:
  195         return "Client Authentication certificate needed";
  196     case ERROR_INTERNET_BAD_AUTO_PROXY_SCRIPT:
  197         return "Bad auto proxy script.";
  198     case ERROR_INTERNET_UNABLE_TO_DOWNLOAD_SCRIPT:
  199         return "Unable to download script.";
  200     case ERROR_INTERNET_NOT_INITIALIZED:
  201         return "Internet has not be initialized.";
  202     case ERROR_INTERNET_UNABLE_TO_CACHE_FILE:
  203         return "Unable to cache the file.";
  204     case ERROR_INTERNET_TCPIP_NOT_INSTALLED:
  205         return "TPC/IP not installed.";
  206     case ERROR_INTERNET_DISCONNECTED:
  207         return "Internet is disconnected.";
  208     case ERROR_INTERNET_SERVER_UNREACHABLE:
  209         return "Server could not be reached.";
  210     case ERROR_INTERNET_PROXY_SERVER_UNREACHABLE:
  211         return "Proxy server could not be reached.";
  212     case ERROR_INTERNET_FAILED_DUETOSECURITYCHECK:
  213         return "A security check prevented internet connection.";
  214     case ERROR_INTERNET_NEED_MSN_SSPI_PKG:
  215         return "This connection requires an MSN Security Support Provider Interface package.";
  216     case ERROR_INTERNET_LOGIN_FAILURE_DISPLAY_ENTITY_BODY:
  217         return "Please ask Microsoft about that one!";
  218     case ERROR_INTERNET_EXTENDED_ERROR:
  219         if (pfInternetGetLastResponseInfoA != NULL) {
  220             pfInternetGetLastResponseInfoA(&error_code, error_string, &size);
  221             return error_string;
  222         }
  223         // fall through
  224     default:
  225         static_sprintf(error_string, "Unknown internet error 0x%08lX", error_code);
  226         return error_string;
  227     }
  228 }
  229 
  230 static char* GetShortName(const char* url)
  231 {
  232     static char short_name[128];
  233     char *p;
  234     size_t i, len = safe_strlen(url);
  235     if (len < 5)
  236         return NULL;
  237 
  238     for (i = len - 2; i > 0; i--) {
  239         if (url[i] == '/') {
  240             i++;
  241             break;
  242         }
  243     }
  244     memset(short_name, 0, sizeof(short_name));
  245     static_strcpy(short_name, &url[i]);
  246     // If the URL is followed by a query, remove that part
  247     // Make sure we detect escaped queries too
  248     p = strstr(short_name, "%3F");
  249     if (p != NULL)
  250         *p = 0;
  251     p = strstr(short_name, "%3f");
  252     if (p != NULL)
  253         *p = 0;
  254     for (i = 0; i < strlen(short_name); i++) {
  255         if ((short_name[i] == '?') || (short_name[i] == '#')) {
  256             short_name[i] = 0;
  257             break;
  258         }
  259     }
  260     return short_name;
  261 }
  262 
  263 // Open an Internet session
  264 static HINTERNET GetInternetSession(BOOL bRetry)
  265 {
  266     int i;
  267     char agent[64];
  268     BOOL r, decodingSupport = TRUE;
  269     DWORD dwFlags, dwTimeout = NET_SESSION_TIMEOUT, dwProtocolSupport = HTTP_PROTOCOL_FLAG_HTTP2;
  270     HINTERNET hSession = NULL;
  271 
  272     PF_TYPE_DECL(WINAPI, BOOL, InternetGetConnectedState, (LPDWORD, DWORD));
  273     PF_TYPE_DECL(WINAPI, HINTERNET, InternetOpenA, (LPCSTR, DWORD, LPCSTR, LPCSTR, DWORD));
  274     PF_TYPE_DECL(WINAPI, BOOL, InternetSetOptionA, (HINTERNET, DWORD, LPVOID, DWORD));
  275     PF_INIT_OR_OUT(InternetGetConnectedState, WinInet);
  276     PF_INIT_OR_OUT(InternetOpenA, WinInet);
  277     PF_INIT_OR_OUT(InternetSetOptionA, WinInet);
  278 
  279     for (i = 0; i <= WRITE_RETRIES; i++) {
  280         r = pfInternetGetConnectedState(&dwFlags, 0);
  281         if (r || !bRetry)
  282             break;
  283         Sleep(1000);
  284     }
  285     if (!r) {
  286         // http://msdn.microsoft.com/en-us/library/windows/desktop/aa384702.aspx is wrong...
  287         SetLastError(ERROR_INTERNET_NOT_INITIALIZED);
  288         uprintf("Network is unavailable: %s", WinInetErrorString());
  289         goto out;
  290     }
  291     static_sprintf(agent, APPLICATION_NAME "/%d.%d.%d (Windows NT %d.%d%s)",
  292         rufus_version[0], rufus_version[1], rufus_version[2],
  293         nWindowsVersion >> 4, nWindowsVersion & 0x0F, is_x64() ? "; WOW64" : "");
  294     hSession = pfInternetOpenA(agent, INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0);
  295     // Set the timeouts
  296     pfInternetSetOptionA(hSession, INTERNET_OPTION_CONNECT_TIMEOUT, (LPVOID)&dwTimeout, sizeof(dwTimeout));
  297     pfInternetSetOptionA(hSession, INTERNET_OPTION_SEND_TIMEOUT, (LPVOID)&dwTimeout, sizeof(dwTimeout));
  298     pfInternetSetOptionA(hSession, INTERNET_OPTION_RECEIVE_TIMEOUT, (LPVOID)&dwTimeout, sizeof(dwTimeout));
  299     // Enable gzip and deflate decoding schemes
  300     pfInternetSetOptionA(hSession, INTERNET_OPTION_HTTP_DECODING, (LPVOID)&decodingSupport, sizeof(decodingSupport));
  301     // Enable HTTP/2 protocol support
  302     pfInternetSetOptionA(hSession, INTERNET_OPTION_ENABLE_HTTP_PROTOCOL, (LPVOID)&dwProtocolSupport, sizeof(dwProtocolSupport));
  303 
  304 out:
  305     return hSession;
  306 }
  307 
  308 /*
  309  * Download a file or fill a buffer from an URL
  310  * Mostly taken from http://support.microsoft.com/kb/234913
  311  * If file is NULL, a buffer is allocated for the download (that needs to be freed by the caller)
  312  * If hProgressDialog is not NULL, this function will send INIT and EXIT messages
  313  * to the dialog in question, with WPARAM being set to nonzero for EXIT on success
  314  * and also attempt to indicate progress using an IDC_PROGRESS control
  315  * Note that when a buffer is used, the actual size of the buffer is one more than its reported
  316  * size (with the extra byte set to 0) to accomodate for calls that need a NUL-terminated buffer.
  317  */
  318 uint64_t DownloadToFileOrBuffer(const char* url, const char* file, BYTE** buffer, HWND hProgressDialog, BOOL bTaskBarProgress)
  319 {
  320     const char* accept_types[] = {"*/*\0", NULL};
  321     const char* short_name;
  322     unsigned char buf[DOWNLOAD_BUFFER_SIZE];
  323     char hostname[64], urlpath[128], strsize[32];
  324     BOOL r = FALSE;
  325     DWORD dwSize, dwWritten, dwDownloaded;
  326     HANDLE hFile = INVALID_HANDLE_VALUE;
  327     HINTERNET hSession = NULL, hConnection = NULL, hRequest = NULL;
  328     URL_COMPONENTSA UrlParts = {sizeof(URL_COMPONENTSA), NULL, 1, (INTERNET_SCHEME)0,
  329         hostname, sizeof(hostname), 0, NULL, 1, urlpath, sizeof(urlpath), NULL, 1};
  330     uint64_t size = 0, total_size = 0;
  331 
  332     // Can't link with wininet.lib because of sideloading issues
  333     PF_TYPE_DECL(WINAPI, BOOL, InternetCrackUrlA, (LPCSTR, DWORD, DWORD, LPURL_COMPONENTSA));
  334     PF_TYPE_DECL(WINAPI, HINTERNET, InternetConnectA, (HINTERNET, LPCSTR, INTERNET_PORT, LPCSTR, LPCSTR, DWORD, DWORD, DWORD_PTR));
  335     PF_TYPE_DECL(WINAPI, BOOL, InternetReadFile, (HINTERNET, LPVOID, DWORD, LPDWORD));
  336     PF_TYPE_DECL(WINAPI, BOOL, InternetCloseHandle, (HINTERNET));
  337     PF_TYPE_DECL(WINAPI, HINTERNET, HttpOpenRequestA, (HINTERNET, LPCSTR, LPCSTR, LPCSTR, LPCSTR, LPCSTR*, DWORD, DWORD_PTR));
  338     PF_TYPE_DECL(WINAPI, BOOL, HttpSendRequestA, (HINTERNET, LPCSTR, DWORD, LPVOID, DWORD));
  339     PF_TYPE_DECL(WINAPI, BOOL, HttpQueryInfoA, (HINTERNET, DWORD, LPVOID, LPDWORD, LPDWORD));
  340     PF_INIT_OR_OUT(InternetCrackUrlA, WinInet);
  341     PF_INIT_OR_OUT(InternetConnectA, WinInet);
  342     PF_INIT_OR_OUT(InternetReadFile, WinInet);
  343     PF_INIT_OR_OUT(InternetCloseHandle, WinInet);
  344     PF_INIT_OR_OUT(HttpOpenRequestA, WinInet);
  345     PF_INIT_OR_OUT(HttpSendRequestA, WinInet);
  346     PF_INIT_OR_OUT(HttpQueryInfoA, WinInet);
  347 
  348     FormatStatus = 0;
  349     DownloadStatus = 404;
  350     if (hProgressDialog != NULL)
  351         UpdateProgressWithInfoInit(hProgressDialog, FALSE);
  352 
  353     assert(url != NULL);
  354     if (buffer != NULL)
  355         *buffer = NULL;
  356 
  357     short_name = (file != NULL) ? PathFindFileNameU(file) : PathFindFileNameU(url);
  358 
  359     if (hProgressDialog != NULL) {
  360         PrintInfo(0, MSG_085, short_name);
  361         uprintf("Downloading %s", url);
  362     }
  363 
  364     if ( (!pfInternetCrackUrlA(url, (DWORD)safe_strlen(url), 0, &UrlParts))
  365       || (UrlParts.lpszHostName == NULL) || (UrlParts.lpszUrlPath == NULL)) {
  366         uprintf("Unable to decode URL: %s", WinInetErrorString());
  367         goto out;
  368     }
  369     hostname[sizeof(hostname)-1] = 0;
  370 
  371     hSession = GetInternetSession(TRUE);
  372     if (hSession == NULL) {
  373         uprintf("Could not open Internet session: %s", WinInetErrorString());
  374         goto out;
  375     }
  376 
  377     hConnection = pfInternetConnectA(hSession, UrlParts.lpszHostName, UrlParts.nPort, NULL, NULL, INTERNET_SERVICE_HTTP, 0, (DWORD_PTR)NULL);
  378     if (hConnection == NULL) {
  379         uprintf("Could not connect to server %s:%d: %s", UrlParts.lpszHostName, UrlParts.nPort, WinInetErrorString());
  380         goto out;
  381     }
  382 
  383     hRequest = pfHttpOpenRequestA(hConnection, "GET", UrlParts.lpszUrlPath, NULL, NULL, accept_types,
  384         INTERNET_FLAG_IGNORE_REDIRECT_TO_HTTP|INTERNET_FLAG_IGNORE_REDIRECT_TO_HTTPS|
  385         INTERNET_FLAG_NO_COOKIES|INTERNET_FLAG_NO_UI|INTERNET_FLAG_NO_CACHE_WRITE|INTERNET_FLAG_HYPERLINK|
  386         ((UrlParts.nScheme==INTERNET_SCHEME_HTTPS)?INTERNET_FLAG_SECURE:0), (DWORD_PTR)NULL);
  387     if (hRequest == NULL) {
  388         uprintf("Could not open URL %s: %s", url, WinInetErrorString());
  389         goto out;
  390     }
  391 
  392     if (!pfHttpSendRequestA(hRequest, request_headers, -1L, NULL, 0)) {
  393         uprintf("Unable to send request: %s", WinInetErrorString());
  394         goto out;
  395     }
  396 
  397     // Get the file size
  398     dwSize = sizeof(DownloadStatus);
  399     pfHttpQueryInfoA(hRequest, HTTP_QUERY_STATUS_CODE|HTTP_QUERY_FLAG_NUMBER, (LPVOID)&DownloadStatus, &dwSize, NULL);
  400     if (DownloadStatus != 200) {
  401         error_code = ERROR_INTERNET_ITEM_NOT_FOUND;
  402         SetLastError(ERROR_SEVERITY_ERROR | FAC(FACILITY_HTTP) | error_code);
  403         uprintf("Unable to access file: %d", DownloadStatus);
  404         goto out;
  405     }
  406     dwSize = sizeof(strsize);
  407     if (!pfHttpQueryInfoA(hRequest, HTTP_QUERY_CONTENT_LENGTH, (LPVOID)strsize, &dwSize, NULL)) {
  408         uprintf("Unable to retrieve file length: %s", WinInetErrorString());
  409         goto out;
  410     }
  411     total_size = (uint64_t)atoll(strsize);
  412     if (hProgressDialog != NULL) {
  413         char msg[128];
  414         uprintf("File length: %s", SizeToHumanReadable(total_size, FALSE, FALSE));
  415         if (right_to_left_mode)
  416             static_sprintf(msg, "(%s) %s", SizeToHumanReadable(total_size, FALSE, FALSE), GetShortName(url));
  417         else
  418             static_sprintf(msg, "%s (%s)", GetShortName(url), SizeToHumanReadable(total_size, FALSE, FALSE));
  419         PrintStatus(0, MSG_085, msg);
  420     }
  421 
  422     if (file != NULL) {
  423         hFile = CreateFileU(file, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
  424         if (hFile == INVALID_HANDLE_VALUE) {
  425             uprintf("Unable to create file '%s': %s", short_name, WinInetErrorString());
  426             goto out;
  427         }
  428     } else {
  429         if (buffer == NULL) {
  430             uprintf("No buffer pointer provided for download");
  431             goto out;
  432         }
  433         // Allocate one extra byte, so that caller can rely on NUL-terminated text if needed
  434         *buffer = calloc((size_t)total_size + 1, 1);
  435         if (*buffer == NULL) {
  436             uprintf("Could not allocate buffer for download");
  437             goto out;
  438         }
  439     }
  440 
  441     // Keep checking for data until there is nothing left.
  442     while (1) {
  443         // User may have cancelled the download
  444         if (IS_ERROR(FormatStatus))
  445             goto out;
  446         if (!pfInternetReadFile(hRequest, buf, sizeof(buf), &dwDownloaded) || (dwDownloaded == 0))
  447             break;
  448         if (hProgressDialog != NULL)
  449             UpdateProgressWithInfo(OP_NOOP, MSG_241, size, total_size);
  450         if (file != NULL) {
  451             if (!WriteFile(hFile, buf, dwDownloaded, &dwWritten, NULL)) {
  452                 uprintf("Error writing file '%s': %s", short_name, WinInetErrorString());
  453                 goto out;
  454             } else if (dwDownloaded != dwWritten) {
  455                 uprintf("Error writing file '%s': Only %d/%d bytes written", short_name, dwWritten, dwDownloaded);
  456                 goto out;
  457             }
  458         } else {
  459             memcpy(&(*buffer)[size], buf, dwDownloaded);
  460         }
  461         size += dwDownloaded;
  462     }
  463 
  464     if (size != total_size) {
  465         uprintf("Could not download complete file - read: %lld bytes, expected: %lld bytes", size, total_size);
  466         FormatStatus = ERROR_SEVERITY_ERROR|FAC(FACILITY_STORAGE)|ERROR_WRITE_FAULT;
  467         goto out;
  468     } else {
  469         DownloadStatus = 200;
  470         r = TRUE;
  471         if (hProgressDialog != NULL) {
  472             UpdateProgressWithInfo(OP_NOOP, MSG_241, total_size, total_size);
  473             uprintf("Successfully downloaded '%s'", short_name);
  474         }
  475     }
  476 
  477 out:
  478     error_code = GetLastError();
  479     if (hFile != INVALID_HANDLE_VALUE) {
  480         // Force a flush - May help with the PKI API trying to process downloaded updates too early...
  481         FlushFileBuffers(hFile);
  482         CloseHandle(hFile);
  483     }
  484     if (!r) {
  485         if (file != NULL)
  486             DeleteFileU(file);
  487         if (buffer != NULL)
  488             safe_free(*buffer);
  489     }
  490     if (hRequest)
  491         pfInternetCloseHandle(hRequest);
  492     if (hConnection)
  493         pfInternetCloseHandle(hConnection);
  494     if (hSession)
  495         pfInternetCloseHandle(hSession);
  496 
  497     SetLastError(error_code);
  498     return r ? size : 0;
  499 }
  500 
  501 // Download and validate a signed file. The file must have a corresponding '.sig' on the server.
  502 DWORD DownloadSignedFile(const char* url, const char* file, HWND hProgressDialog, BOOL bPromptOnError)
  503 {
  504     char* url_sig = NULL;
  505     BYTE *buf = NULL, *sig = NULL;
  506     DWORD buf_len = 0, sig_len = 0;
  507     DWORD ret = 0;
  508     HANDLE hFile = INVALID_HANDLE_VALUE;
  509 
  510     assert(url != NULL);
  511 
  512     url_sig = malloc(strlen(url) + 5);
  513     if (url_sig == NULL) {
  514         uprintf("Could not allocate signature URL");
  515         goto out;
  516     }
  517     strcpy(url_sig, url);
  518     strcat(url_sig, ".sig");
  519 
  520     buf_len = (DWORD)DownloadToFileOrBuffer(url, NULL, &buf, hProgressDialog, FALSE);
  521     if (buf_len == 0)
  522         goto out;
  523     sig_len = (DWORD)DownloadToFileOrBuffer(url_sig, NULL, &sig, NULL, FALSE);
  524     if ((sig_len != RSA_SIGNATURE_SIZE) || (!ValidateOpensslSignature(buf, buf_len, sig, sig_len))) {
  525         uprintf("FATAL: Download signature is invalid ✗");
  526         DownloadStatus = 403;   // Forbidden
  527         FormatStatus = ERROR_SEVERITY_ERROR | FAC(FACILITY_STORAGE) | APPERR(ERROR_BAD_SIGNATURE);
  528         SendMessage(GetDlgItem(hProgressDialog, IDC_PROGRESS), PBM_SETSTATE, (WPARAM)PBST_ERROR, 0);
  529         SetTaskbarProgressState(TASKBAR_ERROR);
  530         goto out;
  531     }
  532 
  533     uprintf("Download signature is valid ✓");
  534     DownloadStatus = 206;   // Partial content
  535     hFile = CreateFileU(file, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
  536     if (hFile == INVALID_HANDLE_VALUE) {
  537         uprintf("Unable to create file '%s': %s", PathFindFileNameU(file), WinInetErrorString());
  538         goto out;
  539     }
  540     if (!WriteFile(hFile, buf, buf_len, &ret, NULL)) {
  541         uprintf("Error writing file '%s': %s", PathFindFileNameU(file), WinInetErrorString());
  542         ret = 0;
  543         goto out;
  544     } else if (ret != buf_len) {
  545         uprintf("Error writing file '%s': Only %d/%d bytes written", PathFindFileNameU(file), ret, buf_len);
  546         ret = 0;
  547         goto out;
  548     }
  549     DownloadStatus = 200;   // Full content
  550 
  551 out:
  552     if (hProgressDialog != NULL)
  553         SendMessage(hProgressDialog, UM_PROGRESS_EXIT, (WPARAM)ret, 0);
  554     if ((bPromptOnError) && (DownloadStatus != 200)) {
  555         PrintInfo(0, MSG_242);
  556         SetLastError(error_code);
  557         MessageBoxExU(hMainDialog, IS_ERROR(FormatStatus) ? StrError(FormatStatus, FALSE) : WinInetErrorString(),
  558             lmprintf(MSG_044), MB_OK | MB_ICONERROR | MB_IS_RTL, selected_langid);
  559     }
  560     safe_closehandle(hFile);
  561     free(url_sig);
  562     free(buf);
  563     free(sig);
  564     return ret;
  565 }
  566 
  567 /* Threaded download */
  568 typedef struct {
  569     const char* url;
  570     const char* file;
  571     HWND hProgressDialog;
  572     BOOL bPromptOnError;
  573 } DownloadSignedFileThreadArgs;
  574 
  575 static DWORD WINAPI DownloadSignedFileThread(LPVOID param)
  576 {
  577     DownloadSignedFileThreadArgs* args = (DownloadSignedFileThreadArgs*)param;
  578     ExitThread(DownloadSignedFile(args->url, args->file, args->hProgressDialog, args->bPromptOnError));
  579 }
  580 
  581 HANDLE DownloadSignedFileThreaded(const char* url, const char* file, HWND hProgressDialog, BOOL bPromptOnError)
  582 {
  583     static DownloadSignedFileThreadArgs args;
  584     args.url = url;
  585     args.file = file;
  586     args.hProgressDialog = hProgressDialog;
  587     args.bPromptOnError = bPromptOnError;
  588     return CreateThread(NULL, 0, DownloadSignedFileThread, &args, 0, NULL);
  589 }
  590 
  591 static __inline uint64_t to_uint64_t(uint16_t x[4]) {
  592     int i;
  593     uint64_t ret = 0;
  594     for (i=0; i<3; i++)
  595         ret = (ret<<16) + x[i];
  596     return ret;
  597 }
  598 
  599 /*
  600  * Background thread to check for updates
  601  */
  602 static DWORD WINAPI CheckForUpdatesThread(LPVOID param)
  603 {
  604     BOOL releases_only = TRUE, found_new_version = FALSE;
  605     int status = 0;
  606     const char* server_url = RUFUS_URL "/";
  607     int i, j, k, max_channel, verbose = 0, verpos[4];
  608     static const char* archname[] = {"win_x86", "win_x64", "win_arm", "win_arm64", "win_none"};
  609     static const char* channel[] = {"release", "beta", "test"};     // release channel
  610     const char* accept_types[] = {"*/*\0", NULL};
  611     char* buf = NULL;
  612     char agent[64], hostname[64], urlpath[128], sigpath[256];
  613     DWORD dwSize, dwDownloaded, dwTotalSize, dwStatus;
  614     BYTE *sig = NULL;
  615     OSVERSIONINFOA os_version = {sizeof(OSVERSIONINFOA), 0, 0, 0, 0, ""};
  616     HINTERNET hSession = NULL, hConnection = NULL, hRequest = NULL;
  617     URL_COMPONENTSA UrlParts = {sizeof(URL_COMPONENTSA), NULL, 1, (INTERNET_SCHEME)0,
  618         hostname, sizeof(hostname), 0, NULL, 1, urlpath, sizeof(urlpath), NULL, 1};
  619     SYSTEMTIME ServerTime, LocalTime;
  620     FILETIME FileTime;
  621     int64_t local_time = 0, reg_time, server_time, update_interval;
  622 
  623     // Can't link with wininet.lib because of sideloading issues
  624     PF_TYPE_DECL(WINAPI, BOOL, InternetCrackUrlA, (LPCSTR, DWORD, DWORD, LPURL_COMPONENTSA));
  625     PF_TYPE_DECL(WINAPI, HINTERNET, InternetConnectA, (HINTERNET, LPCSTR, INTERNET_PORT, LPCSTR, LPCSTR, DWORD, DWORD, DWORD_PTR));
  626     PF_TYPE_DECL(WINAPI, BOOL, InternetReadFile, (HINTERNET, LPVOID, DWORD, LPDWORD));
  627     PF_TYPE_DECL(WINAPI, BOOL, InternetCloseHandle, (HINTERNET));
  628     PF_TYPE_DECL(WINAPI, HINTERNET, HttpOpenRequestA, (HINTERNET, LPCSTR, LPCSTR, LPCSTR, LPCSTR, LPCSTR*, DWORD, DWORD_PTR));
  629     PF_TYPE_DECL(WINAPI, BOOL, HttpSendRequestA, (HINTERNET, LPCSTR, DWORD, LPVOID, DWORD));
  630     PF_TYPE_DECL(WINAPI, BOOL, HttpQueryInfoA, (HINTERNET, DWORD, LPVOID, LPDWORD, LPDWORD));
  631     PF_INIT_OR_OUT(InternetCrackUrlA, WinInet);
  632     PF_INIT_OR_OUT(InternetConnectA, WinInet);
  633     PF_INIT_OR_OUT(InternetReadFile, WinInet);
  634     PF_INIT_OR_OUT(InternetCloseHandle, WinInet);
  635     PF_INIT_OR_OUT(HttpOpenRequestA, WinInet);
  636     PF_INIT_OR_OUT(HttpSendRequestA, WinInet);
  637     PF_INIT_OR_OUT(HttpQueryInfoA, WinInet);
  638 
  639     verbose = ReadSetting32(SETTING_VERBOSE_UPDATES);
  640     // Without this the FileDialog will produce error 0x8001010E when compiled for Vista or later
  641     IGNORE_RETVAL(CoInitializeEx(NULL, COINIT_APARTMENTTHREADED));
  642     // Unless the update was forced, wait a while before performing the update check
  643     if (!force_update_check) {
  644         // It would of course be a lot nicer to use a timer and wake the thread, but my
  645         // development time is limited and this is FASTER to implement.
  646         do {
  647             for (i=0; (i<30) && (!force_update_check); i++)
  648                 Sleep(500);
  649         } while ((!force_update_check) && ((op_in_progress || (dialog_showing > 0))));
  650         if (!force_update_check) {
  651             if ((ReadSetting32(SETTING_UPDATE_INTERVAL) == -1)) {
  652                 vuprintf("Check for updates disabled, as per settings.");
  653                 goto out;
  654             }
  655             reg_time = ReadSetting64(SETTING_LAST_UPDATE);
  656             update_interval = (int64_t)ReadSetting32(SETTING_UPDATE_INTERVAL);
  657             if (update_interval == 0) {
  658                 WriteSetting32(SETTING_UPDATE_INTERVAL, DEFAULT_UPDATE_INTERVAL);
  659                 update_interval = DEFAULT_UPDATE_INTERVAL;
  660             }
  661             GetSystemTime(&LocalTime);
  662             if (!SystemTimeToFileTime(&LocalTime, &FileTime))
  663                 goto out;
  664             local_time = ((((int64_t)FileTime.dwHighDateTime)<<32) + FileTime.dwLowDateTime) / 10000000;
  665             vvuprintf("Local time: %" PRId64, local_time);
  666             if (local_time < reg_time + update_interval) {
  667                 vuprintf("Next update check in %" PRId64 " seconds.", reg_time + update_interval - local_time);
  668                 goto out;
  669             }
  670         }
  671     }
  672 
  673     PrintInfoDebug(3000, MSG_243);
  674     status++;   // 1
  675 
  676     if (!GetVersionExA(&os_version)) {
  677         uprintf("Could not read Windows version - Check for updates cancelled.");
  678         goto out;
  679     }
  680 
  681     if (!pfInternetCrackUrlA(server_url, (DWORD)safe_strlen(server_url), 0, &UrlParts))
  682         goto out;
  683     hostname[sizeof(hostname)-1] = 0;
  684 
  685     static_sprintf(agent, APPLICATION_NAME "/%d.%d.%d (Windows NT %d.%d%s)",
  686         rufus_version[0], rufus_version[1], rufus_version[2],
  687         nWindowsVersion >> 4, nWindowsVersion & 0x0F, is_x64() ? "; WOW64" : "");
  688     hSession = GetInternetSession(FALSE);
  689     if (hSession == NULL)
  690         goto out;
  691     hConnection = pfInternetConnectA(hSession, UrlParts.lpszHostName, UrlParts.nPort, NULL, NULL, INTERNET_SERVICE_HTTP, 0, (DWORD_PTR)NULL);
  692     if (hConnection == NULL)
  693         goto out;
  694 
  695     status++;   // 2
  696     // BETAs are only made available for x86_32
  697     if (is_x86_32)
  698         releases_only = !ReadSettingBool(SETTING_INCLUDE_BETAS);
  699 
  700     // Test releases get their own distribution channel (and also force beta checks)
  701 #if defined(TEST)
  702     max_channel = (int)ARRAYSIZE(channel);
  703 #else
  704     max_channel = releases_only ? 1 : (int)ARRAYSIZE(channel) - 1;
  705 #endif
  706     vuprintf("Using %s for the update check", RUFUS_URL);
  707     for (k=0; (k<max_channel) && (!found_new_version); k++) {
  708         // Free any previous buffers we might have used
  709         safe_free(buf);
  710         safe_free(sig);
  711         uprintf("Checking %s channel...", channel[k]);
  712         // At this stage we can query the server for various update version files.
  713         // We first try to lookup for "<appname>_<os_arch>_<os_version_major>_<os_version_minor>.ver"
  714         // and then remove each of the <os_> components until we find our match. For instance, we may first
  715         // look for rufus_win_x64_6.2.ver (Win8 x64) but only get a match for rufus_win_x64_6.ver (Vista x64 or later)
  716         // This allows sunsetting OS versions (eg XP) or providing different downloads for different archs/groups.
  717         static_sprintf(urlpath, "%s%s%s_%s_%lu.%lu.ver", APPLICATION_NAME, (k==0)?"":"_",
  718             (k==0)?"":channel[k], archname[GetCpuArch()], os_version.dwMajorVersion, os_version.dwMinorVersion);
  719         vuprintf("Base update check: %s", urlpath);
  720         for (i=0, j=(int)safe_strlen(urlpath)-5; (j>0)&&(i<ARRAYSIZE(verpos)); j--) {
  721             if ((urlpath[j] == '.') || (urlpath[j] == '_')) {
  722                 verpos[i++] = j;
  723             }
  724         }
  725         assert(i == ARRAYSIZE(verpos));
  726 
  727         UrlParts.lpszUrlPath = urlpath;
  728         UrlParts.dwUrlPathLength = sizeof(urlpath);
  729         for (i=0; i<ARRAYSIZE(verpos); i++) {
  730             vvuprintf("Trying %s", UrlParts.lpszUrlPath);
  731             hRequest = pfHttpOpenRequestA(hConnection, "GET", UrlParts.lpszUrlPath, NULL, NULL, accept_types,
  732                 INTERNET_FLAG_IGNORE_REDIRECT_TO_HTTP|INTERNET_FLAG_IGNORE_REDIRECT_TO_HTTPS|
  733                 INTERNET_FLAG_NO_COOKIES|INTERNET_FLAG_NO_UI|INTERNET_FLAG_NO_CACHE_WRITE|INTERNET_FLAG_HYPERLINK|
  734                 ((UrlParts.nScheme == INTERNET_SCHEME_HTTPS)?INTERNET_FLAG_SECURE:0), (DWORD_PTR)NULL);
  735             if ((hRequest == NULL) || (!pfHttpSendRequestA(hRequest, request_headers, -1L, NULL, 0))) {
  736                 uprintf("Unable to send request: %s", WinInetErrorString());
  737                 goto out;
  738             }
  739 
  740             // Ensure that we get a text file
  741             dwSize = sizeof(dwStatus);
  742             dwStatus = 404;
  743             pfHttpQueryInfoA(hRequest, HTTP_QUERY_STATUS_CODE|HTTP_QUERY_FLAG_NUMBER, (LPVOID)&dwStatus, &dwSize, NULL);
  744             if (dwStatus == 200)
  745                 break;
  746             pfInternetCloseHandle(hRequest);
  747             hRequest = NULL;
  748             safe_strcpy(&urlpath[verpos[i]], 5, ".ver");
  749         }
  750         if (dwStatus != 200) {
  751             vuprintf("Could not find a %s version file on server %s", channel[k], server_url);
  752             if ((releases_only) || (k+1 >= ARRAYSIZE(channel)))
  753                 goto out;
  754             continue;
  755         }
  756         vuprintf("Found match for %s on server %s", urlpath, server_url);
  757 
  758         // We also get a date from the web server, which we'll use to avoid out of sync check,
  759         // in case some set their clock way into the future and back.
  760         // On the other hand, if local clock is set way back in the past, we will never check.
  761         dwSize = sizeof(ServerTime);
  762         // If we can't get a date we can trust, don't bother...
  763         if ( (!pfHttpQueryInfoA(hRequest, HTTP_QUERY_DATE|HTTP_QUERY_FLAG_SYSTEMTIME, (LPVOID)&ServerTime, &dwSize, NULL))
  764             || (!SystemTimeToFileTime(&ServerTime, &FileTime)) )
  765             goto out;
  766         server_time = ((((int64_t)FileTime.dwHighDateTime)<<32) + FileTime.dwLowDateTime) / 10000000;
  767         vvuprintf("Server time: %" PRId64, server_time);
  768         // Always store the server response time - the only clock we trust!
  769         WriteSetting64(SETTING_LAST_UPDATE, server_time);
  770         // Might as well let the user know
  771         if (!force_update_check) {
  772             if ((local_time > server_time + 600) || (local_time < server_time - 600)) {
  773                 uprintf("IMPORTANT: Your local clock is more than 10 minutes in the %s. Unless you fix this, " APPLICATION_NAME " may not be able to check for updates...",
  774                     (local_time > server_time + 600)?"future":"past");
  775             }
  776         }
  777 
  778         dwSize = sizeof(dwTotalSize);
  779         if (!pfHttpQueryInfoA(hRequest, HTTP_QUERY_CONTENT_LENGTH|HTTP_QUERY_FLAG_NUMBER, (LPVOID)&dwTotalSize, &dwSize, NULL))
  780             goto out;
  781 
  782         // Make sure the file is NUL terminated
  783         buf = (char*)calloc(dwTotalSize+1, 1);
  784         if (buf == NULL)
  785             goto out;
  786         // This is a version file - we should be able to gulp it down in one go
  787         if (!pfInternetReadFile(hRequest, buf, dwTotalSize, &dwDownloaded) || (dwDownloaded != dwTotalSize))
  788             goto out;
  789         vuprintf("Successfully downloaded version file (%d bytes)", dwTotalSize);
  790 
  791         // Now download the signature file
  792         static_sprintf(sigpath, "%s/%s.sig", server_url, urlpath);
  793         dwDownloaded = (DWORD)DownloadToFileOrBuffer(sigpath, NULL, &sig, NULL, FALSE);
  794         if ((dwDownloaded != RSA_SIGNATURE_SIZE) || (!ValidateOpensslSignature(buf, dwTotalSize, sig, dwDownloaded))) {
  795             uprintf("FATAL: Version signature is invalid ✗");
  796             goto out;
  797         }
  798         vuprintf("Version signature is valid ✓");
  799 
  800         status++;
  801         parse_update(buf, dwTotalSize + 1);
  802 
  803         vuprintf("UPDATE DATA:");
  804         vuprintf("  version: %d.%d.%d (%s)", update.version[0], update.version[1], update.version[2], channel[k]);
  805         vuprintf("  platform_min: %d.%d", update.platform_min[0], update.platform_min[1]);
  806         vuprintf("  url: %s", update.download_url);
  807 
  808         found_new_version = ((to_uint64_t(update.version) > to_uint64_t(rufus_version)) || (force_update))
  809             && ((os_version.dwMajorVersion > update.platform_min[0])
  810                 || ((os_version.dwMajorVersion == update.platform_min[0]) && (os_version.dwMinorVersion >= update.platform_min[1])));
  811         uprintf("N%sew %s version found%c", found_new_version ? "" : "o n", channel[k], found_new_version ? '!' : '.');
  812     }
  813 
  814 out:
  815     safe_free(buf);
  816     safe_free(sig);
  817     if (hRequest)
  818         pfInternetCloseHandle(hRequest);
  819     if (hConnection)
  820         pfInternetCloseHandle(hConnection);
  821     if (hSession)
  822         pfInternetCloseHandle(hSession);
  823     switch (status) {
  824     case 1:
  825         PrintInfoDebug(3000, MSG_244);
  826         break;
  827     case 2:
  828         PrintInfoDebug(3000, MSG_245);
  829         break;
  830     case 3:
  831     case 4:
  832         PrintInfo(3000, found_new_version ? MSG_246 : MSG_247);
  833     default:
  834         break;
  835     }
  836     // Start the new download after cleanup
  837     if (found_new_version) {
  838         // User may have started an operation while we were checking
  839         while ((!force_update_check) && (op_in_progress || (dialog_showing > 0))) {
  840             Sleep(15000);
  841         }
  842         DownloadNewVersion();
  843     } else if (force_update_check) {
  844         PostMessage(hMainDialog, UM_NO_UPDATE, 0, 0);
  845     }
  846     force_update_check = FALSE;
  847     update_check_thread = NULL;
  848     ExitThread(0);
  849 }
  850 
  851 /*
  852  * Initiate a check for updates. If force is true, ignore the wait period
  853  */
  854 BOOL CheckForUpdates(BOOL force)
  855 {
  856     force_update_check = force;
  857     if (update_check_thread != NULL)
  858         return FALSE;
  859 
  860     update_check_thread = CreateThread(NULL, 0, CheckForUpdatesThread, NULL, 0, NULL);
  861     if (update_check_thread == NULL) {
  862         uprintf("Unable to start update check thread");
  863         return FALSE;
  864     }
  865     return TRUE;
  866 }
  867 
  868 /*
  869  * Download an ISO through Fido
  870  */
  871 static DWORD WINAPI DownloadISOThread(LPVOID param)
  872 {
  873     char locale_str[1024], cmdline[sizeof(locale_str) + 512], pipe[MAX_GUID_STRING_LENGTH + 16] = "\\\\.\\pipe\\";
  874     char powershell_path[MAX_PATH], icon_path[MAX_PATH] = { 0 }, script_path[MAX_PATH] = { 0 };
  875     char *url = NULL, sig_url[128];
  876     uint64_t uncompressed_size;
  877     int64_t size = -1;
  878     BYTE *compressed = NULL, *sig = NULL;
  879     HANDLE hFile, hPipe;
  880     DWORD dwExitCode = 99, dwCompressedSize, dwSize, dwAvail, dwPipeSize = 4096;
  881     GUID guid;
  882 
  883     dialog_showing++;
  884     IGNORE_RETVAL(CoInitializeEx(NULL, COINIT_APARTMENTTHREADED));
  885 
  886     // Use a GUID as random unique string, else ill-intentioned security "researchers"
  887     // may either spam our pipe or replace our script to fool antivirus solutions into
  888     // thinking that Rufus is doing something malicious...
  889     IGNORE_RETVAL(CoCreateGuid(&guid));
  890     // coverity[fixed_size_dest]
  891     strcpy(&pipe[9], GuidToString(&guid));
  892     static_sprintf(icon_path, "%s%s.ico", temp_dir, APPLICATION_NAME);
  893     ExtractAppIcon(icon_path, TRUE);
  894 
  895 //#define FORCE_URL "https://github.com/pbatard/rufus/raw/master/res/loc/test/windows_to_go.iso"
  896 //#define FORCE_URL "https://cdimage.debian.org/debian-cd/current/amd64/iso-cd/debian-9.8.0-amd64-netinst.iso"
  897 #if !defined(FORCE_URL)
  898 #if defined(RUFUS_TEST)
  899     IGNORE_RETVAL(hFile);
  900     IGNORE_RETVAL(sig_url);
  901     IGNORE_RETVAL(dwCompressedSize);
  902     IGNORE_RETVAL(uncompressed_size);
  903     // In test mode, just use our local script
  904     static_strcpy(script_path, "D:\\Projects\\Fido\\Fido.ps1");
  905 #else
  906     // If we don't have the script, download it
  907     if (fido_script == NULL) {
  908         dwCompressedSize = (DWORD)DownloadToFileOrBuffer(fido_url, NULL, &compressed, hMainDialog, FALSE);
  909         if (dwCompressedSize == 0)
  910             goto out;
  911         static_sprintf(sig_url, "%s.sig", fido_url);
  912         dwSize = (DWORD)DownloadToFileOrBuffer(sig_url, NULL, &sig, NULL, FALSE);
  913         if ((dwSize != RSA_SIGNATURE_SIZE) || (!ValidateOpensslSignature(compressed, dwCompressedSize, sig, dwSize))) {
  914             uprintf("FATAL: Signature is invalid ✗");
  915             FormatStatus = ERROR_SEVERITY_ERROR | FAC(FACILITY_STORAGE) | APPERR(ERROR_BAD_SIGNATURE);
  916             SendMessage(hProgress, PBM_SETSTATE, (WPARAM)PBST_ERROR, 0);
  917             SetTaskbarProgressState(TASKBAR_ERROR);
  918             safe_free(compressed);
  919             free(sig);
  920             goto out;
  921         }
  922         free(sig);
  923         uprintf("Signature is valid ✓");
  924         uncompressed_size = *((uint64_t*)&compressed[5]);
  925         if ((uncompressed_size < 1 * MB) && (bled_init(_uprintf, NULL, NULL, NULL, NULL, &FormatStatus) >= 0)) {
  926             fido_script = malloc((size_t)uncompressed_size);
  927             size = bled_uncompress_from_buffer_to_buffer(compressed, dwCompressedSize, fido_script, (size_t)uncompressed_size, BLED_COMPRESSION_LZMA);
  928             bled_exit();
  929         }
  930         safe_free(compressed);
  931         if (size != uncompressed_size) {
  932             uprintf("FATAL: Could not uncompressed download script");
  933             safe_free(fido_script);
  934             FormatStatus = ERROR_SEVERITY_ERROR | FAC(FACILITY_STORAGE) | ERROR_INVALID_DATA;
  935             SendMessage(hProgress, PBM_SETSTATE, (WPARAM)PBST_ERROR, 0);
  936             SetTaskbarProgressState(TASKBAR_ERROR);
  937             goto out;
  938         }
  939         fido_len = (DWORD)size;
  940         SendMessage(hProgress, PBM_SETSTATE, (WPARAM)PBST_NORMAL, 0);
  941         SetTaskbarProgressState(TASKBAR_NORMAL);
  942         SetTaskbarProgressValue(0, MAX_PROGRESS);
  943         SendMessage(hProgress, PBM_SETPOS, 0, 0);
  944     }
  945     PrintInfo(0, MSG_148);
  946 
  947     assert((fido_script != NULL) && (fido_len != 0));
  948 
  949     static_sprintf(script_path, "%s%s.ps1", temp_dir, GuidToString(&guid));
  950     hFile = CreateFileU(script_path, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_READONLY, NULL);
  951     if (hFile == INVALID_HANDLE_VALUE) {
  952         uprintf("Unable to create download script '%s': %s", script_path, WindowsErrorString());
  953         goto out;
  954     }
  955     if ((!WriteFile(hFile, fido_script, fido_len, &dwSize, NULL)) || (dwSize != fido_len)) {
  956         uprintf("Unable to write download script '%s': %s", script_path, WindowsErrorString());
  957         goto out;
  958     }
  959     // Why oh why does PowerShell refuse to open read-only files that haven't been closed?
  960     // Because of this limitation, we can't use LockFileEx() on the file we create...
  961     safe_closehandle(hFile);
  962 #endif
  963     static_sprintf(powershell_path, "%s\\WindowsPowerShell\\v1.0\\powershell.exe", system_dir);
  964     static_sprintf(locale_str, "%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s",
  965         selected_locale->txt[0], lmprintf(MSG_135), lmprintf(MSG_136), lmprintf(MSG_137),
  966         lmprintf(MSG_138), lmprintf(MSG_139), lmprintf(MSG_040), lmprintf(MSG_140), lmprintf(MSG_141),
  967         lmprintf(MSG_006), lmprintf(MSG_007), lmprintf(MSG_042), lmprintf(MSG_142), lmprintf(MSG_143),
  968         lmprintf(MSG_144), lmprintf(MSG_145), lmprintf(MSG_146));
  969 
  970     hPipe = CreateNamedPipeA(pipe, PIPE_ACCESS_INBOUND,
  971         PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT, PIPE_UNLIMITED_INSTANCES,
  972         dwPipeSize, dwPipeSize, 0, NULL);
  973     if (hPipe == INVALID_HANDLE_VALUE) {
  974         uprintf("Could not create pipe '%s': %s", pipe, WindowsErrorString);
  975         goto out;
  976     }
  977 
  978     static_sprintf(cmdline, "\"%s\" -NonInteractive -Sta -NoProfile –ExecutionPolicy Bypass "
  979         "-File \"%s\" -DisableFirstRunCustomize -PipeName %s -LocData \"%s\" -Icon \"%s\" -AppTitle \"%s\"",
  980         powershell_path, script_path, &pipe[9], locale_str, icon_path, lmprintf(MSG_149));
  981     // Signal our Windows alert hook that it should close the IE cookie prompts from Fido
  982     close_fido_cookie_prompts = TRUE;
  983     dwExitCode = RunCommand(cmdline, app_dir, TRUE);
  984     uprintf("Exited download script with code: %d", dwExitCode);
  985     close_fido_cookie_prompts = FALSE;
  986     if ((dwExitCode == 0) && PeekNamedPipe(hPipe, NULL, dwPipeSize, NULL, &dwAvail, NULL) && (dwAvail != 0)) {
  987         url = malloc(dwAvail + 1);
  988         dwSize = 0;
  989         if ((url != NULL) && ReadFile(hPipe, url, dwAvail, &dwSize, NULL) && (dwSize > 4)) {
  990 #else
  991     {   {   url = strdup(FORCE_URL);
  992             dwSize = (DWORD)strlen(FORCE_URL);
  993 #endif
  994             IMG_SAVE img_save = { 0 };
  995 // WTF is wrong with Microsoft's static analyzer reporting a potential buffer overflow here?!?
  996 #if defined(_MSC_VER)
  997 #pragma warning(disable: 6386)
  998 #endif
  999             url[min(dwSize, dwAvail)] = 0;
 1000 #if defined(_MSC_VER)
 1001 #pragma warning(default: 6386)
 1002 #endif
 1003             EXT_DECL(img_ext, GetShortName(url), __VA_GROUP__("*.iso"), __VA_GROUP__(lmprintf(MSG_036)));
 1004             img_save.Type = IMG_SAVE_TYPE_ISO;
 1005             img_save.ImagePath = FileDialog(TRUE, NULL, &img_ext, 0);
 1006             if (img_save.ImagePath == NULL) {
 1007                 goto out;
 1008             }
 1009             // Download the ISO and report errors if any
 1010             SendMessage(hMainDialog, UM_PROGRESS_INIT, 0, 0);
 1011             FormatStatus = 0;
 1012             SendMessage(hMainDialog, UM_TIMER_START, 0, 0);
 1013             if (DownloadToFileOrBuffer(url, img_save.ImagePath, NULL, hMainDialog, TRUE) == 0) {
 1014                 SendMessage(hMainDialog, UM_PROGRESS_EXIT, 0, 0);
 1015                 if (SCODE_CODE(FormatStatus) == ERROR_CANCELLED) {
 1016                     uprintf("Download cancelled by user");
 1017                     Notification(MSG_INFO, NULL, NULL, lmprintf(MSG_211), lmprintf(MSG_041));
 1018                     PrintInfo(0, MSG_211);
 1019                 } else {
 1020                     Notification(MSG_ERROR, NULL, NULL, lmprintf(MSG_194, GetShortName(url)), lmprintf(MSG_043, WinInetErrorString()));
 1021                     PrintInfo(0, MSG_212);
 1022                 }
 1023             } else {
 1024                 // Download was successful => Select and scan the ISO
 1025                 image_path = safe_strdup(img_save.ImagePath);
 1026                 PostMessage(hMainDialog, UM_SELECT_ISO, 0, 0);
 1027             }
 1028             safe_free(img_save.ImagePath);
 1029         }
 1030     }
 1031 
 1032 out:
 1033     if (icon_path[0] != 0)
 1034         DeleteFileU(icon_path);
 1035 #if !defined(RUFUS_TEST)
 1036     if (script_path[0] != 0) {
 1037         SetFileAttributesU(script_path, FILE_ATTRIBUTE_NORMAL);
 1038         DeleteFileU(script_path);
 1039     }
 1040 #endif
 1041     free(url);
 1042     SendMessage(hMainDialog, UM_ENABLE_CONTROLS, 0, 0);
 1043     dialog_showing--;
 1044     ExitThread(dwExitCode);
 1045 }
 1046 
 1047 BOOL DownloadISO()
 1048 {
 1049     if (CreateThread(NULL, 0, DownloadISOThread, NULL, 0, NULL) == NULL) {
 1050         uprintf("Unable to start Windows ISO download thread");
 1051         FormatStatus = ERROR_SEVERITY_ERROR | FAC(FACILITY_STORAGE) | APPERR(ERROR_CANT_START_THREAD);
 1052         SendMessage(hMainDialog, UM_ENABLE_CONTROLS, 0, 0);
 1053         return FALSE;
 1054     }
 1055     return TRUE;
 1056 }
 1057 
 1058 BOOL IsDownloadable(const char* url)
 1059 {
 1060     DWORD dwSize, dwTotalSize = 0;
 1061     const char* accept_types[] = { "*/*\0", NULL };
 1062     char hostname[64], urlpath[128];
 1063     HINTERNET hSession = NULL, hConnection = NULL, hRequest = NULL;
 1064     URL_COMPONENTSA UrlParts = { sizeof(URL_COMPONENTSA), NULL, 1, (INTERNET_SCHEME)0,
 1065         hostname, sizeof(hostname), 0, NULL, 1, urlpath, sizeof(urlpath), NULL, 1 };
 1066 
 1067     PF_TYPE_DECL(WINAPI, BOOL, InternetCrackUrlA, (LPCSTR, DWORD, DWORD, LPURL_COMPONENTSA));
 1068     PF_TYPE_DECL(WINAPI, HINTERNET, InternetConnectA, (HINTERNET, LPCSTR, INTERNET_PORT, LPCSTR, LPCSTR, DWORD, DWORD, DWORD_PTR));
 1069     PF_TYPE_DECL(WINAPI, BOOL, InternetCloseHandle, (HINTERNET));
 1070     PF_TYPE_DECL(WINAPI, HINTERNET, HttpOpenRequestA, (HINTERNET, LPCSTR, LPCSTR, LPCSTR, LPCSTR, LPCSTR*, DWORD, DWORD_PTR));
 1071     PF_TYPE_DECL(WINAPI, BOOL, HttpSendRequestA, (HINTERNET, LPCSTR, DWORD, LPVOID, DWORD));
 1072     PF_TYPE_DECL(WINAPI, BOOL, HttpQueryInfoA, (HINTERNET, DWORD, LPVOID, LPDWORD, LPDWORD));
 1073     PF_INIT_OR_OUT(InternetCrackUrlA, WinInet);
 1074     PF_INIT_OR_OUT(InternetConnectA, WinInet);
 1075     PF_INIT_OR_OUT(InternetCloseHandle, WinInet);
 1076     PF_INIT_OR_OUT(HttpOpenRequestA, WinInet);
 1077     PF_INIT_OR_OUT(HttpSendRequestA, WinInet);
 1078     PF_INIT_OR_OUT(HttpQueryInfoA, WinInet);
 1079 
 1080     if (url == NULL)
 1081         return FALSE;
 1082 
 1083     FormatStatus = 0;
 1084     DownloadStatus = 404;
 1085 
 1086     if ((!pfInternetCrackUrlA(url, (DWORD)safe_strlen(url), 0, &UrlParts))
 1087         || (UrlParts.lpszHostName == NULL) || (UrlParts.lpszUrlPath == NULL))
 1088         goto out;
 1089     hostname[sizeof(hostname) - 1] = 0;
 1090 
 1091     // Open an Internet session
 1092     hSession = GetInternetSession(FALSE);
 1093     if (hSession == NULL)
 1094         goto out;
 1095 
 1096     hConnection = pfInternetConnectA(hSession, UrlParts.lpszHostName, UrlParts.nPort, NULL, NULL, INTERNET_SERVICE_HTTP, 0, (DWORD_PTR)NULL);
 1097     if (hConnection == NULL)
 1098         goto out;
 1099 
 1100     hRequest = pfHttpOpenRequestA(hConnection, "GET", UrlParts.lpszUrlPath, NULL, NULL, accept_types,
 1101         INTERNET_FLAG_IGNORE_REDIRECT_TO_HTTP | INTERNET_FLAG_IGNORE_REDIRECT_TO_HTTPS |
 1102         INTERNET_FLAG_NO_COOKIES | INTERNET_FLAG_NO_UI | INTERNET_FLAG_NO_CACHE_WRITE | INTERNET_FLAG_HYPERLINK |
 1103         ((UrlParts.nScheme == INTERNET_SCHEME_HTTPS) ? INTERNET_FLAG_SECURE : 0), (DWORD_PTR)NULL);
 1104     if (hRequest == NULL)
 1105         goto out;
 1106 
 1107     if (!pfHttpSendRequestA(hRequest, request_headers, -1L, NULL, 0))
 1108         goto out;
 1109 
 1110     // Get the file size
 1111     dwSize = sizeof(DownloadStatus);
 1112     pfHttpQueryInfoA(hRequest, HTTP_QUERY_STATUS_CODE | HTTP_QUERY_FLAG_NUMBER, (LPVOID)&DownloadStatus, &dwSize, NULL);
 1113     if (DownloadStatus != 200)
 1114         goto out;
 1115     dwSize = sizeof(dwTotalSize);
 1116     pfHttpQueryInfoA(hRequest, HTTP_QUERY_CONTENT_LENGTH | HTTP_QUERY_FLAG_NUMBER, (LPVOID)&dwTotalSize, &dwSize, NULL);
 1117 
 1118 out:
 1119     if (hRequest)
 1120         pfInternetCloseHandle(hRequest);
 1121     if (hConnection)
 1122         pfInternetCloseHandle(hConnection);
 1123     if (hSession)
 1124         pfInternetCloseHandle(hSession);
 1125 
 1126     return (dwTotalSize > 0);
 1127 }