Merge lp:~dobey/pay-service/backport-ual-session into lp:pay-service/15.04
- backport-ual-session
- Merge into trunk.15.04
Proposed by
dobey
Status: | Merged |
---|---|
Approved by: | dobey |
Approved revision: | 47 |
Merged at revision: | 49 |
Proposed branch: | lp:~dobey/pay-service/backport-ual-session |
Merge into: | lp:pay-service/15.04 |
Diff against target: |
1579 lines (+491/-930) 8 files modified
CMakeLists.txt (+1/-1) debian/control (+1/-1) debian/pay-service.install (+0/-1) service/CMakeLists.txt (+0/-10) service/exec-tool.c (+114/-185) service/mir-connection-demangler.c (+0/-116) service/purchase-ual.cpp (+236/-481) tests/purchase-ual-tests.cpp (+139/-135) |
To merge this branch: | bzr merge lp:~dobey/pay-service/backport-ual-session |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Kyle Fazzari (community) | Approve | ||
PS Jenkins bot (community) | continuous-integration | Approve | |
Review via email: mp+270064@code.launchpad.net |
Commit message
Use libubuntu-
Description of the change
To post a comment you must log in.
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote : | # |
review:
Approve
(continuous-integration)
Revision history for this message
Kyle Fazzari (kyrofa) wrote : | # |
I see one typo (that can be fixed later if you want), but I don't see any real problems with this. That said, I'm not sure I should be the only reviewer here since the context is not entirely clear to me.
review:
Approve
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'CMakeLists.txt' |
2 | --- CMakeLists.txt 2015-07-01 21:23:27 +0000 |
3 | +++ CMakeLists.txt 2015-09-03 14:04:08 +0000 |
4 | @@ -37,7 +37,7 @@ |
5 | accounts-qt5 |
6 | libcurl |
7 | jsoncpp |
8 | - ubuntu-app-launch-2 |
9 | + ubuntu-app-launch-2>=0.5 |
10 | dbustest-1 |
11 | gio-2.0 |
12 | gio-unix-2.0 |
13 | |
14 | === modified file 'debian/control' |
15 | --- debian/control 2015-07-06 16:52:08 +0000 |
16 | +++ debian/control 2015-09-03 14:04:08 +0000 |
17 | @@ -19,7 +19,7 @@ |
18 | libgtest-dev, |
19 | libmirclient-dev (>= 0.5), |
20 | libprocess-cpp-dev (>= 2.0.0), |
21 | - libubuntu-app-launch2-dev, |
22 | + libubuntu-app-launch2-dev (>= 0.5), |
23 | libubuntuoneauth-2.0-dev, |
24 | libjsoncpp-dev, |
25 | python3-dbusmock, |
26 | |
27 | === modified file 'debian/pay-service.install' |
28 | --- debian/pay-service.install 2014-08-06 15:07:19 +0000 |
29 | +++ debian/pay-service.install 2015-09-03 14:04:08 +0000 |
30 | @@ -1,5 +1,4 @@ |
31 | usr/lib/*/pay-service/pay-service |
32 | usr/lib/*/pay-service/setup-staging.sh |
33 | -usr/lib/*/pay-service/mir-connection-demangler |
34 | usr/lib/*/ubuntu-app-launch/pay-ui/* |
35 | usr/share/upstart/sessions/* |
36 | |
37 | === modified file 'service/CMakeLists.txt' |
38 | --- service/CMakeLists.txt 2015-07-06 16:52:08 +0000 |
39 | +++ service/CMakeLists.txt 2015-09-03 14:04:08 +0000 |
40 | @@ -41,7 +41,6 @@ |
41 | |
42 | add_gdbus_codegen_with_namespace(SERVICE_LIB_GEN_SOURCES proxy-service com.canonical. proxy ${CMAKE_SOURCE_DIR}/data/com.canonical.pay.xml) |
43 | add_gdbus_codegen_with_namespace(SERVICE_LIB_GEN_SOURCES proxy-package com.canonical. proxy ${CMAKE_SOURCE_DIR}/data/com.canonical.pay.package.xml) |
44 | -add_gdbus_codegen_with_namespace(SERVICE_LIB_GEN_SOURCES proxy-payui com.canonical. proxy ${CMAKE_SOURCE_DIR}/data/com.canonical.pay.payui.xml) |
45 | |
46 | add_library(pay-service-lib STATIC ${SERVICE_LIB_SOURCES} ${SERVICE_LIB_GEN_SOURCES}) |
47 | |
48 | @@ -54,19 +53,10 @@ |
49 | |
50 | install(TARGETS pay-service RUNTIME DESTINATION ${CMAKE_INSTALL_FULL_PKGLIBEXECDIR}) |
51 | |
52 | -########################### |
53 | -# Mir Connection Demangler |
54 | -########################### |
55 | - |
56 | -add_executable(mir-connection-demangler mir-connection-demangler.c proxy-payui.c proxy-payui.h) |
57 | -target_link_libraries(mir-connection-demangler ${SERVICE_DEPS_LIBRARIES}) |
58 | -install(TARGETS mir-connection-demangler RUNTIME DESTINATION ${CMAKE_INSTALL_FULL_PKGLIBEXECDIR}) |
59 | - |
60 | ############################# |
61 | # Untrusted Helper Exec Tool |
62 | ############################# |
63 | |
64 | -add_definitions(-DSOCKET_TOOL="${CMAKE_INSTALL_FULL_PKGLIBEXECDIR}/mir-connection-demangler") |
65 | add_executable(exec-tool exec-tool.c) |
66 | target_link_libraries(exec-tool ${SERVICE_DEPS_LIBRARIES}) |
67 | install(TARGETS exec-tool RUNTIME DESTINATION "${CMAKE_INSTALL_FULL_LIBEXECDIR}/ubuntu-app-launch/pay-ui") |
68 | |
69 | === modified file 'service/exec-tool.c' |
70 | --- service/exec-tool.c 2014-07-24 17:37:40 +0000 |
71 | +++ service/exec-tool.c 2015-09-03 14:04:08 +0000 |
72 | @@ -22,198 +22,127 @@ |
73 | #include <ubuntu-app-launch.h> |
74 | |
75 | gchar * |
76 | -build_exec_envvar (const gchar * appid) |
77 | -{ |
78 | - gchar * appid_desktop = g_strdup_printf("%s.desktop", appid); |
79 | - gchar * desktopfilepath = g_build_filename(g_get_user_cache_dir(), "pay-service", "pay-ui", appid_desktop, NULL); |
80 | - g_free(appid_desktop); |
81 | - |
82 | - if (!g_file_test(desktopfilepath, G_FILE_TEST_EXISTS)) { |
83 | - g_error("Can not file desktop file for '%s', expecting: '%s'", appid, desktopfilepath); |
84 | - g_free(desktopfilepath); |
85 | - return NULL; |
86 | - } |
87 | - |
88 | - GError * error = NULL; |
89 | - GKeyFile * keyfile = g_key_file_new(); |
90 | - g_key_file_load_from_file(keyfile, desktopfilepath, G_KEY_FILE_NONE, &error); |
91 | - |
92 | - if (error != NULL) { |
93 | - g_error("Unable to read pay-ui desktop file '%s': %s", desktopfilepath, error->message); |
94 | - g_free(desktopfilepath); |
95 | - g_key_file_free(keyfile); |
96 | - g_error_free(error); |
97 | - return NULL; |
98 | - } |
99 | - |
100 | - g_free(desktopfilepath); |
101 | - |
102 | - if (!g_key_file_has_key(keyfile, "Desktop Entry", "Exec", NULL)) { |
103 | - g_error("Desktop file does not have 'Exec' key"); |
104 | - g_key_file_free(keyfile); |
105 | - return NULL; |
106 | - } |
107 | - |
108 | - gchar * exec = g_key_file_get_string(keyfile, "Desktop Entry", "Exec", NULL); |
109 | - g_key_file_free(keyfile); |
110 | - |
111 | - gchar * prepend = g_strdup_printf("%s %s", SOCKET_TOOL, exec); |
112 | - g_free(exec); |
113 | - g_debug("Final Exec line: %s", prepend); |
114 | - |
115 | - gchar * envvar = g_strdup_printf("APP_EXEC=%s", prepend); |
116 | - g_free(prepend); |
117 | - |
118 | - return envvar; |
119 | -} |
120 | - |
121 | -gboolean |
122 | -build_uri_envvar(const gchar * appuris, gchar ** euri, gchar ** esocket) |
123 | -{ |
124 | - gint argc; |
125 | - gchar ** argv; |
126 | - GError * error = NULL; |
127 | - |
128 | - g_shell_parse_argv(appuris, &argc, &argv, &error); |
129 | - if (error != NULL) { |
130 | - g_critical("Unable to parse URIs '%s': %s", appuris, error->message); |
131 | - g_error_free(error); |
132 | - return FALSE; |
133 | - } |
134 | - |
135 | - if (argc != 2) { |
136 | - g_critical("We should be getting 2 entries from '%s' but got %d", appuris, argc); |
137 | - g_strfreev(argv); |
138 | - return FALSE; |
139 | - } |
140 | - |
141 | - *esocket = g_strdup_printf("PAY_SERVICE_MIR_SOCKET=%s", argv[0]); |
142 | - gchar * quoted = g_shell_quote(argv[1]); |
143 | - *euri = g_strdup_printf("APP_URIS=%s", quoted); |
144 | - |
145 | - g_free(quoted); |
146 | - g_strfreev(argv); |
147 | - |
148 | - return TRUE; |
149 | +build_exec (const gchar * appid) |
150 | +{ |
151 | + gchar * appid_desktop = g_strdup_printf("%s.desktop", appid); |
152 | + gchar * desktopfilepath = g_build_filename(g_get_user_cache_dir(), "pay-service", "pay-ui", appid_desktop, NULL); |
153 | + g_free(appid_desktop); |
154 | + |
155 | + if (!g_file_test(desktopfilepath, G_FILE_TEST_EXISTS)) { |
156 | + g_error("Can not file desktop file for '%s', expecting: '%s'", appid, desktopfilepath); |
157 | + g_free(desktopfilepath); |
158 | + return NULL; |
159 | + } |
160 | + |
161 | + GError * error = NULL; |
162 | + GKeyFile * keyfile = g_key_file_new(); |
163 | + g_key_file_load_from_file(keyfile, desktopfilepath, G_KEY_FILE_NONE, &error); |
164 | + |
165 | + if (error != NULL) { |
166 | + g_error("Unable to read pay-ui desktop file '%s': %s", desktopfilepath, error->message); |
167 | + g_free(desktopfilepath); |
168 | + g_key_file_free(keyfile); |
169 | + g_error_free(error); |
170 | + return NULL; |
171 | + } |
172 | + |
173 | + g_free(desktopfilepath); |
174 | + |
175 | + if (!g_key_file_has_key(keyfile, "Desktop Entry", "Exec", NULL)) { |
176 | + g_error("Desktop file does not have 'Exec' key"); |
177 | + g_key_file_free(keyfile); |
178 | + return NULL; |
179 | + } |
180 | + |
181 | + gchar * exec = g_key_file_get_string(keyfile, "Desktop Entry", "Exec", NULL); |
182 | + g_key_file_free(keyfile); |
183 | + |
184 | + return exec; |
185 | } |
186 | |
187 | gchar * |
188 | -build_dir_envvar (const gchar * appid) |
189 | +build_dir (const gchar * appid) |
190 | { |
191 | - GError * error = NULL; |
192 | - gchar * package = NULL; |
193 | - /* 'Parse' the App ID */ |
194 | - if (!ubuntu_app_launch_app_id_parse(appid, &package, NULL, NULL)) { |
195 | - g_warning("Unable to parse App ID: '%s'", appid); |
196 | - return NULL; |
197 | - } |
198 | - |
199 | - /* Check click to find out where the files are */ |
200 | - ClickDB * db = click_db_new(); |
201 | - /* If TEST_CLICK_DB is unset, this reads the system database. */ |
202 | - click_db_read(db, g_getenv("TEST_CLICK_DB"), &error); |
203 | - if (error != NULL) { |
204 | - g_warning("Unable to read Click database: %s", error->message); |
205 | - g_error_free(error); |
206 | - g_free(package); |
207 | - return NULL; |
208 | - } |
209 | - /* If TEST_CLICK_USER is unset, this uses the current user name. */ |
210 | - ClickUser * user = click_user_new_for_user(db, g_getenv("TEST_CLICK_USER"), &error); |
211 | - if (error != NULL) { |
212 | - g_warning("Unable to read Click database: %s", error->message); |
213 | - g_error_free(error); |
214 | - g_free(package); |
215 | - g_object_unref(db); |
216 | - return NULL; |
217 | - } |
218 | - gchar * pkgdir = click_user_get_path(user, package, &error); |
219 | - |
220 | - g_object_unref(user); |
221 | - g_object_unref(db); |
222 | - g_free(package); |
223 | - |
224 | - if (error != NULL) { |
225 | - g_warning("Unable to get the Click package directory for %s: %s", package, error->message); |
226 | - g_error_free(error); |
227 | - return NULL; |
228 | - } |
229 | - |
230 | - gchar * envvar = g_strdup_printf("APP_DIR=%s", pkgdir); |
231 | - g_free(pkgdir); |
232 | - return envvar; |
233 | + GError * error = NULL; |
234 | + gchar * package = NULL; |
235 | + |
236 | + /* 'Parse' the App ID */ |
237 | + if (!ubuntu_app_launch_app_id_parse(appid, &package, NULL, NULL)) { |
238 | + g_warning("Unable to parse App ID: '%s'", appid); |
239 | + return NULL; |
240 | + } |
241 | + |
242 | + /* Check click to find out where the files are */ |
243 | + ClickDB * db = click_db_new(); |
244 | + |
245 | + /* If TEST_CLICK_DB is unset, this reads the system database. */ |
246 | + click_db_read(db, g_getenv("TEST_CLICK_DB"), &error); |
247 | + if (error != NULL) { |
248 | + g_warning("Unable to read Click database: %s", error->message); |
249 | + g_error_free(error); |
250 | + g_free(package); |
251 | + return NULL; |
252 | + } |
253 | + |
254 | + /* If TEST_CLICK_USER is unset, this uses the current user name. */ |
255 | + ClickUser * user = click_user_new_for_user(db, g_getenv("TEST_CLICK_USER"), &error); |
256 | + if (error != NULL) { |
257 | + g_warning("Unable to read Click database: %s", error->message); |
258 | + g_error_free(error); |
259 | + g_free(package); |
260 | + g_object_unref(db); |
261 | + return NULL; |
262 | + } |
263 | + |
264 | + gchar * pkgdir = click_user_get_path(user, package, &error); |
265 | + |
266 | + g_object_unref(user); |
267 | + g_object_unref(db); |
268 | + g_free(package); |
269 | + |
270 | + if (error != NULL) { |
271 | + g_warning("Unable to get the Click package directory for %s: %s", package, error->message); |
272 | + g_error_free(error); |
273 | + return NULL; |
274 | + } |
275 | + |
276 | + return pkgdir; |
277 | } |
278 | |
279 | int |
280 | main (int argc, char * argv[]) |
281 | { |
282 | - GError * error = NULL; |
283 | - |
284 | - /* Build up our exec */ |
285 | - const gchar * appid = g_getenv("APP_ID"); |
286 | - if (appid == NULL) { |
287 | - g_error("Environment variable 'APP_ID' required"); |
288 | - return -1; |
289 | - } |
290 | - |
291 | - gchar * envexec = build_exec_envvar(appid); |
292 | - if (envexec == NULL) { |
293 | - return -1; |
294 | - } |
295 | - |
296 | - /* Build up our socket name URL */ |
297 | - const gchar * appuris = g_getenv("APP_URIS"); |
298 | - if (appuris == NULL) { |
299 | - g_error("Environment variable 'APP_URIS' required"); |
300 | - g_free(envexec); |
301 | - return -1; |
302 | - } |
303 | - |
304 | - gchar * envuri = NULL; |
305 | - gchar * envsocket = NULL; |
306 | - |
307 | - if (!build_uri_envvar(appuris, &envuri, &envsocket)) { |
308 | - g_free(envexec); |
309 | - return -1; |
310 | - } |
311 | - |
312 | - gchar * envdir = build_dir_envvar(appid); |
313 | - /* envdir might be NULL if not a click */ |
314 | - |
315 | - /* Execute the setting of the variables! */ |
316 | - |
317 | - gchar * initctlargv[7] = { |
318 | - "initctl", |
319 | - "set-env", |
320 | - envexec, |
321 | - envuri, |
322 | - envsocket, |
323 | - envdir, |
324 | - NULL |
325 | - }; |
326 | - |
327 | - g_spawn_sync( |
328 | - NULL, /* pwd */ |
329 | - initctlargv, |
330 | - NULL, /* env */ |
331 | - G_SPAWN_SEARCH_PATH, |
332 | - NULL, /* child setup */ |
333 | - NULL, /* user data ^ */ |
334 | - NULL, /* stdout */ |
335 | - NULL, /* stderr */ |
336 | - NULL, /* return code */ |
337 | - &error |
338 | - ); |
339 | - |
340 | - g_free(envexec); |
341 | - g_free(envuri); |
342 | - g_free(envsocket); |
343 | - |
344 | - if (error == NULL) { |
345 | - return 0; |
346 | - } else { |
347 | - g_error("Unable to spawn 'initctl': %s", error->message); |
348 | - g_error_free(error); |
349 | - return -1; |
350 | - } |
351 | + GError * error = NULL; |
352 | + |
353 | + /* Build up our exec */ |
354 | + const gchar * appid = g_getenv("APP_ID"); |
355 | + if (appid == NULL) { |
356 | + g_error("Environment variable 'APP_ID' required"); |
357 | + return -1; |
358 | + } |
359 | + |
360 | + gchar * exec = build_exec(appid); |
361 | + if (exec == NULL) { |
362 | + return -1; |
363 | + } |
364 | + |
365 | + gchar * dir = build_dir(appid); |
366 | + if (dir == NULL) { |
367 | + return -1; |
368 | + } |
369 | + |
370 | + GDBusConnection * bus = g_bus_get_sync(G_BUS_TYPE_SESSION, NULL, NULL); |
371 | + g_return_val_if_fail(bus != NULL, -1); |
372 | + |
373 | + g_debug("Pay UI Exec: %s", exec); |
374 | + g_debug("Pay UI Dir: %s", dir); |
375 | + ubuntu_app_launch_helper_set_exec(exec, dir); |
376 | + g_free(exec); |
377 | + g_free(dir); |
378 | + |
379 | + /* Ensuring the messages get on the bus before we quit */ |
380 | + g_dbus_connection_flush_sync(bus, NULL, NULL); |
381 | + g_clear_object(&bus); |
382 | + |
383 | + return 0; |
384 | } |
385 | |
386 | === removed file 'service/mir-connection-demangler.c' |
387 | --- service/mir-connection-demangler.c 2014-08-06 15:03:54 +0000 |
388 | +++ service/mir-connection-demangler.c 1970-01-01 00:00:00 +0000 |
389 | @@ -1,116 +0,0 @@ |
390 | -/* |
391 | - * Copyright © 2014 Canonical Ltd. |
392 | - * |
393 | - * This program is free software: you can redistribute it and/or modify it |
394 | - * under the terms of the GNU General Public License version 3, as published |
395 | - * by the Free Software Foundation. |
396 | - * |
397 | - * This program is distributed in the hope that it will be useful, but |
398 | - * WITHOUT ANY WARRANTY; without even the implied warranties of |
399 | - * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
400 | - * PURPOSE. See the GNU General Public License for more details. |
401 | - * |
402 | - * You should have received a copy of the GNU General Public License along |
403 | - * with this program. If not, see <http://www.gnu.org/licenses/>. |
404 | - * |
405 | - * Authors: |
406 | - * Ted Gould <ted.gould@canonical.com> |
407 | - */ |
408 | - |
409 | -#include <gio/gio.h> |
410 | -#include <gio/gunixfdlist.h> |
411 | - |
412 | -#include <errno.h> |
413 | -#include <fcntl.h> |
414 | - |
415 | -int |
416 | -main (int argc, char * argv[]) |
417 | -{ |
418 | - const gchar * mir_socket = g_getenv("PAY_SERVICE_MIR_SOCKET"); |
419 | - if (mir_socket == NULL || mir_socket[0] == '\0') { |
420 | - g_error("Unable to find Mir connection from Pay Service"); |
421 | - return -1; |
422 | - } |
423 | - |
424 | - g_print("Mir Connection Path: %s\n", mir_socket); |
425 | - |
426 | - GError * error = NULL; |
427 | - GDBusConnection * bus = g_bus_get_sync(G_BUS_TYPE_SESSION, NULL, &error); |
428 | - |
429 | - if (error != NULL) { |
430 | - g_error("Unable to get session bus: %s", error->message); |
431 | - g_error_free(error); |
432 | - return -1; |
433 | - } |
434 | - |
435 | - GVariant * retval; |
436 | - GUnixFDList * fdlist; |
437 | - |
438 | - retval = g_dbus_connection_call_with_unix_fd_list_sync( |
439 | - bus, |
440 | - "com.canonical.pay", |
441 | - mir_socket, |
442 | - "com.canonical.pay.payui", |
443 | - "GetMirSocket", |
444 | - NULL, |
445 | - G_VARIANT_TYPE("(h)"), |
446 | - G_DBUS_CALL_FLAGS_NO_AUTO_START, |
447 | - -1, /* timeout */ |
448 | - NULL, /* fd list in */ |
449 | - &fdlist, |
450 | - NULL, /* cancelable */ |
451 | - &error); |
452 | - |
453 | - g_clear_object(&bus); |
454 | - |
455 | - if (error != NULL) { |
456 | - g_error("Unable to get Mir socket over dbus: %s", error->message); |
457 | - g_error_free(error); |
458 | - return -1; |
459 | - } |
460 | - |
461 | - GVariant * outhandle = g_variant_get_child_value(retval, 0); |
462 | - |
463 | - if (outhandle == NULL) { |
464 | - g_error("Unable to get data from function"); |
465 | - return -1; |
466 | - } |
467 | - |
468 | - gint32 handle = g_variant_get_handle(outhandle); |
469 | - g_variant_unref(outhandle); |
470 | - g_variant_unref(retval); |
471 | - |
472 | - if (handle >= g_unix_fd_list_get_length(fdlist)) { |
473 | - g_error("Handle is %d but the FD list only has %d entries", handle, g_unix_fd_list_get_length(fdlist)); |
474 | - g_clear_object(&fdlist); |
475 | - return -1; |
476 | - } |
477 | - |
478 | - gint32 fd = g_unix_fd_list_get(fdlist, handle, &error); |
479 | - g_clear_object(&fdlist); |
480 | - |
481 | - if (error != NULL) { |
482 | - g_error("Unable to Unix FD: %s", error->message); |
483 | - g_error_free(error); |
484 | - return -1; |
485 | - } |
486 | - |
487 | - errno = 0; |
488 | - fcntl(fd, F_GETFD); |
489 | - if (errno != 0) { |
490 | - perror("File descriptor is invalid"); |
491 | - return -1; |
492 | - } |
493 | - |
494 | - /* Make sure the FD doesn't close on exec */ |
495 | - fcntl(fd, F_SETFD, 0); |
496 | - |
497 | - gchar * mirsocketbuf = g_strdup_printf("fd://%d", fd); |
498 | - setenv("MIR_SOCKET", mirsocketbuf, 1); |
499 | - g_print("Setting MIR_SOCKET to: '%s'\n", mirsocketbuf); |
500 | - |
501 | - g_free(mirsocketbuf); |
502 | - |
503 | - /* Thought, is argv NULL terminated? */ |
504 | - return execvp(argv[1], argv + 1); |
505 | -} |
506 | |
507 | === modified file 'service/purchase-ual.cpp' |
508 | --- service/purchase-ual.cpp 2015-03-24 22:11:07 +0000 |
509 | +++ service/purchase-ual.cpp 2015-09-03 14:04:08 +0000 |
510 | @@ -18,7 +18,6 @@ |
511 | */ |
512 | |
513 | #include "purchase-ual.h" |
514 | -#include "proxy-payui.h" |
515 | |
516 | #include <thread> |
517 | #include <future> |
518 | @@ -38,462 +37,11 @@ |
519 | |
520 | class UalItem : public Item |
521 | { |
522 | -private: |
523 | - class HelperThread |
524 | - { |
525 | - private: |
526 | - /* Members Only */ |
527 | - std::string socketname; |
528 | - GLib::ContextThread thread; |
529 | - std::string helperid; |
530 | - std::string appid; |
531 | - std::string purchaseUrl; |
532 | - Item::Status status = Item::ERROR; |
533 | - |
534 | - static void helper_stop_static_helper (const gchar* appid, const gchar* instanceid, const gchar* helpertype, |
535 | - gpointer user_data) |
536 | - { |
537 | - auto notthis = static_cast<HelperThread*>(user_data); |
538 | - notthis->helperStop(std::string(appid)); |
539 | - } |
540 | - |
541 | - void helperStop (std::string stop_appid) |
542 | - { |
543 | - if (stop_appid != appid) |
544 | - { |
545 | - return; |
546 | - } |
547 | - |
548 | - status = Item::PURCHASED; |
549 | - helperid.clear(); /* It has stopped, now an invalid ID */ |
550 | - thread.quit(); |
551 | - } |
552 | - |
553 | - public: |
554 | - core::Signal<Item::Status> helperFinished; |
555 | - |
556 | - HelperThread (const std::string& in_socketname, const std::string& in_appid, const std::string& in_purchaseUrl) |
557 | - : socketname(in_socketname) |
558 | - , appid(in_appid) |
559 | - , purchaseUrl(in_purchaseUrl) |
560 | - , thread([this] |
561 | - { |
562 | - if (!ubuntu_app_launch_observer_add_helper_stop(helper_stop_static_helper, HELPER_TYPE, this)) |
563 | - throw std::runtime_error("Unable to register Stop Helper"); |
564 | - /* TODO: Add failed when in UAL */ |
565 | - }, |
566 | - [this] |
567 | - { |
568 | - /* Clean up */ |
569 | - if (!ubuntu_app_launch_observer_delete_helper_stop(helper_stop_static_helper, HELPER_TYPE, this)) |
570 | - throw std::runtime_error("Unable to unregister Stop Helper"); |
571 | - |
572 | - /* If we've still got a helper ID we need to kill it. */ |
573 | - if (!helperid.empty()) |
574 | - { |
575 | - ubuntu_app_launch_stop_multiple_helper(HELPER_TYPE, appid.c_str(), helperid.c_str()); |
576 | - helperid.clear(); |
577 | - } |
578 | - |
579 | - helperFinished(status); |
580 | - }) |
581 | - |
582 | - { |
583 | - helperid = thread.executeOnThread<std::string>([this]() -> std::string |
584 | - { |
585 | - /* Building a URL to pass info to the Pay UI */ |
586 | - std::string retval; |
587 | - std::array<const gchar*, 3> urls{ |
588 | - socketname.c_str(), |
589 | - purchaseUrl.c_str(), |
590 | - nullptr |
591 | - }; |
592 | - |
593 | - gchar* helperid = ubuntu_app_launch_start_multiple_helper(HELPER_TYPE, appid.c_str(), urls.data()); |
594 | - if (helperid != nullptr) |
595 | - { |
596 | - retval = helperid; |
597 | - g_free(helperid); |
598 | - } |
599 | - |
600 | - return retval; |
601 | - }); |
602 | - } |
603 | - |
604 | - ~HelperThread (void) |
605 | - { |
606 | - /* Quit before other variables are free'd */ |
607 | - thread.quit(); |
608 | - } |
609 | - }; |
610 | - |
611 | - class MirSocketThread |
612 | - { |
613 | - /* From Init */ |
614 | - std::shared_ptr<MirConnection> connection; |
615 | - std::string appid; |
616 | - std::string ui_appid; |
617 | - |
618 | - /* Built at Init */ |
619 | - std::shared_ptr<MirPromptSession> session; |
620 | - std::string socketName; |
621 | - |
622 | - /* On thread */ |
623 | - std::shared_ptr<proxyPayPayui> payuiobj; |
624 | - std::shared_ptr<GDBusConnection> bus; |
625 | - int fdlist[1] = {0}; |
626 | - GLib::ContextThread thread; |
627 | - |
628 | - /* Creates a Mir Prompt Session by finding the overlay pid and making it. */ |
629 | - std::shared_ptr<MirPromptSession> setupSession (void) |
630 | - { |
631 | - pid_t overlaypid = appid2pid(appid); |
632 | - if (overlaypid == 0) |
633 | - { |
634 | - /* We can't overlay nothin' */ |
635 | - g_warning("Unable to find PID for: %s", appid.c_str()); |
636 | - return nullptr; |
637 | - } |
638 | - |
639 | - /* Setup the trusted prompt session */ |
640 | - auto session = std::shared_ptr<MirPromptSession>( |
641 | - mir_connection_create_prompt_session_sync(connection.get(), overlaypid, stateChanged, this), |
642 | - [](MirPromptSession * session) |
643 | - { |
644 | - if (session != nullptr) |
645 | - { |
646 | - mir_prompt_session_release_sync(session); |
647 | - } |
648 | - }); |
649 | - |
650 | - if (!session) |
651 | - { |
652 | - g_critical("Unable to create a trusted prompt session"); |
653 | - } |
654 | - |
655 | - return session; |
656 | - } |
657 | - |
658 | - /* Creates the abstract socket for communicating the file handle to the |
659 | - Pay UI and builds a thread to service it */ |
660 | - std::string setupSocket () |
661 | - { |
662 | - return thread.executeOnThread<std::string>([this]() |
663 | - { |
664 | - GError* error = nullptr; |
665 | - std::string socketName; |
666 | - |
667 | - bus = std::shared_ptr<GDBusConnection>(g_bus_get_sync(G_BUS_TYPE_SESSION, nullptr, |
668 | - nullptr), [](GDBusConnection * bus) |
669 | - { |
670 | - g_clear_object(&bus); |
671 | - }); |
672 | - if (!bus) |
673 | - { |
674 | - g_critical("Unable to get session bus"); |
675 | - return std::string(); |
676 | - } |
677 | - |
678 | - /* Export an Object on DBus */ |
679 | - payuiobj = std::shared_ptr<proxyPayPayui>( |
680 | - proxy_pay_payui_skeleton_new(), |
681 | - [](proxyPayPayui * payui) |
682 | - { |
683 | - if (payui != nullptr) |
684 | - { |
685 | - g_dbus_interface_skeleton_unexport(G_DBUS_INTERFACE_SKELETON(payui)); |
686 | - g_object_unref(payui); |
687 | - } |
688 | - }); |
689 | - g_signal_connect(payuiobj.get(), "handle-get-mir-socket", G_CALLBACK(mirHandle), fdlist); |
690 | - |
691 | - auto encodedAppId = encodePath(ui_appid); |
692 | - /* Loop until we fine an object path that isn't taken (probably only once) */ |
693 | - while (socketName.empty()) |
694 | - { |
695 | - gchar* tryname = g_strdup_printf("/com/canonical/pay/%s/%X", encodedAppId.c_str(), g_random_int()); |
696 | - g_dbus_interface_skeleton_export(G_DBUS_INTERFACE_SKELETON(payuiobj.get()), |
697 | - bus.get(), |
698 | - tryname, |
699 | - &error); |
700 | - |
701 | - if (error == NULL) |
702 | - { |
703 | - socketName = std::string(tryname); |
704 | - } |
705 | - else |
706 | - { |
707 | - /* Always print the error, but if the object path is in use let's |
708 | - not exit the loop. Let's just try again. */ |
709 | - bool exitnow = (error->domain != G_DBUS_ERROR || error->code != G_DBUS_ERROR_OBJECT_PATH_IN_USE); |
710 | - g_critical("Unable to export payui object: %s", error->message); |
711 | - g_error_free(error); |
712 | - if (exitnow) |
713 | - { |
714 | - g_free(tryname); |
715 | - break; |
716 | - } |
717 | - } |
718 | - |
719 | - g_free(tryname); |
720 | - } |
721 | - |
722 | - /* If we didn't get a socket name, we should just exit. And |
723 | - make sure to clean up the socket. */ |
724 | - if (socketName.empty()) |
725 | - { |
726 | - g_critical("Unable to export object to any name"); |
727 | - return std::string(); |
728 | - } |
729 | - |
730 | - auto mirwait = mir_prompt_session_new_fds_for_prompt_providers(session.get(), |
731 | - 1, |
732 | - [](MirPromptSession * session, size_t count, int const * fdsin, void* context) -> void |
733 | - { |
734 | - g_debug("FDs %d Returned from Mir", count); |
735 | - if (count != 1) return; |
736 | - int* fdout = reinterpret_cast<int*>(context); |
737 | - fdout[0] = fdsin[0]; |
738 | - }, |
739 | - fdlist); |
740 | - |
741 | - mir_wait_for(mirwait); |
742 | - |
743 | - if (fdlist[0] == 0) |
744 | - { |
745 | - g_critical("FD from Mir was a 0"); |
746 | - return std::string(); |
747 | - } |
748 | - |
749 | - return socketName; |
750 | - }); |
751 | - } |
752 | - |
753 | - static bool mirHandle (GObject* obj, GDBusMethodInvocation* invocation, gpointer user_data) |
754 | - { |
755 | - int* fds = reinterpret_cast<int*>(user_data); |
756 | - |
757 | - if (fds[0] == 0) |
758 | - { |
759 | - g_critical("No FDs to give!"); |
760 | - return false; |
761 | - } |
762 | - |
763 | - /* Index into fds */ |
764 | - GVariant* handle = g_variant_new_handle(0); |
765 | - GVariant* tuple = g_variant_new_tuple(&handle, 1); |
766 | - |
767 | - GError* error = nullptr; |
768 | - GUnixFDList* list = g_unix_fd_list_new(); |
769 | - g_unix_fd_list_append(list, fds[0], &error); |
770 | - |
771 | - if (error == nullptr) |
772 | - { |
773 | - g_dbus_method_invocation_return_value_with_unix_fd_list(invocation, tuple, list); |
774 | - } |
775 | - |
776 | - g_object_unref(list); |
777 | - |
778 | - if (error != nullptr) |
779 | - { |
780 | - g_critical("Unable to pass FD %d: %s", fds[0], error->message); |
781 | - g_error_free(error); |
782 | - return false; |
783 | - } |
784 | - |
785 | - return true; |
786 | - } |
787 | - |
788 | - static std::string encodePath (const std::string& input) |
789 | - { |
790 | - std::string output = ""; |
791 | - bool first = true; |
792 | - |
793 | - for (unsigned char c : input) |
794 | - { |
795 | - if ((c >= 'a' && c <= 'z') || |
796 | - (c >= 'A' && c <= 'Z') || |
797 | - (c >= '0' && c <= '9' && !first)) |
798 | - { |
799 | - output += c; |
800 | - } |
801 | - else |
802 | - { |
803 | - char buffer[5] = {0}; |
804 | - std::snprintf(buffer, sizeof(buffer), "_%2X", c); |
805 | - output += buffer; |
806 | - } |
807 | - |
808 | - first = false; |
809 | - } |
810 | - |
811 | - return output; |
812 | - } |
813 | - |
814 | - static pid_t pidForUpstartJob (const gchar* jobname) |
815 | - { |
816 | - GError* error = nullptr; |
817 | - |
818 | - if (jobname == nullptr) |
819 | - { |
820 | - return 0; |
821 | - } |
822 | - |
823 | - auto bus = std::shared_ptr<GDBusConnection>(g_bus_get_sync(G_BUS_TYPE_SESSION, nullptr, |
824 | - NULL), [](GDBusConnection * bus) |
825 | - { |
826 | - g_clear_object(&bus); |
827 | - }); |
828 | - if (!bus) |
829 | - { |
830 | - g_critical("Unable to get session bus"); |
831 | - return 0; |
832 | - } |
833 | - |
834 | - GVariant* retval = nullptr; |
835 | - retval = g_dbus_connection_call_sync( |
836 | - bus.get(), |
837 | - "com.ubuntu.Upstart", |
838 | - "/com/ubuntu/Upstart", |
839 | - "com.ubuntu.Upstart0_6", |
840 | - "GetJobByName", |
841 | - g_variant_new("(s)", jobname), |
842 | - G_VARIANT_TYPE("(o)"), |
843 | - G_DBUS_CALL_FLAGS_NO_AUTO_START, |
844 | - -1, /* timeout */ |
845 | - NULL, /* cancel */ |
846 | - &error); |
847 | - |
848 | - if (error != NULL) |
849 | - { |
850 | - g_warning("Unable to get path for job '%s': %s", jobname, error->message); |
851 | - g_error_free(error); |
852 | - return 0; |
853 | - } |
854 | - |
855 | - gchar* path = nullptr; |
856 | - g_variant_get(retval, "(o)", &path); |
857 | - g_variant_unref(retval); |
858 | - |
859 | - retval = g_dbus_connection_call_sync( |
860 | - bus.get(), |
861 | - "com.ubuntu.Upstart", |
862 | - path, |
863 | - "com.ubuntu.Upstart0_6.Job", |
864 | - "GetInstanceByName", |
865 | - g_variant_new("(s)", ""), |
866 | - G_VARIANT_TYPE("(o)"), |
867 | - G_DBUS_CALL_FLAGS_NO_AUTO_START, |
868 | - -1, /* timeout */ |
869 | - NULL, /* cancel */ |
870 | - &error); |
871 | - |
872 | - g_free(path); |
873 | - |
874 | - if (error != NULL) |
875 | - { |
876 | - g_warning("Unable to get instance for job '%s': %s", jobname, error->message); |
877 | - g_error_free(error); |
878 | - return 0; |
879 | - } |
880 | - |
881 | - g_variant_get(retval, "(o)", &path); |
882 | - g_variant_unref(retval); |
883 | - |
884 | - retval = g_dbus_connection_call_sync( |
885 | - bus.get(), |
886 | - "com.ubuntu.Upstart", |
887 | - path, |
888 | - "org.freedesktop.DBus.Properties", |
889 | - "Get", |
890 | - g_variant_new("(ss)", "com.ubuntu.Upstart0_6.Instance", "processes"), |
891 | - G_VARIANT_TYPE("(v)"), |
892 | - G_DBUS_CALL_FLAGS_NO_AUTO_START, |
893 | - -1, /* timeout */ |
894 | - NULL, /* cancel */ |
895 | - &error); |
896 | - |
897 | - g_free(path); |
898 | - |
899 | - if (error != NULL) |
900 | - { |
901 | - g_warning("Unable to get processes for job '%s': %s", jobname, error->message); |
902 | - g_error_free(error); |
903 | - return 0; |
904 | - } |
905 | - |
906 | - GPid pid = 0; |
907 | - GVariant* variant = g_variant_get_child_value(retval, 0); |
908 | - GVariant* array = g_variant_get_variant(variant); |
909 | - if (g_variant_n_children(array) > 0) |
910 | - { |
911 | - /* (si) */ |
912 | - GVariant* firstitem = g_variant_get_child_value(array, 0); |
913 | - GVariant* vpid = g_variant_get_child_value(firstitem, 1); |
914 | - pid = g_variant_get_int32(vpid); |
915 | - g_variant_unref(vpid); |
916 | - g_variant_unref(firstitem); |
917 | - } |
918 | - g_variant_unref(variant); |
919 | - g_variant_unref(array); |
920 | - g_variant_unref(retval); |
921 | - |
922 | - return pid; |
923 | - } |
924 | - |
925 | - /* Figures out the PID that we should be overlaying with the PayUI */ |
926 | - static pid_t appid2pid (std::string& appid) |
927 | - { |
928 | - if (appid == "click-scope") |
929 | - { |
930 | - /* FIXME: Before other scopes can use pay, we'll need to figure out |
931 | - how to detect if they're scopes or not. But for now we'll only just |
932 | - look for 'click-scope' as it's our primary use-case */ |
933 | - return pidForUpstartJob("unity8-dash"); |
934 | - } |
935 | - else |
936 | - { |
937 | - return ubuntu_app_launch_get_primary_pid(appid.c_str()); |
938 | - } |
939 | - } |
940 | - |
941 | - static void stateChanged (MirPromptSession* session, MirPromptSessionState state, void* user_data) |
942 | - { |
943 | - g_debug("Mir Prompt session is in state: %d", state); |
944 | - } |
945 | - |
946 | - public: |
947 | - MirSocketThread (std::shared_ptr<MirConnection> in_connection, const std::string& in_appid, |
948 | - const std::string& in_ui_appid) |
949 | - : connection(in_connection) |
950 | - , appid(in_appid) |
951 | - , ui_appid(in_ui_appid) |
952 | - , thread([]() {}, [this]() |
953 | - { |
954 | - payuiobj.reset(); |
955 | - bus.reset(); |
956 | - }) |
957 | - { |
958 | - session = setupSession(); |
959 | - socketName = setupSocket(); |
960 | - } |
961 | - |
962 | - ~MirSocketThread (void) |
963 | - { |
964 | - /* Quit before other variables are free'd */ |
965 | - thread.quit(); |
966 | - } |
967 | - |
968 | - std::string getSocketName (void) |
969 | - { |
970 | - return socketName; |
971 | - } |
972 | - }; |
973 | - |
974 | public: |
975 | typedef std::shared_ptr<Item> Ptr; |
976 | |
977 | UalItem (const std::string& in_appid, const std::string& in_itemid, const std::shared_ptr<MirConnection>& mir) : |
978 | + status(Item::ERROR), |
979 | appid(in_appid), |
980 | itemid(in_itemid), |
981 | connection(mir) |
982 | @@ -503,10 +51,6 @@ |
983 | |
984 | ~UalItem () |
985 | { |
986 | - helperFinishedConnection->disconnect(); |
987 | - helperFinishedConnection.reset(); |
988 | - helperThread.reset(); |
989 | - socketThread.reset(); |
990 | } |
991 | |
992 | /* Goes through the basis phases of building up the environment for the |
993 | @@ -514,42 +58,89 @@ |
994 | the socket to pass the session. And then starts the UI. */ |
995 | virtual bool run (void) |
996 | { |
997 | - helperThread.reset(); |
998 | - socketThread.reset(); |
999 | - |
1000 | - auto ui_appid = discoverUiAppid(); |
1001 | - |
1002 | + ui_appid = discoverUiAppid(); |
1003 | if (ui_appid.empty()) |
1004 | { |
1005 | - g_debug("Empty UI App ID"); |
1006 | - return false; |
1007 | - } |
1008 | - |
1009 | - socketThread = std::make_shared<MirSocketThread>(connection, appid, ui_appid); |
1010 | - helperThread = std::make_shared<HelperThread>(socketThread->getSocketName(), ui_appid, buildPurchaseUrl()); |
1011 | - |
1012 | - helperFinishedConnection = std::make_shared<core::Connection>(helperThread->helperFinished.connect([this]( |
1013 | - Item::Status status) |
1014 | - { |
1015 | + g_warning("Empty UI App ID for PayUI"); |
1016 | + return false; |
1017 | + } |
1018 | + |
1019 | + auto purchase_url = buildPurchaseUrl(); |
1020 | + if (purchase_url.empty()) |
1021 | + { |
1022 | + g_warning("Unable to determine purchase URL"); |
1023 | + return false; |
1024 | + } |
1025 | + |
1026 | + thread = std::make_shared<GLib::ContextThread>([]() {}, [this]() |
1027 | + { |
1028 | + if (!instanceid.empty()) |
1029 | + { |
1030 | + ubuntu_app_launch_stop_multiple_helper(HELPER_TYPE, ui_appid.c_str(), instanceid.c_str()); |
1031 | + instanceid.clear(); |
1032 | + } |
1033 | + |
1034 | + if (session) |
1035 | + { |
1036 | + session.reset(); |
1037 | + } |
1038 | + |
1039 | purchaseComplete(status); |
1040 | - })); |
1041 | - |
1042 | - return true; |
1043 | + ubuntu_app_launch_observer_delete_helper_stop(helper_stop_static_helper, HELPER_TYPE, this); |
1044 | + }); |
1045 | + instanceid = thread->executeOnThread<std::string>([this, purchase_url]() |
1046 | + { |
1047 | + std::string instid; |
1048 | + |
1049 | + pid_t overlaypid = appid2pid(appid); |
1050 | + if (overlaypid == 0) |
1051 | + { |
1052 | + return instid; |
1053 | + } |
1054 | + |
1055 | + session = std::shared_ptr<MirPromptSession>( |
1056 | + mir_connection_create_prompt_session_sync(connection.get(), overlaypid, stateChanged, this), |
1057 | + [](MirPromptSession * session) |
1058 | + { |
1059 | + if (session != nullptr) |
1060 | + { |
1061 | + mir_prompt_session_release_sync(session); |
1062 | + } |
1063 | + }); |
1064 | + |
1065 | + if (!session) |
1066 | + { |
1067 | + return instid; |
1068 | + } |
1069 | + |
1070 | + ubuntu_app_launch_observer_add_helper_stop(helper_stop_static_helper, HELPER_TYPE, this); |
1071 | + |
1072 | + std::array<const gchar*, 2>urls {purchase_url.c_str(), nullptr}; |
1073 | + auto instance_c = ubuntu_app_launch_start_session_helper(HELPER_TYPE, |
1074 | + session.get(), |
1075 | + ui_appid.c_str(), |
1076 | + urls.data()); |
1077 | + instid = std::string(instance_c); |
1078 | + g_free(instance_c); |
1079 | + return instid; |
1080 | + }); |
1081 | + |
1082 | + return !instanceid.empty(); |
1083 | } |
1084 | |
1085 | private: |
1086 | /* Set at init */ |
1087 | std::string appid; |
1088 | + std::string ui_appid; |
1089 | std::string itemid; |
1090 | + std::string instanceid; |
1091 | + std::shared_ptr<GLib::ContextThread> thread; |
1092 | + std::shared_ptr<MirPromptSession> session; |
1093 | + Item::Status status; |
1094 | |
1095 | /* Given to us by our parents */ |
1096 | std::shared_ptr<MirConnection> connection; |
1097 | |
1098 | - /* Created by run, destroyed with the object */ |
1099 | - std::shared_ptr<HelperThread> helperThread; |
1100 | - std::shared_ptr<MirSocketThread> socketThread; |
1101 | - std::shared_ptr<core::Connection> helperFinishedConnection; |
1102 | - |
1103 | /* Looks through a directory to find the first entry that is a .desktop file |
1104 | and uses that as our AppID. We don't support more than one entry being in |
1105 | a directory */ |
1106 | @@ -560,7 +151,7 @@ |
1107 | const gchar* clickhookdir = g_getenv("PAY_SERVICE_CLICK_DIR"); |
1108 | if (clickhookdir == nullptr) |
1109 | { |
1110 | - gchar* cacheclickdir = g_build_filename(g_get_user_cache_dir(), "pay-service", "pay-ui", nullptr); |
1111 | + gchar* cacheclickdir = g_build_filename(g_get_user_cache_dir(), "pay-service", HELPER_TYPE, nullptr); |
1112 | g_debug("Looking for Pay UI in: %s", cacheclickdir); |
1113 | dir = g_dir_open(cacheclickdir, 0, nullptr); |
1114 | g_free(cacheclickdir); |
1115 | @@ -618,6 +209,170 @@ |
1116 | return purchase_url; |
1117 | } |
1118 | |
1119 | + static pid_t pidForUpstartJob (const gchar* jobname) |
1120 | + { |
1121 | + GError* error = nullptr; |
1122 | + |
1123 | + if (jobname == nullptr) |
1124 | + { |
1125 | + return 0; |
1126 | + } |
1127 | + |
1128 | + auto bus = std::shared_ptr<GDBusConnection>(g_bus_get_sync(G_BUS_TYPE_SESSION, nullptr, |
1129 | + NULL), [](GDBusConnection * bus) |
1130 | + { |
1131 | + g_clear_object(&bus); |
1132 | + }); |
1133 | + if (!bus) |
1134 | + { |
1135 | + g_critical("Unable to get session bus"); |
1136 | + return 0; |
1137 | + } |
1138 | + |
1139 | + GVariant* retval = nullptr; |
1140 | + retval = g_dbus_connection_call_sync( |
1141 | + bus.get(), |
1142 | + "com.ubuntu.Upstart", |
1143 | + "/com/ubuntu/Upstart", |
1144 | + "com.ubuntu.Upstart0_6", |
1145 | + "GetJobByName", |
1146 | + g_variant_new("(s)", jobname), |
1147 | + G_VARIANT_TYPE("(o)"), |
1148 | + G_DBUS_CALL_FLAGS_NO_AUTO_START, |
1149 | + -1, /* timeout */ |
1150 | + NULL, /* cancel */ |
1151 | + &error); |
1152 | + |
1153 | + if (error != NULL) |
1154 | + { |
1155 | + g_warning("Unable to get path for job '%s': %s", jobname, error->message); |
1156 | + g_error_free(error); |
1157 | + return 0; |
1158 | + } |
1159 | + |
1160 | + gchar* path = nullptr; |
1161 | + g_variant_get(retval, "(o)", &path); |
1162 | + g_variant_unref(retval); |
1163 | + |
1164 | + retval = g_dbus_connection_call_sync( |
1165 | + bus.get(), |
1166 | + "com.ubuntu.Upstart", |
1167 | + path, |
1168 | + "com.ubuntu.Upstart0_6.Job", |
1169 | + "GetInstanceByName", |
1170 | + g_variant_new("(s)", ""), |
1171 | + G_VARIANT_TYPE("(o)"), |
1172 | + G_DBUS_CALL_FLAGS_NO_AUTO_START, |
1173 | + -1, /* timeout */ |
1174 | + NULL, /* cancel */ |
1175 | + &error); |
1176 | + |
1177 | + g_free(path); |
1178 | + |
1179 | + if (error != NULL) |
1180 | + { |
1181 | + g_warning("Unable to get instance for job '%s': %s", jobname, error->message); |
1182 | + g_error_free(error); |
1183 | + return 0; |
1184 | + } |
1185 | + |
1186 | + g_variant_get(retval, "(o)", &path); |
1187 | + g_variant_unref(retval); |
1188 | + |
1189 | + retval = g_dbus_connection_call_sync( |
1190 | + bus.get(), |
1191 | + "com.ubuntu.Upstart", |
1192 | + path, |
1193 | + "org.freedesktop.DBus.Properties", |
1194 | + "Get", |
1195 | + g_variant_new("(ss)", "com.ubuntu.Upstart0_6.Instance", "processes"), |
1196 | + G_VARIANT_TYPE("(v)"), |
1197 | + G_DBUS_CALL_FLAGS_NO_AUTO_START, |
1198 | + -1, /* timeout */ |
1199 | + NULL, /* cancel */ |
1200 | + &error); |
1201 | + |
1202 | + g_free(path); |
1203 | + |
1204 | + if (error != NULL) |
1205 | + { |
1206 | + g_warning("Unable to get processes for job '%s': %s", jobname, error->message); |
1207 | + g_error_free(error); |
1208 | + return 0; |
1209 | + } |
1210 | + |
1211 | + GPid pid = 0; |
1212 | + GVariant* variant = g_variant_get_child_value(retval, 0); |
1213 | + GVariant* array = g_variant_get_variant(variant); |
1214 | + if (g_variant_n_children(array) > 0) |
1215 | + { |
1216 | + /* (si) */ |
1217 | + GVariant* firstitem = g_variant_get_child_value(array, 0); |
1218 | + GVariant* vpid = g_variant_get_child_value(firstitem, 1); |
1219 | + pid = g_variant_get_int32(vpid); |
1220 | + g_variant_unref(vpid); |
1221 | + g_variant_unref(firstitem); |
1222 | + } |
1223 | + g_variant_unref(variant); |
1224 | + g_variant_unref(array); |
1225 | + g_variant_unref(retval); |
1226 | + |
1227 | + return pid; |
1228 | + } |
1229 | + |
1230 | + /* Figures out the PID that we should be overlaying with the PayUI */ |
1231 | + static pid_t appid2pid (std::string& appid) |
1232 | + { |
1233 | + if (appid == "click-scope") |
1234 | + { |
1235 | + /* FIXME: Before other scopes can use pay, we'll need to figure out |
1236 | + how to detect if they're scopes or not. But for now we'll only just |
1237 | + look for 'click-scope' as it's our primary use-case */ |
1238 | + return pidForUpstartJob("unity8-dash"); |
1239 | + } |
1240 | + else |
1241 | + { |
1242 | + return ubuntu_app_launch_get_primary_pid(appid.c_str()); |
1243 | + } |
1244 | + } |
1245 | + |
1246 | + static void stateChanged (MirPromptSession* session, MirPromptSessionState state, void* user_data) |
1247 | + { |
1248 | + g_debug("Mir Prompt session is in state: %d", state); |
1249 | + } |
1250 | + |
1251 | + static void helper_stop_static_helper (const gchar* appid, |
1252 | + const gchar* instanceid, |
1253 | + const gchar* helpertype, |
1254 | + gpointer user_data) |
1255 | + { |
1256 | + UalItem* notthis = static_cast<UalItem*>(user_data); |
1257 | + g_debug("UAL Stop callback, appid: '%s', instance: '%s', helper: '%s'", appid, instanceid, helpertype); |
1258 | + |
1259 | + if (instanceid == nullptr) /* Causes std::string to hate us, and we don't care about it if not set */ |
1260 | + { |
1261 | + return; |
1262 | + } |
1263 | + |
1264 | + notthis->helperStop(std::string(appid), std::string(instanceid)); |
1265 | + } |
1266 | + |
1267 | + void helperStop (std::string stop_appid, std::string stop_instanceid) |
1268 | + { |
1269 | + if (stop_appid != ui_appid) |
1270 | + { |
1271 | + return; |
1272 | + } |
1273 | + |
1274 | + if (instanceid.empty() || instanceid != stop_instanceid) |
1275 | + { |
1276 | + return; |
1277 | + } |
1278 | + |
1279 | + status = Item::PURCHASED; |
1280 | + instanceid.clear(); |
1281 | + thread->quit(); |
1282 | + } |
1283 | }; |
1284 | |
1285 | class UalFactory::Impl |
1286 | |
1287 | === modified file 'tests/purchase-ual-tests.cpp' |
1288 | --- tests/purchase-ual-tests.cpp 2015-03-23 15:59:38 +0000 |
1289 | +++ tests/purchase-ual-tests.cpp 2015-09-03 14:04:08 +0000 |
1290 | @@ -27,150 +27,154 @@ |
1291 | |
1292 | struct PurchaseUALTests : public ::testing::Test |
1293 | { |
1294 | - protected: |
1295 | - DbusTestService * service = NULL; |
1296 | - DbusTestDbusMock * mock = NULL; |
1297 | - DbusTestDbusMockObject * obj = NULL; |
1298 | - DbusTestDbusMockObject * jobobj = NULL; |
1299 | - DbusTestDbusMockObject * instobj = NULL; |
1300 | - GDBusConnection * bus = NULL; |
1301 | - |
1302 | - virtual void SetUp() { |
1303 | - g_setenv("PAY_SERVICE_CLICK_DIR", CMAKE_SOURCE_DIR "/click-hook-data", TRUE); |
1304 | - |
1305 | - service = dbus_test_service_new(NULL); |
1306 | - |
1307 | - mock = dbus_test_dbus_mock_new("com.ubuntu.Upstart"); |
1308 | - |
1309 | - obj = dbus_test_dbus_mock_get_object(mock, "/com/ubuntu/Upstart", "com.ubuntu.Upstart0_6", NULL); |
1310 | - |
1311 | - dbus_test_dbus_mock_object_add_method(mock, obj, |
1312 | - "GetJobByName", |
1313 | - G_VARIANT_TYPE_STRING, |
1314 | - G_VARIANT_TYPE_OBJECT_PATH, /* out */ |
1315 | - "ret = dbus.ObjectPath('/job')", /* python */ |
1316 | - NULL); /* error */ |
1317 | - |
1318 | - jobobj = dbus_test_dbus_mock_get_object(mock, "/job", "com.ubuntu.Upstart0_6.Job", NULL); |
1319 | - |
1320 | - dbus_test_dbus_mock_object_add_method(mock, jobobj, |
1321 | - "Start", |
1322 | - G_VARIANT_TYPE("(asb)"), |
1323 | - G_VARIANT_TYPE_OBJECT_PATH, /* out */ |
1324 | - "ret = dbus.ObjectPath('/instance')", /* python */ |
1325 | - NULL); /* error */ |
1326 | - dbus_test_dbus_mock_object_add_method(mock, jobobj, |
1327 | - "GetAllInstances", |
1328 | - NULL, |
1329 | - G_VARIANT_TYPE("ao"), /* out */ |
1330 | - "ret = [dbus.ObjectPath('/instance')]", /* python */ |
1331 | - NULL); /* error */ |
1332 | - dbus_test_dbus_mock_object_add_method(mock, jobobj, |
1333 | - "GetInstanceByName", |
1334 | - G_VARIANT_TYPE_STRING, |
1335 | - G_VARIANT_TYPE_OBJECT_PATH, /* out */ |
1336 | - "ret = dbus.ObjectPath('/instance')", /* python */ |
1337 | - NULL); /* error */ |
1338 | - |
1339 | - instobj = dbus_test_dbus_mock_get_object(mock, "/instance", "com.ubuntu.Upstart0_6.Instance", NULL); |
1340 | - |
1341 | - dbus_test_dbus_mock_object_add_property(mock, instobj, |
1342 | - "processes", |
1343 | - G_VARIANT_TYPE("a(si)"), |
1344 | - g_variant_new_parsed("[('main', 1234)]"), |
1345 | - NULL); |
1346 | - |
1347 | - dbus_test_service_add_task(service, DBUS_TEST_TASK(mock)); |
1348 | - |
1349 | - dbus_test_service_start_tasks(service); |
1350 | - |
1351 | - bus = g_bus_get_sync(G_BUS_TYPE_SESSION, NULL, NULL); |
1352 | - g_dbus_connection_set_exit_on_close(bus, FALSE); |
1353 | - g_object_add_weak_pointer(G_OBJECT(bus), (gpointer *)&bus); |
1354 | - } |
1355 | - |
1356 | - virtual void TearDown() { |
1357 | - g_clear_object(&mock); |
1358 | - g_clear_object(&service); |
1359 | - |
1360 | - g_object_unref(bus); |
1361 | - |
1362 | - unsigned int cleartry = 0; |
1363 | - while (bus != NULL && cleartry < 100) { |
1364 | - g_usleep(100000); |
1365 | - while (g_main_pending()) |
1366 | - g_main_iteration(TRUE); |
1367 | - cleartry++; |
1368 | - } |
1369 | - |
1370 | - ASSERT_LT(cleartry, 100); |
1371 | - } |
1372 | + protected: |
1373 | + DbusTestService * service = NULL; |
1374 | + DbusTestDbusMock * mock = NULL; |
1375 | + DbusTestDbusMockObject * obj = NULL; |
1376 | + DbusTestDbusMockObject * jobobj = NULL; |
1377 | + DbusTestDbusMockObject * instobj = NULL; |
1378 | + GDBusConnection * bus = NULL; |
1379 | + |
1380 | + virtual void SetUp() { |
1381 | + g_setenv("PAY_SERVICE_CLICK_DIR", CMAKE_SOURCE_DIR "/click-hook-data", TRUE); |
1382 | + |
1383 | + service = dbus_test_service_new(NULL); |
1384 | + |
1385 | + mock = dbus_test_dbus_mock_new("com.ubuntu.Upstart"); |
1386 | + |
1387 | + obj = dbus_test_dbus_mock_get_object(mock, "/com/ubuntu/Upstart", "com.ubuntu.Upstart0_6", NULL); |
1388 | + |
1389 | + dbus_test_dbus_mock_object_add_method(mock, obj, |
1390 | + "GetJobByName", |
1391 | + G_VARIANT_TYPE_STRING, |
1392 | + G_VARIANT_TYPE_OBJECT_PATH, /* out */ |
1393 | + "ret = dbus.ObjectPath('/job')", /* python */ |
1394 | + NULL); /* error */ |
1395 | + |
1396 | + jobobj = dbus_test_dbus_mock_get_object(mock, "/job", "com.ubuntu.Upstart0_6.Job", NULL); |
1397 | + |
1398 | + dbus_test_dbus_mock_object_add_method(mock, jobobj, |
1399 | + "Start", |
1400 | + G_VARIANT_TYPE("(asb)"), |
1401 | + G_VARIANT_TYPE_OBJECT_PATH, /* out */ |
1402 | + "ret = dbus.ObjectPath('/instance')", /* python */ |
1403 | + NULL); /* error */ |
1404 | + dbus_test_dbus_mock_object_add_method(mock, jobobj, |
1405 | + "GetAllInstances", |
1406 | + NULL, |
1407 | + G_VARIANT_TYPE("ao"), /* out */ |
1408 | + "ret = [dbus.ObjectPath('/instance')]", /* python */ |
1409 | + NULL); /* error */ |
1410 | + dbus_test_dbus_mock_object_add_method(mock, jobobj, |
1411 | + "GetInstanceByName", |
1412 | + G_VARIANT_TYPE_STRING, |
1413 | + G_VARIANT_TYPE_OBJECT_PATH, /* out */ |
1414 | + "ret = dbus.ObjectPath('/instance')", /* python */ |
1415 | + NULL); /* error */ |
1416 | + |
1417 | + instobj = dbus_test_dbus_mock_get_object(mock, "/instance", "com.ubuntu.Upstart0_6.Instance", NULL); |
1418 | + |
1419 | + dbus_test_dbus_mock_object_add_property(mock, instobj, |
1420 | + "processes", |
1421 | + G_VARIANT_TYPE("a(si)"), |
1422 | + g_variant_new_parsed("[('main', 1234)]"), |
1423 | + NULL); |
1424 | + |
1425 | + dbus_test_service_add_task(service, DBUS_TEST_TASK(mock)); |
1426 | + |
1427 | + dbus_test_service_start_tasks(service); |
1428 | + |
1429 | + bus = g_bus_get_sync(G_BUS_TYPE_SESSION, NULL, NULL); |
1430 | + g_dbus_connection_set_exit_on_close(bus, FALSE); |
1431 | + g_object_add_weak_pointer(G_OBJECT(bus), (gpointer *)&bus); |
1432 | + } |
1433 | + |
1434 | + virtual void TearDown() { |
1435 | + g_clear_object(&mock); |
1436 | + g_clear_object(&service); |
1437 | + |
1438 | + g_object_unref(bus); |
1439 | + |
1440 | + unsigned int cleartry = 0; |
1441 | + while (bus != NULL && cleartry < 100) { |
1442 | + g_usleep(100000); |
1443 | + while (g_main_pending()) |
1444 | + g_main_iteration(TRUE); |
1445 | + cleartry++; |
1446 | + } |
1447 | + |
1448 | + ASSERT_LT(cleartry, 100); |
1449 | + } |
1450 | }; |
1451 | |
1452 | TEST_F(PurchaseUALTests, InitTest) { |
1453 | - auto purchase = std::make_shared<Purchase::UalFactory>(); |
1454 | - EXPECT_NE(nullptr, purchase); |
1455 | - purchase.reset(); |
1456 | - EXPECT_EQ(nullptr, purchase); |
1457 | + auto purchase = std::make_shared<Purchase::UalFactory>(); |
1458 | + EXPECT_NE(nullptr, purchase); |
1459 | + purchase.reset(); |
1460 | + EXPECT_EQ(nullptr, purchase); |
1461 | } |
1462 | |
1463 | static gchar * |
1464 | find_env (GVariant * env, const gchar * varname) |
1465 | { |
1466 | - GVariantIter iter; |
1467 | - g_variant_iter_init(&iter, env); |
1468 | - const gchar * entry = NULL; |
1469 | - |
1470 | - while (g_variant_iter_loop(&iter, "&s", &entry)) { |
1471 | - if (g_str_has_prefix(entry, varname)) { |
1472 | - const gchar * value = entry + strlen(varname) + 1; |
1473 | - return g_strdup(value); |
1474 | - } |
1475 | - } |
1476 | - |
1477 | - return NULL; |
1478 | + GVariantIter iter; |
1479 | + g_variant_iter_init(&iter, env); |
1480 | + const gchar * entry = NULL; |
1481 | + |
1482 | + while (g_variant_iter_loop(&iter, "&s", &entry)) { |
1483 | + if (g_str_has_prefix(entry, varname)) { |
1484 | + const gchar * value = entry + strlen(varname) + 1; |
1485 | + return g_strdup(value); |
1486 | + } |
1487 | + } |
1488 | + |
1489 | + return NULL; |
1490 | } |
1491 | |
1492 | TEST_F(PurchaseUALTests, PurchaseTest) { |
1493 | - auto purchase = std::make_shared<Purchase::UalFactory>(); |
1494 | - ASSERT_NE(nullptr, purchase); |
1495 | - |
1496 | - /*** Purchase an item ***/ |
1497 | - std::string appname("click-scope"); |
1498 | - std::string itemname("item"); |
1499 | - auto item = purchase->purchaseItem(appname, itemname); |
1500 | - |
1501 | - ASSERT_NE(nullptr, item); |
1502 | - |
1503 | - Purchase::Item::Status status = Purchase::Item::Status::ERROR; |
1504 | - item->purchaseComplete.connect([&status](Purchase::Item::Status in_status) { |
1505 | - std::cout << "Purchase Status Callback: " << in_status << std::endl; |
1506 | - status = in_status; |
1507 | - }); |
1508 | - |
1509 | - EXPECT_TRUE(item->run()); |
1510 | - usleep(20 * 1000); |
1511 | - |
1512 | - guint callcount = 0; |
1513 | - auto calls = dbus_test_dbus_mock_object_get_method_calls(mock, jobobj, |
1514 | - "Start", &callcount, NULL); |
1515 | - ASSERT_EQ(1, callcount); |
1516 | - |
1517 | - GVariant * env = g_variant_get_child_value(calls->params, 0); |
1518 | - |
1519 | - gchar * helpertype = find_env(env, "HELPER_TYPE"); |
1520 | - EXPECT_STREQ("pay-ui", helpertype); |
1521 | - g_free(helpertype); |
1522 | - |
1523 | - gchar * untrustedappid = find_env(env, "APP_ID"); |
1524 | - EXPECT_STREQ("payuihelper", untrustedappid); |
1525 | - g_free(untrustedappid); |
1526 | - |
1527 | - gchar * instanceid = find_env(env, "INSTANCE_ID"); |
1528 | - dbus_test_dbus_mock_object_emit_signal(mock, obj, "EventEmitted", G_VARIANT_TYPE("(sas)"), g_variant_new_parsed("('stopped', ['JOB=untrusted-helper', 'INSTANCE=pay-ui:%1:payuihelper'])", instanceid), NULL); |
1529 | - g_free(instanceid); |
1530 | - |
1531 | - usleep(20 * 1000); |
1532 | - |
1533 | - EXPECT_EQ(Purchase::Item::Status::PURCHASED, status); |
1534 | + auto purchase = std::make_shared<Purchase::UalFactory>(); |
1535 | + ASSERT_NE(nullptr, purchase); |
1536 | + |
1537 | + /*** Purchase an item ***/ |
1538 | + std::string appname("click-scope"); |
1539 | + std::string itemname("item"); |
1540 | + auto item = purchase->purchaseItem(appname, itemname); |
1541 | + |
1542 | + ASSERT_NE(nullptr, item); |
1543 | + |
1544 | + Purchase::Item::Status status = Purchase::Item::Status::ERROR; |
1545 | + item->purchaseComplete.connect([&status](Purchase::Item::Status in_status) { |
1546 | + std::cout << "Purchase Status Callback: " << in_status << std::endl; |
1547 | + status = in_status; |
1548 | + }); |
1549 | + |
1550 | + EXPECT_TRUE(item->run()); |
1551 | + usleep(20 * 1000); |
1552 | + |
1553 | + guint callcount = 0; |
1554 | + auto calls = dbus_test_dbus_mock_object_get_method_calls(mock, jobobj, |
1555 | + "Start", &callcount, NULL); |
1556 | + ASSERT_EQ(1, callcount); |
1557 | + |
1558 | + GVariant * env = g_variant_get_child_value(calls->params, 0); |
1559 | + |
1560 | + gchar * helpertype = find_env(env, "HELPER_TYPE"); |
1561 | + EXPECT_STREQ("pay-ui", helpertype); |
1562 | + g_free(helpertype); |
1563 | + |
1564 | + gchar * untrustedappid = find_env(env, "APP_ID"); |
1565 | + EXPECT_STREQ("payuihelper", untrustedappid); |
1566 | + g_free(untrustedappid); |
1567 | + |
1568 | + gchar * instanceid = find_env(env, "INSTANCE_ID"); |
1569 | + gchar * gvariantstr = g_strdup_printf("('stopped', ['JOB=untrusted-helper', 'INSTANCE=pay-ui:%s:payuihelper'])", instanceid); |
1570 | + dbus_test_dbus_mock_object_emit_signal(mock, obj, "EventEmitted", G_VARIANT_TYPE("(sas)"), g_variant_new_parsed(gvariantstr), NULL); |
1571 | + g_free(gvariantstr); |
1572 | + g_free(instanceid); |
1573 | + |
1574 | + g_variant_unref(env); |
1575 | + |
1576 | + usleep(20 * 1000); |
1577 | + |
1578 | + EXPECT_EQ(Purchase::Item::Status::PURCHASED, status); |
1579 | } |
PASSED: Continuous integration, rev:47 jenkins. qa.ubuntu. com/job/ pay-service- 15.04-ci/ 4/ jenkins. qa.ubuntu. com/job/ pay-service- 15.04-vivid- amd64-ci/ 4 jenkins. qa.ubuntu. com/job/ pay-service- 15.04-vivid- armhf-ci/ 4 jenkins. qa.ubuntu. com/job/ pay-service- 15.04-vivid- armhf-ci/ 4/artifact/ work/output/ *zip*/output. zip
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
Click here to trigger a rebuild: s-jenkins. ubuntu- ci:8080/ job/pay- service- 15.04-ci/ 4/rebuild
http://