summaryrefslogtreecommitdiff
path: root/indra/win_updater/updater.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'indra/win_updater/updater.cpp')
-rw-r--r--indra/win_updater/updater.cpp527
1 files changed, 527 insertions, 0 deletions
diff --git a/indra/win_updater/updater.cpp b/indra/win_updater/updater.cpp
new file mode 100644
index 0000000000..99680984d1
--- /dev/null
+++ b/indra/win_updater/updater.cpp
@@ -0,0 +1,527 @@
+/**
+ * @file updater.cpp
+ * @brief Windows auto-updater
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+//
+// Usage: updater -userserver <server> [-name <window_title>] [-program <program_name>] [-silent]
+//
+
+#include <windows.h>
+#include <wininet.h>
+
+#include <stdio.h>
+#include "llfile.h"
+
+#define BUFSIZE 8192
+
+int gTotalBytesRead = 0;
+HWND gWindow = NULL;
+WCHAR gProgress[256];
+char* gUserServer;
+char* gProgramName;
+char* gProductName;
+bool gIsSilent;
+
+#if _DEBUG
+FILE* logfile = 0;
+#endif
+
+char* wchars_to_utf8chars(WCHAR* in_chars)
+{
+ int tlen = 0;
+ WCHAR* twc = in_chars;
+ while (*twc++ != 0)
+ {
+ tlen++;
+ }
+ char* outchars = new char[tlen];
+ char* res = outchars;
+ for (int i=0; i<tlen; i++)
+ {
+ int cur_char = (int)(*in_chars++);
+ if (cur_char < 0x80)
+ {
+ *outchars++ = (char)cur_char;
+ }
+ else
+ {
+ *outchars++ = '?';
+ }
+ }
+ *outchars = 0;
+ return res;
+}
+
+int WINAPI get_url_into_file(WCHAR *uri, char *path, int *cancelled)
+{
+ int success = FALSE;
+ *cancelled = FALSE;
+
+ HINTERNET hinet, hdownload;
+ char data[BUFSIZE];
+ unsigned long bytes_read;
+
+#if _DEBUG
+ fprintf(logfile,"Opening '%s'\n",path);
+ fflush(logfile);
+#endif
+
+ FILE *fp = fopen(path, "wb");
+
+ 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)
+ {
+ 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;
+ }
+
+ 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\n",total_bytes);
+ fflush(logfile);
+#endif
+ return success;
+ }
+
+ success = FALSE;
+ while(!success && !(*cancelled))
+ {
+ MSG msg;
+
+#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);
+ }
+#endif
+
+ if (!data)
+ {
+#if _DEBUG
+ fprintf(logfile,"InternetReadFile Returned NULL data, bytes_read = %d.\n",bytes_read);
+ fflush(logfile);
+#endif
+ // ...an error occurred
+ return FALSE;
+ }
+
+#if _DEBUG
+ fprintf(logfile,"Reading Data, bytes_read = %d\n",bytes_read);
+ fflush(logfile);
+#endif
+
+ if (bytes_read == 0)
+ {
+ // If InternetFileRead returns TRUE AND bytes_read == 0
+ // we've successfully downloaded the entire file
+ wsprintf(gProgress, L"Download complete.");
+ success = TRUE;
+ }
+ else
+ {
+ // write what we've got, then continue
+ fwrite(data, sizeof(char), bytes_read, fp);
+
+ gTotalBytesRead += int(bytes_read);
+
+ wsprintf(gProgress, L"Downloaded: %dK", gTotalBytesRead / 1024);
+ }
+
+#if _DEBUG
+ fprintf(logfile,"Calling InvalidateRect\n");
+ fflush(logfile);
+#endif
+
+ // 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
+ UpdateWindow(gWindow);
+
+#if _DEBUG
+ fprintf(logfile,"Calling PeekMessage\n");
+ fflush(logfile);
+#endif
+ if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
+ {
+ TranslateMessage(&msg);
+ DispatchMessage(&msg);
+
+ if (msg.message == WM_QUIT)
+ {
+ // bail out, user cancelled
+ *cancelled = TRUE;
+ }
+ }
+ }
+
+#if _DEBUG
+ fprintf(logfile,"Calling InternetCloseHandle\n");
+ fclose(logfile);
+#endif
+
+ fclose(fp);
+ InternetCloseHandle(hdownload);
+ InternetCloseHandle(hinet);
+
+ return success;
+}
+
+LRESULT CALLBACK WinProc(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam)
+{
+ HDC hdc; // Drawing context
+ PAINTSTRUCT ps;
+
+ switch(message)
+ {
+ case WM_PAINT:
+ {
+ hdc = BeginPaint(hwnd, &ps);
+
+ RECT rect;
+ GetClientRect(hwnd, &rect);
+ DrawText(hdc, gProgress, -1, &rect,
+ DT_SINGLELINE | DT_CENTER | DT_VCENTER);
+
+ EndPaint(hwnd, &ps);
+ return 0;
+ }
+ case WM_CLOSE:
+ case WM_DESTROY:
+ // Get out of full screen
+ // full_screen_mode(false);
+ PostQuitMessage(0);
+ return 0;
+ }
+ return DefWindowProc(hwnd, message, wparam, lparam);
+}
+
+#define win_class_name L"FullScreen"
+#define UPDATE_URIBASE L"http://secondlife.com/update.php?userserver="
+
+int parse_args(int argc, char **argv)
+{
+ // Check for old-type arguments.
+ if (2 == argc)
+ {
+ gUserServer = argv[1];
+ return 0;
+ }
+
+ int j;
+
+ for (j = 1; j < argc; j++)
+ {
+ if ((!strcmp(argv[j], "-userserver")) && (++j < argc))
+ {
+ gUserServer = argv[j];
+ }
+ else if ((!strcmp(argv[j], "-name")) && (++j < argc))
+ {
+ gProductName = 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 (!gUserServer && !gProductName && !gProgramName && !gIsSilent)
+ {
+ return 1;
+ }
+ return 0;
+}
+
+int WINAPI
+WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd)
+{
+ // Parse the command line.
+ LPSTR cmd_line_including_exe_name = GetCommandLineA();
+
+ const int MAX_ARGS = 100;
+ int argc = 0;
+ char *argv[MAX_ARGS];
+
+#if _DEBUG
+ logfile = _wfopen(TEXT("updater.log"),TEXT("wt"));
+ fprintf(logfile,"Parsing command arguments\n");
+ fflush(logfile);
+#endif
+
+ char *token = NULL;
+ if( cmd_line_including_exe_name[0] == '\"' )
+ {
+ // Exe name is enclosed in quotes
+ token = strtok( cmd_line_including_exe_name, "\"" );
+ argv[argc++] = token;
+ token = strtok( NULL, " \t," );
+ }
+ else
+ {
+ // Exe name is not enclosed in quotes
+ token = strtok( cmd_line_including_exe_name, " \t," );
+ }
+
+ while( (token != NULL) && (argc < MAX_ARGS) )
+ {
+ argv[argc++] = token;
+ /* Get next token: */
+ if (*(token + strlen(token) + 1) == '\"')
+ {
+ token = strtok( NULL, "\"");
+ }
+ else
+ {
+ token = strtok( NULL, " \t," );
+ }
+ }
+
+ gUserServer = NULL;
+ gProgramName = NULL;
+ gProductName = NULL;
+ gIsSilent = false;
+
+ /////////////////////////////////////////
+ //
+ // Process command line arguments
+ //
+
+#if _DEBUG
+ fprintf(logfile,"Processing command arguments\n");
+ fflush(logfile);
+#endif
+
+ //
+ // 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");
+ }
+ else
+ {
+ mbstowcs(window_title, "Second Life Updater", 2048);
+ }
+
+ WNDCLASSEX wndclassex = { 0 };
+ DEVMODE dev_mode = { 0 };
+ char update_exec_path[MAX_PATH];
+ char *ptr;
+ WCHAR update_uri[4096];
+
+ const int WINDOW_WIDTH = 250;
+ const int WINDOW_HEIGHT = 100;
+
+ wsprintf(gProgress, L"Connecting...");
+
+ /* Init the WNDCLASSEX */
+ wndclassex.cbSize = sizeof(WNDCLASSEX);
+ wndclassex.style = CS_HREDRAW | CS_VREDRAW;
+ wndclassex.hInstance = hInstance;
+ wndclassex.lpfnWndProc = WinProc;
+ wndclassex.hbrBackground = (HBRUSH) GetStockObject(WHITE_BRUSH);
+ wndclassex.lpszClassName = win_class_name;
+
+ RegisterClassEx(&wndclassex);
+
+ // Get the size of the screen
+ EnumDisplaySettings(NULL, ENUM_CURRENT_SETTINGS, &dev_mode);
+
+ gWindow = CreateWindowEx(NULL, win_class_name,
+ window_title,
+ WS_OVERLAPPEDWINDOW,
+ CW_USEDEFAULT,
+ CW_USEDEFAULT,
+ WINDOW_WIDTH,
+ WINDOW_HEIGHT,
+ NULL, NULL, hInstance, NULL);
+
+ ShowWindow(gWindow, nShowCmd);
+ UpdateWindow(gWindow);
+
+ if (parse_args_result)
+ {
+ MessageBox(gWindow,
+ L"Usage: updater -userserver <server> [-name <window_title>] [-program <program_name>] [-silent]",
+ L"Usage", MB_OK);
+ return parse_args_result;
+ }
+
+ // Did we get a userserver to work with?
+ if (!gUserServer)
+ {
+ MessageBox(gWindow, L"Please specify the IP address of the userserver on the command line",
+ L"Error", MB_OK);
+ return 1;
+ }
+
+ // Can't feed GetTempPath into GetTempFile directly
+ if (0 == GetTempPathA(MAX_PATH - 14, update_exec_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;
+ wcscpy(update_uri, UPDATE_URIBASE);
+ WCHAR wcmdline[2048];
+ mbstowcs(wcmdline, gUserServer, 2048);
+ wcscat(update_uri, wcmdline);
+
+ int success;
+ int cancelled;
+
+ // 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);
+
+ // 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
+ // will always be greater than 1MB.
+ if (gTotalBytesRead < (1024 * 1024) && ! cancelled)
+ {
+ MessageBox(gWindow,
+ L"The Second Life auto-update has failed.\n"
+ L"The problem may be caused by other software installed \n"
+ L"on your computer, such as a firewall.\n"
+ L"Please visit http://secondlife.com/download/ \n"
+ L"to download the latest version of Second Life.\n",
+ NULL, MB_OK);
+ return 1;
+ }
+
+ if (cancelled)
+ {
+ // silently exit
+ return 0;
+ }
+
+ if (!success)
+ {
+ MessageBox(gWindow,
+ L"Second Life download failed.\n"
+ L"Please try again later.",
+ NULL, MB_OK);
+ return 1;
+ }
+
+ // Construct some parameters.
+ char params[2048];
+ if (gIsSilent && gProgramName)
+ {
+ sprintf(params, "/S /P=\"%s\"", gProgramName);
+ }
+ else if (gProgramName)
+ {
+ sprintf(params, "/P=\"%s\"", gProgramName);
+ }
+ else if (gIsSilent)
+ {
+ sprintf(params, "/S");
+ }
+ else
+ {
+ params[0] = '\0';
+ }
+
+ if (32 >= (int) ShellExecuteA(gWindow, "open", update_exec_path, params,
+ "C:\\", SW_SHOWDEFAULT))
+ {
+ // No shit: less than or equal to 32 means failure
+ MessageBox(gWindow, L"ShellExecute 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);
+ }
+
+ return 0;
+}