summaryrefslogtreecommitdiff
path: root/components/gservice/src/nsPackageKitService.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'components/gservice/src/nsPackageKitService.cpp')
-rw-r--r--components/gservice/src/nsPackageKitService.cpp267
1 files changed, 267 insertions, 0 deletions
diff --git a/components/gservice/src/nsPackageKitService.cpp b/components/gservice/src/nsPackageKitService.cpp
new file mode 100644
index 000000000..c32a91367
--- /dev/null
+++ b/components/gservice/src/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;
+}