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.

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

mercurial