diff options
author | Matt A. Tobin <mattatobin@localhost.localdomain> | 2018-02-02 04:16:08 -0500 |
---|---|---|
committer | Matt A. Tobin <mattatobin@localhost.localdomain> | 2018-02-02 04:16:08 -0500 |
commit | 5f8de423f190bbb79a62f804151bc24824fa32d8 (patch) | |
tree | 10027f336435511475e392454359edea8e25895d /toolkit/system | |
parent | 49ee0794b5d912db1f95dce6eb52d781dc210db5 (diff) | |
download | UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.gz UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.lz UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.xz UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.zip |
Add m-esr52 at 52.6.0
Diffstat (limited to 'toolkit/system')
31 files changed, 4180 insertions, 0 deletions
diff --git a/toolkit/system/androidproxy/moz.build b/toolkit/system/androidproxy/moz.build new file mode 100644 index 000000000..5c6b0c105 --- /dev/null +++ b/toolkit/system/androidproxy/moz.build @@ -0,0 +1,11 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +SOURCES += [ + 'nsAndroidSystemProxySettings.cpp', +] + +FINAL_LIBRARY = 'xul' diff --git a/toolkit/system/androidproxy/nsAndroidSystemProxySettings.cpp b/toolkit/system/androidproxy/nsAndroidSystemProxySettings.cpp new file mode 100644 index 000000000..71e12bb77 --- /dev/null +++ b/toolkit/system/androidproxy/nsAndroidSystemProxySettings.cpp @@ -0,0 +1,89 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nsIURI.h" + +#include "nsISystemProxySettings.h" +#include "nsIServiceManager.h" +#include "mozilla/ModuleUtils.h" +#include "nsPrintfCString.h" +#include "nsNetCID.h" +#include "nsISupportsPrimitives.h" +#include "nsIURI.h" + +#include "AndroidBridge.h" + +class nsAndroidSystemProxySettings : public nsISystemProxySettings +{ +public: + NS_DECL_THREADSAFE_ISUPPORTS + NS_DECL_NSISYSTEMPROXYSETTINGS + + nsAndroidSystemProxySettings() {}; + nsresult Init(); + +private: + virtual ~nsAndroidSystemProxySettings() {} +}; + +NS_IMPL_ISUPPORTS(nsAndroidSystemProxySettings, nsISystemProxySettings) + +NS_IMETHODIMP +nsAndroidSystemProxySettings::GetMainThreadOnly(bool *aMainThreadOnly) +{ + *aMainThreadOnly = true; + return NS_OK; +} + + +nsresult +nsAndroidSystemProxySettings::Init() +{ + return NS_OK; +} + +nsresult +nsAndroidSystemProxySettings::GetPACURI(nsACString& aResult) +{ + return NS_OK; +} + +nsresult +nsAndroidSystemProxySettings::GetProxyForURI(const nsACString & aSpec, + const nsACString & aScheme, + const nsACString & aHost, + const int32_t aPort, + nsACString & aResult) +{ + return mozilla::AndroidBridge::Bridge()->GetProxyForURI(aSpec, aScheme, aHost, aPort, aResult); +} + +NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsAndroidSystemProxySettings, Init) + +#define NS_ANDROIDSYSTEMPROXYSERVICE_CID \ + {0xf01f0060, 0x3708, 0x478e, \ + {0xb9, 0x35, 0x3e, 0xce, 0x8b, 0xe2, 0x94, 0xb8}} + +NS_DEFINE_NAMED_CID(NS_ANDROIDSYSTEMPROXYSERVICE_CID); + +void test() {}; + +static const mozilla::Module::CIDEntry kSysProxyCIDs[] = { + { &kNS_ANDROIDSYSTEMPROXYSERVICE_CID, false, nullptr, nsAndroidSystemProxySettingsConstructor }, + { nullptr } +}; + +static const mozilla::Module::ContractIDEntry kSysProxyContracts[] = { + { NS_SYSTEMPROXYSETTINGS_CONTRACTID, &kNS_ANDROIDSYSTEMPROXYSERVICE_CID }, + { nullptr } +}; + +static const mozilla::Module kSysProxyModule = { + mozilla::Module::kVersion, + kSysProxyCIDs, + kSysProxyContracts +}; + +NSMODULE_DEFN(nsAndroidProxyModule) = &kSysProxyModule; diff --git a/toolkit/system/gnome/moz.build b/toolkit/system/gnome/moz.build new file mode 100644 index 000000000..14b472be3 --- /dev/null +++ b/toolkit/system/gnome/moz.build @@ -0,0 +1,37 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +SOURCES += [ + 'nsAlertsIconListener.cpp', + 'nsGnomeModule.cpp', + 'nsSystemAlertsService.cpp', +] + +if CONFIG['MOZ_ENABLE_GCONF']: + SOURCES += [ + 'nsGConfService.cpp', + ] + +if CONFIG['MOZ_ENABLE_GIO']: + SOURCES += [ + 'nsGIOService.cpp', + 'nsGSettingsService.cpp', + 'nsPackageKitService.cpp' + ] + +FINAL_LIBRARY = 'xul' + +LOCAL_INCLUDES += [ + '/toolkit/components/build/', +] + +CXXFLAGS += CONFIG['MOZ_GCONF_CFLAGS'] +CXXFLAGS += CONFIG['MOZ_GIO_CFLAGS'] +CXXFLAGS += CONFIG['GLIB_CFLAGS'] +CXXFLAGS += CONFIG['MOZ_DBUS_GLIB_CFLAGS'] + +if 'gtk' in CONFIG['MOZ_WIDGET_TOOLKIT']: + CXXFLAGS += CONFIG['TK_CFLAGS'] diff --git a/toolkit/system/gnome/nsAlertsIconListener.cpp b/toolkit/system/gnome/nsAlertsIconListener.cpp new file mode 100644 index 000000000..b11672d30 --- /dev/null +++ b/toolkit/system/gnome/nsAlertsIconListener.cpp @@ -0,0 +1,327 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode:nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nsAlertsIconListener.h" +#include "imgIContainer.h" +#include "imgIRequest.h" +#include "nsNetUtil.h" +#include "nsServiceManagerUtils.h" +#include "nsSystemAlertsService.h" +#include "nsIAlertsService.h" +#include "nsICancelable.h" +#include "nsIImageToPixbuf.h" +#include "nsIStringBundle.h" +#include "nsIObserverService.h" +#include "nsIURI.h" +#include "nsCRT.h" + +#include <dlfcn.h> +#include <gdk/gdk.h> + +static bool gHasActions = false; +static bool gHasCaps = false; + +void* nsAlertsIconListener::libNotifyHandle = nullptr; +bool nsAlertsIconListener::libNotifyNotAvail = false; +nsAlertsIconListener::notify_is_initted_t nsAlertsIconListener::notify_is_initted = nullptr; +nsAlertsIconListener::notify_init_t nsAlertsIconListener::notify_init = nullptr; +nsAlertsIconListener::notify_get_server_caps_t nsAlertsIconListener::notify_get_server_caps = nullptr; +nsAlertsIconListener::notify_notification_new_t nsAlertsIconListener::notify_notification_new = nullptr; +nsAlertsIconListener::notify_notification_show_t nsAlertsIconListener::notify_notification_show = nullptr; +nsAlertsIconListener::notify_notification_set_icon_from_pixbuf_t nsAlertsIconListener::notify_notification_set_icon_from_pixbuf = nullptr; +nsAlertsIconListener::notify_notification_add_action_t nsAlertsIconListener::notify_notification_add_action = nullptr; +nsAlertsIconListener::notify_notification_close_t nsAlertsIconListener::notify_notification_close = nullptr; + +static void notify_action_cb(NotifyNotification *notification, + gchar *action, gpointer user_data) +{ + nsAlertsIconListener* alert = static_cast<nsAlertsIconListener*> (user_data); + alert->SendCallback(); +} + +static void notify_closed_marshal(GClosure* closure, + GValue* return_value, + guint n_param_values, + const GValue* param_values, + gpointer invocation_hint, + gpointer marshal_data) +{ + MOZ_ASSERT(n_param_values >= 1, "No object in params"); + + nsAlertsIconListener* alert = + static_cast<nsAlertsIconListener*>(closure->data); + alert->SendClosed(); + NS_RELEASE(alert); +} + +static GdkPixbuf* +GetPixbufFromImgRequest(imgIRequest* aRequest) +{ + nsCOMPtr<imgIContainer> image; + nsresult rv = aRequest->GetImage(getter_AddRefs(image)); + if (NS_FAILED(rv)) { + return nullptr; + } + + nsCOMPtr<nsIImageToPixbuf> imgToPixbuf = + do_GetService("@mozilla.org/widget/image-to-gdk-pixbuf;1"); + + return imgToPixbuf->ConvertImageToPixbuf(image); +} + +NS_IMPL_ISUPPORTS(nsAlertsIconListener, nsIAlertNotificationImageListener, + nsIObserver, nsISupportsWeakReference) + +nsAlertsIconListener::nsAlertsIconListener(nsSystemAlertsService* aBackend, + const nsAString& aAlertName) +: mAlertName(aAlertName), + mBackend(aBackend), + mNotification(nullptr) +{ + if (!libNotifyHandle && !libNotifyNotAvail) { + libNotifyHandle = dlopen("libnotify.so.4", RTLD_LAZY); + if (!libNotifyHandle) { + libNotifyHandle = dlopen("libnotify.so.1", RTLD_LAZY); + if (!libNotifyHandle) { + libNotifyNotAvail = true; + return; + } + } + + notify_is_initted = (notify_is_initted_t)dlsym(libNotifyHandle, "notify_is_initted"); + notify_init = (notify_init_t)dlsym(libNotifyHandle, "notify_init"); + notify_get_server_caps = (notify_get_server_caps_t)dlsym(libNotifyHandle, "notify_get_server_caps"); + notify_notification_new = (notify_notification_new_t)dlsym(libNotifyHandle, "notify_notification_new"); + notify_notification_show = (notify_notification_show_t)dlsym(libNotifyHandle, "notify_notification_show"); + notify_notification_set_icon_from_pixbuf = (notify_notification_set_icon_from_pixbuf_t)dlsym(libNotifyHandle, "notify_notification_set_icon_from_pixbuf"); + notify_notification_add_action = (notify_notification_add_action_t)dlsym(libNotifyHandle, "notify_notification_add_action"); + notify_notification_close = (notify_notification_close_t)dlsym(libNotifyHandle, "notify_notification_close"); + if (!notify_is_initted || !notify_init || !notify_get_server_caps || !notify_notification_new || !notify_notification_show || !notify_notification_set_icon_from_pixbuf || !notify_notification_add_action || !notify_notification_close) { + dlclose(libNotifyHandle); + libNotifyHandle = nullptr; + } + } +} + +nsAlertsIconListener::~nsAlertsIconListener() +{ + mBackend->RemoveListener(mAlertName, this); + // Don't dlclose libnotify as it uses atexit(). +} + +NS_IMETHODIMP +nsAlertsIconListener::OnImageMissing(nsISupports*) +{ + // This notification doesn't have an image, or there was an error getting + // the image. Show the notification without an icon. + return ShowAlert(nullptr); +} + +NS_IMETHODIMP +nsAlertsIconListener::OnImageReady(nsISupports*, imgIRequest* aRequest) +{ + GdkPixbuf* imagePixbuf = GetPixbufFromImgRequest(aRequest); + if (!imagePixbuf) { + ShowAlert(nullptr); + } else { + ShowAlert(imagePixbuf); + g_object_unref(imagePixbuf); + } + + return NS_OK; +} + +nsresult +nsAlertsIconListener::ShowAlert(GdkPixbuf* aPixbuf) +{ + if (!mBackend->IsActiveListener(mAlertName, this)) + return NS_OK; + + mNotification = notify_notification_new(mAlertTitle.get(), mAlertText.get(), + nullptr, nullptr); + + if (!mNotification) + return NS_ERROR_OUT_OF_MEMORY; + + nsCOMPtr<nsIObserverService> obsServ = + do_GetService("@mozilla.org/observer-service;1"); + if (obsServ) + obsServ->AddObserver(this, "quit-application", true); + + if (aPixbuf) + notify_notification_set_icon_from_pixbuf(mNotification, aPixbuf); + + NS_ADDREF(this); + if (mAlertHasAction) { + // What we put as the label doesn't matter here, if the action + // string is "default" then that makes the entire bubble clickable + // rather than creating a button. + notify_notification_add_action(mNotification, "default", "Activate", + notify_action_cb, this, nullptr); + } + + // Fedora 10 calls NotifyNotification "closed" signal handlers with a + // different signature, so a marshaller is used instead of a C callback to + // get the user_data (this) in a parseable format. |closure| is created + // with a floating reference, which gets sunk by g_signal_connect_closure(). + GClosure* closure = g_closure_new_simple(sizeof(GClosure), this); + g_closure_set_marshal(closure, notify_closed_marshal); + mClosureHandler = g_signal_connect_closure(mNotification, "closed", closure, FALSE); + GError* error = nullptr; + if (!notify_notification_show(mNotification, &error)) { + NS_WARNING(error->message); + g_error_free(error); + return NS_ERROR_FAILURE; + } + + if (mAlertListener) + mAlertListener->Observe(nullptr, "alertshow", mAlertCookie.get()); + + return NS_OK; +} + +void +nsAlertsIconListener::SendCallback() +{ + if (mAlertListener) + mAlertListener->Observe(nullptr, "alertclickcallback", mAlertCookie.get()); +} + +void +nsAlertsIconListener::SendClosed() +{ + if (mNotification) { + g_object_unref(mNotification); + mNotification = nullptr; + } + NotifyFinished(); +} + +NS_IMETHODIMP +nsAlertsIconListener::Observe(nsISupports *aSubject, const char *aTopic, + const char16_t *aData) { + // We need to close any open notifications upon application exit, otherwise + // we will leak since libnotify holds a ref for us. + if (!nsCRT::strcmp(aTopic, "quit-application") && mNotification) { + g_signal_handler_disconnect(mNotification, mClosureHandler); + g_object_unref(mNotification); + mNotification = nullptr; + Release(); // equivalent to NS_RELEASE(this) + } + return NS_OK; +} + +nsresult +nsAlertsIconListener::Close() +{ + if (mIconRequest) { + mIconRequest->Cancel(NS_BINDING_ABORTED); + mIconRequest = nullptr; + } + + if (!mNotification) { + NotifyFinished(); + return NS_OK; + } + + GError* error = nullptr; + if (!notify_notification_close(mNotification, &error)) { + NS_WARNING(error->message); + g_error_free(error); + return NS_ERROR_FAILURE; + } + + return NS_OK; +} + +nsresult +nsAlertsIconListener::InitAlertAsync(nsIAlertNotification* aAlert, + nsIObserver* aAlertListener) +{ + if (!libNotifyHandle) + return NS_ERROR_FAILURE; + + if (!notify_is_initted()) { + // Give the name of this application to libnotify + nsCOMPtr<nsIStringBundleService> bundleService = + do_GetService(NS_STRINGBUNDLE_CONTRACTID); + + nsAutoCString appShortName; + if (bundleService) { + nsCOMPtr<nsIStringBundle> bundle; + bundleService->CreateBundle("chrome://branding/locale/brand.properties", + getter_AddRefs(bundle)); + nsAutoString appName; + + if (bundle) { + bundle->GetStringFromName(u"brandShortName", + getter_Copies(appName)); + appShortName = NS_ConvertUTF16toUTF8(appName); + } else { + NS_WARNING("brand.properties not present, using default application name"); + appShortName.AssignLiteral("Mozilla"); + } + } else { + appShortName.AssignLiteral("Mozilla"); + } + + if (!notify_init(appShortName.get())) + return NS_ERROR_FAILURE; + + GList *server_caps = notify_get_server_caps(); + if (server_caps) { + gHasCaps = true; + for (GList* cap = server_caps; cap != nullptr; cap = cap->next) { + if (!strcmp((char*) cap->data, "actions")) { + gHasActions = true; + break; + } + } + g_list_foreach(server_caps, (GFunc)g_free, nullptr); + g_list_free(server_caps); + } + } + + if (!gHasCaps) { + // if notify_get_server_caps() failed above we need to assume + // there is no notification-server to display anything + return NS_ERROR_FAILURE; + } + + nsresult rv = aAlert->GetTextClickable(&mAlertHasAction); + NS_ENSURE_SUCCESS(rv, rv); + if (!gHasActions && mAlertHasAction) + return NS_ERROR_FAILURE; // No good, fallback to XUL + + nsAutoString title; + rv = aAlert->GetTitle(title); + NS_ENSURE_SUCCESS(rv, rv); + // Workaround for a libnotify bug - blank titles aren't dealt with + // properly so we use a space + if (title.IsEmpty()) { + mAlertTitle = NS_LITERAL_CSTRING(" "); + } else { + mAlertTitle = NS_ConvertUTF16toUTF8(title); + } + + nsAutoString text; + rv = aAlert->GetText(text); + NS_ENSURE_SUCCESS(rv, rv); + mAlertText = NS_ConvertUTF16toUTF8(text); + + mAlertListener = aAlertListener; + + rv = aAlert->GetCookie(mAlertCookie); + NS_ENSURE_SUCCESS(rv, rv); + + return aAlert->LoadImage(/* aTimeout = */ 0, this, /* aUserData = */ nullptr, + getter_AddRefs(mIconRequest)); +} + +void nsAlertsIconListener::NotifyFinished() +{ + if (mAlertListener) + mAlertListener->Observe(nullptr, "alertfinished", mAlertCookie.get()); +} diff --git a/toolkit/system/gnome/nsAlertsIconListener.h b/toolkit/system/gnome/nsAlertsIconListener.h new file mode 100644 index 000000000..f3a745d33 --- /dev/null +++ b/toolkit/system/gnome/nsAlertsIconListener.h @@ -0,0 +1,91 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef nsAlertsIconListener_h__ +#define nsAlertsIconListener_h__ + +#include "nsCOMPtr.h" +#include "nsIAlertsService.h" +#include "nsString.h" +#include "nsIObserver.h" +#include "nsWeakReference.h" + +#include <gdk-pixbuf/gdk-pixbuf.h> + +class nsIAlertNotification; +class nsICancelable; +class nsSystemAlertsService; + +struct NotifyNotification; + +class nsAlertsIconListener : public nsIAlertNotificationImageListener, + public nsIObserver, + public nsSupportsWeakReference +{ +public: + NS_DECL_ISUPPORTS + NS_DECL_NSIALERTNOTIFICATIONIMAGELISTENER + NS_DECL_NSIOBSERVER + + nsAlertsIconListener(nsSystemAlertsService* aBackend, + const nsAString& aAlertName); + + nsresult InitAlertAsync(nsIAlertNotification* aAlert, + nsIObserver* aAlertListener); + nsresult Close(); + + void SendCallback(); + void SendClosed(); + +protected: + virtual ~nsAlertsIconListener(); + + /** + * The only difference between libnotify.so.4 and libnotify.so.1 for these symbols + * is that notify_notification_new takes three arguments in libnotify.so.4 and + * four in libnotify.so.1. + * Passing the fourth argument as NULL is binary compatible. + */ + typedef void (*NotifyActionCallback)(NotifyNotification*, char*, gpointer); + typedef bool (*notify_is_initted_t)(void); + typedef bool (*notify_init_t)(const char*); + typedef GList* (*notify_get_server_caps_t)(void); + typedef NotifyNotification* (*notify_notification_new_t)(const char*, const char*, const char*, const char*); + typedef bool (*notify_notification_show_t)(void*, GError**); + typedef void (*notify_notification_set_icon_from_pixbuf_t)(void*, GdkPixbuf*); + typedef void (*notify_notification_add_action_t)(void*, const char*, const char*, NotifyActionCallback, gpointer, GFreeFunc); + typedef bool (*notify_notification_close_t)(void*, GError**); + + nsCOMPtr<nsICancelable> mIconRequest; + nsCString mAlertTitle; + nsCString mAlertText; + + nsCOMPtr<nsIObserver> mAlertListener; + nsString mAlertCookie; + nsString mAlertName; + + RefPtr<nsSystemAlertsService> mBackend; + + bool mAlertHasAction; + + static void* libNotifyHandle; + static bool libNotifyNotAvail; + static notify_is_initted_t notify_is_initted; + static notify_init_t notify_init; + static notify_get_server_caps_t notify_get_server_caps; + static notify_notification_new_t notify_notification_new; + static notify_notification_show_t notify_notification_show; + static notify_notification_set_icon_from_pixbuf_t notify_notification_set_icon_from_pixbuf; + static notify_notification_add_action_t notify_notification_add_action; + static notify_notification_close_t notify_notification_close; + NotifyNotification* mNotification; + gulong mClosureHandler; + + nsresult ShowAlert(GdkPixbuf* aPixbuf); + + void NotifyFinished(); +}; + +#endif diff --git a/toolkit/system/gnome/nsGConfService.cpp b/toolkit/system/gnome/nsGConfService.cpp new file mode 100644 index 000000000..ede71e588 --- /dev/null +++ b/toolkit/system/gnome/nsGConfService.cpp @@ -0,0 +1,305 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/ArrayUtils.h" +#include "nsGConfService.h" +#include "nsString.h" +#include "nsCOMPtr.h" +#include "nsComponentManagerUtils.h" +#include "nsISupportsPrimitives.h" +#include "nsIMutableArray.h" +#include "prlink.h" + +#include <gconf/gconf-client.h> + +using namespace mozilla; + +#define GCONF_FUNCTIONS \ + FUNC(gconf_client_get_default, GConfClient*, (void)) \ + FUNC(gconf_client_get_bool, gboolean, (GConfClient*, const gchar*, GError**)) \ + FUNC(gconf_client_get_string, gchar*, (GConfClient*, const gchar*, GError**)) \ + FUNC(gconf_client_get_int, gint, (GConfClient*, const gchar*, GError**)) \ + FUNC(gconf_client_get_float, gdouble, (GConfClient*, const gchar*, GError**)) \ + FUNC(gconf_client_get_list, GSList*, (GConfClient*, const gchar*, GConfValueType, GError**)) \ + FUNC(gconf_client_set_bool, gboolean, (GConfClient*, const gchar*, gboolean, GError**)) \ + FUNC(gconf_client_set_string, gboolean, (GConfClient*, const gchar*, const gchar*, GError**)) \ + FUNC(gconf_client_set_int, gboolean, (GConfClient*, const gchar*, gint, GError**)) \ + FUNC(gconf_client_set_float, gboolean, (GConfClient*, const gchar*, gdouble, GError**)) \ + FUNC(gconf_client_unset, gboolean, (GConfClient*, const gchar*, GError**)) + +#define FUNC(name, type, params) \ + typedef type (*_##name##_fn) params; \ + static _##name##_fn _##name; + +GCONF_FUNCTIONS + +#undef FUNC + +#define gconf_client_get_default _gconf_client_get_default +#define gconf_client_get_bool _gconf_client_get_bool +#define gconf_client_get_string _gconf_client_get_string +#define gconf_client_get_int _gconf_client_get_int +#define gconf_client_get_float _gconf_client_get_float +#define gconf_client_get_list _gconf_client_get_list +#define gconf_client_set_bool _gconf_client_set_bool +#define gconf_client_set_string _gconf_client_set_string +#define gconf_client_set_int _gconf_client_set_int +#define gconf_client_set_float _gconf_client_set_float +#define gconf_client_unset _gconf_client_unset + +static PRLibrary *gconfLib = nullptr; + +typedef void (*nsGConfFunc)(); +struct nsGConfDynamicFunction { + const char *functionName; + nsGConfFunc *function; +}; + +nsGConfService::~nsGConfService() +{ + if (mClient) + g_object_unref(mClient); + + // We don't unload gconf here because liborbit uses atexit(). In addition to + // this, it's not a good idea to unload any gobject based library, as it + // leaves types registered in glib's type system +} + +nsresult +nsGConfService::Init() +{ +#define FUNC(name, type, params) { #name, (nsGConfFunc *)&_##name }, + static const nsGConfDynamicFunction kGConfSymbols[] = { + GCONF_FUNCTIONS + }; +#undef FUNC + + if (!gconfLib) { + gconfLib = PR_LoadLibrary("libgconf-2.so.4"); + if (!gconfLib) + return NS_ERROR_FAILURE; + } + + for (uint32_t i = 0; i < ArrayLength(kGConfSymbols); i++) { + *kGConfSymbols[i].function = + PR_FindFunctionSymbol(gconfLib, kGConfSymbols[i].functionName); + if (!*kGConfSymbols[i].function) { + return NS_ERROR_FAILURE; + } + } + + mClient = gconf_client_get_default(); + return mClient ? NS_OK : NS_ERROR_FAILURE; +} + +NS_IMPL_ISUPPORTS(nsGConfService, nsIGConfService) + +NS_IMETHODIMP +nsGConfService::GetBool(const nsACString &aKey, bool *aResult) +{ + GError* error = nullptr; + *aResult = gconf_client_get_bool(mClient, PromiseFlatCString(aKey).get(), + &error); + + if (error) { + g_error_free(error); + return NS_ERROR_FAILURE; + } + + return NS_OK; +} + +NS_IMETHODIMP +nsGConfService::GetString(const nsACString &aKey, nsACString &aResult) +{ + GError* error = nullptr; + gchar *result = gconf_client_get_string(mClient, + PromiseFlatCString(aKey).get(), + &error); + + if (error) { + g_error_free(error); + return NS_ERROR_FAILURE; + } + + // We do a string copy here so that the caller doesn't need to worry about + // freeing the string with g_free(). + + aResult.Assign(result); + g_free(result); + + return NS_OK; +} + +NS_IMETHODIMP +nsGConfService::GetInt(const nsACString &aKey, int32_t* aResult) +{ + GError* error = nullptr; + *aResult = gconf_client_get_int(mClient, PromiseFlatCString(aKey).get(), + &error); + + if (error) { + g_error_free(error); + return NS_ERROR_FAILURE; + } + + return NS_OK; +} + +NS_IMETHODIMP +nsGConfService::GetFloat(const nsACString &aKey, float* aResult) +{ + GError* error = nullptr; + *aResult = gconf_client_get_float(mClient, PromiseFlatCString(aKey).get(), + &error); + + if (error) { + g_error_free(error); + return NS_ERROR_FAILURE; + } + + return NS_OK; +} + +NS_IMETHODIMP +nsGConfService::GetStringList(const nsACString &aKey, nsIArray** aResult) +{ + nsCOMPtr<nsIMutableArray> items(do_CreateInstance(NS_ARRAY_CONTRACTID)); + if (!items) + return NS_ERROR_OUT_OF_MEMORY; + + GError* error = nullptr; + GSList* list = gconf_client_get_list(mClient, PromiseFlatCString(aKey).get(), + GCONF_VALUE_STRING, &error); + if (error) { + g_error_free(error); + return NS_ERROR_FAILURE; + } + + for (GSList* l = list; l; l = l->next) { + nsCOMPtr<nsISupportsString> obj(do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID)); + if (!obj) { + g_slist_free(list); + return NS_ERROR_OUT_OF_MEMORY; + } + obj->SetData(NS_ConvertUTF8toUTF16((const char*)l->data)); + items->AppendElement(obj, false); + g_free(l->data); + } + + g_slist_free(list); + items.forget(aResult); + return NS_OK; +} + +NS_IMETHODIMP +nsGConfService::SetBool(const nsACString &aKey, bool aValue) +{ + bool res = gconf_client_set_bool(mClient, PromiseFlatCString(aKey).get(), + aValue, nullptr); + + return res ? NS_OK : NS_ERROR_FAILURE; +} + +NS_IMETHODIMP +nsGConfService::SetString(const nsACString &aKey, const nsACString &aValue) +{ + bool res = gconf_client_set_string(mClient, PromiseFlatCString(aKey).get(), + PromiseFlatCString(aValue).get(), + nullptr); + + return res ? NS_OK : NS_ERROR_FAILURE; +} + +NS_IMETHODIMP +nsGConfService::SetInt(const nsACString &aKey, int32_t aValue) +{ + bool res = gconf_client_set_int(mClient, PromiseFlatCString(aKey).get(), + aValue, nullptr); + + return res ? NS_OK : NS_ERROR_FAILURE; +} + +NS_IMETHODIMP +nsGConfService::SetFloat(const nsACString &aKey, float aValue) +{ + bool res = gconf_client_set_float(mClient, PromiseFlatCString(aKey).get(), + aValue, nullptr); + + return res ? NS_OK : NS_ERROR_FAILURE; +} + +NS_IMETHODIMP +nsGConfService::GetAppForProtocol(const nsACString &aScheme, bool *aEnabled, + nsACString &aHandler) +{ + nsAutoCString key("/desktop/gnome/url-handlers/"); + key.Append(aScheme); + key.AppendLiteral("/command"); + + GError *err = nullptr; + gchar *command = gconf_client_get_string(mClient, key.get(), &err); + if (!err && command) { + key.Replace(key.Length() - 7, 7, NS_LITERAL_CSTRING("enabled")); + *aEnabled = gconf_client_get_bool(mClient, key.get(), &err); + } else { + *aEnabled = false; + } + + aHandler.Assign(command); + g_free(command); + + if (err) { + g_error_free(err); + return NS_ERROR_FAILURE; + } + + return NS_OK; +} + +NS_IMETHODIMP +nsGConfService::HandlerRequiresTerminal(const nsACString &aScheme, + bool *aResult) +{ + nsAutoCString key("/desktop/gnome/url-handlers/"); + key.Append(aScheme); + key.AppendLiteral("/requires_terminal"); + + GError *err = nullptr; + *aResult = gconf_client_get_bool(mClient, key.get(), &err); + if (err) { + g_error_free(err); + return NS_ERROR_FAILURE; + } + + return NS_OK; +} + +NS_IMETHODIMP +nsGConfService::SetAppForProtocol(const nsACString &aScheme, + const nsACString &aCommand) +{ + nsAutoCString key("/desktop/gnome/url-handlers/"); + key.Append(aScheme); + key.AppendLiteral("/command"); + + bool res = gconf_client_set_string(mClient, key.get(), + PromiseFlatCString(aCommand).get(), + nullptr); + if (res) { + key.Replace(key.Length() - 7, 7, NS_LITERAL_CSTRING("enabled")); + res = gconf_client_set_bool(mClient, key.get(), true, nullptr); + if (res) { + key.Replace(key.Length() - 7, 7, NS_LITERAL_CSTRING("needs_terminal")); + res = gconf_client_set_bool(mClient, key.get(), false, nullptr); + if (res) { + key.Replace(key.Length() - 14, 14, NS_LITERAL_CSTRING("command-id")); + res = gconf_client_unset(mClient, key.get(), nullptr); + } + } + } + + return res ? NS_OK : NS_ERROR_FAILURE; +} diff --git a/toolkit/system/gnome/nsGConfService.h b/toolkit/system/gnome/nsGConfService.h new file mode 100644 index 000000000..4e500a40d --- /dev/null +++ b/toolkit/system/gnome/nsGConfService.h @@ -0,0 +1,31 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef nsGConfService_h_ +#define nsGConfService_h_ + +#include "nsIGConfService.h" +#include "gconf/gconf-client.h" +#include "mozilla/Attributes.h" + +#define NS_GCONFSERVICE_CID \ +{0xd96d5985, 0xa13a, 0x4bdc, {0x93, 0x86, 0xef, 0x34, 0x8d, 0x7a, 0x97, 0xa1}} + +class nsGConfService final : public nsIGConfService +{ +public: + NS_DECL_ISUPPORTS + NS_DECL_NSIGCONFSERVICE + + nsGConfService() : mClient(nullptr) {} + nsresult Init(); + +private: + ~nsGConfService(); + + GConfClient *mClient; +}; + +#endif diff --git a/toolkit/system/gnome/nsGIOService.cpp b/toolkit/system/gnome/nsGIOService.cpp new file mode 100644 index 000000000..9196c7d76 --- /dev/null +++ b/toolkit/system/gnome/nsGIOService.cpp @@ -0,0 +1,475 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nsGIOService.h" +#include "nsString.h" +#include "nsIURI.h" +#include "nsTArray.h" +#include "nsIStringEnumerator.h" +#include "nsAutoPtr.h" + +#include <gio/gio.h> +#include <gtk/gtk.h> +#ifdef MOZ_ENABLE_DBUS +#include <dbus/dbus-glib.h> +#include <dbus/dbus-glib-lowlevel.h> +#endif + + +class nsGIOMimeApp final : public nsIGIOMimeApp +{ +public: + NS_DECL_ISUPPORTS + NS_DECL_NSIGIOMIMEAPP + + explicit nsGIOMimeApp(GAppInfo* aApp) : mApp(aApp) {} + +private: + ~nsGIOMimeApp() { g_object_unref(mApp); } + + GAppInfo *mApp; +}; + +NS_IMPL_ISUPPORTS(nsGIOMimeApp, nsIGIOMimeApp) + +NS_IMETHODIMP +nsGIOMimeApp::GetId(nsACString& aId) +{ + aId.Assign(g_app_info_get_id(mApp)); + return NS_OK; +} + +NS_IMETHODIMP +nsGIOMimeApp::GetName(nsACString& aName) +{ + aName.Assign(g_app_info_get_name(mApp)); + return NS_OK; +} + +NS_IMETHODIMP +nsGIOMimeApp::GetCommand(nsACString& aCommand) +{ + const char *cmd = g_app_info_get_commandline(mApp); + if (!cmd) + return NS_ERROR_FAILURE; + aCommand.Assign(cmd); + return NS_OK; +} + +NS_IMETHODIMP +nsGIOMimeApp::GetExpectsURIs(int32_t* aExpects) +{ + *aExpects = g_app_info_supports_uris(mApp); + return NS_OK; +} + +NS_IMETHODIMP +nsGIOMimeApp::Launch(const nsACString& aUri) +{ + GList uris = { 0 }; + nsPromiseFlatCString flatUri(aUri); + uris.data = const_cast<char*>(flatUri.get()); + + GError *error = nullptr; + gboolean result = g_app_info_launch_uris(mApp, &uris, nullptr, &error); + + if (!result) { + g_warning("Cannot launch application: %s", error->message); + g_error_free(error); + return NS_ERROR_FAILURE; + } + + return NS_OK; +} + +class GIOUTF8StringEnumerator final : public nsIUTF8StringEnumerator +{ + ~GIOUTF8StringEnumerator() { } + +public: + GIOUTF8StringEnumerator() : mIndex(0) { } + + NS_DECL_ISUPPORTS + NS_DECL_NSIUTF8STRINGENUMERATOR + + nsTArray<nsCString> mStrings; + uint32_t mIndex; +}; + +NS_IMPL_ISUPPORTS(GIOUTF8StringEnumerator, nsIUTF8StringEnumerator) + +NS_IMETHODIMP +GIOUTF8StringEnumerator::HasMore(bool* aResult) +{ + *aResult = mIndex < mStrings.Length(); + return NS_OK; +} + +NS_IMETHODIMP +GIOUTF8StringEnumerator::GetNext(nsACString& aResult) +{ + if (mIndex >= mStrings.Length()) + return NS_ERROR_UNEXPECTED; + + aResult.Assign(mStrings[mIndex]); + ++mIndex; + return NS_OK; +} + +NS_IMETHODIMP +nsGIOMimeApp::GetSupportedURISchemes(nsIUTF8StringEnumerator** aSchemes) +{ + *aSchemes = nullptr; + + RefPtr<GIOUTF8StringEnumerator> array = new GIOUTF8StringEnumerator(); + NS_ENSURE_TRUE(array, NS_ERROR_OUT_OF_MEMORY); + + GVfs *gvfs = g_vfs_get_default(); + + if (!gvfs) { + g_warning("Cannot get GVfs object."); + return NS_ERROR_OUT_OF_MEMORY; + } + + const gchar* const * uri_schemes = g_vfs_get_supported_uri_schemes(gvfs); + + while (*uri_schemes != nullptr) { + if (!array->mStrings.AppendElement(*uri_schemes)) { + return NS_ERROR_OUT_OF_MEMORY; + } + uri_schemes++; + } + + array.forget(aSchemes); + return NS_OK; +} + +NS_IMETHODIMP +nsGIOMimeApp::SetAsDefaultForMimeType(nsACString const& aMimeType) +{ + char *content_type = + g_content_type_from_mime_type(PromiseFlatCString(aMimeType).get()); + if (!content_type) + return NS_ERROR_FAILURE; + GError *error = nullptr; + g_app_info_set_as_default_for_type(mApp, + content_type, + &error); + if (error) { + g_warning("Cannot set application as default for MIME type (%s): %s", + PromiseFlatCString(aMimeType).get(), + error->message); + g_error_free(error); + g_free(content_type); + return NS_ERROR_FAILURE; + } + + g_free(content_type); + return NS_OK; +} +/** + * Set default application for files with given extensions + * @param fileExts string of space separated extensions + * @return NS_OK when application was set as default for given extensions, + * NS_ERROR_FAILURE otherwise + */ +NS_IMETHODIMP +nsGIOMimeApp::SetAsDefaultForFileExtensions(nsACString const& fileExts) +{ + GError *error = nullptr; + char *extensions = g_strdup(PromiseFlatCString(fileExts).get()); + char *ext_pos = extensions; + char *space_pos; + + while ( (space_pos = strchr(ext_pos, ' ')) || (*ext_pos != '\0') ) { + if (space_pos) { + *space_pos = '\0'; + } + g_app_info_set_as_default_for_extension(mApp, ext_pos, &error); + if (error) { + g_warning("Cannot set application as default for extension (%s): %s", + ext_pos, + error->message); + g_error_free(error); + g_free(extensions); + return NS_ERROR_FAILURE; + } + if (space_pos) { + ext_pos = space_pos + 1; + } else { + *ext_pos = '\0'; + } + } + g_free(extensions); + return NS_OK; +} + +/** + * Set default application for URI's of a particular scheme + * @param aURIScheme string containing the URI scheme + * @return NS_OK when application was set as default for URI scheme, + * NS_ERROR_FAILURE otherwise + */ +NS_IMETHODIMP +nsGIOMimeApp::SetAsDefaultForURIScheme(nsACString const& aURIScheme) +{ + GError *error = nullptr; + nsAutoCString contentType("x-scheme-handler/"); + contentType.Append(aURIScheme); + + g_app_info_set_as_default_for_type(mApp, + contentType.get(), + &error); + if (error) { + g_warning("Cannot set application as default for URI scheme (%s): %s", + PromiseFlatCString(aURIScheme).get(), + error->message); + g_error_free(error); + return NS_ERROR_FAILURE; + } + + return NS_OK; +} + +NS_IMPL_ISUPPORTS(nsGIOService, nsIGIOService) + +NS_IMETHODIMP +nsGIOService::GetMimeTypeFromExtension(const nsACString& aExtension, + nsACString& aMimeType) +{ + nsAutoCString fileExtToUse("file."); + fileExtToUse.Append(aExtension); + + gboolean result_uncertain; + char *content_type = g_content_type_guess(fileExtToUse.get(), + nullptr, + 0, + &result_uncertain); + if (!content_type) + return NS_ERROR_FAILURE; + + char *mime_type = g_content_type_get_mime_type(content_type); + if (!mime_type) { + g_free(content_type); + return NS_ERROR_FAILURE; + } + + aMimeType.Assign(mime_type); + + g_free(mime_type); + g_free(content_type); + + return NS_OK; +} +// used in nsGNOMERegistry +// ----------------------------------------------------------------------------- +NS_IMETHODIMP +nsGIOService::GetAppForURIScheme(const nsACString& aURIScheme, + nsIGIOMimeApp** aApp) +{ + *aApp = nullptr; + + GAppInfo *app_info = g_app_info_get_default_for_uri_scheme( + PromiseFlatCString(aURIScheme).get()); + if (app_info) { + nsGIOMimeApp *mozApp = new nsGIOMimeApp(app_info); + NS_ADDREF(*aApp = mozApp); + } else { + return NS_ERROR_FAILURE; + } + return NS_OK; +} + +NS_IMETHODIMP +nsGIOService::GetAppForMimeType(const nsACString& aMimeType, + nsIGIOMimeApp** aApp) +{ + *aApp = nullptr; + char *content_type = + g_content_type_from_mime_type(PromiseFlatCString(aMimeType).get()); + if (!content_type) + return NS_ERROR_FAILURE; + + GAppInfo *app_info = g_app_info_get_default_for_type(content_type, false); + if (app_info) { + nsGIOMimeApp *mozApp = new nsGIOMimeApp(app_info); + NS_ENSURE_TRUE(mozApp, NS_ERROR_OUT_OF_MEMORY); + NS_ADDREF(*aApp = mozApp); + } else { + g_free(content_type); + return NS_ERROR_FAILURE; + } + g_free(content_type); + return NS_OK; +} + +NS_IMETHODIMP +nsGIOService::GetDescriptionForMimeType(const nsACString& aMimeType, + nsACString& aDescription) +{ + char *content_type = + g_content_type_from_mime_type(PromiseFlatCString(aMimeType).get()); + if (!content_type) + return NS_ERROR_FAILURE; + + char *desc = g_content_type_get_description(content_type); + if (!desc) { + g_free(content_type); + return NS_ERROR_FAILURE; + } + + aDescription.Assign(desc); + g_free(content_type); + g_free(desc); + return NS_OK; +} + +NS_IMETHODIMP +nsGIOService::ShowURI(nsIURI* aURI) +{ + nsAutoCString spec; + nsresult rv = aURI->GetSpec(spec); + NS_ENSURE_SUCCESS(rv, rv); + GError *error = nullptr; + if (!g_app_info_launch_default_for_uri(spec.get(), nullptr, &error)) { + g_warning("Could not launch default application for URI: %s", error->message); + g_error_free(error); + return NS_ERROR_FAILURE; + } + return NS_OK; +} + +NS_IMETHODIMP +nsGIOService::ShowURIForInput(const nsACString& aUri) +{ + GFile *file = g_file_new_for_commandline_arg(PromiseFlatCString(aUri).get()); + char* spec = g_file_get_uri(file); + nsresult rv = NS_ERROR_FAILURE; + GError *error = nullptr; + + g_app_info_launch_default_for_uri(spec, nullptr, &error); + if (error) { + g_warning("Cannot launch default application: %s", error->message); + g_error_free(error); + } else { + rv = NS_OK; + } + g_object_unref(file); + g_free(spec); + + return rv; +} + +NS_IMETHODIMP +nsGIOService::OrgFreedesktopFileManager1ShowItems(const nsACString& aPath) +{ +#ifndef MOZ_ENABLE_DBUS + return NS_ERROR_FAILURE; +#else + GError* error = nullptr; + static bool org_freedesktop_FileManager1_exists = true; + + if (!org_freedesktop_FileManager1_exists) { + return NS_ERROR_NOT_AVAILABLE; + } + + DBusGConnection* dbusGConnection = dbus_g_bus_get(DBUS_BUS_SESSION, &error); + + if (!dbusGConnection) { + if (error) { + g_printerr("Failed to open connection to session bus: %s\n", error->message); + g_error_free(error); + } + return NS_ERROR_FAILURE; + } + + char *uri = g_filename_to_uri(PromiseFlatCString(aPath).get(), nullptr, nullptr); + if (uri == nullptr) { + return NS_ERROR_FAILURE; + } + + DBusConnection* dbusConnection = dbus_g_connection_get_connection(dbusGConnection); + // Make sure we do not exit the entire program if DBus connection get lost. + dbus_connection_set_exit_on_disconnect(dbusConnection, false); + + DBusGProxy* dbusGProxy = dbus_g_proxy_new_for_name(dbusGConnection, + "org.freedesktop.FileManager1", + "/org/freedesktop/FileManager1", + "org.freedesktop.FileManager1"); + + const char *uris[2] = { uri, nullptr }; + gboolean rv_dbus_call = dbus_g_proxy_call (dbusGProxy, "ShowItems", nullptr, G_TYPE_STRV, uris, + G_TYPE_STRING, "", G_TYPE_INVALID, G_TYPE_INVALID); + + g_object_unref(dbusGProxy); + dbus_g_connection_unref(dbusGConnection); + g_free(uri); + + if (!rv_dbus_call) { + org_freedesktop_FileManager1_exists = false; + return NS_ERROR_NOT_AVAILABLE; + } + + return NS_OK; +#endif +} + +/** + * Create or find already existing application info for specified command + * and application name. + * @param cmd command to execute + * @param appName application name + * @param appInfo location where created GAppInfo is stored + * @return NS_OK when object is created, NS_ERROR_FAILURE otherwise. + */ +NS_IMETHODIMP +nsGIOService::CreateAppFromCommand(nsACString const& cmd, + nsACString const& appName, + nsIGIOMimeApp** appInfo) +{ + GError *error = nullptr; + *appInfo = nullptr; + + GAppInfo *app_info = nullptr, *app_info_from_list = nullptr; + GList *apps = g_app_info_get_all(); + GList *apps_p = apps; + + // Try to find relevant and existing GAppInfo in all installed application + // We do this by comparing each GAppInfo's executable with out own + while (apps_p) { + app_info_from_list = (GAppInfo*) apps_p->data; + if (!app_info) { + // If the executable is not absolute, get it's full path + char *executable = g_find_program_in_path(g_app_info_get_executable(app_info_from_list)); + + if (executable && strcmp(executable, PromiseFlatCString(cmd).get()) == 0) { + g_object_ref (app_info_from_list); + app_info = app_info_from_list; + } + g_free(executable); + } + + g_object_unref(app_info_from_list); + apps_p = apps_p->next; + } + g_list_free(apps); + + if (!app_info) { + app_info = g_app_info_create_from_commandline(PromiseFlatCString(cmd).get(), + PromiseFlatCString(appName).get(), + G_APP_INFO_CREATE_SUPPORTS_URIS, + &error); + } + + if (!app_info) { + g_warning("Cannot create application info from command: %s", error->message); + g_error_free(error); + return NS_ERROR_FAILURE; + } + nsGIOMimeApp *mozApp = new nsGIOMimeApp(app_info); + NS_ENSURE_TRUE(mozApp, NS_ERROR_OUT_OF_MEMORY); + NS_ADDREF(*appInfo = mozApp); + return NS_OK; +} diff --git a/toolkit/system/gnome/nsGIOService.h b/toolkit/system/gnome/nsGIOService.h new file mode 100644 index 000000000..2ae7a7e1a --- /dev/null +++ b/toolkit/system/gnome/nsGIOService.h @@ -0,0 +1,24 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef nsGIOService_h_ +#define nsGIOService_h_ + +#include "nsIGIOService.h" + +#define NS_GIOSERVICE_CID \ +{0xe3a1f3c9, 0x3ae1, 0x4b40, {0xa5, 0xe0, 0x7b, 0x45, 0x7f, 0xc9, 0xa9, 0xad}} + +class nsGIOService final : public nsIGIOService +{ + ~nsGIOService() {} + +public: + NS_DECL_ISUPPORTS + NS_DECL_NSIGIOSERVICE +}; + +#endif + diff --git a/toolkit/system/gnome/nsGSettingsService.cpp b/toolkit/system/gnome/nsGSettingsService.cpp new file mode 100644 index 000000000..d8c46b4fe --- /dev/null +++ b/toolkit/system/gnome/nsGSettingsService.cpp @@ -0,0 +1,350 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/ArrayUtils.h" + +#include "nsGSettingsService.h" +#include "nsString.h" +#include "nsCOMPtr.h" +#include "nsMemory.h" +#include "prlink.h" +#include "nsComponentManagerUtils.h" +#include "nsIMutableArray.h" +#include "nsISupportsPrimitives.h" + +#include <glib.h> +#include <glib-object.h> + +using namespace mozilla; + +typedef struct _GSettings GSettings; +typedef struct _GVariantType GVariantType; +typedef struct _GVariant GVariant; + +#ifndef G_VARIANT_TYPE_INT32 +# define G_VARIANT_TYPE_INT32 ((const GVariantType *) "i") +# define G_VARIANT_TYPE_BOOLEAN ((const GVariantType *) "b") +# define G_VARIANT_TYPE_STRING ((const GVariantType *) "s") +# define G_VARIANT_TYPE_OBJECT_PATH ((const GVariantType *) "o") +# define G_VARIANT_TYPE_SIGNATURE ((const GVariantType *) "g") +#endif +#ifndef G_VARIANT_TYPE_STRING_ARRAY +# define G_VARIANT_TYPE_STRING_ARRAY ((const GVariantType *) "as") +#endif + +#define GSETTINGS_FUNCTIONS \ + FUNC(g_settings_new, GSettings *, (const char* schema)) \ + FUNC(g_settings_list_schemas, const char * const *, (void)) \ + FUNC(g_settings_list_keys, char **, (GSettings* settings)) \ + FUNC(g_settings_get_value, GVariant *, (GSettings* settings, const char* key)) \ + FUNC(g_settings_set_value, gboolean, (GSettings* settings, const char* key, GVariant* value)) \ + FUNC(g_settings_range_check, gboolean, (GSettings* settings, const char* key, GVariant* value)) \ + FUNC(g_variant_get_int32, gint32, (GVariant* variant)) \ + FUNC(g_variant_get_boolean, gboolean, (GVariant* variant)) \ + FUNC(g_variant_get_string, const char *, (GVariant* value, gsize* length)) \ + FUNC(g_variant_get_strv, const char **, (GVariant* value, gsize* length)) \ + FUNC(g_variant_is_of_type, gboolean, (GVariant* value, const GVariantType* type)) \ + FUNC(g_variant_new_int32, GVariant *, (gint32 value)) \ + FUNC(g_variant_new_boolean, GVariant *, (gboolean value)) \ + FUNC(g_variant_new_string, GVariant *, (const char* string)) \ + FUNC(g_variant_unref, void, (GVariant* value)) + +#define FUNC(name, type, params) \ + typedef type (*_##name##_fn) params; \ + static _##name##_fn _##name; + +GSETTINGS_FUNCTIONS + +#undef FUNC + +#define g_settings_new _g_settings_new +#define g_settings_list_schemas _g_settings_list_schemas +#define g_settings_list_keys _g_settings_list_keys +#define g_settings_get_value _g_settings_get_value +#define g_settings_set_value _g_settings_set_value +#define g_settings_range_check _g_settings_range_check +#define g_variant_get_int32 _g_variant_get_int32 +#define g_variant_get_boolean _g_variant_get_boolean +#define g_variant_get_string _g_variant_get_string +#define g_variant_get_strv _g_variant_get_strv +#define g_variant_is_of_type _g_variant_is_of_type +#define g_variant_new_int32 _g_variant_new_int32 +#define g_variant_new_boolean _g_variant_new_boolean +#define g_variant_new_string _g_variant_new_string +#define g_variant_unref _g_variant_unref + +static PRLibrary *gioLib = nullptr; + +class nsGSettingsCollection final : public nsIGSettingsCollection +{ +public: + NS_DECL_ISUPPORTS + NS_DECL_NSIGSETTINGSCOLLECTION + + explicit nsGSettingsCollection(GSettings* aSettings) : mSettings(aSettings), + mKeys(nullptr) {} +private: + ~nsGSettingsCollection(); + + bool KeyExists(const nsACString& aKey); + bool SetValue(const nsACString& aKey, + GVariant *aValue); + + GSettings *mSettings; + char **mKeys; +}; + +nsGSettingsCollection::~nsGSettingsCollection() +{ + g_strfreev(mKeys); + g_object_unref(mSettings); +} + +bool +nsGSettingsCollection::KeyExists(const nsACString& aKey) +{ + if (!mKeys) + mKeys = g_settings_list_keys(mSettings); + + for (uint32_t i = 0; mKeys[i] != nullptr; i++) { + if (aKey.Equals(mKeys[i])) + return true; + } + + return false; +} + +bool +nsGSettingsCollection::SetValue(const nsACString& aKey, + GVariant *aValue) +{ + if (!KeyExists(aKey) || + !g_settings_range_check(mSettings, + PromiseFlatCString(aKey).get(), + aValue)) { + g_variant_unref(aValue); + return false; + } + + return g_settings_set_value(mSettings, + PromiseFlatCString(aKey).get(), + aValue); +} + +NS_IMPL_ISUPPORTS(nsGSettingsCollection, nsIGSettingsCollection) + +NS_IMETHODIMP +nsGSettingsCollection::SetString(const nsACString& aKey, + const nsACString& aValue) +{ + GVariant *value = g_variant_new_string(PromiseFlatCString(aValue).get()); + if (!value) + return NS_ERROR_OUT_OF_MEMORY; + + bool res = SetValue(aKey, value); + + return res ? NS_OK : NS_ERROR_FAILURE; +} + +NS_IMETHODIMP +nsGSettingsCollection::SetBoolean(const nsACString& aKey, + bool aValue) +{ + GVariant *value = g_variant_new_boolean(aValue); + if (!value) + return NS_ERROR_OUT_OF_MEMORY; + + bool res = SetValue(aKey, value); + + return res ? NS_OK : NS_ERROR_FAILURE; +} + +NS_IMETHODIMP +nsGSettingsCollection::SetInt(const nsACString& aKey, + int32_t aValue) +{ + GVariant *value = g_variant_new_int32(aValue); + if (!value) + return NS_ERROR_OUT_OF_MEMORY; + + bool res = SetValue(aKey, value); + + return res ? NS_OK : NS_ERROR_FAILURE; +} + +NS_IMETHODIMP +nsGSettingsCollection::GetString(const nsACString& aKey, + nsACString& aResult) +{ + if (!KeyExists(aKey)) + return NS_ERROR_INVALID_ARG; + + GVariant *value = g_settings_get_value(mSettings, + PromiseFlatCString(aKey).get()); + if (!g_variant_is_of_type(value, G_VARIANT_TYPE_STRING) && + !g_variant_is_of_type(value, G_VARIANT_TYPE_OBJECT_PATH) && + !g_variant_is_of_type(value, G_VARIANT_TYPE_SIGNATURE)) { + g_variant_unref(value); + return NS_ERROR_FAILURE; + } + + aResult.Assign(g_variant_get_string(value, nullptr)); + g_variant_unref(value); + + return NS_OK; +} + +NS_IMETHODIMP +nsGSettingsCollection::GetBoolean(const nsACString& aKey, + bool* aResult) +{ + NS_ENSURE_ARG_POINTER(aResult); + + if (!KeyExists(aKey)) + return NS_ERROR_INVALID_ARG; + + GVariant *value = g_settings_get_value(mSettings, + PromiseFlatCString(aKey).get()); + if (!g_variant_is_of_type(value, G_VARIANT_TYPE_BOOLEAN)) { + g_variant_unref(value); + return NS_ERROR_FAILURE; + } + + gboolean res = g_variant_get_boolean(value); + *aResult = res ? true : false; + g_variant_unref(value); + + return NS_OK; +} + +NS_IMETHODIMP +nsGSettingsCollection::GetInt(const nsACString& aKey, + int32_t* aResult) +{ + NS_ENSURE_ARG_POINTER(aResult); + + if (!KeyExists(aKey)) + return NS_ERROR_INVALID_ARG; + + GVariant *value = g_settings_get_value(mSettings, + PromiseFlatCString(aKey).get()); + if (!g_variant_is_of_type(value, G_VARIANT_TYPE_INT32)) { + g_variant_unref(value); + return NS_ERROR_FAILURE; + } + + *aResult = g_variant_get_int32(value); + g_variant_unref(value); + + return NS_OK; +} + +// These types are local to nsGSettingsService::Init, but ISO C++98 doesn't +// allow a template (ArrayLength) to be instantiated based on a local type. +// Boo-urns! +typedef void (*nsGSettingsFunc)(); +struct nsGSettingsDynamicFunction { + const char *functionName; + nsGSettingsFunc *function; +}; + +NS_IMETHODIMP +nsGSettingsCollection::GetStringList(const nsACString& aKey, nsIArray** aResult) +{ + if (!KeyExists(aKey)) + return NS_ERROR_INVALID_ARG; + + nsCOMPtr<nsIMutableArray> items(do_CreateInstance(NS_ARRAY_CONTRACTID)); + if (!items) { + return NS_ERROR_OUT_OF_MEMORY; + } + + GVariant *value = g_settings_get_value(mSettings, + PromiseFlatCString(aKey).get()); + + if (!g_variant_is_of_type(value, G_VARIANT_TYPE_STRING_ARRAY)) { + g_variant_unref(value); + return NS_ERROR_FAILURE; + } + + const gchar ** gs_strings = g_variant_get_strv(value, nullptr); + if (!gs_strings) { + // empty array + items.forget(aResult); + g_variant_unref(value); + return NS_OK; + } + + const gchar** p_gs_strings = gs_strings; + while (*p_gs_strings != nullptr) + { + nsCOMPtr<nsISupportsCString> obj(do_CreateInstance(NS_SUPPORTS_CSTRING_CONTRACTID)); + if (obj) { + obj->SetData(nsDependentCString(*p_gs_strings)); + items->AppendElement(obj, false); + } + p_gs_strings++; + } + g_free(gs_strings); + items.forget(aResult); + g_variant_unref(value); + return NS_OK; +} + +nsresult +nsGSettingsService::Init() +{ +#define FUNC(name, type, params) { #name, (nsGSettingsFunc *)&_##name }, + static const nsGSettingsDynamicFunction kGSettingsSymbols[] = { + GSETTINGS_FUNCTIONS + }; +#undef FUNC + + if (!gioLib) { + gioLib = PR_LoadLibrary("libgio-2.0.so.0"); + if (!gioLib) + return NS_ERROR_FAILURE; + } + + for (uint32_t i = 0; i < ArrayLength(kGSettingsSymbols); i++) { + *kGSettingsSymbols[i].function = + PR_FindFunctionSymbol(gioLib, kGSettingsSymbols[i].functionName); + if (!*kGSettingsSymbols[i].function) { + return NS_ERROR_FAILURE; + } + } + + return NS_OK; +} + +NS_IMPL_ISUPPORTS(nsGSettingsService, nsIGSettingsService) + +nsGSettingsService::~nsGSettingsService() +{ + if (gioLib) { + PR_UnloadLibrary(gioLib); + gioLib = nullptr; + } +} + +NS_IMETHODIMP +nsGSettingsService::GetCollectionForSchema(const nsACString& schema, + nsIGSettingsCollection** collection) +{ + NS_ENSURE_ARG_POINTER(collection); + + const char * const *schemas = g_settings_list_schemas(); + + for (uint32_t i = 0; schemas[i] != nullptr; i++) { + if (schema.Equals(schemas[i])) { + GSettings *settings = g_settings_new(PromiseFlatCString(schema).get()); + nsGSettingsCollection *mozGSettings = new nsGSettingsCollection(settings); + NS_ADDREF(*collection = mozGSettings); + return NS_OK; + } + } + + return NS_ERROR_FAILURE; +} diff --git a/toolkit/system/gnome/nsGSettingsService.h b/toolkit/system/gnome/nsGSettingsService.h new file mode 100644 index 000000000..6ffcdedd1 --- /dev/null +++ b/toolkit/system/gnome/nsGSettingsService.h @@ -0,0 +1,27 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef nsGSettingsService_h_ +#define nsGSettingsService_h_ + +#include "nsIGSettingsService.h" + +#define NS_GSETTINGSSERVICE_CID \ +{0xbfd4a9d8, 0xd886, 0x4161, {0x81, 0xef, 0x88, 0x68, 0xda, 0x11, 0x41, 0x70}} + +class nsGSettingsService final : public nsIGSettingsService +{ +public: + NS_DECL_ISUPPORTS + NS_DECL_NSIGSETTINGSSERVICE + + nsresult Init(); + +private: + ~nsGSettingsService(); +}; + +#endif + diff --git a/toolkit/system/gnome/nsGnomeModule.cpp b/toolkit/system/gnome/nsGnomeModule.cpp new file mode 100644 index 000000000..6ecebcc1f --- /dev/null +++ b/toolkit/system/gnome/nsGnomeModule.cpp @@ -0,0 +1,78 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nsToolkitCompsCID.h" +#include "mozilla/ModuleUtils.h" + +#include <glib-object.h> + +#ifdef MOZ_ENABLE_GCONF +#include "nsGConfService.h" +NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsGConfService, Init) +#endif +#ifdef MOZ_ENABLE_GIO +#include "nsGIOService.h" +#include "nsGSettingsService.h" +#include "nsPackageKitService.h" +NS_GENERIC_FACTORY_CONSTRUCTOR(nsGIOService) +NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsGSettingsService, Init) +NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsPackageKitService, Init) +#endif +#include "nsSystemAlertsService.h" +NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsSystemAlertsService, Init) + +#ifdef MOZ_ENABLE_GCONF +NS_DEFINE_NAMED_CID(NS_GCONFSERVICE_CID); +#endif +#ifdef MOZ_ENABLE_GIO +NS_DEFINE_NAMED_CID(NS_GIOSERVICE_CID); +NS_DEFINE_NAMED_CID(NS_GSETTINGSSERVICE_CID); +NS_DEFINE_NAMED_CID(NS_PACKAGEKITSERVICE_CID); +#endif +NS_DEFINE_NAMED_CID(NS_SYSTEMALERTSSERVICE_CID); + +static const mozilla::Module::CIDEntry kGnomeCIDs[] = { +#ifdef MOZ_ENABLE_GCONF + { &kNS_GCONFSERVICE_CID, false, nullptr, nsGConfServiceConstructor }, +#endif +#ifdef MOZ_ENABLE_GIO + { &kNS_GIOSERVICE_CID, false, nullptr, nsGIOServiceConstructor }, + { &kNS_GSETTINGSSERVICE_CID, false, nullptr, nsGSettingsServiceConstructor }, + { &kNS_PACKAGEKITSERVICE_CID, false, nullptr, nsPackageKitServiceConstructor }, +#endif + { &kNS_SYSTEMALERTSSERVICE_CID, false, nullptr, nsSystemAlertsServiceConstructor }, + { nullptr } +}; + +static const mozilla::Module::ContractIDEntry kGnomeContracts[] = { +#ifdef MOZ_ENABLE_GCONF + { NS_GCONFSERVICE_CONTRACTID, &kNS_GCONFSERVICE_CID }, +#endif +#ifdef MOZ_ENABLE_GIO + { NS_GIOSERVICE_CONTRACTID, &kNS_GIOSERVICE_CID }, + { NS_GSETTINGSSERVICE_CONTRACTID, &kNS_GSETTINGSSERVICE_CID }, + { NS_PACKAGEKITSERVICE_CONTRACTID, &kNS_PACKAGEKITSERVICE_CID }, +#endif + { NS_SYSTEMALERTSERVICE_CONTRACTID, &kNS_SYSTEMALERTSSERVICE_CID }, + { nullptr } +}; + +static nsresult +InitGType () +{ + g_type_init(); + return NS_OK; +} + +static const mozilla::Module kGnomeModule = { + mozilla::Module::kVersion, + kGnomeCIDs, + kGnomeContracts, + nullptr, + nullptr, + InitGType +}; + +NSMODULE_DEFN(mozgnome) = &kGnomeModule; diff --git a/toolkit/system/gnome/nsPackageKitService.cpp b/toolkit/system/gnome/nsPackageKitService.cpp new file mode 100644 index 000000000..c32a91367 --- /dev/null +++ b/toolkit/system/gnome/nsPackageKitService.cpp @@ -0,0 +1,267 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nsArrayUtils.h" +#include "nsIObserver.h" +#include "nsIObserverService.h" +#include "nsISupportsPrimitives.h" +#include "nsPackageKitService.h" +#include "nsString.h" +#include "prlink.h" +#include "mozilla/Unused.h" +#include "mozilla/UniquePtr.h" + +#include <glib.h> +#include <glib-object.h> +#include <limits> + +using namespace mozilla; + +typedef struct _GAsyncResult GAsyncResult; +typedef enum { + G_BUS_TYPE_STARTER = -1, + G_BUS_TYPE_NONE = 0, + G_BUS_TYPE_SYSTEM = 1, + G_BUS_TYPE_SESSION = 2 +} GBusType; +typedef struct _GCancellable GCancellable; +typedef enum { + G_DBUS_CALL_FLAGS_NONE = 0, + G_DBUS_CALL_FLAGS_NO_AUTO_START = (1<<0) +} GDBusCallFlags; +typedef struct _GDBusInterfaceInfo GDBusInterfaceInfo; +typedef struct _GDBusProxy GDBusProxy; +typedef enum { + G_DBUS_PROXY_FLAGS_NONE = 0, + G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES = (1<<0), + G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS = (1<<1), + G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START = (1<<2), + G_DBUS_PROXY_FLAGS_GET_INVALIDATED_PROPERTIES = (1<<3) +} GDBusProxyFlags; +typedef struct _GVariant GVariant; +typedef void (*GAsyncReadyCallback) (GObject *source_object, + GAsyncResult *res, + gpointer user_data); + +#define GDBUS_FUNCTIONS \ + FUNC(g_dbus_proxy_call, void, (GDBusProxy *proxy, const gchar *method_name, GVariant *parameters, GDBusCallFlags flags, gint timeout_msec, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data)) \ + FUNC(g_dbus_proxy_call_finish, GVariant*, (GDBusProxy *proxy, GAsyncResult *res, GError **error)) \ + FUNC(g_dbus_proxy_new_finish, GDBusProxy*, (GAsyncResult *res, GError **error)) \ + FUNC(g_dbus_proxy_new_for_bus, void, (GBusType bus_type, GDBusProxyFlags flags, GDBusInterfaceInfo *info, const gchar *name, const gchar *object_path, const gchar *interface_name, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data)) \ + FUNC(g_variant_is_floating, gboolean, (GVariant *value)) \ + FUNC(g_variant_new, GVariant*, (const gchar *format_string, ...)) \ + FUNC(g_variant_unref, void, (GVariant* value)) + +#define FUNC(name, type, params) \ + typedef type (*_##name##_fn) params; \ + static _##name##_fn _##name; + +GDBUS_FUNCTIONS + +#undef FUNC + +#define g_dbus_proxy_call _g_dbus_proxy_call +#define g_dbus_proxy_call_finish _g_dbus_proxy_call_finish +#define g_dbus_proxy_new_finish _g_dbus_proxy_new_finish +#define g_dbus_proxy_new_for_bus _g_dbus_proxy_new_for_bus +#define g_variant_is_floating _g_variant_is_floating +#define g_variant_new _g_variant_new +#define g_variant_unref _g_variant_unref + +static PRLibrary *gioLib = nullptr; + +typedef void (*nsGDBusFunc)(); +struct nsGDBusDynamicFunction { + const char *functionName; + nsGDBusFunc *function; +}; + +nsresult +nsPackageKitService::Init() +{ +#define FUNC(name, type, params) { #name, (nsGDBusFunc *)&_##name }, + const nsGDBusDynamicFunction kGDBusSymbols[] = { + GDBUS_FUNCTIONS + }; +#undef FUNC + + if (!gioLib) { + gioLib = PR_LoadLibrary("libgio-2.0.so.0"); + if (!gioLib) + return NS_ERROR_FAILURE; + } + + for (uint32_t i = 0; i < ArrayLength(kGDBusSymbols); i++) { + *kGDBusSymbols[i].function = + PR_FindFunctionSymbol(gioLib, kGDBusSymbols[i].functionName); + if (!*kGDBusSymbols[i].function) { + return NS_ERROR_FAILURE; + } + } + + return NS_OK; +} + +NS_IMPL_ISUPPORTS(nsPackageKitService, nsIPackageKitService) + +nsPackageKitService::~nsPackageKitService() +{ + if (gioLib) { + PR_UnloadLibrary(gioLib); + gioLib = nullptr; + } +} + +static const char* InstallPackagesMethods[] = { + "InstallPackageNames", + "InstallMimeTypes", + "InstallFontconfigResources", + "InstallGStreamerResources" +}; + +struct InstallPackagesProxyNewData { + nsCOMPtr<nsIObserver> observer; + uint32_t method; + GVariant* parameters; +}; + +static void +InstallPackagesNotifyObserver(nsIObserver* aObserver, + gchar* aErrorMessage) +{ + if (aObserver) { + aObserver->Observe(nullptr, "packagekit-install", + aErrorMessage ? + NS_ConvertUTF8toUTF16(aErrorMessage).get() : + nullptr); + } +} + +static void +InstallPackagesProxyCallCallback(GObject *aSourceObject, + GAsyncResult *aResult, + gpointer aUserData) +{ + nsCOMPtr<nsIObserver> observer = static_cast<nsIObserver*>(aUserData); + GDBusProxy* proxy = reinterpret_cast<GDBusProxy*>(aSourceObject); + + GError* error = nullptr; + GVariant* result = g_dbus_proxy_call_finish(proxy, aResult, &error); + if (result) { + InstallPackagesNotifyObserver(observer, nullptr); + g_variant_unref(result); + } else { + NS_ASSERTION(error, "g_dbus_proxy_call_finish should set error when it returns NULL"); + InstallPackagesNotifyObserver(observer, error->message); + g_error_free(error); + } + + g_object_unref(proxy); + Unused << observer.forget().take(); +} + +static void +InstallPackagesProxyNewCallback(GObject *aSourceObject, + GAsyncResult *aResult, + gpointer aUserData) +{ + InstallPackagesProxyNewData* userData = + static_cast<InstallPackagesProxyNewData*>(aUserData); + + NS_ASSERTION(g_variant_is_floating(userData->parameters), + "userData->parameters should be a floating reference."); + + GError* error = nullptr; + GDBusProxy* proxy = g_dbus_proxy_new_finish(aResult, &error); + + if (proxy) { + // Send the asynchronous request to install the packages + // This call will consume the floating reference userData->parameters so we + // don't need to release it explicitly. + nsIObserver* observer; + userData->observer.forget(&observer); + g_dbus_proxy_call(proxy, + InstallPackagesMethods[userData->method], + userData->parameters, + G_DBUS_CALL_FLAGS_NONE, + G_MAXINT, + nullptr, + &InstallPackagesProxyCallCallback, + static_cast<gpointer>(observer)); + } else { + NS_ASSERTION(error, "g_dbus_proxy_new_finish should set error when it returns NULL"); + InstallPackagesNotifyObserver(userData->observer, error->message); + g_error_free(error); + g_variant_unref(userData->parameters); + } + delete userData; +} + +NS_IMETHODIMP +nsPackageKitService::InstallPackages(uint32_t aInstallMethod, + nsIArray* aPackageArray, + nsIObserver* aObserver) +{ + NS_ENSURE_ARG(aPackageArray); + + uint32_t arrayLength; + aPackageArray->GetLength(&arrayLength); + if (arrayLength == 0 || + arrayLength == std::numeric_limits<uint32_t>::max() || + aInstallMethod >= PK_INSTALL_METHOD_COUNT) { + return NS_ERROR_INVALID_ARG; + } + + // Create the GVariant* parameter from the list of packages. + GVariant* parameters = nullptr; + auto packages = MakeUnique<gchar*[]>(arrayLength + 1); + + nsresult rv = NS_OK; + for (uint32_t i = 0; i < arrayLength; i++) { + nsCOMPtr<nsISupportsString> package = + do_QueryElementAt(aPackageArray, i); + if (!package) { + rv = NS_ERROR_FAILURE; + break; + } + nsString data; + package->GetData(data); + packages[i] = g_strdup(NS_ConvertUTF16toUTF8(data).get()); + if (!packages[i]) { + rv = NS_ERROR_OUT_OF_MEMORY; + break; + } + } + packages[arrayLength] = nullptr; + + if (NS_SUCCEEDED(rv)) { + // We create a new GVariant object to send parameters to PackageKit. + parameters = g_variant_new("(u^ass)", static_cast<guint32>(0), + packages.get(), "hide-finished"); + if (!parameters) { + rv = NS_ERROR_OUT_OF_MEMORY; + } + } + for (uint32_t i = 0; i < arrayLength; i++) { + g_free(packages[i]); + } + NS_ENSURE_SUCCESS(rv, rv); + + // Send the asynchronous request to load the bus proxy + InstallPackagesProxyNewData* data = new InstallPackagesProxyNewData; + data->observer = aObserver; + data->method = aInstallMethod; + data->parameters = parameters; + g_dbus_proxy_new_for_bus(G_BUS_TYPE_SESSION, + G_DBUS_PROXY_FLAGS_NONE, + nullptr, + "org.freedesktop.PackageKit", + "/org/freedesktop/PackageKit", + "org.freedesktop.PackageKit.Modify", + nullptr, + &InstallPackagesProxyNewCallback, + static_cast<gpointer>(data)); + return NS_OK; +} diff --git a/toolkit/system/gnome/nsPackageKitService.h b/toolkit/system/gnome/nsPackageKitService.h new file mode 100644 index 000000000..b14c7c51f --- /dev/null +++ b/toolkit/system/gnome/nsPackageKitService.h @@ -0,0 +1,26 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef nsPackageKitService_h_ +#define nsPackageKitService_h_ + +#include "nsIPackageKitService.h" + +#define NS_PACKAGEKITSERVICE_CID \ +{0x9c95515e, 0x611d, 0x11e4, {0xb9, 0x7e, 0x60, 0xa4, 0x4c, 0x71, 0x70, 0x42}} + +class nsPackageKitService final : public nsIPackageKitService +{ +public: + NS_DECL_ISUPPORTS + NS_DECL_NSIPACKAGEKITSERVICE + + nsresult Init(); + +private: + ~nsPackageKitService(); +}; + +#endif diff --git a/toolkit/system/gnome/nsSystemAlertsService.cpp b/toolkit/system/gnome/nsSystemAlertsService.cpp new file mode 100644 index 000000000..2af64383c --- /dev/null +++ b/toolkit/system/gnome/nsSystemAlertsService.cpp @@ -0,0 +1,118 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode:nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nsComponentManagerUtils.h" +#include "nsXULAppAPI.h" +#include "nsSystemAlertsService.h" +#include "nsAlertsIconListener.h" +#include "nsAutoPtr.h" + +NS_IMPL_ADDREF(nsSystemAlertsService) +NS_IMPL_RELEASE(nsSystemAlertsService) + +NS_INTERFACE_MAP_BEGIN(nsSystemAlertsService) + NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIAlertsService) + NS_INTERFACE_MAP_ENTRY(nsIAlertsService) +NS_INTERFACE_MAP_END_THREADSAFE + +nsSystemAlertsService::nsSystemAlertsService() +{ +} + +nsSystemAlertsService::~nsSystemAlertsService() +{} + +nsresult +nsSystemAlertsService::Init() +{ + return NS_OK; +} + +NS_IMETHODIMP nsSystemAlertsService::ShowAlertNotification(const nsAString & aImageUrl, const nsAString & aAlertTitle, + const nsAString & aAlertText, bool aAlertTextClickable, + const nsAString & aAlertCookie, + nsIObserver * aAlertListener, + const nsAString & aAlertName, + const nsAString & aBidi, + const nsAString & aLang, + const nsAString & aData, + nsIPrincipal * aPrincipal, + bool aInPrivateBrowsing, + bool aRequireInteraction) +{ + nsCOMPtr<nsIAlertNotification> alert = + do_CreateInstance(ALERT_NOTIFICATION_CONTRACTID); + NS_ENSURE_TRUE(alert, NS_ERROR_FAILURE); + nsresult rv = alert->Init(aAlertName, aImageUrl, aAlertTitle, + aAlertText, aAlertTextClickable, + aAlertCookie, aBidi, aLang, aData, + aPrincipal, aInPrivateBrowsing, + aRequireInteraction); + NS_ENSURE_SUCCESS(rv, rv); + return ShowAlert(alert, aAlertListener); +} + +NS_IMETHODIMP nsSystemAlertsService::ShowPersistentNotification(const nsAString& aPersistentData, + nsIAlertNotification* aAlert, + nsIObserver* aAlertListener) +{ + return ShowAlert(aAlert, aAlertListener); +} + +NS_IMETHODIMP nsSystemAlertsService::ShowAlert(nsIAlertNotification* aAlert, + nsIObserver* aAlertListener) +{ + NS_ENSURE_ARG(aAlert); + + nsAutoString alertName; + nsresult rv = aAlert->GetName(alertName); + NS_ENSURE_SUCCESS(rv, rv); + + RefPtr<nsAlertsIconListener> alertListener = + new nsAlertsIconListener(this, alertName); + if (!alertListener) + return NS_ERROR_OUT_OF_MEMORY; + + AddListener(alertName, alertListener); + return alertListener->InitAlertAsync(aAlert, aAlertListener); +} + +NS_IMETHODIMP nsSystemAlertsService::CloseAlert(const nsAString& aAlertName, + nsIPrincipal* aPrincipal) +{ + RefPtr<nsAlertsIconListener> listener = mActiveListeners.Get(aAlertName); + if (!listener) { + return NS_OK; + } + mActiveListeners.Remove(aAlertName); + return listener->Close(); +} + +bool nsSystemAlertsService::IsActiveListener(const nsAString& aAlertName, + nsAlertsIconListener* aListener) +{ + return mActiveListeners.Get(aAlertName) == aListener; +} + +void nsSystemAlertsService::AddListener(const nsAString& aAlertName, + nsAlertsIconListener* aListener) +{ + RefPtr<nsAlertsIconListener> oldListener = mActiveListeners.Get(aAlertName); + mActiveListeners.Put(aAlertName, aListener); + if (oldListener) { + // If an alert with this name already exists, close it. + oldListener->Close(); + } +} + +void nsSystemAlertsService::RemoveListener(const nsAString& aAlertName, + nsAlertsIconListener* aListener) +{ + if (IsActiveListener(aAlertName, aListener)) { + // The alert may have been replaced; only remove it from the active + // listeners map if it's the same. + mActiveListeners.Remove(aAlertName); + } +} diff --git a/toolkit/system/gnome/nsSystemAlertsService.h b/toolkit/system/gnome/nsSystemAlertsService.h new file mode 100644 index 000000000..66ae2ea27 --- /dev/null +++ b/toolkit/system/gnome/nsSystemAlertsService.h @@ -0,0 +1,39 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef nsSystemAlertsService_h__ +#define nsSystemAlertsService_h__ + +#include "nsIAlertsService.h" +#include "nsDataHashtable.h" +#include "nsCOMPtr.h" + +class nsAlertsIconListener; + +class nsSystemAlertsService : public nsIAlertsService +{ +public: + NS_DECL_NSIALERTSSERVICE + NS_DECL_ISUPPORTS + + nsSystemAlertsService(); + + nsresult Init(); + + bool IsActiveListener(const nsAString& aAlertName, + nsAlertsIconListener* aListener); + void RemoveListener(const nsAString& aAlertName, + nsAlertsIconListener* aListener); + +protected: + virtual ~nsSystemAlertsService(); + + void AddListener(const nsAString& aAlertName, + nsAlertsIconListener* aListener); + + nsDataHashtable<nsStringHashKey, nsAlertsIconListener*> mActiveListeners; +}; + +#endif /* nsSystemAlertsService_h__ */ diff --git a/toolkit/system/osxproxy/ProxyUtils.h b/toolkit/system/osxproxy/ProxyUtils.h new file mode 100644 index 000000000..d6e5ddbd4 --- /dev/null +++ b/toolkit/system/osxproxy/ProxyUtils.h @@ -0,0 +1,21 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_toolkit_system_osxproxy_ProxyUtils_h +#define mozilla_toolkit_system_osxproxy_ProxyUtils_h + +#include "nsStringGlue.h" + +namespace mozilla { +namespace toolkit { +namespace system { + +bool IsHostProxyEntry(const nsACString& aHost, const nsACString& aOverride); + +} // namespace system +} // namespace toolkit +} // namespace mozilla + +#endif // mozilla_toolkit_system_osxproxy_ProxyUtils_h diff --git a/toolkit/system/osxproxy/ProxyUtils.mm b/toolkit/system/osxproxy/ProxyUtils.mm new file mode 100644 index 000000000..4e59f226a --- /dev/null +++ b/toolkit/system/osxproxy/ProxyUtils.mm @@ -0,0 +1,182 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "ProxyUtils.h" +#include "nsTArray.h" +#include "prnetdb.h" +#include "prtypes.h" + +namespace mozilla { +namespace toolkit { +namespace system { + +/** + * Normalize the short IP form into the complete form. + * For example, it converts "192.168" into "192.168.0.0" + */ +static bool +NormalizeAddr(const nsACString& aAddr, nsCString& aNormalized) +{ + nsTArray<nsCString> addr; + if (!ParseString(aAddr, '.', addr)) { + return false; + } + aNormalized = ""; + for (uint32_t i = 0; i < 4; ++i) { + if (i != 0) { + aNormalized.Append("."); + } + if (i < addr.Length()) { + aNormalized.Append(addr[i]); + } else { + aNormalized.Append("0"); + } + } + return true; +} + +static PRUint32 +MaskIPv4Addr(PRUint32 aAddr, uint16_t aMaskLen) +{ + if (aMaskLen == 32) { + return aAddr; + } + return PR_htonl(PR_ntohl(aAddr) & (~0L << (32 - aMaskLen))); +} + +static void +MaskIPv6Addr(PRIPv6Addr& aAddr, uint16_t aMaskLen) +{ + if (aMaskLen == 128) { + return; + } + + if (aMaskLen > 96) { + aAddr.pr_s6_addr32[3] = PR_htonl( + PR_ntohl(aAddr.pr_s6_addr32[3]) & (~0L << (128 - aMaskLen))); + } else if (aMaskLen > 64) { + aAddr.pr_s6_addr32[3] = 0; + aAddr.pr_s6_addr32[2] = PR_htonl( + PR_ntohl(aAddr.pr_s6_addr32[2]) & (~0L << (96 - aMaskLen))); + } else if (aMaskLen > 32) { + aAddr.pr_s6_addr32[3] = 0; + aAddr.pr_s6_addr32[2] = 0; + aAddr.pr_s6_addr32[1] = PR_htonl( + PR_ntohl(aAddr.pr_s6_addr32[1]) & (~0L << (64 - aMaskLen))); + } else { + aAddr.pr_s6_addr32[3] = 0; + aAddr.pr_s6_addr32[2] = 0; + aAddr.pr_s6_addr32[1] = 0; + aAddr.pr_s6_addr32[0] = PR_htonl( + PR_ntohl(aAddr.pr_s6_addr32[0]) & (~0L << (32 - aMaskLen))); + } + + return; +} + +static bool +IsMatchMask(const nsACString& aHost, const nsACString& aOverride) +{ + nsresult rv; + + auto tokenEnd = aOverride.FindChar('/'); + if (tokenEnd == -1) { + return false; + } + + nsAutoCString prefixStr(Substring(aOverride, + tokenEnd + 1, + aOverride.Length() - tokenEnd - 1)); + auto maskLen = prefixStr.ToInteger(&rv); + if (NS_WARN_IF(NS_FAILED(rv))) { + return false; + } + + nsAutoCString override(aOverride); + if (!NormalizeAddr(Substring(aOverride, 0, tokenEnd), override)) { + return false; + } + + PRNetAddr prAddrHost; + PRNetAddr prAddrOverride; + if (PR_SUCCESS != PR_StringToNetAddr(PromiseFlatCString(aHost).get(), + &prAddrHost) || + PR_SUCCESS != PR_StringToNetAddr(override.get(), + &prAddrOverride)) { + return false; + } + + if (prAddrHost.raw.family == PR_AF_INET && + prAddrOverride.raw.family == PR_AF_INET) { + return MaskIPv4Addr(prAddrHost.inet.ip, maskLen) == + MaskIPv4Addr(prAddrOverride.inet.ip, maskLen); + } + else if (prAddrHost.raw.family == PR_AF_INET6 && + prAddrOverride.raw.family == PR_AF_INET6) { + MaskIPv6Addr(prAddrHost.ipv6.ip, maskLen); + MaskIPv6Addr(prAddrOverride.ipv6.ip, maskLen); + + return memcmp(&prAddrHost.ipv6.ip, + &prAddrOverride.ipv6.ip, + sizeof(PRIPv6Addr)) == 0; + } + + return false; +} + +static bool +IsMatchWildcard(const nsACString& aHost, const nsACString& aOverride) +{ + nsAutoCString host(aHost); + nsAutoCString override(aOverride); + + int32_t overrideLength = override.Length(); + int32_t tokenStart = 0; + int32_t offset = 0; + bool star = false; + + while (tokenStart < overrideLength) { + int32_t tokenEnd = override.FindChar('*', tokenStart); + if (tokenEnd == tokenStart) { + // Star is the first character in the token. + star = true; + tokenStart++; + // If the character following the '*' is a '.' character then skip + // it so that "*.foo.com" allows "foo.com". + if (override.FindChar('.', tokenStart) == tokenStart) { + nsAutoCString token(Substring(override, + tokenStart + 1, + overrideLength - tokenStart - 1)); + if (host.Equals(token)) { + return true; + } + } + } else { + if (tokenEnd == -1) { + tokenEnd = overrideLength; // no '*' char, match rest of string + } + nsAutoCString token(Substring(override, tokenStart, tokenEnd - tokenStart)); + offset = host.Find(token, offset); + if (offset == -1 || (!star && offset)) { + return false; + } + star = false; + tokenStart = tokenEnd; + offset += token.Length(); + } + } + + return (star || (offset == static_cast<int32_t>(host.Length()))); +} + +bool +IsHostProxyEntry(const nsACString& aHost, const nsACString& aOverride) +{ + return IsMatchMask(aHost, aOverride) || IsMatchWildcard(aHost, aOverride); +} + +} // namespace system +} // namespace toolkit +} // namespace mozilla diff --git a/toolkit/system/osxproxy/moz.build b/toolkit/system/osxproxy/moz.build new file mode 100644 index 000000000..e9024f8cf --- /dev/null +++ b/toolkit/system/osxproxy/moz.build @@ -0,0 +1,14 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +TEST_DIRS += ['tests/gtest'] + +SOURCES += [ + 'nsOSXSystemProxySettings.mm', + 'ProxyUtils.mm', +] + +FINAL_LIBRARY = 'xul' diff --git a/toolkit/system/osxproxy/nsOSXSystemProxySettings.mm b/toolkit/system/osxproxy/nsOSXSystemProxySettings.mm new file mode 100644 index 000000000..77fd2e482 --- /dev/null +++ b/toolkit/system/osxproxy/nsOSXSystemProxySettings.mm @@ -0,0 +1,326 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim:set ts=2 sw=2 sts=2 et: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#import <Cocoa/Cocoa.h> +#import <SystemConfiguration/SystemConfiguration.h> + +#include "nsISystemProxySettings.h" +#include "mozilla/ModuleUtils.h" +#include "nsIServiceManager.h" +#include "nsPrintfCString.h" +#include "nsNetCID.h" +#include "nsISupportsPrimitives.h" +#include "nsIURI.h" +#include "nsObjCExceptions.h" +#include "mozilla/Attributes.h" +#include "ProxyUtils.h" + +class nsOSXSystemProxySettings final : public nsISystemProxySettings { +public: + NS_DECL_THREADSAFE_ISUPPORTS + NS_DECL_NSISYSTEMPROXYSETTINGS + + nsOSXSystemProxySettings(); + nsresult Init(); + + // called by OSX when the proxy settings have changed + void ProxyHasChanged(); + + // is there a PAC url specified in the system configuration + bool IsAutoconfigEnabled() const; + // retrieve the pac url + nsresult GetAutoconfigURL(nsAutoCString& aResult) const; + + // Find the SystemConfiguration proxy & port for a given URI + nsresult FindSCProxyPort(const nsACString &aScheme, nsACString& aResultHost, int32_t& aResultPort, bool& aResultSocksProxy); + + // is host:port on the proxy exception list? + bool IsInExceptionList(const nsACString& aHost) const; + +private: + ~nsOSXSystemProxySettings(); + + SCDynamicStoreContext mContext; + SCDynamicStoreRef mSystemDynamicStore; + NSDictionary* mProxyDict; + + + // Mapping of URI schemes to SystemConfiguration keys + struct SchemeMapping { + const char* mScheme; + CFStringRef mEnabled; + CFStringRef mHost; + CFStringRef mPort; + bool mIsSocksProxy; + }; + static const SchemeMapping gSchemeMappingList[]; +}; + +NS_IMPL_ISUPPORTS(nsOSXSystemProxySettings, nsISystemProxySettings) + +NS_IMETHODIMP +nsOSXSystemProxySettings::GetMainThreadOnly(bool *aMainThreadOnly) +{ + *aMainThreadOnly = true; + return NS_OK; +} + +// Mapping of URI schemes to SystemConfiguration keys +const nsOSXSystemProxySettings::SchemeMapping nsOSXSystemProxySettings::gSchemeMappingList[] = { + {"http", kSCPropNetProxiesHTTPEnable, kSCPropNetProxiesHTTPProxy, kSCPropNetProxiesHTTPPort, false}, + {"https", kSCPropNetProxiesHTTPSEnable, kSCPropNetProxiesHTTPSProxy, kSCPropNetProxiesHTTPSPort, false}, + {"ftp", kSCPropNetProxiesFTPEnable, kSCPropNetProxiesFTPProxy, kSCPropNetProxiesFTPPort, false}, + {"socks", kSCPropNetProxiesSOCKSEnable, kSCPropNetProxiesSOCKSProxy, kSCPropNetProxiesSOCKSPort, true}, + {NULL, NULL, NULL, NULL, false}, +}; + +static void +ProxyHasChangedWrapper(SCDynamicStoreRef aStore, CFArrayRef aChangedKeys, void* aInfo) +{ + static_cast<nsOSXSystemProxySettings*>(aInfo)->ProxyHasChanged(); +} + + +nsOSXSystemProxySettings::nsOSXSystemProxySettings() + : mSystemDynamicStore(NULL), mProxyDict(NULL) +{ + mContext = (SCDynamicStoreContext){0, this, NULL, NULL, NULL}; +} + +nsresult +nsOSXSystemProxySettings::Init() +{ + NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; + + // Register for notification of proxy setting changes + // See: http://developer.apple.com/documentation/Networking/Conceptual/CFNetwork/CFStreamTasks/chapter_4_section_5.html + mSystemDynamicStore = SCDynamicStoreCreate(NULL, CFSTR("Mozilla"), ProxyHasChangedWrapper, &mContext); + if (!mSystemDynamicStore) + return NS_ERROR_FAILURE; + + // Set up the store to monitor any changes to the proxies + CFStringRef proxiesKey = SCDynamicStoreKeyCreateProxies(NULL); + if (!proxiesKey) + return NS_ERROR_FAILURE; + + CFArrayRef keyArray = CFArrayCreate(NULL, (const void**)(&proxiesKey), 1, &kCFTypeArrayCallBacks); + CFRelease(proxiesKey); + if (!keyArray) + return NS_ERROR_FAILURE; + + SCDynamicStoreSetNotificationKeys(mSystemDynamicStore, keyArray, NULL); + CFRelease(keyArray); + + // Add the dynamic store to the run loop + CFRunLoopSourceRef storeRLSource = SCDynamicStoreCreateRunLoopSource(NULL, mSystemDynamicStore, 0); + if (!storeRLSource) + return NS_ERROR_FAILURE; + CFRunLoopAddSource(CFRunLoopGetCurrent(), storeRLSource, kCFRunLoopCommonModes); + CFRelease(storeRLSource); + + // Load the initial copy of proxy info + mProxyDict = (NSDictionary*)SCDynamicStoreCopyProxies(mSystemDynamicStore); + if (!mProxyDict) + return NS_ERROR_FAILURE; + + return NS_OK; + + NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; +} + +nsOSXSystemProxySettings::~nsOSXSystemProxySettings() +{ + NS_OBJC_BEGIN_TRY_ABORT_BLOCK; + + [mProxyDict release]; + + if (mSystemDynamicStore) { + // Invalidate the dynamic store's run loop source + // to get the store out of the run loop + CFRunLoopSourceRef rls = SCDynamicStoreCreateRunLoopSource(NULL, mSystemDynamicStore, 0); + if (rls) { + CFRunLoopSourceInvalidate(rls); + CFRelease(rls); + } + CFRelease(mSystemDynamicStore); + } + + NS_OBJC_END_TRY_ABORT_BLOCK; +} + + +void +nsOSXSystemProxySettings::ProxyHasChanged() +{ + NS_OBJC_BEGIN_TRY_ABORT_BLOCK; + + [mProxyDict release]; + mProxyDict = (NSDictionary*)SCDynamicStoreCopyProxies(mSystemDynamicStore); + + NS_OBJC_END_TRY_ABORT_BLOCK; +} + +nsresult +nsOSXSystemProxySettings::FindSCProxyPort(const nsACString &aScheme, nsACString& aResultHost, int32_t& aResultPort, bool& aResultSocksProxy) +{ + NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; + + NS_ENSURE_TRUE(mProxyDict != NULL, NS_ERROR_FAILURE); + + for (const SchemeMapping* keys = gSchemeMappingList; keys->mScheme != NULL; ++keys) { + // Check for matching scheme (when appropriate) + if (strcasecmp(keys->mScheme, PromiseFlatCString(aScheme).get()) && + !keys->mIsSocksProxy) + continue; + + // Check the proxy is enabled + NSNumber* enabled = [mProxyDict objectForKey:(NSString*)keys->mEnabled]; + NS_ENSURE_TRUE(enabled == NULL || [enabled isKindOfClass:[NSNumber class]], NS_ERROR_FAILURE); + if ([enabled intValue] == 0) + continue; + + // Get the proxy host + NSString* host = [mProxyDict objectForKey:(NSString*)keys->mHost]; + if (host == NULL) + break; + NS_ENSURE_TRUE([host isKindOfClass:[NSString class]], NS_ERROR_FAILURE); + aResultHost.Assign([host UTF8String]); + + // Get the proxy port + NSNumber* port = [mProxyDict objectForKey:(NSString*)keys->mPort]; + NS_ENSURE_TRUE([port isKindOfClass:[NSNumber class]], NS_ERROR_FAILURE); + aResultPort = [port intValue]; + + aResultSocksProxy = keys->mIsSocksProxy; + + return NS_OK; + } + + return NS_ERROR_FAILURE; + + NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; +} + +bool +nsOSXSystemProxySettings::IsAutoconfigEnabled() const +{ + NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN; + + NSNumber* value = [mProxyDict objectForKey:(NSString*)kSCPropNetProxiesProxyAutoConfigEnable]; + NS_ENSURE_TRUE(value == NULL || [value isKindOfClass:[NSNumber class]], false); + return ([value intValue] != 0); + + NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(false); +} + +nsresult +nsOSXSystemProxySettings::GetAutoconfigURL(nsAutoCString& aResult) const +{ + NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; + + NSString* value = [mProxyDict objectForKey:(NSString*)kSCPropNetProxiesProxyAutoConfigURLString]; + if (value != NULL) { + NS_ENSURE_TRUE([value isKindOfClass:[NSString class]], NS_ERROR_FAILURE); + aResult.Assign([value UTF8String]); + return NS_OK; + } + + return NS_ERROR_FAILURE; + + NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; +} + +bool +nsOSXSystemProxySettings::IsInExceptionList(const nsACString& aHost) const +{ + NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN; + + NS_ENSURE_TRUE(mProxyDict != NULL, false); + + NSArray* exceptionList = [mProxyDict objectForKey:(NSString*)kSCPropNetProxiesExceptionsList]; + NS_ENSURE_TRUE(exceptionList == NULL || [exceptionList isKindOfClass:[NSArray class]], false); + + NSEnumerator* exceptionEnumerator = [exceptionList objectEnumerator]; + NSString* currentValue = NULL; + while ((currentValue = [exceptionEnumerator nextObject])) { + NS_ENSURE_TRUE([currentValue isKindOfClass:[NSString class]], false); + nsAutoCString overrideStr([currentValue UTF8String]); + if (mozilla::toolkit::system::IsHostProxyEntry(aHost, overrideStr)) + return true; + } + + NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(false); +} + +nsresult +nsOSXSystemProxySettings::GetPACURI(nsACString& aResult) +{ + NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; + + NS_ENSURE_TRUE(mProxyDict != NULL, NS_ERROR_FAILURE); + + nsAutoCString pacUrl; + if (IsAutoconfigEnabled() && NS_SUCCEEDED(GetAutoconfigURL(pacUrl))) { + aResult.Assign(pacUrl); + return NS_OK; + } + + return NS_ERROR_FAILURE; + + NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; +} + +nsresult +nsOSXSystemProxySettings::GetProxyForURI(const nsACString & aSpec, + const nsACString & aScheme, + const nsACString & aHost, + const int32_t aPort, + nsACString & aResult) +{ + NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; + + int32_t proxyPort; + nsAutoCString proxyHost; + bool proxySocks; + nsresult rv = FindSCProxyPort(aScheme, proxyHost, proxyPort, proxySocks); + + if (NS_FAILED(rv) || IsInExceptionList(aHost)) { + aResult.AssignLiteral("DIRECT"); + } else if (proxySocks) { + aResult.Assign(NS_LITERAL_CSTRING("SOCKS ") + proxyHost + nsPrintfCString(":%d", proxyPort)); + } else { + aResult.Assign(NS_LITERAL_CSTRING("PROXY ") + proxyHost + nsPrintfCString(":%d", proxyPort)); + } + + return NS_OK; + + NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; +} + +#define NS_OSXSYSTEMPROXYSERVICE_CID /* 9afcd4b8-2e0f-41f4-8f1f-3bf0d3cf67de */\ + { 0x9afcd4b8, 0x2e0f, 0x41f4, \ + { 0x8f, 0x1f, 0x3b, 0xf0, 0xd3, 0xcf, 0x67, 0xde } } + +NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsOSXSystemProxySettings, Init); +NS_DEFINE_NAMED_CID(NS_OSXSYSTEMPROXYSERVICE_CID); + +static const mozilla::Module::CIDEntry kOSXSysProxyCIDs[] = { + { &kNS_OSXSYSTEMPROXYSERVICE_CID, false, NULL, nsOSXSystemProxySettingsConstructor }, + { NULL } +}; + +static const mozilla::Module::ContractIDEntry kOSXSysProxyContracts[] = { + { NS_SYSTEMPROXYSETTINGS_CONTRACTID, &kNS_OSXSYSTEMPROXYSERVICE_CID }, + { NULL } +}; + +static const mozilla::Module kOSXSysProxyModule = { + mozilla::Module::kVersion, + kOSXSysProxyCIDs, + kOSXSysProxyContracts +}; + +NSMODULE_DEFN(nsOSXProxyModule) = &kOSXSysProxyModule; diff --git a/toolkit/system/osxproxy/tests/gtest/TestProxyBypassRules.cpp b/toolkit/system/osxproxy/tests/gtest/TestProxyBypassRules.cpp new file mode 100644 index 000000000..6d8000d71 --- /dev/null +++ b/toolkit/system/osxproxy/tests/gtest/TestProxyBypassRules.cpp @@ -0,0 +1,42 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "gtest/gtest.h" +#include "ProxyUtils.h" + +using namespace mozilla::toolkit::system; + +TEST(OSXProxy, TestProxyBypassRules) +{ + EXPECT_TRUE(IsHostProxyEntry(NS_LITERAL_CSTRING("mozilla.org"), NS_LITERAL_CSTRING("mozilla.org"))); + EXPECT_TRUE(IsHostProxyEntry(NS_LITERAL_CSTRING("mozilla.org"),NS_LITERAL_CSTRING("*mozilla.org"))); + EXPECT_TRUE(IsHostProxyEntry(NS_LITERAL_CSTRING("mozilla.org"), NS_LITERAL_CSTRING("*.mozilla.org"))); + EXPECT_FALSE(IsHostProxyEntry(NS_LITERAL_CSTRING("notmozilla.org"), NS_LITERAL_CSTRING("*.mozilla.org"))); + EXPECT_TRUE(IsHostProxyEntry(NS_LITERAL_CSTRING("www.mozilla.org"), NS_LITERAL_CSTRING("*mozilla.org"))); + EXPECT_TRUE(IsHostProxyEntry(NS_LITERAL_CSTRING("www.mozilla.org"), NS_LITERAL_CSTRING("*.mozilla.org"))); + EXPECT_TRUE(IsHostProxyEntry(NS_LITERAL_CSTRING("www.mozilla.com"), NS_LITERAL_CSTRING("*.mozilla.*"))); +} + +TEST(OSXProxy, TestProxyBypassRulesIPv4) +{ + EXPECT_TRUE(IsHostProxyEntry(NS_LITERAL_CSTRING("192.168.1.1"), NS_LITERAL_CSTRING("192.168.1.*"))); + EXPECT_FALSE(IsHostProxyEntry(NS_LITERAL_CSTRING("192.168.1.1"), NS_LITERAL_CSTRING("192.168.2.*"))); + + EXPECT_TRUE(IsHostProxyEntry(NS_LITERAL_CSTRING("10.1.2.3"), NS_LITERAL_CSTRING("10.0.0.0/8"))); + EXPECT_TRUE(IsHostProxyEntry(NS_LITERAL_CSTRING("192.168.192.1"), NS_LITERAL_CSTRING("192.168/16"))); + EXPECT_FALSE(IsHostProxyEntry(NS_LITERAL_CSTRING("192.168.192.1"), NS_LITERAL_CSTRING("192.168/17"))); + EXPECT_TRUE(IsHostProxyEntry(NS_LITERAL_CSTRING("192.168.192.1"), NS_LITERAL_CSTRING("192.168.128/17"))); + EXPECT_TRUE(IsHostProxyEntry(NS_LITERAL_CSTRING("192.168.1.1"), NS_LITERAL_CSTRING("192.168.1.1/32"))); +} + +TEST(OSXProxy, TestProxyBypassRulesIPv6) +{ + EXPECT_TRUE(IsHostProxyEntry(NS_LITERAL_CSTRING("2001:0DB8:ABCD:0012:0123:4567:89AB:CDEF"), NS_LITERAL_CSTRING("2001:db8:abcd:0012::0/64"))); + EXPECT_TRUE(IsHostProxyEntry(NS_LITERAL_CSTRING("2001:0DB8:ABCD:0012:0000:4567:89AB:CDEF"), NS_LITERAL_CSTRING("2001:db8:abcd:0012::0/80"))); + EXPECT_FALSE(IsHostProxyEntry(NS_LITERAL_CSTRING("2001:0DB8:ABCD:0012:0123:4567:89AB:CDEF"), NS_LITERAL_CSTRING("2001:db8:abcd:0012::0/80"))); + EXPECT_TRUE(IsHostProxyEntry(NS_LITERAL_CSTRING("2001:0DB8:ABCD:0012:0000:0000:89AB:CDEF"), NS_LITERAL_CSTRING("2001:db8:abcd:0012::0/96"))); + EXPECT_FALSE(IsHostProxyEntry(NS_LITERAL_CSTRING("2001:0DB8:ABCD:0012:0123:4567:89AB:CDEF"), NS_LITERAL_CSTRING("2001:db8:abcd:0012::0/96"))); +} diff --git a/toolkit/system/osxproxy/tests/gtest/moz.build b/toolkit/system/osxproxy/tests/gtest/moz.build new file mode 100644 index 000000000..c36b3e9bc --- /dev/null +++ b/toolkit/system/osxproxy/tests/gtest/moz.build @@ -0,0 +1,18 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, you can obtain one at http://mozilla.org/MPL/2.0/. + +UNIFIED_SOURCES += [ + 'TestProxyBypassRules.cpp', +] + +LOCAL_INCLUDES += [ + '/toolkit/system/osxproxy', +] + +FINAL_LIBRARY = 'xul-gtest' + +if CONFIG['GNU_CXX']: + CXXFLAGS += ['-Wshadow'] diff --git a/toolkit/system/unixproxy/moz.build b/toolkit/system/unixproxy/moz.build new file mode 100644 index 000000000..e3be2532e --- /dev/null +++ b/toolkit/system/unixproxy/moz.build @@ -0,0 +1,17 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +if CONFIG['MOZ_ENABLE_LIBPROXY']: + CXXFLAGS += CONFIG['MOZ_LIBPROXY_CFLAGS'] + SOURCES += [ + 'nsLibProxySettings.cpp', + ] +else: + SOURCES += [ + 'nsUnixSystemProxySettings.cpp', + ] + +FINAL_LIBRARY = 'xul' diff --git a/toolkit/system/unixproxy/nsLibProxySettings.cpp b/toolkit/system/unixproxy/nsLibProxySettings.cpp new file mode 100644 index 000000000..e9179c114 --- /dev/null +++ b/toolkit/system/unixproxy/nsLibProxySettings.cpp @@ -0,0 +1,141 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nsISystemProxySettings.h" +#include "mozilla/ModuleUtils.h" +#include "nsIServiceManager.h" +#include "nsIURI.h" +#include "nsString.h" +#include "nsCOMPtr.h" +#include "nsNetCID.h" +#include "nspr.h" + +extern "C" { +#include <proxy.h> +} + +class nsUnixSystemProxySettings : public nsISystemProxySettings { +public: + NS_DECL_THREADSAFE_ISUPPORTS + NS_DECL_NSISYSTEMPROXYSETTINGS + + nsUnixSystemProxySettings() { mProxyFactory = nullptr; } + nsresult Init(); + +private: + ~nsUnixSystemProxySettings() { + if (mProxyFactory) + px_proxy_factory_free(mProxyFactory); + } + + pxProxyFactory *mProxyFactory; +}; + +NS_IMPL_ISUPPORTS(nsUnixSystemProxySettings, nsISystemProxySettings) + +NS_IMETHODIMP +nsUnixSystemProxySettings::GetMainThreadOnly(bool *aMainThreadOnly) +{ + *aMainThreadOnly = false; + return NS_OK; +} + +nsresult +nsUnixSystemProxySettings::Init() +{ + return NS_OK; +} + +nsresult +nsUnixSystemProxySettings::GetPACURI(nsACString& aResult) +{ + // Make sure we return an empty result. + aResult.Truncate(); + return NS_OK; +} + +nsresult +nsUnixSystemProxySettings::GetProxyForURI(const nsACString & aSpec, + const nsACString & aScheme, + const nsACString & aHost, + const int32_t aPort, + nsACString & aResult) +{ + nsresult rv; + + if (!mProxyFactory) { + mProxyFactory = px_proxy_factory_new(); + } + NS_ENSURE_TRUE(mProxyFactory, NS_ERROR_NOT_AVAILABLE); + + char **proxyArray = nullptr; + proxyArray = px_proxy_factory_get_proxies(mProxyFactory, + PromiseFlatCString(aSpec).get()); + NS_ENSURE_TRUE(proxyArray, NS_ERROR_NOT_AVAILABLE); + + // Translate libproxy's output to PAC string as expected + // libproxy returns an array of proxies in the format: + // <procotol>://[username:password@]proxy:port + // or + // direct:// + // + // PAC format: "PROXY proxy1.foo.com:8080; PROXY proxy2.foo.com:8080; DIRECT" + // but nsISystemProxySettings allows "PROXY http://proxy.foo.com:8080" as well. + + int c = 0; + while (proxyArray[c] != nullptr) { + if (!aResult.IsEmpty()) { + aResult.AppendLiteral("; "); + } + + // figure out the scheme, and we can't use nsIIOService::NewURI because + // this is not the main thread. + char *colon = strchr (proxyArray[c], ':'); + uint32_t schemelen = colon ? colon - proxyArray[c] : 0; + if (schemelen < 1) { + c++; + continue; + } + + if (schemelen == 6 && !strncasecmp(proxyArray[c], "direct", 6)) { + aResult.AppendLiteral("DIRECT"); + } + else { + aResult.AppendLiteral("PROXY "); + aResult.Append(proxyArray[c]); + } + + c++; + } + + PR_Free(proxyArray); + return NS_OK; +} + +#define NS_UNIXSYSTEMPROXYSERVICE_CID /* 0fa3158c-d5a7-43de-9181-a285e74cf1d4 */\ + { 0x0fa3158c, 0xd5a7, 0x43de, \ + {0x91, 0x81, 0xa2, 0x85, 0xe7, 0x4c, 0xf1, 0xd4 } } + +NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsUnixSystemProxySettings, Init) +NS_DEFINE_NAMED_CID(NS_UNIXSYSTEMPROXYSERVICE_CID); + +static const mozilla::Module::CIDEntry kUnixProxyCIDs[] = { + { &kNS_UNIXSYSTEMPROXYSERVICE_CID, false, nullptr, nsUnixSystemProxySettingsConstructor }, + { nullptr } +}; + +static const mozilla::Module::ContractIDEntry kUnixProxyContracts[] = { + { NS_SYSTEMPROXYSETTINGS_CONTRACTID, &kNS_UNIXSYSTEMPROXYSERVICE_CID }, + { nullptr } +}; + +static const mozilla::Module kUnixProxyModule = { + mozilla::Module::kVersion, + kUnixProxyCIDs, + kUnixProxyContracts +}; + +NSMODULE_DEFN(nsUnixProxyModule) = &kUnixProxyModule; + diff --git a/toolkit/system/unixproxy/nsUnixSystemProxySettings.cpp b/toolkit/system/unixproxy/nsUnixSystemProxySettings.cpp new file mode 100644 index 000000000..c190da91d --- /dev/null +++ b/toolkit/system/unixproxy/nsUnixSystemProxySettings.cpp @@ -0,0 +1,543 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nsISystemProxySettings.h" +#include "mozilla/ModuleUtils.h" +#include "nsIServiceManager.h" +#include "nsIGConfService.h" +#include "nsIURI.h" +#include "nsReadableUtils.h" +#include "nsArrayUtils.h" +#include "prnetdb.h" +#include "prenv.h" +#include "nsPrintfCString.h" +#include "nsNetCID.h" +#include "nsNetUtil.h" +#include "nsISupportsPrimitives.h" +#include "nsIGSettingsService.h" +#include "nsInterfaceHashtable.h" +#include "mozilla/Attributes.h" +#include "nsIURI.h" + +class nsUnixSystemProxySettings final : public nsISystemProxySettings { +public: + NS_DECL_ISUPPORTS + NS_DECL_NSISYSTEMPROXYSETTINGS + + nsUnixSystemProxySettings() + : mSchemeProxySettings(4) + { + } + nsresult Init(); + +private: + ~nsUnixSystemProxySettings() {} + + nsCOMPtr<nsIGConfService> mGConf; + nsCOMPtr<nsIGSettingsService> mGSettings; + nsCOMPtr<nsIGSettingsCollection> mProxySettings; + nsInterfaceHashtable<nsCStringHashKey, nsIGSettingsCollection> mSchemeProxySettings; + bool IsProxyMode(const char* aMode); + nsresult SetProxyResultFromGConf(const char* aKeyBase, const char* aType, nsACString& aResult); + nsresult GetProxyFromGConf(const nsACString& aScheme, const nsACString& aHost, int32_t aPort, nsACString& aResult); + nsresult GetProxyFromGSettings(const nsACString& aScheme, const nsACString& aHost, int32_t aPort, nsACString& aResult); + nsresult SetProxyResultFromGSettings(const char* aKeyBase, const char* aType, nsACString& aResult); +}; + +NS_IMPL_ISUPPORTS(nsUnixSystemProxySettings, nsISystemProxySettings) + +NS_IMETHODIMP +nsUnixSystemProxySettings::GetMainThreadOnly(bool *aMainThreadOnly) +{ + // dbus prevents us from being threadsafe, but this routine should not block anyhow + *aMainThreadOnly = true; + return NS_OK; +} + +nsresult +nsUnixSystemProxySettings::Init() +{ + mGSettings = do_GetService(NS_GSETTINGSSERVICE_CONTRACTID); + if (mGSettings) { + mGSettings->GetCollectionForSchema(NS_LITERAL_CSTRING("org.gnome.system.proxy"), + getter_AddRefs(mProxySettings)); + } + if (!mProxySettings) { + mGConf = do_GetService(NS_GCONFSERVICE_CONTRACTID); + } + + return NS_OK; +} + +bool +nsUnixSystemProxySettings::IsProxyMode(const char* aMode) +{ + nsAutoCString mode; + return NS_SUCCEEDED(mGConf->GetString(NS_LITERAL_CSTRING("/system/proxy/mode"), mode)) && + mode.EqualsASCII(aMode); +} + +nsresult +nsUnixSystemProxySettings::GetPACURI(nsACString& aResult) +{ + if (mProxySettings) { + nsCString proxyMode; + // Check if mode is auto + nsresult rv = mProxySettings->GetString(NS_LITERAL_CSTRING("mode"), proxyMode); + if (rv == NS_OK && proxyMode.EqualsLiteral("auto")) { + return mProxySettings->GetString(NS_LITERAL_CSTRING("autoconfig-url"), aResult); + } + /* The org.gnome.system.proxy schema has been found, but auto mode is not set. + * Don't try the GConf and return empty string. */ + aResult.Truncate(); + return NS_OK; + } + + if (mGConf && IsProxyMode("auto")) { + return mGConf->GetString(NS_LITERAL_CSTRING("/system/proxy/autoconfig_url"), + aResult); + } + // Return an empty string when auto mode is not set. + aResult.Truncate(); + return NS_OK; +} + +static bool +IsInNoProxyList(const nsACString& aHost, int32_t aPort, const char* noProxyVal) +{ + NS_ASSERTION(aPort >= 0, "Negative port?"); + + nsAutoCString noProxy(noProxyVal); + if (noProxy.EqualsLiteral("*")) + return true; + + noProxy.StripWhitespace(); + + nsReadingIterator<char> pos; + nsReadingIterator<char> end; + noProxy.BeginReading(pos); + noProxy.EndReading(end); + while (pos != end) { + nsReadingIterator<char> last = pos; + nsReadingIterator<char> nextPos; + if (FindCharInReadable(',', last, end)) { + nextPos = last; + ++nextPos; + } else { + last = end; + nextPos = end; + } + + nsReadingIterator<char> colon = pos; + int32_t port = -1; + if (FindCharInReadable(':', colon, last)) { + ++colon; + nsDependentCSubstring portStr(colon, last); + nsAutoCString portStr2(portStr); // We need this for ToInteger. String API's suck. + nsresult err; + port = portStr2.ToInteger(&err); + if (NS_FAILED(err)) { + port = -2; // don't match any port, so we ignore this pattern + } + --colon; + } else { + colon = last; + } + + if (port == -1 || port == aPort) { + nsDependentCSubstring hostStr(pos, colon); + // By using StringEndsWith instead of an equality comparator, we can include sub-domains + if (StringEndsWith(aHost, hostStr, nsCaseInsensitiveCStringComparator())) + return true; + } + + pos = nextPos; + } + + return false; +} + +static void SetProxyResult(const char* aType, const nsACString& aHost, + int32_t aPort, nsACString& aResult) +{ + aResult.AppendASCII(aType); + aResult.Append(' '); + aResult.Append(aHost); + if (aPort > 0) { + aResult.Append(':'); + aResult.Append(nsPrintfCString("%d", aPort)); + } +} + +static nsresult +GetProxyFromEnvironment(const nsACString& aScheme, + const nsACString& aHost, + int32_t aPort, + nsACString& aResult) +{ + nsAutoCString envVar; + envVar.Append(aScheme); + envVar.AppendLiteral("_proxy"); + const char* proxyVal = PR_GetEnv(envVar.get()); + if (!proxyVal) { + proxyVal = PR_GetEnv("all_proxy"); + if (!proxyVal) { + // Return failure so that the caller can detect the failure and + // fall back to other proxy detection (e.g., WPAD) + return NS_ERROR_FAILURE; + } + } + + const char* noProxyVal = PR_GetEnv("no_proxy"); + if (noProxyVal && IsInNoProxyList(aHost, aPort, noProxyVal)) { + aResult.AppendLiteral("DIRECT"); + return NS_OK; + } + + // Use our URI parser to crack the proxy URI + nsCOMPtr<nsIURI> proxyURI; + nsresult rv = NS_NewURI(getter_AddRefs(proxyURI), proxyVal); + NS_ENSURE_SUCCESS(rv, rv); + + // Is there a way to specify "socks://" or something in these environment + // variables? I can't find any documentation. + bool isHTTP; + rv = proxyURI->SchemeIs("http", &isHTTP); + NS_ENSURE_SUCCESS(rv, rv); + if (!isHTTP) + return NS_ERROR_UNKNOWN_PROTOCOL; + + nsAutoCString proxyHost; + rv = proxyURI->GetHost(proxyHost); + NS_ENSURE_SUCCESS(rv, rv); + + int32_t proxyPort; + rv = proxyURI->GetPort(&proxyPort); + NS_ENSURE_SUCCESS(rv, rv); + + SetProxyResult("PROXY", proxyHost, proxyPort, aResult); + return NS_OK; +} + +nsresult +nsUnixSystemProxySettings::SetProxyResultFromGConf(const char* aKeyBase, const char* aType, + nsACString& aResult) +{ + nsAutoCString hostKey; + hostKey.AppendASCII(aKeyBase); + hostKey.AppendLiteral("host"); + nsAutoCString host; + nsresult rv = mGConf->GetString(hostKey, host); + NS_ENSURE_SUCCESS(rv, rv); + if (host.IsEmpty()) + return NS_ERROR_FAILURE; + + nsAutoCString portKey; + portKey.AppendASCII(aKeyBase); + portKey.AppendLiteral("port"); + int32_t port; + rv = mGConf->GetInt(portKey, &port); + NS_ENSURE_SUCCESS(rv, rv); + + /* When port is 0, proxy is not considered as enabled even if host is set. */ + if (port == 0) + return NS_ERROR_FAILURE; + + SetProxyResult(aType, host, port, aResult); + return NS_OK; +} + +nsresult +nsUnixSystemProxySettings::SetProxyResultFromGSettings(const char* aKeyBase, const char* aType, + nsACString& aResult) +{ + nsDependentCString key(aKeyBase); + + nsCOMPtr<nsIGSettingsCollection> proxy_settings = mSchemeProxySettings.Get(key); + nsresult rv; + if (!proxy_settings) { + rv = mGSettings->GetCollectionForSchema(key, getter_AddRefs(proxy_settings)); + NS_ENSURE_SUCCESS(rv, rv); + + mSchemeProxySettings.Put(key, proxy_settings); + } + + nsAutoCString host; + rv = proxy_settings->GetString(NS_LITERAL_CSTRING("host"), host); + NS_ENSURE_SUCCESS(rv, rv); + if (host.IsEmpty()) + return NS_ERROR_FAILURE; + + int32_t port; + rv = proxy_settings->GetInt(NS_LITERAL_CSTRING("port"), &port); + NS_ENSURE_SUCCESS(rv, rv); + + /* When port is 0, proxy is not considered as enabled even if host is set. */ + if (port == 0) + return NS_ERROR_FAILURE; + + SetProxyResult(aType, host, port, aResult); + return NS_OK; +} + +/* copied from nsProtocolProxyService.cpp --- we should share this! */ +static void +proxy_MaskIPv6Addr(PRIPv6Addr &addr, uint16_t mask_len) +{ + if (mask_len == 128) + return; + + if (mask_len > 96) { + addr.pr_s6_addr32[3] = PR_htonl( + PR_ntohl(addr.pr_s6_addr32[3]) & (~0L << (128 - mask_len))); + } + else if (mask_len > 64) { + addr.pr_s6_addr32[3] = 0; + addr.pr_s6_addr32[2] = PR_htonl( + PR_ntohl(addr.pr_s6_addr32[2]) & (~0L << (96 - mask_len))); + } + else if (mask_len > 32) { + addr.pr_s6_addr32[3] = 0; + addr.pr_s6_addr32[2] = 0; + addr.pr_s6_addr32[1] = PR_htonl( + PR_ntohl(addr.pr_s6_addr32[1]) & (~0L << (64 - mask_len))); + } + else { + addr.pr_s6_addr32[3] = 0; + addr.pr_s6_addr32[2] = 0; + addr.pr_s6_addr32[1] = 0; + addr.pr_s6_addr32[0] = PR_htonl( + PR_ntohl(addr.pr_s6_addr32[0]) & (~0L << (32 - mask_len))); + } +} + +static bool ConvertToIPV6Addr(const nsACString& aName, + PRIPv6Addr* aAddr, int32_t* aMask) +{ + PRNetAddr addr; + // try to convert hostname to IP + if (PR_StringToNetAddr(PromiseFlatCString(aName).get(), &addr) != PR_SUCCESS) + return false; + + // convert parsed address to IPv6 + if (addr.raw.family == PR_AF_INET) { + // convert to IPv4-mapped address + PR_ConvertIPv4AddrToIPv6(addr.inet.ip, aAddr); + if (aMask) { + if (*aMask <= 32) + *aMask += 96; + else + return false; + } + } else if (addr.raw.family == PR_AF_INET6) { + // copy the address + memcpy(aAddr, &addr.ipv6.ip, sizeof(PRIPv6Addr)); + } else { + return false; + } + + return true; +} + +static bool HostIgnoredByProxy(const nsACString& aIgnore, + const nsACString& aHost) +{ + if (aIgnore.Equals(aHost, nsCaseInsensitiveCStringComparator())) + return true; + + if (aIgnore.First() == '*' && + StringEndsWith(aHost, nsDependentCSubstring(aIgnore, 1), + nsCaseInsensitiveCStringComparator())) + return true; + + int32_t mask = 128; + nsReadingIterator<char> start; + nsReadingIterator<char> slash; + nsReadingIterator<char> end; + aIgnore.BeginReading(start); + aIgnore.BeginReading(slash); + aIgnore.EndReading(end); + if (FindCharInReadable('/', slash, end)) { + ++slash; + nsDependentCSubstring maskStr(slash, end); + nsAutoCString maskStr2(maskStr); + nsresult err; + mask = maskStr2.ToInteger(&err); + if (NS_FAILED(err)) { + mask = 128; + } + --slash; + } else { + slash = end; + } + + nsDependentCSubstring ignoreStripped(start, slash); + PRIPv6Addr ignoreAddr, hostAddr; + if (!ConvertToIPV6Addr(ignoreStripped, &ignoreAddr, &mask) || + !ConvertToIPV6Addr(aHost, &hostAddr, nullptr)) + return false; + + proxy_MaskIPv6Addr(ignoreAddr, mask); + proxy_MaskIPv6Addr(hostAddr, mask); + + return memcmp(&ignoreAddr, &hostAddr, sizeof(PRIPv6Addr)) == 0; +} + +nsresult +nsUnixSystemProxySettings::GetProxyFromGConf(const nsACString& aScheme, + const nsACString& aHost, + int32_t aPort, + nsACString& aResult) +{ + bool masterProxySwitch = false; + mGConf->GetBool(NS_LITERAL_CSTRING("/system/http_proxy/use_http_proxy"), &masterProxySwitch); + // if no proxy is set in GConf return NS_ERROR_FAILURE + if (!(IsProxyMode("manual") || masterProxySwitch)) { + return NS_ERROR_FAILURE; + } + + nsCOMPtr<nsIArray> ignoreList; + if (NS_SUCCEEDED(mGConf->GetStringList(NS_LITERAL_CSTRING("/system/http_proxy/ignore_hosts"), + getter_AddRefs(ignoreList))) && ignoreList) { + uint32_t len = 0; + ignoreList->GetLength(&len); + for (uint32_t i = 0; i < len; ++i) { + nsCOMPtr<nsISupportsString> str = do_QueryElementAt(ignoreList, i); + if (str) { + nsAutoString s; + if (NS_SUCCEEDED(str->GetData(s)) && !s.IsEmpty()) { + if (HostIgnoredByProxy(NS_ConvertUTF16toUTF8(s), aHost)) { + aResult.AppendLiteral("DIRECT"); + return NS_OK; + } + } + } + } + } + + bool useHttpProxyForAll = false; + // This setting sometimes doesn't exist, don't bail on failure + mGConf->GetBool(NS_LITERAL_CSTRING("/system/http_proxy/use_same_proxy"), &useHttpProxyForAll); + + nsresult rv; + if (!useHttpProxyForAll) { + rv = SetProxyResultFromGConf("/system/proxy/socks_", "SOCKS", aResult); + if (NS_SUCCEEDED(rv)) + return rv; + } + + if (aScheme.LowerCaseEqualsLiteral("http") || useHttpProxyForAll) { + rv = SetProxyResultFromGConf("/system/http_proxy/", "PROXY", aResult); + } else if (aScheme.LowerCaseEqualsLiteral("https")) { + rv = SetProxyResultFromGConf("/system/proxy/secure_", "PROXY", aResult); + } else if (aScheme.LowerCaseEqualsLiteral("ftp")) { + rv = SetProxyResultFromGConf("/system/proxy/ftp_", "PROXY", aResult); + } else { + rv = NS_ERROR_FAILURE; + } + + return rv; +} + +nsresult +nsUnixSystemProxySettings::GetProxyFromGSettings(const nsACString& aScheme, + const nsACString& aHost, + int32_t aPort, + nsACString& aResult) +{ + nsCString proxyMode; + nsresult rv = mProxySettings->GetString(NS_LITERAL_CSTRING("mode"), proxyMode); + NS_ENSURE_SUCCESS(rv, rv); + + // return NS_ERROR_FAILURE when no proxy is set + if (!proxyMode.EqualsLiteral("manual")) { + return NS_ERROR_FAILURE; + } + + nsCOMPtr<nsIArray> ignoreList; + if (NS_SUCCEEDED(mProxySettings->GetStringList(NS_LITERAL_CSTRING("ignore-hosts"), + getter_AddRefs(ignoreList))) && ignoreList) { + uint32_t len = 0; + ignoreList->GetLength(&len); + for (uint32_t i = 0; i < len; ++i) { + nsCOMPtr<nsISupportsCString> str = do_QueryElementAt(ignoreList, i); + if (str) { + nsCString s; + if (NS_SUCCEEDED(str->GetData(s)) && !s.IsEmpty()) { + if (HostIgnoredByProxy(s, aHost)) { + aResult.AppendLiteral("DIRECT"); + return NS_OK; + } + } + } + } + } + + if (aScheme.LowerCaseEqualsLiteral("http")) { + rv = SetProxyResultFromGSettings("org.gnome.system.proxy.http", "PROXY", aResult); + } else if (aScheme.LowerCaseEqualsLiteral("https")) { + rv = SetProxyResultFromGSettings("org.gnome.system.proxy.https", "PROXY", aResult); + /* Try to use HTTP proxy when HTTPS proxy is not explicitly defined */ + if (rv != NS_OK) + rv = SetProxyResultFromGSettings("org.gnome.system.proxy.http", "PROXY", aResult); + } else if (aScheme.LowerCaseEqualsLiteral("ftp")) { + rv = SetProxyResultFromGSettings("org.gnome.system.proxy.ftp", "PROXY", aResult); + } else { + rv = NS_ERROR_FAILURE; + } + if (rv != NS_OK) { + /* If proxy for scheme is not specified, use SOCKS proxy for all schemes */ + rv = SetProxyResultFromGSettings("org.gnome.system.proxy.socks", "SOCKS", aResult); + } + + if (NS_FAILED(rv)) { + aResult.AppendLiteral("DIRECT"); + } + + return NS_OK; +} + +nsresult +nsUnixSystemProxySettings::GetProxyForURI(const nsACString & aSpec, + const nsACString & aScheme, + const nsACString & aHost, + const int32_t aPort, + nsACString & aResult) +{ + if (mProxySettings) { + nsresult rv = GetProxyFromGSettings(aScheme, aHost, aPort, aResult); + if (NS_SUCCEEDED(rv)) + return rv; + } + if (mGConf) + return GetProxyFromGConf(aScheme, aHost, aPort, aResult); + + return GetProxyFromEnvironment(aScheme, aHost, aPort, aResult); +} + +#define NS_UNIXSYSTEMPROXYSERVICE_CID /* 0fa3158c-d5a7-43de-9181-a285e74cf1d4 */\ + { 0x0fa3158c, 0xd5a7, 0x43de, \ + {0x91, 0x81, 0xa2, 0x85, 0xe7, 0x4c, 0xf1, 0xd4 } } + +NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsUnixSystemProxySettings, Init) +NS_DEFINE_NAMED_CID(NS_UNIXSYSTEMPROXYSERVICE_CID); + +static const mozilla::Module::CIDEntry kUnixProxyCIDs[] = { + { &kNS_UNIXSYSTEMPROXYSERVICE_CID, false, nullptr, nsUnixSystemProxySettingsConstructor }, + { nullptr } +}; + +static const mozilla::Module::ContractIDEntry kUnixProxyContracts[] = { + { NS_SYSTEMPROXYSETTINGS_CONTRACTID, &kNS_UNIXSYSTEMPROXYSERVICE_CID }, + { nullptr } +}; + +static const mozilla::Module kUnixProxyModule = { + mozilla::Module::kVersion, + kUnixProxyCIDs, + kUnixProxyContracts +}; + +NSMODULE_DEFN(nsUnixProxyModule) = &kUnixProxyModule; diff --git a/toolkit/system/windowsproxy/ProxyUtils.cpp b/toolkit/system/windowsproxy/ProxyUtils.cpp new file mode 100644 index 000000000..4e59f226a --- /dev/null +++ b/toolkit/system/windowsproxy/ProxyUtils.cpp @@ -0,0 +1,182 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "ProxyUtils.h" +#include "nsTArray.h" +#include "prnetdb.h" +#include "prtypes.h" + +namespace mozilla { +namespace toolkit { +namespace system { + +/** + * Normalize the short IP form into the complete form. + * For example, it converts "192.168" into "192.168.0.0" + */ +static bool +NormalizeAddr(const nsACString& aAddr, nsCString& aNormalized) +{ + nsTArray<nsCString> addr; + if (!ParseString(aAddr, '.', addr)) { + return false; + } + aNormalized = ""; + for (uint32_t i = 0; i < 4; ++i) { + if (i != 0) { + aNormalized.Append("."); + } + if (i < addr.Length()) { + aNormalized.Append(addr[i]); + } else { + aNormalized.Append("0"); + } + } + return true; +} + +static PRUint32 +MaskIPv4Addr(PRUint32 aAddr, uint16_t aMaskLen) +{ + if (aMaskLen == 32) { + return aAddr; + } + return PR_htonl(PR_ntohl(aAddr) & (~0L << (32 - aMaskLen))); +} + +static void +MaskIPv6Addr(PRIPv6Addr& aAddr, uint16_t aMaskLen) +{ + if (aMaskLen == 128) { + return; + } + + if (aMaskLen > 96) { + aAddr.pr_s6_addr32[3] = PR_htonl( + PR_ntohl(aAddr.pr_s6_addr32[3]) & (~0L << (128 - aMaskLen))); + } else if (aMaskLen > 64) { + aAddr.pr_s6_addr32[3] = 0; + aAddr.pr_s6_addr32[2] = PR_htonl( + PR_ntohl(aAddr.pr_s6_addr32[2]) & (~0L << (96 - aMaskLen))); + } else if (aMaskLen > 32) { + aAddr.pr_s6_addr32[3] = 0; + aAddr.pr_s6_addr32[2] = 0; + aAddr.pr_s6_addr32[1] = PR_htonl( + PR_ntohl(aAddr.pr_s6_addr32[1]) & (~0L << (64 - aMaskLen))); + } else { + aAddr.pr_s6_addr32[3] = 0; + aAddr.pr_s6_addr32[2] = 0; + aAddr.pr_s6_addr32[1] = 0; + aAddr.pr_s6_addr32[0] = PR_htonl( + PR_ntohl(aAddr.pr_s6_addr32[0]) & (~0L << (32 - aMaskLen))); + } + + return; +} + +static bool +IsMatchMask(const nsACString& aHost, const nsACString& aOverride) +{ + nsresult rv; + + auto tokenEnd = aOverride.FindChar('/'); + if (tokenEnd == -1) { + return false; + } + + nsAutoCString prefixStr(Substring(aOverride, + tokenEnd + 1, + aOverride.Length() - tokenEnd - 1)); + auto maskLen = prefixStr.ToInteger(&rv); + if (NS_WARN_IF(NS_FAILED(rv))) { + return false; + } + + nsAutoCString override(aOverride); + if (!NormalizeAddr(Substring(aOverride, 0, tokenEnd), override)) { + return false; + } + + PRNetAddr prAddrHost; + PRNetAddr prAddrOverride; + if (PR_SUCCESS != PR_StringToNetAddr(PromiseFlatCString(aHost).get(), + &prAddrHost) || + PR_SUCCESS != PR_StringToNetAddr(override.get(), + &prAddrOverride)) { + return false; + } + + if (prAddrHost.raw.family == PR_AF_INET && + prAddrOverride.raw.family == PR_AF_INET) { + return MaskIPv4Addr(prAddrHost.inet.ip, maskLen) == + MaskIPv4Addr(prAddrOverride.inet.ip, maskLen); + } + else if (prAddrHost.raw.family == PR_AF_INET6 && + prAddrOverride.raw.family == PR_AF_INET6) { + MaskIPv6Addr(prAddrHost.ipv6.ip, maskLen); + MaskIPv6Addr(prAddrOverride.ipv6.ip, maskLen); + + return memcmp(&prAddrHost.ipv6.ip, + &prAddrOverride.ipv6.ip, + sizeof(PRIPv6Addr)) == 0; + } + + return false; +} + +static bool +IsMatchWildcard(const nsACString& aHost, const nsACString& aOverride) +{ + nsAutoCString host(aHost); + nsAutoCString override(aOverride); + + int32_t overrideLength = override.Length(); + int32_t tokenStart = 0; + int32_t offset = 0; + bool star = false; + + while (tokenStart < overrideLength) { + int32_t tokenEnd = override.FindChar('*', tokenStart); + if (tokenEnd == tokenStart) { + // Star is the first character in the token. + star = true; + tokenStart++; + // If the character following the '*' is a '.' character then skip + // it so that "*.foo.com" allows "foo.com". + if (override.FindChar('.', tokenStart) == tokenStart) { + nsAutoCString token(Substring(override, + tokenStart + 1, + overrideLength - tokenStart - 1)); + if (host.Equals(token)) { + return true; + } + } + } else { + if (tokenEnd == -1) { + tokenEnd = overrideLength; // no '*' char, match rest of string + } + nsAutoCString token(Substring(override, tokenStart, tokenEnd - tokenStart)); + offset = host.Find(token, offset); + if (offset == -1 || (!star && offset)) { + return false; + } + star = false; + tokenStart = tokenEnd; + offset += token.Length(); + } + } + + return (star || (offset == static_cast<int32_t>(host.Length()))); +} + +bool +IsHostProxyEntry(const nsACString& aHost, const nsACString& aOverride) +{ + return IsMatchMask(aHost, aOverride) || IsMatchWildcard(aHost, aOverride); +} + +} // namespace system +} // namespace toolkit +} // namespace mozilla diff --git a/toolkit/system/windowsproxy/ProxyUtils.h b/toolkit/system/windowsproxy/ProxyUtils.h new file mode 100644 index 000000000..7d6ae220f --- /dev/null +++ b/toolkit/system/windowsproxy/ProxyUtils.h @@ -0,0 +1,21 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_toolkit_system_windowsproxy_ProxyUtils_h +#define mozilla_toolkit_system_windowsproxy_ProxyUtils_h + +#include "nsStringGlue.h" + +namespace mozilla { +namespace toolkit { +namespace system { + +bool IsHostProxyEntry(const nsACString& aHost, const nsACString& aOverride); + +} // namespace system +} // namespace toolkit +} // namespace mozilla + +#endif // mozilla_toolkit_system_windowsproxy_ProxyUtils_h diff --git a/toolkit/system/windowsproxy/moz.build b/toolkit/system/windowsproxy/moz.build new file mode 100644 index 000000000..6a1dcc73c --- /dev/null +++ b/toolkit/system/windowsproxy/moz.build @@ -0,0 +1,14 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +TEST_DIRS += ['tests/gtest'] + +SOURCES += [ + 'nsWindowsSystemProxySettings.cpp', + 'ProxyUtils.cpp' +] + +FINAL_LIBRARY = 'xul' diff --git a/toolkit/system/windowsproxy/nsWindowsSystemProxySettings.cpp b/toolkit/system/windowsproxy/nsWindowsSystemProxySettings.cpp new file mode 100644 index 000000000..bb5f72b69 --- /dev/null +++ b/toolkit/system/windowsproxy/nsWindowsSystemProxySettings.cpp @@ -0,0 +1,304 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include <windows.h> +#include <ras.h> +#include <wininet.h> + +#include "mozilla/ArrayUtils.h" +#include "mozilla/Attributes.h" +#include "nsISystemProxySettings.h" +#include "nsIServiceManager.h" +#include "mozilla/ModuleUtils.h" +#include "nsPrintfCString.h" +#include "nsNetCID.h" +#include "nsISupportsPrimitives.h" +#include "nsIURI.h" +#include "GeckoProfiler.h" +#include "prnetdb.h" +#include "ProxyUtils.h" + +class nsWindowsSystemProxySettings final : public nsISystemProxySettings +{ +public: + NS_DECL_THREADSAFE_ISUPPORTS + NS_DECL_NSISYSTEMPROXYSETTINGS + + nsWindowsSystemProxySettings() {}; + nsresult Init(); + +private: + ~nsWindowsSystemProxySettings() {}; + + bool MatchOverride(const nsACString& aHost); + bool PatternMatch(const nsACString& aHost, const nsACString& aOverride); +}; + +NS_IMPL_ISUPPORTS(nsWindowsSystemProxySettings, nsISystemProxySettings) + +NS_IMETHODIMP +nsWindowsSystemProxySettings::GetMainThreadOnly(bool *aMainThreadOnly) +{ + *aMainThreadOnly = false; + return NS_OK; +} + + +nsresult +nsWindowsSystemProxySettings::Init() +{ + return NS_OK; +} + +static void SetProxyResult(const char* aType, const nsACString& aHostPort, + nsACString& aResult) +{ + aResult.AssignASCII(aType); + aResult.Append(' '); + aResult.Append(aHostPort); +} + +static void SetProxyResultDirect(nsACString& aResult) +{ + // For whatever reason, a proxy is not to be used. + aResult.AssignASCII("DIRECT"); +} + +static nsresult ReadInternetOption(uint32_t aOption, uint32_t& aFlags, + nsAString& aValue) +{ + DWORD connFlags = 0; + WCHAR connName[RAS_MaxEntryName + 1]; + MOZ_SEH_TRY { + InternetGetConnectedStateExW(&connFlags, connName, + mozilla::ArrayLength(connName), 0); + } MOZ_SEH_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { + return NS_ERROR_FAILURE; + } + + INTERNET_PER_CONN_OPTIONW options[2]; + options[0].dwOption = INTERNET_PER_CONN_FLAGS_UI; + options[1].dwOption = aOption; + + INTERNET_PER_CONN_OPTION_LISTW list; + list.dwSize = sizeof(INTERNET_PER_CONN_OPTION_LISTW); + list.pszConnection = connFlags & INTERNET_CONNECTION_MODEM ? + connName : nullptr; + list.dwOptionCount = mozilla::ArrayLength(options); + list.dwOptionError = 0; + list.pOptions = options; + + unsigned long size = sizeof(INTERNET_PER_CONN_OPTION_LISTW); + if (!InternetQueryOptionW(nullptr, INTERNET_OPTION_PER_CONNECTION_OPTION, + &list, &size)) { + if (GetLastError() != ERROR_INVALID_PARAMETER) { + return NS_ERROR_FAILURE; + } + options[0].dwOption = INTERNET_PER_CONN_FLAGS; + size = sizeof(INTERNET_PER_CONN_OPTION_LISTW); + MOZ_SEH_TRY { + if (!InternetQueryOptionW(nullptr, + INTERNET_OPTION_PER_CONNECTION_OPTION, + &list, &size)) { + return NS_ERROR_FAILURE; + } + } MOZ_SEH_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { + return NS_ERROR_FAILURE; + } + } + + aFlags = options[0].Value.dwValue; + aValue.Assign(options[1].Value.pszValue); + GlobalFree(options[1].Value.pszValue); + + return NS_OK; +} + +bool +nsWindowsSystemProxySettings::MatchOverride(const nsACString& aHost) +{ + nsresult rv; + uint32_t flags = 0; + nsAutoString buf; + + rv = ReadInternetOption(INTERNET_PER_CONN_PROXY_BYPASS, flags, buf); + if (NS_FAILED(rv)) + return false; + + NS_ConvertUTF16toUTF8 cbuf(buf); + + nsAutoCString host(aHost); + int32_t start = 0; + int32_t end = cbuf.Length(); + + // Windows formats its proxy override list in the form: + // server;server;server where 'server' is a server name pattern or IP + // address, or "<local>". "<local>" must be translated to + // "localhost;127.0.0.1". + // In a server name pattern, a '*' character matches any substring and + // all other characters must match themselves; the whole pattern must match + // the whole hostname. + while (true) { + int32_t delimiter = cbuf.FindCharInSet(" ;", start); + if (delimiter == -1) + delimiter = end; + + if (delimiter != start) { + const nsAutoCString override(Substring(cbuf, start, + delimiter - start)); + if (override.EqualsLiteral("<local>")) { + PRNetAddr addr; + bool isIpAddr = (PR_StringToNetAddr(host.get(), &addr) == PR_SUCCESS); + + // Don't use proxy for local hosts (plain hostname, no dots) + if (!isIpAddr && !host.Contains('.')) { + return true; + } + + if (host.EqualsLiteral("127.0.0.1") || + host.EqualsLiteral("::1")) { + return true; + } + } else if (PatternMatch(host, override)) { + return true; + } + } + + if (delimiter == end) + break; + start = ++delimiter; + } + + return false; +} + +bool +nsWindowsSystemProxySettings::PatternMatch(const nsACString& aHost, + const nsACString& aOverride) +{ + return mozilla::toolkit::system::IsHostProxyEntry(aHost, aOverride); +} + +nsresult +nsWindowsSystemProxySettings::GetPACURI(nsACString& aResult) +{ + PROFILER_LABEL_FUNC(js::ProfileEntry::Category::STORAGE); + nsresult rv; + uint32_t flags = 0; + nsAutoString buf; + + rv = ReadInternetOption(INTERNET_PER_CONN_AUTOCONFIG_URL, flags, buf); + if (!(flags & PROXY_TYPE_AUTO_PROXY_URL)) { + aResult.Truncate(); + return rv; + } + + if (NS_SUCCEEDED(rv)) + aResult = NS_ConvertUTF16toUTF8(buf); + return rv; +} + +nsresult +nsWindowsSystemProxySettings::GetProxyForURI(const nsACString & aSpec, + const nsACString & aScheme, + const nsACString & aHost, + const int32_t aPort, + nsACString & aResult) +{ + nsresult rv; + uint32_t flags = 0; + nsAutoString buf; + + rv = ReadInternetOption(INTERNET_PER_CONN_PROXY_SERVER, flags, buf); + if (NS_FAILED(rv) || !(flags & PROXY_TYPE_PROXY)) { + SetProxyResultDirect(aResult); + return NS_OK; + } + + if (MatchOverride(aHost)) { + SetProxyResultDirect(aResult); + return NS_OK; + } + + NS_ConvertUTF16toUTF8 cbuf(buf); + + nsAutoCString prefix; + ToLowerCase(aScheme, prefix); + + prefix.Append('='); + + nsAutoCString specificProxy; + nsAutoCString defaultProxy; + nsAutoCString socksProxy; + int32_t start = 0; + int32_t end = cbuf.Length(); + + while (true) { + int32_t delimiter = cbuf.FindCharInSet(" ;", start); + if (delimiter == -1) + delimiter = end; + + if (delimiter != start) { + const nsAutoCString proxy(Substring(cbuf, start, + delimiter - start)); + if (proxy.FindChar('=') == -1) { + // If a proxy name is listed by itself, it is used as the + // default proxy for any protocols that do not have a specific + // proxy specified. + // (http://msdn.microsoft.com/en-us/library/aa383996%28VS.85%29.aspx) + defaultProxy = proxy; + } else if (proxy.Find(prefix) == 0) { + // To list a proxy for a specific protocol, the string must + // follow the format "<protocol>=<protocol>://<proxy_name>". + // (http://msdn.microsoft.com/en-us/library/aa383996%28VS.85%29.aspx) + specificProxy = Substring(proxy, prefix.Length()); + break; + } else if (proxy.Find("socks=") == 0) { + // SOCKS proxy. + socksProxy = Substring(proxy, 5); // "socks=" length. + } + } + + if (delimiter == end) + break; + start = ++delimiter; + } + + if (!specificProxy.IsEmpty()) + SetProxyResult("PROXY", specificProxy, aResult); // Protocol-specific proxy. + else if (!defaultProxy.IsEmpty()) + SetProxyResult("PROXY", defaultProxy, aResult); // Default proxy. + else if (!socksProxy.IsEmpty()) + SetProxyResult("SOCKS", socksProxy, aResult); // SOCKS proxy. + else + SetProxyResultDirect(aResult); // Direct connection. + + return NS_OK; +} + +#define NS_WINDOWSSYSTEMPROXYSERVICE_CID /* 4e22d3ea-aaa2-436e-ada4-9247de57d367 */\ + { 0x4e22d3ea, 0xaaa2, 0x436e, \ + {0xad, 0xa4, 0x92, 0x47, 0xde, 0x57, 0xd3, 0x67 } } + +NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsWindowsSystemProxySettings, Init) +NS_DEFINE_NAMED_CID(NS_WINDOWSSYSTEMPROXYSERVICE_CID); + +static const mozilla::Module::CIDEntry kSysProxyCIDs[] = { + { &kNS_WINDOWSSYSTEMPROXYSERVICE_CID, false, nullptr, nsWindowsSystemProxySettingsConstructor }, + { nullptr } +}; + +static const mozilla::Module::ContractIDEntry kSysProxyContracts[] = { + { NS_SYSTEMPROXYSETTINGS_CONTRACTID, &kNS_WINDOWSSYSTEMPROXYSERVICE_CID }, + { nullptr } +}; + +static const mozilla::Module kSysProxyModule = { + mozilla::Module::kVersion, + kSysProxyCIDs, + kSysProxyContracts +}; + +NSMODULE_DEFN(nsWindowsProxyModule) = &kSysProxyModule; diff --git a/toolkit/system/windowsproxy/tests/gtest/TestProxyBypassRules.cpp b/toolkit/system/windowsproxy/tests/gtest/TestProxyBypassRules.cpp new file mode 100644 index 000000000..9cd6bf981 --- /dev/null +++ b/toolkit/system/windowsproxy/tests/gtest/TestProxyBypassRules.cpp @@ -0,0 +1,42 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "gtest/gtest.h" +#include "ProxyUtils.h" + +using namespace mozilla::toolkit::system; + +TEST(WindowsProxy, TestProxyBypassRules) +{ + EXPECT_TRUE(IsHostProxyEntry(NS_LITERAL_CSTRING("mozilla.org"), NS_LITERAL_CSTRING("mozilla.org"))); + EXPECT_TRUE(IsHostProxyEntry(NS_LITERAL_CSTRING("mozilla.org"),NS_LITERAL_CSTRING("*mozilla.org"))); + EXPECT_TRUE(IsHostProxyEntry(NS_LITERAL_CSTRING("mozilla.org"), NS_LITERAL_CSTRING("*.mozilla.org"))); + EXPECT_FALSE(IsHostProxyEntry(NS_LITERAL_CSTRING("notmozilla.org"), NS_LITERAL_CSTRING("*.mozilla.org"))); + EXPECT_TRUE(IsHostProxyEntry(NS_LITERAL_CSTRING("www.mozilla.org"), NS_LITERAL_CSTRING("*mozilla.org"))); + EXPECT_TRUE(IsHostProxyEntry(NS_LITERAL_CSTRING("www.mozilla.org"), NS_LITERAL_CSTRING("*.mozilla.org"))); + EXPECT_TRUE(IsHostProxyEntry(NS_LITERAL_CSTRING("www.mozilla.com"), NS_LITERAL_CSTRING("*.mozilla.*"))); +} + +TEST(WindowsProxy, TestProxyBypassRulesIPv4) +{ + EXPECT_TRUE(IsHostProxyEntry(NS_LITERAL_CSTRING("192.168.1.1"), NS_LITERAL_CSTRING("192.168.1.*"))); + EXPECT_FALSE(IsHostProxyEntry(NS_LITERAL_CSTRING("192.168.1.1"), NS_LITERAL_CSTRING("192.168.2.*"))); + + EXPECT_TRUE(IsHostProxyEntry(NS_LITERAL_CSTRING("10.1.2.3"), NS_LITERAL_CSTRING("10.0.0.0/8"))); + EXPECT_TRUE(IsHostProxyEntry(NS_LITERAL_CSTRING("192.168.192.1"), NS_LITERAL_CSTRING("192.168/16"))); + EXPECT_FALSE(IsHostProxyEntry(NS_LITERAL_CSTRING("192.168.192.1"), NS_LITERAL_CSTRING("192.168/17"))); + EXPECT_TRUE(IsHostProxyEntry(NS_LITERAL_CSTRING("192.168.192.1"), NS_LITERAL_CSTRING("192.168.128/17"))); + EXPECT_TRUE(IsHostProxyEntry(NS_LITERAL_CSTRING("192.168.1.1"), NS_LITERAL_CSTRING("192.168.1.1/32"))); +} + +TEST(WindowsProxy, TestProxyBypassRulesIPv6) +{ + EXPECT_TRUE(IsHostProxyEntry(NS_LITERAL_CSTRING("2001:0DB8:ABCD:0012:0123:4567:89AB:CDEF"), NS_LITERAL_CSTRING("2001:db8:abcd:0012::0/64"))); + EXPECT_TRUE(IsHostProxyEntry(NS_LITERAL_CSTRING("2001:0DB8:ABCD:0012:0000:4567:89AB:CDEF"), NS_LITERAL_CSTRING("2001:db8:abcd:0012::0/80"))); + EXPECT_FALSE(IsHostProxyEntry(NS_LITERAL_CSTRING("2001:0DB8:ABCD:0012:0123:4567:89AB:CDEF"), NS_LITERAL_CSTRING("2001:db8:abcd:0012::0/80"))); + EXPECT_TRUE(IsHostProxyEntry(NS_LITERAL_CSTRING("2001:0DB8:ABCD:0012:0000:0000:89AB:CDEF"), NS_LITERAL_CSTRING("2001:db8:abcd:0012::0/96"))); + EXPECT_FALSE(IsHostProxyEntry(NS_LITERAL_CSTRING("2001:0DB8:ABCD:0012:0123:4567:89AB:CDEF"), NS_LITERAL_CSTRING("2001:db8:abcd:0012::0/96"))); +} diff --git a/toolkit/system/windowsproxy/tests/gtest/moz.build b/toolkit/system/windowsproxy/tests/gtest/moz.build new file mode 100644 index 000000000..3ae4e73c2 --- /dev/null +++ b/toolkit/system/windowsproxy/tests/gtest/moz.build @@ -0,0 +1,18 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, you can obtain one at http://mozilla.org/MPL/2.0/. + +UNIFIED_SOURCES += [ + 'TestProxyBypassRules.cpp', +] + +LOCAL_INCLUDES += [ + '/toolkit/system/windowsproxy', +] + +FINAL_LIBRARY = 'xul-gtest' + +if CONFIG['GNU_CXX']: + CXXFLAGS += ['-Wshadow'] |