PageRenderTime 45ms CodeModel.GetById 24ms RepoModel.GetById 1ms app.codeStats 0ms

/indra/win_updater/updater.cpp

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