1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/toolkit/mozapps/update/common/updatehelper.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,827 @@ 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 +#ifdef MOZ_METRO 1.9 +// Needed for COM calls to launch Metro applications 1.10 +#undef WINVER 1.11 +#undef _WIN32_WINNT 1.12 +#define WINVER 0x602 1.13 +#define _WIN32_WINNT 0x602 1.14 +#include <objbase.h> 1.15 +#include <shobjidl.h> 1.16 +#pragma comment(lib, "ole32.lib") 1.17 +#endif 1.18 + 1.19 +#include <windows.h> 1.20 + 1.21 +// Needed for CreateToolhelp32Snapshot 1.22 +#include <tlhelp32.h> 1.23 +#ifndef ONLY_SERVICE_LAUNCHING 1.24 + 1.25 +#include <stdio.h> 1.26 +#include "shlobj.h" 1.27 +#include "updatehelper.h" 1.28 +#include "uachelper.h" 1.29 +#include "pathhash.h" 1.30 +#include "mozilla/Scoped.h" 1.31 + 1.32 +// Needed for PathAppendW 1.33 +#include <shlwapi.h> 1.34 + 1.35 +WCHAR* MakeCommandLine(int argc, WCHAR **argv); 1.36 +BOOL PathAppendSafe(LPWSTR base, LPCWSTR extra); 1.37 + 1.38 +/** 1.39 + * Obtains the path of a file in the same directory as the specified file. 1.40 + * 1.41 + * @param destinationBuffer A buffer of size MAX_PATH + 1 to store the result. 1.42 + * @param siblingFIlePath The path of another file in the same directory 1.43 + * @param newFileName The filename of another file in the same directory 1.44 + * @return TRUE if successful 1.45 + */ 1.46 +BOOL 1.47 +PathGetSiblingFilePath(LPWSTR destinationBuffer, 1.48 + LPCWSTR siblingFilePath, 1.49 + LPCWSTR newFileName) 1.50 +{ 1.51 + if (wcslen(siblingFilePath) >= MAX_PATH) { 1.52 + return FALSE; 1.53 + } 1.54 + 1.55 + wcsncpy(destinationBuffer, siblingFilePath, MAX_PATH); 1.56 + if (!PathRemoveFileSpecW(destinationBuffer)) { 1.57 + return FALSE; 1.58 + } 1.59 + 1.60 + if (wcslen(destinationBuffer) + wcslen(newFileName) >= MAX_PATH) { 1.61 + return FALSE; 1.62 + } 1.63 + 1.64 + return PathAppendSafe(destinationBuffer, newFileName); 1.65 +} 1.66 + 1.67 +/** 1.68 + * Launch the post update application as the specified user (helper.exe). 1.69 + * It takes in the path of the callback application to calculate the path 1.70 + * of helper.exe. For service updates this is called from both the system 1.71 + * account and the current user account. 1.72 + * 1.73 + * @param installationDir The path to the callback application binary. 1.74 + * @param updateInfoDir The directory where update info is stored. 1.75 + * @param forceSync If true even if the ini file specifies async, the 1.76 + * process will wait for termination of PostUpdate. 1.77 + * @param userToken The user token to run as, if nullptr the current 1.78 + * user will be used. 1.79 + * @return TRUE if there was no error starting the process. 1.80 + */ 1.81 +BOOL 1.82 +LaunchWinPostProcess(const WCHAR *installationDir, 1.83 + const WCHAR *updateInfoDir, 1.84 + bool forceSync, 1.85 + HANDLE userToken) 1.86 +{ 1.87 + WCHAR workingDirectory[MAX_PATH + 1] = { L'\0' }; 1.88 + wcsncpy(workingDirectory, installationDir, MAX_PATH); 1.89 + 1.90 + // Launch helper.exe to perform post processing (e.g. registry and log file 1.91 + // modifications) for the update. 1.92 + WCHAR inifile[MAX_PATH + 1] = { L'\0' }; 1.93 + wcsncpy(inifile, installationDir, MAX_PATH); 1.94 + if (!PathAppendSafe(inifile, L"updater.ini")) { 1.95 + return FALSE; 1.96 + } 1.97 + 1.98 + WCHAR exefile[MAX_PATH + 1]; 1.99 + WCHAR exearg[MAX_PATH + 1]; 1.100 + WCHAR exeasync[10]; 1.101 + bool async = true; 1.102 + if (!GetPrivateProfileStringW(L"PostUpdateWin", L"ExeRelPath", nullptr, 1.103 + exefile, MAX_PATH + 1, inifile)) { 1.104 + return FALSE; 1.105 + } 1.106 + 1.107 + if (!GetPrivateProfileStringW(L"PostUpdateWin", L"ExeArg", nullptr, exearg, 1.108 + MAX_PATH + 1, inifile)) { 1.109 + return FALSE; 1.110 + } 1.111 + 1.112 + if (!GetPrivateProfileStringW(L"PostUpdateWin", L"ExeAsync", L"TRUE", 1.113 + exeasync, 1.114 + sizeof(exeasync)/sizeof(exeasync[0]), 1.115 + inifile)) { 1.116 + return FALSE; 1.117 + } 1.118 + 1.119 + WCHAR exefullpath[MAX_PATH + 1] = { L'\0' }; 1.120 + wcsncpy(exefullpath, installationDir, MAX_PATH); 1.121 + if (!PathAppendSafe(exefullpath, exefile)) { 1.122 + return false; 1.123 + } 1.124 + 1.125 + WCHAR dlogFile[MAX_PATH + 1]; 1.126 + if (!PathGetSiblingFilePath(dlogFile, exefullpath, L"uninstall.update")) { 1.127 + return FALSE; 1.128 + } 1.129 + 1.130 + WCHAR slogFile[MAX_PATH + 1] = { L'\0' }; 1.131 + wcsncpy(slogFile, updateInfoDir, MAX_PATH); 1.132 + if (!PathAppendSafe(slogFile, L"update.log")) { 1.133 + return FALSE; 1.134 + } 1.135 + 1.136 + WCHAR dummyArg[14] = { L'\0' }; 1.137 + wcsncpy(dummyArg, L"argv0ignored ", sizeof(dummyArg) / sizeof(dummyArg[0]) - 1); 1.138 + 1.139 + size_t len = wcslen(exearg) + wcslen(dummyArg); 1.140 + WCHAR *cmdline = (WCHAR *) malloc((len + 1) * sizeof(WCHAR)); 1.141 + if (!cmdline) { 1.142 + return FALSE; 1.143 + } 1.144 + 1.145 + wcsncpy(cmdline, dummyArg, len); 1.146 + wcscat(cmdline, exearg); 1.147 + 1.148 + if (forceSync || 1.149 + !_wcsnicmp(exeasync, L"false", 6) || 1.150 + !_wcsnicmp(exeasync, L"0", 2)) { 1.151 + async = false; 1.152 + } 1.153 + 1.154 + // We want to launch the post update helper app to update the Windows 1.155 + // registry even if there is a failure with removing the uninstall.update 1.156 + // file or copying the update.log file. 1.157 + CopyFileW(slogFile, dlogFile, false); 1.158 + 1.159 + STARTUPINFOW si = {sizeof(si), 0}; 1.160 + si.lpDesktop = L""; 1.161 + PROCESS_INFORMATION pi = {0}; 1.162 + 1.163 + bool ok; 1.164 + if (userToken) { 1.165 + ok = CreateProcessAsUserW(userToken, 1.166 + exefullpath, 1.167 + cmdline, 1.168 + nullptr, // no special security attributes 1.169 + nullptr, // no special thread attributes 1.170 + false, // don't inherit filehandles 1.171 + 0, // No special process creation flags 1.172 + nullptr, // inherit my environment 1.173 + workingDirectory, 1.174 + &si, 1.175 + &pi); 1.176 + } else { 1.177 + ok = CreateProcessW(exefullpath, 1.178 + cmdline, 1.179 + nullptr, // no special security attributes 1.180 + nullptr, // no special thread attributes 1.181 + false, // don't inherit filehandles 1.182 + 0, // No special process creation flags 1.183 + nullptr, // inherit my environment 1.184 + workingDirectory, 1.185 + &si, 1.186 + &pi); 1.187 + } 1.188 + free(cmdline); 1.189 + if (ok) { 1.190 + if (!async) 1.191 + WaitForSingleObject(pi.hProcess, INFINITE); 1.192 + CloseHandle(pi.hProcess); 1.193 + CloseHandle(pi.hThread); 1.194 + } 1.195 + return ok; 1.196 +} 1.197 + 1.198 +/** 1.199 + * Starts the upgrade process for update of the service if it is 1.200 + * already installed. 1.201 + * 1.202 + * @param installDir the installation directory where 1.203 + * maintenanceservice_installer.exe is located. 1.204 + * @return TRUE if successful 1.205 + */ 1.206 +BOOL 1.207 +StartServiceUpdate(LPCWSTR installDir) 1.208 +{ 1.209 + // Get a handle to the local computer SCM database 1.210 + SC_HANDLE manager = OpenSCManager(nullptr, nullptr, 1.211 + SC_MANAGER_ALL_ACCESS); 1.212 + if (!manager) { 1.213 + return FALSE; 1.214 + } 1.215 + 1.216 + // Open the service 1.217 + SC_HANDLE svc = OpenServiceW(manager, SVC_NAME, 1.218 + SERVICE_ALL_ACCESS); 1.219 + if (!svc) { 1.220 + CloseServiceHandle(manager); 1.221 + return FALSE; 1.222 + } 1.223 + 1.224 + // If we reach here, then the service is installed, so 1.225 + // proceed with upgrading it. 1.226 + 1.227 + CloseServiceHandle(manager); 1.228 + 1.229 + // The service exists and we opened it, get the config bytes needed 1.230 + DWORD bytesNeeded; 1.231 + if (!QueryServiceConfigW(svc, nullptr, 0, &bytesNeeded) && 1.232 + GetLastError() != ERROR_INSUFFICIENT_BUFFER) { 1.233 + CloseServiceHandle(svc); 1.234 + return FALSE; 1.235 + } 1.236 + 1.237 + // Get the service config information, in particular we want the binary 1.238 + // path of the service. 1.239 + mozilla::ScopedDeleteArray<char> serviceConfigBuffer(new char[bytesNeeded]); 1.240 + if (!QueryServiceConfigW(svc, 1.241 + reinterpret_cast<QUERY_SERVICE_CONFIGW*>(serviceConfigBuffer.get()), 1.242 + bytesNeeded, &bytesNeeded)) { 1.243 + CloseServiceHandle(svc); 1.244 + return FALSE; 1.245 + } 1.246 + 1.247 + CloseServiceHandle(svc); 1.248 + 1.249 + QUERY_SERVICE_CONFIGW &serviceConfig = 1.250 + *reinterpret_cast<QUERY_SERVICE_CONFIGW*>(serviceConfigBuffer.get()); 1.251 + 1.252 + PathUnquoteSpacesW(serviceConfig.lpBinaryPathName); 1.253 + 1.254 + // Obtain the temp path of the maintenance service binary 1.255 + WCHAR tmpService[MAX_PATH + 1] = { L'\0' }; 1.256 + if (!PathGetSiblingFilePath(tmpService, serviceConfig.lpBinaryPathName, 1.257 + L"maintenanceservice_tmp.exe")) { 1.258 + return FALSE; 1.259 + } 1.260 + 1.261 + // Get the new maintenance service path from the install dir 1.262 + WCHAR newMaintServicePath[MAX_PATH + 1] = { L'\0' }; 1.263 + wcsncpy(newMaintServicePath, installDir, MAX_PATH); 1.264 + PathAppendSafe(newMaintServicePath, 1.265 + L"maintenanceservice.exe"); 1.266 + 1.267 + // Copy the temp file in alongside the maintenace service. 1.268 + // This is a requirement for maintenance service upgrades. 1.269 + if (!CopyFileW(newMaintServicePath, tmpService, FALSE)) { 1.270 + return FALSE; 1.271 + } 1.272 + 1.273 + // Start the upgrade comparison process 1.274 + STARTUPINFOW si = {0}; 1.275 + si.cb = sizeof(STARTUPINFOW); 1.276 + // No particular desktop because no UI 1.277 + si.lpDesktop = L""; 1.278 + PROCESS_INFORMATION pi = {0}; 1.279 + WCHAR cmdLine[64] = { '\0' }; 1.280 + wcsncpy(cmdLine, L"dummyparam.exe upgrade", 1.281 + sizeof(cmdLine) / sizeof(cmdLine[0]) - 1); 1.282 + BOOL svcUpdateProcessStarted = CreateProcessW(tmpService, 1.283 + cmdLine, 1.284 + nullptr, nullptr, FALSE, 1.285 + 0, 1.286 + nullptr, installDir, &si, &pi); 1.287 + if (svcUpdateProcessStarted) { 1.288 + CloseHandle(pi.hProcess); 1.289 + CloseHandle(pi.hThread); 1.290 + } 1.291 + return svcUpdateProcessStarted; 1.292 +} 1.293 + 1.294 +#endif 1.295 + 1.296 +/** 1.297 + * Executes a maintenance service command 1.298 + * 1.299 + * @param argc The total number of arguments in argv 1.300 + * @param argv An array of null terminated strings to pass to the service, 1.301 + * @return ERROR_SUCCESS if the service command was started. 1.302 + * Less than 16000, a windows system error code from StartServiceW 1.303 + * More than 20000, 20000 + the last state of the service constant if 1.304 + * the last state is something other than stopped. 1.305 + * 17001 if the SCM could not be opened 1.306 + * 17002 if the service could not be opened 1.307 +*/ 1.308 +DWORD 1.309 +StartServiceCommand(int argc, LPCWSTR* argv) 1.310 +{ 1.311 + DWORD lastState = WaitForServiceStop(SVC_NAME, 5); 1.312 + if (lastState != SERVICE_STOPPED) { 1.313 + return 20000 + lastState; 1.314 + } 1.315 + 1.316 + // Get a handle to the SCM database. 1.317 + SC_HANDLE serviceManager = OpenSCManager(nullptr, nullptr, 1.318 + SC_MANAGER_CONNECT | 1.319 + SC_MANAGER_ENUMERATE_SERVICE); 1.320 + if (!serviceManager) { 1.321 + return 17001; 1.322 + } 1.323 + 1.324 + // Get a handle to the service. 1.325 + SC_HANDLE service = OpenServiceW(serviceManager, 1.326 + SVC_NAME, 1.327 + SERVICE_START); 1.328 + if (!service) { 1.329 + CloseServiceHandle(serviceManager); 1.330 + return 17002; 1.331 + } 1.332 + 1.333 + // Wait at most 5 seconds trying to start the service in case of errors 1.334 + // like ERROR_SERVICE_DATABASE_LOCKED or ERROR_SERVICE_REQUEST_TIMEOUT. 1.335 + const DWORD maxWaitMS = 5000; 1.336 + DWORD currentWaitMS = 0; 1.337 + DWORD lastError = ERROR_SUCCESS; 1.338 + while (currentWaitMS < maxWaitMS) { 1.339 + BOOL result = StartServiceW(service, argc, argv); 1.340 + if (result) { 1.341 + lastError = ERROR_SUCCESS; 1.342 + break; 1.343 + } else { 1.344 + lastError = GetLastError(); 1.345 + } 1.346 + Sleep(100); 1.347 + currentWaitMS += 100; 1.348 + } 1.349 + CloseServiceHandle(service); 1.350 + CloseServiceHandle(serviceManager); 1.351 + return lastError; 1.352 +} 1.353 + 1.354 +#ifndef ONLY_SERVICE_LAUNCHING 1.355 + 1.356 +/** 1.357 + * Launch a service initiated action for a software update with the 1.358 + * specified arguments. 1.359 + * 1.360 + * @param exePath The path of the executable to run 1.361 + * @param argc The total number of arguments in argv 1.362 + * @param argv An array of null terminated strings to pass to the exePath, 1.363 + * argv[0] must be the path to the updater.exe 1.364 + * @return ERROR_SUCCESS if successful 1.365 + */ 1.366 +DWORD 1.367 +LaunchServiceSoftwareUpdateCommand(int argc, LPCWSTR* argv) 1.368 +{ 1.369 + // The service command is the same as the updater.exe command line except 1.370 + // it has 2 extra args: 1) The Path to udpater.exe, and 2) the command 1.371 + // being executed which is "software-update" 1.372 + LPCWSTR *updaterServiceArgv = new LPCWSTR[argc + 2]; 1.373 + updaterServiceArgv[0] = L"MozillaMaintenance"; 1.374 + updaterServiceArgv[1] = L"software-update"; 1.375 + 1.376 + for (int i = 0; i < argc; ++i) { 1.377 + updaterServiceArgv[i + 2] = argv[i]; 1.378 + } 1.379 + 1.380 + // Execute the service command by starting the service with 1.381 + // the passed in arguments. 1.382 + DWORD ret = StartServiceCommand(argc + 2, updaterServiceArgv); 1.383 + delete[] updaterServiceArgv; 1.384 + return ret; 1.385 +} 1.386 + 1.387 +/** 1.388 + * Joins a base directory path with a filename. 1.389 + * 1.390 + * @param base The base directory path of size MAX_PATH + 1 1.391 + * @param extra The filename to append 1.392 + * @return TRUE if the file name was successful appended to base 1.393 + */ 1.394 +BOOL 1.395 +PathAppendSafe(LPWSTR base, LPCWSTR extra) 1.396 +{ 1.397 + if (wcslen(base) + wcslen(extra) >= MAX_PATH) { 1.398 + return FALSE; 1.399 + } 1.400 + 1.401 + return PathAppendW(base, extra); 1.402 +} 1.403 + 1.404 +/** 1.405 + * Sets update.status to pending so that the next startup will not use 1.406 + * the service and instead will attempt an update the with a UAC prompt. 1.407 + * 1.408 + * @param updateDirPath The path of the update directory 1.409 + * @return TRUE if successful 1.410 + */ 1.411 +BOOL 1.412 +WriteStatusPending(LPCWSTR updateDirPath) 1.413 +{ 1.414 + WCHAR updateStatusFilePath[MAX_PATH + 1] = { L'\0' }; 1.415 + wcsncpy(updateStatusFilePath, updateDirPath, MAX_PATH); 1.416 + if (!PathAppendSafe(updateStatusFilePath, L"update.status")) { 1.417 + return FALSE; 1.418 + } 1.419 + 1.420 + const char pending[] = "pending"; 1.421 + HANDLE statusFile = CreateFileW(updateStatusFilePath, GENERIC_WRITE, 0, 1.422 + nullptr, CREATE_ALWAYS, 0, nullptr); 1.423 + if (statusFile == INVALID_HANDLE_VALUE) { 1.424 + return FALSE; 1.425 + } 1.426 + 1.427 + DWORD wrote; 1.428 + BOOL ok = WriteFile(statusFile, pending, 1.429 + sizeof(pending) - 1, &wrote, nullptr); 1.430 + CloseHandle(statusFile); 1.431 + return ok && (wrote == sizeof(pending) - 1); 1.432 +} 1.433 + 1.434 +/** 1.435 + * Sets update.status to a specific failure code 1.436 + * 1.437 + * @param updateDirPath The path of the update directory 1.438 + * @return TRUE if successful 1.439 + */ 1.440 +BOOL 1.441 +WriteStatusFailure(LPCWSTR updateDirPath, int errorCode) 1.442 +{ 1.443 + WCHAR updateStatusFilePath[MAX_PATH + 1] = { L'\0' }; 1.444 + wcsncpy(updateStatusFilePath, updateDirPath, MAX_PATH); 1.445 + if (!PathAppendSafe(updateStatusFilePath, L"update.status")) { 1.446 + return FALSE; 1.447 + } 1.448 + 1.449 + HANDLE statusFile = CreateFileW(updateStatusFilePath, GENERIC_WRITE, 0, 1.450 + nullptr, CREATE_ALWAYS, 0, nullptr); 1.451 + if (statusFile == INVALID_HANDLE_VALUE) { 1.452 + return FALSE; 1.453 + } 1.454 + char failure[32]; 1.455 + sprintf(failure, "failed: %d", errorCode); 1.456 + 1.457 + DWORD toWrite = strlen(failure); 1.458 + DWORD wrote; 1.459 + BOOL ok = WriteFile(statusFile, failure, 1.460 + toWrite, &wrote, nullptr); 1.461 + CloseHandle(statusFile); 1.462 + return ok && wrote == toWrite; 1.463 +} 1.464 + 1.465 +#endif 1.466 + 1.467 +/** 1.468 + * Waits for a service to enter a stopped state. 1.469 + * This function does not stop the service, it just blocks until the service 1.470 + * is stopped. 1.471 + * 1.472 + * @param serviceName The service to wait for. 1.473 + * @param maxWaitSeconds The maximum number of seconds to wait 1.474 + * @return state of the service after a timeout or when stopped. 1.475 + * A value of 255 is returned for an error. Typical values are: 1.476 + * SERVICE_STOPPED 0x00000001 1.477 + * SERVICE_START_PENDING 0x00000002 1.478 + * SERVICE_STOP_PENDING 0x00000003 1.479 + * SERVICE_RUNNING 0x00000004 1.480 + * SERVICE_CONTINUE_PENDING 0x00000005 1.481 + * SERVICE_PAUSE_PENDING 0x00000006 1.482 + * SERVICE_PAUSED 0x00000007 1.483 + * last status not set 0x000000CF 1.484 + * Could no query status 0x000000DF 1.485 + * Could not open service, access denied 0x000000EB 1.486 + * Could not open service, invalid handle 0x000000EC 1.487 + * Could not open service, invalid name 0x000000ED 1.488 + * Could not open service, does not exist 0x000000EE 1.489 + * Could not open service, other error 0x000000EF 1.490 + * Could not open SCM, access denied 0x000000FD 1.491 + * Could not open SCM, database does not exist 0x000000FE; 1.492 + * Could not open SCM, other error 0x000000FF; 1.493 + * Note: The strange choice of error codes above SERVICE_PAUSED are chosen 1.494 + * in case Windows comes out with other service stats higher than 7, they 1.495 + * would likely call it 8 and above. JS code that uses this in TestAUSHelper 1.496 + * only handles values up to 255 so that's why we don't use GetLastError 1.497 + * directly. 1.498 + */ 1.499 +DWORD 1.500 +WaitForServiceStop(LPCWSTR serviceName, DWORD maxWaitSeconds) 1.501 +{ 1.502 + // 0x000000CF is defined above to be not set 1.503 + DWORD lastServiceState = 0x000000CF; 1.504 + 1.505 + // Get a handle to the SCM database. 1.506 + SC_HANDLE serviceManager = OpenSCManager(nullptr, nullptr, 1.507 + SC_MANAGER_CONNECT | 1.508 + SC_MANAGER_ENUMERATE_SERVICE); 1.509 + if (!serviceManager) { 1.510 + DWORD lastError = GetLastError(); 1.511 + switch(lastError) { 1.512 + case ERROR_ACCESS_DENIED: 1.513 + return 0x000000FD; 1.514 + case ERROR_DATABASE_DOES_NOT_EXIST: 1.515 + return 0x000000FE; 1.516 + default: 1.517 + return 0x000000FF; 1.518 + } 1.519 + } 1.520 + 1.521 + // Get a handle to the service. 1.522 + SC_HANDLE service = OpenServiceW(serviceManager, 1.523 + serviceName, 1.524 + SERVICE_QUERY_STATUS); 1.525 + if (!service) { 1.526 + DWORD lastError = GetLastError(); 1.527 + CloseServiceHandle(serviceManager); 1.528 + switch(lastError) { 1.529 + case ERROR_ACCESS_DENIED: 1.530 + return 0x000000EB; 1.531 + case ERROR_INVALID_HANDLE: 1.532 + return 0x000000EC; 1.533 + case ERROR_INVALID_NAME: 1.534 + return 0x000000ED; 1.535 + case ERROR_SERVICE_DOES_NOT_EXIST: 1.536 + return 0x000000EE; 1.537 + default: 1.538 + return 0x000000EF; 1.539 + } 1.540 + } 1.541 + 1.542 + DWORD currentWaitMS = 0; 1.543 + SERVICE_STATUS_PROCESS ssp; 1.544 + ssp.dwCurrentState = lastServiceState; 1.545 + while (currentWaitMS < maxWaitSeconds * 1000) { 1.546 + DWORD bytesNeeded; 1.547 + if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO, (LPBYTE)&ssp, 1.548 + sizeof(SERVICE_STATUS_PROCESS), &bytesNeeded)) { 1.549 + DWORD lastError = GetLastError(); 1.550 + switch (lastError) { 1.551 + case ERROR_INVALID_HANDLE: 1.552 + ssp.dwCurrentState = 0x000000D9; 1.553 + break; 1.554 + case ERROR_ACCESS_DENIED: 1.555 + ssp.dwCurrentState = 0x000000DA; 1.556 + break; 1.557 + case ERROR_INSUFFICIENT_BUFFER: 1.558 + ssp.dwCurrentState = 0x000000DB; 1.559 + break; 1.560 + case ERROR_INVALID_PARAMETER: 1.561 + ssp.dwCurrentState = 0x000000DC; 1.562 + break; 1.563 + case ERROR_INVALID_LEVEL: 1.564 + ssp.dwCurrentState = 0x000000DD; 1.565 + break; 1.566 + case ERROR_SHUTDOWN_IN_PROGRESS: 1.567 + ssp.dwCurrentState = 0x000000DE; 1.568 + break; 1.569 + // These 3 errors can occur when the service is not yet stopped but 1.570 + // it is stopping. 1.571 + case ERROR_INVALID_SERVICE_CONTROL: 1.572 + case ERROR_SERVICE_CANNOT_ACCEPT_CTRL: 1.573 + case ERROR_SERVICE_NOT_ACTIVE: 1.574 + currentWaitMS += 50; 1.575 + Sleep(50); 1.576 + continue; 1.577 + default: 1.578 + ssp.dwCurrentState = 0x000000DF; 1.579 + } 1.580 + 1.581 + // We couldn't query the status so just break out 1.582 + break; 1.583 + } 1.584 + 1.585 + // The service is already in use. 1.586 + if (ssp.dwCurrentState == SERVICE_STOPPED) { 1.587 + break; 1.588 + } 1.589 + currentWaitMS += 50; 1.590 + Sleep(50); 1.591 + } 1.592 + 1.593 + lastServiceState = ssp.dwCurrentState; 1.594 + CloseServiceHandle(service); 1.595 + CloseServiceHandle(serviceManager); 1.596 + return lastServiceState; 1.597 +} 1.598 + 1.599 +#ifndef ONLY_SERVICE_LAUNCHING 1.600 + 1.601 +/** 1.602 + * Determines if there is at least one process running for the specified 1.603 + * application. A match will be found across any session for any user. 1.604 + * 1.605 + * @param process The process to check for existance 1.606 + * @return ERROR_NOT_FOUND if the process was not found 1.607 + * ERROR_SUCCESS if the process was found and there were no errors 1.608 + * Other Win32 system error code for other errors 1.609 +**/ 1.610 +DWORD 1.611 +IsProcessRunning(LPCWSTR filename) 1.612 +{ 1.613 + // Take a snapshot of all processes in the system. 1.614 + HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); 1.615 + if (INVALID_HANDLE_VALUE == snapshot) { 1.616 + return GetLastError(); 1.617 + } 1.618 + 1.619 + PROCESSENTRY32W processEntry; 1.620 + processEntry.dwSize = sizeof(PROCESSENTRY32W); 1.621 + if (!Process32FirstW(snapshot, &processEntry)) { 1.622 + DWORD lastError = GetLastError(); 1.623 + CloseHandle(snapshot); 1.624 + return lastError; 1.625 + } 1.626 + 1.627 + do { 1.628 + if (wcsicmp(filename, processEntry.szExeFile) == 0) { 1.629 + CloseHandle(snapshot); 1.630 + return ERROR_SUCCESS; 1.631 + } 1.632 + } while (Process32NextW(snapshot, &processEntry)); 1.633 + CloseHandle(snapshot); 1.634 + return ERROR_NOT_FOUND; 1.635 +} 1.636 + 1.637 +/** 1.638 + * Waits for the specified applicaiton to exit. 1.639 + * 1.640 + * @param filename The application to wait for. 1.641 + * @param maxSeconds The maximum amount of seconds to wait for all 1.642 + * instances of the application to exit. 1.643 + * @return ERROR_SUCCESS if no instances of the application exist 1.644 + * WAIT_TIMEOUT if the process is still running after maxSeconds. 1.645 + * Any other Win32 system error code. 1.646 +*/ 1.647 +DWORD 1.648 +WaitForProcessExit(LPCWSTR filename, DWORD maxSeconds) 1.649 +{ 1.650 + DWORD applicationRunningError = WAIT_TIMEOUT; 1.651 + for(DWORD i = 0; i < maxSeconds; i++) { 1.652 + DWORD applicationRunningError = IsProcessRunning(filename); 1.653 + if (ERROR_NOT_FOUND == applicationRunningError) { 1.654 + return ERROR_SUCCESS; 1.655 + } 1.656 + Sleep(1000); 1.657 + } 1.658 + 1.659 + if (ERROR_SUCCESS == applicationRunningError) { 1.660 + return WAIT_TIMEOUT; 1.661 + } 1.662 + 1.663 + return applicationRunningError; 1.664 +} 1.665 + 1.666 +/** 1.667 + * Determines if the fallback key exists or not 1.668 + * 1.669 + * @return TRUE if the fallback key exists and there was no error checking 1.670 +*/ 1.671 +BOOL 1.672 +DoesFallbackKeyExist() 1.673 +{ 1.674 + HKEY testOnlyFallbackKey; 1.675 + if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, 1.676 + TEST_ONLY_FALLBACK_KEY_PATH, 0, 1.677 + KEY_READ | KEY_WOW64_64KEY, 1.678 + &testOnlyFallbackKey) != ERROR_SUCCESS) { 1.679 + return FALSE; 1.680 + } 1.681 + 1.682 + RegCloseKey(testOnlyFallbackKey); 1.683 + return TRUE; 1.684 +} 1.685 + 1.686 +#endif 1.687 + 1.688 +/** 1.689 + * Determines if the file system for the specified file handle is local 1.690 + * @param file path to check the filesystem type for, must be at most MAX_PATH 1.691 + * @param isLocal out parameter which will hold TRUE if the drive is local 1.692 + * @return TRUE if the call succeeded 1.693 +*/ 1.694 +BOOL 1.695 +IsLocalFile(LPCWSTR file, BOOL &isLocal) 1.696 +{ 1.697 + WCHAR rootPath[MAX_PATH + 1] = { L'\0' }; 1.698 + if (wcslen(file) > MAX_PATH) { 1.699 + return FALSE; 1.700 + } 1.701 + 1.702 + wcsncpy(rootPath, file, MAX_PATH); 1.703 + PathStripToRootW(rootPath); 1.704 + isLocal = GetDriveTypeW(rootPath) == DRIVE_FIXED; 1.705 + return TRUE; 1.706 +} 1.707 + 1.708 + 1.709 +/** 1.710 + * Determines the DWORD value of a registry key value 1.711 + * 1.712 + * @param key The base key to where the value name exists 1.713 + * @param valueName The name of the value 1.714 + * @param retValue Out parameter which will hold the value 1.715 + * @return TRUE on success 1.716 +*/ 1.717 +static BOOL 1.718 +GetDWORDValue(HKEY key, LPCWSTR valueName, DWORD &retValue) 1.719 +{ 1.720 + DWORD regDWORDValueSize = sizeof(DWORD); 1.721 + LONG retCode = RegQueryValueExW(key, valueName, 0, nullptr, 1.722 + reinterpret_cast<LPBYTE>(&retValue), 1.723 + ®DWORDValueSize); 1.724 + return ERROR_SUCCESS == retCode; 1.725 +} 1.726 + 1.727 +/** 1.728 + * Determines if the the system's elevation type allows 1.729 + * unprmopted elevation. 1.730 + * 1.731 + * @param isUnpromptedElevation Out parameter which specifies if unprompted 1.732 + * elevation is allowed. 1.733 + * @return TRUE if the user can actually elevate and the value was obtained 1.734 + * successfully. 1.735 +*/ 1.736 +BOOL 1.737 +IsUnpromptedElevation(BOOL &isUnpromptedElevation) 1.738 +{ 1.739 + if (!UACHelper::CanUserElevate()) { 1.740 + return FALSE; 1.741 + } 1.742 + 1.743 + LPCWSTR UACBaseRegKey = 1.744 + L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Policies\\System"; 1.745 + HKEY baseKey; 1.746 + LONG retCode = RegOpenKeyExW(HKEY_LOCAL_MACHINE, 1.747 + UACBaseRegKey, 0, 1.748 + KEY_READ, &baseKey); 1.749 + if (retCode != ERROR_SUCCESS) { 1.750 + return FALSE; 1.751 + } 1.752 + 1.753 + DWORD consent, secureDesktop; 1.754 + BOOL success = GetDWORDValue(baseKey, L"ConsentPromptBehaviorAdmin", 1.755 + consent); 1.756 + success = success && 1.757 + GetDWORDValue(baseKey, L"PromptOnSecureDesktop", secureDesktop); 1.758 + isUnpromptedElevation = !consent && !secureDesktop; 1.759 + 1.760 + RegCloseKey(baseKey); 1.761 + return success; 1.762 +} 1.763 + 1.764 +#ifdef MOZ_METRO 1.765 + /* 1.766 + * Retrieve the app model id of the firefox metro browser. 1.767 + * 1.768 + * @aPathBuffer Buffer to fill 1.769 + * @aCharLength Length of buffer to fill in characters 1.770 + */ 1.771 + bool GetDefaultBrowserAppModelID(WCHAR* aIDBuffer, long aCharLength) 1.772 + { 1.773 + if (!aIDBuffer || aCharLength <= 0) 1.774 + return false; 1.775 + 1.776 + memset(aIDBuffer, 0, (sizeof(WCHAR)*aCharLength)); 1.777 + static const WCHAR* kDefaultMetroBrowserIDPathKey = L"FirefoxURL"; 1.778 + 1.779 + HKEY key; 1.780 + if (RegOpenKeyExW(HKEY_CLASSES_ROOT, kDefaultMetroBrowserIDPathKey, 1.781 + 0, KEY_READ, &key) != ERROR_SUCCESS) { 1.782 + return false; 1.783 + } 1.784 + DWORD len = aCharLength * sizeof(WCHAR); 1.785 + memset(aIDBuffer, 0, len); 1.786 + if (RegQueryValueExW(key, L"AppUserModelID", nullptr, nullptr, 1.787 + (LPBYTE)aIDBuffer, &len) != ERROR_SUCCESS || !len) { 1.788 + RegCloseKey(key); 1.789 + return false; 1.790 + } 1.791 + RegCloseKey(key); 1.792 + return true; 1.793 + } 1.794 + 1.795 + HRESULT 1.796 + LaunchDefaultMetroBrowser() 1.797 + { 1.798 + CoInitialize(nullptr); 1.799 + HRESULT hr = E_FAIL; 1.800 + // The interface that allows us to activate the browser 1.801 + IApplicationActivationManager *activateMgr; 1.802 + if (FAILED(hr = CoCreateInstance(CLSID_ApplicationActivationManager, 1.803 + nullptr, CLSCTX_LOCAL_SERVER, 1.804 + IID_IApplicationActivationManager, 1.805 + (void**)&activateMgr))) { 1.806 + CoUninitialize(); 1.807 + return hr; 1.808 + } 1.809 + 1.810 + // Activation is based on the browser's registered app model id 1.811 + WCHAR appModelID[256]; 1.812 + if (!GetDefaultBrowserAppModelID(appModelID, (sizeof(appModelID)/sizeof(WCHAR)))) { 1.813 + activateMgr->Release(); 1.814 + CoUninitialize(); 1.815 + return hr; 1.816 + } 1.817 + 1.818 + // Hand off focus rights to the out-of-process activation server. Without 1.819 + // this the metro interface won't launch. 1.820 + CoAllowSetForegroundWindow(activateMgr, nullptr); 1.821 + 1.822 + // Launch default browser in Metro 1.823 + DWORD processID; 1.824 + hr = activateMgr->ActivateApplication(appModelID, L"", AO_NOERRORUI, 1.825 + &processID); 1.826 + activateMgr->Release(); 1.827 + CoUninitialize(); 1.828 + return hr; 1.829 + } 1.830 +#endif