Merge ~nteodosio/+git/ubuntu-pro:ubuntupro into ~nteodosio/+git/ubuntu-pro:42.2-1ubuntu1-patches-applied

Proposed by Nathan Teodosio
Status: Superseded
Proposed branch: ~nteodosio/+git/ubuntu-pro:ubuntupro
Merge into: ~nteodosio/+git/ubuntu-pro:42.2-1ubuntu1-patches-applied
Diff against target: 1865 lines (+1688/-4) (has conflicts)
16 files modified
data/com.ubuntu.welcome.policy.in (+11/-1)
gnome-initial-setup/gnome-initial-setup.c (+14/-0)
gnome-initial-setup/meson.build (+8/-2)
gnome-initial-setup/pages/account/gis-account-page-local.c (+1/-1)
gnome-initial-setup/pages/apps/gis-apps-page.c (+4/-0)
gnome-initial-setup/pages/meson.build (+5/-0)
gnome-initial-setup/pages/ubuntu-pro/checkmark.svg (+1/-0)
gnome-initial-setup/pages/ubuntu-pro/fail.svg (+1/-0)
gnome-initial-setup/pages/ubuntu-pro/gis-ubuntupro-page-1.ui (+233/-0)
gnome-initial-setup/pages/ubuntu-pro/gis-ubuntupro-page-2.ui (+208/-0)
gnome-initial-setup/pages/ubuntu-pro/gis-ubuntupro-page-3.ui (+172/-0)
gnome-initial-setup/pages/ubuntu-pro/gis-ubuntupro-page.c (+850/-0)
gnome-initial-setup/pages/ubuntu-pro/gis-ubuntupro-page.h (+118/-0)
gnome-initial-setup/pages/ubuntu-pro/gis-ubuntupro-page.ui (+39/-0)
gnome-initial-setup/pages/ubuntu-pro/meson.build (+10/-0)
gnome-initial-setup/pages/ubuntu-pro/ubuntupro.gresource.xml (+13/-0)
Conflict in data/com.ubuntu.welcome.policy.in
Conflict in gnome-initial-setup/gnome-initial-setup.c
Conflict in gnome-initial-setup/meson.build
Conflict in gnome-initial-setup/pages/apps/gis-apps-page.c
Conflict in gnome-initial-setup/pages/meson.build
Reviewer Review Type Date Requested Status
Robert Ancell (community) Needs Fixing
Review via email: mp+434494@code.launchpad.net

Description of the change

1. This has dependencies right for Kinetic as per commit 950144a5[1]. It is atomic, so reverting we can target Jammy.

2. Have Polkit running if you don't: /usr/lib/policykit-1-gnomeY/polkit-gnome-authentication-agent-1.

3. I use this script to test:

---->
#!/bin/sh
if ! [ -d build ]; then
    mkdir build
    meson build
fi
ninja -C build || exit 1
if [ "$1" = debug ]; then
    XDG_CURRENT_DESKTOP=ubuntu gdb -ex run --args ./build/gnome-initial-setup/gnome-initial-setup --existing-user
elif [ "$1" = harddebug ]; then
    G_DEBUG=fatal-warnings GTK_THEME=Adwaita XDG_CURRENT_DESKTOP=ubuntu gdb -ex run --args ./build/gnome-initial-setup/gnome-initial-setup --existing-user
else
    XDG_CURRENT_DESKTOP=ubuntu ./build/gnome-initial-setup/gnome-initial-setup --existing-user
fi
<----

4. This has 42.2-1ubuntu1 as its base, with all its patches applied.

[1]: https://git.launchpad.net/~nteodosio/+git/ubuntu-pro/commit/?id=950144a5b91841d5ae16b086ff2f422fcda97cbb

To post a comment you must log in.
~nteodosio/+git/ubuntu-pro:ubuntupro updated
54ac2c3... by Nathan Teodosio

Remove loading.svg, it was replaced by the spinner.

Revision history for this message
Nathan Teodosio (nteodosio) wrote (last edit ):

You will note that commit "Use one function to update GUI" mirrors the logic in software-properties.

~nteodosio/+git/ubuntu-pro:ubuntupro updated
6802adb... by Nathan Teodosio

UI: Remove no-op strings.

c735f34... by Nathan Teodosio

Remove unused code.

5864713... by Nathan Teodosio

Code style only

06abde8... by Nathan Teodosio

Remove strappend, parse_ua_status reads from file now.

5a6bde9... by Nathan Teodosio

Stop polling after success.

b305f19... by Nathan Teodosio

Handle parse_ua_status, but not display_ua_services.

e91d600... by Nathan Teodosio

Change page 3 bottom label.

Revision history for this message
Robert Ancell (robert-ancell) wrote :

I'm very confused as to what this PR is doing - it's proposing merging two branches you control, not merging into the gnome-initial-setup branch that matches a released version. The information below also shows merge conflicts, so it seems broken.

I would recommend that you squash all the merges together if this is a single change (you can make a backup branch containing all your commits if you want to refer to them later). It is much harder to review with all these commits.

Just looking at you ubuntupro branch and the files in gnome-initial-setup/pages/ubuntu-pro/:
- It would be easier to read if the three pages were in three .c files.
- The pages would be clearer if they had names that related to what they did, i.e. "gs-ubuntupro-page-1" -> "gs-ubuntupro-services-page"
- The .ui files contain unused id properties.
- The .ui files contain unused position properties (these make future changes more complicated).
- RestJSONResponse is only used inside the .c files, but is defined in the header - if this is private it doesn't need to be exposed like this.
- display_ua_services() should use g_autofree/g_autoptr for variables. This will allow you to remove the goto and just return directly from the function.
- Lots of other functions don't use g_autofree/g_autoptr.
- _GisUbuntuProPage1Private contains unused cancellable, apply_complete_callback, apply_complete_data fields.
- contractToken should be called contract_token.
- You store the PIN in a variable and then set it in the GtkLabel - this is unnecessary and would be simpler just to store in the GtkLabel directly. Otherwise you have the same data in two places (which could get out of sync).
- There's no dispose methods for the page classes, so they leak data.
- You've used 543210 as a timeout, if you want no timeout use G_MAXINT.
- The labels set in update_gui() aren't translatable.
- I haven't been able to get it to compile here or seen the designs so I haven't been able to review that.

review: Needs Fixing
Revision history for this message
Nathan Teodosio (nteodosio) wrote :

Please let's us continue on https://code.launchpad.net/~nteodosio/+git/ubuntu-pro/+merge/434628, so as to target the correct version of gnome-initial-setup.

Unmerged commits

e91d600... by Nathan Teodosio

Change page 3 bottom label.

b305f19... by Nathan Teodosio

Handle parse_ua_status, but not display_ua_services.

5a6bde9... by Nathan Teodosio

Stop polling after success.

06abde8... by Nathan Teodosio

Remove strappend, parse_ua_status reads from file now.

5864713... by Nathan Teodosio

Code style only

c735f34... by Nathan Teodosio

Remove unused code.

6802adb... by Nathan Teodosio

UI: Remove no-op strings.

54ac2c3... by Nathan Teodosio

Remove loading.svg, it was replaced by the spinner.

19c9759... by Nathan Teodosio

Use one function to update GUI.

4eba2bb... by Nathan Teodosio

