1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
2  *
3  * Copyright 2012  Red Hat, Inc,
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program 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
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, see <http://www.gnu.org/licenses/>.
17  *
18  * Written by: Ondrej Holy <oholy@redhat.com>
19  */
20 
21 #include "config.h"
22 
23 #include <unistd.h>
24 #include <stdlib.h>
25 #include <sys/types.h>
26 #include <sys/wait.h>
27 
28 #include <glib.h>
29 #include <glib/gi18n.h>
30 #include <gtk/gtk.h>
31 #include <act/act.h>
32 
33 #include "cc-login-history-dialog.h"
34 #include "cc-user-accounts-resources.h"
35 #include "cc-util.h"
36 #include "user-utils.h"
37 
38 struct _CcLoginHistoryDialog
39 {
40         GtkDialog     parent_instance;
41 
42         GtkHeaderBar *header_bar;
43         GtkListBox   *history_box;
44         GtkButton    *next_button;
45         GtkButton    *previous_button;
46 
47         GDateTime    *week;
48         GDateTime    *current_week;
49 
50         ActUser      *user;
51 };
52 
53 G_DEFINE_TYPE (CcLoginHistoryDialog, cc_login_history_dialog, GTK_TYPE_DIALOG)
54 
55 typedef struct {
56 	gint64 login_time;
57 	gint64 logout_time;
58 	const gchar *type;
59 } CcLoginHistory;
60 
61 static void
show_week_label(CcLoginHistoryDialog * self)62 show_week_label (CcLoginHistoryDialog *self)
63 {
64         g_autofree gchar *label = NULL;
65         GTimeSpan span;
66 
67         span = g_date_time_difference (self->current_week, self->week);
68         if (span == 0) {
69                 label = g_strdup (_("This Week"));
70         }
71         else if (span == G_TIME_SPAN_DAY * 7) {
72                 label = g_strdup (_("Last Week"));
73         }
74         else {
75                 g_autofree gchar *from = NULL;
76                 g_autofree gchar *to = NULL;
77                 g_autoptr(GDateTime) date = NULL;
78 
79                 date = g_date_time_add_days (self->week, 6);
80                 /* Translators: This is a date format string in the style of "Feb 18",
81                    shown as the first day of a week on login history dialog. */
82                 from = g_date_time_format (self->week, C_("login history week label","%b %e"));
83                 if (g_date_time_get_year (self->week) == g_date_time_get_year (self->current_week)) {
84                         /* Translators: This is a date format string in the style of "Feb 24",
85                            shown as the last day of a week on login history dialog. */
86                         to = g_date_time_format (date, C_("login history week label","%b %e"));
87                 }
88                 else {
89                         /* Translators: This is a date format string in the style of "Feb 24, 2013",
90                            shown as the last day of a week on login history dialog. */
91                         to = g_date_time_format (date, C_("login history week label","%b %e, %Y"));
92                 }
93 
94                 /* Translators: This indicates a week label on a login history.
95                    The first %s is the first day of a week, and the second %s the last day. */
96                 label = g_strdup_printf(C_("login history week label", "%s — %s"), from, to);
97         }
98 
99         gtk_header_bar_set_subtitle (self->header_bar, label);
100 }
101 
102 static void
clear_history(CcLoginHistoryDialog * self)103 clear_history (CcLoginHistoryDialog *self)
104 {
105         g_autoptr(GList) list = NULL;
106         GList *it;
107 
108         list = gtk_container_get_children (GTK_CONTAINER (self->history_box));
109         for (it = list; it != NULL; it = it->next) {
110                 gtk_container_remove (GTK_CONTAINER (self->history_box), GTK_WIDGET (it->data));
111         }
112 }
113 
114 static GArray *
get_login_history(ActUser * user)115 get_login_history (ActUser *user)
116 {
117 	GArray *login_history;
118 	GVariantIter *iter, *iter2;
119 	GVariant *variant;
120 	const GVariant *value;
121 	const gchar *key;
122 	CcLoginHistory history;
123 
124 	login_history = NULL;
125 	value = act_user_get_login_history (user);
126 	g_variant_get ((GVariant *) value, "a(xxa{sv})", &iter);
127 	while (g_variant_iter_loop (iter, "(xxa{sv})", &history.login_time, &history.logout_time, &iter2)) {
128 		while (g_variant_iter_loop (iter2, "{&sv}", &key, &variant)) {
129 			if (g_strcmp0 (key, "type") == 0) {
130 				history.type = g_variant_get_string (variant, NULL);
131 			}
132 		}
133 
134 		if (login_history == NULL) {
135 			login_history = g_array_new (FALSE, TRUE, sizeof (CcLoginHistory));
136 		}
137 
138 		g_array_append_val (login_history, history);
139 	}
140 
141 	return login_history;
142 }
143 
144 static void
set_sensitivity(CcLoginHistoryDialog * self)145 set_sensitivity (CcLoginHistoryDialog *self)
146 {
147         g_autoptr(GArray) login_history = NULL;
148         CcLoginHistory history;
149         gboolean sensitive = FALSE;
150 
151         login_history = get_login_history (self->user);
152         if (login_history != NULL) {
153                 history = g_array_index (login_history, CcLoginHistory, 0);
154                 sensitive = g_date_time_to_unix (self->week) > history.login_time;
155         }
156         gtk_widget_set_sensitive (GTK_WIDGET (self->previous_button), sensitive);
157 
158         sensitive = (g_date_time_compare (self->current_week, self->week) == 1);
159         gtk_widget_set_sensitive (GTK_WIDGET (self->next_button), sensitive);
160 }
161 
162 static void
add_record(CcLoginHistoryDialog * self,GDateTime * datetime,gchar * record_string,gint line)163 add_record (CcLoginHistoryDialog *self, GDateTime *datetime, gchar *record_string, gint line)
164 {
165         g_autofree gchar *date = NULL;
166         g_autofree gchar *time = NULL;
167         g_autofree gchar *str = NULL;
168         GtkWidget *label, *row;
169 
170         date = cc_util_get_smart_date (datetime);
171         /* Translators: This is a time format string in the style of "22:58".
172            It indicates a login time which follows a date. */
173         time = g_date_time_format (datetime, C_("login date-time", "%k:%M"));
174         /* Translators: This indicates a login date-time.
175            The first %s is a date, and the second %s a time. */
176         str = g_strdup_printf(C_("login date-time", "%s, %s"), date, time);
177 
178         row = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12);
179         gtk_widget_show (row);
180         gtk_box_set_homogeneous (GTK_BOX (row), TRUE);
181         gtk_container_set_border_width (GTK_CONTAINER (row), 6);
182 
183         label = gtk_label_new (record_string);
184         gtk_widget_show (label);
185         gtk_widget_set_halign (label, GTK_ALIGN_START);
186         gtk_box_pack_start (GTK_BOX (row), label, TRUE, TRUE, 0);
187 
188         label = gtk_label_new (str);
189         gtk_widget_show (label);
190         gtk_widget_set_halign (label, GTK_ALIGN_START);
191         gtk_box_pack_start (GTK_BOX (row), label, TRUE, TRUE, 0);
192 
193         gtk_list_box_insert (self->history_box, row, line);
194 }
195 
196 static void
show_week(CcLoginHistoryDialog * self)197 show_week (CcLoginHistoryDialog *self)
198 {
199         g_autoptr(GArray) login_history = NULL;
200         g_autoptr(GDateTime) datetime = NULL;
201         g_autoptr(GDateTime) temp = NULL;
202         gint64 from, to;
203         gint i, line;
204         CcLoginHistory history;
205 
206         show_week_label (self);
207         clear_history (self);
208         set_sensitivity (self);
209 
210         login_history = get_login_history (self->user);
211         if (login_history == NULL) {
212                 return;
213         }
214 
215         /* Find first record for week */
216         from = g_date_time_to_unix (self->week);
217         temp = g_date_time_add_weeks (self->week, 1);
218         to = g_date_time_to_unix (temp);
219         for (i = login_history->len - 1; i >= 0; i--) {
220                 history = g_array_index (login_history, CcLoginHistory, i);
221                 if (history.login_time < to) {
222                         break;
223                 }
224         }
225 
226         /* Add new session records */
227         line = 0;
228         for (;i >= 0; i--) {
229                 history = g_array_index (login_history, CcLoginHistory, i);
230 
231                 /* Display only x-session and tty records */
232                 if (!g_str_has_prefix (history.type, ":") &&
233                     !g_str_has_prefix (history.type, "tty")) {
234                         continue;
235                 }
236 
237                 if (history.logout_time > 0 && history.logout_time < from) {
238                         break;
239                 }
240 
241                 if (history.logout_time > 0 && history.logout_time < to) {
242                         datetime = g_date_time_new_from_unix_local (history.logout_time);
243                         add_record (self, datetime, _("Session Ended"), line);
244                         line++;
245                 }
246 
247                 if (history.login_time >= from) {
248                         datetime = g_date_time_new_from_unix_local (history.login_time);
249                         add_record (self, datetime, _("Session Started"), line);
250                         line++;
251                 }
252         }
253 }
254 
255 static void
previous_button_clicked_cb(CcLoginHistoryDialog * self)256 previous_button_clicked_cb (CcLoginHistoryDialog *self)
257 {
258         g_autoptr(GDateTime) temp = NULL;
259 
260         temp = self->week;
261         self->week = g_date_time_add_weeks (self->week, -1);
262 
263         show_week (self);
264 }
265 
266 static void
next_button_clicked_cb(CcLoginHistoryDialog * self)267 next_button_clicked_cb (CcLoginHistoryDialog *self)
268 {
269         g_autoptr(GDateTime) temp = NULL;
270 
271         temp = self->week;
272         self->week = g_date_time_add_weeks (self->week, 1);
273 
274         show_week (self);
275 }
276 
277 static void
cc_login_history_dialog_dispose(GObject * object)278 cc_login_history_dialog_dispose (GObject *object)
279 {
280         CcLoginHistoryDialog *self = CC_LOGIN_HISTORY_DIALOG (object);
281 
282         g_clear_object (&self->user);
283         g_clear_pointer (&self->week, g_date_time_unref);
284         g_clear_pointer (&self->current_week, g_date_time_unref);
285 
286         G_OBJECT_CLASS (cc_login_history_dialog_parent_class)->dispose (object);
287 }
288 
289 void
cc_login_history_dialog_class_init(CcLoginHistoryDialogClass * klass)290 cc_login_history_dialog_class_init (CcLoginHistoryDialogClass *klass)
291 {
292         GObjectClass   *object_class = G_OBJECT_CLASS (klass);
293         GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
294 
295         object_class->dispose = cc_login_history_dialog_dispose;
296 
297         gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/control-center/user-accounts/cc-login-history-dialog.ui");
298 
299         gtk_widget_class_bind_template_child (widget_class, CcLoginHistoryDialog, header_bar);
300         gtk_widget_class_bind_template_child (widget_class, CcLoginHistoryDialog, history_box);
301         gtk_widget_class_bind_template_child (widget_class, CcLoginHistoryDialog, next_button);
302         gtk_widget_class_bind_template_child (widget_class, CcLoginHistoryDialog, previous_button);
303 
304         gtk_widget_class_bind_template_callback (widget_class, next_button_clicked_cb);
305         gtk_widget_class_bind_template_callback (widget_class, previous_button_clicked_cb);
306 }
307 
308 void
cc_login_history_dialog_init(CcLoginHistoryDialog * self)309 cc_login_history_dialog_init (CcLoginHistoryDialog *self)
310 {
311         g_resources_register (cc_user_accounts_get_resource ());
312 
313         gtk_widget_init_template (GTK_WIDGET (self));
314 }
315 
316 CcLoginHistoryDialog *
cc_login_history_dialog_new(ActUser * user)317 cc_login_history_dialog_new (ActUser *user)
318 {
319         CcLoginHistoryDialog *self;
320         g_autoptr(GDateTime) temp = NULL;
321         g_autoptr(GDateTime) local = NULL;
322         g_autofree gchar *title = NULL;
323 
324         g_return_val_if_fail (ACT_IS_USER (user), NULL);
325 
326         self = g_object_new (CC_TYPE_LOGIN_HISTORY_DIALOG,
327                              "use-header-bar", 1,
328                              NULL);
329 
330         self->user = g_object_ref (user);
331 
332         /* Set the first day of this week */
333         local = g_date_time_new_now_local ();
334         temp = g_date_time_new_local (g_date_time_get_year (local),
335                                       g_date_time_get_month (local),
336                                       g_date_time_get_day_of_month (local),
337                                       0, 0, 0);
338         self->week = g_date_time_add_days (temp, 1 - g_date_time_get_day_of_week (temp));
339         self->current_week = g_date_time_ref (self->week);
340 
341         /* Translators: This is the title of the "Account Activity" dialog.
342            The %s is the user real name. */
343         title = g_strdup_printf (_("%s — Account Activity"),
344                                  act_user_get_real_name (self->user));
345         gtk_header_bar_set_title (self->header_bar, title);
346 
347         show_week (self);
348 
349         return self;
350 }
351