chiark / gitweb /
src/quotacheck.c: Fix typo
[elogind.git] / src / gnome-ask-password-agent.vala
1 /***
2   This file is part of systemd.
3
4   Copyright 2010 Lennart Poettering
5
6   systemd is free software; you can redistribute it and/or modify it
7   under the terms of the GNU General Public License as published by
8   the Free Software Foundation; either version 2 of the License, or
9   (at your option) any later version.
10
11   systemd is distributed in the hope that it will be useful, but
12   WITHOUT ANY WARRANTY; without even the implied warranty of
13   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14   General Public License for more details.
15
16   You should have received a copy of the GNU General Public License
17   along with systemd; If not, see <http://www.gnu.org/licenses/>.
18 ***/
19
20 using Gtk;
21 using GLib;
22 using DBus;
23 using Linux;
24 using Posix;
25 using Notify;
26
27 [CCode (cheader_filename = "time.h")]
28 extern int clock_gettime(int id, out timespec ts);
29
30 public class PasswordDialog : Dialog {
31
32         public Entry entry;
33
34         public PasswordDialog(string message, string icon) {
35                 set_title("System Password");
36                 set_has_separator(false);
37                 set_border_width(8);
38                 set_default_response(ResponseType.OK);
39                 set_icon_name(icon);
40
41 #if LIBNOTIFY07
42                 add_button(Stock.CANCEL, ResponseType.CANCEL);
43                 add_button(Stock.OK, ResponseType.OK);
44 #else
45                 add_button(STOCK_CANCEL, ResponseType.CANCEL);
46                 add_button(STOCK_OK, ResponseType.OK);
47 #endif
48
49                 Container content = (Container) get_content_area();
50
51                 Box hbox = new HBox(false, 16);
52                 hbox.set_border_width(8);
53                 content.add(hbox);
54
55                 Image image = new Image.from_icon_name(icon, IconSize.DIALOG);
56                 hbox.pack_start(image, false, false);
57
58                 Box vbox = new VBox(false, 8);
59                 hbox.pack_start(vbox, true, true);
60
61                 Label label = new Label(message);
62                 vbox.pack_start(label, false, false);
63
64                 entry = new Entry();
65                 entry.set_visibility(false);
66                 entry.set_activates_default(true);
67                 vbox.pack_start(entry, false, false);
68
69                 entry.activate.connect(on_entry_activated);
70
71                 show_all();
72         }
73
74         public void on_entry_activated() {
75                 response(ResponseType.OK);
76         }
77 }
78
79 public class MyStatusIcon : StatusIcon {
80
81         File directory;
82         File current;
83         FileMonitor file_monitor;
84
85         string message;
86         string icon;
87         string socket;
88
89         PasswordDialog password_dialog;
90
91         public MyStatusIcon() throws GLib.Error {
92                 GLib.Object(icon_name : "dialog-password");
93                 set_title("System Password");
94
95                 directory = File.new_for_path("/dev/.systemd/ask-password/");
96                 file_monitor = directory.monitor_directory(0);
97                 file_monitor.changed.connect(file_monitor_changed);
98
99                 current = null;
100                 look_for_password();
101
102                 activate.connect(status_icon_activate);
103         }
104
105         void file_monitor_changed(GLib.File file, GLib.File? other_file, GLib.FileMonitorEvent event_type) throws GLib.Error {
106
107                 if (!file.get_basename().has_prefix("ask."))
108                         return;
109
110                 if (event_type == FileMonitorEvent.CREATED ||
111                     event_type == FileMonitorEvent.DELETED)
112                         look_for_password();
113         }
114
115         void look_for_password() throws GLib.Error {
116
117                 if (current != null) {
118                         if (!current.query_exists()) {
119                                 current = null;
120                                 if (password_dialog != null)
121                                         password_dialog.response(ResponseType.REJECT);
122                         }
123                 }
124
125                 if (current == null) {
126                         FileEnumerator enumerator = directory.enumerate_children("standard::name", FileQueryInfoFlags.NOFOLLOW_SYMLINKS);
127
128                         FileInfo i;
129                         while ((i = enumerator.next_file()) != null) {
130                                 if (!i.get_name().has_prefix("ask."))
131                                         continue;
132
133                                 current = directory.get_child(i.get_name());
134
135                                 if (load_password())
136                                         break;
137
138                                 current = null;
139                         }
140                 }
141
142                 if (current == null)
143                         set_visible(false);
144
145         }
146
147         bool load_password() throws GLib.Error {
148
149                 KeyFile key_file = new KeyFile();
150
151                 try {
152                         timespec ts;
153
154                         key_file.load_from_file(current.get_path(), KeyFileFlags.NONE);
155
156                         string not_after_as_string = key_file.get_string("Ask", "NotAfter");
157
158                         clock_gettime(1, out ts);
159                         uint64 now = (ts.tv_sec * 1000000) + (ts.tv_nsec / 1000);
160
161                         uint64 not_after;
162                         if (not_after_as_string.scanf("%llu", out not_after) != 1)
163                                 return false;
164
165                         if (not_after < now)
166                                 return false;
167
168                         socket = key_file.get_string("Ask", "Socket");
169                 } catch (GLib.Error e) {
170                         return false;
171                 }
172
173                 try {
174                         message = key_file.get_string("Ask", "Message").compress();
175                 } catch (GLib.Error e) {
176                         message = "Please Enter System Password!";
177                 }
178                 set_tooltip_text(message);
179
180                 try {
181                         icon = key_file.get_string("Ask", "Icon");
182                 } catch (GLib.Error e) {
183                         icon = "dialog-password";
184                 }
185                 set_from_icon_name(icon);
186
187                 set_visible(true);
188
189 #if LIBNOTIFY07
190                 Notification n = new Notification(title, message, icon);
191 #else
192                 Notification n = new Notification(title, message, icon, null);
193                 n.attach_to_status_icon(this);
194 #endif
195                 n.set_timeout(5000);
196                 n.show();
197
198                 return true;
199         }
200
201         void status_icon_activate() throws GLib.Error {
202
203                 if (current == null)
204                         return;
205
206                 if (password_dialog != null) {
207                         password_dialog.present();
208                         return;
209                 }
210
211                 password_dialog = new PasswordDialog(message, icon);
212
213                 int result = password_dialog.run();
214                 string password = password_dialog.entry.get_text();
215
216                 password_dialog.destroy();
217                 password_dialog = null;
218
219                 if (result == ResponseType.REJECT ||
220                     result == ResponseType.DELETE_EVENT)
221                         return;
222
223                 int to_process;
224
225                 Process.spawn_async_with_pipes(
226                                 null,
227                                 { "/usr/bin/pkexec", "/lib/systemd/systemd-reply-password", result == ResponseType.OK ? "1" : "0", socket },
228                                 null,
229                                 0,
230                                 null,
231                                 null,
232                                 out to_process,
233                                 null,
234                                 null);
235
236                 OutputStream stream = new UnixOutputStream(to_process, true);
237
238 #if LIBNOTIFY07
239                 stream.write(password.data, null);
240 #else
241                 stream.write(password, password.length, null);
242 #endif
243         }
244 }
245
246 static const OptionEntry entries[] = {
247         { null }
248 };
249
250 void show_error(string e) {
251         var m = new MessageDialog(null, 0, MessageType.ERROR, ButtonsType.CLOSE, "%s", e);
252         m.run();
253         m.destroy();
254 }
255
256 int main(string[] args) {
257         try {
258                 Gtk.init_with_args(ref args, "[OPTION...]", entries, "systemd-ask-password-agent");
259                 Notify.init("Password Agent");
260
261                 MyStatusIcon i = new MyStatusIcon();
262                 Gtk.main();
263
264         } catch (DBus.Error e) {
265                 show_error(e.message);
266         } catch (GLib.Error e) {
267                 show_error(e.message);
268         }
269
270         return 0;
271 }