toolkit/crashreporter/client/crashreporter_gtk_common.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

michael@0 1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
michael@0 2 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 3 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 5
michael@0 6 #include "crashreporter.h"
michael@0 7
michael@0 8 #include <unistd.h>
michael@0 9 #include <dlfcn.h>
michael@0 10 #include <errno.h>
michael@0 11 #include <glib.h>
michael@0 12 #include <gtk/gtk.h>
michael@0 13 #include <signal.h>
michael@0 14 #include <stdio.h>
michael@0 15 #include <stdlib.h>
michael@0 16 #include <string.h>
michael@0 17 #include <sys/stat.h>
michael@0 18 #include <sys/types.h>
michael@0 19 #include <sys/wait.h>
michael@0 20 #include <gdk/gdkkeysyms.h>
michael@0 21
michael@0 22 #include <algorithm>
michael@0 23 #include <string>
michael@0 24 #include <vector>
michael@0 25
michael@0 26 #include "mozilla/NullPtr.h"
michael@0 27 #include "common/linux/http_upload.h"
michael@0 28 #include "crashreporter.h"
michael@0 29 #include "crashreporter_gtk_common.h"
michael@0 30
michael@0 31 #ifndef GDK_KEY_Escape
michael@0 32 #define GDK_KEY_Escape GDK_Escape
michael@0 33 #endif
michael@0 34
michael@0 35 using std::string;
michael@0 36 using std::vector;
michael@0 37
michael@0 38 using namespace CrashReporter;
michael@0 39
michael@0 40 GtkWidget* gWindow = 0;
michael@0 41 GtkWidget* gSubmitReportCheck = 0;
michael@0 42 GtkWidget* gIncludeURLCheck = 0;
michael@0 43 GtkWidget* gThrobber = 0;
michael@0 44 GtkWidget* gProgressLabel = 0;
michael@0 45 GtkWidget* gCloseButton = 0;
michael@0 46 GtkWidget* gRestartButton = 0;
michael@0 47
michael@0 48 bool gInitialized = false;
michael@0 49 bool gDidTrySend = false;
michael@0 50 string gDumpFile;
michael@0 51 StringTable gQueryParameters;
michael@0 52 string gHttpProxy;
michael@0 53 string gAuth;
michael@0 54 string gCACertificateFile;
michael@0 55 string gSendURL;
michael@0 56 string gURLParameter;
michael@0 57 vector<string> gRestartArgs;
michael@0 58 GThread* gSendThreadID;
michael@0 59
michael@0 60 // From crashreporter_linux.cpp
michael@0 61 void SaveSettings();
michael@0 62 void SendReport();
michael@0 63 void TryInitGnome();
michael@0 64 void UpdateSubmit();
michael@0 65
michael@0 66 static bool RestartApplication()
michael@0 67 {
michael@0 68 char** argv = reinterpret_cast<char**>(
michael@0 69 malloc(sizeof(char*) * (gRestartArgs.size() + 1)));
michael@0 70
michael@0 71 if (!argv) return false;
michael@0 72
michael@0 73 unsigned int i;
michael@0 74 for (i = 0; i < gRestartArgs.size(); i++) {
michael@0 75 argv[i] = (char*)gRestartArgs[i].c_str();
michael@0 76 }
michael@0 77 argv[i] = 0;
michael@0 78
michael@0 79 pid_t pid = fork();
michael@0 80 if (pid == -1)
michael@0 81 return false;
michael@0 82 else if (pid == 0) {
michael@0 83 (void)execv(argv[0], argv);
michael@0 84 _exit(1);
michael@0 85 }
michael@0 86
michael@0 87 free(argv);
michael@0 88
michael@0 89 return true;
michael@0 90 }
michael@0 91
michael@0 92 // Quit the app, used as a timeout callback
michael@0 93 static gboolean CloseApp(gpointer data)
michael@0 94 {
michael@0 95 gtk_main_quit();
michael@0 96 g_thread_join(gSendThreadID);
michael@0 97 return FALSE;
michael@0 98 }
michael@0 99
michael@0 100 static gboolean ReportCompleted(gpointer success)
michael@0 101 {
michael@0 102 gtk_widget_hide(gThrobber);
michael@0 103 string str = success ? gStrings[ST_REPORTSUBMITSUCCESS]
michael@0 104 : gStrings[ST_SUBMITFAILED];
michael@0 105 gtk_label_set_text(GTK_LABEL(gProgressLabel), str.c_str());
michael@0 106 g_timeout_add(5000, CloseApp, 0);
michael@0 107 return FALSE;
michael@0 108 }
michael@0 109
michael@0 110 #ifdef MOZ_ENABLE_GCONF
michael@0 111 #define HTTP_PROXY_DIR "/system/http_proxy"
michael@0 112
michael@0 113 void LoadProxyinfo()
michael@0 114 {
michael@0 115 class GConfClient;
michael@0 116 typedef GConfClient * (*_gconf_default_fn)();
michael@0 117 typedef gboolean (*_gconf_bool_fn)(GConfClient *, const gchar *, GError **);
michael@0 118 typedef gint (*_gconf_int_fn)(GConfClient *, const gchar *, GError **);
michael@0 119 typedef gchar * (*_gconf_string_fn)(GConfClient *, const gchar *, GError **);
michael@0 120
michael@0 121 if (getenv ("http_proxy"))
michael@0 122 return; // libcurl can use the value from the environment
michael@0 123
michael@0 124 static void* gconfLib = dlopen("libgconf-2.so.4", RTLD_LAZY);
michael@0 125 if (!gconfLib)
michael@0 126 return;
michael@0 127
michael@0 128 _gconf_default_fn gconf_client_get_default =
michael@0 129 (_gconf_default_fn)dlsym(gconfLib, "gconf_client_get_default");
michael@0 130 _gconf_bool_fn gconf_client_get_bool =
michael@0 131 (_gconf_bool_fn)dlsym(gconfLib, "gconf_client_get_bool");
michael@0 132 _gconf_int_fn gconf_client_get_int =
michael@0 133 (_gconf_int_fn)dlsym(gconfLib, "gconf_client_get_int");
michael@0 134 _gconf_string_fn gconf_client_get_string =
michael@0 135 (_gconf_string_fn)dlsym(gconfLib, "gconf_client_get_string");
michael@0 136
michael@0 137 if(!(gconf_client_get_default &&
michael@0 138 gconf_client_get_bool &&
michael@0 139 gconf_client_get_int &&
michael@0 140 gconf_client_get_string)) {
michael@0 141 dlclose(gconfLib);
michael@0 142 return;
michael@0 143 }
michael@0 144
michael@0 145 GConfClient *conf = gconf_client_get_default();
michael@0 146
michael@0 147 if (gconf_client_get_bool(conf, HTTP_PROXY_DIR "/use_http_proxy", nullptr)) {
michael@0 148 gint port;
michael@0 149 gchar *host = nullptr, *httpproxy = nullptr;
michael@0 150
michael@0 151 host = gconf_client_get_string(conf, HTTP_PROXY_DIR "/host", nullptr);
michael@0 152 port = gconf_client_get_int(conf, HTTP_PROXY_DIR "/port", nullptr);
michael@0 153
michael@0 154 if (port && host && *host != '\0') {
michael@0 155 httpproxy = g_strdup_printf("http://%s:%d/", host, port);
michael@0 156 gHttpProxy = httpproxy;
michael@0 157 }
michael@0 158
michael@0 159 g_free(host);
michael@0 160 g_free(httpproxy);
michael@0 161
michael@0 162 if (gconf_client_get_bool(conf, HTTP_PROXY_DIR "/use_authentication",
michael@0 163 nullptr)) {
michael@0 164 gchar *user, *password, *auth = nullptr;
michael@0 165
michael@0 166 user = gconf_client_get_string(conf,
michael@0 167 HTTP_PROXY_DIR "/authentication_user",
michael@0 168 nullptr);
michael@0 169 password = gconf_client_get_string(conf,
michael@0 170 HTTP_PROXY_DIR
michael@0 171 "/authentication_password",
michael@0 172 nullptr);
michael@0 173
michael@0 174 if (user && password) {
michael@0 175 auth = g_strdup_printf("%s:%s", user, password);
michael@0 176 gAuth = auth;
michael@0 177 }
michael@0 178
michael@0 179 g_free(user);
michael@0 180 g_free(password);
michael@0 181 g_free(auth);
michael@0 182 }
michael@0 183 }
michael@0 184
michael@0 185 g_object_unref(conf);
michael@0 186
michael@0 187 // Don't dlclose gconfLib as libORBit-2 uses atexit().
michael@0 188 }
michael@0 189 #endif
michael@0 190
michael@0 191 gpointer SendThread(gpointer args)
michael@0 192 {
michael@0 193 string response, error;
michael@0 194 long response_code;
michael@0 195
michael@0 196 bool success = google_breakpad::HTTPUpload::SendRequest
michael@0 197 (gSendURL,
michael@0 198 gQueryParameters,
michael@0 199 gDumpFile,
michael@0 200 "upload_file_minidump",
michael@0 201 gHttpProxy, gAuth,
michael@0 202 gCACertificateFile,
michael@0 203 &response,
michael@0 204 &response_code,
michael@0 205 &error);
michael@0 206 if (success) {
michael@0 207 LogMessage("Crash report submitted successfully");
michael@0 208 }
michael@0 209 else {
michael@0 210 LogMessage("Crash report submission failed: " + error);
michael@0 211 }
michael@0 212
michael@0 213 SendCompleted(success, response);
michael@0 214 // Apparently glib is threadsafe, and will schedule this
michael@0 215 // on the main thread, see:
michael@0 216 // http://library.gnome.org/devel/gtk-faq/stable/x499.html
michael@0 217 g_idle_add(ReportCompleted, (gpointer)success);
michael@0 218
michael@0 219 return nullptr;
michael@0 220 }
michael@0 221
michael@0 222 gboolean WindowDeleted(GtkWidget* window,
michael@0 223 GdkEvent* event,
michael@0 224 gpointer userData)
michael@0 225 {
michael@0 226 SaveSettings();
michael@0 227 gtk_main_quit();
michael@0 228 return TRUE;
michael@0 229 }
michael@0 230
michael@0 231 gboolean check_escape(GtkWidget* window,
michael@0 232 GdkEventKey* event,
michael@0 233 gpointer userData)
michael@0 234 {
michael@0 235 if (event->keyval == GDK_KEY_Escape) {
michael@0 236 gtk_main_quit();
michael@0 237 return TRUE;
michael@0 238 }
michael@0 239 return FALSE;
michael@0 240 }
michael@0 241
michael@0 242 static void MaybeSubmitReport()
michael@0 243 {
michael@0 244 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(gSubmitReportCheck))) {
michael@0 245 gDidTrySend = true;
michael@0 246 SendReport();
michael@0 247 } else {
michael@0 248 gtk_main_quit();
michael@0 249 }
michael@0 250 }
michael@0 251
michael@0 252 void CloseClicked(GtkButton* button,
michael@0 253 gpointer userData)
michael@0 254 {
michael@0 255 SaveSettings();
michael@0 256 MaybeSubmitReport();
michael@0 257 }
michael@0 258
michael@0 259 void RestartClicked(GtkButton* button,
michael@0 260 gpointer userData)
michael@0 261 {
michael@0 262 SaveSettings();
michael@0 263 RestartApplication();
michael@0 264 MaybeSubmitReport();
michael@0 265 }
michael@0 266
michael@0 267 static void UpdateURL()
michael@0 268 {
michael@0 269 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(gIncludeURLCheck))) {
michael@0 270 gQueryParameters["URL"] = gURLParameter;
michael@0 271 } else {
michael@0 272 gQueryParameters.erase("URL");
michael@0 273 }
michael@0 274 }
michael@0 275
michael@0 276 void SubmitReportChecked(GtkButton* sender, gpointer userData)
michael@0 277 {
michael@0 278 UpdateSubmit();
michael@0 279 }
michael@0 280
michael@0 281 void IncludeURLClicked(GtkButton* sender, gpointer userData)
michael@0 282 {
michael@0 283 UpdateURL();
michael@0 284 }
michael@0 285
michael@0 286 /* === Crashreporter UI Functions === */
michael@0 287
michael@0 288 bool UIInit()
michael@0 289 {
michael@0 290 // breakpad probably left us with blocked signals, unblock them here
michael@0 291 sigset_t signals, old;
michael@0 292 sigfillset(&signals);
michael@0 293 sigprocmask(SIG_UNBLOCK, &signals, &old);
michael@0 294
michael@0 295 // tell glib we're going to use threads
michael@0 296 g_thread_init(nullptr);
michael@0 297
michael@0 298 if (gtk_init_check(&gArgc, &gArgv)) {
michael@0 299 gInitialized = true;
michael@0 300
michael@0 301 if (gStrings.find("isRTL") != gStrings.end() &&
michael@0 302 gStrings["isRTL"] == "yes")
michael@0 303 gtk_widget_set_default_direction(GTK_TEXT_DIR_RTL);
michael@0 304
michael@0 305 return true;
michael@0 306 }
michael@0 307
michael@0 308 return false;
michael@0 309 }
michael@0 310
michael@0 311 void UIShowDefaultUI()
michael@0 312 {
michael@0 313 GtkWidget* errorDialog =
michael@0 314 gtk_message_dialog_new(nullptr, GTK_DIALOG_MODAL,
michael@0 315 GTK_MESSAGE_ERROR,
michael@0 316 GTK_BUTTONS_CLOSE,
michael@0 317 "%s", gStrings[ST_CRASHREPORTERDEFAULT].c_str());
michael@0 318
michael@0 319 gtk_window_set_title(GTK_WINDOW(errorDialog),
michael@0 320 gStrings[ST_CRASHREPORTERTITLE].c_str());
michael@0 321 gtk_dialog_run(GTK_DIALOG(errorDialog));
michael@0 322 }
michael@0 323
michael@0 324 void UIError_impl(const string& message)
michael@0 325 {
michael@0 326 if (!gInitialized) {
michael@0 327 // Didn't initialize, this is the best we can do
michael@0 328 printf("Error: %s\n", message.c_str());
michael@0 329 return;
michael@0 330 }
michael@0 331
michael@0 332 GtkWidget* errorDialog =
michael@0 333 gtk_message_dialog_new(nullptr, GTK_DIALOG_MODAL,
michael@0 334 GTK_MESSAGE_ERROR,
michael@0 335 GTK_BUTTONS_CLOSE,
michael@0 336 "%s", message.c_str());
michael@0 337
michael@0 338 gtk_window_set_title(GTK_WINDOW(errorDialog),
michael@0 339 gStrings[ST_CRASHREPORTERTITLE].c_str());
michael@0 340 gtk_dialog_run(GTK_DIALOG(errorDialog));
michael@0 341 }
michael@0 342
michael@0 343 bool UIGetIniPath(string& path)
michael@0 344 {
michael@0 345 path = gArgv[0];
michael@0 346 path.append(".ini");
michael@0 347
michael@0 348 return true;
michael@0 349 }
michael@0 350
michael@0 351 /*
michael@0 352 * Settings are stored in ~/.vendor/product, or
michael@0 353 * ~/.product if vendor is empty.
michael@0 354 */
michael@0 355 bool UIGetSettingsPath(const string& vendor,
michael@0 356 const string& product,
michael@0 357 string& settingsPath)
michael@0 358 {
michael@0 359 char* home = getenv("HOME");
michael@0 360
michael@0 361 if (!home)
michael@0 362 return false;
michael@0 363
michael@0 364 settingsPath = home;
michael@0 365 settingsPath += "/.";
michael@0 366 if (!vendor.empty()) {
michael@0 367 string lc_vendor;
michael@0 368 std::transform(vendor.begin(), vendor.end(), back_inserter(lc_vendor),
michael@0 369 (int(*)(int)) std::tolower);
michael@0 370 settingsPath += lc_vendor + "/";
michael@0 371 }
michael@0 372 string lc_product;
michael@0 373 std::transform(product.begin(), product.end(), back_inserter(lc_product),
michael@0 374 (int(*)(int)) std::tolower);
michael@0 375 settingsPath += lc_product + "/Crash Reports";
michael@0 376 return true;
michael@0 377 }
michael@0 378
michael@0 379 bool UIEnsurePathExists(const string& path)
michael@0 380 {
michael@0 381 int ret = mkdir(path.c_str(), S_IRWXU);
michael@0 382 int e = errno;
michael@0 383 if (ret == -1 && e != EEXIST)
michael@0 384 return false;
michael@0 385
michael@0 386 return true;
michael@0 387 }
michael@0 388
michael@0 389 bool UIFileExists(const string& path)
michael@0 390 {
michael@0 391 struct stat sb;
michael@0 392 int ret = stat(path.c_str(), &sb);
michael@0 393 if (ret == -1 || !(sb.st_mode & S_IFREG))
michael@0 394 return false;
michael@0 395
michael@0 396 return true;
michael@0 397 }
michael@0 398
michael@0 399 bool UIMoveFile(const string& file, const string& newfile)
michael@0 400 {
michael@0 401 if (!rename(file.c_str(), newfile.c_str()))
michael@0 402 return true;
michael@0 403 if (errno != EXDEV)
michael@0 404 return false;
michael@0 405
michael@0 406 // use system /bin/mv instead, time to fork
michael@0 407 pid_t pID = vfork();
michael@0 408 if (pID < 0) {
michael@0 409 // Failed to fork
michael@0 410 return false;
michael@0 411 }
michael@0 412 if (pID == 0) {
michael@0 413 char* const args[4] = {
michael@0 414 "mv",
michael@0 415 strdup(file.c_str()),
michael@0 416 strdup(newfile.c_str()),
michael@0 417 0
michael@0 418 };
michael@0 419 if (args[1] && args[2])
michael@0 420 execve("/bin/mv", args, 0);
michael@0 421 if (args[1])
michael@0 422 free(args[1]);
michael@0 423 if (args[2])
michael@0 424 free(args[2]);
michael@0 425 exit(-1);
michael@0 426 }
michael@0 427 int status;
michael@0 428 waitpid(pID, &status, 0);
michael@0 429 return UIFileExists(newfile);
michael@0 430 }
michael@0 431
michael@0 432 bool UIDeleteFile(const string& file)
michael@0 433 {
michael@0 434 return (unlink(file.c_str()) != -1);
michael@0 435 }
michael@0 436
michael@0 437 std::ifstream* UIOpenRead(const string& filename)
michael@0 438 {
michael@0 439 return new std::ifstream(filename.c_str(), std::ios::in);
michael@0 440 }
michael@0 441
michael@0 442 std::ofstream* UIOpenWrite(const string& filename, bool append) // append=false
michael@0 443 {
michael@0 444 return new std::ofstream(filename.c_str(),
michael@0 445 append ? std::ios::out | std::ios::app
michael@0 446 : std::ios::out);
michael@0 447 }

mercurial