toolkit/mozapps/update/common/updatehelper.cpp

Sat, 03 Jan 2015 20:18:00 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Sat, 03 Jan 2015 20:18:00 +0100
branch
TOR_BUG_3246
changeset 7
129ffea94266
permissions
-rw-r--r--

Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.

     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                                   &regDWORDValueSize);
   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

mercurial