Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 #ifdef MOZ_METRO
6 // Needed for COM calls to launch Metro applications
7 #undef WINVER
8 #undef _WIN32_WINNT
9 #define WINVER 0x602
10 #define _WIN32_WINNT 0x602
11 #include <objbase.h>
12 #include <shobjidl.h>
13 #pragma comment(lib, "ole32.lib")
14 #endif
16 #include <windows.h>
18 // Needed for CreateToolhelp32Snapshot
19 #include <tlhelp32.h>
20 #ifndef ONLY_SERVICE_LAUNCHING
22 #include <stdio.h>
23 #include "shlobj.h"
24 #include "updatehelper.h"
25 #include "uachelper.h"
26 #include "pathhash.h"
27 #include "mozilla/Scoped.h"
29 // Needed for PathAppendW
30 #include <shlwapi.h>
32 WCHAR* MakeCommandLine(int argc, WCHAR **argv);
33 BOOL PathAppendSafe(LPWSTR base, LPCWSTR extra);
35 /**
36 * Obtains the path of a file in the same directory as the specified file.
37 *
38 * @param destinationBuffer A buffer of size MAX_PATH + 1 to store the result.
39 * @param siblingFIlePath The path of another file in the same directory
40 * @param newFileName The filename of another file in the same directory
41 * @return TRUE if successful
42 */
43 BOOL
44 PathGetSiblingFilePath(LPWSTR destinationBuffer,
45 LPCWSTR siblingFilePath,
46 LPCWSTR newFileName)
47 {
48 if (wcslen(siblingFilePath) >= MAX_PATH) {
49 return FALSE;
50 }
52 wcsncpy(destinationBuffer, siblingFilePath, MAX_PATH);
53 if (!PathRemoveFileSpecW(destinationBuffer)) {
54 return FALSE;
55 }
57 if (wcslen(destinationBuffer) + wcslen(newFileName) >= MAX_PATH) {
58 return FALSE;
59 }
61 return PathAppendSafe(destinationBuffer, newFileName);
62 }
64 /**
65 * Launch the post update application as the specified user (helper.exe).
66 * It takes in the path of the callback application to calculate the path
67 * of helper.exe. For service updates this is called from both the system
68 * account and the current user account.
69 *
70 * @param installationDir The path to the callback application binary.
71 * @param updateInfoDir The directory where update info is stored.
72 * @param forceSync If true even if the ini file specifies async, the
73 * process will wait for termination of PostUpdate.
74 * @param userToken The user token to run as, if nullptr the current
75 * user will be used.
76 * @return TRUE if there was no error starting the process.
77 */
78 BOOL
79 LaunchWinPostProcess(const WCHAR *installationDir,
80 const WCHAR *updateInfoDir,
81 bool forceSync,
82 HANDLE userToken)
83 {
84 WCHAR workingDirectory[MAX_PATH + 1] = { L'\0' };
85 wcsncpy(workingDirectory, installationDir, MAX_PATH);
87 // Launch helper.exe to perform post processing (e.g. registry and log file
88 // modifications) for the update.
89 WCHAR inifile[MAX_PATH + 1] = { L'\0' };
90 wcsncpy(inifile, installationDir, MAX_PATH);
91 if (!PathAppendSafe(inifile, L"updater.ini")) {
92 return FALSE;
93 }
95 WCHAR exefile[MAX_PATH + 1];
96 WCHAR exearg[MAX_PATH + 1];
97 WCHAR exeasync[10];
98 bool async = true;
99 if (!GetPrivateProfileStringW(L"PostUpdateWin", L"ExeRelPath", nullptr,
100 exefile, MAX_PATH + 1, inifile)) {
101 return FALSE;
102 }
104 if (!GetPrivateProfileStringW(L"PostUpdateWin", L"ExeArg", nullptr, exearg,
105 MAX_PATH + 1, inifile)) {
106 return FALSE;
107 }
109 if (!GetPrivateProfileStringW(L"PostUpdateWin", L"ExeAsync", L"TRUE",
110 exeasync,
111 sizeof(exeasync)/sizeof(exeasync[0]),
112 inifile)) {
113 return FALSE;
114 }
116 WCHAR exefullpath[MAX_PATH + 1] = { L'\0' };
117 wcsncpy(exefullpath, installationDir, MAX_PATH);
118 if (!PathAppendSafe(exefullpath, exefile)) {
119 return false;
120 }
122 WCHAR dlogFile[MAX_PATH + 1];
123 if (!PathGetSiblingFilePath(dlogFile, exefullpath, L"uninstall.update")) {
124 return FALSE;
125 }
127 WCHAR slogFile[MAX_PATH + 1] = { L'\0' };
128 wcsncpy(slogFile, updateInfoDir, MAX_PATH);
129 if (!PathAppendSafe(slogFile, L"update.log")) {
130 return FALSE;
131 }
133 WCHAR dummyArg[14] = { L'\0' };
134 wcsncpy(dummyArg, L"argv0ignored ", sizeof(dummyArg) / sizeof(dummyArg[0]) - 1);
136 size_t len = wcslen(exearg) + wcslen(dummyArg);
137 WCHAR *cmdline = (WCHAR *) malloc((len + 1) * sizeof(WCHAR));
138 if (!cmdline) {
139 return FALSE;
140 }
142 wcsncpy(cmdline, dummyArg, len);
143 wcscat(cmdline, exearg);
145 if (forceSync ||
146 !_wcsnicmp(exeasync, L"false", 6) ||
147 !_wcsnicmp(exeasync, L"0", 2)) {
148 async = false;
149 }
151 // We want to launch the post update helper app to update the Windows
152 // registry even if there is a failure with removing the uninstall.update
153 // file or copying the update.log file.
154 CopyFileW(slogFile, dlogFile, false);
156 STARTUPINFOW si = {sizeof(si), 0};
157 si.lpDesktop = L"";
158 PROCESS_INFORMATION pi = {0};
160 bool ok;
161 if (userToken) {
162 ok = CreateProcessAsUserW(userToken,
163 exefullpath,
164 cmdline,
165 nullptr, // no special security attributes
166 nullptr, // no special thread attributes
167 false, // don't inherit filehandles
168 0, // No special process creation flags
169 nullptr, // inherit my environment
170 workingDirectory,
171 &si,
172 &pi);
173 } else {
174 ok = CreateProcessW(exefullpath,
175 cmdline,
176 nullptr, // no special security attributes
177 nullptr, // no special thread attributes
178 false, // don't inherit filehandles
179 0, // No special process creation flags
180 nullptr, // inherit my environment
181 workingDirectory,
182 &si,
183 &pi);
184 }
185 free(cmdline);
186 if (ok) {
187 if (!async)
188 WaitForSingleObject(pi.hProcess, INFINITE);
189 CloseHandle(pi.hProcess);
190 CloseHandle(pi.hThread);
191 }
192 return ok;
193 }
195 /**
196 * Starts the upgrade process for update of the service if it is
197 * already installed.
198 *
199 * @param installDir the installation directory where
200 * maintenanceservice_installer.exe is located.
201 * @return TRUE if successful
202 */
203 BOOL
204 StartServiceUpdate(LPCWSTR installDir)
205 {
206 // Get a handle to the local computer SCM database
207 SC_HANDLE manager = OpenSCManager(nullptr, nullptr,
208 SC_MANAGER_ALL_ACCESS);
209 if (!manager) {
210 return FALSE;
211 }
213 // Open the service
214 SC_HANDLE svc = OpenServiceW(manager, SVC_NAME,
215 SERVICE_ALL_ACCESS);
216 if (!svc) {
217 CloseServiceHandle(manager);
218 return FALSE;
219 }
221 // If we reach here, then the service is installed, so
222 // proceed with upgrading it.
224 CloseServiceHandle(manager);
226 // The service exists and we opened it, get the config bytes needed
227 DWORD bytesNeeded;
228 if (!QueryServiceConfigW(svc, nullptr, 0, &bytesNeeded) &&
229 GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
230 CloseServiceHandle(svc);
231 return FALSE;
232 }
234 // Get the service config information, in particular we want the binary
235 // path of the service.
236 mozilla::ScopedDeleteArray<char> serviceConfigBuffer(new char[bytesNeeded]);
237 if (!QueryServiceConfigW(svc,
238 reinterpret_cast<QUERY_SERVICE_CONFIGW*>(serviceConfigBuffer.get()),
239 bytesNeeded, &bytesNeeded)) {
240 CloseServiceHandle(svc);
241 return FALSE;
242 }
244 CloseServiceHandle(svc);
246 QUERY_SERVICE_CONFIGW &serviceConfig =
247 *reinterpret_cast<QUERY_SERVICE_CONFIGW*>(serviceConfigBuffer.get());
249 PathUnquoteSpacesW(serviceConfig.lpBinaryPathName);
251 // Obtain the temp path of the maintenance service binary
252 WCHAR tmpService[MAX_PATH + 1] = { L'\0' };
253 if (!PathGetSiblingFilePath(tmpService, serviceConfig.lpBinaryPathName,
254 L"maintenanceservice_tmp.exe")) {
255 return FALSE;
256 }
258 // Get the new maintenance service path from the install dir
259 WCHAR newMaintServicePath[MAX_PATH + 1] = { L'\0' };
260 wcsncpy(newMaintServicePath, installDir, MAX_PATH);
261 PathAppendSafe(newMaintServicePath,
262 L"maintenanceservice.exe");
264 // Copy the temp file in alongside the maintenace service.
265 // This is a requirement for maintenance service upgrades.
266 if (!CopyFileW(newMaintServicePath, tmpService, FALSE)) {
267 return FALSE;
268 }
270 // Start the upgrade comparison process
271 STARTUPINFOW si = {0};
272 si.cb = sizeof(STARTUPINFOW);
273 // No particular desktop because no UI
274 si.lpDesktop = L"";
275 PROCESS_INFORMATION pi = {0};
276 WCHAR cmdLine[64] = { '\0' };
277 wcsncpy(cmdLine, L"dummyparam.exe upgrade",
278 sizeof(cmdLine) / sizeof(cmdLine[0]) - 1);
279 BOOL svcUpdateProcessStarted = CreateProcessW(tmpService,
280 cmdLine,
281 nullptr, nullptr, FALSE,
282 0,
283 nullptr, installDir, &si, &pi);
284 if (svcUpdateProcessStarted) {
285 CloseHandle(pi.hProcess);
286 CloseHandle(pi.hThread);
287 }
288 return svcUpdateProcessStarted;
289 }
291 #endif
293 /**
294 * Executes a maintenance service command
295 *
296 * @param argc The total number of arguments in argv
297 * @param argv An array of null terminated strings to pass to the service,
298 * @return ERROR_SUCCESS if the service command was started.
299 * Less than 16000, a windows system error code from StartServiceW
300 * More than 20000, 20000 + the last state of the service constant if
301 * the last state is something other than stopped.
302 * 17001 if the SCM could not be opened
303 * 17002 if the service could not be opened
304 */
305 DWORD
306 StartServiceCommand(int argc, LPCWSTR* argv)
307 {
308 DWORD lastState = WaitForServiceStop(SVC_NAME, 5);
309 if (lastState != SERVICE_STOPPED) {
310 return 20000 + lastState;
311 }
313 // Get a handle to the SCM database.
314 SC_HANDLE serviceManager = OpenSCManager(nullptr, nullptr,
315 SC_MANAGER_CONNECT |
316 SC_MANAGER_ENUMERATE_SERVICE);
317 if (!serviceManager) {
318 return 17001;
319 }
321 // Get a handle to the service.
322 SC_HANDLE service = OpenServiceW(serviceManager,
323 SVC_NAME,
324 SERVICE_START);
325 if (!service) {
326 CloseServiceHandle(serviceManager);
327 return 17002;
328 }
330 // Wait at most 5 seconds trying to start the service in case of errors
331 // like ERROR_SERVICE_DATABASE_LOCKED or ERROR_SERVICE_REQUEST_TIMEOUT.
332 const DWORD maxWaitMS = 5000;
333 DWORD currentWaitMS = 0;
334 DWORD lastError = ERROR_SUCCESS;
335 while (currentWaitMS < maxWaitMS) {
336 BOOL result = StartServiceW(service, argc, argv);
337 if (result) {
338 lastError = ERROR_SUCCESS;
339 break;
340 } else {
341 lastError = GetLastError();
342 }
343 Sleep(100);
344 currentWaitMS += 100;
345 }
346 CloseServiceHandle(service);
347 CloseServiceHandle(serviceManager);
348 return lastError;
349 }
351 #ifndef ONLY_SERVICE_LAUNCHING
353 /**
354 * Launch a service initiated action for a software update with the
355 * specified arguments.
356 *
357 * @param exePath The path of the executable to run
358 * @param argc The total number of arguments in argv
359 * @param argv An array of null terminated strings to pass to the exePath,
360 * argv[0] must be the path to the updater.exe
361 * @return ERROR_SUCCESS if successful
362 */
363 DWORD
364 LaunchServiceSoftwareUpdateCommand(int argc, LPCWSTR* argv)
365 {
366 // The service command is the same as the updater.exe command line except
367 // it has 2 extra args: 1) The Path to udpater.exe, and 2) the command
368 // being executed which is "software-update"
369 LPCWSTR *updaterServiceArgv = new LPCWSTR[argc + 2];
370 updaterServiceArgv[0] = L"MozillaMaintenance";
371 updaterServiceArgv[1] = L"software-update";
373 for (int i = 0; i < argc; ++i) {
374 updaterServiceArgv[i + 2] = argv[i];
375 }
377 // Execute the service command by starting the service with
378 // the passed in arguments.
379 DWORD ret = StartServiceCommand(argc + 2, updaterServiceArgv);
380 delete[] updaterServiceArgv;
381 return ret;
382 }
384 /**
385 * Joins a base directory path with a filename.
386 *
387 * @param base The base directory path of size MAX_PATH + 1
388 * @param extra The filename to append
389 * @return TRUE if the file name was successful appended to base
390 */
391 BOOL
392 PathAppendSafe(LPWSTR base, LPCWSTR extra)
393 {
394 if (wcslen(base) + wcslen(extra) >= MAX_PATH) {
395 return FALSE;
396 }
398 return PathAppendW(base, extra);
399 }
401 /**
402 * Sets update.status to pending so that the next startup will not use
403 * the service and instead will attempt an update the with a UAC prompt.
404 *
405 * @param updateDirPath The path of the update directory
406 * @return TRUE if successful
407 */
408 BOOL
409 WriteStatusPending(LPCWSTR updateDirPath)
410 {
411 WCHAR updateStatusFilePath[MAX_PATH + 1] = { L'\0' };
412 wcsncpy(updateStatusFilePath, updateDirPath, MAX_PATH);
413 if (!PathAppendSafe(updateStatusFilePath, L"update.status")) {
414 return FALSE;
415 }
417 const char pending[] = "pending";
418 HANDLE statusFile = CreateFileW(updateStatusFilePath, GENERIC_WRITE, 0,
419 nullptr, CREATE_ALWAYS, 0, nullptr);
420 if (statusFile == INVALID_HANDLE_VALUE) {
421 return FALSE;
422 }
424 DWORD wrote;
425 BOOL ok = WriteFile(statusFile, pending,
426 sizeof(pending) - 1, &wrote, nullptr);
427 CloseHandle(statusFile);
428 return ok && (wrote == sizeof(pending) - 1);
429 }
431 /**
432 * Sets update.status to a specific failure code
433 *
434 * @param updateDirPath The path of the update directory
435 * @return TRUE if successful
436 */
437 BOOL
438 WriteStatusFailure(LPCWSTR updateDirPath, int errorCode)
439 {
440 WCHAR updateStatusFilePath[MAX_PATH + 1] = { L'\0' };
441 wcsncpy(updateStatusFilePath, updateDirPath, MAX_PATH);
442 if (!PathAppendSafe(updateStatusFilePath, L"update.status")) {
443 return FALSE;
444 }
446 HANDLE statusFile = CreateFileW(updateStatusFilePath, GENERIC_WRITE, 0,
447 nullptr, CREATE_ALWAYS, 0, nullptr);
448 if (statusFile == INVALID_HANDLE_VALUE) {
449 return FALSE;
450 }
451 char failure[32];
452 sprintf(failure, "failed: %d", errorCode);
454 DWORD toWrite = strlen(failure);
455 DWORD wrote;
456 BOOL ok = WriteFile(statusFile, failure,
457 toWrite, &wrote, nullptr);
458 CloseHandle(statusFile);
459 return ok && wrote == toWrite;
460 }
462 #endif
464 /**
465 * Waits for a service to enter a stopped state.
466 * This function does not stop the service, it just blocks until the service
467 * is stopped.
468 *
469 * @param serviceName The service to wait for.
470 * @param maxWaitSeconds The maximum number of seconds to wait
471 * @return state of the service after a timeout or when stopped.
472 * A value of 255 is returned for an error. Typical values are:
473 * SERVICE_STOPPED 0x00000001
474 * SERVICE_START_PENDING 0x00000002
475 * SERVICE_STOP_PENDING 0x00000003
476 * SERVICE_RUNNING 0x00000004
477 * SERVICE_CONTINUE_PENDING 0x00000005
478 * SERVICE_PAUSE_PENDING 0x00000006
479 * SERVICE_PAUSED 0x00000007
480 * last status not set 0x000000CF
481 * Could no query status 0x000000DF
482 * Could not open service, access denied 0x000000EB
483 * Could not open service, invalid handle 0x000000EC
484 * Could not open service, invalid name 0x000000ED
485 * Could not open service, does not exist 0x000000EE
486 * Could not open service, other error 0x000000EF
487 * Could not open SCM, access denied 0x000000FD
488 * Could not open SCM, database does not exist 0x000000FE;
489 * Could not open SCM, other error 0x000000FF;
490 * Note: The strange choice of error codes above SERVICE_PAUSED are chosen
491 * in case Windows comes out with other service stats higher than 7, they
492 * would likely call it 8 and above. JS code that uses this in TestAUSHelper
493 * only handles values up to 255 so that's why we don't use GetLastError
494 * directly.
495 */
496 DWORD
497 WaitForServiceStop(LPCWSTR serviceName, DWORD maxWaitSeconds)
498 {
499 // 0x000000CF is defined above to be not set
500 DWORD lastServiceState = 0x000000CF;
502 // Get a handle to the SCM database.
503 SC_HANDLE serviceManager = OpenSCManager(nullptr, nullptr,
504 SC_MANAGER_CONNECT |
505 SC_MANAGER_ENUMERATE_SERVICE);
506 if (!serviceManager) {
507 DWORD lastError = GetLastError();
508 switch(lastError) {
509 case ERROR_ACCESS_DENIED:
510 return 0x000000FD;
511 case ERROR_DATABASE_DOES_NOT_EXIST:
512 return 0x000000FE;
513 default:
514 return 0x000000FF;
515 }
516 }
518 // Get a handle to the service.
519 SC_HANDLE service = OpenServiceW(serviceManager,
520 serviceName,
521 SERVICE_QUERY_STATUS);
522 if (!service) {
523 DWORD lastError = GetLastError();
524 CloseServiceHandle(serviceManager);
525 switch(lastError) {
526 case ERROR_ACCESS_DENIED:
527 return 0x000000EB;
528 case ERROR_INVALID_HANDLE:
529 return 0x000000EC;
530 case ERROR_INVALID_NAME:
531 return 0x000000ED;
532 case ERROR_SERVICE_DOES_NOT_EXIST:
533 return 0x000000EE;
534 default:
535 return 0x000000EF;
536 }
537 }
539 DWORD currentWaitMS = 0;
540 SERVICE_STATUS_PROCESS ssp;
541 ssp.dwCurrentState = lastServiceState;
542 while (currentWaitMS < maxWaitSeconds * 1000) {
543 DWORD bytesNeeded;
544 if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO, (LPBYTE)&ssp,
545 sizeof(SERVICE_STATUS_PROCESS), &bytesNeeded)) {
546 DWORD lastError = GetLastError();
547 switch (lastError) {
548 case ERROR_INVALID_HANDLE:
549 ssp.dwCurrentState = 0x000000D9;
550 break;
551 case ERROR_ACCESS_DENIED:
552 ssp.dwCurrentState = 0x000000DA;
553 break;
554 case ERROR_INSUFFICIENT_BUFFER:
555 ssp.dwCurrentState = 0x000000DB;
556 break;
557 case ERROR_INVALID_PARAMETER:
558 ssp.dwCurrentState = 0x000000DC;
559 break;
560 case ERROR_INVALID_LEVEL:
561 ssp.dwCurrentState = 0x000000DD;
562 break;
563 case ERROR_SHUTDOWN_IN_PROGRESS:
564 ssp.dwCurrentState = 0x000000DE;
565 break;
566 // These 3 errors can occur when the service is not yet stopped but
567 // it is stopping.
568 case ERROR_INVALID_SERVICE_CONTROL:
569 case ERROR_SERVICE_CANNOT_ACCEPT_CTRL:
570 case ERROR_SERVICE_NOT_ACTIVE:
571 currentWaitMS += 50;
572 Sleep(50);
573 continue;
574 default:
575 ssp.dwCurrentState = 0x000000DF;
576 }
578 // We couldn't query the status so just break out
579 break;
580 }
582 // The service is already in use.
583 if (ssp.dwCurrentState == SERVICE_STOPPED) {
584 break;
585 }
586 currentWaitMS += 50;
587 Sleep(50);
588 }
590 lastServiceState = ssp.dwCurrentState;
591 CloseServiceHandle(service);
592 CloseServiceHandle(serviceManager);
593 return lastServiceState;
594 }
596 #ifndef ONLY_SERVICE_LAUNCHING
598 /**
599 * Determines if there is at least one process running for the specified
600 * application. A match will be found across any session for any user.
601 *
602 * @param process The process to check for existance
603 * @return ERROR_NOT_FOUND if the process was not found
604 * ERROR_SUCCESS if the process was found and there were no errors
605 * Other Win32 system error code for other errors
606 **/
607 DWORD
608 IsProcessRunning(LPCWSTR filename)
609 {
610 // Take a snapshot of all processes in the system.
611 HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
612 if (INVALID_HANDLE_VALUE == snapshot) {
613 return GetLastError();
614 }
616 PROCESSENTRY32W processEntry;
617 processEntry.dwSize = sizeof(PROCESSENTRY32W);
618 if (!Process32FirstW(snapshot, &processEntry)) {
619 DWORD lastError = GetLastError();
620 CloseHandle(snapshot);
621 return lastError;
622 }
624 do {
625 if (wcsicmp(filename, processEntry.szExeFile) == 0) {
626 CloseHandle(snapshot);
627 return ERROR_SUCCESS;
628 }
629 } while (Process32NextW(snapshot, &processEntry));
630 CloseHandle(snapshot);
631 return ERROR_NOT_FOUND;
632 }
634 /**
635 * Waits for the specified applicaiton to exit.
636 *
637 * @param filename The application to wait for.
638 * @param maxSeconds The maximum amount of seconds to wait for all
639 * instances of the application to exit.
640 * @return ERROR_SUCCESS if no instances of the application exist
641 * WAIT_TIMEOUT if the process is still running after maxSeconds.
642 * Any other Win32 system error code.
643 */
644 DWORD
645 WaitForProcessExit(LPCWSTR filename, DWORD maxSeconds)
646 {
647 DWORD applicationRunningError = WAIT_TIMEOUT;
648 for(DWORD i = 0; i < maxSeconds; i++) {
649 DWORD applicationRunningError = IsProcessRunning(filename);
650 if (ERROR_NOT_FOUND == applicationRunningError) {
651 return ERROR_SUCCESS;
652 }
653 Sleep(1000);
654 }
656 if (ERROR_SUCCESS == applicationRunningError) {
657 return WAIT_TIMEOUT;
658 }
660 return applicationRunningError;
661 }
663 /**
664 * Determines if the fallback key exists or not
665 *
666 * @return TRUE if the fallback key exists and there was no error checking
667 */
668 BOOL
669 DoesFallbackKeyExist()
670 {
671 HKEY testOnlyFallbackKey;
672 if (RegOpenKeyExW(HKEY_LOCAL_MACHINE,
673 TEST_ONLY_FALLBACK_KEY_PATH, 0,
674 KEY_READ | KEY_WOW64_64KEY,
675 &testOnlyFallbackKey) != ERROR_SUCCESS) {
676 return FALSE;
677 }
679 RegCloseKey(testOnlyFallbackKey);
680 return TRUE;
681 }
683 #endif
685 /**
686 * Determines if the file system for the specified file handle is local
687 * @param file path to check the filesystem type for, must be at most MAX_PATH
688 * @param isLocal out parameter which will hold TRUE if the drive is local
689 * @return TRUE if the call succeeded
690 */
691 BOOL
692 IsLocalFile(LPCWSTR file, BOOL &isLocal)
693 {
694 WCHAR rootPath[MAX_PATH + 1] = { L'\0' };
695 if (wcslen(file) > MAX_PATH) {
696 return FALSE;
697 }
699 wcsncpy(rootPath, file, MAX_PATH);
700 PathStripToRootW(rootPath);
701 isLocal = GetDriveTypeW(rootPath) == DRIVE_FIXED;
702 return TRUE;
703 }
706 /**
707 * Determines the DWORD value of a registry key value
708 *
709 * @param key The base key to where the value name exists
710 * @param valueName The name of the value
711 * @param retValue Out parameter which will hold the value
712 * @return TRUE on success
713 */
714 static BOOL
715 GetDWORDValue(HKEY key, LPCWSTR valueName, DWORD &retValue)
716 {
717 DWORD regDWORDValueSize = sizeof(DWORD);
718 LONG retCode = RegQueryValueExW(key, valueName, 0, nullptr,
719 reinterpret_cast<LPBYTE>(&retValue),
720 ®DWORDValueSize);
721 return ERROR_SUCCESS == retCode;
722 }
724 /**
725 * Determines if the the system's elevation type allows
726 * unprmopted elevation.
727 *
728 * @param isUnpromptedElevation Out parameter which specifies if unprompted
729 * elevation is allowed.
730 * @return TRUE if the user can actually elevate and the value was obtained
731 * successfully.
732 */
733 BOOL
734 IsUnpromptedElevation(BOOL &isUnpromptedElevation)
735 {
736 if (!UACHelper::CanUserElevate()) {
737 return FALSE;
738 }
740 LPCWSTR UACBaseRegKey =
741 L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Policies\\System";
742 HKEY baseKey;
743 LONG retCode = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
744 UACBaseRegKey, 0,
745 KEY_READ, &baseKey);
746 if (retCode != ERROR_SUCCESS) {
747 return FALSE;
748 }
750 DWORD consent, secureDesktop;
751 BOOL success = GetDWORDValue(baseKey, L"ConsentPromptBehaviorAdmin",
752 consent);
753 success = success &&
754 GetDWORDValue(baseKey, L"PromptOnSecureDesktop", secureDesktop);
755 isUnpromptedElevation = !consent && !secureDesktop;
757 RegCloseKey(baseKey);
758 return success;
759 }
761 #ifdef MOZ_METRO
762 /*
763 * Retrieve the app model id of the firefox metro browser.
764 *
765 * @aPathBuffer Buffer to fill
766 * @aCharLength Length of buffer to fill in characters
767 */
768 bool GetDefaultBrowserAppModelID(WCHAR* aIDBuffer, long aCharLength)
769 {
770 if (!aIDBuffer || aCharLength <= 0)
771 return false;
773 memset(aIDBuffer, 0, (sizeof(WCHAR)*aCharLength));
774 static const WCHAR* kDefaultMetroBrowserIDPathKey = L"FirefoxURL";
776 HKEY key;
777 if (RegOpenKeyExW(HKEY_CLASSES_ROOT, kDefaultMetroBrowserIDPathKey,
778 0, KEY_READ, &key) != ERROR_SUCCESS) {
779 return false;
780 }
781 DWORD len = aCharLength * sizeof(WCHAR);
782 memset(aIDBuffer, 0, len);
783 if (RegQueryValueExW(key, L"AppUserModelID", nullptr, nullptr,
784 (LPBYTE)aIDBuffer, &len) != ERROR_SUCCESS || !len) {
785 RegCloseKey(key);
786 return false;
787 }
788 RegCloseKey(key);
789 return true;
790 }
792 HRESULT
793 LaunchDefaultMetroBrowser()
794 {
795 CoInitialize(nullptr);
796 HRESULT hr = E_FAIL;
797 // The interface that allows us to activate the browser
798 IApplicationActivationManager *activateMgr;
799 if (FAILED(hr = CoCreateInstance(CLSID_ApplicationActivationManager,
800 nullptr, CLSCTX_LOCAL_SERVER,
801 IID_IApplicationActivationManager,
802 (void**)&activateMgr))) {
803 CoUninitialize();
804 return hr;
805 }
807 // Activation is based on the browser's registered app model id
808 WCHAR appModelID[256];
809 if (!GetDefaultBrowserAppModelID(appModelID, (sizeof(appModelID)/sizeof(WCHAR)))) {
810 activateMgr->Release();
811 CoUninitialize();
812 return hr;
813 }
815 // Hand off focus rights to the out-of-process activation server. Without
816 // this the metro interface won't launch.
817 CoAllowSetForegroundWindow(activateMgr, nullptr);
819 // Launch default browser in Metro
820 DWORD processID;
821 hr = activateMgr->ActivateApplication(appModelID, L"", AO_NOERRORUI,
822 &processID);
823 activateMgr->Release();
824 CoUninitialize();
825 return hr;
826 }
827 #endif