Merge lp:~donadigo/switchboard-plug-parental-controls/merge-cli-daemon into lp:~elementary-pantheon/switchboard-plug-parental-controls/trunk

Proposed by Adam Bieńkowski
Status: Merged
Approved by: Felipe Escoto
Approved revision: 234
Merged at revision: 226
Proposed branch: lp:~donadigo/switchboard-plug-parental-controls/merge-cli-daemon
Merge into: lp:~elementary-pantheon/switchboard-plug-parental-controls/trunk
Diff against target: 3420 lines (+1413/-1256)
32 files modified
lib/exec-monitor.c (+6/-6)
lib/exec-monitor.h (+1/-0)
src/CMakeLists.txt (+3/-2)
src/Plug.vala (+0/-1)
src/Widgets/AppsBox.vala (+48/-51)
src/Widgets/ControlPage.vala (+24/-34)
src/Widgets/GeneralBox.vala (+61/-115)
src/Widgets/InternetBox.vala (+14/-14)
src/Widgets/UserItem.vala (+8/-2)
src/Widgets/WeekSpinBox.vala (+7/-13)
src/cli/Application.vala (+0/-191)
src/cli/CMakeLists.txt (+0/-28)
src/cli/PAMWriter.vala (+0/-204)
src/client/AppUnavailableDialog.vala (+3/-3)
src/client/CMakeLists.txt (+3/-1)
src/client/Client.vala (+20/-50)
src/daemon/CMakeLists.txt (+6/-2)
src/daemon/Daemon.vala (+9/-10)
src/daemon/Interfaces.vala (+30/-34)
src/daemon/IptablesHelper.vala (+46/-48)
src/daemon/ProcessWatcher.vala (+49/-74)
src/daemon/Server.vala (+213/-7)
src/daemon/SessionHandler.vala (+47/-163)
src/daemon/SessionManager.vala (+53/-38)
src/daemon/Timer.vala (+116/-0)
src/daemon/UserConfig.vala (+208/-0)
src/shared/PAM/Reader.vala (+66/-0)
src/shared/PAM/Token.vala (+190/-0)
src/shared/PAM/Writer.vala (+110/-0)
src/shared/PAMControl.vala (+0/-128)
src/shared/Utils.vala (+71/-34)
src/shared/Vars.vala (+1/-3)
To merge this branch: bzr merge lp:~donadigo/switchboard-plug-parental-controls/merge-cli-daemon
Reviewer Review Type Date Requested Status
Felipe Escoto (community) Approve
Review via email: mp+304403@code.launchpad.net

Commit message

* Merge CLI functionalities with daemon
* Remove unneded code
* Rework PAM time module parsing
* Use more native objects like Resolver to resolve addresses
* Expose Get and Set methods over DBus for easier access
* Code cleanup and rework
* Split big classes onto smaller ones for better code understanding

Description of the change

This branch is an update for Loki stable or either Loki update. It reworks how the daemon and removes the unneded code, makes it a lot easier to read and understand. It does not introduce any new features or string replacements over trunk.

The command line utility is now completely removed. It's function is now built-in to the daemon, through a DBus server. We now have different classes for parsing PAM time module which use native string utilities unlike the old parsing methods. The PAM timer logic (to show a timeout to the user) was moved into a Timer class which is also much simpler than the previous handling of this feature. There is also UserConfig class which is a representation of a key file in ~/.config/pantheon-parental-controls-daemon.conf, it can get and set the properties of it live, that means that changing something in the UI will update the daemon to the new properties.

All Get () methods of the daemon are accessible to the normal user (for displaying them in the UI without authentication) and all Set () methods need authentication to be done before calling them.

To post a comment you must log in.
233. By Adam Bieńkowski

Remove C debug prints

234. By Adam Bieńkowski

Moved set active contents of AppsBox to ControlPage

