Line data Source code
1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* This Source Code Form is subject to the terms of the Mozilla Public
3 : * License, v. 2.0. If a copy of the MPL was not distributed with this
4 : * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 :
6 : #include "nsArrayUtils.h"
7 : #include "nsIObserver.h"
8 : #include "nsIObserverService.h"
9 : #include "nsISupportsPrimitives.h"
10 : #include "nsPackageKitService.h"
11 : #include "nsString.h"
12 : #include "prlink.h"
13 : #include "mozilla/Unused.h"
14 : #include "mozilla/UniquePtr.h"
15 :
16 : #include <glib.h>
17 : #include <glib-object.h>
18 : #include <limits>
19 :
20 : using namespace mozilla;
21 :
22 : typedef struct _GAsyncResult GAsyncResult;
23 : typedef enum {
24 : G_BUS_TYPE_STARTER = -1,
25 : G_BUS_TYPE_NONE = 0,
26 : G_BUS_TYPE_SYSTEM = 1,
27 : G_BUS_TYPE_SESSION = 2
28 : } GBusType;
29 : typedef struct _GCancellable GCancellable;
30 : typedef enum {
31 : G_DBUS_CALL_FLAGS_NONE = 0,
32 : G_DBUS_CALL_FLAGS_NO_AUTO_START = (1<<0)
33 : } GDBusCallFlags;
34 : typedef struct _GDBusInterfaceInfo GDBusInterfaceInfo;
35 : typedef struct _GDBusProxy GDBusProxy;
36 : typedef enum {
37 : G_DBUS_PROXY_FLAGS_NONE = 0,
38 : G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES = (1<<0),
39 : G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS = (1<<1),
40 : G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START = (1<<2),
41 : G_DBUS_PROXY_FLAGS_GET_INVALIDATED_PROPERTIES = (1<<3)
42 : } GDBusProxyFlags;
43 : typedef struct _GVariant GVariant;
44 : typedef void (*GAsyncReadyCallback) (GObject *source_object,
45 : GAsyncResult *res,
46 : gpointer user_data);
47 :
48 : #define GDBUS_FUNCTIONS \
49 : 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)) \
50 : FUNC(g_dbus_proxy_call_finish, GVariant*, (GDBusProxy *proxy, GAsyncResult *res, GError **error)) \
51 : FUNC(g_dbus_proxy_new_finish, GDBusProxy*, (GAsyncResult *res, GError **error)) \
52 : 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)) \
53 : FUNC(g_variant_is_floating, gboolean, (GVariant *value)) \
54 : FUNC(g_variant_new, GVariant*, (const gchar *format_string, ...)) \
55 : FUNC(g_variant_unref, void, (GVariant* value))
56 :
57 : #define FUNC(name, type, params) \
58 : typedef type (*_##name##_fn) params; \
59 : static _##name##_fn _##name;
60 :
61 : GDBUS_FUNCTIONS
62 :
63 : #undef FUNC
64 :
65 : #define g_dbus_proxy_call _g_dbus_proxy_call
66 : #define g_dbus_proxy_call_finish _g_dbus_proxy_call_finish
67 : #define g_dbus_proxy_new_finish _g_dbus_proxy_new_finish
68 : #define g_dbus_proxy_new_for_bus _g_dbus_proxy_new_for_bus
69 : #define g_variant_is_floating _g_variant_is_floating
70 : #define g_variant_new _g_variant_new
71 : #define g_variant_unref _g_variant_unref
72 :
73 : static PRLibrary *gioLib = nullptr;
74 :
75 : typedef void (*nsGDBusFunc)();
76 : struct nsGDBusDynamicFunction {
77 : const char *functionName;
78 : nsGDBusFunc *function;
79 : };
80 :
81 : nsresult
82 0 : nsPackageKitService::Init()
83 : {
84 : #define FUNC(name, type, params) { #name, (nsGDBusFunc *)&_##name },
85 : const nsGDBusDynamicFunction kGDBusSymbols[] = {
86 : GDBUS_FUNCTIONS
87 0 : };
88 : #undef FUNC
89 :
90 0 : if (!gioLib) {
91 0 : gioLib = PR_LoadLibrary("libgio-2.0.so.0");
92 0 : if (!gioLib)
93 0 : return NS_ERROR_FAILURE;
94 : }
95 :
96 0 : for (auto GDBusSymbol : kGDBusSymbols) {
97 0 : *GDBusSymbol.function =
98 0 : PR_FindFunctionSymbol(gioLib, GDBusSymbol.functionName);
99 0 : if (!*GDBusSymbol.function) {
100 0 : return NS_ERROR_FAILURE;
101 : }
102 : }
103 :
104 0 : return NS_OK;
105 : }
106 :
107 0 : NS_IMPL_ISUPPORTS(nsPackageKitService, nsIPackageKitService)
108 :
109 0 : nsPackageKitService::~nsPackageKitService()
110 : {
111 0 : if (gioLib) {
112 0 : PR_UnloadLibrary(gioLib);
113 0 : gioLib = nullptr;
114 : }
115 0 : }
116 :
117 : static const char* InstallPackagesMethods[] = {
118 : "InstallPackageNames",
119 : "InstallMimeTypes",
120 : "InstallFontconfigResources",
121 : "InstallGStreamerResources"
122 : };
123 :
124 0 : struct InstallPackagesProxyNewData {
125 : nsCOMPtr<nsIObserver> observer;
126 : uint32_t method;
127 : GVariant* parameters;
128 : };
129 :
130 : static void
131 0 : InstallPackagesNotifyObserver(nsIObserver* aObserver,
132 : gchar* aErrorMessage)
133 : {
134 0 : if (aObserver) {
135 0 : aObserver->Observe(nullptr, "packagekit-install",
136 : aErrorMessage ?
137 0 : NS_ConvertUTF8toUTF16(aErrorMessage).get() :
138 0 : nullptr);
139 : }
140 0 : }
141 :
142 : static void
143 0 : InstallPackagesProxyCallCallback(GObject *aSourceObject,
144 : GAsyncResult *aResult,
145 : gpointer aUserData)
146 : {
147 0 : nsCOMPtr<nsIObserver> observer = static_cast<nsIObserver*>(aUserData);
148 0 : GDBusProxy* proxy = reinterpret_cast<GDBusProxy*>(aSourceObject);
149 :
150 0 : GError* error = nullptr;
151 0 : GVariant* result = g_dbus_proxy_call_finish(proxy, aResult, &error);
152 0 : if (result) {
153 0 : InstallPackagesNotifyObserver(observer, nullptr);
154 0 : g_variant_unref(result);
155 : } else {
156 0 : NS_ASSERTION(error, "g_dbus_proxy_call_finish should set error when it returns NULL");
157 0 : InstallPackagesNotifyObserver(observer, error->message);
158 0 : g_error_free(error);
159 : }
160 :
161 0 : g_object_unref(proxy);
162 0 : Unused << observer.forget().take();
163 0 : }
164 :
165 : static void
166 0 : InstallPackagesProxyNewCallback(GObject *aSourceObject,
167 : GAsyncResult *aResult,
168 : gpointer aUserData)
169 : {
170 : InstallPackagesProxyNewData* userData =
171 0 : static_cast<InstallPackagesProxyNewData*>(aUserData);
172 :
173 0 : NS_ASSERTION(g_variant_is_floating(userData->parameters),
174 : "userData->parameters should be a floating reference.");
175 :
176 0 : GError* error = nullptr;
177 0 : GDBusProxy* proxy = g_dbus_proxy_new_finish(aResult, &error);
178 :
179 0 : if (proxy) {
180 : // Send the asynchronous request to install the packages
181 : // This call will consume the floating reference userData->parameters so we
182 : // don't need to release it explicitly.
183 : nsIObserver* observer;
184 0 : userData->observer.forget(&observer);
185 0 : g_dbus_proxy_call(proxy,
186 0 : InstallPackagesMethods[userData->method],
187 : userData->parameters,
188 : G_DBUS_CALL_FLAGS_NONE,
189 : G_MAXINT,
190 : nullptr,
191 : &InstallPackagesProxyCallCallback,
192 0 : static_cast<gpointer>(observer));
193 : } else {
194 0 : NS_ASSERTION(error, "g_dbus_proxy_new_finish should set error when it returns NULL");
195 0 : InstallPackagesNotifyObserver(userData->observer, error->message);
196 0 : g_error_free(error);
197 0 : g_variant_unref(userData->parameters);
198 : }
199 0 : delete userData;
200 0 : }
201 :
202 : NS_IMETHODIMP
203 0 : nsPackageKitService::InstallPackages(uint32_t aInstallMethod,
204 : nsIArray* aPackageArray,
205 : nsIObserver* aObserver)
206 : {
207 0 : NS_ENSURE_ARG(aPackageArray);
208 :
209 : uint32_t arrayLength;
210 0 : aPackageArray->GetLength(&arrayLength);
211 0 : if (arrayLength == 0 ||
212 0 : arrayLength == std::numeric_limits<uint32_t>::max() ||
213 : aInstallMethod >= PK_INSTALL_METHOD_COUNT) {
214 0 : return NS_ERROR_INVALID_ARG;
215 : }
216 :
217 : // Create the GVariant* parameter from the list of packages.
218 0 : GVariant* parameters = nullptr;
219 0 : auto packages = MakeUnique<gchar*[]>(arrayLength + 1);
220 :
221 0 : nsresult rv = NS_OK;
222 0 : for (uint32_t i = 0; i < arrayLength; i++) {
223 : nsCOMPtr<nsISupportsString> package =
224 0 : do_QueryElementAt(aPackageArray, i);
225 0 : if (!package) {
226 0 : rv = NS_ERROR_FAILURE;
227 0 : break;
228 : }
229 0 : nsString data;
230 0 : package->GetData(data);
231 0 : packages[i] = g_strdup(NS_ConvertUTF16toUTF8(data).get());
232 0 : if (!packages[i]) {
233 0 : rv = NS_ERROR_OUT_OF_MEMORY;
234 0 : break;
235 : }
236 : }
237 0 : packages[arrayLength] = nullptr;
238 :
239 0 : if (NS_SUCCEEDED(rv)) {
240 : // We create a new GVariant object to send parameters to PackageKit.
241 0 : parameters = g_variant_new("(u^ass)", static_cast<guint32>(0),
242 0 : packages.get(), "hide-finished");
243 0 : if (!parameters) {
244 0 : rv = NS_ERROR_OUT_OF_MEMORY;
245 : }
246 : }
247 0 : for (uint32_t i = 0; i < arrayLength; i++) {
248 0 : g_free(packages[i]);
249 : }
250 0 : NS_ENSURE_SUCCESS(rv, rv);
251 :
252 : // Send the asynchronous request to load the bus proxy
253 0 : InstallPackagesProxyNewData* data = new InstallPackagesProxyNewData;
254 0 : data->observer = aObserver;
255 0 : data->method = aInstallMethod;
256 0 : data->parameters = parameters;
257 : g_dbus_proxy_new_for_bus(G_BUS_TYPE_SESSION,
258 : G_DBUS_PROXY_FLAGS_NONE,
259 : nullptr,
260 : "org.freedesktop.PackageKit",
261 : "/org/freedesktop/PackageKit",
262 : "org.freedesktop.PackageKit.Modify",
263 : nullptr,
264 : &InstallPackagesProxyNewCallback,
265 0 : static_cast<gpointer>(data));
266 0 : return NS_OK;
267 : }
|