webapprt/gtk2/webapprt.cpp

Tue, 06 Jan 2015 21:39:09 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Tue, 06 Jan 2015 21:39:09 +0100
branch
TOR_BUG_9701
changeset 8
97036ab72558
permissions
-rw-r--r--

Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.

     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 // GTK headers
     8 #include <gtk/gtk.h>
    10 // Linux headers
    11 #include <fcntl.h>
    12 #include <unistd.h>
    13 #include <dlfcn.h>
    15 // Mozilla headers
    16 #include "nsIFile.h"
    17 #include "nsINIParser.h"
    18 #include "nsXPCOMGlue.h"
    19 #include "nsXPCOMPrivate.h"              // for MAXPATHLEN and XPCOM_DLL
    20 #include "nsXULAppAPI.h"
    21 #include "BinaryPath.h"
    23 const char kAPP_INI[] = "application.ini";
    24 const char kWEBAPP_INI[] = "webapp.ini";
    25 const char kWEBAPP_JSON[] = "webapp.json";
    26 const char kWEBAPP_PACKAGE[] = "application.zip";
    27 const char kWEBAPPRT_INI[] = "webapprt.ini";
    28 const char kWEBAPPRT_PATH[] = "webapprt";
    29 const char kAPP_ENV_VAR[] = "XUL_APP_FILE";
    30 const char kAPP_RT[] = "webapprt-stub";
    32 int* pargc;
    33 char*** pargv;
    34 char profile[MAXPATHLEN];
    35 bool isProfileOverridden = false;
    37 XRE_GetFileFromPathType XRE_GetFileFromPath;
    38 XRE_CreateAppDataType XRE_CreateAppData;
    39 XRE_FreeAppDataType XRE_FreeAppData;
    40 XRE_mainType XRE_main;
    42 const nsDynamicFunctionLoad kXULFuncs[] = {
    43   { "XRE_GetFileFromPath", (NSFuncPtr*) &XRE_GetFileFromPath },
    44   { "XRE_CreateAppData", (NSFuncPtr*) &XRE_CreateAppData },
    45   { "XRE_FreeAppData", (NSFuncPtr*) &XRE_FreeAppData },
    46   { "XRE_main", (NSFuncPtr*) &XRE_main },
    47   { nullptr, nullptr }
    48 };
    50 class ScopedLogging
    51 {
    52   public:
    53     ScopedLogging() { NS_LogInit(); }
    54     ~ScopedLogging() { NS_LogTerm(); }
    55 };
    57 // Copied from toolkit/xre/nsAppData.cpp.
    58 void SetAllocatedString(const char *&str, const char *newvalue)
    59 {
    60   NS_Free(const_cast<char*>(str));
    61   if (newvalue) {
    62     str = NS_strdup(newvalue);
    63   }
    64   else {
    65     str = nullptr;
    66   }
    67 }
    69 // Function to open a dialog box displaying the message provided
    70 void ErrorDialog(const char* message)
    71 {
    72   gtk_init(pargc, pargv);
    74   GtkWidget* dialog = gtk_message_dialog_new(nullptr, GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE, "%s", message);
    75   gtk_window_set_title(GTK_WINDOW(dialog), "Error launching webapp");
    76   gtk_window_set_skip_taskbar_hint(GTK_WINDOW(dialog), false);
    77   gtk_dialog_run(GTK_DIALOG(dialog));
    78   gtk_widget_destroy(dialog);
    79 }
    81 // Function to get the parent dir of a file
    82 void GetDirFromPath(char* parentDir, char* filePath)
    83 {
    84   char* base = strrchr(filePath, '/');
    86   if (!base) {
    87     strcpy(parentDir, ".");
    88   }
    89   else {
    90     while (base > filePath && *base == '/')
    91       base--;
    93     int len = 1 + base - filePath;
    94     strncpy(parentDir, filePath, len);
    95     parentDir[len] = '\0';
    96   }
    97 }
    99 bool CopyFile(const char* inputFile, const char* outputFile)
   100 {
   101   // Open input file
   102   int inputFD = open(inputFile, O_RDONLY);
   103   if (!inputFD)
   104     return false;
   106   // Open output file
   107   int outputFD = creat(outputFile, S_IRWXU);
   108   if (!outputFD)
   109     return false;
   111   // Copy file
   112   char buf[BUFSIZ];
   113   ssize_t bytesRead;
   115   while ((bytesRead = read(inputFD, buf, BUFSIZ)) > 0) {
   116     ssize_t bytesWritten = write(outputFD, buf, bytesRead);
   117     if (bytesWritten < 0) {
   118       bytesRead = -1;
   119       break;
   120     }
   121   }
   123   // Close file descriptors
   124   close(inputFD);
   125   close(outputFD);
   127   return (bytesRead >= 0);
   128 }
   130 bool GRELoadAndLaunch(const char* firefoxDir, bool silentFail)
   131 {
   132   char xpcomDllPath[MAXPATHLEN];
   133   snprintf(xpcomDllPath, MAXPATHLEN, "%s/%s", firefoxDir, XPCOM_DLL);
   135   if (silentFail && access(xpcomDllPath, F_OK) != 0)
   136     return false;
   138   if (NS_FAILED(XPCOMGlueStartup(xpcomDllPath))) {
   139     ErrorDialog("Couldn't load the XPCOM library");
   140     return false;
   141   }
   143   if (NS_FAILED(XPCOMGlueLoadXULFunctions(kXULFuncs))) {
   144     ErrorDialog("Couldn't load libxul");
   145     return false;
   146   }
   148   // NOTE: The GRE has successfully loaded, so we can use XPCOM now
   149   { // Scope for any XPCOM stuff we create
   150     ScopedLogging log;
   152     // Get the path to the runtime
   153     char rtPath[MAXPATHLEN];
   154     snprintf(rtPath, MAXPATHLEN, "%s/%s", firefoxDir, kWEBAPPRT_PATH);
   156     // Get the path to the runtime's INI file
   157     char rtIniPath[MAXPATHLEN];
   158     snprintf(rtIniPath, MAXPATHLEN, "%s/%s", rtPath, kWEBAPPRT_INI);
   160     // Load the runtime's INI from its path
   161     nsCOMPtr<nsIFile> rtINI;
   162     if (NS_FAILED(XRE_GetFileFromPath(rtIniPath, getter_AddRefs(rtINI)))) {
   163       ErrorDialog("Couldn't load the runtime INI");
   164       return false;
   165     }
   167     bool exists;
   168     nsresult rv = rtINI->Exists(&exists);
   169     if (NS_FAILED(rv) || !exists) {
   170       ErrorDialog("The runtime INI doesn't exist");
   171       return false;
   172     }
   174     nsXREAppData *webShellAppData;
   175     if (NS_FAILED(XRE_CreateAppData(rtINI, &webShellAppData))) {
   176       ErrorDialog("Couldn't read WebappRT application.ini");
   177       return false;
   178     }
   180     if (!isProfileOverridden) {
   181       SetAllocatedString(webShellAppData->profile, profile);
   182       // nsXREAppData::name is used for the class name part of the WM_CLASS
   183       // property. Set it so that the DE can match our window to the correct
   184       // launcher.
   185       char programClass[MAXPATHLEN];
   186       snprintf(programClass, MAXPATHLEN, "owa-%s", profile);
   187       SetAllocatedString(webShellAppData->name, programClass);
   188     }
   190     nsCOMPtr<nsIFile> directory;
   191     if (NS_FAILED(XRE_GetFileFromPath(rtPath, getter_AddRefs(directory)))) {
   192       ErrorDialog("Couldn't open runtime directory");
   193       return false;
   194     }
   196     nsCOMPtr<nsIFile> xreDir;
   197     if (NS_FAILED(XRE_GetFileFromPath(firefoxDir, getter_AddRefs(xreDir)))) {
   198       ErrorDialog("Couldn't open XRE directory");
   199       return false;
   200     }
   202     xreDir.forget(&webShellAppData->xreDirectory);
   203     NS_IF_RELEASE(webShellAppData->directory);
   204     directory.forget(&webShellAppData->directory);
   206     XRE_main(*pargc, *pargv, webShellAppData, 0);
   208     XRE_FreeAppData(webShellAppData);
   209   }
   211   return true;
   212 }
   214 void CopyAndRelaunch(const char* firefoxDir, const char* curExePath)
   215 {
   216   char newExePath[MAXPATHLEN];
   217   snprintf(newExePath, MAXPATHLEN, "%s/%s", firefoxDir, kAPP_RT);
   219   if (unlink(curExePath) == -1) {
   220     ErrorDialog("Couldn't remove the old webapprt-stub executable");
   221     return;
   222   }
   224   if (!CopyFile(newExePath, curExePath)) {
   225     ErrorDialog("Couldn't copy the new webapprt-stub executable");
   226     return;
   227   }
   229   execv(curExePath, *pargv);
   231   ErrorDialog("Couldn't execute the new webapprt-stub executable");
   232 }
   234 void RemoveApplication(nsINIParser& parser, const char* curExeDir, const char* profile)  {
   235   if (!isProfileOverridden) {
   236     // Remove the desktop entry file.
   237     char desktopEntryFilePath[MAXPATHLEN];
   239     char* dataDir = getenv("XDG_DATA_HOME");
   241     if (dataDir && *dataDir) {
   242       snprintf(desktopEntryFilePath, MAXPATHLEN, "%s/applications/owa-%s.desktop", dataDir, profile);
   243     } else {
   244       char* home = getenv("HOME");
   245       snprintf(desktopEntryFilePath, MAXPATHLEN, "%s/.local/share/applications/owa-%s.desktop", home, profile);
   246     }
   248     unlink(desktopEntryFilePath);
   249   }
   251   // Remove the files from the installation directory.
   252   char webAppIniPath[MAXPATHLEN];
   253   snprintf(webAppIniPath, MAXPATHLEN, "%s/%s", curExeDir, kWEBAPP_INI);
   254   unlink(webAppIniPath);
   256   char curExePath[MAXPATHLEN];
   257   snprintf(curExePath, MAXPATHLEN, "%s/%s", curExeDir, kAPP_RT);
   258   unlink(curExePath);
   260   char webAppJsonPath[MAXPATHLEN];
   261   snprintf(webAppJsonPath, MAXPATHLEN, "%s/%s", curExeDir, kWEBAPP_JSON);
   262   unlink(webAppJsonPath);
   264   char iconPath[MAXPATHLEN];
   265   snprintf(iconPath, MAXPATHLEN, "%s/icon.png", curExeDir);
   266   unlink(iconPath);
   268   char packagePath[MAXPATHLEN];
   269   snprintf(packagePath, MAXPATHLEN, "%s/%s", curExeDir, kWEBAPP_PACKAGE);
   270   unlink(packagePath);
   272   char appName[MAXPATHLEN];
   273   if (NS_FAILED(parser.GetString("Webapp", "Name", appName, MAXPATHLEN))) {
   274     strcpy(appName, profile);
   275   }
   277   char uninstallMsg[MAXPATHLEN];
   278   if (NS_SUCCEEDED(parser.GetString("Webapp", "UninstallMsg", uninstallMsg, MAXPATHLEN))) {
   279     /**
   280      * The only difference between libnotify.so.4 and libnotify.so.1 for these symbols
   281      * is that notify_notification_new takes three arguments in libnotify.so.4 and
   282      * four in libnotify.so.1.
   283      * Passing the fourth argument as nullptr is binary compatible.
   284      */
   285     typedef void  (*notify_init_t)(const char*);
   286     typedef void* (*notify_notification_new_t)(const char*, const char*, const char*, const char*);
   287     typedef void  (*notify_notification_show_t)(void*, void**);
   289     void *handle = dlopen("libnotify.so.4", RTLD_LAZY);
   290     if (!handle) {
   291       handle = dlopen("libnotify.so.1", RTLD_LAZY);
   292       if (!handle)
   293         return;
   294     }
   296     notify_init_t nn_init = (notify_init_t)(uintptr_t)dlsym(handle, "notify_init");
   297     notify_notification_new_t nn_new = (notify_notification_new_t)(uintptr_t)dlsym(handle, "notify_notification_new");
   298     notify_notification_show_t nn_show = (notify_notification_show_t)(uintptr_t)dlsym(handle, "notify_notification_show");
   299     if (!nn_init || !nn_new || !nn_show) {
   300       dlclose(handle);
   301       return;
   302     }
   304     nn_init(appName);
   306     void* n = nn_new(uninstallMsg, nullptr, "dialog-information", nullptr);
   308     nn_show(n, nullptr);
   310     dlclose(handle);
   311   }
   312 }
   314 int main(int argc, char *argv[])
   315 {
   316   pargc = &argc;
   317   pargv = &argv;
   319   // Get current executable path
   320   char curExePath[MAXPATHLEN];
   321   if (NS_FAILED(mozilla::BinaryPath::Get(argv[0], curExePath))) {
   322     ErrorDialog("Couldn't read current executable path");
   323     return 255;
   324   }
   325   char curExeDir[MAXPATHLEN];
   326   GetDirFromPath(curExeDir, curExePath);
   328   bool removeApp = false;
   329   for (int i = 1; i < argc; i++) {
   330     if (!strcmp(argv[i], "-profile")) {
   331       isProfileOverridden = true;
   332     }
   333     else if (!strcmp(argv[i], "-remove")) {
   334       removeApp = true;
   335     }
   336   }
   338   char firefoxDir[MAXPATHLEN];
   340   // Check if Firefox is in the same directory as the webapp runtime.
   341   // This is the case for webapprt chrome and content tests.
   342   if (GRELoadAndLaunch(curExeDir, true)) {
   343     return 0;
   344   }
   346   // Set up webAppIniPath with path to webapp.ini
   347   char webAppIniPath[MAXPATHLEN];
   348   snprintf(webAppIniPath, MAXPATHLEN, "%s/%s", curExeDir, kWEBAPP_INI);
   350   // Open webapp.ini as an INI file
   351   nsINIParser parser;
   352   if (NS_FAILED(parser.Init(webAppIniPath))) {
   353     ErrorDialog("Couldn't open webapp.ini");
   354     return 255;
   355   }
   357   // Set up our environment to know where webapp.ini was loaded from
   358   if (setenv(kAPP_ENV_VAR, webAppIniPath, 1) == -1) {
   359     ErrorDialog("Couldn't set up app environment");
   360     return 255;
   361   }
   363   // Get profile dir from webapp.ini
   364   if (NS_FAILED(parser.GetString("Webapp", "Profile", profile, MAXPATHLEN))) {
   365     ErrorDialog("Couldn't retrieve profile from web app INI file");
   366     return 255;
   367   }
   369   if (removeApp) {
   370     RemoveApplication(parser, curExeDir, profile);
   371     return 0;
   372   }
   374   // Get the location of Firefox from our webapp.ini
   375   if (NS_FAILED(parser.GetString("WebappRT", "InstallDir", firefoxDir, MAXPATHLEN))) {
   376     ErrorDialog("Couldn't find your Firefox install directory.");
   377     return 255;
   378   }
   380   // Set up appIniPath with path to application.ini.
   381   // This is in the Firefox installation directory.
   382   char appIniPath[MAXPATHLEN];
   383   snprintf(appIniPath, MAXPATHLEN, "%s/%s", firefoxDir, kAPP_INI);
   385   if (NS_FAILED(parser.Init(appIniPath))) {
   386     ErrorDialog("This app requires that Firefox version 16 or above is installed."
   387                 " Firefox 16+ has not been detected.");
   388     return 255;
   389   }
   391   // Get buildid of Firefox we're trying to load (MAXPATHLEN only for convenience)
   392   char buildid[MAXPATHLEN];
   393   if (NS_FAILED(parser.GetString("App", "BuildID", buildid, MAXPATHLEN))) {
   394     ErrorDialog("Couldn't read BuildID from Firefox application.ini");
   395     return 255;
   396   }
   398   // If WebAppRT version == Firefox version, load XUL and execute the application
   399   if (!strcmp(buildid, NS_STRINGIFY(GRE_BUILDID))) {
   400     if (GRELoadAndLaunch(firefoxDir, false))
   401       return 0;
   402   }
   403   // Else, copy WebAppRT from Firefox installation and re-execute the process
   404   else
   405     CopyAndRelaunch(firefoxDir, curExePath);
   407   return 255;
   408 }

mercurial