updater.cpp
上传用户:king477883
上传日期:2021-03-01
资源大小:9553k
文件大小:13k
源码类别:

游戏引擎

开发平台:

C++ Builder

  1. /** 
  2.  * @file updater.cpp
  3.  * @brief Windows auto-updater
  4.  *
  5.  * $LicenseInfo:firstyear=2002&license=viewergpl$
  6.  * 
  7.  * Copyright (c) 2002-2010, Linden Research, Inc.
  8.  * 
  9.  * Second Life Viewer Source Code
  10.  * The source code in this file ("Source Code") is provided by Linden Lab
  11.  * to you under the terms of the GNU General Public License, version 2.0
  12.  * ("GPL"), unless you have obtained a separate licensing agreement
  13.  * ("Other License"), formally executed by you and Linden Lab.  Terms of
  14.  * the GPL can be found in doc/GPL-license.txt in this distribution, or
  15.  * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
  16.  * 
  17.  * There are special exceptions to the terms and conditions of the GPL as
  18.  * it is applied to this Source Code. View the full text of the exception
  19.  * in the file doc/FLOSS-exception.txt in this software distribution, or
  20.  * online at
  21.  * http://secondlifegrid.net/programs/open_source/licensing/flossexception
  22.  * 
  23.  * By copying, modifying or distributing this software, you acknowledge
  24.  * that you have read and understood your obligations described above,
  25.  * and agree to abide by those obligations.
  26.  * 
  27.  * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
  28.  * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
  29.  * COMPLETENESS OR PERFORMANCE.
  30.  * $/LicenseInfo$
  31.  */
  32. //
  33. // Usage: updater -url <url>
  34. //
  35. // We use dangerous fopen, strtok, mbstowcs, sprintf
  36. // which generates warnings on VC2005.
  37. // *TODO: Switch to fopen_s, strtok_s, etc.
  38. #define _CRT_SECURE_NO_DEPRECATE
  39. #include <windows.h>
  40. #include <wininet.h>
  41. #include <stdio.h>
  42. #include <string>
  43. #include <iostream>
  44. #include <stdexcept>
  45. #include <sstream>
  46. #include <fstream>
  47. #define BUFSIZE 8192
  48. int  gTotalBytesRead = 0;
  49. DWORD gTotalBytes = -1;
  50. HWND gWindow = NULL;
  51. WCHAR gProgress[256];
  52. char* gUpdateURL = NULL;
  53. #if _DEBUG
  54. std::ofstream logfile;
  55. #define DEBUG(expr) logfile << expr << std::endl
  56. #else
  57. #define DEBUG(expr) /**/
  58. #endif
  59. char* wchars_to_utf8chars(const WCHAR* in_chars)
  60. {
  61. int tlen = 0;
  62. const WCHAR* twc = in_chars;
  63. while (*twc++ != 0)
  64. {
  65. tlen++;
  66. }
  67. char* outchars = new char[tlen];
  68. char* res = outchars;
  69. for (int i=0; i<tlen; i++)
  70. {
  71. int cur_char = (int)(*in_chars++);
  72. if (cur_char < 0x80)
  73. {
  74. *outchars++ = (char)cur_char;
  75. }
  76. else
  77. {
  78. *outchars++ = '?';
  79. }
  80. }
  81. *outchars = 0;
  82. return res;
  83. }
  84. class Fetcher
  85. {
  86. public:
  87.     Fetcher(const std::wstring& uri)
  88.     {
  89.         // These actions are broken out as separate methods not because it
  90.         // makes the code clearer, but to avoid triggering AntiVir and
  91.         // McAfee-GW-Edition virus scanners (DEV-31680).
  92.         mInet = openInet();
  93.         mDownload = openUrl(uri);
  94.     }
  95.     ~Fetcher()
  96.     {
  97.         DEBUG("Calling InternetCloseHandle");
  98.         InternetCloseHandle(mDownload);
  99.         InternetCloseHandle(mInet);
  100.     }
  101.     unsigned long read(char* buffer, size_t bufflen) const;
  102.     DWORD getTotalBytes() const
  103.     {
  104.         DWORD totalBytes;
  105.         DWORD sizeof_total_bytes = sizeof(totalBytes);
  106.         HttpQueryInfo(mDownload, HTTP_QUERY_CONTENT_LENGTH | HTTP_QUERY_FLAG_NUMBER,
  107.                       &totalBytes, &sizeof_total_bytes, NULL);
  108.         return totalBytes;
  109.     }
  110.     struct InetError: public std::runtime_error
  111.     {
  112.         InetError(const std::string& what): std::runtime_error(what) {}
  113.     };
  114. private:
  115.     // We test results from a number of different MS functions with different
  116.     // return types -- but the common characteristic is that 0 (i.e. (! result))
  117.     // means an error of some kind.
  118.     template <typename RESULT>
  119.     static RESULT check(const std::string& desc, RESULT result)
  120.     {
  121.         if (result)
  122.         {
  123.             // success, show caller
  124.             return result;
  125.         }
  126.         DWORD err = GetLastError();
  127.         std::ostringstream out;
  128.         out << desc << " Failed: " << err;
  129.         DEBUG(out.str());
  130.         throw InetError(out.str());
  131.     }
  132.     HINTERNET openUrl(const std::wstring& uri) const;
  133.     HINTERNET openInet() const;
  134.     HINTERNET mInet, mDownload;
  135. };
  136. HINTERNET Fetcher::openInet() const
  137. {
  138.     DEBUG("Calling InternetOpen");
  139.     // Init wininet subsystem
  140.     return check("InternetOpen",
  141.                  InternetOpen(L"LindenUpdater", INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0));
  142. }
  143. HINTERNET Fetcher::openUrl(const std::wstring& uri) const
  144. {
  145.     DEBUG("Calling InternetOpenUrl: " << wchars_to_utf8chars(uri.c_str()));
  146.     return check("InternetOpenUrl",
  147.                  InternetOpenUrl(mInet, uri.c_str(), NULL, 0, INTERNET_FLAG_NEED_FILE, NULL));
  148. }
  149. unsigned long Fetcher::read(char* buffer, size_t bufflen) const
  150. {
  151.     unsigned long bytes_read = 0;
  152.     DEBUG("Calling InternetReadFile");
  153.     check("InternetReadFile",
  154.           InternetReadFile(mDownload, buffer, bufflen, &bytes_read));
  155.     return bytes_read;
  156. }
  157. int WINAPI get_url_into_file(const std::wstring& uri, const std::string& path, int *cancelled)
  158. {
  159. int success = FALSE;
  160. *cancelled = FALSE;
  161.     DEBUG("Opening '" << path << "'");
  162. FILE* fp = fopen(path.c_str(), "wb"); /* Flawfinder: ignore */
  163. if (!fp)
  164. {
  165.         DEBUG("Failed to open '" << path << "'");
  166. return success;
  167. }
  168.     // Note, ctor can throw, since it uses check() function.
  169.     Fetcher fetcher(uri);
  170.     gTotalBytes = fetcher.getTotalBytes();
  171. /*==========================================================================*|
  172.     // nobody uses total_bytes?!? What's this doing here?
  173. DWORD total_bytes = 0;
  174. success = check("InternetQueryDataAvailable",
  175.                     InternetQueryDataAvailable(hdownload, &total_bytes, 0, 0));
  176. |*==========================================================================*/
  177. success = FALSE;
  178. while(!success && !(*cancelled))
  179. {
  180.         char data[BUFSIZE]; /* Flawfinder: ignore */
  181.         unsigned long bytes_read = fetcher.read(data, sizeof(data));
  182. if (!bytes_read)
  183. {
  184.             DEBUG("InternetReadFile Read " << bytes_read << " bytes.");
  185. }
  186.         DEBUG("Reading Data, bytes_read = " << bytes_read);
  187. if (bytes_read == 0)
  188. {
  189. // If InternetFileRead returns TRUE AND bytes_read == 0
  190. // we've successfully downloaded the entire file
  191. wsprintf(gProgress, L"Download complete.");
  192. success = TRUE;
  193. }
  194. else
  195. {
  196. // write what we've got, then continue
  197. fwrite(data, sizeof(char), bytes_read, fp);
  198. gTotalBytesRead += int(bytes_read);
  199. if (gTotalBytes != -1)
  200. wsprintf(gProgress, L"Downloaded: %d%%", 100 * gTotalBytesRead / gTotalBytes);
  201. else
  202. wsprintf(gProgress, L"Downloaded: %dK", gTotalBytesRead / 1024);
  203. }
  204.         DEBUG("Calling InvalidateRect");
  205. // Mark the window as needing redraw (of the whole thing)
  206. InvalidateRect(gWindow, NULL, TRUE);
  207. // Do the redraw
  208.         DEBUG("Calling UpdateWindow");
  209. UpdateWindow(gWindow);
  210.         DEBUG("Calling PeekMessage");
  211. MSG msg;
  212. while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
  213. {
  214. TranslateMessage(&msg);
  215. DispatchMessage(&msg);
  216. if (msg.message == WM_QUIT)
  217. {
  218. // bail out, user cancelled
  219. *cancelled = TRUE;
  220. }
  221. }
  222. }
  223. fclose(fp);
  224. return success;
  225. }
  226. LRESULT CALLBACK WinProc(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam)
  227. {
  228. HDC hdc; // Drawing context
  229. PAINTSTRUCT ps;
  230. switch(message)
  231. {
  232. case WM_PAINT:
  233. {
  234. hdc = BeginPaint(hwnd, &ps);
  235. RECT rect;
  236. GetClientRect(hwnd, &rect);
  237. DrawText(hdc, gProgress, -1, &rect, 
  238. DT_SINGLELINE | DT_CENTER | DT_VCENTER);
  239. EndPaint(hwnd, &ps);
  240. return 0;
  241. }
  242. case WM_CLOSE:
  243. case WM_DESTROY:
  244. // Get out of full screen
  245. // full_screen_mode(false);
  246. PostQuitMessage(0);
  247. return 0;
  248. }
  249. return DefWindowProc(hwnd, message, wparam, lparam);
  250. }
  251. #define win_class_name L"FullScreen"
  252. int parse_args(int argc, char **argv)
  253. {
  254. int j;
  255. for (j = 1; j < argc; j++) 
  256. {
  257. if ((!strcmp(argv[j], "-url")) && (++j < argc)) 
  258. {
  259. gUpdateURL = argv[j];
  260. }
  261. }
  262. // If nothing was set, let the caller know.
  263. if (!gUpdateURL)
  264. {
  265. return 1;
  266. }
  267. return 0;
  268. }
  269. int WINAPI
  270. WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd)
  271. {
  272. // Parse the command line.
  273. LPSTR cmd_line_including_exe_name = GetCommandLineA();
  274. const int MAX_ARGS = 100;
  275. int argc = 0;
  276. char* argv[MAX_ARGS]; /* Flawfinder: ignore */
  277. #if _DEBUG
  278. logfile.open("updater.log", std::ios_base::out);
  279.     DEBUG("Parsing command arguments");
  280. #endif
  281. char *token = NULL;
  282. if( cmd_line_including_exe_name[0] == '"' )
  283. {
  284. // Exe name is enclosed in quotes
  285. token = strtok( cmd_line_including_exe_name, """ );
  286. argv[argc++] = token;
  287. token = strtok( NULL, " t," );
  288. }
  289. else
  290. {
  291. // Exe name is not enclosed in quotes
  292. token = strtok( cmd_line_including_exe_name, " t," );
  293. }
  294. while( (token != NULL) && (argc < MAX_ARGS) )
  295. {
  296. argv[argc++] = token;
  297. /* Get next token: */
  298. if (*(token + strlen(token) + 1) == '"') /* Flawfinder: ignore */
  299. {
  300. token = strtok( NULL, """);
  301. }
  302. else
  303. {
  304. token = strtok( NULL, " t," );
  305. }
  306. }
  307. gUpdateURL = NULL;
  308. /////////////////////////////////////////
  309. //
  310. // Process command line arguments
  311. //
  312.     DEBUG("Processing command arguments");
  313. //
  314. // Parse the command line arguments
  315. //
  316. int parse_args_result = parse_args(argc, argv);
  317. WNDCLASSEX wndclassex = { 0 };
  318. //DEVMODE dev_mode = { 0 };
  319. const int WINDOW_WIDTH = 250;
  320. const int WINDOW_HEIGHT = 100;
  321. wsprintf(gProgress, L"Connecting...");
  322. /* Init the WNDCLASSEX */
  323. wndclassex.cbSize = sizeof(WNDCLASSEX);
  324. wndclassex.style = CS_HREDRAW | CS_VREDRAW;
  325. wndclassex.hInstance = hInstance;
  326. wndclassex.lpfnWndProc = WinProc;
  327. wndclassex.hbrBackground = (HBRUSH) GetStockObject(WHITE_BRUSH);
  328. wndclassex.lpszClassName = win_class_name;
  329. RegisterClassEx(&wndclassex);
  330. // Get the size of the screen
  331. //EnumDisplaySettings(NULL, ENUM_CURRENT_SETTINGS, &dev_mode);
  332. gWindow = CreateWindowEx(NULL, win_class_name, 
  333. L"Second Life Updater",
  334. WS_OVERLAPPEDWINDOW, 
  335. CW_USEDEFAULT, 
  336. CW_USEDEFAULT, 
  337. WINDOW_WIDTH, 
  338. WINDOW_HEIGHT,
  339. NULL, NULL, hInstance, NULL);
  340. ShowWindow(gWindow, nShowCmd);
  341. UpdateWindow(gWindow);
  342. if (parse_args_result)
  343. {
  344. MessageBox(gWindow, 
  345. L"Usage: updater -url <url> [-name <window_title>] [-program <program_name>] [-silent]",
  346. L"Usage", MB_OK);
  347. return parse_args_result;
  348. }
  349. // Did we get a userserver to work with?
  350. if (!gUpdateURL)
  351. {
  352. MessageBox(gWindow, L"Please specify the download url from the command line",
  353. L"Error", MB_OK);
  354. return 1;
  355. }
  356. // Can't feed GetTempPath into GetTempFile directly
  357. char temp_path[MAX_PATH]; /* Flawfinder: ignore */
  358. if (0 == GetTempPathA(sizeof(temp_path), temp_path))
  359. {
  360. MessageBox(gWindow, L"Problem with GetTempPath()",
  361. L"Error", MB_OK);
  362. return 1;
  363. }
  364.     std::string update_exec_path(temp_path);
  365. update_exec_path.append("Second_Life_Updater.exe");
  366. WCHAR update_uri[4096];
  367.     mbstowcs(update_uri, gUpdateURL, sizeof(update_uri));
  368. int success = 0;
  369. int cancelled = 0;
  370. // Actually do the download
  371.     try
  372.     {
  373.         DEBUG("Calling get_url_into_file");
  374.         success = get_url_into_file(update_uri, update_exec_path, &cancelled);
  375.     }
  376.     catch (const Fetcher::InetError& e)
  377.     {
  378.         (void)e;
  379.         success = FALSE;
  380.         DEBUG("Caught: " << e.what());
  381.     }
  382. // WinInet can't tell us if we got a 404 or not.  Therefor, we check
  383. // for the size of the downloaded file, and assume that our installer
  384. // will always be greater than 1MB.
  385. if (gTotalBytesRead < (1024 * 1024) && ! cancelled)
  386. {
  387. MessageBox(gWindow,
  388. L"The Second Life auto-update has failed.n"
  389. L"The problem may be caused by other software installed n"
  390. L"on your computer, such as a firewall.n"
  391. L"Please visit http://secondlife.com/download/ n"
  392. L"to download the latest version of Second Life.n",
  393. NULL, MB_OK);
  394. return 1;
  395. }
  396. if (cancelled)
  397. {
  398. // silently exit
  399. return 0;
  400. }
  401. if (!success)
  402. {
  403. MessageBox(gWindow, 
  404. L"Second Life download failed.n"
  405. L"Please try again later.", 
  406. NULL, MB_OK);
  407. return 1;
  408. }
  409. // TODO: Make updates silent (with /S to NSIS)
  410. //char params[256]; /* Flawfinder: ignore */
  411. //sprintf(params, "/S"); /* Flawfinder: ignore */
  412. //MessageBox(gWindow, 
  413. // L"Updating Second Life.nnSecond Life will automatically start once the update is complete.  This may take a minute...",
  414. // L"Download Complete",
  415. // MB_OK);
  416. /*==========================================================================*|
  417.     // DEV-31680: ShellExecuteA() causes McAfee-GW-Edition and AntiVir
  418.     // scanners to flag this executable as a probable virus vector.
  419.     // Less than or equal to 32 means failure
  420. if (32 >= (int) ShellExecuteA(gWindow, "open", update_exec_path.c_str(), NULL, 
  421. "C:\", SW_SHOWDEFAULT))
  422. |*==========================================================================*/
  423.     // from http://msdn.microsoft.com/en-us/library/ms682512(VS.85).aspx
  424.     STARTUPINFOA si;
  425.     PROCESS_INFORMATION pi;
  426.     ZeroMemory(&si, sizeof(si));
  427.     si.cb = sizeof(si);
  428.     ZeroMemory(&pi, sizeof(pi));
  429.     if (! CreateProcessA(update_exec_path.c_str(), // executable file
  430.                   NULL,                            // command line
  431.                   NULL,             // process cannot be inherited
  432.                   NULL,             // thread cannot be inherited
  433.                   FALSE,            // do not inherit existing handles
  434.                   0,                // process creation flags
  435.                   NULL,             // inherit parent's environment
  436.                   NULL,             // inherit parent's current dir
  437.                   &si,              // STARTUPINFO
  438.                   &pi))             // PROCESS_INFORMATION
  439. {
  440. MessageBox(gWindow, L"Update failed.  Please try again later.", NULL, MB_OK);
  441. return 1;
  442. }
  443. // Give installer some time to open a window
  444. Sleep(1000);
  445. return 0;
  446. }