Revision history for this message
Felipe Escoto (philip.scott) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'lib/exec-monitor.c'
2--- lib/exec-monitor.c 2016-05-19 19:47:38 +0000
3+++ lib/exec-monitor.c 2016-08-30 20:17:54 +0000
4@@ -110,12 +110,12 @@
5
6 iface = EXEC_MONITOR_GET_IFACE (self);
7 iface->monitor_events = FALSE;
8+ close (iface->sk_nl);
9 }
10
11 void
12 exec_monitor_start_internal (ExecMonitor *self)
13 {
14- int sk_nl;
15 int err;
16 struct sockaddr_nl my_nla, kern_nla, from_nla;
17 socklen_t from_nla_len;
18@@ -132,8 +132,8 @@
19 ExecMonitorInterface *iface;
20 iface = EXEC_MONITOR_GET_IFACE (self);
21
22- sk_nl = socket (PF_NETLINK, SOCK_DGRAM, NETLINK_CONNECTOR);
23- if (sk_nl == -1) {
24+ iface->sk_nl = socket (PF_NETLINK, SOCK_DGRAM, NETLINK_CONNECTOR);
25+ if (iface->sk_nl == -1) {
26 return;
27 }
28
29@@ -145,7 +145,7 @@
30 kern_nla.nl_groups = CN_IDX_PROC;
31 kern_nla.nl_pid = 1;
32
33- err = bind (sk_nl, (struct sockaddr *)&my_nla, sizeof (my_nla));
34+ err = bind (iface->sk_nl, (struct sockaddr *)&my_nla, sizeof (my_nla));
35 if (err == -1) {
36 return;
37 }
38@@ -169,7 +169,7 @@
39 cn_hdr->ack = 0;
40 cn_hdr->len = sizeof (enum proc_cn_mcast_op);
41
42- if (send (sk_nl, nl_hdr, nl_hdr->nlmsg_len, 0) != nl_hdr->nlmsg_len) {
43+ if (send (iface->sk_nl, nl_hdr, nl_hdr->nlmsg_len, 0) != nl_hdr->nlmsg_len) {
44 return;
45 }
46
47@@ -182,7 +182,7 @@
48 struct nlmsghdr *nlh = (struct nlmsghdr*)buff;
49 memcpy (&from_nla, &kern_nla, sizeof (from_nla));
50
51- recv_len = recvfrom (sk_nl, buff, BUFF_SIZE, 0,
52+ recv_len = recvfrom (iface->sk_nl, buff, BUFF_SIZE, 0,
53 (struct sockaddr*)&from_nla, &from_nla_len);
54 if (from_nla.nl_pid != 0) {
55 continue;
56
57=== modified file 'lib/exec-monitor.h'
58--- lib/exec-monitor.h 2016-02-21 18:52:25 +0000
59+++ lib/exec-monitor.h 2016-08-30 20:17:54 +0000
60@@ -42,6 +42,7 @@
61 {
62 GTypeInterface parent_iface;
63 gboolean monitor_events;
64+ int sk_nl;
65
66 void (* handle_pid) (ExecMonitor *exec_monitor,
67 gint pid);
68
69=== modified file 'src/CMakeLists.txt'
70--- src/CMakeLists.txt 2016-02-25 18:47:22 +0000
71+++ src/CMakeLists.txt 2016-08-30 20:17:54 +0000
72@@ -23,8 +23,10 @@
73 Widgets/AppChooser.vala
74 Widgets/WeekSpinBox.vala
75 shared/Vars.vala
76- shared/PAMControl.vala
77 shared/Utils.vala
78+ shared/PAM/Token.vala
79+ shared/PAM/Reader.vala
80+ shared/PAM/Writer.vala
81 ${CMAKE_CURRENT_BINARY_DIR}/config.vala
82 PACKAGES
83 gtk+-3.0
84@@ -38,7 +40,6 @@
85 --thread
86 )
87
88-add_subdirectory (cli)
89 add_subdirectory (client)
90 add_subdirectory (daemon)
91
92
93=== modified file 'src/Plug.vala'
94--- src/Plug.vala 2016-06-16 20:22:58 +0000
95+++ src/Plug.vala 2016-08-30 20:17:54 +0000
96@@ -35,7 +35,6 @@
97 private Act.UserManager usermanager;
98
99 public MainBox () {
100- Utils.set_user_name (Environment.get_user_name ());
101 usermanager = Utils.get_usermanager ();
102
103 stack = new Gtk.Stack ();
104
105=== modified file 'src/Widgets/AppsBox.vala'
106--- src/Widgets/AppsBox.vala 2016-05-19 18:58:09 +0000
107+++ src/Widgets/AppsBox.vala 2016-08-30 20:17:54 +0000
108@@ -22,12 +22,6 @@
109
110 namespace PC.Widgets {
111 public class AppsBox : Gtk.Grid {
112- public signal void update_key_file ();
113-
114- public string[] targets = {};
115- public bool admin = false;
116- public bool daemon_active = false;
117-
118 private List<AppEntry> entries;
119 private Act.User user;
120
121@@ -37,7 +31,6 @@
122 private Gtk.Button remove_button;
123 private Gtk.Button clear_button;
124
125-
126 protected class AppEntry : Gtk.ListBoxRow {
127 public signal void deleted ();
128
129@@ -101,7 +94,7 @@
130 header_label.get_style_context ().add_class ("h4");
131
132 list_box = new Gtk.ListBox ();
133- list_box.row_selected.connect (on_changed);
134+ list_box.row_selected.connect (update_sensitivity);
135 scrolled.add (list_box);
136
137 var add_button = new Gtk.Button.from_icon_name ("list-add-symbolic", Gtk.IconSize.MENU);
138@@ -142,25 +135,16 @@
139
140 admin_switch_btn = new Gtk.Switch ();
141 admin_switch_btn.halign = Gtk.Align.START;
142- admin_switch_btn.notify["active"].connect (on_changed);
143+ admin_switch_btn.notify["active"].connect (update_admin);
144
145 attach (frame, 0, 0, 2, 1);
146 attach (admin_label, 0, 1, 1, 1);
147 attach (admin_switch_btn, 1, 1, 1, 1);
148
149- load_existing ();
150+ load_existing.begin ();
151 show_all ();
152 }
153
154- public bool get_active () {
155- return daemon_active;
156- }
157-
158- public void set_active (bool active) {
159- daemon_active = active;
160- on_changed ();
161- }
162-
163 private void on_add_button_clicked () {
164 apps_popover.show_all ();
165 }
166@@ -180,15 +164,17 @@
167 }
168
169 private void load_info (AppInfo info) {
170- if (!get_info_loaded (info)) {
171- var row = new AppEntry (info);
172- row.deleted.connect (on_deleted);
173-
174- entries.append (row);
175- list_box.add (row);
176- list_box.show_all ();
177- on_changed ();
178+ if (get_info_loaded (info)) {
179+ return;
180 }
181+
182+ var row = new AppEntry (info);
183+ row.deleted.connect (on_deleted);
184+
185+ entries.append (row);
186+ list_box.add (row);
187+ list_box.show_all ();
188+ update_targets ();
189 }
190
191 private bool get_info_loaded (AppInfo info) {
192@@ -204,39 +190,50 @@
193 private void on_deleted (AppEntry row) {
194 entries.remove (row);
195 row.destroy ();
196- on_changed ();
197- }
198-
199- private void on_changed () {
200+ update_targets ();
201+ }
202+
203+ private void update_admin () {
204+ update_sensitivity ();
205+
206+ if (Utils.get_permission ().get_allowed ()) {
207+ Utils.get_api ().set_user_daemon_admin.begin (user.get_user_name (), admin_switch_btn.get_active ());
208+ }
209+ }
210+
211+ private void update_targets () {
212+ update_sensitivity ();
213+
214+ if (!Utils.get_permission ().get_allowed ()) {
215+ return;
216+ }
217+
218+ string[] targets = {};
219+ foreach (var entry in entries) {
220+ targets += Environment.find_program_in_path (entry.get_executable ());
221+ }
222+
223+ Utils.get_api ().set_user_daemon_targets.begin (user.get_user_name (), targets);
224+ }
225+
226+ private void update_sensitivity () {
227 remove_button.sensitive = (list_box.get_selected_row () != null);
228 clear_button.sensitive = (entries.length () > 0);
229-
230- string[] _targets = {};
231- foreach (var entry in entries) {
232- _targets += Environment.find_program_in_path (entry.get_executable ());
233- }
234-
235- admin = admin_switch_btn.get_active ();
236- targets = _targets;
237- update_key_file ();
238 }
239
240- private void load_existing () {
241- var key_file = new KeyFile ();
242+ private async void load_existing () {
243 try {
244- key_file.load_from_file (Utils.build_daemon_conf_path (user), 0);
245- string[] _targets = key_file.get_string_list (Vars.DAEMON_GROUP, Vars.DAEMON_KEY_TARGETS);
246- daemon_active = key_file.get_boolean (Vars.DAEMON_GROUP, Vars.DAEMON_KEY_ACTIVE);
247+ string[] targets = yield Utils.get_api ().get_user_daemon_targets (user.get_user_name ());
248+ bool admin = yield Utils.get_api ().get_user_daemon_admin (user.get_user_name ());
249+ admin_switch_btn.set_active (admin);
250+
251 foreach (var info in AppInfo.get_all ()) {
252- if (info.should_show ()
253- && Environment.find_program_in_path (info.get_executable ()) in _targets) {
254+ if (info.should_show () && Environment.find_program_in_path (info.get_executable ()) in targets) {
255 load_info (info);
256 }
257 }
258- } catch (KeyFileError e) {
259- warning ("%s\n", e.message);
260- } catch (FileError e) {
261- warning ("%s\n", e.message);
262+ } catch (Error e) {
263+ warning (e.message);
264 }
265 }
266 }
267
268=== modified file 'src/Widgets/ControlPage.vala'
269--- src/Widgets/ControlPage.vala 2016-05-20 13:35:36 +0000
270+++ src/Widgets/ControlPage.vala 2016-08-30 20:17:54 +0000
271@@ -29,25 +29,6 @@
272 private AppsBox apps_box;
273 private KeyFile key_file;
274
275- public bool active {
276- get {
277- return apps_box.get_active ();
278- }
279- set {
280- apps_box.set_active (value);
281- if (Utils.get_permission ().get_allowed ()) {
282- if (value) {
283- general_box.refresh ();
284- Utils.call_cli ({"--user", user.get_user_name (), "--enable-restrict"});
285- } else {
286- general_box.set_lock_dock_active (false);
287- general_box.set_printer_active (true);
288- Utils.call_cli ({"--user", user.get_user_name (), "--disable-restrict"});
289- }
290- }
291- }
292- }
293-
294 public ControlPage (Act.User user) {
295 this.user = user;
296
297@@ -64,11 +45,9 @@
298
299 internet_box = new InternetBox (user);
300 internet_box.expand = true;
301- internet_box.update_key_file.connect (on_update_key_file);
302
303 apps_box = new AppsBox (user);
304 apps_box.expand = true;
305- apps_box.update_key_file.connect (on_update_key_file);
306
307 stack = new Gtk.Stack ();
308 stack.add_titled (general_box, "general", _("General"));
309@@ -87,24 +66,35 @@
310 show_all ();
311 }
312
313+ public void set_active (bool active) {
314+ if (Utils.get_permission ().get_allowed ()) {
315+ Utils.get_api ().set_user_daemon_active.begin (user.get_user_name (), active);
316+ if (active) {
317+ general_box.refresh ();
318+ general_box.update_pam ();
319+ } else {
320+ general_box.set_lock_dock_active (false);
321+ general_box.set_printer_active (true);
322+ Utils.get_api ().remove_restriction_for_user.begin (user.get_user_name ());
323+ }
324+ }
325+ }
326+
327+ public async bool get_active () {
328+ try {
329+ return yield Utils.get_api ().get_user_daemon_active (user.get_user_name ());
330+ } catch (IOError e) {
331+ warning (e.message);
332+ }
333+
334+ return false;
335+ }
336+
337 private void update_view_state () {
338 bool allowed = Utils.get_permission ().get_allowed ();
339 general_box.sensitive = allowed;
340 internet_box.sensitive = allowed;
341 apps_box.sensitive = allowed;
342 }
343-
344- private void on_update_key_file () {
345- if (!Utils.get_permission ().get_allowed ()) {
346- return;
347- }
348-
349- key_file.set_boolean (Vars.DAEMON_GROUP, Vars.DAEMON_KEY_ACTIVE, apps_box.daemon_active);
350- key_file.set_string_list (Vars.DAEMON_GROUP, Vars.DAEMON_KEY_TARGETS, apps_box.targets);
351- key_file.set_boolean (Vars.DAEMON_GROUP, Vars.DAEMON_KEY_ADMIN, apps_box.admin);
352- key_file.set_string_list (Vars.DAEMON_GROUP, Vars.DAEMON_KEY_BLOCK_URLS, internet_box.urls);
353-
354- Utils.call_cli ({ "--set-contents", key_file.to_data (), "--file", Utils.build_daemon_conf_path (user) });
355- }
356 }
357 }
358
359=== modified file 'src/Widgets/GeneralBox.vala'
360--- src/Widgets/GeneralBox.vala 2016-07-27 16:14:53 +0000
361+++ src/Widgets/GeneralBox.vala 2016-08-30 20:17:54 +0000
362@@ -50,8 +50,6 @@
363 weekday_box.changed.connect (update_pam);
364 weekend_box.changed.connect (update_pam);
365
366- monitor_updates ();
367- update ();
368 load_restrictions ();
369 }
370
371@@ -89,10 +87,10 @@
372
373 limit_combobox = new Gtk.ComboBoxText ();
374 limit_combobox.hexpand = true;
375- limit_combobox.append (Vars.ALL_ID, _("On weekdays and weekends"));
376- limit_combobox.append (Vars.WEEKDAYS_ID, _("Only on weekdays"));
377- limit_combobox.append (Vars.WEEKENDS_ID, _("Only on weekends"));
378- limit_combobox.active_id = "all";
379+ limit_combobox.append (PAM.DayType.WEEKDAY.to_string () + PAM.DayType.WEEKEND.to_string (), _("On weekdays and weekends"));
380+ limit_combobox.append (PAM.DayType.WEEKDAY.to_string (), _("Only on weekdays"));
381+ limit_combobox.append (PAM.DayType.WEEKEND.to_string (), _("Only on weekends"));
382+ limit_combobox.active = 0;
383
384 frame = new Gtk.Frame (null);
385 frame.get_style_context ().add_class (Gtk.STYLE_CLASS_VIEW);
386@@ -134,104 +132,54 @@
387 }
388
389 private void load_restrictions () {
390- var restricts = PAMControl.get_all_restrictions ();
391- foreach (var restrict in restricts) {
392- if (restrict.user == user.get_user_name ()) {
393- limit_switch.active = true;
394-
395- limit_combobox.active_id = restrict.day_id;
396- switch (restrict.day_id) {
397- case Vars.ALL_ID:
398- string from_weekday = restrict.weekday_hours.split ("-")[0];
399- string to_weekday = restrict.weekday_hours.split ("-")[1];
400- string from_weekend = restrict.weekend_hours.split ("-")[0];
401- string to_weekend = restrict.weekend_hours.split ("-")[1];
402-
403- weekday_box.set_from (from_weekday);
404- weekday_box.set_to (to_weekday);
405-
406- weekend_box.set_from (from_weekend);
407- weekend_box.set_to (to_weekend);
408- break;
409- case Vars.WEEKDAYS_ID:
410- weekday_box.set_from (restrict.from);
411- weekday_box.set_to (restrict.to);
412- break;
413- case Vars.WEEKENDS_ID:
414- weekend_box.set_from (restrict.from);
415- weekend_box.set_to (restrict.to);
416- break;
417- default:
418- break;
419- }
420+ var token = PAM.Reader.get_token_for_user (Vars.PAM_TIME_CONF_PATH, user.get_user_name ());
421+ if (token == null) {
422+ return;
423+ }
424+
425+ limit_switch.active = true;
426+
427+ string[] ids = {};
428+ foreach (PAM.TimeInfo info in token.get_times_info ()) {
429+ ids += info.day_type.to_string ();
430+ switch (info.day_type) {
431+ case PAM.DayType.WEEKDAY:
432+ weekday_box.set_from (info.from);
433+ weekday_box.set_to (info.to);
434+ break;
435+ case PAM.DayType.WEEKEND:
436+ weekend_box.set_from (info.from);
437+ weekend_box.set_to (info.to);
438+ break;
439+ default:
440+ break;
441 }
442 }
443+
444+ if (ids.length > 0) {
445+ limit_combobox.active_id = string.joinv ("|", ids);
446+ }
447 }
448
449- private void update_pam () {
450- string restrict = "";
451+ public void update_pam () {
452+ if (!Utils.get_permission ().get_allowed ()) {
453+ return;
454+ }
455+
456+ string[] times = {};
457+ string[] users = { user.get_user_name () };
458 string id = limit_combobox.get_active_id ();
459- switch (id) {
460- case Vars.ALL_ID:
461- restrict = generate_pam_conf_restriction (id, weekday_box.get_from (), weekday_box.get_to ());
462- restrict += "|" + weekend_box.get_from () + "-" + weekend_box.get_to ();
463- break;
464- case Vars.WEEKDAYS_ID:
465- restrict = generate_pam_conf_restriction (id, weekday_box.get_from (), weekday_box.get_to ());
466- break;
467- case Vars.WEEKENDS_ID:
468- restrict = generate_pam_conf_restriction (id, weekend_box.get_from (), weekend_box.get_to ());
469- break;
470- }
471-
472- PAMControl.try_add_restrict_line (user.get_user_name (), restrict);
473- }
474-
475- private string generate_pam_conf_restriction (string id, string from, string to) {
476- string retval = "*;*;";
477- string days = "";
478- switch (id) {
479- case Vars.ALL_ID:
480- days = "Al";
481- break;
482- case Vars.WEEKDAYS_ID:
483- days = "Wk";
484- break;
485- case Vars.WEEKENDS_ID:
486- days = "Wd";
487- break;
488- }
489-
490- retval += user.get_user_name () + ";" + days + from + "-" + to;
491- return retval;
492- }
493-
494- private void monitor_updates () {
495- try {
496- var monitor = File.new_for_path (plank_conf_file_path).monitor_file (FileMonitorFlags.NONE);
497- monitor.changed.connect (update);
498- } catch (IOError e) {
499- warning ("%s\n", e.message);
500- }
501- }
502-
503- private void update () {
504- var key_file = new KeyFile ();
505- try {
506- key_file.load_from_file (plank_conf_file_path, KeyFileFlags.NONE);
507- dock_btn.active = !key_file.get_boolean (Vars.PLANK_CONF_GROUP, Vars.PLANK_CONF_LOCK_ITEMS_KEY);
508- } catch (FileError e) {
509- dock_btn.active = true;
510- warning ("%s\n", e.message);
511- } catch (Error e) {
512- dock_btn.active = true;
513- warning ("%s\n", e.message);
514- }
515-
516- /* TODO: Get denied users for printing configuration */
517-
518- /* For now, we assume that printing is enabled */
519- print_btn.active = true;
520+
521+ if (PAM.DayType.WEEKDAY.to_string () in id) {
522+ times += PAM.DayType.WEEKDAY.to_string () + weekday_box.get_from () + "-" + weekday_box.get_to ();
523+ }
524+
525+ if (PAM.DayType.WEEKEND.to_string () in id) {
526+ times += PAM.DayType.WEEKEND.to_string () + weekend_box.get_from () + "-" + weekend_box.get_to ();
527+ }
528+
529+ string input = PAM.Token.construct_pam_restriction_simple (users, times);
530+ Utils.get_api ().add_restriction_for_user.begin (input, true);
531 }
532
533 private void on_dock_btn_activate () {
534@@ -240,7 +188,7 @@
535
536 public void set_lock_dock_active (bool active) {
537 if (Utils.get_permission ().get_allowed ()) {
538- Utils.call_cli ({"--user", user.get_user_name (), "--lock-dock", active.to_string ()});
539+ Utils.get_api ().lock_dock_icons_for_user.begin (user.get_user_name (), active);
540 }
541 }
542
543@@ -272,25 +220,23 @@
544 private void on_limit_switch_changed () {
545 if (limit_switch.get_active ()) {
546 update_pam ();
547- } else {
548- PAMControl.try_remove_user_restrict (user.get_user_name ());
549+ } else if (Utils.get_permission ().get_allowed ()) {
550+ Utils.get_api ().remove_restriction_for_user.begin (user.get_user_name ());
551 }
552 }
553
554 private void on_limit_combobox_changed () {
555- switch (limit_combobox.get_active_id ()) {
556- case Vars.ALL_ID:
557- weekday_box.sensitive = true;
558- weekend_box.sensitive = true;
559- break;
560- case Vars.WEEKDAYS_ID:
561- weekday_box.sensitive = true;
562- weekend_box.sensitive = false;
563- break;
564- case Vars.WEEKENDS_ID:
565- weekday_box.sensitive = false;
566- weekend_box.sensitive = true;
567- break;
568+ string id = limit_combobox.get_active_id ();
569+
570+ if (PAM.DayType.WEEKDAY.to_string () in id && PAM.DayType.WEEKEND.to_string () in id) {
571+ weekday_box.sensitive = true;
572+ weekend_box.sensitive = true;
573+ } else if (PAM.DayType.WEEKDAY.to_string () in id) {
574+ weekday_box.sensitive = true;
575+ weekend_box.sensitive = false;
576+ } else if (PAM.DayType.WEEKEND.to_string () in id) {
577+ weekday_box.sensitive = false;
578+ weekend_box.sensitive = true;
579 }
580
581 update_pam ();
582
583=== modified file 'src/Widgets/InternetBox.vala'
584--- src/Widgets/InternetBox.vala 2016-05-19 18:58:09 +0000
585+++ src/Widgets/InternetBox.vala 2016-08-30 20:17:54 +0000
586@@ -22,7 +22,6 @@
587
588 namespace PC.Widgets {
589 public class InternetBox : Gtk.Grid {
590- public signal void update_key_file ();
591 public string[] urls;
592
593 private const string URL_REGEX_RULE = "[-a-zA-Z0-9@:%._\\+~#=]{2,256}\\.[a-z]{2,4}\\b([-a-zA-Z0-9@:%_\\+.~#?&//=]*)";
594@@ -118,16 +117,14 @@
595 frame.add (main_box);
596
597 add (frame);
598- load_existing ();
599+ load_existing.begin ();
600 show_all ();
601 }
602
603- private void load_existing () {
604- var key_file = new KeyFile ();
605+ private async void load_existing () {
606 try {
607- key_file.load_from_file (Utils.build_daemon_conf_path (user), 0);
608- urls = key_file.get_string_list (Vars.DAEMON_GROUP, Vars.DAEMON_KEY_BLOCK_URLS);
609- foreach (string url in urls) {
610+ string[] block_urls = yield Utils.get_api ().get_user_daemon_block_urls (user.get_user_name ());
611+ foreach (string url in block_urls) {
612 add_entry (new UrlEntry (url));
613 }
614 } catch (Error e) {
615@@ -135,14 +132,17 @@
616 }
617 }
618
619- private void update () {
620- string[] _urls = {};
621+ private void update_block_urls () {
622+ if (!Utils.get_permission ().get_allowed ()) {
623+ return;
624+ }
625+
626+ string[] block_urls = {};
627 foreach (var url_entry in url_list) {
628- _urls += url_entry.get_url ();
629+ block_urls += url_entry.get_url ();
630 }
631
632- urls = _urls;
633- update_key_file ();
634+ Utils.get_api ().set_user_daemon_block_urls.begin (user.get_user_name (), block_urls);
635 }
636
637 private void on_entry_changed () {
638@@ -173,7 +173,7 @@
639 add_entry (url_entry);
640
641 entry.text = "";
642- update ();
643+ update_block_urls ();
644 }
645
646 private void add_entry (UrlEntry url_entry) {
647@@ -186,7 +186,7 @@
648 private void on_url_entry_deleted (UrlEntry url_entry) {
649 url_list.remove (url_entry);
650 list_box.remove (url_entry);
651- update ();
652+ update_block_urls ();
653 }
654 }
655 }
656
657=== modified file 'src/Widgets/UserItem.vala'
658--- src/Widgets/UserItem.vala 2016-05-20 21:18:29 +0000
659+++ src/Widgets/UserItem.vala 2016-08-30 20:17:54 +0000
660@@ -64,7 +64,10 @@
661 grid.attach (username_label, 1, 1, 1, 1);
662 grid.attach (master_switch, 2, 0, 1, 2);
663
664- master_switch.bind_property ("active", page, "active", BindingFlags.DEFAULT);
665+ master_switch.notify["active"].connect (() => {
666+ page.set_active (master_switch.get_active ());
667+ });
668+
669 master_switch.bind_property ("active", page.stack, "sensitive", BindingFlags.SYNC_CREATE);
670
671 Utils.get_permission ().notify["allowed"].connect (update_view);
672@@ -75,7 +78,10 @@
673 }
674
675 public void update_view () {
676- master_switch.active = page.active;
677+ page.get_active.begin ((obj, res) => {
678+ master_switch.active = page.get_active.end (res);
679+ });
680+
681 master_switch.sensitive = Utils.get_permission ().get_allowed ();
682
683 try {
684
685=== modified file 'src/Widgets/WeekSpinBox.vala'
686--- src/Widgets/WeekSpinBox.vala 2016-05-20 21:48:27 +0000
687+++ src/Widgets/WeekSpinBox.vala 2016-08-30 20:17:54 +0000
688@@ -32,14 +32,14 @@
689 spacing = 12;
690
691 picker_from = new Granite.Widgets.TimePicker ();
692+ picker_from.time_changed.connect (() => changed ());
693+
694 picker_to = new Granite.Widgets.TimePicker ();
695+ picker_to.time_changed.connect (() => changed ());
696
697 var label = new Gtk.Label (title);
698 label.get_style_context ().add_class ("h4");
699
700- picker_from.time_changed.connect (_changed);
701- picker_to.time_changed.connect (_changed);
702-
703 add (label);
704 add (new Gtk.Label (_("From:")));
705 add (picker_from);
706@@ -59,27 +59,21 @@
707 }
708
709 public void set_from (string from) {
710- char[] data = from.to_utf8 ();
711- string hours = data[0].to_string () + data[1].to_string ();
712- string minutes = data[2].to_string () + data[3].to_string ();
713+ string hours = from.slice (0, 2);
714+ string minutes = from.substring (2);
715
716 var time = new DateTime.local (new DateTime.now_local ().get_year (), 1, 1, int.parse (hours), int.parse (minutes), 0);
717 picker_from.time = time;
718 }
719
720 public void set_to (string to) {
721- char[] data = to.to_utf8 ();
722- string hours = data[0].to_string () + data[1].to_string ();
723- string minutes = data[2].to_string () + data[3].to_string ();
724+ string hours = to.slice (0, 2);
725+ string minutes = to.substring (2);
726
727 var time = new DateTime.local (new DateTime.now_local ().get_year (), 1, 1, int.parse (hours), int.parse (minutes), 0);
728 picker_to.time = time;
729 }
730
731- private void _changed () {
732- changed ();
733- }
734-
735 private string format_time_string (int val) {
736 if (val < 10) {
737 return "0" + val.to_string ();
738
739=== removed directory 'src/cli'
740=== removed file 'src/cli/Application.vala'
741--- src/cli/Application.vala 2016-05-18 15:29:00 +0000
742+++ src/cli/Application.vala 1970-01-01 00:00:00 +0000
743@@ -1,191 +0,0 @@
744-// -*- Mode: vala; indent-tabs-mode: nil; tab-width: 4 -*-
745-/*-
746- * Copyright (c) 2015 Adam Bieńkowski (https://launchpad.net/switchboard-plug-parental-controls)
747- *
748- * This library is free software; you can redistribute it and/or
749- * modify it under the terms of the GNU Library General Public
750- * License as published by the Free Software Foundation; either
751- * version 3 of the License, or (at your option) any later version.
752- *
753- * This library is distributed in the hope that it will be useful,
754- * but WITHOUT ANY WARRANTY; without even the implied warranty of
755- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
756- * Library General Public License for more details.
757- *
758- * You should have received a copy of the GNU Library General Public
759- * License along with this library; if not, write to the
760- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
761- * Boston, MA 02111-1307, USA.
762- *
763- * Authored by: Adam Bieńkowski <donadigos159@gmail.com>
764- */
765-
766-namespace PC.Cli {
767- public class App : Application {
768- private const string TIME_CONF_FILE = "/etc/security/time.conf";
769- private string plank_conf_file_path = "";
770-
771- public App () {
772- Object (flags: ApplicationFlags.HANDLES_COMMAND_LINE);
773- }
774-
775- public static int main (string[] args) {
776- var app = new App ();
777- return app.run (args);
778- }
779-
780- private int _command_line (ApplicationCommandLine command_line) {
781- string? user = null;
782- string? restrict_pam_line = null;
783- string? lock_dock = null;
784- string? set_contents = null;
785- string? file = null;
786-
787- bool remove_restrict = false;
788- bool enable_restrict = false;
789- bool disable_restrict = false;
790-
791- var options = new OptionEntry[9];
792- options[0] = { "user", 0, 0, OptionArg.STRING, ref user, "Use specific user", null };
793- options[1] = { "lock-dock", 0, 0, OptionArg.STRING, ref lock_dock, "Lock the given user dock", null };
794- options[2] = { "restrict-pam-line", 0, 0, OptionArg.STRING, ref restrict_pam_line, "Add specified line to pam configuration", null };
795- options[3] = { "remove-restrict", 0, 0, OptionArg.NONE, ref remove_restrict, "Remove all time restrictions for specified user", null };
796- options[4] = { "set-contents", 0, 0, OptionArg.STRING, ref set_contents, "Set contents of specified filename", null };
797- options[5] = { "file", 0, 0, OptionArg.FILENAME, ref file, "A file to write contents to", null };
798- options[6] = { "enable-restrict", 0, 0, OptionArg.NONE, ref enable_restrict, "Enable PAM restrictions for the user", null };
799- options[7] = { "disable-restrict", 0, 0, OptionArg.NONE, ref disable_restrict, "Disable PAM restrictions for the user", null };
800-
801- string[] args = command_line.get_arguments ();
802- string*[] _args = new string[args.length];
803- for (int i = 0; i < args.length; i++) {
804- _args[i] = args[i];
805- }
806-
807- try {
808- var opt_context = new OptionContext ("context");
809- opt_context.set_help_enabled (true);
810- opt_context.add_main_entries (options, null);
811- unowned string[] tmp = _args;
812- opt_context.parse (ref tmp);
813- } catch (OptionError e) {
814- command_line.print ("error: %s\n", e.message);
815- command_line.print ("Run '%s --help' to see a full list of available command line options.\n", args[0]);
816- return 1;
817- }
818-
819- if (Posix.getuid () != 0) {
820- command_line.print ("Error: To run this program you need root privigiles\n\n");
821- Process.exit (1);
822- }
823-
824- if (remove_restrict && user != "") {
825- var pam_writer = new PAMWriter (File.new_for_path (TIME_CONF_FILE));
826- pam_writer.remove_user_restrictions (user);
827- }
828-
829- if (enable_restrict && user != "") {
830- var pam_writer = new PAMWriter (File.new_for_path (TIME_CONF_FILE));
831- pam_writer.modify_user_restrictions (user, true);
832- }
833-
834- if (disable_restrict && user != "") {
835- var pam_writer = new PAMWriter (File.new_for_path (TIME_CONF_FILE));
836- pam_writer.modify_user_restrictions (user, false);
837- }
838-
839- if (restrict_pam_line != null && user != null) {
840- ensure_pam_lightdm_enabled ();
841-
842- var pam_writer = new PAMWriter (File.new_for_path (TIME_CONF_FILE));
843- pam_writer.add_conf_line (restrict_pam_line, user);
844- }
845-
846- if (user != null && lock_dock != null) {
847- var loop = new MainLoop ();
848-
849- Utils.get_usermanager ().notify["is-loaded"].connect (() => {
850- if (Utils.get_usermanager ().is_loaded) {
851- lock_dock_for_user (user, bool.parse (lock_dock));
852- loop.quit ();
853- }
854- });
855-
856- loop.run ();
857- }
858-
859- if (set_contents != null && file != null) {
860- set_file_contents (file, set_contents);
861- }
862-
863- return 0;
864- }
865-
866- private void set_file_contents (string path, string contents) {
867- var file = File.new_for_path (path);
868- if (!file.query_exists ()) {
869- try {
870- var os = file.create (FileCreateFlags.REPLACE_DESTINATION);
871- os.write (contents.data);
872- } catch (Error e) {
873- warning ("%s\n", e.message);
874- }
875- } else {
876- try {
877- FileUtils.set_contents (path, contents);
878- } catch (Error e) {
879- warning ("%s\n", e.message);
880- }
881- }
882- }
883-
884- private void ensure_pam_lightdm_enabled () {
885- string path = "/etc/pam.d/lightdm";
886- string contents = "";
887- try {
888- FileUtils.get_contents (path, out contents);
889- } catch (FileError e) {
890- warning ("%s\n", e.message);
891- }
892-
893- string conf_line = "\naccount required pam_time.so";
894- if (!(conf_line in contents)) {
895- contents += conf_line;
896-
897- try {
898- FileUtils.set_contents (path, contents);
899- } catch (FileError e) {
900- warning ("%s\n", e.message);
901- }
902- }
903- }
904-
905- private void lock_dock_for_user (string user, bool lock) {
906- plank_conf_file_path = Path.build_filename (Utils.get_usermanager ().get_user (user).get_home_dir (), Vars.PLANK_CONF_DIR);
907- if (plank_conf_file_path != "" && File.new_for_path (plank_conf_file_path).query_exists ()) {
908- var key_file = new KeyFile ();
909- try {
910- key_file.load_from_file (plank_conf_file_path, KeyFileFlags.KEEP_COMMENTS | KeyFileFlags.KEEP_TRANSLATIONS);
911- } catch (KeyFileError e) {
912- warning ("%s\n", e.message);
913- } catch (FileError e) {
914- warning ("%s\n", e.message);
915- }
916-
917- key_file.set_boolean (Vars.PLANK_CONF_GROUP, Vars.PLANK_CONF_LOCK_ITEMS_KEY, lock);
918-
919- try {
920- key_file.save_to_file (plank_conf_file_path);
921- } catch (FileError e) {
922- warning ("%s\n", e.message);
923- }
924- }
925- }
926-
927- public override int command_line (ApplicationCommandLine command_line) {
928- this.hold ();
929- int res = _command_line (command_line);
930- this.release ();
931- return res;
932- }
933- }
934-}
935\ No newline at end of file
936
937=== removed file 'src/cli/CMakeLists.txt'
938--- src/cli/CMakeLists.txt 2016-03-06 18:26:44 +0000
939+++ src/cli/CMakeLists.txt 1970-01-01 00:00:00 +0000
940@@ -1,28 +0,0 @@
941-find_package (PkgConfig)
942-
943-pkg_check_modules (CLI_DEPS REQUIRED glib-2.0 gio-2.0 polkit-gobject-1 accountsservice)
944-add_definitions (${CLI_DEPS_CFLAGS})
945-link_directories (${CLI_DEPS_LIBRARY_DIRS})
946-
947-set (CLI_EXEC_NAME pantheon-parental-controls-cli)
948-
949-vala_precompile (VALA_C ${CLI_EXEC_NAME}
950- Application.vala
951- PAMWriter.vala
952- ${CMAKE_SOURCE_DIR}/src/shared/Vars.vala
953- ${CMAKE_SOURCE_DIR}/src/shared/Utils.vala
954-PACKAGES
955- gio-2.0
956- accountsservice
957- polkit-gobject-1
958- posix
959-OPTIONS
960- --vapidir=${CMAKE_SOURCE_DIR}/vapi/
961- --thread
962-)
963-
964-add_executable (${CLI_EXEC_NAME} ${VALA_C})
965-target_link_libraries (${CLI_EXEC_NAME} ${DEPS_LIBRARIES})
966-
967-# Installation
968-install (TARGETS ${CLI_EXEC_NAME} RUNTIME DESTINATION bin)
969\ No newline at end of file
970
971=== removed file 'src/cli/PAMWriter.vala'
972--- src/cli/PAMWriter.vala 2016-03-06 18:26:44 +0000
973+++ src/cli/PAMWriter.vala 1970-01-01 00:00:00 +0000
974@@ -1,204 +0,0 @@
975-// -*- Mode: vala; indent-tabs-mode: nil; tab-width: 4 -*-
976-/*-
977- * Copyright (c) 2015 Adam Bieńkowski (https://launchpad.net/switchboard-plug-parental-controls)
978- *
979- * This library is free software; you can redistribute it and/or
980- * modify it under the terms of the GNU Library General Public
981- * License as published by the Free Software Foundation; either
982- * version 3 of the License, or (at your option) any later version.
983- *
984- * This library is distributed in the hope that it will be useful,
985- * but WITHOUT ANY WARRANTY; without even the implied warranty of
986- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
987- * Library General Public License for more details.
988- *
989- * You should have received a copy of the GNU Library General Public
990- * License along with this library; if not, write to the
991- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
992- * Boston, MA 02111-1307, USA.
993- *
994- * Authored by: Adam Bieńkowski <donadigos159@gmail.com>
995- */
996-
997-namespace PC.Cli {
998- public class PAMWriter : Object {
999- public File file;
1000-
1001- public PAMWriter (File file) {
1002- this.file = file;
1003- }
1004-
1005- public string get_conf_section () {
1006- string contents = read_contents (file);
1007- try {
1008- var regex = new Regex (Vars.PAM_CONF_REGEX);
1009-
1010- if (regex.match (contents)) {
1011- int i = 0;
1012- foreach (string str in regex.split (contents)) {
1013- // Do not replace the contents of the PC plug section
1014- if (i != 1) {
1015- contents = contents.replace (str, "");
1016- }
1017-
1018- i++;
1019- }
1020- } else {
1021- return "";
1022- }
1023- } catch (RegexError e) {
1024- warning ("%s\n", e.message);
1025- }
1026-
1027-
1028- return contents;
1029- }
1030-
1031- public void remove_conf_section () {
1032- string contents = read_contents (file);
1033- string final_contents = contents.replace (get_conf_section (), "");
1034-
1035- try {
1036- FileUtils.set_contents (file.get_path (), final_contents);
1037- } catch (FileError e) {
1038- warning ("%s\n", e.message);
1039- }
1040- }
1041-
1042- public void remove_user_restrictions (string user) {
1043- string contents = read_contents (file);
1044- string conf_section = get_conf_section ();
1045-
1046- string new_conf = "";
1047- if (conf_section != "" && user != "") {
1048- int i = 0;
1049- string[] split = conf_section.split ("\n");
1050- foreach (string section_line in split) {
1051- bool contains = (user in section_line);
1052- if (!contains) {
1053- if (i > 0) {
1054- new_conf += "\n";
1055- }
1056-
1057- new_conf += section_line;
1058- }
1059-
1060- i++;
1061- }
1062-
1063- try {
1064- FileUtils.set_contents (file.get_path (), contents.replace (conf_section, new_conf));
1065- } catch (FileError e) {
1066- warning ("%s\n", e.message);
1067- }
1068- }
1069- }
1070-
1071- public void modify_user_restrictions (string user, bool enable) {
1072- string contents = read_contents (file);
1073- string conf_section = get_conf_section ();
1074-
1075- string new_conf = "";
1076- if (conf_section != "" && user != "") {
1077- int i = 0;
1078- string[] split = conf_section.split ("\n");
1079- foreach (string section_line in split) {
1080- if (i > 0) {
1081- new_conf += "\n";
1082- }
1083-
1084- bool contains = (user in section_line);
1085- if (!contains) {
1086- new_conf += section_line;
1087- } else {
1088- string prefix = "#";
1089- if (enable) {
1090- prefix = "";
1091- section_line = section_line.replace ("#", "");
1092- }
1093-
1094- new_conf += prefix + section_line;
1095- }
1096-
1097- i++;
1098- }
1099-
1100- try {
1101- FileUtils.set_contents (file.get_path (), contents.replace (conf_section, new_conf));
1102- } catch (FileError e) {
1103- warning ("%s\n", e.message);
1104- }
1105- }
1106- }
1107-
1108- public void add_conf_line (string line, string? user = null) {
1109- string contents = read_contents (file);
1110- string conf_section = get_conf_section ();
1111-
1112- string new_conf = "";
1113- if (conf_section != "" && user != null) {
1114- int i = 0;
1115- string[] split = conf_section.split ("\n");
1116- foreach (string section_line in split) {
1117- bool contains = (user in section_line);
1118- if (!contains) {
1119- if (i > 0) {
1120- new_conf += "\n";
1121- }
1122-
1123- new_conf += section_line;
1124- }
1125-
1126- i++;
1127- }
1128-
1129- try {
1130- FileUtils.set_contents (file.get_path (), contents.replace (conf_section, new_conf));
1131- } catch (FileError e) {
1132- warning ("%s\n", e.message);
1133- }
1134-
1135- contents = read_contents (file);
1136- conf_section = get_conf_section ();
1137- }
1138-
1139-
1140- string final_contents = "";
1141-
1142- if (conf_section == "") {
1143- final_contents = contents +
1144- "\n" +
1145- Vars.PAM_CONF_START +
1146- "\n" +
1147- line +
1148- "\n" +
1149- Vars.PAM_CONF_END;
1150- } else {
1151- final_contents = conf_section.replace (Vars.PAM_CONF_END, "");
1152- final_contents += line + "\n" + Vars.PAM_CONF_END;
1153- final_contents = contents.replace (conf_section, final_contents);
1154- }
1155-
1156- try {
1157- FileUtils.set_contents (file.get_path (), final_contents);
1158- } catch (FileError e) {
1159- warning ("%s\n", e.message);
1160- }
1161- }
1162-
1163- private string read_contents (File file) {
1164- string data = "";
1165- if (!file.query_exists ()) {
1166- return "";
1167- }
1168-
1169- try {
1170- FileUtils.get_contents (file.get_path (), out data);
1171- } catch (FileError e) {
1172- warning ("%s\n", e.message);
1173- }
1174-
1175- return data;
1176- }
1177- }
1178-}
1179\ No newline at end of file
1180
1181=== renamed file 'src/client/AppLockDialog.vala' => 'src/client/AppUnavailableDialog.vala'
1182--- src/client/AppLockDialog.vala 2016-02-25 18:47:22 +0000
1183+++ src/client/AppUnavailableDialog.vala 2016-08-30 20:17:54 +0000
1184@@ -20,9 +20,9 @@
1185 * Authored by: Adam Bieńkowski <donadigos159@gmail.com>
1186 */
1187
1188-namespace AppLock {
1189- public class AppLockDialog : Gtk.MessageDialog {
1190- public AppLockDialog () {
1191+namespace PC.Client {
1192+ public class AppUnavailableDialog : Gtk.MessageDialog {
1193+ public AppUnavailableDialog () {
1194 deletable = false;
1195 text = _("You cannot run this application");
1196 secondary_text = _("You are not permitted to run this application.");
1197
1198=== modified file 'src/client/CMakeLists.txt'
1199--- src/client/CMakeLists.txt 2016-02-25 18:47:22 +0000
1200+++ src/client/CMakeLists.txt 2016-08-30 20:17:54 +0000
1201@@ -8,11 +8,13 @@
1202
1203 vala_precompile (VALA_C ${CLIENT_EXEC_NAME}
1204 Client.vala
1205- AppLockDialog.vala
1206+ AppUnavailableDialog.vala
1207 ${CMAKE_SOURCE_DIR}/src/shared/Vars.vala
1208+ ${CMAKE_SOURCE_DIR}/src/shared/Utils.vala
1209 PACKAGES
1210 posix
1211 polkit-gobject-1
1212+ accountsservice
1213 gio-2.0
1214 gtk+-3.0
1215 OPTIONS
1216
1217=== modified file 'src/client/Client.vala'
1218--- src/client/Client.vala 2016-07-31 00:18:06 +0000
1219+++ src/client/Client.vala 2016-08-30 20:17:54 +0000
1220@@ -20,19 +20,8 @@
1221 * Authored by: Adam Bieńkowski <donadigos159@gmail.com>
1222 */
1223
1224- namespace PC.Client {
1225- [DBus (name = "org.pantheon.ParentalControls")]
1226- public interface ParentalControls : Object {
1227- public signal void show_app_lock_dialog ();
1228- public signal void authorize (string user, string action_id);
1229- public signal void launch (string[] args);
1230- public signal void send_time_notification (int hours, int minutes);
1231-
1232- public abstract void end_authorization (int client_pid) throws IOError;
1233- }
1234-
1235+namespace PC.Client {
1236 public class Client : Gtk.Application {
1237- private ParentalControls? parental_controls;
1238 private Polkit.Permission? permission = null;
1239
1240 public static int main (string[] args) {
1241@@ -43,31 +32,22 @@
1242 }
1243
1244 public override void activate () {
1245- try {
1246- parental_controls = Bus.get_proxy_sync (BusType.SYSTEM, Vars.PARENTAL_CONTROLS_IFACE, Vars.PARENTAL_CONTROLS_OBJECT_PATH);
1247- } catch (Error e) {
1248- warning ("%s\n", e.message);
1249- return;
1250- }
1251-
1252- if (parental_controls == null) {
1253- return;
1254- }
1255-
1256- parental_controls.show_app_lock_dialog.connect (on_show_app_lock_dialog);
1257- parental_controls.authorize.connect (on_authorize);
1258- parental_controls.launch.connect (on_launch);
1259- parental_controls.send_time_notification.connect (on_send_time_notification);
1260+ var api = Utils.get_api ();
1261+
1262+ api.show_app_unavailable.connect (on_show_app_unavailable);
1263+ api.app_authorize.connect (on_authorize);
1264+ api.launch.connect (on_launch);
1265+ api.show_timeout.connect (on_show_timeout);
1266
1267 Gtk.main ();
1268 }
1269
1270- private void on_show_app_lock_dialog () {
1271- var app_lock_dialog = new AppLock.AppLockDialog ();
1272+ private void on_show_app_unavailable (string path) {
1273+ var app_lock_dialog = new AppUnavailableDialog ();
1274 app_lock_dialog.show_all ();
1275 }
1276
1277- private void on_authorize (string user_name, string action_id) {
1278+ private void on_authorize (string username, string path, string action_id) {
1279 if (permission != null && permission.get_can_release ()) {
1280 try {
1281 permission.release ();
1282@@ -77,36 +57,26 @@
1283 }
1284
1285 try {
1286- var user = (Polkit.UnixUser)Polkit.UnixUser.new_for_name (user_name);
1287+ var user = (Polkit.UnixUser)Polkit.UnixUser.new_for_name (username);
1288 permission = new Polkit.Permission.sync (action_id,
1289 Polkit.UnixProcess.new_for_owner (Posix.getpid (), 0, user.get_uid ()));
1290 } catch (Error e) {
1291 warning ("%s\n", e.message);
1292 }
1293
1294+ ulong signal_id = 0;
1295+ signal_id = permission.notify["allowed"].connect (() => {
1296+ Utils.get_api ().end_app_authorization.begin ();
1297+ permission.disconnect (signal_id);
1298+ });
1299+
1300 permission.acquire_async.begin ();
1301- permission.notify["allowed"].connect (() => {
1302- if (parental_controls != null) {
1303- try {
1304- parental_controls.end_authorization (Posix.getpid ());
1305- } catch (IOError e) {
1306- warning ("%s\n", e.message);
1307- }
1308- }
1309- });
1310 }
1311
1312 private void on_launch (string[] args) {
1313- string[] _args = {};
1314- for (int i = 0; i < args.length; i++) {
1315- if (args[i].strip () != "") {
1316- _args[i] = args[i];
1317- }
1318- }
1319-
1320 try {
1321 GLib.Process.spawn_async ("/",
1322- _args,
1323+ args,
1324 Environ.get (),
1325 SpawnFlags.SEARCH_PATH | SpawnFlags.DO_NOT_REAP_CHILD,
1326 null,
1327@@ -117,7 +87,7 @@
1328 }
1329
1330
1331- private void on_send_time_notification (int hours, int minutes) {
1332+ private void on_show_timeout (int hours, int minutes) {
1333 string hours_str = "";
1334 string minutes_str = "";
1335 string info = "";
1336@@ -138,8 +108,8 @@
1337 var notification = new Notification (_("Time left"));
1338 var icon = new ThemedIcon ("dialog-warning");
1339 notification.set_icon (icon);
1340-
1341 notification.set_body (body);
1342+
1343 send_notification (null, notification);
1344 }
1345 }
1346
1347=== modified file 'src/daemon/CMakeLists.txt'
1348--- src/daemon/CMakeLists.txt 2016-06-03 20:27:01 +0000
1349+++ src/daemon/CMakeLists.txt 2016-08-30 20:17:54 +0000
1350@@ -9,16 +9,20 @@
1351
1352 vala_precompile (VALA_C ${DAEMON_EXEC_NAME}
1353 Daemon.vala
1354- Core.vala
1355+ ProcessWatcher.vala
1356 Process.vala
1357 IptablesHelper.vala
1358+ Timer.vala
1359 SessionManager.vala
1360 SessionHandler.vala
1361 Interfaces.vala
1362+ UserConfig.vala
1363 Server.vala
1364 ${CMAKE_SOURCE_DIR}/src/shared/Vars.vala
1365 ${CMAKE_SOURCE_DIR}/src/shared/Utils.vala
1366- ${CMAKE_SOURCE_DIR}/src/shared/PAMControl.vala
1367+ ${CMAKE_SOURCE_DIR}/src/shared/PAM/Token.vala
1368+ ${CMAKE_SOURCE_DIR}/src/shared/PAM/Reader.vala
1369+ ${CMAKE_SOURCE_DIR}/src/shared/PAM/Writer.vala
1370 PACKAGES
1371 posix
1372 accountsservice
1373
1374=== modified file 'src/daemon/Daemon.vala'
1375--- src/daemon/Daemon.vala 2016-05-19 19:47:38 +0000
1376+++ src/daemon/Daemon.vala 2016-08-30 20:17:54 +0000
1377@@ -45,22 +45,16 @@
1378
1379 public static void on_exit (int signum) {
1380 if (session_manager != null) {
1381- session_manager.current_handler.stop ();
1382- session_manager.current_handler.unref ();
1383+ session_manager.stop ();
1384 }
1385
1386 terminate ();
1387 }
1388
1389 public override void activate () {
1390+ Utils.get_usermanager ().notify["is-loaded"].connect (on_usermanager_loaded);
1391+
1392 loop = new MainLoop ();
1393-
1394- Bus.own_name (BusType.SYSTEM, Vars.PARENTAL_CONTROLS_IFACE, BusNameOwnerFlags.REPLACE,
1395- on_bus_aquired,
1396- () => {},
1397- on_bus_lost);
1398- Utils.get_usermanager ().notify["is-loaded"].connect (on_usermanager_loaded);
1399-
1400 loop.run ();
1401 }
1402
1403@@ -68,7 +62,7 @@
1404 warning ("Could not acquire name: %s\n", name);
1405 }
1406
1407- private void on_bus_aquired (DBusConnection connection) {
1408+ private void on_bus_acquired (DBusConnection connection) {
1409 try {
1410 connection.register_object (Vars.PARENTAL_CONTROLS_OBJECT_PATH, Server.get_default ());
1411 } catch (IOError e) {
1412@@ -81,6 +75,11 @@
1413 return;
1414 }
1415
1416+ Bus.own_name (BusType.SYSTEM, Vars.PARENTAL_CONTROLS_IFACE, BusNameOwnerFlags.REPLACE,
1417+ on_bus_acquired,
1418+ () => {},
1419+ on_bus_lost);
1420+
1421 session_manager = new SessionManager ();
1422 session_manager.start ();
1423 }
1424
1425=== modified file 'src/daemon/Interfaces.vala'
1426--- src/daemon/Interfaces.vala 2016-05-18 14:56:00 +0000
1427+++ src/daemon/Interfaces.vala 2016-08-30 20:17:54 +0000
1428@@ -19,38 +19,34 @@
1429 *
1430 * Authored by: Adam Bieńkowski <donadigos159@gmail.com>
1431 */
1432-
1433-public struct SessionStruct {
1434- string name;
1435- uint32 uid;
1436- string user;
1437- string seat;
1438- GLib.ObjectPath object_path;
1439-}
1440-
1441-public struct ActiveSessionStruct {
1442- string session_id;
1443- GLib.ObjectPath object_path;
1444-}
1445-
1446-[DBus (name = "org.freedesktop.login1.Manager")]
1447-public interface IManager : Object {
1448- public abstract SessionStruct[] list_sessions () throws IOError;
1449- public abstract GLib.ObjectPath get_seat (string seat) throws IOError;
1450- public signal void session_new (string user, GLib.ObjectPath object_path);
1451- public signal void session_removed (string user, GLib.ObjectPath object_path);
1452-}
1453-
1454-[DBus (name = "org.freedesktop.login1.Session")]
1455-public interface ISession : Object {
1456- public abstract string name { owned get; }
1457-
1458- public abstract void lock () throws IOError;
1459-
1460- public signal void unlock ();
1461-}
1462-
1463-[DBus (name = "org.freedesktop.login1.Seat")]
1464-public interface ISeat : Object {
1465- public abstract ActiveSessionStruct active_session { owned get; }
1466+namespace PC.Daemon {
1467+ public struct SessionStruct {
1468+ string name;
1469+ uint32 uid;
1470+ string user;
1471+ string seat;
1472+ GLib.ObjectPath object_path;
1473+ }
1474+
1475+ public struct SeatStruct {
1476+ string seat_id;
1477+ GLib.ObjectPath object_path;
1478+ }
1479+
1480+ [DBus (name = "org.freedesktop.login1.Manager")]
1481+ public interface IManager : Object {
1482+ public abstract SessionStruct[] list_sessions () throws IOError;
1483+ public abstract SeatStruct[] list_seats () throws IOError;
1484+ public abstract GLib.ObjectPath get_seat (string seat) throws IOError;
1485+ public signal void session_new (string session, GLib.ObjectPath object_path);
1486+ public signal void session_removed (string session, GLib.ObjectPath object_path);
1487+ }
1488+
1489+ [DBus (name = "org.freedesktop.login1.Session")]
1490+ public interface ISession : Object {
1491+ public abstract bool active { owned get; }
1492+ public abstract string name { owned get; }
1493+ public abstract string id { owned get; }
1494+ public abstract void terminate () throws IOError;
1495+ }
1496 }
1497\ No newline at end of file
1498
1499=== modified file 'src/daemon/IptablesHelper.vala'
1500--- src/daemon/IptablesHelper.vala 2016-05-17 13:51:36 +0000
1501+++ src/daemon/IptablesHelper.vala 2016-08-30 20:17:54 +0000
1502@@ -20,66 +20,64 @@
1503 * Authored by: Adam Bieńkowski <donadigos159@gmail.com>
1504 */
1505
1506- namespace PC {
1507+ namespace PC.Daemon {
1508 public class IptablesHelper : Object {
1509- public bool valid = false;
1510-
1511 private const string IPTABLES_EXEC = "iptables";
1512- private const string HOST_EXEC = "host";
1513- private const int ADDRESS_INDEX = 3;
1514 private const int DPORT = 80;
1515
1516- private string[] urls;
1517-
1518- public IptablesHelper (string[] urls) {
1519- this.urls = urls;
1520- valid = Environment.find_program_in_path (IPTABLES_EXEC) != null &&
1521- Environment.find_program_in_path (HOST_EXEC) != null &&
1522- urls.length > 0;
1523- }
1524-
1525- public void add_rules () {
1526- foreach (string url in urls) {
1527- string[] addresses = get_addresses_from_url (url);
1528+ private UserConfig config;
1529+ private string[] current_urls;
1530+ private ulong changed_signal_id;
1531+
1532+ public static bool get_can_start () {
1533+ return Environment.find_program_in_path (IPTABLES_EXEC) != null;
1534+ }
1535+
1536+ public IptablesHelper (UserConfig config) {
1537+ this.config = config;
1538+ }
1539+
1540+ public void start () {
1541+ this.current_urls = config.get_block_urls ();
1542+ add_rules ();
1543+
1544+ changed_signal_id = config.changed.connect (() => {
1545+ remove_rules ();
1546+
1547+ current_urls = config.get_block_urls ();
1548+ add_rules ();
1549+ });
1550+ }
1551+
1552+ public void stop () {
1553+ remove_rules ();
1554+ disconnect (changed_signal_id);
1555+ }
1556+
1557+ private void add_rules () {
1558+ foreach (string url in current_urls) {
1559+ string[] addresses = get_addresses_from_name (url);
1560 foreach (string address in addresses) {
1561 process_adress (address, "-A");
1562 }
1563 }
1564 }
1565
1566- private string[] get_addresses_from_url (string url) {
1567- string[] result = {};
1568-
1569- string output;
1570- int status;
1571-
1572+ private string[] get_addresses_from_name (string name) {
1573+ string[] address_list = {};
1574+ var resolver = Resolver.get_default ();
1575 try {
1576- GLib.Process.spawn_sync ("/",
1577- { HOST_EXEC, url },
1578- Environ.get (),
1579- SpawnFlags.SEARCH_PATH,
1580- null,
1581- out output,
1582- null,
1583- out status);
1584- } catch (SpawnError e) {
1585- warning ("%s\n", e.message);
1586- }
1587-
1588- if (status != 0) {
1589- return result;
1590- }
1591-
1592- foreach (string line in output.split ("\n")) {
1593- if (line.contains ("has address")) {
1594- string[] elements = line.split (" ");
1595- if (elements.length >= 3) {
1596- result += elements[ADDRESS_INDEX];
1597+ var addresses = resolver.lookup_by_name (name, null);
1598+ foreach (InetAddress address in addresses) {
1599+ if (address.get_family () == SocketFamily.IPV4) {
1600+ address_list += address.to_string ();
1601 }
1602 }
1603+ } catch (Error e) {
1604+ warning (e.message);
1605 }
1606
1607- return result;
1608+ return address_list;
1609 }
1610
1611 private void process_adress (string address, string option) {
1612@@ -97,9 +95,9 @@
1613 }
1614 }
1615
1616- public void reset () {
1617- foreach (string url in urls) {
1618- string[] addresses = get_addresses_from_url (url);
1619+ private void remove_rules () {
1620+ foreach (string url in current_urls) {
1621+ string[] addresses = get_addresses_from_name (url);
1622 foreach (string address in addresses) {
1623 process_adress (address, "-D");
1624 }
1625
1626=== renamed file 'src/daemon/Core.vala' => 'src/daemon/ProcessWatcher.vala'
1627--- src/daemon/Core.vala 2016-05-19 19:54:37 +0000
1628+++ src/daemon/ProcessWatcher.vala 2016-08-30 20:17:54 +0000
1629@@ -21,68 +21,42 @@
1630 */
1631
1632 namespace PC.Daemon {
1633- public class Core : GLib.Object, ExecMonitor {
1634- public bool valid = true;
1635- public KeyFile key_file;
1636-
1637- private Act.User user;
1638-
1639- private string[] targets = {};
1640- private bool admin = false;
1641-
1642+ public class ProcessWatcher : GLib.Object, ExecMonitor {
1643+ public UserConfig? config = null;
1644 private List<string> allowed_executables;
1645-
1646 private Polkit.Authority authority;
1647- private Server server;
1648
1649- public Core (Act.User _user, Server _server) {
1650- user = _user;
1651- server = _server;
1652+ public ProcessWatcher () {
1653 allowed_executables = new List<string> ();
1654+
1655 try {
1656 authority = Polkit.Authority.get_sync ();
1657 } catch (Error e) {
1658 warning ("%s\n", e.message);
1659 }
1660-
1661- if (user == null) {
1662- valid = false;
1663- return;
1664- }
1665-
1666- key_file = new KeyFile ();
1667-
1668- string conf_path = Utils.build_daemon_conf_path (user);
1669- if (conf_path.strip () != "" && FileUtils.test (conf_path, FileTest.EXISTS)) {
1670- try {
1671- key_file.load_from_file (conf_path, 0);
1672-
1673- targets = key_file.get_string_list (Vars.DAEMON_GROUP, Vars.DAEMON_KEY_TARGETS);
1674- admin = key_file.get_boolean (Vars.DAEMON_GROUP, Vars.DAEMON_KEY_ADMIN);
1675-
1676- valid = targets.length > 0;
1677- } catch (FileError e) {
1678- warning ("%s\n", e.message);
1679- } catch (Error e) {
1680- warning ("%s\n", e.message);
1681- }
1682- } else {
1683- valid = false;
1684- }
1685+ }
1686+
1687+ public void set_config (UserConfig? config) {
1688+ this.config = config;
1689+ allowed_executables = new List<string> ();
1690 }
1691
1692 private void handle_pid (int pid) {
1693+ if (config == null) {
1694+ return;
1695+ }
1696+
1697 var process = new Process (pid);
1698
1699- string command = process.get_command ();
1700+ string? command = process.get_command ();
1701 if (command == null || command == "") {
1702 return;
1703 }
1704
1705 string[]? args = {};
1706 try {
1707- bool success = Shell.parse_argv (command, out args);
1708- if (!success || args == null || args.length < 1) {
1709+ Shell.parse_argv (command, out args);
1710+ if (args == null || args.length < 1) {
1711 return;
1712 }
1713 } catch (ShellError e) {
1714@@ -91,43 +65,44 @@
1715 }
1716
1717 string executable = args[0];
1718- foreach (string _executable in allowed_executables) {
1719- if (_executable == executable) {
1720- allowed_executables.remove (_executable);
1721- return;
1722- }
1723- }
1724
1725- if (executable != null && !executable.has_prefix (Path.DIR_SEPARATOR_S)) {
1726+ if (!executable.has_prefix (Path.DIR_SEPARATOR_S)) {
1727 executable = Environment.find_program_in_path (executable);
1728 }
1729
1730- if (executable != null && executable != "") {
1731- if (executable in targets) {
1732- process.kill ();
1733-
1734- if (admin && authority != null) {
1735- server.authorize (user.get_user_name (), Vars.PARENTAL_CONTROLS_ACTION_ID);
1736- ulong signal_id = 0;
1737- signal_id = server.authorization_ended.connect ((client_pid) => {
1738- try {
1739- var result = authority.check_authorization_sync (Polkit.UnixProcess.new_for_owner (client_pid, 0, (int)user.get_uid ()),
1740- Vars.PARENTAL_CONTROLS_ACTION_ID,
1741- null,
1742- Polkit.CheckAuthorizationFlags.ALLOW_USER_INTERACTION);
1743- if (result.get_is_authorized ()) {
1744- allowed_executables.append (args[0]);
1745- server.launch (args);
1746- }
1747- } catch (Error e) {
1748- warning ("%s\n", e.message);
1749+ unowned List<string> link = allowed_executables.find_custom (executable, strcmp);
1750+ if (link != null) {
1751+ allowed_executables.remove_link (link);
1752+ return;
1753+ }
1754+
1755+ if (executable != null && executable in config.get_targets ()) {
1756+ process.kill ();
1757+
1758+ var server = Server.get_default ();
1759+ if (config.get_admin () && authority != null) {
1760+ ulong signal_id = 0;
1761+ signal_id = server.app_authorization_ended.connect ((client_pid) => {
1762+ try {
1763+ var unix_user = (Polkit.UnixUser)Polkit.UnixUser.new_for_name (config.username);
1764+ var result = authority.check_authorization_sync (Polkit.UnixProcess.new_for_owner (client_pid, 0, unix_user.get_uid ()),
1765+ Vars.PARENTAL_CONTROLS_ACTION_ID,
1766+ null,
1767+ Polkit.CheckAuthorizationFlags.NONE);
1768+ if (result.get_is_authorized ()) {
1769+ allowed_executables.append (executable);
1770+ server.launch (args);
1771 }
1772-
1773- server.disconnect (signal_id);
1774- });
1775- } else {
1776- server.show_app_lock_dialog ();
1777- }
1778+ } catch (Error e) {
1779+ warning ("%s\n", e.message);
1780+ }
1781+
1782+ server.disconnect (signal_id);
1783+ });
1784+
1785+ server.app_authorize (config.username, executable, Vars.PARENTAL_CONTROLS_ACTION_ID);
1786+ } else {
1787+ server.show_app_unavailable (executable);
1788 }
1789 }
1790 }
1791
1792=== modified file 'src/daemon/Server.vala'
1793--- src/daemon/Server.vala 2016-02-25 18:47:22 +0000
1794+++ src/daemon/Server.vala 2016-08-30 20:17:54 +0000
1795@@ -21,9 +21,26 @@
1796 */
1797
1798 namespace PC.Daemon {
1799+ [DBus (name = "org.freedesktop.DBus")]
1800+ private interface DBus : Object {
1801+ [DBus (name = "GetConnectionUnixProcessID")]
1802+ public abstract uint32 get_connection_unix_process_id (string name) throws IOError;
1803+
1804+ public abstract uint32 get_connection_unix_user (string name) throws IOError;
1805+ }
1806+
1807+ [DBus (name = "org.pantheon.ParentalControls")]
1808+ public errordomain ParentalControlsError {
1809+ NOT_AUTHORIZED,
1810+ NOT_IMPLEMENTED,
1811+ USER_CONFIG_NOT_VAILD,
1812+ DBUS_CONNECTION_FAILED
1813+ }
1814+
1815 [DBus (name = "org.pantheon.ParentalControls")]
1816 public class Server : Object {
1817 private static Server? instance = null;
1818+ private DBus? bus_proxy = null;
1819
1820 [DBus (visible = false)]
1821 public static Server get_default () {
1822@@ -34,16 +51,205 @@
1823 return instance;
1824 }
1825
1826+ protected Server () {
1827+ try {
1828+ bus_proxy = Bus.get_proxy_sync (BusType.SYSTEM, "org.freedesktop.DBus", "/");
1829+ } catch (Error e) {
1830+ warning (e.message);
1831+ bus_proxy = null;
1832+ }
1833+
1834+ UserConfig.init ();
1835+ }
1836+
1837 [DBus (visible = false)]
1838- public signal void authorization_ended (int client_pid);
1839+ public signal void app_authorization_ended (int client_pid);
1840
1841- public signal void show_app_lock_dialog ();
1842- public signal void authorize (string user, string action_id);
1843+ public signal void app_authorize (string username, string path, string action_id);
1844 public signal void launch (string[] args);
1845- public signal void send_time_notification (int hours, int minutes);
1846-
1847- public void end_authorization (int client_pid) {
1848- authorization_ended (client_pid);
1849+ public signal void show_app_unavailable (string path);
1850+ public signal void show_timeout (int hours, int minutes);
1851+ public signal void user_config_changed (string username);
1852+
1853+ public void end_app_authorization (BusName sender) {
1854+ uint32 pid = get_pid_from_sender (sender);
1855+ app_authorization_ended ((int)pid);
1856+ }
1857+
1858+ public void add_restriction_for_user (string input, bool clean, BusName sender) throws ParentalControlsError {
1859+ if (!get_sender_is_authorized (sender)) {
1860+ throw new ParentalControlsError.NOT_AUTHORIZED ("Error: sender not authorized");
1861+ }
1862+
1863+ ensure_pam_lightdm_enabled ();
1864+
1865+ var writer = PAM.Writer.new_for_time ();
1866+ writer.add_restriction_for_user (input, clean);
1867+ }
1868+
1869+ public void remove_restriction_for_user (string username, BusName sender) throws ParentalControlsError {
1870+ if (!get_sender_is_authorized (sender)) {
1871+ throw new ParentalControlsError.NOT_AUTHORIZED ("Error: sender not authorized");
1872+ }
1873+
1874+ var writer = PAM.Writer.new_for_time ();
1875+ writer.remove_restriction_for_user (username);
1876+ }
1877+
1878+ public void lock_dock_icons_for_user (string username, bool lock, BusName sender) throws ParentalControlsError {
1879+ throw new ParentalControlsError.NOT_IMPLEMENTED ("Error: not implemented");
1880+ }
1881+
1882+ public void set_user_daemon_active (string username, bool active, BusName sender) throws ParentalControlsError {
1883+ if (!get_sender_is_authorized (sender)) {
1884+ throw new ParentalControlsError.NOT_AUTHORIZED ("Error: sender not authorized");
1885+ }
1886+
1887+ var config = UserConfig.get_for_username (username, true);
1888+ if (config == null) {
1889+ throw new ParentalControlsError.USER_CONFIG_NOT_VAILD ("Error: config for %s is not valid or could not be created".printf (username));
1890+ }
1891+
1892+ config.set_active (active);
1893+ }
1894+
1895+ public void set_user_daemon_targets (string username, string[] targets, BusName sender) throws ParentalControlsError {
1896+ if (!get_sender_is_authorized (sender)) {
1897+ throw new ParentalControlsError.NOT_AUTHORIZED ("Error: sender not authorized");
1898+ }
1899+
1900+ var config = UserConfig.get_for_username (username, true);
1901+ if (config == null) {
1902+ throw new ParentalControlsError.USER_CONFIG_NOT_VAILD ("Error: config for %s is not valid or could not be created".printf (username));
1903+ }
1904+
1905+ config.set_targets (targets);
1906+ }
1907+
1908+ public void set_user_daemon_block_urls (string username, string[] block_urls, BusName sender) throws ParentalControlsError {
1909+ if (!get_sender_is_authorized (sender)) {
1910+ throw new ParentalControlsError.NOT_AUTHORIZED ("Error: sender not authorized");
1911+ }
1912+
1913+ var config = UserConfig.get_for_username (username, true);
1914+ if (config == null) {
1915+ throw new ParentalControlsError.USER_CONFIG_NOT_VAILD ("Error: config for %s is not valid or could not be created".printf (username));
1916+ }
1917+
1918+ config.set_block_urls (block_urls);
1919+ }
1920+
1921+ public void set_user_daemon_admin (string username, bool admin, BusName sender) throws ParentalControlsError {
1922+ if (!get_sender_is_authorized (sender)) {
1923+ throw new ParentalControlsError.NOT_AUTHORIZED ("Error: sender not authorized");
1924+ }
1925+
1926+ var config = UserConfig.get_for_username (username, true);
1927+ if (config == null) {
1928+ throw new ParentalControlsError.USER_CONFIG_NOT_VAILD ("Error: config for %s is not valid or could not be created".printf (username));
1929+ }
1930+
1931+ config.set_admin (admin);
1932+ }
1933+
1934+ public bool get_user_daemon_active (string username) throws ParentalControlsError {
1935+ var config = UserConfig.get_for_username (username, false);
1936+ if (config == null) {
1937+ throw new ParentalControlsError.USER_CONFIG_NOT_VAILD ("Error: config for %s is not valid or does not exist".printf (username));
1938+ }
1939+
1940+ return config.get_active ();
1941+ }
1942+
1943+ public string[] get_user_daemon_targets (string username) throws ParentalControlsError {
1944+ var config = UserConfig.get_for_username (username, false);
1945+ if (config == null) {
1946+ throw new ParentalControlsError.USER_CONFIG_NOT_VAILD ("Error: config for %s is not valid or does not exist".printf (username));
1947+ }
1948+
1949+ return config.get_targets ();
1950+ }
1951+
1952+ public string[] get_user_daemon_block_urls (string username) throws ParentalControlsError {
1953+ var config = UserConfig.get_for_username (username, false);
1954+ if (config == null) {
1955+ throw new ParentalControlsError.USER_CONFIG_NOT_VAILD ("Error: config for %s is not valid or does not exist".printf (username));
1956+ }
1957+
1958+ return config.get_block_urls ();
1959+ }
1960+
1961+ public bool get_user_daemon_admin (string username) throws ParentalControlsError {
1962+ var config = UserConfig.get_for_username (username, false);
1963+ if (config == null) {
1964+ throw new ParentalControlsError.USER_CONFIG_NOT_VAILD ("Error: config for %s is not valid or does not exist".printf (username));
1965+ }
1966+
1967+ return config.get_admin ();
1968+ }
1969+
1970+ private void ensure_pam_lightdm_enabled () {
1971+ string path = "/etc/pam.d/lightdm";
1972+
1973+ string contents;
1974+ try {
1975+ FileUtils.get_contents (path, out contents);
1976+ } catch (FileError e) {
1977+ warning (e.message);
1978+ return;
1979+ }
1980+
1981+ string conf_line = "\naccount required pam_time.so";
1982+ if (conf_line in contents) {
1983+ return;
1984+ }
1985+
1986+ contents += conf_line;
1987+
1988+ try {
1989+ FileUtils.set_contents (path, contents);
1990+ } catch (FileError e) {
1991+ warning ("%s\n", e.message);
1992+ }
1993+ }
1994+
1995+ private bool get_sender_is_authorized (BusName sender) throws ParentalControlsError {
1996+ if (bus_proxy == null) {
1997+ throw new ParentalControlsError.DBUS_CONNECTION_FAILED ("Error: connecting to org.freedesktop.DBus failed.");
1998+ }
1999+
2000+ uint32 user = 0, pid = 0;
2001+
2002+ try {
2003+ pid = get_pid_from_sender (sender);
2004+ user = bus_proxy.get_connection_unix_user (sender);
2005+ } catch (Error e) {
2006+ warning (e.message);
2007+ }
2008+
2009+ var subject = Polkit.UnixProcess.new_for_owner ((int)pid, 0, (int)user);
2010+
2011+ try {
2012+ var authority = Polkit.Authority.get_sync (null);
2013+ var auth_result = authority.check_authorization_sync (subject, Vars.PARENTAL_CONTROLS_ACTION_ID, null, Polkit.CheckAuthorizationFlags.NONE);
2014+ return auth_result.get_is_authorized ();
2015+ } catch (Error e) {
2016+ warning (e.message);
2017+ }
2018+
2019+ return false;
2020+ }
2021+
2022+ private uint32 get_pid_from_sender (BusName sender) {
2023+ uint32 pid = 0;
2024+
2025+ try {
2026+ pid = bus_proxy.get_connection_unix_process_id (sender);
2027+ } catch (Error e) {
2028+ warning (e.message);
2029+ }
2030+
2031+ return pid;
2032 }
2033 }
2034 }
2035\ No newline at end of file
2036
2037=== modified file 'src/daemon/SessionHandler.vala'
2038--- src/daemon/SessionHandler.vala 2016-05-18 14:52:41 +0000
2039+++ src/daemon/SessionHandler.vala 2016-08-30 20:17:54 +0000
2040@@ -22,40 +22,46 @@
2041
2042 namespace PC.Daemon {
2043 public class SessionHandler : Object {
2044- private const int MINUTE_INTERVAL = 60;
2045- private const int HOUR_INTERVAL = 3600;
2046-
2047- public Core core;
2048- public IptablesHelper iptables_helper;
2049-
2050- private ISession session;
2051+ private IptablesHelper iptables_helper;
2052+ private Timer? timer;
2053+
2054+ private UserConfig config;
2055+ public ISession session;
2056 private Server server;
2057
2058 private bool can_start = true;
2059
2060- public SessionHandler (ISession _session) {
2061- session = _session;
2062+ public SessionHandler (ISession session) {
2063+ this.session = session;
2064 server = Server.get_default ();
2065- Utils.set_user_name (session.name);
2066-
2067- core = new Core (Utils.get_current_user (), server);
2068-
2069- try {
2070- if (!core.key_file.has_group (Vars.DAEMON_GROUP) || !core.key_file.get_boolean (Vars.DAEMON_GROUP, Vars.DAEMON_KEY_ACTIVE)) {
2071- can_start = false;
2072- }
2073- } catch (Error e) {
2074- warning ("%s\n", e.message);
2075- }
2076-
2077- string[] block_urls = {};
2078- try {
2079- block_urls = core.key_file.get_string_list (Vars.DAEMON_GROUP, Vars.DAEMON_KEY_BLOCK_URLS);
2080- } catch (KeyFileError e) {
2081- warning ("%s\n", e.message);
2082- }
2083-
2084- iptables_helper = new IptablesHelper (block_urls);
2085+
2086+ config = UserConfig.get_for_username (session.name, false);
2087+ if (config == null || !config.get_active ()) {
2088+ can_start = false;
2089+ return;
2090+ }
2091+
2092+ iptables_helper = new IptablesHelper (config);
2093+
2094+ var token = PAM.Reader.get_token_for_user (Vars.PAM_TIME_CONF_PATH, session.name);
2095+ if (token != null) {
2096+ timer = new Timer (token);
2097+ timer.terminate.connect (() => {
2098+ try {
2099+ session.terminate ();
2100+ } catch (IOError e) {
2101+ warning (e.message);
2102+ }
2103+ });
2104+ }
2105+ }
2106+
2107+ public string get_id () {
2108+ return session.id;
2109+ }
2110+
2111+ public UserConfig get_config () {
2112+ return config;
2113 }
2114
2115 public void start () {
2116@@ -63,142 +69,20 @@
2117 return;
2118 }
2119
2120- if (core.valid) {
2121- core.start.begin ();
2122- }
2123-
2124- if (iptables_helper.valid) {
2125- iptables_helper.add_rules ();
2126- }
2127-
2128- var restricts = PAMControl.get_all_restrictions ();
2129- foreach (var restrict in restricts) {
2130- if (restrict.user == Utils.get_current_user ().get_user_name ()) {
2131- var current_date = new DateTime.now_local ();
2132- string minute = current_date.get_minute ().to_string ();
2133- if (int.parse (minute) < 10) {
2134- minute = "0" + minute;
2135- }
2136-
2137- switch (restrict.day_id) {
2138- case Vars.WEEKDAYS_ID:
2139- if (current_date.get_day_of_week () < 6) {
2140- int estimated_time = int.parse (restrict.to);
2141- var span = get_difference_span (estimated_time, current_date);
2142- start_loop ((int)((span / GLib.TimeSpan.MINUTE * -1) + 1) - 24 * MINUTE_INTERVAL);
2143- }
2144-
2145- break;
2146- case Vars.WEEKENDS_ID:
2147- if (current_date.get_day_of_week () >= 6) {
2148- int estimated_time = int.parse (restrict.to);
2149- var span = get_difference_span (estimated_time, current_date);
2150- start_loop ((int)((span / GLib.TimeSpan.MINUTE * -1) + 1) - 24 * MINUTE_INTERVAL);
2151- }
2152-
2153- break;
2154- case Vars.ALL_ID:
2155- int estimated_time = 2400;
2156- if (current_date.get_day_of_week () < 6) {
2157- estimated_time = int.parse (restrict.weekday_hours.split ("-")[1]);
2158- } else if (current_date.get_day_of_week () >= 6) {
2159- estimated_time = int.parse (restrict.weekend_hours.split ("-")[1]);
2160- }
2161-
2162- var span = get_difference_span (estimated_time, current_date);
2163- int minutes = (int)((span / GLib.TimeSpan.MINUTE * -1) + 1) - 24 * MINUTE_INTERVAL;
2164- if (minutes > 0) {
2165- start_loop (minutes);
2166- } else {
2167- lock_session ();
2168- }
2169-
2170- break;
2171- default:
2172- break;
2173- }
2174- }
2175- }
2176+ if (IptablesHelper.get_can_start ()) {
2177+ iptables_helper.start ();
2178+ }
2179+
2180+ if (timer != null) {
2181+ timer.start ();
2182+ }
2183 }
2184
2185 public void stop () {
2186- core.stop ();
2187- iptables_helper.reset ();
2188- }
2189-
2190- private TimeSpan get_difference_span (int estimated_time, DateTime current_date) {
2191- bool end_day = (estimated_time == 2400 || estimated_time == 0);
2192- if (end_day) {
2193- estimated_time = 2359;
2194- }
2195-
2196- char[] tmp = estimated_time.to_string ().to_utf8 ();
2197- int _h, _m;
2198- _h = int.parse (tmp[0].to_string () + tmp[1].to_string ());
2199- _m = int.parse (tmp[2].to_string () + tmp[3].to_string ());
2200-
2201- var estimated_date = new DateTime.local (current_date.get_year (),
2202- current_date.get_month (),
2203- current_date.get_day_of_week (),
2204- _h, _m, 0);
2205-
2206- var span = current_date.difference (estimated_date);
2207- if (end_day) {
2208- span -= GLib.TimeSpan.MINUTE;
2209- }
2210-
2211- return span;
2212- }
2213-
2214- private int get_estimated_hours (int minutes) {
2215- if (minutes >= MINUTE_INTERVAL) {
2216- return minutes / MINUTE_INTERVAL;
2217- }
2218-
2219- return 0;
2220- }
2221-
2222- private void start_loop (int minutes) {
2223- int _hours = get_estimated_hours (minutes);
2224- int remaining_minutes = minutes - (MINUTE_INTERVAL * _hours);
2225- server.send_time_notification (_hours, remaining_minutes);
2226-
2227- schedule_notification (remaining_minutes, 5);
2228- schedule_notification (remaining_minutes, 1);
2229-
2230- Timeout.add_seconds (MINUTE_INTERVAL, () => {
2231- remaining_minutes--;
2232- if (remaining_minutes == 0) {
2233- lock_session ();
2234- }
2235-
2236- return (remaining_minutes != 0);
2237- });
2238-
2239- Timeout.add_seconds (HOUR_INTERVAL, () => {
2240- int hours = get_estimated_hours (minutes);
2241- if (hours > 0) {
2242- server.send_time_notification (hours, minutes - (MINUTE_INTERVAL * hours));
2243- }
2244-
2245- return (hours != 0 || minutes != 0);
2246- });
2247- }
2248-
2249- private void schedule_notification (int remaining_minutes, int minutes) {
2250- Timeout.add_seconds ((remaining_minutes - minutes) * MINUTE_INTERVAL, () => {
2251- server.send_time_notification (0, minutes);
2252-
2253- return false;
2254- });
2255- }
2256-
2257- private void lock_session () {
2258- try {
2259- session.lock ();
2260- } catch (IOError e) {
2261- warning ("%s\n", e.message);
2262- }
2263- }
2264+ iptables_helper.stop ();
2265+ if (timer != null) {
2266+ timer.stop ();
2267+ }
2268+ }
2269 }
2270 }
2271\ No newline at end of file
2272
2273=== modified file 'src/daemon/SessionManager.vala'
2274--- src/daemon/SessionManager.vala 2016-05-18 14:56:00 +0000
2275+++ src/daemon/SessionManager.vala 2016-08-30 20:17:54 +0000
2276@@ -22,10 +22,12 @@
2277
2278 namespace PC.Daemon {
2279 public class SessionManager : Object {
2280- public SessionHandler? current_handler = null;
2281-
2282+ private SessionHandler? current_handler = null;
2283 private IManager? manager = null;
2284 private DBusConnection? conn = null;
2285+ private ProcessWatcher pwatcher;
2286+
2287+ private uint[] signal_ids;
2288
2289 public SessionManager () {
2290 try {
2291@@ -34,6 +36,9 @@
2292 } catch (IOError e) {
2293 warning ("%s\n", e.message);
2294 }
2295+
2296+ pwatcher = new ProcessWatcher ();
2297+ pwatcher.start.begin ();
2298 }
2299
2300 public void start () {
2301@@ -44,22 +49,39 @@
2302 manager.session_new.connect (() => update_session ());
2303 manager.session_removed.connect (() => update_session ());
2304
2305- conn.signal_subscribe (null,
2306- Vars.DBUS_PROPERTIES_IFACE,
2307- "PropertiesChanged",
2308- get_current_seat_path (),
2309- null,
2310- 0,
2311- () => update_session ());
2312+ try {
2313+ foreach (SeatStruct seat_s in manager.list_seats ()) {
2314+ signal_ids += conn.signal_subscribe (null,
2315+ Vars.DBUS_PROPERTIES_IFACE,
2316+ "PropertiesChanged",
2317+ seat_s.object_path,
2318+ null,
2319+ 0,
2320+ () => update_session ());
2321+ }
2322+ } catch (IOError e) {
2323+ warning (e.message);
2324+ }
2325+
2326 update_session ();
2327 }
2328
2329+ public void stop () {
2330+ foreach (uint signal_id in signal_ids) {
2331+ conn.signal_unsubscribe (signal_id);
2332+ }
2333+
2334+ stop_current_handler ();
2335+ }
2336+
2337 private ISession? get_current_session () {
2338 try {
2339- string? seat_path = get_current_seat_path ();
2340- if (seat_path != null) {
2341- ISeat? seat = Bus.get_proxy_sync (BusType.SYSTEM, Vars.LOGIN_IFACE, seat_path);
2342- return Bus.get_proxy_sync (BusType.SYSTEM, Vars.LOGIN_IFACE, seat.active_session.object_path);
2343+ var structs = manager.list_sessions ();
2344+ foreach (SessionStruct session_s in structs) {
2345+ ISession? session = Bus.get_proxy_sync (BusType.SYSTEM, Vars.LOGIN_IFACE, session_s.object_path);
2346+ if (session != null && session.active) {
2347+ return session;
2348+ }
2349 }
2350 } catch (IOError e) {
2351 warning ("%s\n", e.message);
2352@@ -68,37 +90,30 @@
2353 return null;
2354 }
2355
2356- private string? get_current_seat_path () {
2357- try {
2358- string seat_id = "seat0";
2359- string? seat_variable = Environment.get_variable ("XDG_SEAT");
2360- if (seat_variable != null && seat_variable.has_prefix ("seat")) {
2361- seat_id = seat_variable;
2362- }
2363-
2364- return manager.get_seat (seat_id);
2365- } catch (Error e) {
2366- warning ("%s\n", e.message);
2367- }
2368-
2369- return null;
2370- }
2371-
2372 private void update_session () {
2373+ var session = get_current_session ();
2374+ if (session.id == current_handler.get_id ()) {
2375+ return;
2376+ }
2377+
2378+ stop_current_handler ();
2379+
2380+ if (session != null &&
2381+ session.name != null &&
2382+ !(session.name in Vars.DAEMON_IGNORED_USERS)) {
2383+ current_handler = new SessionHandler (session);
2384+ current_handler.start ();
2385+ pwatcher.set_config (current_handler.get_config ());
2386+ }
2387+ }
2388+
2389+ private void stop_current_handler () {
2390 if (current_handler != null) {
2391 current_handler.stop ();
2392- current_handler.unref ();
2393 current_handler = null;
2394 }
2395
2396- var current_session = get_current_session ();
2397- if (current_session != null &&
2398- current_session.name != null &&
2399- current_session.name.strip () != "" &&
2400- !(current_session.name in Vars.DAEMON_IGNORED_USERS)) {
2401- current_handler = new SessionHandler (current_session);
2402- current_handler.start ();
2403- }
2404+ pwatcher.set_config (null);
2405 }
2406 }
2407 }
2408\ No newline at end of file
2409
2410=== added file 'src/daemon/Timer.vala'
2411--- src/daemon/Timer.vala 1970-01-01 00:00:00 +0000
2412+++ src/daemon/Timer.vala 2016-08-30 20:17:54 +0000
2413@@ -0,0 +1,116 @@
2414+// -*- Mode: vala; indent-tabs-mode: nil; tab-width: 4 -*-
2415+/*-
2416+ * Copyright (c) 2016 Adam Bieńkowski (https://launchpad.net/switchboard-plug-parental-controls)
2417+ *
2418+ * This library is free software; you can redistribute it and/or
2419+ * modify it under the terms of the GNU Library General Public
2420+ * License as published by the Free Software Foundation; either
2421+ * version 3 of the License, or (at your option) any later version.
2422+ *
2423+ * This library is distributed in the hope that it will be useful,
2424+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
2425+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
2426+ * Library General Public License for more details.
2427+ *
2428+ * You should have received a copy of the GNU Library General Public
2429+ * License along with this library; if not, write to the
2430+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
2431+ * Boston, MA 02111-1307, USA.
2432+ *
2433+ * Authored by: Adam Bieńkowski <donadigos159@gmail.com>
2434+ */
2435+
2436+namespace PC.Daemon {
2437+ public class Timer : Object {
2438+ private const int MINUTE_INTERVAL = 60;
2439+ private const int HOUR_INTERVAL = 3600;
2440+
2441+ public signal void terminate ();
2442+
2443+ private PAM.Token token;
2444+ private uint[] timeout_ids;
2445+
2446+ public Timer (PAM.Token token) {
2447+ this.token = token;
2448+ }
2449+
2450+ public void start () {
2451+ var times_info = token.get_times_info ();
2452+ if (times_info.length () == 0) {
2453+ return;
2454+ }
2455+
2456+ PAM.TimeInfo? current = null;
2457+
2458+ int day_of_week = new DateTime.now_local ().get_day_of_week ();
2459+ foreach (PAM.TimeInfo info in times_info) {
2460+ if ((day_of_week < 6 && info.day_type == PAM.DayType.WEEKDAY)
2461+ || (day_of_week >= 6 && info.day_type == PAM.DayType.WEEKEND)
2462+ || info.day_type == PAM.DayType.ALL) {
2463+ current = info;
2464+ break;
2465+ }
2466+ }
2467+
2468+ if (current == null) {
2469+ return;
2470+ }
2471+
2472+ var span = get_difference_span (current.to);
2473+ int minutes = ((int)(span / GLib.TimeSpan.MINUTE)).abs ();
2474+
2475+ if (minutes > 0) {
2476+ start_loop (minutes);
2477+ } else {
2478+ terminate ();
2479+ }
2480+
2481+ timeout_ids += Timeout.add_seconds (HOUR_INTERVAL * 24, () => {
2482+ start ();
2483+ return true;
2484+ });
2485+ }
2486+
2487+ public void stop () {
2488+ foreach (uint timeout_id in timeout_ids) {
2489+ GLib.Source.remove (timeout_id);
2490+ }
2491+ }
2492+
2493+ private TimeSpan get_difference_span (string estimated_time_str) {
2494+ int hour = int.parse (estimated_time_str.slice (0, 2));
2495+ int minute = int.parse (estimated_time_str.substring (2));
2496+
2497+ if (hour == 24) {
2498+ hour = 0;
2499+ }
2500+
2501+ var current_date = new DateTime.now_local ();
2502+ var estimated_date = current_date.add_full (0, 0,
2503+ (hour < current_date.get_hour ()) ? 1 : 0,
2504+ hour - current_date.get_hour (),
2505+ minute - current_date.get_minute (),
2506+ 0);
2507+ return estimated_date.difference (current_date);
2508+ }
2509+
2510+ private void start_loop (int minutes) {
2511+ Server.get_default ().show_timeout (minutes / MINUTE_INTERVAL, minutes % MINUTE_INTERVAL);
2512+ timeout_ids += Timeout.add_seconds (MINUTE_INTERVAL, () => {
2513+ minutes--;
2514+ if (minutes == MINUTE_INTERVAL ||
2515+ minutes == 10 ||
2516+ minutes == 5 ||
2517+ minutes == 1) {
2518+ Server.get_default ().show_timeout (minutes / MINUTE_INTERVAL, minutes % MINUTE_INTERVAL);
2519+ }
2520+
2521+ if (minutes == 0) {
2522+ terminate ();
2523+ }
2524+
2525+ return (minutes > 0);
2526+ });
2527+ }
2528+ }
2529+}
2530\ No newline at end of file
2531
2532=== added file 'src/daemon/UserConfig.vala'
2533--- src/daemon/UserConfig.vala 1970-01-01 00:00:00 +0000
2534+++ src/daemon/UserConfig.vala 2016-08-30 20:17:54 +0000
2535@@ -0,0 +1,208 @@
2536+// -*- Mode: vala; indent-tabs-mode: nil; tab-width: 4 -*-
2537+/*-
2538+ * Copyright (c) 2015-2016 Adam Bieńkowski (https://launchpad.net/switchboard-plug-parental-controls)
2539+ *
2540+ * This library is free software; you can redistribute it and/or
2541+ * modify it under the terms of the GNU Library General Public
2542+ * License as published by the Free Software Foundation; either
2543+ * version 3 of the License, or (at your option) any later version.
2544+ *
2545+ * This library is distributed in the hope that it will be useful,
2546+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
2547+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
2548+ * Library General Public License for more details.
2549+ *
2550+ * You should have received a copy of the GNU Library General Public
2551+ * License along with this library; if not, write to the
2552+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
2553+ * Boston, MA 02111-1307, USA.
2554+ *
2555+ * Authored by: Adam Bieńkowski <donadigos159@gmail.com>
2556+ */
2557+
2558+namespace PC.Daemon {
2559+ public class UserConfig : Object {
2560+ public string username;
2561+ public signal void changed ();
2562+
2563+ private KeyFile key;
2564+ private string config_path;
2565+
2566+ private static List<UserConfig> config_list;
2567+
2568+ public static UserConfig? get_for_username (string username, bool create) {
2569+ foreach (UserConfig config in config_list) {
2570+ if (config.username == username) {
2571+ return config;
2572+ }
2573+ }
2574+
2575+ if (create) {
2576+ return create_for_username (username);
2577+ }
2578+
2579+ return null;
2580+ }
2581+
2582+ public static List<UserConfig> get_all () {
2583+ return config_list.copy ();
2584+ }
2585+
2586+ public static void init () {
2587+ config_list = new List<UserConfig> ();
2588+
2589+ foreach (Act.User user in Utils.get_usermanager ().list_users ()) {
2590+ string config_path = Utils.build_daemon_conf_path (user);
2591+ if (!FileUtils.test (config_path, FileTest.IS_REGULAR)) {
2592+ continue;
2593+ }
2594+
2595+ var key = new KeyFile ();
2596+ key.set_list_separator (';');
2597+ try {
2598+ if (!key.load_from_file (config_path, KeyFileFlags.NONE)) {
2599+ continue;
2600+ }
2601+ } catch (KeyFileError e) {
2602+ warning (e.message);
2603+ continue;
2604+ } catch (FileError e) {
2605+ warning (e.message);
2606+ continue;
2607+ }
2608+
2609+ var user_config = new UserConfig (config_path, user.get_user_name (), key);
2610+ config_list.append (user_config);
2611+ }
2612+ }
2613+
2614+ private static UserConfig? create_for_username (string username) {
2615+ var user = Utils.get_usermanager ().get_user (username);
2616+ if (user == null) {
2617+ return null;
2618+ }
2619+
2620+ string config_path = Utils.build_daemon_conf_path (user);
2621+ var file = File.new_for_path (config_path);
2622+ try {
2623+ file.create (FileCreateFlags.PRIVATE);
2624+ } catch (FileError e) {
2625+ warning (e.message);
2626+ return null;
2627+ } catch (Error e) {
2628+ warning (e.message);
2629+ return null;
2630+ }
2631+
2632+ var config = new UserConfig (config_path, username, new KeyFile ());
2633+ config_list.append (config);
2634+ return config;
2635+ }
2636+
2637+ private UserConfig (string config_path, string username, KeyFile key) {
2638+ this.username = username;
2639+ this.key = key;
2640+ this.config_path = config_path;
2641+ monitor_file ();
2642+ }
2643+
2644+ public void set_active (bool active) {
2645+ key.set_boolean (Vars.DAEMON_GROUP, Vars.DAEMON_KEY_ACTIVE, active);
2646+ save ();
2647+ }
2648+
2649+ public void set_targets (string[] targets) {
2650+ key.set_string_list (Vars.DAEMON_GROUP, Vars.DAEMON_KEY_TARGETS, targets);
2651+ save ();
2652+ }
2653+
2654+ public void set_block_urls (string[] block_urls) {
2655+ key.set_string_list (Vars.DAEMON_GROUP, Vars.DAEMON_KEY_BLOCK_URLS, block_urls);
2656+ save ();
2657+ }
2658+
2659+ public void set_admin (bool admin) {
2660+ key.set_boolean (Vars.DAEMON_GROUP, Vars.DAEMON_KEY_ADMIN, admin);
2661+ save ();
2662+ }
2663+
2664+ public bool get_active () {
2665+ try {
2666+ return key.get_boolean (Vars.DAEMON_GROUP, Vars.DAEMON_KEY_ACTIVE);
2667+ } catch (KeyFileError e) {
2668+ warning (e.message);
2669+ }
2670+
2671+ return false;
2672+ }
2673+
2674+ public string[] get_targets () {
2675+ try {
2676+ return key.get_string_list (Vars.DAEMON_GROUP, Vars.DAEMON_KEY_TARGETS);
2677+ } catch (KeyFileError e) {
2678+ warning (e.message);
2679+ }
2680+
2681+ return {};
2682+ }
2683+
2684+ public string[] get_block_urls () {
2685+ try {
2686+ return key.get_string_list (Vars.DAEMON_GROUP, Vars.DAEMON_KEY_BLOCK_URLS);
2687+ } catch (KeyFileError e) {
2688+ warning (e.message);
2689+ }
2690+
2691+ return {};
2692+ }
2693+
2694+ public bool get_admin () {
2695+ try {
2696+ return key.get_boolean (Vars.DAEMON_GROUP, Vars.DAEMON_KEY_ADMIN);
2697+ } catch (KeyFileError e) {
2698+ warning (e.message);
2699+ }
2700+
2701+ return false;
2702+ }
2703+
2704+ private void monitor_file () {
2705+ var file = File.new_for_path (config_path);
2706+ try {
2707+ var monitor = file.monitor (FileMonitorFlags.NONE, null);
2708+ monitor.changed.connect ((src, dest, event) => {
2709+ if (event == FileMonitorEvent.CHANGES_DONE_HINT) {
2710+ update_key ();
2711+
2712+ Server.get_default ().user_config_changed (username);
2713+ changed ();
2714+ }
2715+ });
2716+ } catch (Error e) {
2717+ warning (e.message);
2718+ }
2719+ }
2720+
2721+ private void update_key () {
2722+ try {
2723+ key.load_from_file (config_path, KeyFileFlags.NONE);
2724+ } catch (KeyFileError e) {
2725+ warning (e.message);
2726+ } catch (FileError e) {
2727+ warning (e.message);
2728+ }
2729+ }
2730+
2731+ private void save () {
2732+ try {
2733+ key.save_to_file (config_path);
2734+ } catch (FileError e) {
2735+ warning (e.message);
2736+ return;
2737+ }
2738+
2739+ Server.get_default ().user_config_changed (username);
2740+ changed ();
2741+ }
2742+ }
2743+}
2744\ No newline at end of file
2745
2746=== added directory 'src/shared/PAM'
2747=== added file 'src/shared/PAM/Reader.vala'
2748--- src/shared/PAM/Reader.vala 1970-01-01 00:00:00 +0000
2749+++ src/shared/PAM/Reader.vala 2016-08-30 20:17:54 +0000
2750@@ -0,0 +1,66 @@
2751+// -*- Mode: vala; indent-tabs-mode: nil; tab-width: 4 -*-
2752+/*-
2753+ * Copyright (c) 2015 Adam Bieńkowski (https://launchpad.net/switchboard-plug-parental-controls)
2754+ *
2755+ * This library is free software; you can redistribute it and/or
2756+ * modify it under the terms of the GNU Library General Public
2757+ * License as published by the Free Software Foundation; either
2758+ * version 3 of the License, or (at your option) any later version.
2759+ *
2760+ * This library is distributed in the hope that it will be useful,
2761+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
2762+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
2763+ * Library General Public License for more details.
2764+ *
2765+ * You should have received a copy of the GNU Library General Public
2766+ * License along with this library; if not, write to the
2767+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
2768+ * Boston, MA 02111-1307, USA.
2769+ *
2770+ * Authored by: Adam Bieńkowski <donadigos159@gmail.com>
2771+ */
2772+
2773+namespace PC.PAM {
2774+ public class Reader : Object {
2775+ public static string get_config (string contents, bool ignore_comments = true) {
2776+ string config = "";
2777+
2778+ int start_idx = contents.index_of (Vars.PAM_CONF_START);
2779+ int end_idx = contents.index_of (Vars.PAM_CONF_END) + Vars.PAM_CONF_END.char_count ();
2780+ if (start_idx == -1 || end_idx == -1) {
2781+ return config;
2782+ }
2783+
2784+ config = contents.slice (start_idx, end_idx);
2785+
2786+ if (ignore_comments) {
2787+ return Utils.remove_comments (config);
2788+ }
2789+
2790+ return config;
2791+ }
2792+
2793+ public static List<Token> get_tokens (string filename) {
2794+ string contents;
2795+ try {
2796+ FileUtils.get_contents (filename, out contents);
2797+ } catch (FileError e) {
2798+ warning (e.message);
2799+ return new List<Token> ();
2800+ }
2801+
2802+ string config = get_config (contents);
2803+ return Token.parse (config);
2804+ }
2805+
2806+ public static Token? get_token_for_user (string filename, string username) {
2807+ foreach (Token token in get_tokens (filename)) {
2808+ if (token.get_user_arg0 () == username) {
2809+ return token;
2810+ }
2811+ }
2812+
2813+ return null;
2814+ }
2815+ }
2816+}
2817\ No newline at end of file
2818
2819=== added file 'src/shared/PAM/Token.vala'
2820--- src/shared/PAM/Token.vala 1970-01-01 00:00:00 +0000
2821+++ src/shared/PAM/Token.vala 2016-08-30 20:17:54 +0000
2822@@ -0,0 +1,190 @@
2823+// -*- Mode: vala; indent-tabs-mode: nil; tab-width: 4 -*-
2824+/*-
2825+ * Copyright (c) 2015 Adam Bieńkowski (https://launchpad.net/switchboard-plug-parental-controls)
2826+ *
2827+ * This library is free software; you can redistribute it and/or
2828+ * modify it under the terms of the GNU Library General Public
2829+ * License as published by the Free Software Foundation; either
2830+ * version 3 of the License, or (at your option) any later version.
2831+ *
2832+ * This library is distributed in the hope that it will be useful,
2833+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
2834+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
2835+ * Library General Public License for more details.
2836+ *
2837+ * You should have received a copy of the GNU Library General Public
2838+ * License along with this library; if not, write to the
2839+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
2840+ * Boston, MA 02111-1307, USA.
2841+ *
2842+ * Authored by: Adam Bieńkowski <donadigos159@gmail.com>
2843+ */
2844+
2845+namespace PC.PAM {
2846+ public enum DayType {
2847+ UNKNOWN,
2848+ ALL,
2849+ WEEKDAY,
2850+ WEEKEND;
2851+
2852+ public static DayType to_enum (string str) {
2853+ switch (str) {
2854+ case "Al":
2855+ return ALL;
2856+ case "Wk":
2857+ return WEEKDAY;
2858+ case "Wd":
2859+ return WEEKEND;
2860+ default:
2861+ return UNKNOWN;
2862+ }
2863+ }
2864+
2865+ public string to_string () {
2866+ switch (this) {
2867+ case ALL:
2868+ return "Al";
2869+ case WEEKDAY:
2870+ return "Wk";
2871+ case WEEKEND:
2872+ return "Wd";
2873+ default:
2874+ case UNKNOWN:
2875+ return "unknown";
2876+ }
2877+ }
2878+ }
2879+
2880+ public class TimeInfo {
2881+ public DayType day_type = DayType.UNKNOWN;
2882+ public string from = "";
2883+ public string to = "";
2884+ }
2885+
2886+ public class Token : Object {
2887+ private const int SERVICES_INDEX = 0;
2888+ private const int TTYS_INDEX = 1;
2889+ private const int USERS_INDEX = 2;
2890+ private const int TIMES_INDEX = 3;
2891+
2892+ private const string TYPE_SEPARATOR = ";";
2893+ private const string LIST_SEPARATOR = "|";
2894+
2895+ public string[] services;
2896+ public string[] ttys;
2897+ public string[] users;
2898+ public string[] times;
2899+
2900+ public static Token? parse_line (string line) {
2901+ string[] strv = line.split (TYPE_SEPARATOR);
2902+ if (strv.length != 4) {
2903+ return null;
2904+ }
2905+
2906+ var token = new Token ();
2907+ token.services = strv[SERVICES_INDEX].split (LIST_SEPARATOR);
2908+ token.ttys = strv[TTYS_INDEX].split (LIST_SEPARATOR);
2909+ token.users = strv[USERS_INDEX].split (LIST_SEPARATOR);
2910+ token.times = strv[TIMES_INDEX].split (LIST_SEPARATOR);
2911+ return token;
2912+ }
2913+
2914+ public static List<Token> parse (string str) {
2915+ var list = new List<Token> ();
2916+
2917+ foreach (string line in str.split ("\n")) {
2918+ var token = parse_line (line);
2919+ if (token != null) {
2920+ list.append (token);
2921+ }
2922+ }
2923+
2924+ return list;
2925+ }
2926+
2927+ public static string construct_pam_restriction (string[] services, string[] ttys, string users[], string[] times) {
2928+ string services_str = string.joinv (LIST_SEPARATOR, services);
2929+ string ttys_str = string.joinv (LIST_SEPARATOR, ttys);
2930+ string users_str = string.joinv (LIST_SEPARATOR, users);
2931+ string times_str = string.joinv (LIST_SEPARATOR, times);
2932+
2933+ return "%s;%s;%s;%s".printf (services_str, ttys_str, users_str, times_str);
2934+ }
2935+
2936+ public static string construct_pam_restriction_simple (string[] users, string[] times) {
2937+ return construct_pam_restriction ({ "*" }, { "*" }, users, times);
2938+ }
2939+
2940+ public List<TimeInfo> get_times_info () {
2941+ var list = new List<TimeInfo> ();
2942+
2943+ if (times.length == 0) {
2944+ return list;
2945+ }
2946+
2947+ foreach (string time in times) {
2948+ string[] bounds = time.substring (2).split ("-");
2949+ if (bounds.length < 2) {
2950+ continue;
2951+ }
2952+
2953+ var info = new TimeInfo ();
2954+ info.day_type = DayType.to_enum (time.slice (0, 2));
2955+ info.from = bounds[0];
2956+ info.to = bounds[1];
2957+
2958+ list.append (info);
2959+ }
2960+
2961+ return list;
2962+ }
2963+
2964+ public string get_user_arg0 () {
2965+ if (users.length == 0) {
2966+ return "";
2967+ }
2968+
2969+ return users[0];
2970+ }
2971+
2972+ public string to_string () {
2973+ return construct_pam_restriction (services, ttys, users, times);
2974+ }
2975+
2976+ public void get_weekday_hours (out int from, out int to) {
2977+ if (times.length < 1) {
2978+ from = 0;
2979+ to = 0;
2980+ return;
2981+ }
2982+
2983+ string[] bounds = times[0].substring (2).split ("-");
2984+ if (bounds.length < 2) {
2985+ from = 0;
2986+ to = 0;
2987+ return;
2988+ }
2989+
2990+ from = int.parse (bounds[0]);
2991+ to = int.parse (bounds[1]);
2992+ }
2993+
2994+ public void get_weekend_hours (out int from, out int to) {
2995+ if (times.length < 2) {
2996+ from = 0;
2997+ to = 0;
2998+ return;
2999+ }
3000+
3001+ string[] bounds = times[1].split ("-");
3002+ if (bounds.length < 2) {
3003+ from = 0;
3004+ to = 0;
3005+ return;
3006+ }
3007+
3008+ from = int.parse (bounds[0]);
3009+ to = int.parse (bounds[1]);
3010+ }
3011+ }
3012+}
3013\ No newline at end of file
3014
3015=== added file 'src/shared/PAM/Writer.vala'
3016--- src/shared/PAM/Writer.vala 1970-01-01 00:00:00 +0000
3017+++ src/shared/PAM/Writer.vala 2016-08-30 20:17:54 +0000
3018@@ -0,0 +1,110 @@
3019+// -*- Mode: vala; indent-tabs-mode: nil; tab-width: 4 -*-
3020+/*-
3021+ * Copyright (c) 2015 Adam Bieńkowski (https://launchpad.net/switchboard-plug-parental-controls)
3022+ *
3023+ * This library is free software; you can redistribute it and/or
3024+ * modify it under the terms of the GNU Library General Public
3025+ * License as published by the Free Software Foundation; either
3026+ * version 3 of the License, or (at your option) any later version.
3027+ *
3028+ * This library is distributed in the hope that it will be useful,
3029+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
3030+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
3031+ * Library General Public License for more details.
3032+ *
3033+ * You should have received a copy of the GNU Library General Public
3034+ * License along with this library; if not, write to the
3035+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
3036+ * Boston, MA 02111-1307, USA.
3037+ *
3038+ * Authored by: Adam Bieńkowski <donadigos159@gmail.com>
3039+ */
3040+
3041+namespace PC.PAM {
3042+ public class Writer : Object {
3043+ private string filename;
3044+
3045+ public static Writer new_for_time () {
3046+ return new Writer (Vars.PAM_TIME_CONF_PATH);
3047+ }
3048+
3049+ public Writer (string filename) {
3050+ this.filename = filename;
3051+ }
3052+
3053+ public void add_restriction_for_user (string input, bool clean) {
3054+ if (clean) {
3055+ var token = Token.parse_line (input);
3056+ if (token != null) {
3057+ string username = token.get_user_arg0 ();
3058+ if (Reader.get_token_for_user (filename, username) != null) {
3059+ remove_restriction_for_user (username);
3060+ }
3061+ }
3062+ }
3063+
3064+ string contents;
3065+ try {
3066+ FileUtils.get_contents (filename, out contents);
3067+ } catch (FileError e) {
3068+ warning (e.message);
3069+ return;
3070+ }
3071+
3072+ string config = Reader.get_config (contents, false);
3073+
3074+ var builder = new StringBuilder (Vars.PAM_CONF_START);
3075+ if (config != "") {
3076+ builder.append ("\n");
3077+ builder.append (Utils.remove_comments (config));
3078+ } else {
3079+ builder.append ("\n");
3080+ }
3081+
3082+ builder.append (input);
3083+ builder.append ("\n");
3084+ builder.append (Vars.PAM_CONF_END);
3085+
3086+ try {
3087+ if (config != "") {
3088+ FileUtils.set_contents (filename, contents.replace (config, builder.str));
3089+ } else {
3090+ FileUtils.set_contents (filename, "\n%s\n%s\n".printf (contents, builder.str));
3091+ }
3092+ } catch (FileError e) {
3093+ warning ("%s\n", e.message);
3094+ }
3095+ }
3096+
3097+ public void remove_restriction_for_user (string username) {
3098+ string contents;
3099+ try {
3100+ FileUtils.get_contents (filename, out contents);
3101+ } catch (FileError e) {
3102+ warning (e.message);
3103+ return;
3104+ }
3105+
3106+ string config = Reader.get_config (contents);
3107+
3108+ if (config == "") {
3109+ return;
3110+ }
3111+
3112+ string buffer = "";
3113+
3114+ foreach (string line in config.split ("\n")) {
3115+ var token = Token.parse_line (line);
3116+ if (token != null && token.get_user_arg0 () != username) {
3117+ buffer += line;
3118+ }
3119+ }
3120+
3121+ try {
3122+ FileUtils.set_contents (filename, contents.replace (config, buffer));
3123+ } catch (FileError e) {
3124+ warning ("%s\n", e.message);
3125+ }
3126+ }
3127+ }
3128+}
3129\ No newline at end of file
3130
3131=== removed file 'src/shared/PAMControl.vala'
3132--- src/shared/PAMControl.vala 2016-02-21 18:52:25 +0000
3133+++ src/shared/PAMControl.vala 1970-01-01 00:00:00 +0000
3134@@ -1,128 +0,0 @@
3135-// -*- Mode: vala; indent-tabs-mode: nil; tab-width: 4 -*-
3136-/*-
3137- * Copyright (c) 2015 Adam Bieńkowski (https://launchpad.net/switchboard-plug-parental-controls)
3138- *
3139- * This library is free software; you can redistribute it and/or
3140- * modify it under the terms of the GNU Library General Public
3141- * License as published by the Free Software Foundation; either
3142- * version 3 of the License, or (at your option) any later version.
3143- *
3144- * This library is distributed in the hope that it will be useful,
3145- * but WITHOUT ANY WARRANTY; without even the implied warranty of
3146- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
3147- * Library General Public License for more details.
3148- *
3149- * You should have received a copy of the GNU Library General Public
3150- * License along with this library; if not, write to the
3151- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
3152- * Boston, MA 02111-1307, USA.
3153- *
3154- * Authored by: Adam Bieńkowski <donadigos159@gmail.com>
3155- */
3156-
3157-namespace PC {
3158- public class PAMRestrictInfo : Object {
3159- public string user;
3160- public string day_id;
3161- public string weekday_hours;
3162- public string weekend_hours;
3163- public string from;
3164- public string to;
3165- }
3166-
3167- public class PAMControl : Object {
3168- private static string get_conf_section () {
3169- string contents = "";
3170- try {
3171- FileUtils.get_contents ("/etc/security/time.conf", out contents);
3172- } catch (FileError e) {
3173- warning ("%s\n", e.message);
3174- }
3175-
3176- try {
3177- var regex = new Regex (Vars.PAM_CONF_REGEX);
3178-
3179- if (regex.match (contents)) {
3180- int i = 0;
3181- foreach (string str in regex.split (contents)) {
3182- // Do not replace the contents of the PC plug section
3183- if (i != 1) {
3184- contents = contents.replace (str, "");
3185- }
3186-
3187- i++;
3188- }
3189- } else {
3190- return "";
3191- }
3192- } catch (RegexError e) {
3193- warning ("%s\n", e.message);
3194- }
3195-
3196-
3197- return contents;
3198- }
3199-
3200- public static List<PAMRestrictInfo?> get_all_restrictions () {
3201- var retval = new List<PAMRestrictInfo?> ();
3202- string conf = get_conf_section ();
3203-
3204- foreach (string line in conf.split ("\n")) {
3205- if (!line.has_prefix ("#")) {
3206- var restrict_info = new PAMRestrictInfo ();
3207- string[] units = line.split (";");
3208- if (units.length >= 4) {
3209- restrict_info.user = units[2];
3210- string time = units[3];
3211- time = time.replace ("!", "");
3212-
3213- char[] time_chars = time.to_utf8 ();
3214- string day = time_chars[0].to_string () + time_chars[1].to_string ();
3215- switch (day.down ()) {
3216- case "al":
3217- restrict_info.day_id = "all";
3218- break;
3219- case "wk":
3220- restrict_info.day_id = "weekdays";
3221- break;
3222- case "wd":
3223- restrict_info.day_id = "weekends";
3224- break;
3225- default:
3226- restrict_info.day_id = "";
3227- break;
3228- }
3229-
3230- string hours = time.replace (day, "");
3231- string[] tmp = {};
3232- if ("|" in hours) {
3233- tmp = hours.split ("|");
3234- restrict_info.weekday_hours = tmp[0];
3235- restrict_info.weekend_hours = tmp[1];
3236- } else {
3237- tmp = hours.split ("-");
3238- restrict_info.from = tmp[0];
3239- restrict_info.to = tmp[1];
3240- }
3241-
3242- retval.append (restrict_info);
3243- }
3244- }
3245- }
3246-
3247- return retval;
3248- }
3249-
3250- public static void try_add_restrict_line (string user_name, string restrict) {
3251- if (Utils.get_permission ().allowed) {
3252- Utils.call_cli ({"--user", user_name, "--restrict-pam-line", restrict});
3253- }
3254- }
3255-
3256- public static void try_remove_user_restrict (string user_name) {
3257- if (Utils.get_permission ().allowed) {
3258- Utils.call_cli ({"--user", user_name, "--remove-restrict"});
3259- }
3260- }
3261- }
3262-}
3263\ No newline at end of file
3264
3265=== modified file 'src/shared/Utils.vala'
3266--- src/shared/Utils.vala 2016-07-27 16:14:53 +0000
3267+++ src/shared/Utils.vala 2016-08-30 20:17:54 +0000
3268@@ -27,11 +27,62 @@
3269 public abstract void printer_set_users_denied (string printer, string[] users) throws IOError;
3270 }
3271
3272- public class Utils : Object {
3273- public static Polkit.Permission? permission = null;
3274- public static string user_name;
3275-
3276+ [DBus (name = "org.pantheon.ParentalControls")]
3277+ public interface IParentalControls : Object {
3278+ public abstract async void add_restriction_for_user (string input, bool clean) throws IOError;
3279+ public abstract async void remove_restriction_for_user (string username) throws IOError;
3280+ public abstract async void end_app_authorization () throws IOError;
3281+ public abstract async bool get_user_daemon_active (string username) throws IOError;
3282+ public abstract async bool get_user_daemon_admin (string username) throws IOError;
3283+ public abstract async string[] get_user_daemon_block_urls (string username) throws IOError;
3284+ public abstract async string[] get_user_daemon_targets (string username) throws IOError;
3285+ public abstract async void lock_dock_icons_for_user (string username, bool lock) throws IOError;
3286+ public abstract async void set_user_daemon_active (string username, bool active) throws IOError;
3287+ public abstract async void set_user_daemon_admin (string username, bool admin) throws IOError;
3288+ public abstract async void set_user_daemon_block_urls (string username, string[] block_urls) throws IOError;
3289+ public abstract async void set_user_daemon_targets (string username, string[] targets) throws IOError;
3290+
3291+ public signal void app_authorize (string username, string path, string action_id);
3292+ public signal void launch (string[] args);
3293+ public signal void show_app_unavailable (string path);
3294+ public signal void show_timeout (int hours, int minutes);
3295+ public signal void user_config_changed (string username);
3296+ }
3297+
3298+ public class Utils {
3299+ public class DummyParentalControls : Object, IParentalControls {
3300+ public async void add_restriction_for_user (string input, bool clean) throws IOError {}
3301+ public async void remove_restriction_for_user (string username) throws IOError {}
3302+ public async void end_app_authorization () throws IOError {}
3303+ public async bool get_user_daemon_active (string username) throws IOError { return false; }
3304+ public async bool get_user_daemon_admin (string username) throws IOError { return false; }
3305+ public async string[] get_user_daemon_block_urls (string username) throws IOError { return {}; }
3306+ public async string[] get_user_daemon_targets (string username) throws IOError { return {}; }
3307+ public async void lock_dock_icons_for_user (string username, bool lock) throws IOError {}
3308+ public async void set_user_daemon_active (string username, bool active) throws IOError {}
3309+ public async void set_user_daemon_admin (string username, bool admin) throws IOError {}
3310+ public async void set_user_daemon_block_urls (string username, string[] block_urls) throws IOError {}
3311+ public async void set_user_daemon_targets (string username, string[] targets) throws IOError {}
3312+ }
3313+
3314+ private static Polkit.Permission? permission = null;
3315 private static Act.UserManager? usermanager = null;
3316+ private static IParentalControls? api = null;
3317+
3318+ public static IParentalControls? get_api () {
3319+ if (api != null) {
3320+ return api;
3321+ }
3322+
3323+ try {
3324+ api = Bus.get_proxy_sync (BusType.SYSTEM, Vars.PARENTAL_CONTROLS_IFACE, Vars.PARENTAL_CONTROLS_OBJECT_PATH);
3325+ } catch (Error e) {
3326+ critical ("%s, using dummy parental controls backend", e.message);
3327+ api = new DummyParentalControls ();
3328+ }
3329+
3330+ return api;
3331+ }
3332
3333 public static Polkit.Permission? get_permission () {
3334 if (permission != null) {
3335@@ -39,9 +90,9 @@
3336 }
3337
3338 try {
3339- var user = (Polkit.UnixUser)Polkit.UnixUser.new_for_name (user_name);
3340+ var user = (Polkit.UnixUser)Polkit.UnixUser.new_for_name (Environment.get_user_name ());
3341 permission = new Polkit.Permission.sync (Vars.PARENTAL_CONTROLS_ACTION_ID,
3342- Polkit.UnixProcess.new_for_owner (Posix.getpid (), 0, user.get_uid ()));
3343+ Polkit.UnixProcess.new_for_owner (Posix.getpid (), 0, user.get_uid ()));
3344 return permission;
3345 } catch (Error e) {
3346 critical (e.message);
3347@@ -56,29 +107,6 @@
3348 return @"<span font_weight=\"bold\" size=\"large\">$escaped_name</span>\n$escaped_comment";
3349 }
3350
3351- public static void call_cli (string[] args) {
3352- string[] spawn_args = { "pkexec", "pantheon-parental-controls-cli" };
3353- foreach (string arg in args) {
3354- spawn_args += arg;
3355- }
3356-
3357- try {
3358- Pid child_pid;
3359- Process.spawn_async ("/",
3360- spawn_args,
3361- Environ.get (),
3362- SpawnFlags.SEARCH_PATH | SpawnFlags.DO_NOT_REAP_CHILD,
3363- null,
3364- out child_pid);
3365- ChildWatch.add (child_pid, (pid, status) => {
3366- Process.close_pid (pid);
3367- });
3368-
3369- } catch (SpawnError e) {
3370- warning ("%s\n", e.message);
3371- }
3372- }
3373-
3374 public static unowned Act.UserManager? get_usermanager () {
3375 if (usermanager != null) {
3376 return usermanager;
3377@@ -89,15 +117,24 @@
3378 }
3379
3380 public static unowned Act.User? get_current_user () {
3381- return get_usermanager ().get_user (user_name);
3382+ return get_usermanager ().get_user (Environment.get_user_name ());
3383+ }
3384+
3385+ public static string remove_comments (string str) {
3386+ string buffer = "";
3387+
3388+ foreach (string line in str.split ("\n")) {
3389+ if (!line.strip ().has_prefix ("#")) {
3390+ buffer += line;
3391+ buffer += "\n";
3392+ }
3393+ }
3394+
3395+ return buffer;
3396 }
3397
3398 public static string build_daemon_conf_path (Act.User user) {
3399 return Path.build_filename (user.get_home_dir (), Vars.DAEMON_CONF_DIR);
3400 }
3401-
3402- public static void set_user_name (string _user_name) {
3403- user_name = _user_name;
3404- }
3405 }
3406 }
3407
3408=== modified file 'src/shared/Vars.vala'
3409--- src/shared/Vars.vala 2016-07-27 16:14:53 +0000
3410+++ src/shared/Vars.vala 2016-08-30 20:17:54 +0000
3411@@ -41,7 +41,5 @@
3412 public const string PAM_CONF_START = "## PANTHEON_PARENTAL_CONTROLS_START";
3413 public const string PAM_CONF_END = "## PANTHEON_PARENTAL_CONTROLS_END";
3414 public const string PAM_CONF_REGEX = PAM_CONF_START + "|" + PAM_CONF_END;
3415- public const string ALL_ID = "all";
3416- public const string WEEKDAYS_ID = "weekdays";
3417- public const string WEEKENDS_ID = "weekends";
3418+ public const string PAM_TIME_CONF_PATH = "/etc/security/time.conf";
3419 }
3420\ No newline at end of file

Subscribers

People subscribed via source and target branches

to all changes: