webapprt/win/webapprt.cpp

Wed, 31 Dec 2014 13:27:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 13:27:57 +0100
branch
TOR_BUG_3246
changeset 6
8bccb770b82d
permissions
-rw-r--r--

Ignore runtime configuration files generated during quality assurance.

     1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     3 /* This Source Code Form is subject to the terms of the Mozilla Public
     4  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
     5  * You can obtain one at http://mozilla.org/MPL/2.0/. */
     7 // System headers (alphabetical)
     8 #include <fcntl.h>
     9 #include <io.h>
    10 #include <share.h>
    11 #include <sys/stat.h>
    12 #include <windows.h>
    14 // Mozilla headers (alphabetical)
    15 #include "nsIFile.h"
    16 #include "nsINIParser.h"
    17 #include "nsWindowsWMain.cpp"   // we want a wmain entry point
    18 #include "nsXPCOMGlue.h"
    19 #include "nsXPCOMPrivate.h"     // for MAXPATHLEN and XPCOM_DLL
    20 #include "nsXULAppAPI.h"
    21 #include "mozilla/AppData.h"
    23 using namespace mozilla;
    25 XRE_GetFileFromPathType XRE_GetFileFromPath;
    26 XRE_CreateAppDataType XRE_CreateAppData;
    27 XRE_FreeAppDataType XRE_FreeAppData;
    28 XRE_mainType XRE_main;
    30 namespace {
    31   const char kAPP_INI[] = "application.ini";
    32   const char kWEBAPP_INI[] = "webapp.ini";
    33   const char kWEBAPPRT_INI[] = "webapprt.ini";
    34   const char kWEBAPPRT_PATH[] = "webapprt";
    35   const char kAPP_ENV_PREFIX[] = "XUL_APP_FILE=";
    36   const char kAPP_RT[] = "webapprt-stub.exe";
    38   const wchar_t kAPP_RT_BACKUP[] = L"webapprt.old";
    40   wchar_t curExePath[MAXPATHLEN];
    41   wchar_t backupFilePath[MAXPATHLEN];
    42   wchar_t iconPath[MAXPATHLEN];
    43   char profile[MAXPATHLEN];
    44   bool isProfileOverridden = false;
    45   int* pargc;
    46   char*** pargv;
    48   nsresult
    49   joinPath(char* const dest,
    50            char const* const dir,
    51            char const* const leaf,
    52            size_t bufferSize)
    53   {
    54     size_t dirLen = strlen(dir);
    55     size_t leafLen = strlen(leaf);
    56     bool needsSeparator = (dirLen != 0
    57                         && dir[dirLen-1] != '\\'
    58                         && leafLen != 0
    59                         && leaf[0] != '\\');
    61     if (dirLen + (needsSeparator? 1 : 0) + leafLen >= bufferSize) {
    62       return NS_ERROR_FAILURE;
    63     }
    65     strncpy(dest, dir, bufferSize);
    66     char* destEnd = dest + dirLen;
    67     if (needsSeparator) {
    68       *(destEnd++) = '\\';
    69     }
    71     strncpy(destEnd, leaf, leafLen);
    72     return NS_OK;
    73   }
    75   /**
    76    * A helper class which calls NS_LogInit/NS_LogTerm in its scope.
    77    */
    78   class ScopedLogging
    79   {
    80     public:
    81       ScopedLogging() { NS_LogInit(); }
    82       ~ScopedLogging() { NS_LogTerm(); }
    83   };
    85   /**
    86    * A helper class for scope-guarding nsXREAppData.
    87    */
    88   class ScopedXREAppData
    89   {
    90     public:
    91       ScopedXREAppData()
    92         : mAppData(nullptr) { }
    94       nsresult
    95       create(nsIFile* aINIFile)
    96       {
    97         return XRE_CreateAppData(aINIFile, &mAppData);
    98       }
   100       ~ScopedXREAppData()
   101       {
   102         if (nullptr != mAppData) {
   103           XRE_FreeAppData(mAppData);
   104         }
   105       }
   107       nsXREAppData* const
   108       operator->()
   109       {
   110         return get();
   111       }
   113       nsXREAppData
   114       operator*()
   115       {
   116         return *get();
   117       }
   119       operator
   120       nsXREAppData*()
   121       {
   122         return get();
   123       }
   124     private:
   125       nsXREAppData* mAppData;
   126       nsXREAppData* const get() { return mAppData; }
   127   };
   129   void
   130   Output(const wchar_t *fmt, ... )
   131   {
   132     va_list ap;
   133     va_start(ap, fmt);
   135     wchar_t msg[1024];
   136     _vsnwprintf_s(msg, _countof(msg), _countof(msg), fmt, ap);
   138     MessageBoxW(nullptr, msg, L"Web Runtime", MB_OK);
   140     va_end(ap);
   141   }
   143   void
   144   Output(const char *fmt, ... )
   145   {
   146     va_list ap;
   147     va_start(ap, fmt);
   149     char msg[1024];
   150     vsnprintf(msg, sizeof(msg), fmt, ap);
   152     wchar_t wide_msg[1024];
   153     MultiByteToWideChar(CP_UTF8,
   154                         0,
   155                         msg,
   156                         -1,
   157                         wide_msg,
   158                         _countof(wide_msg));
   159     Output(wide_msg);
   161     va_end(ap);
   162   }
   164   const nsDynamicFunctionLoad kXULFuncs[] = {
   165       { "XRE_GetFileFromPath", (NSFuncPtr*) &XRE_GetFileFromPath },
   166       { "XRE_CreateAppData", (NSFuncPtr*) &XRE_CreateAppData },
   167       { "XRE_FreeAppData", (NSFuncPtr*) &XRE_FreeAppData },
   168       { "XRE_main", (NSFuncPtr*) &XRE_main },
   169       { nullptr, nullptr }
   170   };
   172   bool
   173   AttemptCopyAndLaunch(wchar_t* src)
   174   {
   175     // Rename the old app executable
   176     if (FALSE == ::MoveFileExW(curExePath,
   177                                backupFilePath,
   178                                MOVEFILE_REPLACE_EXISTING)) {
   179       return false;
   180     }
   182     // Copy webapprt-stub.exe from the Firefox dir to the app's dir
   183     if (FALSE == ::CopyFileW(src,
   184                              curExePath,
   185                              TRUE)) {
   186       // Try to move the old file back to its original location
   187       ::MoveFileW(backupFilePath,
   188                   curExePath);
   189       return false;
   190     }
   192     // XXX: We will soon embed the app's icon in the EXE here
   194     STARTUPINFOW si;
   195     PROCESS_INFORMATION pi;
   197     ::ZeroMemory(&si, sizeof(si));
   198     si.cb = sizeof(si);
   199     ::ZeroMemory(&pi, sizeof(pi));
   201     if (!CreateProcessW(curExePath, // Module name
   202                         nullptr,    // Command line
   203                         nullptr,    // Process handle not inheritable
   204                         nullptr,    // Thread handle not inheritable
   205                         FALSE,      // Set handle inheritance to FALSE
   206                         0,          // No creation flags
   207                         nullptr,    // Use parent's environment block
   208                         nullptr,    // Use parent's starting directory
   209                         &si,
   210                         &pi)) {
   211       return false;
   212     }
   214     // Close process and thread handles.
   215     CloseHandle( pi.hProcess );
   216     CloseHandle( pi.hThread );
   218     return true;
   219   }
   221   bool
   222   AttemptCopyAndLaunch(char* srcUtf8)
   223   {
   224     wchar_t src[MAXPATHLEN];
   225     if (0 == MultiByteToWideChar(CP_UTF8,
   226                                  0,
   227                                  srcUtf8,
   228                                  -1,
   229                                  src,
   230                                  MAXPATHLEN)) {
   231       return false;
   232     }
   234     return AttemptCopyAndLaunch(src);
   235   }
   237   bool
   238   AttemptGRELoadAndLaunch(char* greDir)
   239   {
   240     nsresult rv;
   242     char xpcomDllPath[MAXPATHLEN];
   243     rv = joinPath(xpcomDllPath, greDir, XPCOM_DLL, MAXPATHLEN);
   244     NS_ENSURE_SUCCESS(rv, false);
   246     rv = XPCOMGlueStartup(xpcomDllPath);
   247     NS_ENSURE_SUCCESS(rv, false);
   249     rv = XPCOMGlueLoadXULFunctions(kXULFuncs);
   250     NS_ENSURE_SUCCESS(rv, false);
   252     // NOTE: The GRE has successfully loaded, so we can use XPCOM now
   253     { // Scope for any XPCOM stuff we create
   255       ScopedLogging log;
   257       // Get the path to the runtime.
   258       char rtPath[MAXPATHLEN];
   259       rv = joinPath(rtPath, greDir, kWEBAPPRT_PATH, MAXPATHLEN);
   260       NS_ENSURE_SUCCESS(rv, false);
   262       // Get the path to the runtime's INI file.
   263       char rtIniPath[MAXPATHLEN];
   264       rv = joinPath(rtIniPath, rtPath, kWEBAPPRT_INI, MAXPATHLEN);
   265       NS_ENSURE_SUCCESS(rv, false);
   267       // Load the runtime's INI from its path.
   268       nsCOMPtr<nsIFile> rtINI;
   269       rv = XRE_GetFileFromPath(rtIniPath, getter_AddRefs(rtINI));
   270       NS_ENSURE_SUCCESS(rv, false);
   272       bool exists;
   273       rv = rtINI->Exists(&exists);
   274       if (NS_FAILED(rv) || !exists)
   275         return false;
   277       ScopedXREAppData webShellAppData;
   278       rv = webShellAppData.create(rtINI);
   279       NS_ENSURE_SUCCESS(rv, false);
   281       if (!isProfileOverridden) {
   282         SetAllocatedString(webShellAppData->profile, profile);
   283         SetAllocatedString(webShellAppData->name, profile);
   284       }
   286       nsCOMPtr<nsIFile> directory;
   287       rv = XRE_GetFileFromPath(rtPath, getter_AddRefs(directory));
   288       NS_ENSURE_SUCCESS(rv, false);
   290       nsCOMPtr<nsIFile> xreDir;
   291       rv = XRE_GetFileFromPath(greDir, getter_AddRefs(xreDir));
   292       NS_ENSURE_SUCCESS(rv, false);
   294       xreDir.forget(&webShellAppData->xreDirectory);
   295       NS_IF_RELEASE(webShellAppData->directory);
   296       directory.forget(&webShellAppData->directory);
   298       // There is only XUL.
   299       XRE_main(*pargc, *pargv, webShellAppData, 0);
   300     }
   302     return true;
   303   }
   305   bool
   306   AttemptLoadFromDir(char* firefoxDir)
   307   {
   308     nsresult rv;
   310     // Here we're going to open Firefox's application.ini
   311     char appIniPath[MAXPATHLEN];
   312     rv = joinPath(appIniPath, firefoxDir, kAPP_INI, MAXPATHLEN);
   313     NS_ENSURE_SUCCESS(rv, false);
   315     nsINIParser parser;
   316     rv = parser.Init(appIniPath);
   317     NS_ENSURE_SUCCESS(rv, false);
   319     // Get buildid of FF we're trying to load
   320     char buildid[MAXPATHLEN]; // This isn't a path, so MAXPATHLEN doesn't
   321                               // necessarily make sense, but it's a
   322                               // convenient number to use.
   323     rv = parser.GetString("App",
   324                           "BuildID",
   325                           buildid,
   326                           MAXPATHLEN);
   327     NS_ENSURE_SUCCESS(rv, false);
   329     if (0 == strcmp(buildid, NS_STRINGIFY(GRE_BUILDID))) {
   330       return AttemptGRELoadAndLaunch(firefoxDir);
   331     }
   333     char webAppRTExe[MAXPATHLEN];
   334     rv = joinPath(webAppRTExe, firefoxDir, kAPP_RT, MAXPATHLEN);
   335     NS_ENSURE_SUCCESS(rv, false);
   337     return AttemptCopyAndLaunch(webAppRTExe);
   338   }
   340   bool
   341   GetFirefoxDirFromRegistry(char* firefoxDir)
   342   {
   343     HKEY key;
   344     wchar_t wideGreDir[MAXPATHLEN];
   346     if (ERROR_SUCCESS !=
   347                 RegOpenKeyExW(HKEY_LOCAL_MACHINE,
   348                               L"SOFTWARE\\Microsoft\\Windows"
   349                               L"\\CurrentVersion\\App paths\\firefox.exe",
   350                               0,
   351                               KEY_READ,
   352                               &key)) {
   353       return false;
   354     }
   356     DWORD length = MAXPATHLEN * sizeof(wchar_t);
   357     // XXX: When Vista/XP64 become our minimum supported client, we can use
   358     //      RegGetValue instead
   359     if (ERROR_SUCCESS != RegQueryValueExW(key,
   360                                           L"Path",
   361                                           nullptr,
   362                                           nullptr,
   363                                           reinterpret_cast<BYTE*>(wideGreDir),
   364                                           &length)) {
   365       RegCloseKey(key);
   366       return false;
   367     };
   368     RegCloseKey(key);
   370     // According to this article, we need to write our own null terminator:
   371     // http://msdn.microsoft.com/en-us/library/ms724911%28v=vs.85%29.aspx
   372     length = length / sizeof(wchar_t);
   373     if (wideGreDir[length] != L'\0') {
   374       if (length >= MAXPATHLEN) {
   375         return false;
   376       }
   377       wideGreDir[length] = L'\0';
   378     }
   380     if (0 == WideCharToMultiByte(CP_UTF8,
   381                                  0,
   382                                  wideGreDir,
   383                                  -1,
   384                                  firefoxDir,
   385                                  MAXPATHLEN,
   386                                  nullptr,
   387                                  nullptr)) {
   388       return false;
   389     }
   391     return true;
   392   }
   393 };
   397 //////////////////////////////////////////////////////////////////////////////
   398 // main
   399 //
   400 // Note: XPCOM cannot be used until AttemptGRELoad has returned successfully.
   401 int
   402 main(int argc, char* argv[])
   403 {
   404   pargc = &argc;
   405   pargv = &argv;
   406   nsresult rv;
   407   char buffer[MAXPATHLEN];
   408   wchar_t wbuffer[MAXPATHLEN];
   410   // Set up curEXEPath
   411   if (!GetModuleFileNameW(0, wbuffer, MAXPATHLEN)) {
   412     Output("Couldn't calculate the application directory.");
   413     return 255;
   414   }
   415   wcsncpy(curExePath, wbuffer, MAXPATHLEN);
   417   // Get the current directory into wbuffer
   418   wchar_t* lastSlash = wcsrchr(wbuffer, L'\\');
   419   if (!lastSlash) {
   420     Output("Application directory format not understood.");
   421     return 255;
   422   }
   423   *(++lastSlash) = L'\0';
   425   // Set up backup file path
   426   if (wcslen(wbuffer) + _countof(kAPP_RT_BACKUP) >= MAXPATHLEN) {
   427     Output("Application directory path is too long (couldn't set up backup file path).");
   428   }
   429   wcsncpy(lastSlash, kAPP_RT_BACKUP, _countof(kAPP_RT_BACKUP));
   430   wcsncpy(backupFilePath, wbuffer, MAXPATHLEN);
   432   *lastSlash = L'\0';
   434   // Convert current directory to utf8 and stuff it in buffer
   435   if (0 == WideCharToMultiByte(CP_UTF8,
   436                                0,
   437                                wbuffer,
   438                                -1,
   439                                buffer,
   440                                MAXPATHLEN,
   441                                nullptr,
   442                                nullptr)) {
   443     Output("Application directory could not be processed.");
   444     return 255;
   445   }
   447   // Check if the runtime was executed with the "-profile" argument
   448   for (int i = 1; i < argc; i++) {
   449     if (!strcmp(argv[i], "-profile")) {
   450       isProfileOverridden = true;
   451       break;
   452     }
   453   }
   455   // First attempt at loading Firefox binaries:
   456   //   Check if the webapprt is in the same directory as the Firefox binary.
   457   //   This is the case during WebappRT chrome and content tests.
   458   if (AttemptLoadFromDir(buffer)) {
   459     return 0;
   460   }
   462   // Set up appIniPath with path to webapp.ini.
   463   // This should be in the same directory as the running executable.
   464   char appIniPath[MAXPATHLEN];
   465   if (NS_FAILED(joinPath(appIniPath, buffer, kWEBAPP_INI, MAXPATHLEN))) {
   466     Output("Path to webapp.ini could not be processed.");
   467     return 255;
   468   }
   470   // Open webapp.ini as an INI file (as opposed to using the
   471   // XRE webapp.ini-specific processing we do later)
   472   nsINIParser parser;
   473   if (NS_FAILED(parser.Init(appIniPath))) {
   474     Output("Could not open webapp.ini");
   475     return 255;
   476   }
   478   // Set up our environment to know where webapp.ini was loaded from.
   479   char appEnv[MAXPATHLEN + _countof(kAPP_ENV_PREFIX)];
   480   strcpy(appEnv, kAPP_ENV_PREFIX);
   481   strcpy(appEnv + _countof(kAPP_ENV_PREFIX) - 1, appIniPath);
   482   if (putenv(appEnv)) {
   483     Output("Couldn't set up app environment");
   484     return 255;
   485   }
   487   if (!isProfileOverridden) {
   488     // Get profile dir from webapp.ini
   489     if (NS_FAILED(parser.GetString("Webapp",
   490                                    "Profile",
   491                                    profile,
   492                                    MAXPATHLEN))) {
   493       Output("Unable to retrieve profile from web app INI file");
   494       return 255;
   495     }
   496   }
   498   char firefoxDir[MAXPATHLEN];
   500   // Second attempt at loading Firefox binaries:
   501   //   Get the location of Firefox from our webapp.ini
   503   // XXX: This string better be UTF-8...
   504   rv = parser.GetString("WebappRT",
   505                         "InstallDir",
   506                         firefoxDir,
   507                         MAXPATHLEN);
   508   if (NS_SUCCEEDED(rv)) {
   509     if (AttemptLoadFromDir(firefoxDir)) {
   510       return 0;
   511     }
   512   }
   514   // Third attempt at loading Firefox binaries:
   515   //   Get the location of Firefox from the registry
   516   if (GetFirefoxDirFromRegistry(firefoxDir)) {
   517     if (AttemptLoadFromDir(firefoxDir)) {
   518       // XXX: Write gre dir location to webapp.ini
   519       return 0;
   520     }
   521   }
   523   // We've done all we know how to do to try to find and launch FF
   524   Output("This app requires that Firefox version 16 or above is installed."
   525          " Firefox 16+ has not been detected.");
   526   return 255;
   527 }

mercurial