1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/toolkit/crashreporter/client/crashreporter_gtk_common.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,447 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +#include "crashreporter.h" 1.10 + 1.11 +#include <unistd.h> 1.12 +#include <dlfcn.h> 1.13 +#include <errno.h> 1.14 +#include <glib.h> 1.15 +#include <gtk/gtk.h> 1.16 +#include <signal.h> 1.17 +#include <stdio.h> 1.18 +#include <stdlib.h> 1.19 +#include <string.h> 1.20 +#include <sys/stat.h> 1.21 +#include <sys/types.h> 1.22 +#include <sys/wait.h> 1.23 +#include <gdk/gdkkeysyms.h> 1.24 + 1.25 +#include <algorithm> 1.26 +#include <string> 1.27 +#include <vector> 1.28 + 1.29 +#include "mozilla/NullPtr.h" 1.30 +#include "common/linux/http_upload.h" 1.31 +#include "crashreporter.h" 1.32 +#include "crashreporter_gtk_common.h" 1.33 + 1.34 +#ifndef GDK_KEY_Escape 1.35 +#define GDK_KEY_Escape GDK_Escape 1.36 +#endif 1.37 + 1.38 +using std::string; 1.39 +using std::vector; 1.40 + 1.41 +using namespace CrashReporter; 1.42 + 1.43 +GtkWidget* gWindow = 0; 1.44 +GtkWidget* gSubmitReportCheck = 0; 1.45 +GtkWidget* gIncludeURLCheck = 0; 1.46 +GtkWidget* gThrobber = 0; 1.47 +GtkWidget* gProgressLabel = 0; 1.48 +GtkWidget* gCloseButton = 0; 1.49 +GtkWidget* gRestartButton = 0; 1.50 + 1.51 +bool gInitialized = false; 1.52 +bool gDidTrySend = false; 1.53 +string gDumpFile; 1.54 +StringTable gQueryParameters; 1.55 +string gHttpProxy; 1.56 +string gAuth; 1.57 +string gCACertificateFile; 1.58 +string gSendURL; 1.59 +string gURLParameter; 1.60 +vector<string> gRestartArgs; 1.61 +GThread* gSendThreadID; 1.62 + 1.63 +// From crashreporter_linux.cpp 1.64 +void SaveSettings(); 1.65 +void SendReport(); 1.66 +void TryInitGnome(); 1.67 +void UpdateSubmit(); 1.68 + 1.69 +static bool RestartApplication() 1.70 +{ 1.71 + char** argv = reinterpret_cast<char**>( 1.72 + malloc(sizeof(char*) * (gRestartArgs.size() + 1))); 1.73 + 1.74 + if (!argv) return false; 1.75 + 1.76 + unsigned int i; 1.77 + for (i = 0; i < gRestartArgs.size(); i++) { 1.78 + argv[i] = (char*)gRestartArgs[i].c_str(); 1.79 + } 1.80 + argv[i] = 0; 1.81 + 1.82 + pid_t pid = fork(); 1.83 + if (pid == -1) 1.84 + return false; 1.85 + else if (pid == 0) { 1.86 + (void)execv(argv[0], argv); 1.87 + _exit(1); 1.88 + } 1.89 + 1.90 + free(argv); 1.91 + 1.92 + return true; 1.93 +} 1.94 + 1.95 +// Quit the app, used as a timeout callback 1.96 +static gboolean CloseApp(gpointer data) 1.97 +{ 1.98 + gtk_main_quit(); 1.99 + g_thread_join(gSendThreadID); 1.100 + return FALSE; 1.101 +} 1.102 + 1.103 +static gboolean ReportCompleted(gpointer success) 1.104 +{ 1.105 + gtk_widget_hide(gThrobber); 1.106 + string str = success ? gStrings[ST_REPORTSUBMITSUCCESS] 1.107 + : gStrings[ST_SUBMITFAILED]; 1.108 + gtk_label_set_text(GTK_LABEL(gProgressLabel), str.c_str()); 1.109 + g_timeout_add(5000, CloseApp, 0); 1.110 + return FALSE; 1.111 +} 1.112 + 1.113 +#ifdef MOZ_ENABLE_GCONF 1.114 +#define HTTP_PROXY_DIR "/system/http_proxy" 1.115 + 1.116 +void LoadProxyinfo() 1.117 +{ 1.118 + class GConfClient; 1.119 + typedef GConfClient * (*_gconf_default_fn)(); 1.120 + typedef gboolean (*_gconf_bool_fn)(GConfClient *, const gchar *, GError **); 1.121 + typedef gint (*_gconf_int_fn)(GConfClient *, const gchar *, GError **); 1.122 + typedef gchar * (*_gconf_string_fn)(GConfClient *, const gchar *, GError **); 1.123 + 1.124 + if (getenv ("http_proxy")) 1.125 + return; // libcurl can use the value from the environment 1.126 + 1.127 + static void* gconfLib = dlopen("libgconf-2.so.4", RTLD_LAZY); 1.128 + if (!gconfLib) 1.129 + return; 1.130 + 1.131 + _gconf_default_fn gconf_client_get_default = 1.132 + (_gconf_default_fn)dlsym(gconfLib, "gconf_client_get_default"); 1.133 + _gconf_bool_fn gconf_client_get_bool = 1.134 + (_gconf_bool_fn)dlsym(gconfLib, "gconf_client_get_bool"); 1.135 + _gconf_int_fn gconf_client_get_int = 1.136 + (_gconf_int_fn)dlsym(gconfLib, "gconf_client_get_int"); 1.137 + _gconf_string_fn gconf_client_get_string = 1.138 + (_gconf_string_fn)dlsym(gconfLib, "gconf_client_get_string"); 1.139 + 1.140 + if(!(gconf_client_get_default && 1.141 + gconf_client_get_bool && 1.142 + gconf_client_get_int && 1.143 + gconf_client_get_string)) { 1.144 + dlclose(gconfLib); 1.145 + return; 1.146 + } 1.147 + 1.148 + GConfClient *conf = gconf_client_get_default(); 1.149 + 1.150 + if (gconf_client_get_bool(conf, HTTP_PROXY_DIR "/use_http_proxy", nullptr)) { 1.151 + gint port; 1.152 + gchar *host = nullptr, *httpproxy = nullptr; 1.153 + 1.154 + host = gconf_client_get_string(conf, HTTP_PROXY_DIR "/host", nullptr); 1.155 + port = gconf_client_get_int(conf, HTTP_PROXY_DIR "/port", nullptr); 1.156 + 1.157 + if (port && host && *host != '\0') { 1.158 + httpproxy = g_strdup_printf("http://%s:%d/", host, port); 1.159 + gHttpProxy = httpproxy; 1.160 + } 1.161 + 1.162 + g_free(host); 1.163 + g_free(httpproxy); 1.164 + 1.165 + if (gconf_client_get_bool(conf, HTTP_PROXY_DIR "/use_authentication", 1.166 + nullptr)) { 1.167 + gchar *user, *password, *auth = nullptr; 1.168 + 1.169 + user = gconf_client_get_string(conf, 1.170 + HTTP_PROXY_DIR "/authentication_user", 1.171 + nullptr); 1.172 + password = gconf_client_get_string(conf, 1.173 + HTTP_PROXY_DIR 1.174 + "/authentication_password", 1.175 + nullptr); 1.176 + 1.177 + if (user && password) { 1.178 + auth = g_strdup_printf("%s:%s", user, password); 1.179 + gAuth = auth; 1.180 + } 1.181 + 1.182 + g_free(user); 1.183 + g_free(password); 1.184 + g_free(auth); 1.185 + } 1.186 + } 1.187 + 1.188 + g_object_unref(conf); 1.189 + 1.190 + // Don't dlclose gconfLib as libORBit-2 uses atexit(). 1.191 +} 1.192 +#endif 1.193 + 1.194 +gpointer SendThread(gpointer args) 1.195 +{ 1.196 + string response, error; 1.197 + long response_code; 1.198 + 1.199 + bool success = google_breakpad::HTTPUpload::SendRequest 1.200 + (gSendURL, 1.201 + gQueryParameters, 1.202 + gDumpFile, 1.203 + "upload_file_minidump", 1.204 + gHttpProxy, gAuth, 1.205 + gCACertificateFile, 1.206 + &response, 1.207 + &response_code, 1.208 + &error); 1.209 + if (success) { 1.210 + LogMessage("Crash report submitted successfully"); 1.211 + } 1.212 + else { 1.213 + LogMessage("Crash report submission failed: " + error); 1.214 + } 1.215 + 1.216 + SendCompleted(success, response); 1.217 + // Apparently glib is threadsafe, and will schedule this 1.218 + // on the main thread, see: 1.219 + // http://library.gnome.org/devel/gtk-faq/stable/x499.html 1.220 + g_idle_add(ReportCompleted, (gpointer)success); 1.221 + 1.222 + return nullptr; 1.223 +} 1.224 + 1.225 +gboolean WindowDeleted(GtkWidget* window, 1.226 + GdkEvent* event, 1.227 + gpointer userData) 1.228 +{ 1.229 + SaveSettings(); 1.230 + gtk_main_quit(); 1.231 + return TRUE; 1.232 +} 1.233 + 1.234 +gboolean check_escape(GtkWidget* window, 1.235 + GdkEventKey* event, 1.236 + gpointer userData) 1.237 +{ 1.238 + if (event->keyval == GDK_KEY_Escape) { 1.239 + gtk_main_quit(); 1.240 + return TRUE; 1.241 + } 1.242 + return FALSE; 1.243 +} 1.244 + 1.245 +static void MaybeSubmitReport() 1.246 +{ 1.247 + if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(gSubmitReportCheck))) { 1.248 + gDidTrySend = true; 1.249 + SendReport(); 1.250 + } else { 1.251 + gtk_main_quit(); 1.252 + } 1.253 +} 1.254 + 1.255 +void CloseClicked(GtkButton* button, 1.256 + gpointer userData) 1.257 +{ 1.258 + SaveSettings(); 1.259 + MaybeSubmitReport(); 1.260 +} 1.261 + 1.262 +void RestartClicked(GtkButton* button, 1.263 + gpointer userData) 1.264 +{ 1.265 + SaveSettings(); 1.266 + RestartApplication(); 1.267 + MaybeSubmitReport(); 1.268 +} 1.269 + 1.270 +static void UpdateURL() 1.271 +{ 1.272 + if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(gIncludeURLCheck))) { 1.273 + gQueryParameters["URL"] = gURLParameter; 1.274 + } else { 1.275 + gQueryParameters.erase("URL"); 1.276 + } 1.277 +} 1.278 + 1.279 +void SubmitReportChecked(GtkButton* sender, gpointer userData) 1.280 +{ 1.281 + UpdateSubmit(); 1.282 +} 1.283 + 1.284 +void IncludeURLClicked(GtkButton* sender, gpointer userData) 1.285 +{ 1.286 + UpdateURL(); 1.287 +} 1.288 + 1.289 +/* === Crashreporter UI Functions === */ 1.290 + 1.291 +bool UIInit() 1.292 +{ 1.293 + // breakpad probably left us with blocked signals, unblock them here 1.294 + sigset_t signals, old; 1.295 + sigfillset(&signals); 1.296 + sigprocmask(SIG_UNBLOCK, &signals, &old); 1.297 + 1.298 + // tell glib we're going to use threads 1.299 + g_thread_init(nullptr); 1.300 + 1.301 + if (gtk_init_check(&gArgc, &gArgv)) { 1.302 + gInitialized = true; 1.303 + 1.304 + if (gStrings.find("isRTL") != gStrings.end() && 1.305 + gStrings["isRTL"] == "yes") 1.306 + gtk_widget_set_default_direction(GTK_TEXT_DIR_RTL); 1.307 + 1.308 + return true; 1.309 + } 1.310 + 1.311 + return false; 1.312 +} 1.313 + 1.314 +void UIShowDefaultUI() 1.315 +{ 1.316 + GtkWidget* errorDialog = 1.317 + gtk_message_dialog_new(nullptr, GTK_DIALOG_MODAL, 1.318 + GTK_MESSAGE_ERROR, 1.319 + GTK_BUTTONS_CLOSE, 1.320 + "%s", gStrings[ST_CRASHREPORTERDEFAULT].c_str()); 1.321 + 1.322 + gtk_window_set_title(GTK_WINDOW(errorDialog), 1.323 + gStrings[ST_CRASHREPORTERTITLE].c_str()); 1.324 + gtk_dialog_run(GTK_DIALOG(errorDialog)); 1.325 +} 1.326 + 1.327 +void UIError_impl(const string& message) 1.328 +{ 1.329 + if (!gInitialized) { 1.330 + // Didn't initialize, this is the best we can do 1.331 + printf("Error: %s\n", message.c_str()); 1.332 + return; 1.333 + } 1.334 + 1.335 + GtkWidget* errorDialog = 1.336 + gtk_message_dialog_new(nullptr, GTK_DIALOG_MODAL, 1.337 + GTK_MESSAGE_ERROR, 1.338 + GTK_BUTTONS_CLOSE, 1.339 + "%s", message.c_str()); 1.340 + 1.341 + gtk_window_set_title(GTK_WINDOW(errorDialog), 1.342 + gStrings[ST_CRASHREPORTERTITLE].c_str()); 1.343 + gtk_dialog_run(GTK_DIALOG(errorDialog)); 1.344 +} 1.345 + 1.346 +bool UIGetIniPath(string& path) 1.347 +{ 1.348 + path = gArgv[0]; 1.349 + path.append(".ini"); 1.350 + 1.351 + return true; 1.352 +} 1.353 + 1.354 +/* 1.355 + * Settings are stored in ~/.vendor/product, or 1.356 + * ~/.product if vendor is empty. 1.357 + */ 1.358 +bool UIGetSettingsPath(const string& vendor, 1.359 + const string& product, 1.360 + string& settingsPath) 1.361 +{ 1.362 + char* home = getenv("HOME"); 1.363 + 1.364 + if (!home) 1.365 + return false; 1.366 + 1.367 + settingsPath = home; 1.368 + settingsPath += "/."; 1.369 + if (!vendor.empty()) { 1.370 + string lc_vendor; 1.371 + std::transform(vendor.begin(), vendor.end(), back_inserter(lc_vendor), 1.372 + (int(*)(int)) std::tolower); 1.373 + settingsPath += lc_vendor + "/"; 1.374 + } 1.375 + string lc_product; 1.376 + std::transform(product.begin(), product.end(), back_inserter(lc_product), 1.377 + (int(*)(int)) std::tolower); 1.378 + settingsPath += lc_product + "/Crash Reports"; 1.379 + return true; 1.380 +} 1.381 + 1.382 +bool UIEnsurePathExists(const string& path) 1.383 +{ 1.384 + int ret = mkdir(path.c_str(), S_IRWXU); 1.385 + int e = errno; 1.386 + if (ret == -1 && e != EEXIST) 1.387 + return false; 1.388 + 1.389 + return true; 1.390 +} 1.391 + 1.392 +bool UIFileExists(const string& path) 1.393 +{ 1.394 + struct stat sb; 1.395 + int ret = stat(path.c_str(), &sb); 1.396 + if (ret == -1 || !(sb.st_mode & S_IFREG)) 1.397 + return false; 1.398 + 1.399 + return true; 1.400 +} 1.401 + 1.402 +bool UIMoveFile(const string& file, const string& newfile) 1.403 +{ 1.404 + if (!rename(file.c_str(), newfile.c_str())) 1.405 + return true; 1.406 + if (errno != EXDEV) 1.407 + return false; 1.408 + 1.409 + // use system /bin/mv instead, time to fork 1.410 + pid_t pID = vfork(); 1.411 + if (pID < 0) { 1.412 + // Failed to fork 1.413 + return false; 1.414 + } 1.415 + if (pID == 0) { 1.416 + char* const args[4] = { 1.417 + "mv", 1.418 + strdup(file.c_str()), 1.419 + strdup(newfile.c_str()), 1.420 + 0 1.421 + }; 1.422 + if (args[1] && args[2]) 1.423 + execve("/bin/mv", args, 0); 1.424 + if (args[1]) 1.425 + free(args[1]); 1.426 + if (args[2]) 1.427 + free(args[2]); 1.428 + exit(-1); 1.429 + } 1.430 + int status; 1.431 + waitpid(pID, &status, 0); 1.432 + return UIFileExists(newfile); 1.433 +} 1.434 + 1.435 +bool UIDeleteFile(const string& file) 1.436 +{ 1.437 + return (unlink(file.c_str()) != -1); 1.438 +} 1.439 + 1.440 +std::ifstream* UIOpenRead(const string& filename) 1.441 +{ 1.442 + return new std::ifstream(filename.c_str(), std::ios::in); 1.443 +} 1.444 + 1.445 +std::ofstream* UIOpenWrite(const string& filename, bool append) // append=false 1.446 +{ 1.447 + return new std::ofstream(filename.c_str(), 1.448 + append ? std::ios::out | std::ios::app 1.449 + : std::ios::out); 1.450 +}