Value initialization for page 2.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/data/com.ubuntu.welcome.policy.in b/data/com.ubuntu.welcome.policy.in
2index cf623d5..3d89089 100644
3--- a/data/com.ubuntu.welcome.policy.in
4+++ b/data/com.ubuntu.welcome.policy.in
5@@ -7,9 +7,15 @@
6 <vendor>Ubuntu Welcome</vendor>
7 <vendor_url>https://www.ubuntu.com/</vendor_url>
8
9+<<<<<<< data/com.ubuntu.welcome.policy.in
10 <action id="com.ubuntu.welcome.livepatch">
11 <description>Manage Livepatch</description>
12 <message>Authentication is required to enable Livepatch</message>
13+=======
14+ <action id="com.ubuntu.welcome.ubuntupro">
15+ <description>Manage Ubuntu Pro</description>
16+ <message>Authentication is required to enable Ubuntu Pro</message>
17+>>>>>>> data/com.ubuntu.welcome.policy.in
18 <defaults>
19 <allow_any>no</allow_any>
20 <allow_inactive>no</allow_inactive>
21@@ -18,4 +24,8 @@
22 <annotate key="org.freedesktop.policykit.imply">io.snapcraft.snapd.login com.ubuntu.softwareproperties.applychanges </annotate>
23 </action>
24
25-</policyconfig>
26\ No newline at end of file
27+<<<<<<< data/com.ubuntu.welcome.policy.in
28+</policyconfig>
29+=======
30+</policyconfig>
31+>>>>>>> data/com.ubuntu.welcome.policy.in
32diff --git a/gnome-initial-setup/gnome-initial-setup.c b/gnome-initial-setup/gnome-initial-setup.c
33index 2078d47..a3a1789 100644
34--- a/gnome-initial-setup/gnome-initial-setup.c
35+++ b/gnome-initial-setup/gnome-initial-setup.c
36@@ -46,7 +46,11 @@
37 #include "pages/password/gis-password-page.h"
38 #include "pages/summary/gis-summary-page.h"
39 #include "pages/ubuntu-report/gis-ubuntu-report-page.h"
40+<<<<<<< gnome-initial-setup/gnome-initial-setup.c
41 #include "pages/livepatch/gis-livepatch-page.h"
42+=======
43+#include "pages/ubuntu-pro/gis-ubuntupro-page.h"
44+>>>>>>> gnome-initial-setup/gnome-initial-setup.c
45 #include "pages/apps/gis-apps-page.h"
46
47 #define VENDOR_PAGES_GROUP "pages"
48@@ -88,8 +92,13 @@ static PageData page_table[] = {
49 };
50
51 static PageData ubuntu_page_table[] = {
52+<<<<<<< gnome-initial-setup/gnome-initial-setup.c
53 PAGE (goa, FALSE),
54 PAGE (livepatch, FALSE),
55+=======
56+ PAGE (ubuntu_pro, FALSE),
57+ PAGE (goa, FALSE),
58+>>>>>>> gnome-initial-setup/gnome-initial-setup.c
59 PAGE (ubuntu_report, FALSE),
60 PAGE (privacy, FALSE),
61 PAGE (account, TRUE),
62@@ -99,8 +108,13 @@ static PageData ubuntu_page_table[] = {
63 };
64
65 static PageData unity_page_table[] = {
66+<<<<<<< gnome-initial-setup/gnome-initial-setup.c
67 PAGE (goa, FALSE),
68 PAGE (livepatch, FALSE),
69+=======
70+ PAGE (ubuntu_pro, FALSE),
71+ PAGE (goa, FALSE),
72+>>>>>>> gnome-initial-setup/gnome-initial-setup.c
73 PAGE (ubuntu_report, FALSE),
74 PAGE (account, TRUE),
75 PAGE (password, TRUE),
76diff --git a/gnome-initial-setup/meson.build b/gnome-initial-setup/meson.build
77index f36c538..450d0a5 100644
78--- a/gnome-initial-setup/meson.build
79+++ b/gnome-initial-setup/meson.build
80@@ -42,19 +42,25 @@ dependencies = [
81 dependency ('glib-2.0', version: '>= 2.63.1'),
82 dependency ('gio-unix-2.0', version: '>= 2.53.0'),
83 dependency ('gdm', version: '>= 3.8.3'),
84- dependency ('geocode-glib-1.0'),
85+ dependency ('geocode-glib-2.0'),
86 dependency ('libgeoclue-2.0', version: '>= 2.3.1'),
87 dependency ('libhandy-1', version: '>= 1.5.90', fallback: ['libhandy', 'libhandy_dep']),
88 cc.find_library('m', required: false),
89 dependency ('pango', version: '>= 1.32.5'),
90- dependency ('rest-0.7'),
91+ dependency ('rest-1.0'),
92 dependency ('json-glib-1.0'),
93 dependency ('krb5'),
94 dependency ('libsecret-1', version: '>= 0.18.8'),
95 dependency ('pwquality'),
96+<<<<<<< gnome-initial-setup/meson.build
97 dependency ('webkit2gtk-4.0', version: '>= 2.26.0'),
98 dependency ('snapd-glib'),
99 dependency ('libsoup-2.4'),
100+=======
101+ dependency ('webkit2gtk-5.0'),
102+ dependency ('snapd-glib-2'),
103+ dependency ('libsoup-3.0'),
104+>>>>>>> gnome-initial-setup/meson.build
105 dependency ('sysmetrics'),
106 cheese_dep,
107 cheese_gtk_dep,
108diff --git a/gnome-initial-setup/pages/account/gis-account-page-local.c b/gnome-initial-setup/pages/account/gis-account-page-local.c
109index c858c60..ff8f70e 100644
110--- a/gnome-initial-setup/pages/account/gis-account-page-local.c
111+++ b/gnome-initial-setup/pages/account/gis-account-page-local.c
112@@ -38,7 +38,7 @@
113 #define GOA_API_IS_SUBJECT_TO_CHANGE
114 #include <goa/goa.h>
115
116-#include <rest/oauth-proxy.h>
117+#include <rest/rest-oauth2-proxy.h>
118 #include <json-glib/json-glib.h>
119
120 #define VALIDATION_TIMEOUT 600
121diff --git a/gnome-initial-setup/pages/apps/gis-apps-page.c b/gnome-initial-setup/pages/apps/gis-apps-page.c
122index 6c0bd47..9e5eac9 100644
123--- a/gnome-initial-setup/pages/apps/gis-apps-page.c
124+++ b/gnome-initial-setup/pages/apps/gis-apps-page.c
125@@ -374,7 +374,11 @@ load_store_icon (GisSnapTile *tile)
126 return;
127
128 message = soup_message_new ("GET", url);
129+<<<<<<< gnome-initial-setup/pages/apps/gis-apps-page.c
130 soup_session_send_async (tile->soup_session, message, NULL, icon_cb, tile);
131+=======
132+ soup_session_send_async (tile->soup_session, message, 0, NULL, icon_cb, tile);
133+>>>>>>> gnome-initial-setup/pages/apps/gis-apps-page.c
134 }
135
136 static void
137diff --git a/gnome-initial-setup/pages/meson.build b/gnome-initial-setup/pages/meson.build
138index 32f615b..49cfd04 100644
139--- a/gnome-initial-setup/pages/meson.build
140+++ b/gnome-initial-setup/pages/meson.build
141@@ -10,8 +10,13 @@ pages = [
142 'software',
143 'summary',
144 'welcome',
145+<<<<<<< gnome-initial-setup/pages/meson.build
146 'ubuntu-report',
147 'livepatch',
148+=======
149+ 'ubuntu-pro',
150+ 'ubuntu-report',
151+>>>>>>> gnome-initial-setup/pages/meson.build
152 'apps',
153 ]
154
155diff --git a/gnome-initial-setup/pages/ubuntu-pro/checkmark.svg b/gnome-initial-setup/pages/ubuntu-pro/checkmark.svg
156new file mode 100644
157index 0000000..a0c8602
158--- /dev/null
159+++ b/gnome-initial-setup/pages/ubuntu-pro/checkmark.svg
160@@ -0,0 +1 @@
161+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><g fill="none" fill-rule="nonzero"><path fill="#0e8420" d="M8 1a7 7 0 110 14A7 7 0 018 1zm2.83 3.502L6.863 9.884 5.174 8.096l-1.09 1.03 2.92 3.096 5.034-6.83-1.208-.89z"/><path fill="#fff" d="M10.83 4.502l1.208.89-5.033 6.83-2.922-3.096 1.091-1.03 1.689 1.789z"/></g></svg>
162\ No newline at end of file
163diff --git a/gnome-initial-setup/pages/ubuntu-pro/fail.svg b/gnome-initial-setup/pages/ubuntu-pro/fail.svg
164new file mode 100644
165index 0000000..e3c9a52
166--- /dev/null
167+++ b/gnome-initial-setup/pages/ubuntu-pro/fail.svg
168@@ -0,0 +1 @@
169+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="16" height="16"><defs><filter id="darkreader-image-filter"><feColorMatrix type="matrix" values="-0.193 -0.490 -1.120 0.000 1.698 -0.965 0.293 -1.105 0.000 1.673 -0.946 -0.489 -0.296 0.000 1.630 0.000 0.000 0.000 1.000 0.000"/></filter></defs><image width="16" height="16" filter="url(#darkreader-image-filter)" xlink:href="data:image/svg+xml,%3Csvg width='16' height='16' xmlns='http://www.w3.org/2000/svg'%3E%3Cg fill='none' fill-rule='evenodd'%3E%3Ccircle stroke='%23c7162b' stroke-width='1.5' fill='%23c7162b' cx='8' cy='8' r='6.25'/%3E%3Cpath fill='%23FFF' fill-rule='nonzero' d='M10.282 4.638l1.06 1.06L9.05 7.99l2.293 2.292-1.06 1.06L7.99 9.05 5.7 11.343l-1.06-1.06 2.29-2.293L4.64 5.7l1.06-1.06 2.291 2.29z'/%3E%3C/g%3E%3C/svg%3E"/></svg>
170\ No newline at end of file
171diff --git a/gnome-initial-setup/pages/ubuntu-pro/gis-ubuntupro-page-1.ui b/gnome-initial-setup/pages/ubuntu-pro/gis-ubuntupro-page-1.ui
172new file mode 100644
173index 0000000..dc1fdef
174--- /dev/null
175+++ b/gnome-initial-setup/pages/ubuntu-pro/gis-ubuntupro-page-1.ui
176@@ -0,0 +1,233 @@
177+<?xml version="1.0" encoding="UTF-8"?>
178+<interface>
179+ <!-- interface-requires gtk+ 3.0 -->
180+ <template class="GisUbuntuProPage1" parent="GtkBin">
181+ <child>
182+ <object class="GtkBox" id="page1">
183+ <property name="visible">True</property>
184+ <property name="can-focus">False</property>
185+ <property name="no-show-all">True</property>
186+ <property name="orientation">vertical</property>
187+ <child>
188+ <object class="GtkLabel" id="label_ubuntu_pro">
189+ <property name="visible">True</property>
190+ <property name="can-focus">False</property>
191+ <property name="halign">start</property>
192+ <property name="margin-top">3</property>
193+ <property name="margin-bottom">0</property>
194+ <property name="label" translatable="yes">&lt;b&gt;Enable Ubuntu Pro&lt;/b&gt;</property>
195+ <property name="use-markup">True</property>
196+ <property name="wrap">True</property>
197+ <property name="xalign">0</property>
198+ <attributes>
199+ <attribute name="scale" value="1.4"/>
200+ </attributes>
201+ </object>
202+ <packing>
203+ <property name="expand">False</property>
204+ <property name="fill">True</property>
205+ <property name="position">0</property>
206+ </packing>
207+ </child>
208+ <child>
209+ <object class="GtkSeparator">
210+ <property name="visible">True</property>
211+ <property name="can-focus">False</property>
212+ <property name="margin-top">10</property>
213+ <property name="margin-bottom">10</property>
214+ </object>
215+ <packing>
216+ <property name="expand">False</property>
217+ <property name="fill">False</property>
218+ <property name="position">1</property>
219+ </packing>
220+ </child>
221+ <child>
222+ <object class="GtkEventBox" id="ubuntu_pro_eventbox">
223+ <property name="visible">True</property>
224+ <property name="can-focus">False</property>
225+ <property name="margin-top">12</property>
226+ <property name="margin-bottom">12</property>
227+ <child>
228+ <object class="GtkBox">
229+ <property name="visible">True</property>
230+ <property name="can-focus">False</property>
231+ <child>
232+ <object class="GtkImage">
233+ <property name="visible">True</property>
234+ <property name="can-focus">False</property>
235+ <property name="margin-left">30</property>
236+ <property name="margin-right">30</property>
237+ <property name="yalign">0</property>
238+ <property name="resource">/org/gnome/initial-setup/ubuntu-pro.svg</property>
239+ </object>
240+ <packing>
241+ <property name="expand">False</property>
242+ <property name="fill">False</property>
243+ <property name="position">0</property>
244+ </packing>
245+ </child>
246+ <child>
247+ <object class="GtkLabel">
248+ <property name="visible">True</property>
249+ <property name="can-focus">True</property>
250+ <property name="label" translatable="yes">Upgrade this machine to Ubuntu Pro for security updates on a much wider range of packages, until 2032. Fulfill FedRAMP, FIPS, STIG and HIPAA and other compliance and hardening requirements with certified tooling and crypto-modules. Free up to 5 machines.
251+
252+More information on &lt;a href="https://ubuntu.com/pro"&gt;ubuntu.com/pro&lt;/a&gt;.</property>
253+ <property name="use-markup">True</property>
254+ <property name="wrap">True</property>
255+ <property name="max-width-chars">130</property>
256+ </object>
257+ <packing>
258+ <property name="expand">False</property>
259+ <property name="fill">True</property>
260+ <property name="position">1</property>
261+ </packing>
262+ </child>
263+ </object>
264+ </child>
265+ </object>
266+ <packing>
267+ <property name="expand">False</property>
268+ <property name="fill">True</property>
269+ <property name="position">2</property>
270+ </packing>
271+ </child>
272+ <child>
273+ <object class="GtkLabel">
274+ <property name="visible">True</property>
275+ <property name="can-focus">False</property>
276+ <property name="margin-top">6</property>
277+ <property name="margin-bottom">6</property>
278+ <property name="label" translatable="yes">Enable Ubuntu Pro for this installation or skip this step.</property>
279+ <property name="xalign">0</property>
280+ </object>
281+ <packing>
282+ <property name="expand">False</property>
283+ <property name="fill">True</property>
284+ <property name="position">3</property>
285+ </packing>
286+ </child>
287+ <child>
288+ <object class="GtkBox">
289+ <property name="visible">True</property>
290+ <property name="can-focus">False</property>
291+ <property name="orientation">vertical</property>
292+ <property name="spacing">6</property>
293+ <child>
294+ <object class="GtkBox">
295+ <property name="visible">True</property>
296+ <property name="can-focus">False</property>
297+ <property name="orientation">horizontal</property>
298+ <child>
299+ <object class="GtkRadioButton" id="enable_pro_select">
300+ <property name="label" translatable="yes">Enable Ubuntu Pro</property>
301+ <property name="visible">True</property>
302+ <property name="can-focus">True</property>
303+ <property name="receives-default">False</property>
304+ <property name="margin-top">6</property>
305+ <property name="xalign">0</property>
306+ <property name="draw-indicator">True</property>
307+ <property name="group">skip_pro_select</property>
308+ </object>
309+ <packing>
310+ <property name="expand">False</property>
311+ <property name="fill">True</property>
312+ <property name="position">0</property>
313+ </packing>
314+ </child>
315+ <child>
316+ <object class="GtkImage" id="pro_status_image">
317+ <property name="can-focus">False</property>
318+ <property name="stock">gtk-dialog-warning</property>
319+ <property name="icon_size">2</property>
320+ <property name="margin-left">30</property>
321+ <property name="margin-right">6</property>
322+ <property name="yalign">0.7</property>
323+ </object>
324+ <packing>
325+ <property name="expand">False</property>
326+ <property name="fill">True</property>
327+ <property name="position">1</property>
328+ </packing>
329+ </child>
330+ <child>
331+ <object class="GtkLabel" id="offline_warning">
332+ <property name="yalign">0.8</property>
333+ <property name="can-focus">False</property>
334+ <property name="label" translatable="yes">An internet connection is required to enable Ubuntu Pro</property>
335+ </object>
336+ <packing>
337+ <property name="expand">False</property>
338+ <property name="fill">True</property>
339+ <property name="position">2</property>
340+ </packing>
341+ </child>
342+ </object>
343+ <packing>
344+ <property name="expand">False</property>
345+ <property name="fill">True</property>
346+ <property name="position">0</property>
347+ </packing>
348+ </child>
349+ <child>
350+ <object class="GtkBox">
351+ <property name="visible">True</property>
352+ <property name="can-focus">False</property>
353+ <property name="orientation">vertical</property>
354+ <child>
355+ <object class="GtkRadioButton" id="skip_pro_select">
356+ <property name="label" translatable="yes">Skip for now</property>
357+ <property name="visible">True</property>
358+ <property name="can-focus">True</property>
359+ <property name="receives-default">False</property>
360+ <property name="margin-top">12</property>
361+ <property name="xalign">0</property>
362+ <property name="active">True</property>
363+ <property name="draw-indicator">True</property>
364+ </object>
365+ <packing>
366+ <property name="expand">False</property>
367+ <property name="fill">True</property>
368+ <property name="position">0</property>
369+ </packing>
370+ </child>
371+ <child>
372+ <object class="GtkAlignment">
373+ <property name="visible">True</property>
374+ <property name="can-focus">False</property>
375+ <property name="left-padding">25</property>
376+ <child>
377+ <object class="GtkLabel">
378+ <property name="visible">True</property>
379+ <property name="can-focus">False</property>
380+ <property name="label" translatable="yes">&lt;small&gt;You can always enable Ubuntu Pro later via the 'pro attach' command&lt;/small&gt;</property>
381+ <property name="use-markup">True</property>
382+ <property name="xalign">0</property>
383+ </object>
384+ </child>
385+ </object>
386+ <packing>
387+ <property name="expand">False</property>
388+ <property name="fill">True</property>
389+ <property name="position">1</property>
390+ </packing>
391+ </child>
392+ </object>
393+ <packing>
394+ <property name="expand">False</property>
395+ <property name="fill">True</property>
396+ <property name="position">1</property>
397+ </packing>
398+ </child>
399+ </object>
400+ <packing>
401+ <property name="expand">False</property>
402+ <property name="fill">True</property>
403+ <property name="position">4</property>
404+ </packing>
405+ </child>
406+ </object>
407+ </child>
408+ </template>
409+</interface>
410diff --git a/gnome-initial-setup/pages/ubuntu-pro/gis-ubuntupro-page-2.ui b/gnome-initial-setup/pages/ubuntu-pro/gis-ubuntupro-page-2.ui
411new file mode 100644
412index 0000000..e0a22f9
413--- /dev/null
414+++ b/gnome-initial-setup/pages/ubuntu-pro/gis-ubuntupro-page-2.ui
415@@ -0,0 +1,208 @@
416+<?xml version="1.0" encoding="UTF-8"?>
417+<interface>
418+ <!-- interface-requires gtk+ 3.0 -->
419+ <template class="GisUbuntuProPage2" parent="GtkBin">
420+ <child>
421+ <object class="GtkBox" id="page2">
422+ <property name="visible">True</property>
423+ <property name="can-focus">False</property>
424+ <property name="no-show-all">True</property>
425+ <property name="orientation">vertical</property>
426+ <child>
427+ <object class="GtkLabel">
428+ <property name="visible">True</property>
429+ <property name="can-focus">False</property>
430+ <property name="halign">start</property>
431+ <property name="margin-top">3</property>
432+ <property name="margin-bottom">0</property>
433+ <property name="label" translatable="yes">&lt;b&gt;Enable Ubuntu Pro&lt;/b&gt;</property>
434+ <property name="use-markup">True</property>
435+ <property name="wrap">True</property>
436+ <property name="xalign">0</property>
437+ <attributes>
438+ <attribute name="scale" value="1.4"/>
439+ </attributes>
440+ </object>
441+ </child>
442+ <child>
443+ <object class="GtkSeparator">
444+ <property name="visible">True</property>
445+ <property name="can-focus">False</property>
446+ <property name="margin-top">10</property>
447+ <property name="margin-bottom">10</property>
448+ </object>
449+ </child>
450+ <child>
451+ <object class="GtkEventBox">
452+ <property name="visible">True</property>
453+ <property name="can-focus">False</property>
454+ <property name="margin-top">12</property>
455+ <property name="margin-bottom">12</property>
456+ <child>
457+ <object class="GtkBox">
458+ <property name="visible">True</property>
459+ <property name="can-focus">False</property>
460+ <child>
461+ <object class="GtkImage">
462+ <property name="visible">True</property>
463+ <property name="can-focus">False</property>
464+ <property name="margin-left">30</property>
465+ <property name="margin-right">30</property>
466+ <property name="yalign">0</property>
467+ <property name="resource">/org/gnome/initial-setup/ubuntu-pro.svg</property>
468+ </object>
469+ </child>
470+ <child>
471+ <object class="GtkLabel">
472+ <property name="visible">True</property>
473+ <property name="can-focus">True</property>
474+ <property name="label" translatable="yes">Upgrade this machine to Ubuntu Pro for security updates on a much wider range of packages, until 2032. Fulfill FedRAMP, FIPS, STIG and HIPAA and other compliance and hardening requirements with certified tooling and crypto-modules. Free up to 5 machines.
475+
476+More information on &lt;a href="https://ubuntu.com/pro"&gt;ubuntu.com/pro&lt;/a&gt;.</property>
477+ <property name="use-markup">True</property>
478+ <property name="wrap">True</property>
479+ <property name="max-width-chars">130</property>
480+ </object>
481+ </child>
482+ </object>
483+ </child>
484+ </object>
485+ </child>
486+ <child>
487+ <object class="GtkRadioButton" id="magic_radio">
488+ <property name="label" translatable="yes">Enter code on ubuntu.com/pro/attach</property>
489+ <property name="visible">True</property>
490+ <property name="can-focus">True</property>
491+ <property name="receives-default">False</property>
492+ <property name="margin-top">6</property>
493+ <property name="xalign">0</property>
494+ <property name="group">magic_radio</property>
495+ <signal name="clicked" handler="on_magic_clicked" swapped="no"/>
496+ </object>
497+ </child>
498+ <child>
499+ <object class="GtkBox">
500+ <property name="visible">True</property>
501+ <property name="can-focus">False</property>
502+ <child>
503+ <object class="GtkLabel" id="pin_label">
504+ <property name="selectable">True</property>
505+ <property name="visible">True</property>
506+ <property name="can-focus">False</property>
507+ <property name="margin-top">12</property>
508+ <property name="margin-bottom">8</property>
509+ <property name="halign">start</property>
510+ <property name="margin-left">20</property>
511+ <attributes>
512+ <attribute name="scale" value="2"/>
513+ </attributes>
514+ </object>
515+ </child>
516+ <child>
517+ <object class="GtkFixed">
518+ <property name="visible">True</property>
519+ <property name="valign">center</property>
520+ <property name="margin">6</property>
521+ <child>
522+ <object class="GtkSpinner" id="pin_spinner">
523+ <property name="visible">True</property>
524+ <property name="valign">center</property>
525+ </object>
526+ </child>
527+ <child>
528+ <object class="GtkImage" id="pin_status_icon">
529+ <property name="visible">False</property>
530+ <property name="valign">center</property>
531+ </object>
532+ </child>
533+ </object>
534+ </child>
535+ <child>
536+ <object class="GtkLabel" id="pin_status">
537+ <property name="visible">True</property>
538+ <property name="use-markup">True</property>
539+ <property name="halign">start</property>
540+ </object>
541+ </child>
542+ </object>
543+ </child>
544+ <child>
545+ <object class="GtkLabel" id="pin_hint">
546+ <property name="visible">True</property>
547+ <property name="can-focus">False</property>
548+ <property name="margin-top">6</property>
549+ <property name="margin-bottom">8</property>
550+ <property name="halign">start</property>
551+ <property name="margin-left">20</property>
552+ </object>
553+ </child>
554+ <child>
555+ <object class="GtkRadioButton" id="token_radio">
556+ <property name="label" translatable="yes">Or add token manually</property>
557+ <property name="visible">True</property>
558+ <property name="can-focus">True</property>
559+ <property name="receives-default">False</property>
560+ <property name="margin-top">6</property>
561+ <property name="xalign">0</property>
562+ <property name="group">magic_radio</property>
563+ <signal name="toggled" handler="on_radio_toggled" swapped="no"/>
564+ </object>
565+ </child>
566+ <child>
567+ <object class="GtkBox">
568+ <property name="visible">True</property>
569+ <property name="can-focus">False</property>
570+ <property name="no-show-all">True</property>
571+ <property name="margin-left">20</property>
572+ <property name="margin-top">12</property>
573+ <property name="margin-bottom">12</property>
574+ <child>
575+ <object class="GtkEntry" id="token_field">
576+ <property name="visible">True</property>
577+ <property name="sensitive">False</property>
578+ <property name="can-focus">True</property>
579+ <property name="width-chars">35</property>
580+ <property name="placeholder-text" translatable="yes">Token</property>
581+ <property name="valign">center</property>
582+ <signal name="activate" handler="request_token_attach" swapped="no"/>
583+ </object>
584+ </child>
585+ <child>
586+ <object class="GtkFixed">
587+ <property name="visible">True</property>
588+ <property name="valign">center</property>
589+ <property name="margin">6</property>
590+ <child>
591+ <object class="GtkSpinner" id="token_spinner">
592+ <property name="visible">True</property>
593+ <property name="valign">center</property>
594+ </object>
595+ </child>
596+ <child>
597+ <object class="GtkImage" id="token_status_icon">
598+ <property name="visible">False</property>
599+ <property name="valign">center</property>
600+ </object>
601+ </child>
602+ </object>
603+ </child>
604+ <child>
605+ <object class="GtkLabel" id="token_status">
606+ <property name="visible">True</property>
607+ <property name="valign">center</property>
608+ </object>
609+ </child>
610+ </object>
611+ </child>
612+ <child>
613+ <object class="GtkLabel">
614+ <property name="visible">True</property>
615+ <property name="label" translatable="yes">From your admin, or from ubuntu.com/pro</property>
616+ <property name="margin-left">22</property>
617+ <property name="halign">start</property>
618+ </object>
619+ </child>
620+ </object>
621+ </child>
622+</template>
623+</interface>
624diff --git a/gnome-initial-setup/pages/ubuntu-pro/gis-ubuntupro-page-3.ui b/gnome-initial-setup/pages/ubuntu-pro/gis-ubuntupro-page-3.ui
625new file mode 100644
626index 0000000..0a2b9b6
627--- /dev/null
628+++ b/gnome-initial-setup/pages/ubuntu-pro/gis-ubuntupro-page-3.ui
629@@ -0,0 +1,172 @@
630+<?xml version="1.0" encoding="UTF-8"?>
631+<interface>
632+ <!-- interface-requires gtk+ 3.0 -->
633+ <template class="GisUbuntuProPage3" parent="GtkBin">
634+ <child>
635+ <object class="GtkBox" id="page3">
636+ <property name="visible">True</property>
637+ <property name="can-focus">False</property>
638+ <property name="no-show-all">True</property>
639+ <property name="orientation">vertical</property>
640+ <child>
641+ <object class="GtkLabel">
642+ <property name="visible">True</property>
643+ <property name="can-focus">False</property>
644+ <property name="halign">start</property>
645+ <property name="margin-top">3</property>
646+ <property name="margin-bottom">0</property>
647+ <property name="label" translatable="yes">&lt;b&gt;Ubuntu Pro Services&lt;/b&gt;</property>
648+ <property name="use-markup">True</property>
649+ <property name="wrap">True</property>
650+ <property name="xalign">0</property>
651+ <attributes>
652+ <attribute name="scale" value="1.4"/>
653+ </attributes>
654+ </object>
655+ <packing>
656+ <property name="expand">False</property>
657+ <property name="fill">True</property>
658+ <property name="position">0</property>
659+ </packing>
660+ </child>
661+ <child>
662+ <object class="GtkSeparator">
663+ <property name="visible">True</property>
664+ <property name="can-focus">False</property>
665+ <property name="margin-top">10</property>
666+ <property name="margin-bottom">10</property>
667+ </object>
668+ <packing>
669+ <property name="expand">False</property>
670+ <property name="fill">False</property>
671+ <property name="position">1</property>
672+ </packing>
673+ </child>
674+ <child>
675+ <object class="GtkBox">
676+ <property name="visible">True</property>
677+ <property name="can-focus">False</property>
678+ <property name="no-show-all">True</property>
679+ <child>
680+ <object class="GtkImage" id="checkmark">
681+ <property name="visible">True</property>
682+ <property name="can-focus">False</property>
683+ <property name="margin-left">10</property>
684+ <property name="margin-right">15</property>
685+ <property name="yalign">0</property>
686+ </object>
687+ <packing>
688+ <property name="expand">False</property>
689+ <property name="fill">False</property>
690+ <property name="position">0</property>
691+ </packing>
692+ </child>
693+ <child>
694+ <object class="GtkBox">
695+ <property name="visible">True</property>
696+ <property name="can-focus">False</property>
697+ <property name="no-show-all">True</property>
698+ <property name="orientation">vertical</property>
699+ <child>
700+ <object class="GtkLabel" id="subscription">
701+ <property name="label" translatable="yes">Your subscription:</property>
702+ <property name="margin-bottom">10</property>
703+ <property name="visible">True</property>
704+ <property name="halign">start</property>
705+ <attributes>
706+ <attribute name="scale" value="1.5"/>
707+ </attributes>
708+ </object>
709+ <packing>
710+ <property name="expand">False</property>
711+ <property name="fill">False</property>
712+ <property name="position">5</property>
713+ </packing>
714+ </child>
715+ <child>
716+ <object class="GtkLabel" id="contract_name">
717+ <property name="visible">True</property>
718+ <property name="margin-bottom">15</property>
719+ <property name="halign">start</property>
720+ </object>
721+ <packing>
722+ <property name="expand">False</property>
723+ <property name="fill">False</property>
724+ <property name="position">5</property>
725+ </packing>
726+ </child>
727+ <child>
728+ <object class="GtkLabel" id="enabled_services_header">
729+ <property name="label" translatable="yes">List of your enabled services:</property>
730+ <property name="margin-bottom">10</property>
731+ <property name="visible">True</property>
732+ <property name="halign">start</property>
733+ <attributes>
734+ <attribute name="scale" value="1.5"/>
735+ </attributes>
736+ </object>
737+ <packing>
738+ <property name="expand">False</property>
739+ <property name="fill">False</property>
740+ <property name="position">5</property>
741+ </packing>
742+ </child>
743+ <child>
744+ <object class="GtkLabel" id="enabled_services">
745+ <property name="visible">True</property>
746+ <property name="halign">start</property>
747+ </object>
748+ <packing>
749+ <property name="expand">False</property>
750+ <property name="fill">False</property>
751+ <property name="position">4</property>
752+ </packing>
753+ </child>
754+ <child>
755+ <object class="GtkLabel" id="available_services_header">
756+ <property name="label" translatable="yes">Other available services:</property>
757+ <property name="margin-bottom">10</property>
758+ <property name="visible">True</property>
759+ <property name="halign">start</property>
760+ <attributes>
761+ <attribute name="scale" value="1.5"/>
762+ </attributes>
763+ </object>
764+ <packing>
765+ <property name="expand">False</property>
766+ <property name="fill">False</property>
767+ <property name="position">5</property>
768+ </packing>
769+ </child>
770+ <child>
771+ <object class="GtkLabel" id="available_services">
772+ <property name="visible">True</property>
773+ <property name="halign">start</property>
774+ </object>
775+ <packing>
776+ <property name="expand">False</property>
777+ <property name="fill">False</property>
778+ <property name="position">6</property>
779+ </packing>
780+ </child>
781+ </object>
782+ </child>
783+ </object>
784+ </child>
785+ <child>
786+ <object class="GtkLabel">
787+ <property name="label" translatable="yes">You can start using Ubuntu Pro features once Ubuntu has finished installing. If you want to change the default enablements for your token you can do so via the ubuntu.com/pro web interface. Alternatively you can change enabled services by going to the Settings menu or the `ua` command-line.</property>
788+ <property name="visible">True</property>
789+ <property name="wrap">True</property>
790+ <property name="halign">start</property>
791+ </object>
792+ <packing>
793+ <property name="expand">False</property>
794+ <property name="fill">False</property>
795+ <property name="position">9</property>
796+ </packing>
797+ </child>
798+ </object>
799+ </child>
800+ </template>
801+</interface>
802diff --git a/gnome-initial-setup/pages/ubuntu-pro/gis-ubuntupro-page.c b/gnome-initial-setup/pages/ubuntu-pro/gis-ubuntupro-page.c
803new file mode 100644
804index 0000000..7a1f940
805--- /dev/null
806+++ b/gnome-initial-setup/pages/ubuntu-pro/gis-ubuntupro-page.c
807@@ -0,0 +1,850 @@
808+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
809+/*
810+ * Copyright (C) 2022 Canonical Ltd.
811+ *
812+ * This program is free software; you can redistribute it and/or
813+ * modify it under the terms of the GNU General Public License as
814+ * published by the Free Software Foundation; either version 2 of the
815+ * License, or (at your option) any later version.
816+ *
817+ * This program is distributed in the hope that it will be useful, but
818+ * WITHOUT ANY WARRANTY; without even the implied warranty of
819+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
820+ * General Public License for more details.
821+ *
822+ * You should have received a copy of the GNU General Public License
823+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
824+ */
825+
826+/* Canonical Ubuntu Pro page {{{1 */
827+
828+#define PAGE_ID "UbuntuPro"
829+
830+#include "config.h"
831+#include "gis-ubuntupro-page.h"
832+#include "ubuntupro-resources.h"
833+
834+#include <glib/gi18n.h>
835+#include <gio/gio.h>
836+#include <polkit/polkit.h>
837+#include <json-glib/json-glib.h>
838+#include <libsoup/soup.h>
839+#include <time.h>
840+
841+static gboolean SuccessfullyAttached = FALSE;
842+
843+typedef enum {
844+ NONE = 0,
845+ SUCCESS,
846+ FAIL,
847+ EXPIRED,
848+} status_t;
849+
850+struct _GisUbuntuProPagePrivate {
851+ GtkWidget *page1;
852+ GtkWidget *page2;
853+ GtkWidget *page3;
854+ GtkWidget *stack;
855+
856+ guint current_page;
857+};
858+typedef struct _GisUbuntuProPagePrivate GisUbuntuProPagePrivate;
859+
860+struct _GisUbuntuProPage1Private {
861+ GtkWidget *enable_pro_select;
862+ GtkWidget *skip_pro_select;
863+ GtkWidget *offline_warning;
864+ GtkWidget *pro_status_image;
865+
866+ GCancellable *cancellable;
867+ GisPageApplyCallback apply_complete_callback;
868+ gpointer apply_complete_data;
869+};
870+typedef struct _GisUbuntuProPage1Private GisUbuntuProPage1Private;
871+
872+struct _GisUbuntuProPage2Private {
873+ GtkWidget *pin_label;
874+ GtkWidget *token_field;
875+ GtkWidget *token_status;
876+ GtkWidget *token_spinner;
877+ GtkWidget *pin_spinner;
878+ GtkWidget *token_status_icon;
879+ GtkWidget *pin_hint;
880+ GtkWidget *pin_status;
881+ GtkWidget *pin_status_icon;
882+ GtkWidget *token_radio;
883+ GtkWidget *magic_radio;
884+ /* This being here is a hack that makes changing the 2nd page's button
885+ * from Skip to Next once the machine is attached possible. */
886+ GisUbuntuProPage *main_page;
887+
888+ gboolean magic_token_polling;
889+ gboolean attaching;
890+ gint64 expired_by;
891+ guint poll_id;
892+ enum active_radio_t {
893+ MAGIC,
894+ TOKEN,
895+ } active_radio;
896+ gchar *contractToken;
897+ gchar *token;
898+ gchar *pin;
899+};
900+typedef struct _GisUbuntuProPage2Private GisUbuntuProPage2Private;
901+
902+struct _GisUbuntuProPage3Private {
903+ GtkWidget *enabled_services;
904+ GtkWidget *enabled_services_header;
905+ GtkWidget *available_services;
906+ GtkWidget *available_services_header;
907+ GtkWidget *contract_name;
908+ GtkWidget *checkmark;
909+};
910+typedef struct _GisUbuntuProPage3Private GisUbuntuProPage3Private;
911+
912+static void ua_attach(const gchar *, GisUbuntuProPage2Private *);
913+static gboolean magic_parser(void*, size_t, RestJSONResponse*);
914+
915+G_DEFINE_TYPE_WITH_PRIVATE (GisUbuntuProPage, gis_ubuntupro_page, GIS_TYPE_PAGE);
916+G_DEFINE_TYPE_WITH_PRIVATE (GisUbuntuProPage1, gis_ubuntupro_page1, GTK_TYPE_BIN);
917+G_DEFINE_TYPE_WITH_PRIVATE (GisUbuntuProPage2, gis_ubuntupro_page2, GTK_TYPE_BIN);
918+G_DEFINE_TYPE_WITH_PRIVATE (GisUbuntuProPage3, gis_ubuntupro_page3, GTK_TYPE_BIN);
919+
920+static void
921+update_gui(GisUbuntuProPage2Private *priv, status_t status)
922+{
923+ GtkWidget *label, *icon, *spinner;
924+ gchar *hint = "Attach machine via your Ubuntu One account";
925+
926+ if (priv->active_radio == MAGIC) {
927+ gtk_label_set_text (GTK_LABEL (priv->pin_label), priv->pin);
928+ icon = priv->pin_status_icon;
929+ label = priv->pin_status;
930+ spinner = priv->pin_spinner;
931+ } else {
932+ icon = priv->token_status_icon;
933+ label = priv->token_status;
934+ spinner = priv->token_spinner;
935+ }
936+
937+ if (priv->attaching) {
938+ gtk_spinner_start (GTK_SPINNER (spinner));
939+ } else {
940+ gtk_spinner_stop (GTK_SPINNER (spinner));
941+ }
942+
943+ gtk_widget_set_sensitive (priv->token_radio, !priv->attaching);
944+ gtk_widget_set_sensitive (priv->magic_radio, !priv->attaching);
945+ gtk_widget_set_sensitive (priv->token_field, !priv->attaching &&
946+ priv->active_radio == TOKEN);
947+
948+ switch (status) {
949+ case SUCCESS:
950+ gtk_label_set_markup(GTK_LABEL(label), "<span foreground=\"green\"><b>Valid token</b></span>");
951+ gtk_image_set_from_resource(GTK_IMAGE(icon), "/org/gnome/initial-setup/checkmark.svg");
952+ gtk_widget_set_sensitive(GTK_WIDGET(priv->token_field), FALSE);
953+ gtk_widget_set_sensitive(GTK_WIDGET(priv->magic_radio), FALSE);
954+ gtk_widget_set_sensitive(GTK_WIDGET(priv->token_radio), FALSE);
955+ break;
956+ case FAIL:
957+ gtk_label_set_markup(GTK_LABEL(label), "<span foreground=\"red\"><b>Invalid token</b></span>");
958+ gtk_image_set_from_resource(GTK_IMAGE(icon), "/org/gnome/initial-setup/fail.svg");
959+ break;
960+ case EXPIRED:
961+ hint = "Click the button to generate a new code.";
962+ gtk_label_set_markup(GTK_LABEL(label), "Code expired");
963+ gtk_image_set_from_resource(GTK_IMAGE(icon), "/org/gnome/initial-setup/fail.svg");
964+ break;
965+ default:
966+ break;
967+ }
968+ gtk_label_set_text(GTK_LABEL(priv->pin_hint), hint);
969+ gtk_widget_set_visible(GTK_WIDGET(priv->token_status_icon), FALSE);
970+ gtk_widget_set_visible(GTK_WIDGET(priv->token_status), FALSE);
971+ gtk_widget_set_visible(GTK_WIDGET(priv->pin_status_icon), FALSE);
972+ gtk_widget_set_visible(GTK_WIDGET(priv->pin_status), FALSE);
973+ gtk_widget_set_visible(GTK_WIDGET(icon), status);
974+ gtk_widget_set_visible(GTK_WIDGET(label), status);
975+}
976+
977+static gboolean
978+get_ubuntu_advantage_attached(gboolean *attached)
979+{
980+ g_autoptr(GError) error = NULL;
981+ g_autoptr(GDBusConnection) bus = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, &error);
982+ if (bus == NULL) {
983+ g_warning ("Failed to get system bus: %s", error->message);
984+ return FALSE;
985+ }
986+
987+ g_autoptr(GVariant) result = g_dbus_connection_call_sync (bus,
988+ "com.canonical.UbuntuAdvantage",
989+ "/com/canonical/UbuntuAdvantage/Manager",
990+ "org.freedesktop.DBus.Properties",
991+ "Get",
992+ g_variant_new ("(ss)", "com.canonical.UbuntuAdvantage.Manager", "Attached"),
993+ G_VARIANT_TYPE ("(v)"),
994+ G_DBUS_CALL_FLAGS_NONE,
995+ -1,
996+ NULL,
997+ &error);
998+ if (result == NULL) {
999+ g_warning ("Failed to contact Ubuntu Advantage D-Bus service: %s", error->message);
1000+ return FALSE;
1001+ }
1002+
1003+ g_autoptr(GVariant) value = NULL;
1004+ g_variant_get (result, "(v)", &value);
1005+ if (!g_variant_is_of_type (value, G_VARIANT_TYPE("b"))) {
1006+ g_warning ("Attached property has wrong type");
1007+ return FALSE;
1008+ }
1009+ g_variant_get (value, "b", attached);
1010+
1011+ return TRUE;
1012+}
1013+
1014+static char *
1015+get_item (const char *buffer, const char *name)
1016+{
1017+ g_autofree gchar *label = NULL;
1018+ gchar *start = NULL, *end = NULL;
1019+ gchar end_char;
1020+
1021+ label = g_strconcat (name, "=", NULL);
1022+ if ((start = strstr (buffer, label)) != NULL) {
1023+ start += strlen (label);
1024+ end_char = '\n';
1025+ if (*start == '"') {
1026+ start++;
1027+ end_char = '"';
1028+ }
1029+ end = strchr (start, end_char);
1030+ }
1031+
1032+ if (start != NULL && end != NULL)
1033+ return g_strndup (start, end - start);
1034+ else
1035+ return NULL;
1036+}
1037+
1038+static gboolean
1039+is_lts ()
1040+{
1041+ g_autofree gchar *buffer = NULL;
1042+ g_autofree gchar *version = NULL;
1043+
1044+ if (g_file_get_contents ("/etc/os-release", &buffer, NULL, NULL))
1045+ version = get_item (buffer, "VERSION");
1046+
1047+ return version && g_strrstr (version, "LTS") != NULL;
1048+}
1049+
1050+static gboolean
1051+is_ubuntupro_supported ()
1052+{
1053+ return is_lts ();
1054+}
1055+
1056+static void
1057+network_status_changed (GNetworkMonitor *monitor,
1058+ gboolean available,
1059+ gpointer user_data)
1060+{
1061+ GisUbuntuProPage1Private *priv = user_data;
1062+
1063+ if (!available) {
1064+ gtk_widget_set_sensitive (priv->enable_pro_select, FALSE);
1065+ gtk_widget_show (GTK_WIDGET (priv->offline_warning));
1066+ gtk_widget_show (GTK_WIDGET (priv->pro_status_image));
1067+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->skip_pro_select), TRUE);
1068+ gtk_image_set_from_icon_name (GTK_IMAGE(priv->pro_status_image), "gtk-dialog-warning", GTK_ICON_SIZE_DND);
1069+ }
1070+ else {
1071+ gtk_widget_set_sensitive (priv->enable_pro_select, TRUE);
1072+ gtk_widget_hide (GTK_WIDGET (priv->offline_warning));
1073+ gtk_widget_hide (GTK_WIDGET (priv->pro_status_image));
1074+ }
1075+}
1076+
1077+static void
1078+gis_ubuntupro_page_constructed (GObject *object)
1079+{
1080+ GisUbuntuProPage *main_page = GIS_UBUNTUPRO_PAGE (object);
1081+ GisUbuntuProPagePrivate *main_page_priv = gis_ubuntupro_page_get_instance_private (main_page);
1082+ GisUbuntuProPage1 *page = GIS_UBUNTUPRO_PAGE1 (main_page_priv->page1);
1083+ GisUbuntuProPage1Private *priv = gis_ubuntupro_page1_get_instance_private (page);
1084+ GisUbuntuProPage2 *page2 = GIS_UBUNTUPRO_PAGE2 (main_page_priv->page2);
1085+ GisUbuntuProPage2Private *priv2 = gis_ubuntupro_page2_get_instance_private (page2);
1086+ priv2->main_page = main_page;
1087+
1088+ gtk_widget_show (GTK_WIDGET (main_page));
1089+
1090+ GNetworkMonitor *network_monitor = g_network_monitor_get_default ();
1091+
1092+ G_OBJECT_CLASS (gis_ubuntupro_page_parent_class)->constructed (object);
1093+
1094+ gis_page_set_skippable (GIS_PAGE (main_page), TRUE);
1095+
1096+ g_signal_connect (network_monitor, "network-changed",
1097+ G_CALLBACK (network_status_changed), priv);
1098+
1099+ gis_page_set_complete (GIS_PAGE (main_page), TRUE);
1100+
1101+ if (!g_network_monitor_get_network_available (network_monitor)) {
1102+ gtk_widget_set_sensitive (priv->enable_pro_select, FALSE);
1103+ gtk_widget_show (GTK_WIDGET (priv->offline_warning));
1104+ gtk_widget_show (GTK_WIDGET (priv->pro_status_image));
1105+ }
1106+
1107+ /* Initializate priv values */
1108+ priv2->magic_token_polling = FALSE;
1109+ priv2->active_radio = MAGIC;
1110+ priv2->expired_by = 0;
1111+ main_page_priv->current_page = 1;
1112+
1113+ gtk_widget_show (GTK_WIDGET (main_page));
1114+}
1115+
1116+static void
1117+gis_ubuntupro_page_locale_changed (GisPage *page)
1118+{
1119+ gis_page_set_title (GIS_PAGE (page), _("Ubuntu Pro"));
1120+}
1121+
1122+static gssize
1123+make_rest_req(void *buf,
1124+ size_t bufsize,
1125+ const char* type, /* POST, GET, ... */
1126+ const char* where, /* URL */
1127+ const char* header_name,
1128+ const char* header)
1129+{
1130+ SoupSession *session = soup_session_new();
1131+ SoupMessage *msg;
1132+ GError *error = NULL;
1133+ msg = soup_message_new(type, where);
1134+ soup_message_set_request_body(msg, "text/plain", NULL, -1);
1135+ if (header_name != NULL && header != NULL) {
1136+ soup_message_headers_append (soup_message_get_request_headers(msg), header_name, header);
1137+ }
1138+ GInputStream *stream = soup_session_send(session, msg, NULL, &error);
1139+ gssize nbytes = g_input_stream_read(stream, buf, bufsize, NULL, &error);
1140+ if (nbytes >= bufsize || error != NULL) {
1141+ g_warning("Didn't get a valid response for token request.");
1142+ return -1;
1143+ }
1144+ return nbytes;
1145+}
1146+
1147+static gboolean
1148+poll_token_attach (GisUbuntuProPage2Private *priv)
1149+{
1150+ gboolean ret = FALSE;
1151+ size_t bufsize = 1024;
1152+ void *buf = malloc(bufsize);
1153+ const char *header_name = "Authorization";
1154+ gchar *header = g_strconcat("Bearer ", priv->token, NULL);
1155+
1156+/* curl -X GET -H 'Authorization: Bearer >>TOKEN<<'
1157+ *
1158+ * Sample unauthorized response:
1159+ * {
1160+ * "code":"unauthorized",
1161+ * "message":"unauthorized",
1162+ * "traceId":"280a7ed5-fdb0-4ac4-be23-d26deafb4616"
1163+ * }
1164+ *
1165+ * Sample authorized response:
1166+ * {
1167+ * "expires":"2022-07-15T17:45:51.902Z",
1168+ * "expiresIn":567,
1169+ * "token":"M1guPBps6fUJpxasdasdxpc",
1170+ * "userCode":"8FHI4I"
1171+ * }
1172+ *
1173+ * Sample authorized and activated response:
1174+ * {
1175+ * "expires":"2022-07-15T17:45:51.902Z",
1176+ * "expiresIn":567,
1177+ * "token":"M1guPBps6fUJpxasdasdxpc",
1178+ * "userCode":"8FHI4I",
1179+ * "contractID":"CONTRACT_ID",
1180+ * "contractToken":"CONTRACT_TOKEN" <= THIS is what we need to atatch
1181+ * }
1182+ *
1183+ */
1184+ gssize nbytes = make_rest_req(buf, bufsize, "GET",
1185+ "https://contracts.staging.canonical.com/v1/magic-attach",
1186+ header_name, header);
1187+ free(header);
1188+ if (nbytes > 0) {
1189+ RestJSONResponse resp;
1190+ if (!magic_parser(buf, nbytes, &resp)) {
1191+ g_warning("Couldn't parse response.\n");
1192+ } else if (resp.contractToken != NULL) {
1193+ priv->contractToken = strdup(resp.contractToken);
1194+ ret = TRUE;
1195+ }
1196+ }
1197+
1198+ free(buf);
1199+ return ret;
1200+}
1201+
1202+static gboolean
1203+poll_magic_token (gpointer data)
1204+{
1205+ GisUbuntuProPage2Private *priv = gis_ubuntupro_page2_get_instance_private (data);
1206+
1207+ if (priv->active_radio != MAGIC) {
1208+ return TRUE;
1209+ }
1210+ priv->magic_token_polling = TRUE;
1211+
1212+ if (time(0) > priv->expired_by) {
1213+ status_t status = EXPIRED;
1214+ update_gui(priv, status);
1215+ priv->magic_token_polling = FALSE;
1216+ }
1217+ gboolean token_received = poll_token_attach(priv);
1218+ if (token_received) {
1219+ priv->magic_token_polling = FALSE;
1220+ ua_attach(priv->contractToken, priv);
1221+ }
1222+
1223+ return priv->magic_token_polling;
1224+}
1225+
1226+static gboolean
1227+magic_parser(void* ptr, /* pointer to actual response */
1228+ size_t nmemb, /* size of data to which ptr points */
1229+ RestJSONResponse *resp) /* relevant response fields */
1230+{
1231+ const char *data = (const char*)ptr; // Not null terminated, use nmemb
1232+
1233+ JsonParser *parser;
1234+ JsonNode *root;
1235+ GError *error;
1236+
1237+ parser = json_parser_new();
1238+ error = NULL;
1239+ json_parser_load_from_data(parser, data, nmemb, &error);
1240+ if (error) {
1241+ g_warning("Couldn't parse magic token JSON; %s\n", error->message);
1242+ g_error_free(error);
1243+ g_object_unref(parser);
1244+ return FALSE;
1245+ }
1246+ root = json_parser_get_root(parser);
1247+ if (!JSON_NODE_HOLDS_OBJECT(root)) {
1248+ g_warning("Invalid magic token JSON\n");
1249+ return FALSE;
1250+ }
1251+
1252+ JsonObject *response = json_node_get_object(root);
1253+ resp->expiresIn = json_object_has_member(response, "expiresIn") ?
1254+ json_object_get_int_member(response, "expiresIn") :
1255+ 0;
1256+ resp->token = json_object_has_member(response, "token") ?
1257+ strdup(json_object_get_string_member(response, "token")) :
1258+ NULL;
1259+ resp->code = json_object_has_member(response, "userCode") ?
1260+ strdup(json_object_get_string_member(response, "userCode")) :
1261+ NULL;
1262+ resp->contractToken = json_object_has_member(response, "contractToken") ?
1263+ strdup(json_object_get_string_member(response, "contractToken")) :
1264+ NULL;
1265+
1266+ g_object_unref(parser);
1267+ return TRUE;
1268+}
1269+
1270+gboolean
1271+parse_ua_status(gchar **contents, gsize *len)
1272+{
1273+ GError *error = NULL;
1274+
1275+ if (!g_file_get_contents("/var/lib/ubuntu-advantage/status.json",
1276+ contents, len, &error)) {
1277+ g_warning("Unable to read pro status: %s\n", error->message);
1278+ g_error_free(error);
1279+ return FALSE;
1280+ }
1281+ return TRUE;
1282+}
1283+
1284+static void
1285+display_checkmark(GisUbuntuProPage3Private *priv)
1286+{
1287+ GError *error = NULL;
1288+ GdkPixbuf *check_pixbuf = gdk_pixbuf_new_from_resource_at_scale (
1289+ "/org/gnome/initial-setup/checkmark.svg",
1290+ 30,
1291+ -1,
1292+ TRUE,
1293+ &error
1294+ );
1295+ if (error) {
1296+ g_warning("Unable to create pixbuf: %s\n", error->message);
1297+ g_error_free(error);
1298+ }
1299+
1300+ if (check_pixbuf != NULL) {
1301+ gtk_image_set_from_pixbuf(GTK_IMAGE(priv->checkmark), check_pixbuf);
1302+ } else {
1303+ g_warning("Unable to load pixbuf: %s\n", error->message);
1304+ }
1305+}
1306+
1307+static void
1308+display_ua_services(GisUbuntuProPage3Private *priv)
1309+{
1310+ JsonParser *parser;
1311+ JsonNode *root_node;
1312+ JsonObject *root, *services, *contract;
1313+ JsonArray *services_array;
1314+ GError *error;
1315+ guint i, n_services;
1316+ const char *status, *description, *available, *contract_name;
1317+ gchar *str, *enabled_str, *available_str;
1318+ gsize len;
1319+
1320+ if (!parse_ua_status(&str, &len)) {
1321+ return;
1322+ }
1323+ available_str = (gchar *)g_malloc0(len);
1324+ enabled_str = (gchar *)g_malloc0(len);
1325+
1326+ parser = json_parser_new();
1327+ error = NULL;
1328+ json_parser_load_from_data(parser, str, strlen(str), &error);
1329+ if (error) {
1330+ g_warning("Couldn't parse magic token JSON; %s\n", error->message);
1331+ g_error_free(error);
1332+ goto end;
1333+ }
1334+ root_node = json_parser_get_root(parser);
1335+ if (!JSON_NODE_HOLDS_OBJECT(root_node)) {
1336+ g_warning("Invalid magic token JSON\n");
1337+ goto end;
1338+ }
1339+
1340+ root = json_node_get_object(root_node);
1341+
1342+ contract = json_object_get_object_member(root, "contract");
1343+ contract_name = json_object_get_string_member(contract, "name");
1344+ gtk_label_set_text(GTK_LABEL(priv->contract_name), contract_name);
1345+
1346+ services_array = json_object_get_array_member(root, "services");
1347+ n_services = json_array_get_length(services_array);
1348+
1349+ /* Get services description, status and availability */
1350+ for (i = 0; i < n_services; i++) {
1351+ services = json_array_get_object_element(services_array, i);
1352+ if (
1353+ json_object_has_member(services, "description") &&
1354+ json_object_has_member(services, "status") &&
1355+ json_object_has_member(services, "available")
1356+ ) {
1357+ description = json_object_get_string_member(services, "description");
1358+ status = json_object_get_string_member(services, "status");
1359+ available = json_object_get_string_member(services, "available");
1360+ if (strcmp(status, "enabled") == 0) {
1361+ g_strlcat(enabled_str, description, len);
1362+ g_strlcat(enabled_str, "\n", len);
1363+ } else if (strcmp(available, "yes") == 0) {
1364+ g_strlcat(available_str, description, len);
1365+ g_strlcat(available_str, "\n", len);
1366+ }
1367+ }
1368+ }
1369+
1370+ /* Display enabled and disabled but avaialable services */
1371+ if (enabled_str == NULL) {
1372+ gtk_widget_destroy(GTK_WIDGET(priv->enabled_services_header));
1373+ } else {
1374+ gtk_label_set_text(GTK_LABEL(priv->enabled_services), enabled_str);
1375+ free(enabled_str);
1376+ }
1377+ if (available_str == NULL) {
1378+ gtk_widget_destroy(GTK_WIDGET(priv->available_services_header));
1379+ } else {
1380+ gtk_label_set_text(GTK_LABEL(priv->available_services), available_str);
1381+ free(available_str);
1382+ }
1383+
1384+end:
1385+ free(str);
1386+ g_object_unref(parser);
1387+}
1388+
1389+static void
1390+request_magic_attach (GisUbuntuProPage2 *page)
1391+{
1392+ GisUbuntuProPage2Private *priv = gis_ubuntupro_page2_get_instance_private (page);
1393+
1394+ if (priv->magic_token_polling) {
1395+ return;
1396+ }
1397+
1398+ size_t bufsize = 1024;
1399+ void *buf = malloc(bufsize);
1400+ gssize nbytes = make_rest_req(buf, bufsize, "POST",
1401+ "https://contracts.staging.canonical.com/v1/magic-attach", NULL, NULL);
1402+ if (nbytes > 0) {
1403+ RestJSONResponse resp;
1404+ if (!magic_parser(buf, nbytes, &resp)) {
1405+ g_warning("Couldn't parse response.\n");
1406+ return;
1407+ }
1408+ priv->pin = strdup(resp.code);
1409+ priv->expired_by = resp.expiresIn + time(0);
1410+ priv->token = strdup(resp.token);
1411+ priv->magic_token_polling = TRUE;
1412+ priv->poll_id = g_timeout_add_seconds (5, poll_magic_token, page);
1413+ update_gui(priv, FALSE);
1414+ free(resp.token);
1415+ free(resp.code);
1416+ }
1417+
1418+ free(buf);
1419+}
1420+
1421+static void
1422+on_ua_attach_requested (GObject *source,
1423+ GAsyncResult *result,
1424+ gpointer user_data)
1425+{
1426+ GisUbuntuProPage2Private *priv = user_data;
1427+ GError *error = NULL;
1428+ GVariant *retval;
1429+ status_t status;
1430+
1431+ retval = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source), result, &error);
1432+ if (retval == NULL) {
1433+ g_warning ("Failed to attach token: %s", error->message);
1434+ g_error_free (error);
1435+ status = FAIL;
1436+ } else {
1437+ g_variant_unref (retval);
1438+ SuccessfullyAttached = TRUE;
1439+ status = SUCCESS;
1440+ g_source_remove(priv->poll_id);
1441+ gis_page_set_complete (GIS_PAGE (priv->main_page), TRUE);
1442+ }
1443+ priv->attaching = FALSE;
1444+ update_gui(priv, status);
1445+}
1446+
1447+static void
1448+ua_attach(const gchar *token, GisUbuntuProPage2Private *priv)
1449+{
1450+ GDBusConnection *bus;
1451+ GError *error = NULL;
1452+ status_t status = NONE;
1453+
1454+ if (priv->attaching) {
1455+ return;
1456+ }
1457+
1458+ bus = g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, &error);
1459+ if (bus == NULL) {
1460+ g_warning("Failed to get system bus: %s", error->message);
1461+ } else {
1462+ priv->attaching = TRUE;
1463+ update_gui(priv, status);
1464+
1465+ g_dbus_connection_call(bus,
1466+ "com.canonical.UbuntuAdvantage",
1467+ "/com/canonical/UbuntuAdvantage/Manager",
1468+ "com.canonical.UbuntuAdvantage.Manager",
1469+ "Attach",
1470+ g_variant_new("(s)", token),
1471+ G_VARIANT_TYPE("()"),
1472+ G_DBUS_CALL_FLAGS_NONE,
1473+ 543210, /* I have observed that -1, the default timeout, is not enough. */
1474+ NULL,
1475+ on_ua_attach_requested,
1476+ priv);
1477+ }
1478+}
1479+
1480+void
1481+request_token_attach (GtkButton *button, GisUbuntuProPage2 *page)
1482+{
1483+ GisUbuntuProPage2Private *priv = gis_ubuntupro_page2_get_instance_private (page);
1484+
1485+ const gchar *token = gtk_entry_get_text(GTK_ENTRY(priv->token_field));
1486+ ua_attach(token, priv);
1487+}
1488+
1489+void
1490+on_magic_clicked (GtkButton *button, GisUbuntuProPage2 *page)
1491+{
1492+ GisUbuntuProPage2Private *priv = gis_ubuntupro_page2_get_instance_private (page);
1493+ priv->active_radio = MAGIC;
1494+
1495+ request_magic_attach(page);
1496+}
1497+
1498+void
1499+on_radio_toggled (GtkButton *button, GisUbuntuProPage2 *page)
1500+{
1501+ GisUbuntuProPage2Private *priv = gis_ubuntupro_page2_get_instance_private (page);
1502+ status_t status = NONE;
1503+
1504+ priv->active_radio = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(priv->token_radio)) ? TOKEN : MAGIC;
1505+ update_gui(priv, status);
1506+}
1507+
1508+static gboolean
1509+gis_ubuntupro_page_apply (GisPage *gis_page,
1510+ GCancellable *cancellable)
1511+{
1512+ GisUbuntuProPage *page = GIS_UBUNTUPRO_PAGE (gis_page);
1513+ GisUbuntuProPagePrivate *priv = gis_ubuntupro_page_get_instance_private (page);
1514+ if (priv->current_page == 1) {
1515+ GisUbuntuProPage1 *page1 = GIS_UBUNTUPRO_PAGE1 (priv->page1);
1516+ GisUbuntuProPage1Private *priv1 = gis_ubuntupro_page1_get_instance_private (page1);
1517+ if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (priv1->skip_pro_select))) {
1518+ /* Bails out of UbuntuPro if skip button was selected */
1519+ return FALSE;
1520+ }
1521+
1522+ /* Request magic token already and advance to next local page */
1523+ on_magic_clicked(NULL, GIS_UBUNTUPRO_PAGE2(priv->page2));
1524+ gis_page_apply_complete (GIS_PAGE (page), FALSE);
1525+ /* Change button from Skip to Next */
1526+ gis_page_set_complete (GIS_PAGE (page), FALSE);
1527+ gtk_stack_set_visible_child (GTK_STACK (priv->stack), priv->page2);
1528+ } else if (priv->current_page == 2 && SuccessfullyAttached) {
1529+ GisUbuntuProPage3 *page3 = GIS_UBUNTUPRO_PAGE3(priv->page3);
1530+ display_ua_services(gis_ubuntupro_page3_get_instance_private (page3));
1531+ display_checkmark(gis_ubuntupro_page3_get_instance_private (page3));
1532+ gtk_stack_set_visible_child (GTK_STACK (priv->stack), priv->page3);
1533+ gis_page_apply_complete (GIS_PAGE (page), FALSE);
1534+ } else {
1535+ return FALSE;
1536+ }
1537+ priv->current_page++;
1538+ return TRUE;
1539+}
1540+
1541+static void
1542+gis_ubuntupro_page_class_init (GisUbuntuProPageClass *klass)
1543+{
1544+ GisPageClass *page_class = GIS_PAGE_CLASS (klass);
1545+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
1546+
1547+ gtk_widget_class_set_template_from_resource (GTK_WIDGET_CLASS (klass), "/org/gnome/initial-setup/gis-ubuntupro-page.ui");
1548+
1549+ gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (klass), GisUbuntuProPage, page1);
1550+ gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (klass), GisUbuntuProPage, page2);
1551+ gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (klass), GisUbuntuProPage, page3);
1552+ gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (klass), GisUbuntuProPage, stack);
1553+ gtk_widget_class_bind_template_callback (GTK_WIDGET_CLASS (klass), request_token_attach);
1554+ gtk_widget_class_bind_template_callback (GTK_WIDGET_CLASS (klass), request_magic_attach);
1555+ gtk_widget_class_bind_template_callback (GTK_WIDGET_CLASS (klass), on_radio_toggled);
1556+ gtk_widget_class_bind_template_callback (GTK_WIDGET_CLASS (klass), on_magic_clicked);
1557+
1558+ page_class->page_id = PAGE_ID;
1559+ page_class->locale_changed = gis_ubuntupro_page_locale_changed;
1560+ page_class->apply = gis_ubuntupro_page_apply;
1561+ object_class->constructed = gis_ubuntupro_page_constructed;
1562+}
1563+static void
1564+gis_ubuntupro_page1_class_init (GisUbuntuProPage1Class *klass)
1565+{
1566+ gtk_widget_class_set_template_from_resource (GTK_WIDGET_CLASS (klass), "/org/gnome/initial-setup/gis-ubuntupro-page-1.ui");
1567+
1568+ gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (klass), GisUbuntuProPage1, enable_pro_select);
1569+ gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (klass), GisUbuntuProPage1, skip_pro_select);
1570+ gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (klass), GisUbuntuProPage1, offline_warning);
1571+ gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (klass), GisUbuntuProPage1, pro_status_image);
1572+}
1573+
1574+static void
1575+gis_ubuntupro_page2_class_init (GisUbuntuProPage2Class *klass)
1576+{
1577+ gtk_widget_class_set_template_from_resource (GTK_WIDGET_CLASS (klass), "/org/gnome/initial-setup/gis-ubuntupro-page-2.ui");
1578+
1579+ gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (klass), GisUbuntuProPage2, pin_label);
1580+ gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (klass), GisUbuntuProPage2, token_field);
1581+ gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (klass), GisUbuntuProPage2, token_status);
1582+ gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (klass), GisUbuntuProPage2, token_spinner);
1583+ gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (klass), GisUbuntuProPage2, token_status_icon);
1584+ gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (klass), GisUbuntuProPage2, pin_status);
1585+ gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (klass), GisUbuntuProPage2, pin_spinner);
1586+ gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (klass), GisUbuntuProPage2, pin_status_icon);
1587+ gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (klass), GisUbuntuProPage2, pin_hint);
1588+ gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (klass), GisUbuntuProPage2, token_radio);
1589+ gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (klass), GisUbuntuProPage2, magic_radio);
1590+}
1591+static void
1592+gis_ubuntupro_page3_class_init (GisUbuntuProPage3Class *klass)
1593+{
1594+ gtk_widget_class_set_template_from_resource (GTK_WIDGET_CLASS (klass), "/org/gnome/initial-setup/gis-ubuntupro-page-3.ui");
1595+
1596+ gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (klass), GisUbuntuProPage3, enabled_services);
1597+ gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (klass), GisUbuntuProPage3, enabled_services_header);
1598+ gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (klass), GisUbuntuProPage3, available_services);
1599+ gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (klass), GisUbuntuProPage3, available_services_header);
1600+ gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (klass), GisUbuntuProPage3, contract_name);
1601+ gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (klass), GisUbuntuProPage3, checkmark);
1602+}
1603+
1604+static void
1605+gis_ubuntupro_page_init (GisUbuntuProPage *page)
1606+{
1607+ g_resources_register (ubuntupro_get_resource ());
1608+
1609+ /* Magic that makes stuff compile */
1610+ gis_ubuntupro_page1_get_type ();
1611+ gis_ubuntupro_page2_get_type ();
1612+ gis_ubuntupro_page3_get_type ();
1613+
1614+ gtk_widget_init_template (GTK_WIDGET (page));
1615+}
1616+static void
1617+gis_ubuntupro_page1_init (GisUbuntuProPage1 *page)
1618+{
1619+ g_resources_register (ubuntupro_get_resource ());
1620+
1621+ gtk_widget_init_template (GTK_WIDGET (page));
1622+}
1623+static void
1624+gis_ubuntupro_page2_init (GisUbuntuProPage2 *page)
1625+{
1626+ g_resources_register (ubuntupro_get_resource ());
1627+
1628+ gtk_widget_init_template (GTK_WIDGET (page));
1629+}
1630+static void
1631+gis_ubuntupro_page3_init (GisUbuntuProPage3 *page)
1632+{
1633+ g_resources_register (ubuntupro_get_resource ());
1634+
1635+ gtk_widget_init_template (GTK_WIDGET (page));
1636+}
1637+
1638+GisPage *
1639+gis_prepare_ubuntu_pro_page (GisDriver *driver)
1640+{
1641+ gboolean attached;
1642+ if (!get_ubuntu_advantage_attached (&attached)) {
1643+ g_print("the ua status fetch failed\n");
1644+ return NULL;
1645+ }
1646+ if (attached) {
1647+ g_print("ua is already attached, skipping Ubuntu Pro pages\n");
1648+ return NULL;
1649+ }
1650+
1651+ if (!is_ubuntupro_supported ())
1652+ return NULL;
1653+
1654+ return g_object_new (GIS_TYPE_UBUNTUPRO_PAGE,
1655+ "driver", driver,
1656+ NULL);
1657+}
1658diff --git a/gnome-initial-setup/pages/ubuntu-pro/gis-ubuntupro-page.h b/gnome-initial-setup/pages/ubuntu-pro/gis-ubuntupro-page.h
1659new file mode 100644
1660index 0000000..974b8ed
1661--- /dev/null
1662+++ b/gnome-initial-setup/pages/ubuntu-pro/gis-ubuntupro-page.h
1663@@ -0,0 +1,118 @@
1664+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
1665+/*
1666+ * Copyright (C) 2018 Canonical Ltd.
1667+ *
1668+ * This program is free software; you can redistribute it and/or
1669+ * modify it under the terms of the GNU General Public License as
1670+ * published by the Free Software Foundation; either version 2 of the
1671+ * License, or (at your option) any later version.
1672+ *
1673+ * This program is distributed in the hope that it will be useful, but
1674+ * WITHOUT ANY WARRANTY; without even the implied warranty of
1675+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
1676+ * General Public License for more details.
1677+ *
1678+ * You should have received a copy of the GNU General Public License
1679+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
1680+ */
1681+
1682+#ifndef __GIS_UBUNTUPRO_PAGE_H__
1683+#define __GIS_UBUNTUPRO_PAGE_H__
1684+
1685+#include "gnome-initial-setup.h"
1686+
1687+G_BEGIN_DECLS
1688+
1689+#define GIS_TYPE_UBUNTUPRO_PAGE (gis_ubuntupro_page_get_type ())
1690+#define GIS_TYPE_UBUNTUPRO_PAGE1 (gis_ubuntupro_page1_get_type ())
1691+#define GIS_TYPE_UBUNTUPRO_PAGE2 (gis_ubuntupro_page2_get_type ())
1692+#define GIS_TYPE_UBUNTUPRO_PAGE3 (gis_ubuntupro_page3_get_type ())
1693+
1694+#define GIS_UBUNTUPRO_PAGE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIS_TYPE_UBUNTUPRO_PAGE, GisUbuntuProPage))
1695+#define GIS_UBUNTUPRO_PAGE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIS_TYPE_UBUNTUPRO_PAGE, GisUbuntuProPageClass))
1696+#define GIS_UBUNTUPRO_PAGE1(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIS_TYPE_UBUNTUPRO_PAGE1, GisUbuntuProPage1))
1697+#define GIS_UBUNTUPRO_PAGE1_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIS_TYPE_UBUNTUPRO_PAGE1, GisUbuntuProPage1Class))
1698+#define GIS_UBUNTUPRO_PAGE2(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIS_TYPE_UBUNTUPRO_PAGE2, GisUbuntuProPage2))
1699+#define GIS_UBUNTUPRO_PAGE2_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIS_TYPE_UBUNTUPRO_PAGE2, GisUbuntuProPage2Class))
1700+#define GIS_UBUNTUPRO_PAGE3(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIS_TYPE_UBUNTUPRO_PAGE3, GisUbuntuProPage3))
1701+#define GIS_UBUNTUPRO_PAGE3_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIS_TYPE_UBUNTUPRO_PAGE3, GisUbuntuProPage3Class))
1702+
1703+#define GIS_IS_UBUNTUPRO_PAGE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIS_TYPE_UBUNTUPRO_PAGE))
1704+#define GIS_IS_UBUNTUPRO_PAGE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIS_TYPE_UBUNTUPRO_PAGE))
1705+#define GIS_IS_UBUNTUPRO_PAGE1(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIS_TYPE_UBUNTUPRO_PAGE1))
1706+#define GIS_IS_UBUNTUPRO_PAGE1_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIS_TYPE_UBUNTUPRO_PAGE1))
1707+#define GIS_IS_UBUNTUPRO_PAGE2(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIS_TYPE_UBUNTUPRO_PAGE2))
1708+#define GIS_IS_UBUNTUPRO_PAGE2_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIS_TYPE_UBUNTUPRO_PAGE2))
1709+#define GIS_IS_UBUNTUPRO_PAGE3(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIS_TYPE_UBUNTUPRO_PAGE3))
1710+#define GIS_IS_UBUNTUPRO_PAGE3_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIS_TYPE_UBUNTUPRO_PAGE3))
1711+
1712+#define GIS_UBUNTUPRO_PAGE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIS_TYPE_UBUNTUPRO_PAGE, GisUbuntuProPageClass))
1713+#define GIS_UBUNTUPRO_PAGE1_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIS_TYPE_UBUNTUPRO_PAGE1, GisUbuntuProPage1Class))
1714+#define GIS_UBUNTUPRO_PAGE2_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIS_TYPE_UBUNTUPRO_PAGE2, GisUbuntuProPage2Class))
1715+#define GIS_UBUNTUPRO_PAGE3_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIS_TYPE_UBUNTUPRO_PAGE3, GisUbuntuProPage3Class))
1716+
1717+typedef struct _GisUbuntuProPage GisUbuntuProPage;
1718+typedef struct _GisUbuntuProPageClass GisUbuntuProPageClass;
1719+typedef struct _GisUbuntuProPage1 GisUbuntuProPage1;
1720+typedef struct _GisUbuntuProPage1Class GisUbuntuProPage1Class;
1721+typedef struct _GisUbuntuProPage2 GisUbuntuProPage2;
1722+typedef struct _GisUbuntuProPage2Class GisUbuntuProPage2Class;
1723+typedef struct _GisUbuntuProPage3 GisUbuntuProPage3;
1724+typedef struct _GisUbuntuProPage3Class GisUbuntuProPage3Class;
1725+typedef struct _RestJSONResponse RestJSONResponse;
1726+
1727+struct _GisUbuntuProPage
1728+{
1729+ GisPage parent;
1730+};
1731+struct _GisUbuntuProPageClass
1732+{
1733+ GisPageClass parent_class;
1734+};
1735+
1736+struct _GisUbuntuProPage1
1737+{
1738+ GtkBin parent;
1739+};
1740+struct _GisUbuntuProPage1Class
1741+{
1742+ GtkBinClass parent_class;
1743+};
1744+
1745+struct _GisUbuntuProPage2
1746+{
1747+ GtkBin parent;
1748+};
1749+struct _GisUbuntuProPage2Class
1750+{
1751+ GtkBinClass parent_class;
1752+};
1753+
1754+struct _GisUbuntuProPage3
1755+{
1756+ GtkBin parent;
1757+};
1758+
1759+struct _GisUbuntuProPage3Class
1760+{
1761+ GtkBinClass parent_class;
1762+};
1763+
1764+struct _RestJSONResponse {
1765+ gint64 expiresIn;
1766+ gchar *token;
1767+ gchar *code;
1768+ gchar *contractToken;
1769+};
1770+
1771+GType gis_ubuntupro_page_get_type (void);
1772+GType gis_ubuntupro_page1_get_type (void);
1773+GType gis_ubuntupro_page2_get_type (void);
1774+GType gis_ubuntupro_page3_get_type (void);
1775+
1776+GisPage *gis_prepare_ubuntu_pro_page (GisDriver *driver);
1777+
1778+
1779+G_END_DECLS
1780+
1781+#endif /* __GIS_UBUNTUPRO_PAGE_H__ */
1782diff --git a/gnome-initial-setup/pages/ubuntu-pro/gis-ubuntupro-page.ui b/gnome-initial-setup/pages/ubuntu-pro/gis-ubuntupro-page.ui
1783new file mode 100644
1784index 0000000..0731995
1785--- /dev/null
1786+++ b/gnome-initial-setup/pages/ubuntu-pro/gis-ubuntupro-page.ui
1787@@ -0,0 +1,39 @@
1788+<?xml version="1.0" encoding="UTF-8"?>
1789+<interface>
1790+ <!-- interface-requires gtk+ 3.0 -->
1791+ <template class="GisUbuntuProPage" parent="GisPage">
1792+ <child>
1793+ <object class="GtkBox" id="stepUbuntuPro">
1794+ <property name="visible">True</property>
1795+ <property name="can-focus">False</property>
1796+ <child>
1797+ <object class="GtkStack" id="stack">
1798+ <property name="visible">True</property>
1799+ <property name="valign">start</property>
1800+ <property name="vexpand">True</property>
1801+ <child>
1802+ <object class="GisUbuntuProPage1" id="page1">
1803+ <property name="visible">True</property>
1804+ </object>
1805+ </child>
1806+ <child>
1807+ <object class="GisUbuntuProPage2" id="page2">
1808+ <property name="visible">True</property>
1809+ </object>
1810+ </child>
1811+ <child>
1812+ <object class="GisUbuntuProPage3" id="page3">
1813+ <property name="visible">True</property>
1814+ </object>
1815+ </child>
1816+ </object>
1817+ <packing>
1818+ <property name="expand">False</property>
1819+ <property name="fill">True</property>
1820+ <property name="position">0</property>
1821+ </packing>
1822+ </child>
1823+ </object>
1824+ </child>
1825+ </template>
1826+</interface>
1827diff --git a/gnome-initial-setup/pages/ubuntu-pro/meson.build b/gnome-initial-setup/pages/ubuntu-pro/meson.build
1828new file mode 100644
1829index 0000000..95a875d
1830--- /dev/null
1831+++ b/gnome-initial-setup/pages/ubuntu-pro/meson.build
1832@@ -0,0 +1,10 @@
1833+sources += gnome.compile_resources(
1834+ 'ubuntupro-resources',
1835+ files('ubuntupro.gresource.xml'),
1836+ c_name: 'ubuntupro'
1837+)
1838+
1839+sources += files(
1840+ 'gis-ubuntupro-page.c',
1841+ 'gis-ubuntupro-page.h',
1842+)
1843diff --git a/gnome-initial-setup/pages/ubuntu-pro/ubuntu-pro.svg b/gnome-initial-setup/pages/ubuntu-pro/ubuntu-pro.svg
1844new file mode 100644
1845index 0000000..dd837b0
1846Binary files /dev/null and b/gnome-initial-setup/pages/ubuntu-pro/ubuntu-pro.svg differ
1847diff --git a/gnome-initial-setup/pages/ubuntu-pro/ubuntupro.gresource.xml b/gnome-initial-setup/pages/ubuntu-pro/ubuntupro.gresource.xml
1848new file mode 100644
1849index 0000000..bc06591
1850--- /dev/null
1851+++ b/gnome-initial-setup/pages/ubuntu-pro/ubuntupro.gresource.xml
1852@@ -0,0 +1,13 @@
1853+<?xml version="1.0" encoding="UTF-8"?>
1854+<gresources>
1855+ <gresource prefix="/org/gnome/initial-setup">
1856+ <file preprocess="xml-stripblanks" alias="gis-ubuntupro-page.ui">gis-ubuntupro-page.ui</file>
1857+ <file preprocess="xml-stripblanks" alias="gis-ubuntupro-page-1.ui">gis-ubuntupro-page-1.ui</file>
1858+ <file preprocess="xml-stripblanks" alias="gis-ubuntupro-page-2.ui">gis-ubuntupro-page-2.ui</file>
1859+ <file preprocess="xml-stripblanks" alias="gis-ubuntupro-page-3.ui">gis-ubuntupro-page-3.ui</file>
1860+ <file alias="ubuntu-pro.svg">ubuntu-pro.svg</file>
1861+ <file alias="checkmark.svg">checkmark.svg</file>
1862+ <file alias="fail.svg">fail.svg</file>
1863+ </gresource>
1864+</gresources>
1865+

Subscribers

People subscribed via source and target branches

to all changes: