1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/toolkit/xre/nsWindowsRestart.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,300 @@ 1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.6 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.7 + 1.8 +// This file is not build directly. Instead, it is included in multiple 1.9 +// shared objects. 1.10 + 1.11 +#ifdef nsWindowsRestart_cpp 1.12 +#error "nsWindowsRestart.cpp is not a header file, and must only be included once." 1.13 +#else 1.14 +#define nsWindowsRestart_cpp 1.15 +#endif 1.16 + 1.17 +#include "nsUTF8Utils.h" 1.18 + 1.19 +#include <shellapi.h> 1.20 + 1.21 +// Needed for CreateEnvironmentBlock 1.22 +#include <userenv.h> 1.23 +#pragma comment(lib, "userenv.lib") 1.24 + 1.25 +/** 1.26 + * Get the length that the string will take and takes into account the 1.27 + * additional length if the string needs to be quoted and if characters need to 1.28 + * be escaped. 1.29 + */ 1.30 +static int ArgStrLen(const wchar_t *s) 1.31 +{ 1.32 + int backslashes = 0; 1.33 + int i = wcslen(s); 1.34 + BOOL hasDoubleQuote = wcschr(s, L'"') != nullptr; 1.35 + // Only add doublequotes if the string contains a space or a tab 1.36 + BOOL addDoubleQuotes = wcspbrk(s, L" \t") != nullptr; 1.37 + 1.38 + if (addDoubleQuotes) { 1.39 + i += 2; // initial and final duoblequote 1.40 + } 1.41 + 1.42 + if (hasDoubleQuote) { 1.43 + while (*s) { 1.44 + if (*s == '\\') { 1.45 + ++backslashes; 1.46 + } else { 1.47 + if (*s == '"') { 1.48 + // Escape the doublequote and all backslashes preceding the doublequote 1.49 + i += backslashes + 1; 1.50 + } 1.51 + 1.52 + backslashes = 0; 1.53 + } 1.54 + 1.55 + ++s; 1.56 + } 1.57 + } 1.58 + 1.59 + return i; 1.60 +} 1.61 + 1.62 +/** 1.63 + * Copy string "s" to string "d", quoting the argument as appropriate and 1.64 + * escaping doublequotes along with any backslashes that immediately precede 1.65 + * doublequotes. 1.66 + * The CRT parses this to retrieve the original argc/argv that we meant, 1.67 + * see STDARGV.C in the MSVC CRT sources. 1.68 + * 1.69 + * @return the end of the string 1.70 + */ 1.71 +static wchar_t* ArgToString(wchar_t *d, const wchar_t *s) 1.72 +{ 1.73 + int backslashes = 0; 1.74 + BOOL hasDoubleQuote = wcschr(s, L'"') != nullptr; 1.75 + // Only add doublequotes if the string contains a space or a tab 1.76 + BOOL addDoubleQuotes = wcspbrk(s, L" \t") != nullptr; 1.77 + 1.78 + if (addDoubleQuotes) { 1.79 + *d = '"'; // initial doublequote 1.80 + ++d; 1.81 + } 1.82 + 1.83 + if (hasDoubleQuote) { 1.84 + int i; 1.85 + while (*s) { 1.86 + if (*s == '\\') { 1.87 + ++backslashes; 1.88 + } else { 1.89 + if (*s == '"') { 1.90 + // Escape the doublequote and all backslashes preceding the doublequote 1.91 + for (i = 0; i <= backslashes; ++i) { 1.92 + *d = '\\'; 1.93 + ++d; 1.94 + } 1.95 + } 1.96 + 1.97 + backslashes = 0; 1.98 + } 1.99 + 1.100 + *d = *s; 1.101 + ++d; ++s; 1.102 + } 1.103 + } else { 1.104 + wcscpy(d, s); 1.105 + d += wcslen(s); 1.106 + } 1.107 + 1.108 + if (addDoubleQuotes) { 1.109 + *d = '"'; // final doublequote 1.110 + ++d; 1.111 + } 1.112 + 1.113 + return d; 1.114 +} 1.115 + 1.116 +/** 1.117 + * Creates a command line from a list of arguments. The returned 1.118 + * string is allocated with "malloc" and should be "free"d. 1.119 + * 1.120 + * argv is UTF8 1.121 + */ 1.122 +wchar_t* 1.123 +MakeCommandLine(int argc, wchar_t **argv) 1.124 +{ 1.125 + int i; 1.126 + int len = 0; 1.127 + 1.128 + // The + 1 of the last argument handles the allocation for null termination 1.129 + for (i = 0; i < argc; ++i) 1.130 + len += ArgStrLen(argv[i]) + 1; 1.131 + 1.132 + // Protect against callers that pass 0 arguments 1.133 + if (len == 0) 1.134 + len = 1; 1.135 + 1.136 + wchar_t *s = (wchar_t*) malloc(len * sizeof(wchar_t)); 1.137 + if (!s) 1.138 + return nullptr; 1.139 + 1.140 + wchar_t *c = s; 1.141 + for (i = 0; i < argc; ++i) { 1.142 + c = ArgToString(c, argv[i]); 1.143 + if (i + 1 != argc) { 1.144 + *c = ' '; 1.145 + ++c; 1.146 + } 1.147 + } 1.148 + 1.149 + *c = '\0'; 1.150 + 1.151 + return s; 1.152 +} 1.153 + 1.154 +/** 1.155 + * Convert UTF8 to UTF16 without using the normal XPCOM goop, which we 1.156 + * can't link to updater.exe. 1.157 + */ 1.158 +static char16_t* 1.159 +AllocConvertUTF8toUTF16(const char *arg) 1.160 +{ 1.161 + // UTF16 can't be longer in units than UTF8 1.162 + int len = strlen(arg); 1.163 + char16_t *s = new char16_t[(len + 1) * sizeof(char16_t)]; 1.164 + if (!s) 1.165 + return nullptr; 1.166 + 1.167 + ConvertUTF8toUTF16 convert(s); 1.168 + convert.write(arg, len); 1.169 + convert.write_terminator(); 1.170 + return s; 1.171 +} 1.172 + 1.173 +static void 1.174 +FreeAllocStrings(int argc, wchar_t **argv) 1.175 +{ 1.176 + while (argc) { 1.177 + --argc; 1.178 + delete [] argv[argc]; 1.179 + } 1.180 + 1.181 + delete [] argv; 1.182 +} 1.183 + 1.184 + 1.185 + 1.186 +/** 1.187 + * Launch a child process with the specified arguments. 1.188 + * @note argv[0] is ignored 1.189 + * @note The form of this function that takes char **argv expects UTF-8 1.190 + */ 1.191 + 1.192 +BOOL 1.193 +WinLaunchChild(const wchar_t *exePath, 1.194 + int argc, wchar_t **argv, 1.195 + HANDLE userToken = nullptr, 1.196 + HANDLE *hProcess = nullptr); 1.197 + 1.198 +BOOL 1.199 +WinLaunchChild(const wchar_t *exePath, 1.200 + int argc, char **argv, 1.201 + HANDLE userToken, 1.202 + HANDLE *hProcess) 1.203 +{ 1.204 + wchar_t** argvConverted = new wchar_t*[argc]; 1.205 + if (!argvConverted) 1.206 + return FALSE; 1.207 + 1.208 + for (int i = 0; i < argc; ++i) { 1.209 + argvConverted[i] = reinterpret_cast<wchar_t*>(AllocConvertUTF8toUTF16(argv[i])); 1.210 + if (!argvConverted[i]) { 1.211 + FreeAllocStrings(i, argvConverted); 1.212 + return FALSE; 1.213 + } 1.214 + } 1.215 + 1.216 + BOOL ok = WinLaunchChild(exePath, argc, argvConverted, userToken, hProcess); 1.217 + FreeAllocStrings(argc, argvConverted); 1.218 + return ok; 1.219 +} 1.220 + 1.221 +BOOL 1.222 +WinLaunchChild(const wchar_t *exePath, 1.223 + int argc, 1.224 + wchar_t **argv, 1.225 + HANDLE userToken, 1.226 + HANDLE *hProcess) 1.227 +{ 1.228 + wchar_t *cl; 1.229 + BOOL ok; 1.230 + 1.231 + cl = MakeCommandLine(argc, argv); 1.232 + if (!cl) { 1.233 + return FALSE; 1.234 + } 1.235 + 1.236 + STARTUPINFOW si = {0}; 1.237 + si.cb = sizeof(STARTUPINFOW); 1.238 + si.lpDesktop = L"winsta0\\Default"; 1.239 + PROCESS_INFORMATION pi = {0}; 1.240 + 1.241 + if (userToken == nullptr) { 1.242 + ok = CreateProcessW(exePath, 1.243 + cl, 1.244 + nullptr, // no special security attributes 1.245 + nullptr, // no special thread attributes 1.246 + FALSE, // don't inherit filehandles 1.247 + 0, // creation flags 1.248 + nullptr, // inherit my environment 1.249 + nullptr, // use my current directory 1.250 + &si, 1.251 + &pi); 1.252 + } else { 1.253 + // Create an environment block for the process we're about to start using 1.254 + // the user's token. 1.255 + LPVOID environmentBlock = nullptr; 1.256 + if (!CreateEnvironmentBlock(&environmentBlock, userToken, TRUE)) { 1.257 + environmentBlock = nullptr; 1.258 + } 1.259 + 1.260 + ok = CreateProcessAsUserW(userToken, 1.261 + exePath, 1.262 + cl, 1.263 + nullptr, // no special security attributes 1.264 + nullptr, // no special thread attributes 1.265 + FALSE, // don't inherit filehandles 1.266 + 0, // creation flags 1.267 + environmentBlock, 1.268 + nullptr, // use my current directory 1.269 + &si, 1.270 + &pi); 1.271 + 1.272 + if (environmentBlock) { 1.273 + DestroyEnvironmentBlock(environmentBlock); 1.274 + } 1.275 + } 1.276 + 1.277 + if (ok) { 1.278 + if (hProcess) { 1.279 + *hProcess = pi.hProcess; // the caller now owns the HANDLE 1.280 + } else { 1.281 + CloseHandle(pi.hProcess); 1.282 + } 1.283 + CloseHandle(pi.hThread); 1.284 + } else { 1.285 + LPVOID lpMsgBuf = nullptr; 1.286 + FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | 1.287 + FORMAT_MESSAGE_FROM_SYSTEM | 1.288 + FORMAT_MESSAGE_IGNORE_INSERTS, 1.289 + nullptr, 1.290 + GetLastError(), 1.291 + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), 1.292 + (LPTSTR) &lpMsgBuf, 1.293 + 0, 1.294 + nullptr); 1.295 + wprintf(L"Error restarting: %s\n", lpMsgBuf ? lpMsgBuf : L"(null)"); 1.296 + if (lpMsgBuf) 1.297 + LocalFree(lpMsgBuf); 1.298 + } 1.299 + 1.300 + free(cl); 1.301 + 1.302 + return ok; 1.303 +}