Merge lp:~dobey/pay-service/backport-ual-session into lp:pay-service/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
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-app-launch >= 0.5 for trusted session helper code.

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 }

Subscribers

People subscribed via source and target branches

to all changes: