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
=== modified file 'lib/exec-monitor.c'
--- lib/exec-monitor.c 2016-05-19 19:47:38 +0000
+++ lib/exec-monitor.c 2016-08-30 20:17:54 +0000
@@ -110,12 +110,12 @@
110110
111 iface = EXEC_MONITOR_GET_IFACE (self);111 iface = EXEC_MONITOR_GET_IFACE (self);
112 iface->monitor_events = FALSE;112 iface->monitor_events = FALSE;
113 close (iface->sk_nl);
113}114}
114115
115void116void
116exec_monitor_start_internal (ExecMonitor *self)117exec_monitor_start_internal (ExecMonitor *self)
117{118{
118 int sk_nl;
119 int err;119 int err;
120 struct sockaddr_nl my_nla, kern_nla, from_nla;120 struct sockaddr_nl my_nla, kern_nla, from_nla;
121 socklen_t from_nla_len;121 socklen_t from_nla_len;
@@ -132,8 +132,8 @@
132 ExecMonitorInterface *iface;132 ExecMonitorInterface *iface;
133 iface = EXEC_MONITOR_GET_IFACE (self);133 iface = EXEC_MONITOR_GET_IFACE (self);
134134
135 sk_nl = socket (PF_NETLINK, SOCK_DGRAM, NETLINK_CONNECTOR);135 iface->sk_nl = socket (PF_NETLINK, SOCK_DGRAM, NETLINK_CONNECTOR);
136 if (sk_nl == -1) {136 if (iface->sk_nl == -1) {
137 return;137 return;
138 }138 }
139139
@@ -145,7 +145,7 @@
145 kern_nla.nl_groups = CN_IDX_PROC;145 kern_nla.nl_groups = CN_IDX_PROC;
146 kern_nla.nl_pid = 1;146 kern_nla.nl_pid = 1;
147147
148 err = bind (sk_nl, (struct sockaddr *)&my_nla, sizeof (my_nla));148 err = bind (iface->sk_nl, (struct sockaddr *)&my_nla, sizeof (my_nla));
149 if (err == -1) {149 if (err == -1) {
150 return;150 return;
151 }151 }
@@ -169,7 +169,7 @@
169 cn_hdr->ack = 0;169 cn_hdr->ack = 0;
170 cn_hdr->len = sizeof (enum proc_cn_mcast_op);170 cn_hdr->len = sizeof (enum proc_cn_mcast_op);
171171
172 if (send (sk_nl, nl_hdr, nl_hdr->nlmsg_len, 0) != nl_hdr->nlmsg_len) {172 if (send (iface->sk_nl, nl_hdr, nl_hdr->nlmsg_len, 0) != nl_hdr->nlmsg_len) {
173 return;173 return;
174 }174 }
175175
@@ -182,7 +182,7 @@
182 struct nlmsghdr *nlh = (struct nlmsghdr*)buff;182 struct nlmsghdr *nlh = (struct nlmsghdr*)buff;
183 memcpy (&from_nla, &kern_nla, sizeof (from_nla));183 memcpy (&from_nla, &kern_nla, sizeof (from_nla));
184184
185 recv_len = recvfrom (sk_nl, buff, BUFF_SIZE, 0,185 recv_len = recvfrom (iface->sk_nl, buff, BUFF_SIZE, 0,
186 (struct sockaddr*)&from_nla, &from_nla_len);186 (struct sockaddr*)&from_nla, &from_nla_len);
187 if (from_nla.nl_pid != 0) {187 if (from_nla.nl_pid != 0) {
188 continue;188 continue;
189189
=== modified file 'lib/exec-monitor.h'
--- lib/exec-monitor.h 2016-02-21 18:52:25 +0000
+++ lib/exec-monitor.h 2016-08-30 20:17:54 +0000
@@ -42,6 +42,7 @@
42{42{
43 GTypeInterface parent_iface;43 GTypeInterface parent_iface;
44 gboolean monitor_events;44 gboolean monitor_events;
45 int sk_nl;
4546
46 void (* handle_pid) (ExecMonitor *exec_monitor,47 void (* handle_pid) (ExecMonitor *exec_monitor,
47 gint pid);48 gint pid);
4849
=== modified file 'src/CMakeLists.txt'
--- src/CMakeLists.txt 2016-02-25 18:47:22 +0000
+++ src/CMakeLists.txt 2016-08-30 20:17:54 +0000
@@ -23,8 +23,10 @@
23 Widgets/AppChooser.vala23 Widgets/AppChooser.vala
24 Widgets/WeekSpinBox.vala24 Widgets/WeekSpinBox.vala
25 shared/Vars.vala25 shared/Vars.vala
26 shared/PAMControl.vala
27 shared/Utils.vala26 shared/Utils.vala
27 shared/PAM/Token.vala
28 shared/PAM/Reader.vala
29 shared/PAM/Writer.vala
28 ${CMAKE_CURRENT_BINARY_DIR}/config.vala30 ${CMAKE_CURRENT_BINARY_DIR}/config.vala
29PACKAGES31PACKAGES
30 gtk+-3.032 gtk+-3.0
@@ -38,7 +40,6 @@
38 --thread40 --thread
39)41)
4042
41add_subdirectory (cli)
42add_subdirectory (client)43add_subdirectory (client)
43add_subdirectory (daemon)44add_subdirectory (daemon)
4445
4546
=== modified file 'src/Plug.vala'
--- src/Plug.vala 2016-06-16 20:22:58 +0000
+++ src/Plug.vala 2016-08-30 20:17:54 +0000
@@ -35,7 +35,6 @@
35 private Act.UserManager usermanager;35 private Act.UserManager usermanager;
3636
37 public MainBox () {37 public MainBox () {
38 Utils.set_user_name (Environment.get_user_name ());
39 usermanager = Utils.get_usermanager ();38 usermanager = Utils.get_usermanager ();
4039
41 stack = new Gtk.Stack ();40 stack = new Gtk.Stack ();
4241
=== modified file 'src/Widgets/AppsBox.vala'
--- src/Widgets/AppsBox.vala 2016-05-19 18:58:09 +0000
+++ src/Widgets/AppsBox.vala 2016-08-30 20:17:54 +0000
@@ -22,12 +22,6 @@
2222
23namespace PC.Widgets {23namespace PC.Widgets {
24 public class AppsBox : Gtk.Grid {24 public class AppsBox : Gtk.Grid {
25 public signal void update_key_file ();
26
27 public string[] targets = {};
28 public bool admin = false;
29 public bool daemon_active = false;
30
31 private List<AppEntry> entries;25 private List<AppEntry> entries;
32 private Act.User user;26 private Act.User user;
3327
@@ -37,7 +31,6 @@
37 private Gtk.Button remove_button;31 private Gtk.Button remove_button;
38 private Gtk.Button clear_button;32 private Gtk.Button clear_button;
3933
40
41 protected class AppEntry : Gtk.ListBoxRow {34 protected class AppEntry : Gtk.ListBoxRow {
42 public signal void deleted ();35 public signal void deleted ();
4336
@@ -101,7 +94,7 @@
101 header_label.get_style_context ().add_class ("h4");94 header_label.get_style_context ().add_class ("h4");
10295
103 list_box = new Gtk.ListBox ();96 list_box = new Gtk.ListBox ();
104 list_box.row_selected.connect (on_changed);97 list_box.row_selected.connect (update_sensitivity);
105 scrolled.add (list_box);98 scrolled.add (list_box);
10699
107 var add_button = new Gtk.Button.from_icon_name ("list-add-symbolic", Gtk.IconSize.MENU);100 var add_button = new Gtk.Button.from_icon_name ("list-add-symbolic", Gtk.IconSize.MENU);
@@ -142,25 +135,16 @@
142135
143 admin_switch_btn = new Gtk.Switch ();136 admin_switch_btn = new Gtk.Switch ();
144 admin_switch_btn.halign = Gtk.Align.START;137 admin_switch_btn.halign = Gtk.Align.START;
145 admin_switch_btn.notify["active"].connect (on_changed);138 admin_switch_btn.notify["active"].connect (update_admin);
146139
147 attach (frame, 0, 0, 2, 1);140 attach (frame, 0, 0, 2, 1);
148 attach (admin_label, 0, 1, 1, 1);141 attach (admin_label, 0, 1, 1, 1);
149 attach (admin_switch_btn, 1, 1, 1, 1);142 attach (admin_switch_btn, 1, 1, 1, 1);
150143
151 load_existing ();144 load_existing.begin ();
152 show_all ();145 show_all ();
153 }146 }
154147
155 public bool get_active () {
156 return daemon_active;
157 }
158
159 public void set_active (bool active) {
160 daemon_active = active;
161 on_changed ();
162 }
163
164 private void on_add_button_clicked () {148 private void on_add_button_clicked () {
165 apps_popover.show_all ();149 apps_popover.show_all ();
166 }150 }
@@ -180,15 +164,17 @@
180 }164 }
181165
182 private void load_info (AppInfo info) {166 private void load_info (AppInfo info) {
183 if (!get_info_loaded (info)) {167 if (get_info_loaded (info)) {
184 var row = new AppEntry (info);168 return;
185 row.deleted.connect (on_deleted);
186
187 entries.append (row);
188 list_box.add (row);
189 list_box.show_all ();
190 on_changed ();
191 }169 }
170
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 update_targets ();
192 }178 }
193179
194 private bool get_info_loaded (AppInfo info) {180 private bool get_info_loaded (AppInfo info) {
@@ -204,39 +190,50 @@
204 private void on_deleted (AppEntry row) {190 private void on_deleted (AppEntry row) {
205 entries.remove (row);191 entries.remove (row);
206 row.destroy ();192 row.destroy ();
207 on_changed ();193 update_targets ();
208 }194 }
209195
210 private void on_changed () {196 private void update_admin () {
197 update_sensitivity ();
198
199 if (Utils.get_permission ().get_allowed ()) {
200 Utils.get_api ().set_user_daemon_admin.begin (user.get_user_name (), admin_switch_btn.get_active ());
201 }
202 }
203
204 private void update_targets () {
205 update_sensitivity ();
206
207 if (!Utils.get_permission ().get_allowed ()) {
208 return;
209 }
210
211 string[] targets = {};
212 foreach (var entry in entries) {
213 targets += Environment.find_program_in_path (entry.get_executable ());
214 }
215
216 Utils.get_api ().set_user_daemon_targets.begin (user.get_user_name (), targets);
217 }
218
219 private void update_sensitivity () {
211 remove_button.sensitive = (list_box.get_selected_row () != null);220 remove_button.sensitive = (list_box.get_selected_row () != null);
212 clear_button.sensitive = (entries.length () > 0);221 clear_button.sensitive = (entries.length () > 0);
213
214 string[] _targets = {};
215 foreach (var entry in entries) {
216 _targets += Environment.find_program_in_path (entry.get_executable ());
217 }
218
219 admin = admin_switch_btn.get_active ();
220 targets = _targets;
221 update_key_file ();
222 }222 }
223223
224 private void load_existing () {224 private async void load_existing () {
225 var key_file = new KeyFile ();
226 try {225 try {
227 key_file.load_from_file (Utils.build_daemon_conf_path (user), 0);226 string[] targets = yield Utils.get_api ().get_user_daemon_targets (user.get_user_name ());
228 string[] _targets = key_file.get_string_list (Vars.DAEMON_GROUP, Vars.DAEMON_KEY_TARGETS);227 bool admin = yield Utils.get_api ().get_user_daemon_admin (user.get_user_name ());
229 daemon_active = key_file.get_boolean (Vars.DAEMON_GROUP, Vars.DAEMON_KEY_ACTIVE);228 admin_switch_btn.set_active (admin);
229
230 foreach (var info in AppInfo.get_all ()) {230 foreach (var info in AppInfo.get_all ()) {
231 if (info.should_show ()231 if (info.should_show () && Environment.find_program_in_path (info.get_executable ()) in targets) {
232 && Environment.find_program_in_path (info.get_executable ()) in _targets) {
233 load_info (info);232 load_info (info);
234 }233 }
235 }234 }
236 } catch (KeyFileError e) {235 } catch (Error e) {
237 warning ("%s\n", e.message);236 warning (e.message);
238 } catch (FileError e) {
239 warning ("%s\n", e.message);
240 }237 }
241 }238 }
242 }239 }
243240
=== modified file 'src/Widgets/ControlPage.vala'
--- src/Widgets/ControlPage.vala 2016-05-20 13:35:36 +0000
+++ src/Widgets/ControlPage.vala 2016-08-30 20:17:54 +0000
@@ -29,25 +29,6 @@
29 private AppsBox apps_box;29 private AppsBox apps_box;
30 private KeyFile key_file;30 private KeyFile key_file;
3131
32 public bool active {
33 get {
34 return apps_box.get_active ();
35 }
36 set {
37 apps_box.set_active (value);
38 if (Utils.get_permission ().get_allowed ()) {
39 if (value) {
40 general_box.refresh ();
41 Utils.call_cli ({"--user", user.get_user_name (), "--enable-restrict"});
42 } else {
43 general_box.set_lock_dock_active (false);
44 general_box.set_printer_active (true);
45 Utils.call_cli ({"--user", user.get_user_name (), "--disable-restrict"});
46 }
47 }
48 }
49 }
50
51 public ControlPage (Act.User user) {32 public ControlPage (Act.User user) {
52 this.user = user;33 this.user = user;
5334
@@ -64,11 +45,9 @@
6445
65 internet_box = new InternetBox (user);46 internet_box = new InternetBox (user);
66 internet_box.expand = true;47 internet_box.expand = true;
67 internet_box.update_key_file.connect (on_update_key_file);
6848
69 apps_box = new AppsBox (user);49 apps_box = new AppsBox (user);
70 apps_box.expand = true;50 apps_box.expand = true;
71 apps_box.update_key_file.connect (on_update_key_file);
7251
73 stack = new Gtk.Stack ();52 stack = new Gtk.Stack ();
74 stack.add_titled (general_box, "general", _("General"));53 stack.add_titled (general_box, "general", _("General"));
@@ -87,24 +66,35 @@
87 show_all ();66 show_all ();
88 }67 }
8968
69 public void set_active (bool active) {
70 if (Utils.get_permission ().get_allowed ()) {
71 Utils.get_api ().set_user_daemon_active.begin (user.get_user_name (), active);
72 if (active) {
73 general_box.refresh ();
74 general_box.update_pam ();
75 } else {
76 general_box.set_lock_dock_active (false);
77 general_box.set_printer_active (true);
78 Utils.get_api ().remove_restriction_for_user.begin (user.get_user_name ());
79 }
80 }
81 }
82
83 public async bool get_active () {
84 try {
85 return yield Utils.get_api ().get_user_daemon_active (user.get_user_name ());
86 } catch (IOError e) {
87 warning (e.message);
88 }
89
90 return false;
91 }
92
90 private void update_view_state () {93 private void update_view_state () {
91 bool allowed = Utils.get_permission ().get_allowed ();94 bool allowed = Utils.get_permission ().get_allowed ();
92 general_box.sensitive = allowed;95 general_box.sensitive = allowed;
93 internet_box.sensitive = allowed;96 internet_box.sensitive = allowed;
94 apps_box.sensitive = allowed;97 apps_box.sensitive = allowed;
95 }98 }
96
97 private void on_update_key_file () {
98 if (!Utils.get_permission ().get_allowed ()) {
99 return;
100 }
101
102 key_file.set_boolean (Vars.DAEMON_GROUP, Vars.DAEMON_KEY_ACTIVE, apps_box.daemon_active);
103 key_file.set_string_list (Vars.DAEMON_GROUP, Vars.DAEMON_KEY_TARGETS, apps_box.targets);
104 key_file.set_boolean (Vars.DAEMON_GROUP, Vars.DAEMON_KEY_ADMIN, apps_box.admin);
105 key_file.set_string_list (Vars.DAEMON_GROUP, Vars.DAEMON_KEY_BLOCK_URLS, internet_box.urls);
106
107 Utils.call_cli ({ "--set-contents", key_file.to_data (), "--file", Utils.build_daemon_conf_path (user) });
108 }
109 }99 }
110}100}
111101
=== modified file 'src/Widgets/GeneralBox.vala'
--- src/Widgets/GeneralBox.vala 2016-07-27 16:14:53 +0000
+++ src/Widgets/GeneralBox.vala 2016-08-30 20:17:54 +0000
@@ -50,8 +50,6 @@
50 weekday_box.changed.connect (update_pam);50 weekday_box.changed.connect (update_pam);
51 weekend_box.changed.connect (update_pam);51 weekend_box.changed.connect (update_pam);
5252
53 monitor_updates ();
54 update ();
55 load_restrictions ();53 load_restrictions ();
56 }54 }
5755
@@ -89,10 +87,10 @@
8987
90 limit_combobox = new Gtk.ComboBoxText ();88 limit_combobox = new Gtk.ComboBoxText ();
91 limit_combobox.hexpand = true;89 limit_combobox.hexpand = true;
92 limit_combobox.append (Vars.ALL_ID, _("On weekdays and weekends"));90 limit_combobox.append (PAM.DayType.WEEKDAY.to_string () + PAM.DayType.WEEKEND.to_string (), _("On weekdays and weekends"));
93 limit_combobox.append (Vars.WEEKDAYS_ID, _("Only on weekdays"));91 limit_combobox.append (PAM.DayType.WEEKDAY.to_string (), _("Only on weekdays"));
94 limit_combobox.append (Vars.WEEKENDS_ID, _("Only on weekends"));92 limit_combobox.append (PAM.DayType.WEEKEND.to_string (), _("Only on weekends"));
95 limit_combobox.active_id = "all";93 limit_combobox.active = 0;
9694
97 frame = new Gtk.Frame (null);95 frame = new Gtk.Frame (null);
98 frame.get_style_context ().add_class (Gtk.STYLE_CLASS_VIEW);96 frame.get_style_context ().add_class (Gtk.STYLE_CLASS_VIEW);
@@ -134,104 +132,54 @@
134 }132 }
135133
136 private void load_restrictions () {134 private void load_restrictions () {
137 var restricts = PAMControl.get_all_restrictions ();135 var token = PAM.Reader.get_token_for_user (Vars.PAM_TIME_CONF_PATH, user.get_user_name ());
138 foreach (var restrict in restricts) {136 if (token == null) {
139 if (restrict.user == user.get_user_name ()) {137 return;
140 limit_switch.active = true;138 }
141139
142 limit_combobox.active_id = restrict.day_id;140 limit_switch.active = true;
143 switch (restrict.day_id) {141
144 case Vars.ALL_ID:142 string[] ids = {};
145 string from_weekday = restrict.weekday_hours.split ("-")[0];143 foreach (PAM.TimeInfo info in token.get_times_info ()) {
146 string to_weekday = restrict.weekday_hours.split ("-")[1];144 ids += info.day_type.to_string ();
147 string from_weekend = restrict.weekend_hours.split ("-")[0];145 switch (info.day_type) {
148 string to_weekend = restrict.weekend_hours.split ("-")[1];146 case PAM.DayType.WEEKDAY:
149147 weekday_box.set_from (info.from);
150 weekday_box.set_from (from_weekday);148 weekday_box.set_to (info.to);
151 weekday_box.set_to (to_weekday);149 break;
152150 case PAM.DayType.WEEKEND:
153 weekend_box.set_from (from_weekend);151 weekend_box.set_from (info.from);
154 weekend_box.set_to (to_weekend);152 weekend_box.set_to (info.to);
155 break;153 break;
156 case Vars.WEEKDAYS_ID:154 default:
157 weekday_box.set_from (restrict.from);155 break;
158 weekday_box.set_to (restrict.to);
159 break;
160 case Vars.WEEKENDS_ID:
161 weekend_box.set_from (restrict.from);
162 weekend_box.set_to (restrict.to);
163 break;
164 default:
165 break;
166 }
167 }156 }
168 }157 }
158
159 if (ids.length > 0) {
160 limit_combobox.active_id = string.joinv ("|", ids);
161 }
169 }162 }
170163
171 private void update_pam () {164 public void update_pam () {
172 string restrict = "";165 if (!Utils.get_permission ().get_allowed ()) {
166 return;
167 }
168
169 string[] times = {};
170 string[] users = { user.get_user_name () };
173 string id = limit_combobox.get_active_id ();171 string id = limit_combobox.get_active_id ();
174 switch (id) {172
175 case Vars.ALL_ID:173 if (PAM.DayType.WEEKDAY.to_string () in id) {
176 restrict = generate_pam_conf_restriction (id, weekday_box.get_from (), weekday_box.get_to ());174 times += PAM.DayType.WEEKDAY.to_string () + weekday_box.get_from () + "-" + weekday_box.get_to ();
177 restrict += "|" + weekend_box.get_from () + "-" + weekend_box.get_to ();175 }
178 break;176
179 case Vars.WEEKDAYS_ID:177 if (PAM.DayType.WEEKEND.to_string () in id) {
180 restrict = generate_pam_conf_restriction (id, weekday_box.get_from (), weekday_box.get_to ());178 times += PAM.DayType.WEEKEND.to_string () + weekend_box.get_from () + "-" + weekend_box.get_to ();
181 break;179 }
182 case Vars.WEEKENDS_ID:180
183 restrict = generate_pam_conf_restriction (id, weekend_box.get_from (), weekend_box.get_to ());181 string input = PAM.Token.construct_pam_restriction_simple (users, times);
184 break;182 Utils.get_api ().add_restriction_for_user.begin (input, true);
185 }
186
187 PAMControl.try_add_restrict_line (user.get_user_name (), restrict);
188 }
189
190 private string generate_pam_conf_restriction (string id, string from, string to) {
191 string retval = "*;*;";
192 string days = "";
193 switch (id) {
194 case Vars.ALL_ID:
195 days = "Al";
196 break;
197 case Vars.WEEKDAYS_ID:
198 days = "Wk";
199 break;
200 case Vars.WEEKENDS_ID:
201 days = "Wd";
202 break;
203 }
204
205 retval += user.get_user_name () + ";" + days + from + "-" + to;
206 return retval;
207 }
208
209 private void monitor_updates () {
210 try {
211 var monitor = File.new_for_path (plank_conf_file_path).monitor_file (FileMonitorFlags.NONE);
212 monitor.changed.connect (update);
213 } catch (IOError e) {
214 warning ("%s\n", e.message);
215 }
216 }
217
218 private void update () {
219 var key_file = new KeyFile ();
220 try {
221 key_file.load_from_file (plank_conf_file_path, KeyFileFlags.NONE);
222 dock_btn.active = !key_file.get_boolean (Vars.PLANK_CONF_GROUP, Vars.PLANK_CONF_LOCK_ITEMS_KEY);
223 } catch (FileError e) {
224 dock_btn.active = true;
225 warning ("%s\n", e.message);
226 } catch (Error e) {
227 dock_btn.active = true;
228 warning ("%s\n", e.message);
229 }
230
231 /* TODO: Get denied users for printing configuration */
232
233 /* For now, we assume that printing is enabled */
234 print_btn.active = true;
235 }183 }
236184
237 private void on_dock_btn_activate () {185 private void on_dock_btn_activate () {
@@ -240,7 +188,7 @@
240188
241 public void set_lock_dock_active (bool active) {189 public void set_lock_dock_active (bool active) {
242 if (Utils.get_permission ().get_allowed ()) {190 if (Utils.get_permission ().get_allowed ()) {
243 Utils.call_cli ({"--user", user.get_user_name (), "--lock-dock", active.to_string ()});191 Utils.get_api ().lock_dock_icons_for_user.begin (user.get_user_name (), active);
244 }192 }
245 }193 }
246194
@@ -272,25 +220,23 @@
272 private void on_limit_switch_changed () {220 private void on_limit_switch_changed () {
273 if (limit_switch.get_active ()) {221 if (limit_switch.get_active ()) {
274 update_pam ();222 update_pam ();
275 } else {223 } else if (Utils.get_permission ().get_allowed ()) {
276 PAMControl.try_remove_user_restrict (user.get_user_name ());224 Utils.get_api ().remove_restriction_for_user.begin (user.get_user_name ());
277 }225 }
278 }226 }
279227
280 private void on_limit_combobox_changed () {228 private void on_limit_combobox_changed () {
281 switch (limit_combobox.get_active_id ()) {229 string id = limit_combobox.get_active_id ();
282 case Vars.ALL_ID:230
283 weekday_box.sensitive = true;231 if (PAM.DayType.WEEKDAY.to_string () in id && PAM.DayType.WEEKEND.to_string () in id) {
284 weekend_box.sensitive = true;232 weekday_box.sensitive = true;
285 break;233 weekend_box.sensitive = true;
286 case Vars.WEEKDAYS_ID:234 } else if (PAM.DayType.WEEKDAY.to_string () in id) {
287 weekday_box.sensitive = true;235 weekday_box.sensitive = true;
288 weekend_box.sensitive = false; 236 weekend_box.sensitive = false;
289 break;237 } else if (PAM.DayType.WEEKEND.to_string () in id) {
290 case Vars.WEEKENDS_ID:238 weekday_box.sensitive = false;
291 weekday_box.sensitive = false;239 weekend_box.sensitive = true;
292 weekend_box.sensitive = true;
293 break;
294 }240 }
295241
296 update_pam ();242 update_pam ();
297243
=== modified file 'src/Widgets/InternetBox.vala'
--- src/Widgets/InternetBox.vala 2016-05-19 18:58:09 +0000
+++ src/Widgets/InternetBox.vala 2016-08-30 20:17:54 +0000
@@ -22,7 +22,6 @@
2222
23namespace PC.Widgets {23namespace PC.Widgets {
24 public class InternetBox : Gtk.Grid {24 public class InternetBox : Gtk.Grid {
25 public signal void update_key_file ();
26 public string[] urls;25 public string[] urls;
2726
28 private const string URL_REGEX_RULE = "[-a-zA-Z0-9@:%._\\+~#=]{2,256}\\.[a-z]{2,4}\\b([-a-zA-Z0-9@:%_\\+.~#?&//=]*)";27 private const string URL_REGEX_RULE = "[-a-zA-Z0-9@:%._\\+~#=]{2,256}\\.[a-z]{2,4}\\b([-a-zA-Z0-9@:%_\\+.~#?&//=]*)";
@@ -118,16 +117,14 @@
118 frame.add (main_box);117 frame.add (main_box);
119118
120 add (frame);119 add (frame);
121 load_existing ();120 load_existing.begin ();
122 show_all ();121 show_all ();
123 }122 }
124123
125 private void load_existing () {124 private async void load_existing () {
126 var key_file = new KeyFile ();
127 try {125 try {
128 key_file.load_from_file (Utils.build_daemon_conf_path (user), 0);126 string[] block_urls = yield Utils.get_api ().get_user_daemon_block_urls (user.get_user_name ());
129 urls = key_file.get_string_list (Vars.DAEMON_GROUP, Vars.DAEMON_KEY_BLOCK_URLS);127 foreach (string url in block_urls) {
130 foreach (string url in urls) {
131 add_entry (new UrlEntry (url));128 add_entry (new UrlEntry (url));
132 }129 }
133 } catch (Error e) {130 } catch (Error e) {
@@ -135,14 +132,17 @@
135 } 132 }
136 }133 }
137134
138 private void update () {135 private void update_block_urls () {
139 string[] _urls = {};136 if (!Utils.get_permission ().get_allowed ()) {
137 return;
138 }
139
140 string[] block_urls = {};
140 foreach (var url_entry in url_list) {141 foreach (var url_entry in url_list) {
141 _urls += url_entry.get_url ();142 block_urls += url_entry.get_url ();
142 }143 }
143144
144 urls = _urls;145 Utils.get_api ().set_user_daemon_block_urls.begin (user.get_user_name (), block_urls);
145 update_key_file ();
146 }146 }
147147
148 private void on_entry_changed () {148 private void on_entry_changed () {
@@ -173,7 +173,7 @@
173 add_entry (url_entry);173 add_entry (url_entry);
174174
175 entry.text = "";175 entry.text = "";
176 update ();176 update_block_urls ();
177 }177 }
178178
179 private void add_entry (UrlEntry url_entry) {179 private void add_entry (UrlEntry url_entry) {
@@ -186,7 +186,7 @@
186 private void on_url_entry_deleted (UrlEntry url_entry) {186 private void on_url_entry_deleted (UrlEntry url_entry) {
187 url_list.remove (url_entry);187 url_list.remove (url_entry);
188 list_box.remove (url_entry);188 list_box.remove (url_entry);
189 update ();189 update_block_urls ();
190 }190 }
191 }191 }
192}192}
193193
=== modified file 'src/Widgets/UserItem.vala'
--- src/Widgets/UserItem.vala 2016-05-20 21:18:29 +0000
+++ src/Widgets/UserItem.vala 2016-08-30 20:17:54 +0000
@@ -64,7 +64,10 @@
64 grid.attach (username_label, 1, 1, 1, 1); 64 grid.attach (username_label, 1, 1, 1, 1);
65 grid.attach (master_switch, 2, 0, 1, 2); 65 grid.attach (master_switch, 2, 0, 1, 2);
6666
67 master_switch.bind_property ("active", page, "active", BindingFlags.DEFAULT);67 master_switch.notify["active"].connect (() => {
68 page.set_active (master_switch.get_active ());
69 });
70
68 master_switch.bind_property ("active", page.stack, "sensitive", BindingFlags.SYNC_CREATE);71 master_switch.bind_property ("active", page.stack, "sensitive", BindingFlags.SYNC_CREATE);
6972
70 Utils.get_permission ().notify["allowed"].connect (update_view);73 Utils.get_permission ().notify["allowed"].connect (update_view);
@@ -75,7 +78,10 @@
75 }78 }
7679
77 public void update_view () {80 public void update_view () {
78 master_switch.active = page.active;81 page.get_active.begin ((obj, res) => {
82 master_switch.active = page.get_active.end (res);
83 });
84
79 master_switch.sensitive = Utils.get_permission ().get_allowed ();85 master_switch.sensitive = Utils.get_permission ().get_allowed ();
8086
81 try {87 try {
8288
=== modified file 'src/Widgets/WeekSpinBox.vala'
--- src/Widgets/WeekSpinBox.vala 2016-05-20 21:48:27 +0000
+++ src/Widgets/WeekSpinBox.vala 2016-08-30 20:17:54 +0000
@@ -32,14 +32,14 @@
32 spacing = 12;32 spacing = 12;
3333
34 picker_from = new Granite.Widgets.TimePicker ();34 picker_from = new Granite.Widgets.TimePicker ();
35 picker_from.time_changed.connect (() => changed ());
36
35 picker_to = new Granite.Widgets.TimePicker ();37 picker_to = new Granite.Widgets.TimePicker ();
38 picker_to.time_changed.connect (() => changed ());
3639
37 var label = new Gtk.Label (title);40 var label = new Gtk.Label (title);
38 label.get_style_context ().add_class ("h4");41 label.get_style_context ().add_class ("h4");
3942
40 picker_from.time_changed.connect (_changed);
41 picker_to.time_changed.connect (_changed);
42
43 add (label);43 add (label);
44 add (new Gtk.Label (_("From:")));44 add (new Gtk.Label (_("From:")));
45 add (picker_from);45 add (picker_from);
@@ -59,27 +59,21 @@
59 }59 }
6060
61 public void set_from (string from) {61 public void set_from (string from) {
62 char[] data = from.to_utf8 ();62 string hours = from.slice (0, 2);
63 string hours = data[0].to_string () + data[1].to_string ();63 string minutes = from.substring (2);
64 string minutes = data[2].to_string () + data[3].to_string ();
6564
66 var time = new DateTime.local (new DateTime.now_local ().get_year (), 1, 1, int.parse (hours), int.parse (minutes), 0);65 var time = new DateTime.local (new DateTime.now_local ().get_year (), 1, 1, int.parse (hours), int.parse (minutes), 0);
67 picker_from.time = time;66 picker_from.time = time;
68 }67 }
6968
70 public void set_to (string to) {69 public void set_to (string to) {
71 char[] data = to.to_utf8 ();70 string hours = to.slice (0, 2);
72 string hours = data[0].to_string () + data[1].to_string ();71 string minutes = to.substring (2);
73 string minutes = data[2].to_string () + data[3].to_string ();
7472
75 var time = new DateTime.local (new DateTime.now_local ().get_year (), 1, 1, int.parse (hours), int.parse (minutes), 0);73 var time = new DateTime.local (new DateTime.now_local ().get_year (), 1, 1, int.parse (hours), int.parse (minutes), 0);
76 picker_to.time = time;74 picker_to.time = time;
77 }75 }
7876
79 private void _changed () {
80 changed ();
81 }
82
83 private string format_time_string (int val) {77 private string format_time_string (int val) {
84 if (val < 10) {78 if (val < 10) {
85 return "0" + val.to_string ();79 return "0" + val.to_string ();
8680
=== removed directory 'src/cli'
=== removed file 'src/cli/Application.vala'
--- src/cli/Application.vala 2016-05-18 15:29:00 +0000
+++ src/cli/Application.vala 1970-01-01 00:00:00 +0000
@@ -1,191 +0,0 @@
1// -*- Mode: vala; indent-tabs-mode: nil; tab-width: 4 -*-
2/*-
3 * Copyright (c) 2015 Adam Bieńkowski (https://launchpad.net/switchboard-plug-parental-controls)
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Library General Public
7 * License as published by the Free Software Foundation; either
8 * version 3 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Library General Public License for more details.
14 *
15 * You should have received a copy of the GNU Library General Public
16 * License along with this library; if not, write to the
17 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 * Boston, MA 02111-1307, USA.
19 *
20 * Authored by: Adam Bieńkowski <donadigos159@gmail.com>
21 */
22
23namespace PC.Cli {
24 public class App : Application {
25 private const string TIME_CONF_FILE = "/etc/security/time.conf";
26 private string plank_conf_file_path = "";
27
28 public App () {
29 Object (flags: ApplicationFlags.HANDLES_COMMAND_LINE);
30 }
31
32 public static int main (string[] args) {
33 var app = new App ();
34 return app.run (args);
35 }
36
37 private int _command_line (ApplicationCommandLine command_line) {
38 string? user = null;
39 string? restrict_pam_line = null;
40 string? lock_dock = null;
41 string? set_contents = null;
42 string? file = null;
43
44 bool remove_restrict = false;
45 bool enable_restrict = false;
46 bool disable_restrict = false;
47
48 var options = new OptionEntry[9];
49 options[0] = { "user", 0, 0, OptionArg.STRING, ref user, "Use specific user", null };
50 options[1] = { "lock-dock", 0, 0, OptionArg.STRING, ref lock_dock, "Lock the given user dock", null };
51 options[2] = { "restrict-pam-line", 0, 0, OptionArg.STRING, ref restrict_pam_line, "Add specified line to pam configuration", null };
52 options[3] = { "remove-restrict", 0, 0, OptionArg.NONE, ref remove_restrict, "Remove all time restrictions for specified user", null };
53 options[4] = { "set-contents", 0, 0, OptionArg.STRING, ref set_contents, "Set contents of specified filename", null };
54 options[5] = { "file", 0, 0, OptionArg.FILENAME, ref file, "A file to write contents to", null };
55 options[6] = { "enable-restrict", 0, 0, OptionArg.NONE, ref enable_restrict, "Enable PAM restrictions for the user", null };
56 options[7] = { "disable-restrict", 0, 0, OptionArg.NONE, ref disable_restrict, "Disable PAM restrictions for the user", null };
57
58 string[] args = command_line.get_arguments ();
59 string*[] _args = new string[args.length];
60 for (int i = 0; i < args.length; i++) {
61 _args[i] = args[i];
62 }
63
64 try {
65 var opt_context = new OptionContext ("context");
66 opt_context.set_help_enabled (true);
67 opt_context.add_main_entries (options, null);
68 unowned string[] tmp = _args;
69 opt_context.parse (ref tmp);
70 } catch (OptionError e) {
71 command_line.print ("error: %s\n", e.message);
72 command_line.print ("Run '%s --help' to see a full list of available command line options.\n", args[0]);
73 return 1;
74 }
75
76 if (Posix.getuid () != 0) {
77 command_line.print ("Error: To run this program you need root privigiles\n\n");
78 Process.exit (1);
79 }
80
81 if (remove_restrict && user != "") {
82 var pam_writer = new PAMWriter (File.new_for_path (TIME_CONF_FILE));
83 pam_writer.remove_user_restrictions (user);
84 }
85
86 if (enable_restrict && user != "") {
87 var pam_writer = new PAMWriter (File.new_for_path (TIME_CONF_FILE));
88 pam_writer.modify_user_restrictions (user, true);
89 }
90
91 if (disable_restrict && user != "") {
92 var pam_writer = new PAMWriter (File.new_for_path (TIME_CONF_FILE));
93 pam_writer.modify_user_restrictions (user, false);
94 }
95
96 if (restrict_pam_line != null && user != null) {
97 ensure_pam_lightdm_enabled ();
98
99 var pam_writer = new PAMWriter (File.new_for_path (TIME_CONF_FILE));
100 pam_writer.add_conf_line (restrict_pam_line, user);
101 }
102
103 if (user != null && lock_dock != null) {
104 var loop = new MainLoop ();
105
106 Utils.get_usermanager ().notify["is-loaded"].connect (() => {
107 if (Utils.get_usermanager ().is_loaded) {
108 lock_dock_for_user (user, bool.parse (lock_dock));
109 loop.quit ();
110 }
111 });
112
113 loop.run ();
114 }
115
116 if (set_contents != null && file != null) {
117 set_file_contents (file, set_contents);
118 }
119
120 return 0;
121 }
122
123 private void set_file_contents (string path, string contents) {
124 var file = File.new_for_path (path);
125 if (!file.query_exists ()) {
126 try {
127 var os = file.create (FileCreateFlags.REPLACE_DESTINATION);
128 os.write (contents.data);
129 } catch (Error e) {
130 warning ("%s\n", e.message);
131 }
132 } else {
133 try {
134 FileUtils.set_contents (path, contents);
135 } catch (Error e) {
136 warning ("%s\n", e.message);
137 }
138 }
139 }
140
141 private void ensure_pam_lightdm_enabled () {
142 string path = "/etc/pam.d/lightdm";
143 string contents = "";
144 try {
145 FileUtils.get_contents (path, out contents);
146 } catch (FileError e) {
147 warning ("%s\n", e.message);
148 }
149
150 string conf_line = "\naccount required pam_time.so";
151 if (!(conf_line in contents)) {
152 contents += conf_line;
153
154 try {
155 FileUtils.set_contents (path, contents);
156 } catch (FileError e) {
157 warning ("%s\n", e.message);
158 }
159 }
160 }
161
162 private void lock_dock_for_user (string user, bool lock) {
163 plank_conf_file_path = Path.build_filename (Utils.get_usermanager ().get_user (user).get_home_dir (), Vars.PLANK_CONF_DIR);
164 if (plank_conf_file_path != "" && File.new_for_path (plank_conf_file_path).query_exists ()) {
165 var key_file = new KeyFile ();
166 try {
167 key_file.load_from_file (plank_conf_file_path, KeyFileFlags.KEEP_COMMENTS | KeyFileFlags.KEEP_TRANSLATIONS);
168 } catch (KeyFileError e) {
169 warning ("%s\n", e.message);
170 } catch (FileError e) {
171 warning ("%s\n", e.message);
172 }
173
174 key_file.set_boolean (Vars.PLANK_CONF_GROUP, Vars.PLANK_CONF_LOCK_ITEMS_KEY, lock);
175
176 try {
177 key_file.save_to_file (plank_conf_file_path);
178 } catch (FileError e) {
179 warning ("%s\n", e.message);
180 }
181 }
182 }
183
184 public override int command_line (ApplicationCommandLine command_line) {
185 this.hold ();
186 int res = _command_line (command_line);
187 this.release ();
188 return res;
189 }
190 }
191}
192\ No newline at end of file0\ No newline at end of file
1931
=== removed file 'src/cli/CMakeLists.txt'
--- src/cli/CMakeLists.txt 2016-03-06 18:26:44 +0000
+++ src/cli/CMakeLists.txt 1970-01-01 00:00:00 +0000
@@ -1,28 +0,0 @@
1find_package (PkgConfig)
2
3pkg_check_modules (CLI_DEPS REQUIRED glib-2.0 gio-2.0 polkit-gobject-1 accountsservice)
4add_definitions (${CLI_DEPS_CFLAGS})
5link_directories (${CLI_DEPS_LIBRARY_DIRS})
6
7set (CLI_EXEC_NAME pantheon-parental-controls-cli)
8
9vala_precompile (VALA_C ${CLI_EXEC_NAME}
10 Application.vala
11 PAMWriter.vala
12 ${CMAKE_SOURCE_DIR}/src/shared/Vars.vala
13 ${CMAKE_SOURCE_DIR}/src/shared/Utils.vala
14PACKAGES
15 gio-2.0
16 accountsservice
17 polkit-gobject-1
18 posix
19OPTIONS
20 --vapidir=${CMAKE_SOURCE_DIR}/vapi/
21 --thread
22)
23
24add_executable (${CLI_EXEC_NAME} ${VALA_C})
25target_link_libraries (${CLI_EXEC_NAME} ${DEPS_LIBRARIES})
26
27# Installation
28install (TARGETS ${CLI_EXEC_NAME} RUNTIME DESTINATION bin)
29\ No newline at end of file0\ No newline at end of file
301
=== removed file 'src/cli/PAMWriter.vala'
--- src/cli/PAMWriter.vala 2016-03-06 18:26:44 +0000
+++ src/cli/PAMWriter.vala 1970-01-01 00:00:00 +0000
@@ -1,204 +0,0 @@
1// -*- Mode: vala; indent-tabs-mode: nil; tab-width: 4 -*-
2/*-
3 * Copyright (c) 2015 Adam Bieńkowski (https://launchpad.net/switchboard-plug-parental-controls)
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Library General Public
7 * License as published by the Free Software Foundation; either
8 * version 3 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Library General Public License for more details.
14 *
15 * You should have received a copy of the GNU Library General Public
16 * License along with this library; if not, write to the
17 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 * Boston, MA 02111-1307, USA.
19 *
20 * Authored by: Adam Bieńkowski <donadigos159@gmail.com>
21 */
22
23namespace PC.Cli {
24 public class PAMWriter : Object {
25 public File file;
26
27 public PAMWriter (File file) {
28 this.file = file;
29 }
30
31 public string get_conf_section () {
32 string contents = read_contents (file);
33 try {
34 var regex = new Regex (Vars.PAM_CONF_REGEX);
35
36 if (regex.match (contents)) {
37 int i = 0;
38 foreach (string str in regex.split (contents)) {
39 // Do not replace the contents of the PC plug section
40 if (i != 1) {
41 contents = contents.replace (str, "");
42 }
43
44 i++;
45 }
46 } else {
47 return "";
48 }
49 } catch (RegexError e) {
50 warning ("%s\n", e.message);
51 }
52
53
54 return contents;
55 }
56
57 public void remove_conf_section () {
58 string contents = read_contents (file);
59 string final_contents = contents.replace (get_conf_section (), "");
60
61 try {
62 FileUtils.set_contents (file.get_path (), final_contents);
63 } catch (FileError e) {
64 warning ("%s\n", e.message);
65 }
66 }
67
68 public void remove_user_restrictions (string user) {
69 string contents = read_contents (file);
70 string conf_section = get_conf_section ();
71
72 string new_conf = "";
73 if (conf_section != "" && user != "") {
74 int i = 0;
75 string[] split = conf_section.split ("\n");
76 foreach (string section_line in split) {
77 bool contains = (user in section_line);
78 if (!contains) {
79 if (i > 0) {
80 new_conf += "\n";
81 }
82
83 new_conf += section_line;
84 }
85
86 i++;
87 }
88
89 try {
90 FileUtils.set_contents (file.get_path (), contents.replace (conf_section, new_conf));
91 } catch (FileError e) {
92 warning ("%s\n", e.message);
93 }
94 }
95 }
96
97 public void modify_user_restrictions (string user, bool enable) {
98 string contents = read_contents (file);
99 string conf_section = get_conf_section ();
100
101 string new_conf = "";
102 if (conf_section != "" && user != "") {
103 int i = 0;
104 string[] split = conf_section.split ("\n");
105 foreach (string section_line in split) {
106 if (i > 0) {
107 new_conf += "\n";
108 }
109
110 bool contains = (user in section_line);
111 if (!contains) {
112 new_conf += section_line;
113 } else {
114 string prefix = "#";
115 if (enable) {
116 prefix = "";
117 section_line = section_line.replace ("#", "");
118 }
119
120 new_conf += prefix + section_line;
121 }
122
123 i++;
124 }
125
126 try {
127 FileUtils.set_contents (file.get_path (), contents.replace (conf_section, new_conf));
128 } catch (FileError e) {
129 warning ("%s\n", e.message);
130 }
131 }
132 }
133
134 public void add_conf_line (string line, string? user = null) {
135 string contents = read_contents (file);
136 string conf_section = get_conf_section ();
137
138 string new_conf = "";
139 if (conf_section != "" && user != null) {
140 int i = 0;
141 string[] split = conf_section.split ("\n");
142 foreach (string section_line in split) {
143 bool contains = (user in section_line);
144 if (!contains) {
145 if (i > 0) {
146 new_conf += "\n";
147 }
148
149 new_conf += section_line;
150 }
151
152 i++;
153 }
154
155 try {
156 FileUtils.set_contents (file.get_path (), contents.replace (conf_section, new_conf));
157 } catch (FileError e) {
158 warning ("%s\n", e.message);
159 }
160
161 contents = read_contents (file);
162 conf_section = get_conf_section ();
163 }
164
165
166 string final_contents = "";
167
168 if (conf_section == "") {
169 final_contents = contents +
170 "\n" +
171 Vars.PAM_CONF_START +
172 "\n" +
173 line +
174 "\n" +
175 Vars.PAM_CONF_END;
176 } else {
177 final_contents = conf_section.replace (Vars.PAM_CONF_END, "");
178 final_contents += line + "\n" + Vars.PAM_CONF_END;
179 final_contents = contents.replace (conf_section, final_contents);
180 }
181
182 try {
183 FileUtils.set_contents (file.get_path (), final_contents);
184 } catch (FileError e) {
185 warning ("%s\n", e.message);
186 }
187 }
188
189 private string read_contents (File file) {
190 string data = "";
191 if (!file.query_exists ()) {
192 return "";
193 }
194
195 try {
196 FileUtils.get_contents (file.get_path (), out data);
197 } catch (FileError e) {
198 warning ("%s\n", e.message);
199 }
200
201 return data;
202 }
203 }
204}
205\ No newline at end of file0\ No newline at end of file
2061
=== renamed file 'src/client/AppLockDialog.vala' => 'src/client/AppUnavailableDialog.vala'
--- src/client/AppLockDialog.vala 2016-02-25 18:47:22 +0000
+++ src/client/AppUnavailableDialog.vala 2016-08-30 20:17:54 +0000
@@ -20,9 +20,9 @@
20 * Authored by: Adam Bieńkowski <donadigos159@gmail.com>20 * Authored by: Adam Bieńkowski <donadigos159@gmail.com>
21 */21 */
2222
23namespace AppLock {23namespace PC.Client {
24 public class AppLockDialog : Gtk.MessageDialog {24 public class AppUnavailableDialog : Gtk.MessageDialog {
25 public AppLockDialog () {25 public AppUnavailableDialog () {
26 deletable = false;26 deletable = false;
27 text = _("You cannot run this application");27 text = _("You cannot run this application");
28 secondary_text = _("You are not permitted to run this application.");28 secondary_text = _("You are not permitted to run this application.");
2929
=== modified file 'src/client/CMakeLists.txt'
--- src/client/CMakeLists.txt 2016-02-25 18:47:22 +0000
+++ src/client/CMakeLists.txt 2016-08-30 20:17:54 +0000
@@ -8,11 +8,13 @@
88
9vala_precompile (VALA_C ${CLIENT_EXEC_NAME}9vala_precompile (VALA_C ${CLIENT_EXEC_NAME}
10 Client.vala10 Client.vala
11 AppLockDialog.vala11 AppUnavailableDialog.vala
12 ${CMAKE_SOURCE_DIR}/src/shared/Vars.vala12 ${CMAKE_SOURCE_DIR}/src/shared/Vars.vala
13 ${CMAKE_SOURCE_DIR}/src/shared/Utils.vala
13PACKAGES14PACKAGES
14 posix15 posix
15 polkit-gobject-116 polkit-gobject-1
17 accountsservice
16 gio-2.018 gio-2.0
17 gtk+-3.019 gtk+-3.0
18OPTIONS20OPTIONS
1921
=== modified file 'src/client/Client.vala'
--- src/client/Client.vala 2016-07-31 00:18:06 +0000
+++ src/client/Client.vala 2016-08-30 20:17:54 +0000
@@ -20,19 +20,8 @@
20 * Authored by: Adam Bieńkowski <donadigos159@gmail.com>20 * Authored by: Adam Bieńkowski <donadigos159@gmail.com>
21 */21 */
2222
23 namespace PC.Client {23namespace PC.Client {
24 [DBus (name = "org.pantheon.ParentalControls")]
25 public interface ParentalControls : Object {
26 public signal void show_app_lock_dialog ();
27 public signal void authorize (string user, string action_id);
28 public signal void launch (string[] args);
29 public signal void send_time_notification (int hours, int minutes);
30
31 public abstract void end_authorization (int client_pid) throws IOError;
32 }
33
34 public class Client : Gtk.Application {24 public class Client : Gtk.Application {
35 private ParentalControls? parental_controls;
36 private Polkit.Permission? permission = null;25 private Polkit.Permission? permission = null;
3726
38 public static int main (string[] args) {27 public static int main (string[] args) {
@@ -43,31 +32,22 @@
43 }32 }
4433
45 public override void activate () {34 public override void activate () {
46 try {35 var api = Utils.get_api ();
47 parental_controls = Bus.get_proxy_sync (BusType.SYSTEM, Vars.PARENTAL_CONTROLS_IFACE, Vars.PARENTAL_CONTROLS_OBJECT_PATH);36
48 } catch (Error e) {37 api.show_app_unavailable.connect (on_show_app_unavailable);
49 warning ("%s\n", e.message);38 api.app_authorize.connect (on_authorize);
50 return;39 api.launch.connect (on_launch);
51 }40 api.show_timeout.connect (on_show_timeout);
52
53 if (parental_controls == null) {
54 return;
55 }
56
57 parental_controls.show_app_lock_dialog.connect (on_show_app_lock_dialog);
58 parental_controls.authorize.connect (on_authorize);
59 parental_controls.launch.connect (on_launch);
60 parental_controls.send_time_notification.connect (on_send_time_notification);
6141
62 Gtk.main ();42 Gtk.main ();
63 }43 }
6444
65 private void on_show_app_lock_dialog () {45 private void on_show_app_unavailable (string path) {
66 var app_lock_dialog = new AppLock.AppLockDialog ();46 var app_lock_dialog = new AppUnavailableDialog ();
67 app_lock_dialog.show_all ();47 app_lock_dialog.show_all ();
68 }48 }
6949
70 private void on_authorize (string user_name, string action_id) {50 private void on_authorize (string username, string path, string action_id) {
71 if (permission != null && permission.get_can_release ()) {51 if (permission != null && permission.get_can_release ()) {
72 try {52 try {
73 permission.release ();53 permission.release ();
@@ -77,36 +57,26 @@
77 }57 }
7858
79 try {59 try {
80 var user = (Polkit.UnixUser)Polkit.UnixUser.new_for_name (user_name);60 var user = (Polkit.UnixUser)Polkit.UnixUser.new_for_name (username);
81 permission = new Polkit.Permission.sync (action_id,61 permission = new Polkit.Permission.sync (action_id,
82 Polkit.UnixProcess.new_for_owner (Posix.getpid (), 0, user.get_uid ()));62 Polkit.UnixProcess.new_for_owner (Posix.getpid (), 0, user.get_uid ()));
83 } catch (Error e) {63 } catch (Error e) {
84 warning ("%s\n", e.message);64 warning ("%s\n", e.message);
85 }65 }
8666
67 ulong signal_id = 0;
68 signal_id = permission.notify["allowed"].connect (() => {
69 Utils.get_api ().end_app_authorization.begin ();
70 permission.disconnect (signal_id);
71 });
72
87 permission.acquire_async.begin ();73 permission.acquire_async.begin ();
88 permission.notify["allowed"].connect (() => {
89 if (parental_controls != null) {
90 try {
91 parental_controls.end_authorization (Posix.getpid ());
92 } catch (IOError e) {
93 warning ("%s\n", e.message);
94 }
95 }
96 });
97 }74 }
9875
99 private void on_launch (string[] args) {76 private void on_launch (string[] args) {
100 string[] _args = {};
101 for (int i = 0; i < args.length; i++) {
102 if (args[i].strip () != "") {
103 _args[i] = args[i];
104 }
105 }
106
107 try {77 try {
108 GLib.Process.spawn_async ("/",78 GLib.Process.spawn_async ("/",
109 _args,79 args,
110 Environ.get (),80 Environ.get (),
111 SpawnFlags.SEARCH_PATH | SpawnFlags.DO_NOT_REAP_CHILD,81 SpawnFlags.SEARCH_PATH | SpawnFlags.DO_NOT_REAP_CHILD,
112 null,82 null,
@@ -117,7 +87,7 @@
117 }87 }
11888
11989
120 private void on_send_time_notification (int hours, int minutes) {90 private void on_show_timeout (int hours, int minutes) {
121 string hours_str = "";91 string hours_str = "";
122 string minutes_str = "";92 string minutes_str = "";
123 string info = "";93 string info = "";
@@ -138,8 +108,8 @@
138 var notification = new Notification (_("Time left"));108 var notification = new Notification (_("Time left"));
139 var icon = new ThemedIcon ("dialog-warning");109 var icon = new ThemedIcon ("dialog-warning");
140 notification.set_icon (icon);110 notification.set_icon (icon);
141
142 notification.set_body (body);111 notification.set_body (body);
112
143 send_notification (null, notification);113 send_notification (null, notification);
144 }114 }
145 }115 }
146116
=== modified file 'src/daemon/CMakeLists.txt'
--- src/daemon/CMakeLists.txt 2016-06-03 20:27:01 +0000
+++ src/daemon/CMakeLists.txt 2016-08-30 20:17:54 +0000
@@ -9,16 +9,20 @@
99
10vala_precompile (VALA_C ${DAEMON_EXEC_NAME}10vala_precompile (VALA_C ${DAEMON_EXEC_NAME}
11 Daemon.vala11 Daemon.vala
12 Core.vala12 ProcessWatcher.vala
13 Process.vala13 Process.vala
14 IptablesHelper.vala14 IptablesHelper.vala
15 Timer.vala
15 SessionManager.vala16 SessionManager.vala
16 SessionHandler.vala17 SessionHandler.vala
17 Interfaces.vala18 Interfaces.vala
19 UserConfig.vala
18 Server.vala20 Server.vala
19 ${CMAKE_SOURCE_DIR}/src/shared/Vars.vala21 ${CMAKE_SOURCE_DIR}/src/shared/Vars.vala
20 ${CMAKE_SOURCE_DIR}/src/shared/Utils.vala22 ${CMAKE_SOURCE_DIR}/src/shared/Utils.vala
21 ${CMAKE_SOURCE_DIR}/src/shared/PAMControl.vala23 ${CMAKE_SOURCE_DIR}/src/shared/PAM/Token.vala
24 ${CMAKE_SOURCE_DIR}/src/shared/PAM/Reader.vala
25 ${CMAKE_SOURCE_DIR}/src/shared/PAM/Writer.vala
22PACKAGES26PACKAGES
23 posix27 posix
24 accountsservice28 accountsservice
2529
=== modified file 'src/daemon/Daemon.vala'
--- src/daemon/Daemon.vala 2016-05-19 19:47:38 +0000
+++ src/daemon/Daemon.vala 2016-08-30 20:17:54 +0000
@@ -45,22 +45,16 @@
4545
46 public static void on_exit (int signum) {46 public static void on_exit (int signum) {
47 if (session_manager != null) {47 if (session_manager != null) {
48 session_manager.current_handler.stop ();48 session_manager.stop ();
49 session_manager.current_handler.unref ();
50 }49 }
5150
52 terminate ();51 terminate ();
53 }52 }
5453
55 public override void activate () {54 public override void activate () {
55 Utils.get_usermanager ().notify["is-loaded"].connect (on_usermanager_loaded);
56
56 loop = new MainLoop ();57 loop = new MainLoop ();
57
58 Bus.own_name (BusType.SYSTEM, Vars.PARENTAL_CONTROLS_IFACE, BusNameOwnerFlags.REPLACE,
59 on_bus_aquired,
60 () => {},
61 on_bus_lost);
62 Utils.get_usermanager ().notify["is-loaded"].connect (on_usermanager_loaded);
63
64 loop.run ();58 loop.run ();
65 }59 }
6660
@@ -68,7 +62,7 @@
68 warning ("Could not acquire name: %s\n", name);62 warning ("Could not acquire name: %s\n", name);
69 }63 }
7064
71 private void on_bus_aquired (DBusConnection connection) {65 private void on_bus_acquired (DBusConnection connection) {
72 try {66 try {
73 connection.register_object (Vars.PARENTAL_CONTROLS_OBJECT_PATH, Server.get_default ());67 connection.register_object (Vars.PARENTAL_CONTROLS_OBJECT_PATH, Server.get_default ());
74 } catch (IOError e) {68 } catch (IOError e) {
@@ -81,6 +75,11 @@
81 return;75 return;
82 }76 }
8377
78 Bus.own_name (BusType.SYSTEM, Vars.PARENTAL_CONTROLS_IFACE, BusNameOwnerFlags.REPLACE,
79 on_bus_acquired,
80 () => {},
81 on_bus_lost);
82
84 session_manager = new SessionManager ();83 session_manager = new SessionManager ();
85 session_manager.start ();84 session_manager.start ();
86 }85 }
8786
=== modified file 'src/daemon/Interfaces.vala'
--- src/daemon/Interfaces.vala 2016-05-18 14:56:00 +0000
+++ src/daemon/Interfaces.vala 2016-08-30 20:17:54 +0000
@@ -19,38 +19,34 @@
19 *19 *
20 * Authored by: Adam Bieńkowski <donadigos159@gmail.com>20 * Authored by: Adam Bieńkowski <donadigos159@gmail.com>
21 */21 */
2222namespace PC.Daemon {
23public struct SessionStruct {23 public struct SessionStruct {
24 string name;24 string name;
25 uint32 uid;25 uint32 uid;
26 string user;26 string user;
27 string seat;27 string seat;
28 GLib.ObjectPath object_path;28 GLib.ObjectPath object_path;
29}29 }
3030
31public struct ActiveSessionStruct {31 public struct SeatStruct {
32 string session_id;32 string seat_id;
33 GLib.ObjectPath object_path;33 GLib.ObjectPath object_path;
34}34 }
3535
36[DBus (name = "org.freedesktop.login1.Manager")]36 [DBus (name = "org.freedesktop.login1.Manager")]
37public interface IManager : Object {37 public interface IManager : Object {
38 public abstract SessionStruct[] list_sessions () throws IOError;38 public abstract SessionStruct[] list_sessions () throws IOError;
39 public abstract GLib.ObjectPath get_seat (string seat) throws IOError;39 public abstract SeatStruct[] list_seats () throws IOError;
40 public signal void session_new (string user, GLib.ObjectPath object_path);40 public abstract GLib.ObjectPath get_seat (string seat) throws IOError;
41 public signal void session_removed (string user, GLib.ObjectPath object_path);41 public signal void session_new (string session, GLib.ObjectPath object_path);
42}42 public signal void session_removed (string session, GLib.ObjectPath object_path);
4343 }
44[DBus (name = "org.freedesktop.login1.Session")]44
45public interface ISession : Object {45 [DBus (name = "org.freedesktop.login1.Session")]
46 public abstract string name { owned get; }46 public interface ISession : Object {
4747 public abstract bool active { owned get; }
48 public abstract void lock () throws IOError;48 public abstract string name { owned get; }
4949 public abstract string id { owned get; }
50 public signal void unlock ();50 public abstract void terminate () throws IOError;
51}51 }
52
53[DBus (name = "org.freedesktop.login1.Seat")]
54public interface ISeat : Object {
55 public abstract ActiveSessionStruct active_session { owned get; }
56}52}
57\ No newline at end of file53\ No newline at end of file
5854
=== modified file 'src/daemon/IptablesHelper.vala'
--- src/daemon/IptablesHelper.vala 2016-05-17 13:51:36 +0000
+++ src/daemon/IptablesHelper.vala 2016-08-30 20:17:54 +0000
@@ -20,66 +20,64 @@
20 * Authored by: Adam Bieńkowski <donadigos159@gmail.com>20 * Authored by: Adam Bieńkowski <donadigos159@gmail.com>
21 */21 */
2222
23 namespace PC {23 namespace PC.Daemon {
24 public class IptablesHelper : Object {24 public class IptablesHelper : Object {
25 public bool valid = false;
26
27 private const string IPTABLES_EXEC = "iptables";25 private const string IPTABLES_EXEC = "iptables";
28 private const string HOST_EXEC = "host";
29 private const int ADDRESS_INDEX = 3;
30 private const int DPORT = 80;26 private const int DPORT = 80;
3127
32 private string[] urls;28 private UserConfig config;
3329 private string[] current_urls;
34 public IptablesHelper (string[] urls) {30 private ulong changed_signal_id;
35 this.urls = urls;31
36 valid = Environment.find_program_in_path (IPTABLES_EXEC) != null &&32 public static bool get_can_start () {
37 Environment.find_program_in_path (HOST_EXEC) != null &&33 return Environment.find_program_in_path (IPTABLES_EXEC) != null;
38 urls.length > 0;34 }
39 }35
4036 public IptablesHelper (UserConfig config) {
41 public void add_rules () {37 this.config = config;
42 foreach (string url in urls) {38 }
43 string[] addresses = get_addresses_from_url (url);39
40 public void start () {
41 this.current_urls = config.get_block_urls ();
42 add_rules ();
43
44 changed_signal_id = config.changed.connect (() => {
45 remove_rules ();
46
47 current_urls = config.get_block_urls ();
48 add_rules ();
49 });
50 }
51
52 public void stop () {
53 remove_rules ();
54 disconnect (changed_signal_id);
55 }
56
57 private void add_rules () {
58 foreach (string url in current_urls) {
59 string[] addresses = get_addresses_from_name (url);
44 foreach (string address in addresses) {60 foreach (string address in addresses) {
45 process_adress (address, "-A");61 process_adress (address, "-A");
46 }62 }
47 } 63 }
48 }64 }
4965
50 private string[] get_addresses_from_url (string url) {66 private string[] get_addresses_from_name (string name) {
51 string[] result = {};67 string[] address_list = {};
5268 var resolver = Resolver.get_default ();
53 string output;
54 int status;
55
56 try {69 try {
57 GLib.Process.spawn_sync ("/",70 var addresses = resolver.lookup_by_name (name, null);
58 { HOST_EXEC, url },71 foreach (InetAddress address in addresses) {
59 Environ.get (),72 if (address.get_family () == SocketFamily.IPV4) {
60 SpawnFlags.SEARCH_PATH,73 address_list += address.to_string ();
61 null,
62 out output,
63 null,
64 out status);
65 } catch (SpawnError e) {
66 warning ("%s\n", e.message);
67 }
68
69 if (status != 0) {
70 return result;
71 }
72
73 foreach (string line in output.split ("\n")) {
74 if (line.contains ("has address")) {
75 string[] elements = line.split (" ");
76 if (elements.length >= 3) {
77 result += elements[ADDRESS_INDEX];
78 }74 }
79 }75 }
76 } catch (Error e) {
77 warning (e.message);
80 }78 }
8179
82 return result;80 return address_list;
83 }81 }
8482
85 private void process_adress (string address, string option) {83 private void process_adress (string address, string option) {
@@ -97,9 +95,9 @@
97 }95 }
98 }96 }
9997
100 public void reset () {98 private void remove_rules () {
101 foreach (string url in urls) {99 foreach (string url in current_urls) {
102 string[] addresses = get_addresses_from_url (url);100 string[] addresses = get_addresses_from_name (url);
103 foreach (string address in addresses) {101 foreach (string address in addresses) {
104 process_adress (address, "-D");102 process_adress (address, "-D");
105 }103 }
106104
=== renamed file 'src/daemon/Core.vala' => 'src/daemon/ProcessWatcher.vala'
--- src/daemon/Core.vala 2016-05-19 19:54:37 +0000
+++ src/daemon/ProcessWatcher.vala 2016-08-30 20:17:54 +0000
@@ -21,68 +21,42 @@
21 */21 */
2222
23namespace PC.Daemon {23namespace PC.Daemon {
24 public class Core : GLib.Object, ExecMonitor {24 public class ProcessWatcher : GLib.Object, ExecMonitor {
25 public bool valid = true;25 public UserConfig? config = null;
26 public KeyFile key_file;
27
28 private Act.User user;
29
30 private string[] targets = {};
31 private bool admin = false;
32
33 private List<string> allowed_executables;26 private List<string> allowed_executables;
34
35 private Polkit.Authority authority;27 private Polkit.Authority authority;
36 private Server server;
3728
38 public Core (Act.User _user, Server _server) {29 public ProcessWatcher () {
39 user = _user;
40 server = _server;
41 allowed_executables = new List<string> ();30 allowed_executables = new List<string> ();
31
42 try {32 try {
43 authority = Polkit.Authority.get_sync ();33 authority = Polkit.Authority.get_sync ();
44 } catch (Error e) {34 } catch (Error e) {
45 warning ("%s\n", e.message);35 warning ("%s\n", e.message);
46 }36 }
4737 }
48 if (user == null) {38
49 valid = false;39 public void set_config (UserConfig? config) {
50 return;40 this.config = config;
51 }41 allowed_executables = new List<string> ();
52
53 key_file = new KeyFile ();
54
55 string conf_path = Utils.build_daemon_conf_path (user);
56 if (conf_path.strip () != "" && FileUtils.test (conf_path, FileTest.EXISTS)) {
57 try {
58 key_file.load_from_file (conf_path, 0);
59
60 targets = key_file.get_string_list (Vars.DAEMON_GROUP, Vars.DAEMON_KEY_TARGETS);
61 admin = key_file.get_boolean (Vars.DAEMON_GROUP, Vars.DAEMON_KEY_ADMIN);
62
63 valid = targets.length > 0;
64 } catch (FileError e) {
65 warning ("%s\n", e.message);
66 } catch (Error e) {
67 warning ("%s\n", e.message);
68 }
69 } else {
70 valid = false;
71 }
72 }42 }
7343
74 private void handle_pid (int pid) {44 private void handle_pid (int pid) {
45 if (config == null) {
46 return;
47 }
48
75 var process = new Process (pid);49 var process = new Process (pid);
7650
77 string command = process.get_command ();51 string? command = process.get_command ();
78 if (command == null || command == "") {52 if (command == null || command == "") {
79 return;53 return;
80 }54 }
8155
82 string[]? args = {};56 string[]? args = {};
83 try {57 try {
84 bool success = Shell.parse_argv (command, out args);58 Shell.parse_argv (command, out args);
85 if (!success || args == null || args.length < 1) {59 if (args == null || args.length < 1) {
86 return;60 return;
87 }61 }
88 } catch (ShellError e) {62 } catch (ShellError e) {
@@ -91,43 +65,44 @@
91 } 65 }
9266
93 string executable = args[0];67 string executable = args[0];
94 foreach (string _executable in allowed_executables) {
95 if (_executable == executable) {
96 allowed_executables.remove (_executable);
97 return;
98 }
99 }
10068
101 if (executable != null && !executable.has_prefix (Path.DIR_SEPARATOR_S)) {69 if (!executable.has_prefix (Path.DIR_SEPARATOR_S)) {
102 executable = Environment.find_program_in_path (executable);70 executable = Environment.find_program_in_path (executable);
103 }71 }
10472
105 if (executable != null && executable != "") {73 unowned List<string> link = allowed_executables.find_custom (executable, strcmp);
106 if (executable in targets) {74 if (link != null) {
107 process.kill ();75 allowed_executables.remove_link (link);
10876 return;
109 if (admin && authority != null) {77 }
110 server.authorize (user.get_user_name (), Vars.PARENTAL_CONTROLS_ACTION_ID);78
111 ulong signal_id = 0;79 if (executable != null && executable in config.get_targets ()) {
112 signal_id = server.authorization_ended.connect ((client_pid) => {80 process.kill ();
113 try {81
114 var result = authority.check_authorization_sync (Polkit.UnixProcess.new_for_owner (client_pid, 0, (int)user.get_uid ()),82 var server = Server.get_default ();
115 Vars.PARENTAL_CONTROLS_ACTION_ID,83 if (config.get_admin () && authority != null) {
116 null,84 ulong signal_id = 0;
117 Polkit.CheckAuthorizationFlags.ALLOW_USER_INTERACTION);85 signal_id = server.app_authorization_ended.connect ((client_pid) => {
118 if (result.get_is_authorized ()) {86 try {
119 allowed_executables.append (args[0]); 87 var unix_user = (Polkit.UnixUser)Polkit.UnixUser.new_for_name (config.username);
120 server.launch (args);88 var result = authority.check_authorization_sync (Polkit.UnixProcess.new_for_owner (client_pid, 0, unix_user.get_uid ()),
121 } 89 Vars.PARENTAL_CONTROLS_ACTION_ID,
122 } catch (Error e) {90 null,
123 warning ("%s\n", e.message);91 Polkit.CheckAuthorizationFlags.NONE);
92 if (result.get_is_authorized ()) {
93 allowed_executables.append (executable);
94 server.launch (args);
124 }95 }
12596 } catch (Error e) {
126 server.disconnect (signal_id);97 warning ("%s\n", e.message);
127 });98 }
128 } else {99
129 server.show_app_lock_dialog ();100 server.disconnect (signal_id);
130 }101 });
102
103 server.app_authorize (config.username, executable, Vars.PARENTAL_CONTROLS_ACTION_ID);
104 } else {
105 server.show_app_unavailable (executable);
131 }106 }
132 } 107 }
133 }108 }
134109
=== modified file 'src/daemon/Server.vala'
--- src/daemon/Server.vala 2016-02-25 18:47:22 +0000
+++ src/daemon/Server.vala 2016-08-30 20:17:54 +0000
@@ -21,9 +21,26 @@
21 */21 */
2222
23namespace PC.Daemon {23namespace PC.Daemon {
24 [DBus (name = "org.freedesktop.DBus")]
25 private interface DBus : Object {
26 [DBus (name = "GetConnectionUnixProcessID")]
27 public abstract uint32 get_connection_unix_process_id (string name) throws IOError;
28
29 public abstract uint32 get_connection_unix_user (string name) throws IOError;
30 }
31
32 [DBus (name = "org.pantheon.ParentalControls")]
33 public errordomain ParentalControlsError {
34 NOT_AUTHORIZED,
35 NOT_IMPLEMENTED,
36 USER_CONFIG_NOT_VAILD,
37 DBUS_CONNECTION_FAILED
38 }
39
24 [DBus (name = "org.pantheon.ParentalControls")]40 [DBus (name = "org.pantheon.ParentalControls")]
25 public class Server : Object {41 public class Server : Object {
26 private static Server? instance = null;42 private static Server? instance = null;
43 private DBus? bus_proxy = null;
2744
28 [DBus (visible = false)]45 [DBus (visible = false)]
29 public static Server get_default () {46 public static Server get_default () {
@@ -34,16 +51,205 @@
34 return instance;51 return instance;
35 }52 }
36 53
54 protected Server () {
55 try {
56 bus_proxy = Bus.get_proxy_sync (BusType.SYSTEM, "org.freedesktop.DBus", "/");
57 } catch (Error e) {
58 warning (e.message);
59 bus_proxy = null;
60 }
61
62 UserConfig.init ();
63 }
64
37 [DBus (visible = false)]65 [DBus (visible = false)]
38 public signal void authorization_ended (int client_pid);66 public signal void app_authorization_ended (int client_pid);
3967
40 public signal void show_app_lock_dialog ();68 public signal void app_authorize (string username, string path, string action_id);
41 public signal void authorize (string user, string action_id);
42 public signal void launch (string[] args);69 public signal void launch (string[] args);
43 public signal void send_time_notification (int hours, int minutes);70 public signal void show_app_unavailable (string path);
4471 public signal void show_timeout (int hours, int minutes);
45 public void end_authorization (int client_pid) {72 public signal void user_config_changed (string username);
46 authorization_ended (client_pid);73
74 public void end_app_authorization (BusName sender) {
75 uint32 pid = get_pid_from_sender (sender);
76 app_authorization_ended ((int)pid);
77 }
78
79 public void add_restriction_for_user (string input, bool clean, BusName sender) throws ParentalControlsError {
80 if (!get_sender_is_authorized (sender)) {
81 throw new ParentalControlsError.NOT_AUTHORIZED ("Error: sender not authorized");
82 }
83
84 ensure_pam_lightdm_enabled ();
85
86 var writer = PAM.Writer.new_for_time ();
87 writer.add_restriction_for_user (input, clean);
88 }
89
90 public void remove_restriction_for_user (string username, BusName sender) throws ParentalControlsError {
91 if (!get_sender_is_authorized (sender)) {
92 throw new ParentalControlsError.NOT_AUTHORIZED ("Error: sender not authorized");
93 }
94
95 var writer = PAM.Writer.new_for_time ();
96 writer.remove_restriction_for_user (username);
97 }
98
99 public void lock_dock_icons_for_user (string username, bool lock, BusName sender) throws ParentalControlsError {
100 throw new ParentalControlsError.NOT_IMPLEMENTED ("Error: not implemented");
101 }
102
103 public void set_user_daemon_active (string username, bool active, BusName sender) throws ParentalControlsError {
104 if (!get_sender_is_authorized (sender)) {
105 throw new ParentalControlsError.NOT_AUTHORIZED ("Error: sender not authorized");
106 }
107
108 var config = UserConfig.get_for_username (username, true);
109 if (config == null) {
110 throw new ParentalControlsError.USER_CONFIG_NOT_VAILD ("Error: config for %s is not valid or could not be created".printf (username));
111 }
112
113 config.set_active (active);
114 }
115
116 public void set_user_daemon_targets (string username, string[] targets, BusName sender) throws ParentalControlsError {
117 if (!get_sender_is_authorized (sender)) {
118 throw new ParentalControlsError.NOT_AUTHORIZED ("Error: sender not authorized");
119 }
120
121 var config = UserConfig.get_for_username (username, true);
122 if (config == null) {
123 throw new ParentalControlsError.USER_CONFIG_NOT_VAILD ("Error: config for %s is not valid or could not be created".printf (username));
124 }
125
126 config.set_targets (targets);
127 }
128
129 public void set_user_daemon_block_urls (string username, string[] block_urls, BusName sender) throws ParentalControlsError {
130 if (!get_sender_is_authorized (sender)) {
131 throw new ParentalControlsError.NOT_AUTHORIZED ("Error: sender not authorized");
132 }
133
134 var config = UserConfig.get_for_username (username, true);
135 if (config == null) {
136 throw new ParentalControlsError.USER_CONFIG_NOT_VAILD ("Error: config for %s is not valid or could not be created".printf (username));
137 }
138
139 config.set_block_urls (block_urls);
140 }
141
142 public void set_user_daemon_admin (string username, bool admin, BusName sender) throws ParentalControlsError {
143 if (!get_sender_is_authorized (sender)) {
144 throw new ParentalControlsError.NOT_AUTHORIZED ("Error: sender not authorized");
145 }
146
147 var config = UserConfig.get_for_username (username, true);
148 if (config == null) {
149 throw new ParentalControlsError.USER_CONFIG_NOT_VAILD ("Error: config for %s is not valid or could not be created".printf (username));
150 }
151
152 config.set_admin (admin);
153 }
154
155 public bool get_user_daemon_active (string username) throws ParentalControlsError {
156 var config = UserConfig.get_for_username (username, false);
157 if (config == null) {
158 throw new ParentalControlsError.USER_CONFIG_NOT_VAILD ("Error: config for %s is not valid or does not exist".printf (username));
159 }
160
161 return config.get_active ();
162 }
163
164 public string[] get_user_daemon_targets (string username) throws ParentalControlsError {
165 var config = UserConfig.get_for_username (username, false);
166 if (config == null) {
167 throw new ParentalControlsError.USER_CONFIG_NOT_VAILD ("Error: config for %s is not valid or does not exist".printf (username));
168 }
169
170 return config.get_targets ();
171 }
172
173 public string[] get_user_daemon_block_urls (string username) throws ParentalControlsError {
174 var config = UserConfig.get_for_username (username, false);
175 if (config == null) {
176 throw new ParentalControlsError.USER_CONFIG_NOT_VAILD ("Error: config for %s is not valid or does not exist".printf (username));
177 }
178
179 return config.get_block_urls ();
180 }
181
182 public bool get_user_daemon_admin (string username) throws ParentalControlsError {
183 var config = UserConfig.get_for_username (username, false);
184 if (config == null) {
185 throw new ParentalControlsError.USER_CONFIG_NOT_VAILD ("Error: config for %s is not valid or does not exist".printf (username));
186 }
187
188 return config.get_admin ();
189 }
190
191 private void ensure_pam_lightdm_enabled () {
192 string path = "/etc/pam.d/lightdm";
193
194 string contents;
195 try {
196 FileUtils.get_contents (path, out contents);
197 } catch (FileError e) {
198 warning (e.message);
199 return;
200 }
201
202 string conf_line = "\naccount required pam_time.so";
203 if (conf_line in contents) {
204 return;
205 }
206
207 contents += conf_line;
208
209 try {
210 FileUtils.set_contents (path, contents);
211 } catch (FileError e) {
212 warning ("%s\n", e.message);
213 }
214 }
215
216 private bool get_sender_is_authorized (BusName sender) throws ParentalControlsError {
217 if (bus_proxy == null) {
218 throw new ParentalControlsError.DBUS_CONNECTION_FAILED ("Error: connecting to org.freedesktop.DBus failed.");
219 }
220
221 uint32 user = 0, pid = 0;
222
223 try {
224 pid = get_pid_from_sender (sender);
225 user = bus_proxy.get_connection_unix_user (sender);
226 } catch (Error e) {
227 warning (e.message);
228 }
229
230 var subject = Polkit.UnixProcess.new_for_owner ((int)pid, 0, (int)user);
231
232 try {
233 var authority = Polkit.Authority.get_sync (null);
234 var auth_result = authority.check_authorization_sync (subject, Vars.PARENTAL_CONTROLS_ACTION_ID, null, Polkit.CheckAuthorizationFlags.NONE);
235 return auth_result.get_is_authorized ();
236 } catch (Error e) {
237 warning (e.message);
238 }
239
240 return false;
241 }
242
243 private uint32 get_pid_from_sender (BusName sender) {
244 uint32 pid = 0;
245
246 try {
247 pid = bus_proxy.get_connection_unix_process_id (sender);
248 } catch (Error e) {
249 warning (e.message);
250 }
251
252 return pid;
47 }253 }
48 }254 }
49}255}
50\ No newline at end of file256\ No newline at end of file
51257
=== modified file 'src/daemon/SessionHandler.vala'
--- src/daemon/SessionHandler.vala 2016-05-18 14:52:41 +0000
+++ src/daemon/SessionHandler.vala 2016-08-30 20:17:54 +0000
@@ -22,40 +22,46 @@
2222
23 namespace PC.Daemon {23 namespace PC.Daemon {
24 public class SessionHandler : Object {24 public class SessionHandler : Object {
25 private const int MINUTE_INTERVAL = 60;25 private IptablesHelper iptables_helper;
26 private const int HOUR_INTERVAL = 3600;26 private Timer? timer;
2727
28 public Core core;28 private UserConfig config;
29 public IptablesHelper iptables_helper;29 public ISession session;
30
31 private ISession session;
32 private Server server;30 private Server server;
3331
34 private bool can_start = true;32 private bool can_start = true;
3533
36 public SessionHandler (ISession _session) {34 public SessionHandler (ISession session) {
37 session = _session;35 this.session = session;
38 server = Server.get_default ();36 server = Server.get_default ();
39 Utils.set_user_name (session.name);37
4038 config = UserConfig.get_for_username (session.name, false);
41 core = new Core (Utils.get_current_user (), server);39 if (config == null || !config.get_active ()) {
4240 can_start = false;
43 try {41 return;
44 if (!core.key_file.has_group (Vars.DAEMON_GROUP) || !core.key_file.get_boolean (Vars.DAEMON_GROUP, Vars.DAEMON_KEY_ACTIVE)) {42 }
45 can_start = false;43
46 }44 iptables_helper = new IptablesHelper (config);
47 } catch (Error e) {45
48 warning ("%s\n", e.message);46 var token = PAM.Reader.get_token_for_user (Vars.PAM_TIME_CONF_PATH, session.name);
49 }47 if (token != null) {
5048 timer = new Timer (token);
51 string[] block_urls = {};49 timer.terminate.connect (() => {
52 try {50 try {
53 block_urls = core.key_file.get_string_list (Vars.DAEMON_GROUP, Vars.DAEMON_KEY_BLOCK_URLS);51 session.terminate ();
54 } catch (KeyFileError e) {52 } catch (IOError e) {
55 warning ("%s\n", e.message);53 warning (e.message);
56 }54 }
5755 });
58 iptables_helper = new IptablesHelper (block_urls);56 }
57 }
58
59 public string get_id () {
60 return session.id;
61 }
62
63 public UserConfig get_config () {
64 return config;
59 }65 }
6066
61 public void start () {67 public void start () {
@@ -63,142 +69,20 @@
63 return;69 return;
64 }70 }
6571
66 if (core.valid) {72 if (IptablesHelper.get_can_start ()) {
67 core.start.begin ();73 iptables_helper.start ();
68 }74 }
6975
70 if (iptables_helper.valid) {76 if (timer != null) {
71 iptables_helper.add_rules ();77 timer.start ();
72 }78 }
73
74 var restricts = PAMControl.get_all_restrictions ();
75 foreach (var restrict in restricts) {
76 if (restrict.user == Utils.get_current_user ().get_user_name ()) {
77 var current_date = new DateTime.now_local ();
78 string minute = current_date.get_minute ().to_string ();
79 if (int.parse (minute) < 10) {
80 minute = "0" + minute;
81 }
82
83 switch (restrict.day_id) {
84 case Vars.WEEKDAYS_ID:
85 if (current_date.get_day_of_week () < 6) {
86 int estimated_time = int.parse (restrict.to);
87 var span = get_difference_span (estimated_time, current_date);
88 start_loop ((int)((span / GLib.TimeSpan.MINUTE * -1) + 1) - 24 * MINUTE_INTERVAL);
89 }
90
91 break;
92 case Vars.WEEKENDS_ID:
93 if (current_date.get_day_of_week () >= 6) {
94 int estimated_time = int.parse (restrict.to);
95 var span = get_difference_span (estimated_time, current_date);
96 start_loop ((int)((span / GLib.TimeSpan.MINUTE * -1) + 1) - 24 * MINUTE_INTERVAL);
97 }
98
99 break;
100 case Vars.ALL_ID:
101 int estimated_time = 2400;
102 if (current_date.get_day_of_week () < 6) {
103 estimated_time = int.parse (restrict.weekday_hours.split ("-")[1]);
104 } else if (current_date.get_day_of_week () >= 6) {
105 estimated_time = int.parse (restrict.weekend_hours.split ("-")[1]);
106 }
107
108 var span = get_difference_span (estimated_time, current_date);
109 int minutes = (int)((span / GLib.TimeSpan.MINUTE * -1) + 1) - 24 * MINUTE_INTERVAL;
110 if (minutes > 0) {
111 start_loop (minutes);
112 } else {
113 lock_session ();
114 }
115
116 break;
117 default:
118 break;
119 }
120 }
121 }
122 }79 }
12380
124 public void stop () {81 public void stop () {
125 core.stop ();82 iptables_helper.stop ();
126 iptables_helper.reset ();83 if (timer != null) {
127 }84 timer.stop ();
12885 }
129 private TimeSpan get_difference_span (int estimated_time, DateTime current_date) {86 }
130 bool end_day = (estimated_time == 2400 || estimated_time == 0);
131 if (end_day) {
132 estimated_time = 2359;
133 }
134
135 char[] tmp = estimated_time.to_string ().to_utf8 ();
136 int _h, _m;
137 _h = int.parse (tmp[0].to_string () + tmp[1].to_string ());
138 _m = int.parse (tmp[2].to_string () + tmp[3].to_string ());
139
140 var estimated_date = new DateTime.local (current_date.get_year (),
141 current_date.get_month (),
142 current_date.get_day_of_week (),
143 _h, _m, 0);
144
145 var span = current_date.difference (estimated_date);
146 if (end_day) {
147 span -= GLib.TimeSpan.MINUTE;
148 }
149
150 return span;
151 }
152
153 private int get_estimated_hours (int minutes) {
154 if (minutes >= MINUTE_INTERVAL) {
155 return minutes / MINUTE_INTERVAL;
156 }
157
158 return 0;
159 }
160
161 private void start_loop (int minutes) {
162 int _hours = get_estimated_hours (minutes);
163 int remaining_minutes = minutes - (MINUTE_INTERVAL * _hours);
164 server.send_time_notification (_hours, remaining_minutes);
165
166 schedule_notification (remaining_minutes, 5);
167 schedule_notification (remaining_minutes, 1);
168
169 Timeout.add_seconds (MINUTE_INTERVAL, () => {
170 remaining_minutes--;
171 if (remaining_minutes == 0) {
172 lock_session ();
173 }
174
175 return (remaining_minutes != 0);
176 });
177
178 Timeout.add_seconds (HOUR_INTERVAL, () => {
179 int hours = get_estimated_hours (minutes);
180 if (hours > 0) {
181 server.send_time_notification (hours, minutes - (MINUTE_INTERVAL * hours));
182 }
183
184 return (hours != 0 || minutes != 0);
185 });
186 }
187
188 private void schedule_notification (int remaining_minutes, int minutes) {
189 Timeout.add_seconds ((remaining_minutes - minutes) * MINUTE_INTERVAL, () => {
190 server.send_time_notification (0, minutes);
191
192 return false;
193 });
194 }
195
196 private void lock_session () {
197 try {
198 session.lock ();
199 } catch (IOError e) {
200 warning ("%s\n", e.message);
201 }
202 }
203 }87 }
204}88}
205\ No newline at end of file89\ No newline at end of file
20690
=== modified file 'src/daemon/SessionManager.vala'
--- src/daemon/SessionManager.vala 2016-05-18 14:56:00 +0000
+++ src/daemon/SessionManager.vala 2016-08-30 20:17:54 +0000
@@ -22,10 +22,12 @@
2222
23 namespace PC.Daemon {23 namespace PC.Daemon {
24 public class SessionManager : Object {24 public class SessionManager : Object {
25 public SessionHandler? current_handler = null;25 private SessionHandler? current_handler = null;
26
27 private IManager? manager = null;26 private IManager? manager = null;
28 private DBusConnection? conn = null;27 private DBusConnection? conn = null;
28 private ProcessWatcher pwatcher;
29
30 private uint[] signal_ids;
2931
30 public SessionManager () {32 public SessionManager () {
31 try {33 try {
@@ -34,6 +36,9 @@
34 } catch (IOError e) {36 } catch (IOError e) {
35 warning ("%s\n", e.message);37 warning ("%s\n", e.message);
36 }38 }
39
40 pwatcher = new ProcessWatcher ();
41 pwatcher.start.begin ();
37 }42 }
3843
39 public void start () {44 public void start () {
@@ -44,22 +49,39 @@
44 manager.session_new.connect (() => update_session ());49 manager.session_new.connect (() => update_session ());
45 manager.session_removed.connect (() => update_session ());50 manager.session_removed.connect (() => update_session ());
4651
47 conn.signal_subscribe (null,52 try {
48 Vars.DBUS_PROPERTIES_IFACE,53 foreach (SeatStruct seat_s in manager.list_seats ()) {
49 "PropertiesChanged",54 signal_ids += conn.signal_subscribe (null,
50 get_current_seat_path (),55 Vars.DBUS_PROPERTIES_IFACE,
51 null,56 "PropertiesChanged",
52 0,57 seat_s.object_path,
53 () => update_session ());58 null,
59 0,
60 () => update_session ());
61 }
62 } catch (IOError e) {
63 warning (e.message);
64 }
65
54 update_session ();66 update_session ();
55 }67 }
5668
69 public void stop () {
70 foreach (uint signal_id in signal_ids) {
71 conn.signal_unsubscribe (signal_id);
72 }
73
74 stop_current_handler ();
75 }
76
57 private ISession? get_current_session () {77 private ISession? get_current_session () {
58 try {78 try {
59 string? seat_path = get_current_seat_path ();79 var structs = manager.list_sessions ();
60 if (seat_path != null) {80 foreach (SessionStruct session_s in structs) {
61 ISeat? seat = Bus.get_proxy_sync (BusType.SYSTEM, Vars.LOGIN_IFACE, seat_path);81 ISession? session = Bus.get_proxy_sync (BusType.SYSTEM, Vars.LOGIN_IFACE, session_s.object_path);
62 return Bus.get_proxy_sync (BusType.SYSTEM, Vars.LOGIN_IFACE, seat.active_session.object_path);82 if (session != null && session.active) {
83 return session;
84 }
63 }85 }
64 } catch (IOError e) {86 } catch (IOError e) {
65 warning ("%s\n", e.message);87 warning ("%s\n", e.message);
@@ -68,37 +90,30 @@
68 return null; 90 return null;
69 }91 }
7092
71 private string? get_current_seat_path () {
72 try {
73 string seat_id = "seat0";
74 string? seat_variable = Environment.get_variable ("XDG_SEAT");
75 if (seat_variable != null && seat_variable.has_prefix ("seat")) {
76 seat_id = seat_variable;
77 }
78
79 return manager.get_seat (seat_id);
80 } catch (Error e) {
81 warning ("%s\n", e.message);
82 }
83
84 return null;
85 }
86
87 private void update_session () {93 private void update_session () {
94 var session = get_current_session ();
95 if (session.id == current_handler.get_id ()) {
96 return;
97 }
98
99 stop_current_handler ();
100
101 if (session != null &&
102 session.name != null &&
103 !(session.name in Vars.DAEMON_IGNORED_USERS)) {
104 current_handler = new SessionHandler (session);
105 current_handler.start ();
106 pwatcher.set_config (current_handler.get_config ());
107 }
108 }
109
110 private void stop_current_handler () {
88 if (current_handler != null) {111 if (current_handler != null) {
89 current_handler.stop ();112 current_handler.stop ();
90 current_handler.unref ();
91 current_handler = null;113 current_handler = null;
92 }114 }
93115
94 var current_session = get_current_session ();116 pwatcher.set_config (null);
95 if (current_session != null &&
96 current_session.name != null &&
97 current_session.name.strip () != "" &&
98 !(current_session.name in Vars.DAEMON_IGNORED_USERS)) {
99 current_handler = new SessionHandler (current_session);
100 current_handler.start ();
101 }
102 }117 }
103 }118 }
104}119}
105\ No newline at end of file120\ No newline at end of file
106121
=== added file 'src/daemon/Timer.vala'
--- src/daemon/Timer.vala 1970-01-01 00:00:00 +0000
+++ src/daemon/Timer.vala 2016-08-30 20:17:54 +0000
@@ -0,0 +1,116 @@
1// -*- Mode: vala; indent-tabs-mode: nil; tab-width: 4 -*-
2/*-
3 * Copyright (c) 2016 Adam Bieńkowski (https://launchpad.net/switchboard-plug-parental-controls)
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Library General Public
7 * License as published by the Free Software Foundation; either
8 * version 3 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Library General Public License for more details.
14 *
15 * You should have received a copy of the GNU Library General Public
16 * License along with this library; if not, write to the
17 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 * Boston, MA 02111-1307, USA.
19 *
20 * Authored by: Adam Bieńkowski <donadigos159@gmail.com>
21 */
22
23namespace PC.Daemon {
24 public class Timer : Object {
25 private const int MINUTE_INTERVAL = 60;
26 private const int HOUR_INTERVAL = 3600;
27
28 public signal void terminate ();
29
30 private PAM.Token token;
31 private uint[] timeout_ids;
32
33 public Timer (PAM.Token token) {
34 this.token = token;
35 }
36
37 public void start () {
38 var times_info = token.get_times_info ();
39 if (times_info.length () == 0) {
40 return;
41 }
42
43 PAM.TimeInfo? current = null;
44
45 int day_of_week = new DateTime.now_local ().get_day_of_week ();
46 foreach (PAM.TimeInfo info in times_info) {
47 if ((day_of_week < 6 && info.day_type == PAM.DayType.WEEKDAY)
48 || (day_of_week >= 6 && info.day_type == PAM.DayType.WEEKEND)
49 || info.day_type == PAM.DayType.ALL) {
50 current = info;
51 break;
52 }
53 }
54
55 if (current == null) {
56 return;
57 }
58
59 var span = get_difference_span (current.to);
60 int minutes = ((int)(span / GLib.TimeSpan.MINUTE)).abs ();
61
62 if (minutes > 0) {
63 start_loop (minutes);
64 } else {
65 terminate ();
66 }
67
68 timeout_ids += Timeout.add_seconds (HOUR_INTERVAL * 24, () => {
69 start ();
70 return true;
71 });
72 }
73
74 public void stop () {
75 foreach (uint timeout_id in timeout_ids) {
76 GLib.Source.remove (timeout_id);
77 }
78 }
79
80 private TimeSpan get_difference_span (string estimated_time_str) {
81 int hour = int.parse (estimated_time_str.slice (0, 2));
82 int minute = int.parse (estimated_time_str.substring (2));
83
84 if (hour == 24) {
85 hour = 0;
86 }
87
88 var current_date = new DateTime.now_local ();
89 var estimated_date = current_date.add_full (0, 0,
90 (hour < current_date.get_hour ()) ? 1 : 0,
91 hour - current_date.get_hour (),
92 minute - current_date.get_minute (),
93 0);
94 return estimated_date.difference (current_date);
95 }
96
97 private void start_loop (int minutes) {
98 Server.get_default ().show_timeout (minutes / MINUTE_INTERVAL, minutes % MINUTE_INTERVAL);
99 timeout_ids += Timeout.add_seconds (MINUTE_INTERVAL, () => {
100 minutes--;
101 if (minutes == MINUTE_INTERVAL ||
102 minutes == 10 ||
103 minutes == 5 ||
104 minutes == 1) {
105 Server.get_default ().show_timeout (minutes / MINUTE_INTERVAL, minutes % MINUTE_INTERVAL);
106 }
107
108 if (minutes == 0) {
109 terminate ();
110 }
111
112 return (minutes > 0);
113 });
114 }
115 }
116}
0\ No newline at end of file117\ No newline at end of file
1118
=== added file 'src/daemon/UserConfig.vala'
--- src/daemon/UserConfig.vala 1970-01-01 00:00:00 +0000
+++ src/daemon/UserConfig.vala 2016-08-30 20:17:54 +0000
@@ -0,0 +1,208 @@
1// -*- Mode: vala; indent-tabs-mode: nil; tab-width: 4 -*-
2/*-
3 * Copyright (c) 2015-2016 Adam Bieńkowski (https://launchpad.net/switchboard-plug-parental-controls)
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Library General Public
7 * License as published by the Free Software Foundation; either
8 * version 3 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Library General Public License for more details.
14 *
15 * You should have received a copy of the GNU Library General Public
16 * License along with this library; if not, write to the
17 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 * Boston, MA 02111-1307, USA.
19 *
20 * Authored by: Adam Bieńkowski <donadigos159@gmail.com>
21 */
22
23namespace PC.Daemon {
24 public class UserConfig : Object {
25 public string username;
26 public signal void changed ();
27
28 private KeyFile key;
29 private string config_path;
30
31 private static List<UserConfig> config_list;
32
33 public static UserConfig? get_for_username (string username, bool create) {
34 foreach (UserConfig config in config_list) {
35 if (config.username == username) {
36 return config;
37 }
38 }
39
40 if (create) {
41 return create_for_username (username);
42 }
43
44 return null;
45 }
46
47 public static List<UserConfig> get_all () {
48 return config_list.copy ();
49 }
50
51 public static void init () {
52 config_list = new List<UserConfig> ();
53
54 foreach (Act.User user in Utils.get_usermanager ().list_users ()) {
55 string config_path = Utils.build_daemon_conf_path (user);
56 if (!FileUtils.test (config_path, FileTest.IS_REGULAR)) {
57 continue;
58 }
59
60 var key = new KeyFile ();
61 key.set_list_separator (';');
62 try {
63 if (!key.load_from_file (config_path, KeyFileFlags.NONE)) {
64 continue;
65 }
66 } catch (KeyFileError e) {
67 warning (e.message);
68 continue;
69 } catch (FileError e) {
70 warning (e.message);
71 continue;
72 }
73
74 var user_config = new UserConfig (config_path, user.get_user_name (), key);
75 config_list.append (user_config);
76 }
77 }
78
79 private static UserConfig? create_for_username (string username) {
80 var user = Utils.get_usermanager ().get_user (username);
81 if (user == null) {
82 return null;
83 }
84
85 string config_path = Utils.build_daemon_conf_path (user);
86 var file = File.new_for_path (config_path);
87 try {
88 file.create (FileCreateFlags.PRIVATE);
89 } catch (FileError e) {
90 warning (e.message);
91 return null;
92 } catch (Error e) {
93 warning (e.message);
94 return null;
95 }
96
97 var config = new UserConfig (config_path, username, new KeyFile ());
98 config_list.append (config);
99 return config;
100 }
101
102 private UserConfig (string config_path, string username, KeyFile key) {
103 this.username = username;
104 this.key = key;
105 this.config_path = config_path;
106 monitor_file ();
107 }
108
109 public void set_active (bool active) {
110 key.set_boolean (Vars.DAEMON_GROUP, Vars.DAEMON_KEY_ACTIVE, active);
111 save ();
112 }
113
114 public void set_targets (string[] targets) {
115 key.set_string_list (Vars.DAEMON_GROUP, Vars.DAEMON_KEY_TARGETS, targets);
116 save ();
117 }
118
119 public void set_block_urls (string[] block_urls) {
120 key.set_string_list (Vars.DAEMON_GROUP, Vars.DAEMON_KEY_BLOCK_URLS, block_urls);
121 save ();
122 }
123
124 public void set_admin (bool admin) {
125 key.set_boolean (Vars.DAEMON_GROUP, Vars.DAEMON_KEY_ADMIN, admin);
126 save ();
127 }
128
129 public bool get_active () {
130 try {
131 return key.get_boolean (Vars.DAEMON_GROUP, Vars.DAEMON_KEY_ACTIVE);
132 } catch (KeyFileError e) {
133 warning (e.message);
134 }
135
136 return false;
137 }
138
139 public string[] get_targets () {
140 try {
141 return key.get_string_list (Vars.DAEMON_GROUP, Vars.DAEMON_KEY_TARGETS);
142 } catch (KeyFileError e) {
143 warning (e.message);
144 }
145
146 return {};
147 }
148
149 public string[] get_block_urls () {
150 try {
151 return key.get_string_list (Vars.DAEMON_GROUP, Vars.DAEMON_KEY_BLOCK_URLS);
152 } catch (KeyFileError e) {
153 warning (e.message);
154 }
155
156 return {};
157 }
158
159 public bool get_admin () {
160 try {
161 return key.get_boolean (Vars.DAEMON_GROUP, Vars.DAEMON_KEY_ADMIN);
162 } catch (KeyFileError e) {
163 warning (e.message);
164 }
165
166 return false;
167 }
168
169 private void monitor_file () {
170 var file = File.new_for_path (config_path);
171 try {
172 var monitor = file.monitor (FileMonitorFlags.NONE, null);
173 monitor.changed.connect ((src, dest, event) => {
174 if (event == FileMonitorEvent.CHANGES_DONE_HINT) {
175 update_key ();
176
177 Server.get_default ().user_config_changed (username);
178 changed ();
179 }
180 });
181 } catch (Error e) {
182 warning (e.message);
183 }
184 }
185
186 private void update_key () {
187 try {
188 key.load_from_file (config_path, KeyFileFlags.NONE);
189 } catch (KeyFileError e) {
190 warning (e.message);
191 } catch (FileError e) {
192 warning (e.message);
193 }
194 }
195
196 private void save () {
197 try {
198 key.save_to_file (config_path);
199 } catch (FileError e) {
200 warning (e.message);
201 return;
202 }
203
204 Server.get_default ().user_config_changed (username);
205 changed ();
206 }
207 }
208}
0\ No newline at end of file209\ No newline at end of file
1210
=== added directory 'src/shared/PAM'
=== added file 'src/shared/PAM/Reader.vala'
--- src/shared/PAM/Reader.vala 1970-01-01 00:00:00 +0000
+++ src/shared/PAM/Reader.vala 2016-08-30 20:17:54 +0000
@@ -0,0 +1,66 @@
1// -*- Mode: vala; indent-tabs-mode: nil; tab-width: 4 -*-
2/*-
3 * Copyright (c) 2015 Adam Bieńkowski (https://launchpad.net/switchboard-plug-parental-controls)
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Library General Public
7 * License as published by the Free Software Foundation; either
8 * version 3 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Library General Public License for more details.
14 *
15 * You should have received a copy of the GNU Library General Public
16 * License along with this library; if not, write to the
17 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 * Boston, MA 02111-1307, USA.
19 *
20 * Authored by: Adam Bieńkowski <donadigos159@gmail.com>
21 */
22
23namespace PC.PAM {
24 public class Reader : Object {
25 public static string get_config (string contents, bool ignore_comments = true) {
26 string config = "";
27
28 int start_idx = contents.index_of (Vars.PAM_CONF_START);
29 int end_idx = contents.index_of (Vars.PAM_CONF_END) + Vars.PAM_CONF_END.char_count ();
30 if (start_idx == -1 || end_idx == -1) {
31 return config;
32 }
33
34 config = contents.slice (start_idx, end_idx);
35
36 if (ignore_comments) {
37 return Utils.remove_comments (config);
38 }
39
40 return config;
41 }
42
43 public static List<Token> get_tokens (string filename) {
44 string contents;
45 try {
46 FileUtils.get_contents (filename, out contents);
47 } catch (FileError e) {
48 warning (e.message);
49 return new List<Token> ();
50 }
51
52 string config = get_config (contents);
53 return Token.parse (config);
54 }
55
56 public static Token? get_token_for_user (string filename, string username) {
57 foreach (Token token in get_tokens (filename)) {
58 if (token.get_user_arg0 () == username) {
59 return token;
60 }
61 }
62
63 return null;
64 }
65 }
66}
0\ No newline at end of file67\ No newline at end of file
168
=== added file 'src/shared/PAM/Token.vala'
--- src/shared/PAM/Token.vala 1970-01-01 00:00:00 +0000
+++ src/shared/PAM/Token.vala 2016-08-30 20:17:54 +0000
@@ -0,0 +1,190 @@
1// -*- Mode: vala; indent-tabs-mode: nil; tab-width: 4 -*-
2/*-
3 * Copyright (c) 2015 Adam Bieńkowski (https://launchpad.net/switchboard-plug-parental-controls)
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Library General Public
7 * License as published by the Free Software Foundation; either
8 * version 3 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Library General Public License for more details.
14 *
15 * You should have received a copy of the GNU Library General Public
16 * License along with this library; if not, write to the
17 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 * Boston, MA 02111-1307, USA.
19 *
20 * Authored by: Adam Bieńkowski <donadigos159@gmail.com>
21 */
22
23namespace PC.PAM {
24 public enum DayType {
25 UNKNOWN,
26 ALL,
27 WEEKDAY,
28 WEEKEND;
29
30 public static DayType to_enum (string str) {
31 switch (str) {
32 case "Al":
33 return ALL;
34 case "Wk":
35 return WEEKDAY;
36 case "Wd":
37 return WEEKEND;
38 default:
39 return UNKNOWN;
40 }
41 }
42
43 public string to_string () {
44 switch (this) {
45 case ALL:
46 return "Al";
47 case WEEKDAY:
48 return "Wk";
49 case WEEKEND:
50 return "Wd";
51 default:
52 case UNKNOWN:
53 return "unknown";
54 }
55 }
56 }
57
58 public class TimeInfo {
59 public DayType day_type = DayType.UNKNOWN;
60 public string from = "";
61 public string to = "";
62 }
63
64 public class Token : Object {
65 private const int SERVICES_INDEX = 0;
66 private const int TTYS_INDEX = 1;
67 private const int USERS_INDEX = 2;
68 private const int TIMES_INDEX = 3;
69
70 private const string TYPE_SEPARATOR = ";";
71 private const string LIST_SEPARATOR = "|";
72
73 public string[] services;
74 public string[] ttys;
75 public string[] users;
76 public string[] times;
77
78 public static Token? parse_line (string line) {
79 string[] strv = line.split (TYPE_SEPARATOR);
80 if (strv.length != 4) {
81 return null;
82 }
83
84 var token = new Token ();
85 token.services = strv[SERVICES_INDEX].split (LIST_SEPARATOR);
86 token.ttys = strv[TTYS_INDEX].split (LIST_SEPARATOR);
87 token.users = strv[USERS_INDEX].split (LIST_SEPARATOR);
88 token.times = strv[TIMES_INDEX].split (LIST_SEPARATOR);
89 return token;
90 }
91
92 public static List<Token> parse (string str) {
93 var list = new List<Token> ();
94
95 foreach (string line in str.split ("\n")) {
96 var token = parse_line (line);
97 if (token != null) {
98 list.append (token);
99 }
100 }
101
102 return list;
103 }
104
105 public static string construct_pam_restriction (string[] services, string[] ttys, string users[], string[] times) {
106 string services_str = string.joinv (LIST_SEPARATOR, services);
107 string ttys_str = string.joinv (LIST_SEPARATOR, ttys);
108 string users_str = string.joinv (LIST_SEPARATOR, users);
109 string times_str = string.joinv (LIST_SEPARATOR, times);
110
111 return "%s;%s;%s;%s".printf (services_str, ttys_str, users_str, times_str);
112 }
113
114 public static string construct_pam_restriction_simple (string[] users, string[] times) {
115 return construct_pam_restriction ({ "*" }, { "*" }, users, times);
116 }
117
118 public List<TimeInfo> get_times_info () {
119 var list = new List<TimeInfo> ();
120
121 if (times.length == 0) {
122 return list;
123 }
124
125 foreach (string time in times) {
126 string[] bounds = time.substring (2).split ("-");
127 if (bounds.length < 2) {
128 continue;
129 }
130
131 var info = new TimeInfo ();
132 info.day_type = DayType.to_enum (time.slice (0, 2));
133 info.from = bounds[0];
134 info.to = bounds[1];
135
136 list.append (info);
137 }
138
139 return list;
140 }
141
142 public string get_user_arg0 () {
143 if (users.length == 0) {
144 return "";
145 }
146
147 return users[0];
148 }
149
150 public string to_string () {
151 return construct_pam_restriction (services, ttys, users, times);
152 }
153
154 public void get_weekday_hours (out int from, out int to) {
155 if (times.length < 1) {
156 from = 0;
157 to = 0;
158 return;
159 }
160
161 string[] bounds = times[0].substring (2).split ("-");
162 if (bounds.length < 2) {
163 from = 0;
164 to = 0;
165 return;
166 }
167
168 from = int.parse (bounds[0]);
169 to = int.parse (bounds[1]);
170 }
171
172 public void get_weekend_hours (out int from, out int to) {
173 if (times.length < 2) {
174 from = 0;
175 to = 0;
176 return;
177 }
178
179 string[] bounds = times[1].split ("-");
180 if (bounds.length < 2) {
181 from = 0;
182 to = 0;
183 return;
184 }
185
186 from = int.parse (bounds[0]);
187 to = int.parse (bounds[1]);
188 }
189 }
190}
0\ No newline at end of file191\ No newline at end of file
1192
=== added file 'src/shared/PAM/Writer.vala'
--- src/shared/PAM/Writer.vala 1970-01-01 00:00:00 +0000
+++ src/shared/PAM/Writer.vala 2016-08-30 20:17:54 +0000
@@ -0,0 +1,110 @@
1// -*- Mode: vala; indent-tabs-mode: nil; tab-width: 4 -*-
2/*-
3 * Copyright (c) 2015 Adam Bieńkowski (https://launchpad.net/switchboard-plug-parental-controls)
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Library General Public
7 * License as published by the Free Software Foundation; either
8 * version 3 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Library General Public License for more details.
14 *
15 * You should have received a copy of the GNU Library General Public
16 * License along with this library; if not, write to the
17 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 * Boston, MA 02111-1307, USA.
19 *
20 * Authored by: Adam Bieńkowski <donadigos159@gmail.com>
21 */
22
23namespace PC.PAM {
24 public class Writer : Object {
25 private string filename;
26
27 public static Writer new_for_time () {
28 return new Writer (Vars.PAM_TIME_CONF_PATH);
29 }
30
31 public Writer (string filename) {
32 this.filename = filename;
33 }
34
35 public void add_restriction_for_user (string input, bool clean) {
36 if (clean) {
37 var token = Token.parse_line (input);
38 if (token != null) {
39 string username = token.get_user_arg0 ();
40 if (Reader.get_token_for_user (filename, username) != null) {
41 remove_restriction_for_user (username);
42 }
43 }
44 }
45
46 string contents;
47 try {
48 FileUtils.get_contents (filename, out contents);
49 } catch (FileError e) {
50 warning (e.message);
51 return;
52 }
53
54 string config = Reader.get_config (contents, false);
55
56 var builder = new StringBuilder (Vars.PAM_CONF_START);
57 if (config != "") {
58 builder.append ("\n");
59 builder.append (Utils.remove_comments (config));
60 } else {
61 builder.append ("\n");
62 }
63
64 builder.append (input);
65 builder.append ("\n");
66 builder.append (Vars.PAM_CONF_END);
67
68 try {
69 if (config != "") {
70 FileUtils.set_contents (filename, contents.replace (config, builder.str));
71 } else {
72 FileUtils.set_contents (filename, "\n%s\n%s\n".printf (contents, builder.str));
73 }
74 } catch (FileError e) {
75 warning ("%s\n", e.message);
76 }
77 }
78
79 public void remove_restriction_for_user (string username) {
80 string contents;
81 try {
82 FileUtils.get_contents (filename, out contents);
83 } catch (FileError e) {
84 warning (e.message);
85 return;
86 }
87
88 string config = Reader.get_config (contents);
89
90 if (config == "") {
91 return;
92 }
93
94 string buffer = "";
95
96 foreach (string line in config.split ("\n")) {
97 var token = Token.parse_line (line);
98 if (token != null && token.get_user_arg0 () != username) {
99 buffer += line;
100 }
101 }
102
103 try {
104 FileUtils.set_contents (filename, contents.replace (config, buffer));
105 } catch (FileError e) {
106 warning ("%s\n", e.message);
107 }
108 }
109 }
110}
0\ No newline at end of file111\ No newline at end of file
1112
=== removed file 'src/shared/PAMControl.vala'
--- src/shared/PAMControl.vala 2016-02-21 18:52:25 +0000
+++ src/shared/PAMControl.vala 1970-01-01 00:00:00 +0000
@@ -1,128 +0,0 @@
1// -*- Mode: vala; indent-tabs-mode: nil; tab-width: 4 -*-
2/*-
3 * Copyright (c) 2015 Adam Bieńkowski (https://launchpad.net/switchboard-plug-parental-controls)
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Library General Public
7 * License as published by the Free Software Foundation; either
8 * version 3 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Library General Public License for more details.
14 *
15 * You should have received a copy of the GNU Library General Public
16 * License along with this library; if not, write to the
17 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 * Boston, MA 02111-1307, USA.
19 *
20 * Authored by: Adam Bieńkowski <donadigos159@gmail.com>
21 */
22
23namespace PC {
24 public class PAMRestrictInfo : Object {
25 public string user;
26 public string day_id;
27 public string weekday_hours;
28 public string weekend_hours;
29 public string from;
30 public string to;
31 }
32
33 public class PAMControl : Object {
34 private static string get_conf_section () {
35 string contents = "";
36 try {
37 FileUtils.get_contents ("/etc/security/time.conf", out contents);
38 } catch (FileError e) {
39 warning ("%s\n", e.message);
40 }
41
42 try {
43 var regex = new Regex (Vars.PAM_CONF_REGEX);
44
45 if (regex.match (contents)) {
46 int i = 0;
47 foreach (string str in regex.split (contents)) {
48 // Do not replace the contents of the PC plug section
49 if (i != 1) {
50 contents = contents.replace (str, "");
51 }
52
53 i++;
54 }
55 } else {
56 return "";
57 }
58 } catch (RegexError e) {
59 warning ("%s\n", e.message);
60 }
61
62
63 return contents;
64 }
65
66 public static List<PAMRestrictInfo?> get_all_restrictions () {
67 var retval = new List<PAMRestrictInfo?> ();
68 string conf = get_conf_section ();
69
70 foreach (string line in conf.split ("\n")) {
71 if (!line.has_prefix ("#")) {
72 var restrict_info = new PAMRestrictInfo ();
73 string[] units = line.split (";");
74 if (units.length >= 4) {
75 restrict_info.user = units[2];
76 string time = units[3];
77 time = time.replace ("!", "");
78
79 char[] time_chars = time.to_utf8 ();
80 string day = time_chars[0].to_string () + time_chars[1].to_string ();
81 switch (day.down ()) {
82 case "al":
83 restrict_info.day_id = "all";
84 break;
85 case "wk":
86 restrict_info.day_id = "weekdays";
87 break;
88 case "wd":
89 restrict_info.day_id = "weekends";
90 break;
91 default:
92 restrict_info.day_id = "";
93 break;
94 }
95
96 string hours = time.replace (day, "");
97 string[] tmp = {};
98 if ("|" in hours) {
99 tmp = hours.split ("|");
100 restrict_info.weekday_hours = tmp[0];
101 restrict_info.weekend_hours = tmp[1];
102 } else {
103 tmp = hours.split ("-");
104 restrict_info.from = tmp[0];
105 restrict_info.to = tmp[1];
106 }
107
108 retval.append (restrict_info);
109 }
110 }
111 }
112
113 return retval;
114 }
115
116 public static void try_add_restrict_line (string user_name, string restrict) {
117 if (Utils.get_permission ().allowed) {
118 Utils.call_cli ({"--user", user_name, "--restrict-pam-line", restrict});
119 }
120 }
121
122 public static void try_remove_user_restrict (string user_name) {
123 if (Utils.get_permission ().allowed) {
124 Utils.call_cli ({"--user", user_name, "--remove-restrict"});
125 }
126 }
127 }
128}
129\ No newline at end of file0\ No newline at end of file
1301
=== modified file 'src/shared/Utils.vala'
--- src/shared/Utils.vala 2016-07-27 16:14:53 +0000
+++ src/shared/Utils.vala 2016-08-30 20:17:54 +0000
@@ -27,11 +27,62 @@
27 public abstract void printer_set_users_denied (string printer, string[] users) throws IOError;27 public abstract void printer_set_users_denied (string printer, string[] users) throws IOError;
28 }28 }
2929
30 public class Utils : Object {30 [DBus (name = "org.pantheon.ParentalControls")]
31 public static Polkit.Permission? permission = null;31 public interface IParentalControls : Object {
32 public static string user_name;32 public abstract async void add_restriction_for_user (string input, bool clean) throws IOError;
3333 public abstract async void remove_restriction_for_user (string username) throws IOError;
34 public abstract async void end_app_authorization () throws IOError;
35 public abstract async bool get_user_daemon_active (string username) throws IOError;
36 public abstract async bool get_user_daemon_admin (string username) throws IOError;
37 public abstract async string[] get_user_daemon_block_urls (string username) throws IOError;
38 public abstract async string[] get_user_daemon_targets (string username) throws IOError;
39 public abstract async void lock_dock_icons_for_user (string username, bool lock) throws IOError;
40 public abstract async void set_user_daemon_active (string username, bool active) throws IOError;
41 public abstract async void set_user_daemon_admin (string username, bool admin) throws IOError;
42 public abstract async void set_user_daemon_block_urls (string username, string[] block_urls) throws IOError;
43 public abstract async void set_user_daemon_targets (string username, string[] targets) throws IOError;
44
45 public signal void app_authorize (string username, string path, string action_id);
46 public signal void launch (string[] args);
47 public signal void show_app_unavailable (string path);
48 public signal void show_timeout (int hours, int minutes);
49 public signal void user_config_changed (string username);
50 }
51
52 public class Utils {
53 public class DummyParentalControls : Object, IParentalControls {
54 public async void add_restriction_for_user (string input, bool clean) throws IOError {}
55 public async void remove_restriction_for_user (string username) throws IOError {}
56 public async void end_app_authorization () throws IOError {}
57 public async bool get_user_daemon_active (string username) throws IOError { return false; }
58 public async bool get_user_daemon_admin (string username) throws IOError { return false; }
59 public async string[] get_user_daemon_block_urls (string username) throws IOError { return {}; }
60 public async string[] get_user_daemon_targets (string username) throws IOError { return {}; }
61 public async void lock_dock_icons_for_user (string username, bool lock) throws IOError {}
62 public async void set_user_daemon_active (string username, bool active) throws IOError {}
63 public async void set_user_daemon_admin (string username, bool admin) throws IOError {}
64 public async void set_user_daemon_block_urls (string username, string[] block_urls) throws IOError {}
65 public async void set_user_daemon_targets (string username, string[] targets) throws IOError {}
66 }
67
68 private static Polkit.Permission? permission = null;
34 private static Act.UserManager? usermanager = null;69 private static Act.UserManager? usermanager = null;
70 private static IParentalControls? api = null;
71
72 public static IParentalControls? get_api () {
73 if (api != null) {
74 return api;
75 }
76
77 try {
78 api = Bus.get_proxy_sync (BusType.SYSTEM, Vars.PARENTAL_CONTROLS_IFACE, Vars.PARENTAL_CONTROLS_OBJECT_PATH);
79 } catch (Error e) {
80 critical ("%s, using dummy parental controls backend", e.message);
81 api = new DummyParentalControls ();
82 }
83
84 return api;
85 }
3586
36 public static Polkit.Permission? get_permission () {87 public static Polkit.Permission? get_permission () {
37 if (permission != null) {88 if (permission != null) {
@@ -39,9 +90,9 @@
39 }90 }
4091
41 try {92 try {
42 var user = (Polkit.UnixUser)Polkit.UnixUser.new_for_name (user_name);93 var user = (Polkit.UnixUser)Polkit.UnixUser.new_for_name (Environment.get_user_name ());
43 permission = new Polkit.Permission.sync (Vars.PARENTAL_CONTROLS_ACTION_ID,94 permission = new Polkit.Permission.sync (Vars.PARENTAL_CONTROLS_ACTION_ID,
44 Polkit.UnixProcess.new_for_owner (Posix.getpid (), 0, user.get_uid ()));95 Polkit.UnixProcess.new_for_owner (Posix.getpid (), 0, user.get_uid ()));
45 return permission;96 return permission;
46 } catch (Error e) {97 } catch (Error e) {
47 critical (e.message);98 critical (e.message);
@@ -56,29 +107,6 @@
56 return @"<span font_weight=\"bold\" size=\"large\">$escaped_name</span>\n$escaped_comment";107 return @"<span font_weight=\"bold\" size=\"large\">$escaped_name</span>\n$escaped_comment";
57 }108 }
58109
59 public static void call_cli (string[] args) {
60 string[] spawn_args = { "pkexec", "pantheon-parental-controls-cli" };
61 foreach (string arg in args) {
62 spawn_args += arg;
63 }
64
65 try {
66 Pid child_pid;
67 Process.spawn_async ("/",
68 spawn_args,
69 Environ.get (),
70 SpawnFlags.SEARCH_PATH | SpawnFlags.DO_NOT_REAP_CHILD,
71 null,
72 out child_pid);
73 ChildWatch.add (child_pid, (pid, status) => {
74 Process.close_pid (pid);
75 });
76
77 } catch (SpawnError e) {
78 warning ("%s\n", e.message);
79 }
80 }
81
82 public static unowned Act.UserManager? get_usermanager () {110 public static unowned Act.UserManager? get_usermanager () {
83 if (usermanager != null) {111 if (usermanager != null) {
84 return usermanager;112 return usermanager;
@@ -89,15 +117,24 @@
89 }117 }
90118
91 public static unowned Act.User? get_current_user () {119 public static unowned Act.User? get_current_user () {
92 return get_usermanager ().get_user (user_name);120 return get_usermanager ().get_user (Environment.get_user_name ());
121 }
122
123 public static string remove_comments (string str) {
124 string buffer = "";
125
126 foreach (string line in str.split ("\n")) {
127 if (!line.strip ().has_prefix ("#")) {
128 buffer += line;
129 buffer += "\n";
130 }
131 }
132
133 return buffer;
93 }134 }
94135
95 public static string build_daemon_conf_path (Act.User user) {136 public static string build_daemon_conf_path (Act.User user) {
96 return Path.build_filename (user.get_home_dir (), Vars.DAEMON_CONF_DIR);137 return Path.build_filename (user.get_home_dir (), Vars.DAEMON_CONF_DIR);
97 }138 }
98
99 public static void set_user_name (string _user_name) {
100 user_name = _user_name;
101 }
102 }139 }
103}140}
104141
=== modified file 'src/shared/Vars.vala'
--- src/shared/Vars.vala 2016-07-27 16:14:53 +0000
+++ src/shared/Vars.vala 2016-08-30 20:17:54 +0000
@@ -41,7 +41,5 @@
41 public const string PAM_CONF_START = "## PANTHEON_PARENTAL_CONTROLS_START";41 public const string PAM_CONF_START = "## PANTHEON_PARENTAL_CONTROLS_START";
42 public const string PAM_CONF_END = "## PANTHEON_PARENTAL_CONTROLS_END";42 public const string PAM_CONF_END = "## PANTHEON_PARENTAL_CONTROLS_END";
43 public const string PAM_CONF_REGEX = PAM_CONF_START + "|" + PAM_CONF_END;43 public const string PAM_CONF_REGEX = PAM_CONF_START + "|" + PAM_CONF_END;
44 public const string ALL_ID = "all";44 public const string PAM_TIME_CONF_PATH = "/etc/security/time.conf";
45 public const string WEEKDAYS_ID = "weekdays";
46 public const string WEEKENDS_ID = "weekends";
47}45}
48\ No newline at end of file46\ No newline at end of file

Subscribers

People subscribed via source and target branches

to all changes: