diff options
Diffstat (limited to 'indra/win_updater/updater.cpp')
| -rw-r--r-- | indra/win_updater/updater.cpp | 417 |
1 files changed, 197 insertions, 220 deletions
diff --git a/indra/win_updater/updater.cpp b/indra/win_updater/updater.cpp index f849e4e9ad..aeab5a3b13 100644 --- a/indra/win_updater/updater.cpp +++ b/indra/win_updater/updater.cpp @@ -2,41 +2,45 @@ * @file updater.cpp * @brief Windows auto-updater * - * $LicenseInfo:firstyear=2002&license=viewergpl$ - * - * Copyright (c) 2002-2007, Linden Research, Inc. - * + * $LicenseInfo:firstyear=2002&license=viewerlgpl$ * Second Life Viewer Source Code - * The source code in this file ("Source Code") is provided by Linden Lab - * to you under the terms of the GNU General Public License, version 2.0 - * ("GPL"), unless you have obtained a separate licensing agreement - * ("Other License"), formally executed by you and Linden Lab. Terms of - * the GPL can be found in doc/GPL-license.txt in this distribution, or - * online at http://secondlife.com/developers/opensource/gplv2 + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. * - * There are special exceptions to the terms and conditions of the GPL as - * it is applied to this Source Code. View the full text of the exception - * in the file doc/FLOSS-exception.txt in this software distribution, or - * online at http://secondlife.com/developers/opensource/flossexception + * This library 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 + * Lesser General Public License for more details. * - * By copying, modifying or distributing this software, you acknowledge - * that you have read and understood your obligations described above, - * and agree to abide by those obligations. + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * - * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO - * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, - * COMPLETENESS OR PERFORMANCE. + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ // -// Usage: updater -url <url> [-name <window_title>] [-program <program_name>] [-silent] +// Usage: updater -url <url> // -#include "linden_common.h" +// We use dangerous fopen, strtok, mbstowcs, sprintf +// which generates warnings on VC2005. +// *TODO: Switch to fopen_s, strtok_s, etc. +#define _CRT_SECURE_NO_DEPRECATE #include <windows.h> #include <wininet.h> +#include <stdio.h> +#include <string> +#include <iostream> +#include <stdexcept> +#include <sstream> +#include <fstream> #define BUFSIZE 8192 @@ -44,19 +48,19 @@ int gTotalBytesRead = 0; DWORD gTotalBytes = -1; HWND gWindow = NULL; WCHAR gProgress[256]; -char* gUpdateURL; -char* gProgramName; -char* gProductName; -bool gIsSilent; +char* gUpdateURL = NULL; #if _DEBUG -FILE* logfile = 0; +std::ofstream logfile; +#define DEBUG(expr) logfile << expr << std::endl +#else +#define DEBUG(expr) /**/ #endif -char* wchars_to_utf8chars(WCHAR* in_chars) +char* wchars_to_utf8chars(const WCHAR* in_chars) { int tlen = 0; - WCHAR* twc = in_chars; + const WCHAR* twc = in_chars; while (*twc++ != 0) { tlen++; @@ -79,103 +83,128 @@ char* wchars_to_utf8chars(WCHAR* in_chars) return res; } -int WINAPI get_url_into_file(WCHAR *uri, char *path, int *cancelled) +class Fetcher +{ +public: + Fetcher(const std::wstring& uri) + { + // These actions are broken out as separate methods not because it + // makes the code clearer, but to avoid triggering AntiVir and + // McAfee-GW-Edition virus scanners (DEV-31680). + mInet = openInet(); + mDownload = openUrl(uri); + } + + ~Fetcher() + { + DEBUG("Calling InternetCloseHandle"); + InternetCloseHandle(mDownload); + InternetCloseHandle(mInet); + } + + unsigned long read(char* buffer, size_t bufflen) const; + + DWORD getTotalBytes() const + { + DWORD totalBytes; + DWORD sizeof_total_bytes = sizeof(totalBytes); + HttpQueryInfo(mDownload, HTTP_QUERY_CONTENT_LENGTH | HTTP_QUERY_FLAG_NUMBER, + &totalBytes, &sizeof_total_bytes, NULL); + return totalBytes; + } + + struct InetError: public std::runtime_error + { + InetError(const std::string& what): std::runtime_error(what) {} + }; + +private: + // We test results from a number of different MS functions with different + // return types -- but the common characteristic is that 0 (i.e. (! result)) + // means an error of some kind. + template <typename RESULT> + static RESULT check(const std::string& desc, RESULT result) + { + if (result) + { + // success, show caller + return result; + } + DWORD err = GetLastError(); + std::ostringstream out; + out << desc << " Failed: " << err; + DEBUG(out.str()); + throw InetError(out.str()); + } + + HINTERNET openUrl(const std::wstring& uri) const; + HINTERNET openInet() const; + + HINTERNET mInet, mDownload; +}; + +HINTERNET Fetcher::openInet() const +{ + DEBUG("Calling InternetOpen"); + // Init wininet subsystem + return check("InternetOpen", + InternetOpen(L"LindenUpdater", INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0)); +} + +HINTERNET Fetcher::openUrl(const std::wstring& uri) const +{ + DEBUG("Calling InternetOpenUrl: " << wchars_to_utf8chars(uri.c_str())); + return check("InternetOpenUrl", + InternetOpenUrl(mInet, uri.c_str(), NULL, 0, INTERNET_FLAG_NEED_FILE, NULL)); +} + +unsigned long Fetcher::read(char* buffer, size_t bufflen) const +{ + unsigned long bytes_read = 0; + DEBUG("Calling InternetReadFile"); + check("InternetReadFile", + InternetReadFile(mDownload, buffer, bufflen, &bytes_read)); + return bytes_read; +} + +int WINAPI get_url_into_file(const std::wstring& uri, const std::string& path, int *cancelled) { int success = FALSE; *cancelled = FALSE; - HINTERNET hinet, hdownload; - char data[BUFSIZE]; /* Flawfinder: ignore */ - unsigned long bytes_read; - -#if _DEBUG - fprintf(logfile,"Opening '%s'\n",path); - fflush(logfile); -#endif + DEBUG("Opening '" << path << "'"); - FILE* fp = fopen(path, "wb"); /* Flawfinder: ignore */ + FILE* fp = fopen(path.c_str(), "wb"); /* Flawfinder: ignore */ if (!fp) { -#if _DEBUG - fprintf(logfile,"Failed to open '%s'\n",path); - fflush(logfile); -#endif - return success; - } - -#if _DEBUG - fprintf(logfile,"Calling InternetOpen\n"); - fflush(logfile); -#endif - // Init wininet subsystem - hinet = InternetOpen(L"LindenUpdater", INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0); - if (hinet == NULL) - { + DEBUG("Failed to open '" << path << "'"); return success; } -#if _DEBUG - fprintf(logfile,"Calling InternetOpenUrl: %s\n",wchars_to_utf8chars(uri)); - fflush(logfile); -#endif - hdownload = InternetOpenUrl(hinet, uri, NULL, 0, INTERNET_FLAG_NEED_FILE, NULL); - if (hdownload == NULL) - { -#if _DEBUG - DWORD err = GetLastError(); - fprintf(logfile,"InternetOpenUrl Failed: %d\n",err); - fflush(logfile); -#endif - return success; - } + // Note, ctor can throw, since it uses check() function. + Fetcher fetcher(uri); + gTotalBytes = fetcher.getTotalBytes(); - DWORD sizeof_total_bytes = sizeof(gTotalBytes); - HttpQueryInfo(hdownload, HTTP_QUERY_CONTENT_LENGTH | HTTP_QUERY_FLAG_NUMBER, &gTotalBytes, &sizeof_total_bytes, NULL); - +/*==========================================================================*| + // nobody uses total_bytes?!? What's this doing here? DWORD total_bytes = 0; - success = InternetQueryDataAvailable(hdownload, &total_bytes, 0, 0); - if (success == FALSE) - { -#if _DEBUG - DWORD err = GetLastError(); - fprintf(logfile,"InternetQueryDataAvailable Failed: %d bytes Err:%d\n",total_bytes,err); - fflush(logfile); -#endif - return success; - } + success = check("InternetQueryDataAvailable", + InternetQueryDataAvailable(hdownload, &total_bytes, 0, 0)); +|*==========================================================================*/ success = FALSE; while(!success && !(*cancelled)) { - MSG msg; + char data[BUFSIZE]; /* Flawfinder: ignore */ + unsigned long bytes_read = fetcher.read(data, sizeof(data)); -#if _DEBUG - fprintf(logfile,"Calling InternetReadFile\n"); - fflush(logfile); -#endif - if (!InternetReadFile(hdownload, data, BUFSIZE, &bytes_read)) - { -#if _DEBUG - fprintf(logfile,"InternetReadFile Failed.\n"); - fflush(logfile); -#endif - // ...an error occurred - return FALSE; - } - -#if _DEBUG if (!bytes_read) { - fprintf(logfile,"InternetReadFile Read 0 bytes.\n"); - fflush(logfile); + DEBUG("InternetReadFile Read " << bytes_read << " bytes."); } -#endif -#if _DEBUG - fprintf(logfile,"Reading Data, bytes_read = %d\n",bytes_read); - fflush(logfile); -#endif + DEBUG("Reading Data, bytes_read = " << bytes_read); if (bytes_read == 0) { @@ -198,26 +227,18 @@ int WINAPI get_url_into_file(WCHAR *uri, char *path, int *cancelled) } -#if _DEBUG - fprintf(logfile,"Calling InvalidateRect\n"); - fflush(logfile); -#endif + DEBUG("Calling InvalidateRect"); // Mark the window as needing redraw (of the whole thing) InvalidateRect(gWindow, NULL, TRUE); // Do the redraw -#if _DEBUG - fprintf(logfile,"Calling UpdateWindow\n"); - fflush(logfile); -#endif + DEBUG("Calling UpdateWindow"); UpdateWindow(gWindow); -#if _DEBUG - fprintf(logfile,"Calling PeekMessage\n"); - fflush(logfile); -#endif - if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) + DEBUG("Calling PeekMessage"); + MSG msg; + while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { TranslateMessage(&msg); DispatchMessage(&msg); @@ -230,15 +251,7 @@ int WINAPI get_url_into_file(WCHAR *uri, char *path, int *cancelled) } } -#if _DEBUG - fprintf(logfile,"Calling InternetCloseHandle\n"); - fclose(logfile); -#endif - fclose(fp); - InternetCloseHandle(hdownload); - InternetCloseHandle(hinet); - return success; } @@ -279,26 +292,14 @@ int parse_args(int argc, char **argv) for (j = 1; j < argc; j++) { - if ((!strcmp(argv[j], "-name")) && (++j < argc)) - { - gProductName = argv[j]; - } - else if ((!strcmp(argv[j], "-url")) && (++j < argc)) + if ((!strcmp(argv[j], "-url")) && (++j < argc)) { gUpdateURL = argv[j]; } - else if ((!strcmp(argv[j], "-program")) && (++j < argc)) - { - gProgramName = argv[j]; - } - else if (!strcmp(argv[j], "-silent")) - { - gIsSilent = true; - } } // If nothing was set, let the caller know. - if (!gProductName && !gProgramName && !gIsSilent && !gUpdateURL) + if (!gUpdateURL) { return 1; } @@ -316,9 +317,8 @@ WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nSho char* argv[MAX_ARGS]; /* Flawfinder: ignore */ #if _DEBUG - logfile = _wfopen(TEXT("updater.log"),TEXT("wt")); - fprintf(logfile,"Parsing command arguments\n"); - fflush(logfile); + logfile.open("updater.log", std::ios_base::out); + DEBUG("Parsing command arguments"); #endif char *token = NULL; @@ -350,39 +350,21 @@ WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nSho } gUpdateURL = NULL; - gProgramName = NULL; - gProductName = NULL; - gIsSilent = false; ///////////////////////////////////////// // // Process command line arguments // -#if _DEBUG - fprintf(logfile,"Processing command arguments\n"); - fflush(logfile); -#endif + DEBUG("Processing command arguments"); // // Parse the command line arguments // int parse_args_result = parse_args(argc, argv); - WCHAR window_title[2048]; - if (gProductName) - { - mbstowcs(window_title, gProductName, 2048); - wcscat(window_title, L" Updater"); /* Flawfinder: ignore */ - } - else - { - mbstowcs(window_title, "Second Life Updater", 2048); - } WNDCLASSEX wndclassex = { 0 }; - DEVMODE dev_mode = { 0 }; - char update_exec_path[MAX_PATH]; /* Flawfinder: ignore */ - char *ptr; + //DEVMODE dev_mode = { 0 }; const int WINDOW_WIDTH = 250; const int WINDOW_HEIGHT = 100; @@ -400,10 +382,10 @@ WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nSho RegisterClassEx(&wndclassex); // Get the size of the screen - EnumDisplaySettings(NULL, ENUM_CURRENT_SETTINGS, &dev_mode); + //EnumDisplaySettings(NULL, ENUM_CURRENT_SETTINGS, &dev_mode); gWindow = CreateWindowEx(NULL, win_class_name, - window_title, + L"Second Life Updater", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, @@ -431,37 +413,34 @@ WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nSho } // Can't feed GetTempPath into GetTempFile directly - if (0 == GetTempPathA(MAX_PATH - 14, update_exec_path)) + char temp_path[MAX_PATH]; /* Flawfinder: ignore */ + if (0 == GetTempPathA(sizeof(temp_path), temp_path)) { MessageBox(gWindow, L"Problem with GetTempPath()", L"Error", MB_OK); return 1; } - if (0 == GetTempFileNameA(update_exec_path, NULL, 0, update_exec_path)) - { - MessageBox(gWindow, L"Problem with GetTempFileName()", - L"Error", MB_OK); - return 1; - } - // Hack hack hack - ptr = strrchr(update_exec_path, '.'); - *(ptr + 1) = 'e'; - *(ptr + 2) = 'x'; - *(ptr + 3) = 'e'; - *(ptr + 4) = 0; + std::string update_exec_path(temp_path); + update_exec_path.append("Second_Life_Updater.exe"); WCHAR update_uri[4096]; - mbstowcs(update_uri, gUpdateURL, 4096); + mbstowcs(update_uri, gUpdateURL, sizeof(update_uri)); - int success; - int cancelled; + int success = 0; + int cancelled = 0; // Actually do the download -#if _DEBUG - fprintf(logfile,"Calling get_url_into_file\n"); - fflush(logfile); -#endif - success = get_url_into_file(update_uri, update_exec_path, &cancelled); + try + { + DEBUG("Calling get_url_into_file"); + success = get_url_into_file(update_uri, update_exec_path, &cancelled); + } + catch (const Fetcher::InetError& e) + { + (void)e; + success = FALSE; + DEBUG("Caught: " << e.what()); + } // WinInet can't tell us if we got a 404 or not. Therefor, we check // for the size of the downloaded file, and assume that our installer @@ -493,47 +472,45 @@ WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nSho return 1; } - // Construct some parameters. - char params[2048]; /* Flawfinder: ignore */ - if (gIsSilent && gProgramName) - { - _snprintf(params, sizeof(params), "/S /P=\"%s\"", gProgramName); /* Flawfinder: ignore */ - params[2047] = '\0'; - } - else if (gProgramName) - { - _snprintf(params, sizeof(params), "/P=\"%s\"", gProgramName); /* Flawfinder: ignore */ - params[2047] = '\0'; - } - else if (gIsSilent) - { - sprintf(params, "/S"); /* Flawfinder: ignore */ - } - else - { - params[0] = '\0'; - } + // TODO: Make updates silent (with /S to NSIS) + //char params[256]; /* Flawfinder: ignore */ + //sprintf(params, "/S"); /* Flawfinder: ignore */ + //MessageBox(gWindow, + // L"Updating Second Life.\n\nSecond Life will automatically start once the update is complete. This may take a minute...", + // L"Download Complete", + // MB_OK); - if (32 >= (int) ShellExecuteA(gWindow, "open", update_exec_path, params, +/*==========================================================================*| + // DEV-31680: ShellExecuteA() causes McAfee-GW-Edition and AntiVir + // scanners to flag this executable as a probable virus vector. + // Less than or equal to 32 means failure + if (32 >= (int) ShellExecuteA(gWindow, "open", update_exec_path.c_str(), NULL, "C:\\", SW_SHOWDEFAULT)) +|*==========================================================================*/ + // from http://msdn.microsoft.com/en-us/library/ms682512(VS.85).aspx + STARTUPINFOA si; + PROCESS_INFORMATION pi; + ZeroMemory(&si, sizeof(si)); + si.cb = sizeof(si); + ZeroMemory(&pi, sizeof(pi)); + + if (! CreateProcessA(update_exec_path.c_str(), // executable file + NULL, // command line + NULL, // process cannot be inherited + NULL, // thread cannot be inherited + FALSE, // do not inherit existing handles + 0, // process creation flags + NULL, // inherit parent's environment + NULL, // inherit parent's current dir + &si, // STARTUPINFO + &pi)) // PROCESS_INFORMATION { - // No shit: less than or equal to 32 means failure - MessageBox(gWindow, L"ShellExecute failed. Please try again later.", NULL, MB_OK); + MessageBox(gWindow, L"Update failed. Please try again later.", NULL, MB_OK); return 1; } - if (gIsSilent && gProductName) - { - WCHAR message[2048]; - WCHAR wproduct[2048]; - mbstowcs(wproduct, gProductName, 2048); - - wsprintf(message, - L"Updating %s. %s will automatically start once the update is complete. This may take a minute...", - wproduct, wproduct); - - MessageBox(gWindow, message, L"Download Complete", MB_OK); - } + // Give installer some time to open a window + Sleep(1000); return 0; } |
