1 /* ****************************************************************************
2 
3  * eID Middleware Project.
4  * Copyright (C) 2014 FedICT.
5  *
6  * This is free software; you can redistribute it and/or modify it
7  * under the terms of the GNU Lesser General Public License version
8  * 3.0 as published by the Free Software Foundation.
9  *
10  * This software 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  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this software; if not, see
17  * http://www.gnu.org/licenses/.
18 
19 **************************************************************************** */
20 
21 #define _GNU_SOURCE
22 
23 #include <config.h>
24 
25 #define _GNU_SOURCE
26 
27 #include <errno.h>
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <unistd.h>
32 
33 #include <sys/stat.h>
34 #include <sys/types.h>
35 #include <sys/wait.h>
36 #include <sys/utsname.h>
37 
38 #include <gtk/gtk.h>
39 #include <gdk/gdkkeysyms.h>
40 
41 #include "about_glade.h"
42 #include "now.h"
43 
44 #include "gettext.h"
45 
46 #if GTK_CHECK_VERSION(3, 96, 0)
47 #define GtkClipboard GdkClipboard
48 #define gtk_clipboard_get(a) gdk_display_get_clipboard(gdk_display_get_default())
49 #define gtk_clipboard_set_text(c, t, l) gdk_clipboard_set_text(c, t)
50 #define gtk_init(a, b) gtk_init()
51 
gtk_widget_show_all_ll(GtkWidget * widget,gpointer data G_GNUC_UNUSED)52 void gtk_widget_show_all_ll(GtkWidget *widget, gpointer data G_GNUC_UNUSED) {
53 	if(GTK_IS_CONTAINER(widget)) {
54 		gtk_container_foreach(GTK_CONTAINER(widget), gtk_widget_show_all_ll, NULL);
55 	}
56 	gtk_widget_show(widget);
57 }
58 
gtk_widget_show_all(GtkWidget * widget)59 void gtk_widget_show_all(GtkWidget *widget) {
60 	gtk_widget_show_all_ll(widget, NULL);
61 }
62 #endif
63 
64 #ifndef _
65 #define _(s) gettext(s)
66 #endif
67 
68 static enum _bits {
69 	BITS_UNKNOWN,
70 	BITS_32,
71 	BITS_64,
72 	BITS_FOREIGN,
73 } bitness;
74 
check_pcsc(GtkWidget * top G_GNUC_UNUSED,GtkListStore * data)75 void check_pcsc(GtkWidget* top G_GNUC_UNUSED, GtkListStore* data) {
76 	FILE* f = popen("pidof pcscd", "r");
77 	GtkTreeIter iter;
78 	char pid[6];
79 	char* tmp;
80 
81 	gtk_list_store_append(data, &iter);
82 	if(!f) {
83 		gtk_list_store_set(data, &iter, 0, _("PCSC daemon status"), 1, _("(check failed)"), -1);
84 		return;
85 	}
86 	if(feof(f)) {
87 		gtk_list_store_set(data, &iter, 0, _("PCSC daemon status"), 1, _("(not running)"), -1);
88 		goto exit;
89 	}
90 	pid[5]='\0';
91 	if(fgets(pid, 5, f)==NULL) {
92 		gtk_list_store_set(data, &iter, 0, _("PCSC daemon status"), 1, _("(not running)"), -1);
93 		goto exit;
94 	}
95 	if((tmp = strchr(pid, '\n'))) {
96 		*tmp = '\0';
97 	}
98 	if(strlen(pid)==0) {
99 		gtk_list_store_set(data, &iter, 0, _("PCSC daemon status"), 1, _("(not running)"), -1);
100 		goto exit;
101 	}
102 	tmp = g_strdup_printf(_("running; pid: %s"), pid);
103 	gtk_list_store_set(data, &iter, 0, _("PCSC daemon status"), 1, tmp, -1);
104 	g_free(tmp);
105 exit:
106 	pclose(f);
107 }
108 
do_viewer(GtkWidget * top G_GNUC_UNUSED,GtkListStore * data)109 void do_viewer(GtkWidget* top G_GNUC_UNUSED, GtkListStore* data) {
110 	FILE* f = popen("which eid-viewer", "r");
111 	GtkTreeIter iter;
112 	char tmp[PATH_MAX];
113 	char* loc;
114 
115 	gtk_list_store_append(data, &iter);
116 	if(!f) {
117 		gtk_list_store_set(data, &iter, 0, _("eID Viewer location"), 1, _("(not found)"), -1);
118 		return;
119 	}
120 	tmp[PATH_MAX-1]='\0';
121 	if(fgets(tmp, PATH_MAX, f) == NULL) {
122 		gtk_list_store_set(data, &iter, 0, _("eID Viewer location"), 1, _("(not found)"), -1);
123 		goto exit;
124 	}
125 	if((loc = strrchr(tmp, '\n')) != NULL) {
126 		*loc = '\0';
127 	}
128 	if(strlen(tmp) == 0) {
129 		gtk_list_store_set(data, &iter, 0, _("eID Viewer location"), 1, _("(not found)"), -1);
130 		goto exit;
131 	}
132 	gtk_list_store_set(data, &iter, 0, _("eID Viewer location"), 1, tmp, -1);
133 exit:
134 	pclose(f);
135 }
136 
do_files(GtkWidget * top,GtkListStore * data)137 void do_files(GtkWidget* top, GtkListStore* data) {
138 	GtkWidget* dialog;
139 	struct stat st;
140 	struct {
141 		enum _bits bitness;
142 		char* loc;
143 	} locs[] = {
144 		{ BITS_32, "/usr/lib/libbeidpkcs11.so.0" },
145 		{ BITS_32, "/usr/lib/i386-linux-gnu/libbeidpkcs11.so.0" },
146 		{ BITS_64, "/usr/lib64/libbeidpkcs11.so.0" },
147 		{ BITS_64, "/usr/lib/x86_64-linux-gnu/libbeidpkcs11.so.0" },
148 		{ bitness, LIBDIR "/libbeidpkcs11.so.0" },
149 	};
150 	unsigned int i;
151 	gboolean found32 = FALSE;
152 	gboolean found64 = FALSE;
153 	gboolean foundforeign = FALSE;
154 	GtkTreeIter iter;
155 
156 	for(i=0;i<(sizeof(locs) / sizeof(locs[0]));i++) {
157 		if(stat(locs[i].loc, &st) < 0) {
158 			switch(errno) {
159 			case ENOENT:
160 				/* file does not exist */
161 				break;
162 			default:
163 				dialog = gtk_message_dialog_new(GTK_WINDOW(top), GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, "stat: %s", strerror(errno));
164 				gtk_dialog_run(GTK_DIALOG(dialog));
165 				gtk_widget_destroy(dialog);
166 				break;
167 			}
168 		} else {
169 			gchar* str;
170 			switch(locs[i].bitness) {
171 				case BITS_32:
172 					if(found32) continue;
173 					str = _("32-bit PKCS#11 location");
174 					found32 = TRUE;
175 					break;
176 				case BITS_64:
177 					if(found64) continue;
178 					str = _("64-bit PKCS#11 location");
179 					found64 = TRUE;
180 					break;
181 				default:
182 					str = _("PKCS#11 location");
183 					foundforeign = TRUE;
184 					break;
185 			}
186 
187 			gtk_list_store_append(data, &iter);
188 			gtk_list_store_set(data, &iter, 0, str, 1, locs[i].loc, -1);
189 		}
190 	}
191 	switch(bitness) {
192 	case BITS_32:
193 	case BITS_64:
194 		if(!found32) {
195 			gtk_list_store_append(data, &iter);
196 			gtk_list_store_set(data, &iter, 0, _("32-bit PKCS#11 location"), 1, _("(not found)"), -1);
197 		}
198 		if(!found64) {
199 			gtk_list_store_append(data, &iter);
200 			gtk_list_store_set(data, &iter, 0, _("64-bit PKCS#11 location"), 1, _("(not found)"), -1);
201 		}
202 		break;
203 	default:
204 		if(!foundforeign) {
205 			gtk_list_store_append(data, &iter);
206 			gtk_list_store_set(data, &iter, 0, _("PKCS#11 location"), 1, _("(not found)"), -1);
207 		}
208 		break;
209 	}
210 }
211 
do_gtk(GtkWidget * top,GtkListStore * data)212 void do_gtk(GtkWidget* top, GtkListStore* data) {
213 	guint maj = gtk_get_major_version();
214 	guint min = gtk_get_minor_version();
215 	guint mic = gtk_get_micro_version();
216 	GtkTreeIter iter;
217 
218 	gtk_list_store_append(data, &iter);
219 	gchar *version = g_strdup_printf("%d.%d.%d", maj, min, mic);
220 	gtk_list_store_set(data, &iter, 0, _("GTK library version"), 1, version, -1);
221 }
222 
copyline_simple(GtkTreeModel * model,GtkTreePath * path G_GNUC_UNUSED,GtkTreeIter * iter,gchar ** text)223 void copyline_simple(GtkTreeModel* model, GtkTreePath *path G_GNUC_UNUSED, GtkTreeIter *iter, gchar** text) {
224 	gchar *old = *text;
225 	gchar *value;
226 
227 	gtk_tree_model_get(model, iter, 1, &value, -1);
228 	if(*text == NULL) {
229 		*text = g_strdup_printf("%s", value);
230 	} else {
231 		// should not happen, but better safe than sorry...
232 		*text = g_strdup_printf("%s\n%s", old, value);
233 		g_free(old);
234 	}
235 }
236 
copyline_detail(GtkTreeModel * model,GtkTreePath * path G_GNUC_UNUSED,GtkTreeIter * iter,gchar ** text)237 void copyline_detail(GtkTreeModel* model, GtkTreePath *path G_GNUC_UNUSED, GtkTreeIter *iter, gchar** text) {
238 	gchar *old = *text;
239 	gchar *name, *value;
240 
241 	gtk_tree_model_get(model, iter, 0, &name, 1, &value, -1);
242 	if(*text == NULL) {
243 		*text = g_strdup_printf("%s: %s", name, value);
244 	} else {
245 		*text = g_strdup_printf("%s\n%s: %s", old, name, value);
246 		g_free(old);
247 	}
248 }
249 
copy2clip(GtkTreeView * tv)250 void copy2clip(GtkTreeView* tv) {
251 	GtkClipboard* clip = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD);
252 	GtkTreeSelection* sel = gtk_tree_view_get_selection(tv);
253 	GtkTreeSelectionForeachFunc copyline = (GtkTreeSelectionForeachFunc)copyline_detail;
254 	gchar* text = NULL;
255 	gint rowcount = gtk_tree_selection_count_selected_rows(sel);
256 
257 	if(rowcount == 1) {
258 		copyline = (GtkTreeSelectionForeachFunc)copyline_simple;
259 	}
260 
261 	gtk_tree_selection_selected_foreach(sel, copyline, &text);
262 	if(!text) return;
263 	gtk_clipboard_set_text(clip, text, strlen(text));
264 }
265 
copy2prim(GtkTreeSelection * sel,gpointer user_data G_GNUC_UNUSED)266 void copy2prim(GtkTreeSelection* sel, gpointer user_data G_GNUC_UNUSED) {
267 	GtkClipboard* clip = gtk_clipboard_get(GDK_SELECTION_PRIMARY);
268 	gchar* text = NULL;
269 	gint rowcount = gtk_tree_selection_count_selected_rows(sel);
270 
271 	if(rowcount != 1) {
272 		return;
273 	}
274 	gtk_tree_selection_selected_foreach(sel, (GtkTreeSelectionForeachFunc)copyline_simple, &text);
275 	gtk_clipboard_set_text(clip, text, strlen(text));
276 }
277 
do_uname(GtkWidget * top,GtkListStore * data)278 void do_uname(GtkWidget* top, GtkListStore* data) {
279 	GtkTreeIter iter;
280 	struct utsname undat;
281 	char* values;
282 
283 	if(uname(&undat) < 0) {
284 		GtkWidget* dialog = gtk_message_dialog_new(GTK_WINDOW(top), GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, "uname: %s", strerror(errno));
285 		gtk_dialog_run(GTK_DIALOG(dialog));
286 		gtk_widget_destroy(dialog);
287 	}
288 
289 	gtk_list_store_append(data, &iter);
290 	if(!strcmp(undat.machine, "x86_64")) {
291 		bitness = BITS_64;
292 		gtk_list_store_set(data, &iter, 0, _("System architecture"), 1, _("64-bit PC"), -1);
293 	} else if(undat.machine[0] == 'i' && undat.machine[2] == '8' && undat.machine[3] == '6') {
294 		bitness = BITS_32;
295 		gtk_list_store_set(data, &iter, 0, _("System architecture"), 1, _("32-bit PC"), -1);
296 	} else {
297 		bitness = BITS_FOREIGN;
298 		if(!strncmp(undat.machine, "arm", 3)) {
299 			values = strdup(undat.machine);
300 		} else {
301 			asprintf(&values, _("Unknown (%s)"), undat.machine);
302 		}
303 		gtk_list_store_set(data, &iter, 0, _("System architecture"), 1, values, -1);
304 		free(values);
305 	}
306 
307 	gtk_list_store_append(data, &iter);
308 	asprintf(&values, "%s %s %s %s %s", undat.sysname, undat.nodename, undat.release, undat.version, undat.machine);
309 	gtk_list_store_set(data, &iter, 0, "uname", 1, values, -1);
310 	free(values);
311 }
312 
get_lsb_info(char opt)313 char* get_lsb_info(char opt) {
314 	char cmd[] = "lsb_release -. -s";
315 	char *rv, *loc;
316 	FILE *f;
317 
318 	*(strchr(cmd, '.')) = opt;
319 	f=popen(cmd, "r");
320 	if(!f) {
321 		return strdup(_("(unknown)"));
322 	}
323 	rv = malloc(80);
324 	rv[79]='\0';
325 	rv[0]='\0';
326 	if(fgets(rv, 79, f) == NULL) {
327 		free(rv);
328 		rv = strdup(_("(unknown)"));
329 		goto exit;
330 	}
331 	if(strlen(rv) == 0) {
332 		free(rv);
333 		rv = strdup(_("(unknown)"));
334 		goto exit;
335 	}
336 	if((loc = strrchr(rv, '\n')) != NULL) {
337 		*loc = '\0';
338 	}
339 exit:
340 	pclose(f);
341 	return rv;
342 }
343 
do_distro(GtkWidget * top G_GNUC_UNUSED,GtkListStore * data)344 void do_distro(GtkWidget* top G_GNUC_UNUSED, GtkListStore* data) {
345 	GtkTreeIter iter;
346 	char *dat;
347 
348 	dat = get_lsb_info('i');
349 	gtk_list_store_append(data, &iter);
350 	gtk_list_store_set(data, &iter, 0, _("Distribution"), 1, dat, -1);
351 	free(dat);
352 
353 	dat = get_lsb_info('r');
354 	gtk_list_store_append(data, &iter);
355 	gtk_list_store_set(data, &iter, 0, _("Distribution version"), 1, dat, -1);
356 	free(dat);
357 
358 	dat = get_lsb_info('c');
359 	gtk_list_store_append(data, &iter);
360 	gtk_list_store_set(data, &iter, 0, _("Distribution codename"), 1, dat, -1);
361 	free(dat);
362 }
363 
main(int argc,char ** argv)364 int main(int argc, char** argv) {
365 	GtkBuilder* builder;
366 	GtkWidget *window, *treeview, *button;
367 	GtkCellRenderer *renderer;
368 	GtkTreeViewColumn* col;
369 	GtkTreeIter iter;
370 	GtkListStore *store;
371 	GtkAccelGroup *group;
372 	GtkTreeSelection* sel;
373 	gchar *tmp, *loc;
374 	GError *err = NULL;
375 
376 	bindtextdomain("about-eid-mw", DATAROOTDIR "/locale");
377 	textdomain("about-eid-mw");
378 
379 	gtk_init(&argc, &argv);
380 
381 	builder = gtk_builder_new();
382 	if(!gtk_builder_add_from_string(builder, ABOUT_GLADE_STRING, strlen(ABOUT_GLADE_STRING), &err)) {
383 		g_critical("could not load builder: %s", err->message);
384 		exit(EXIT_FAILURE);
385 	}
386 	window = GTK_WIDGET(gtk_builder_get_object(builder, "window"));
387 	group = gtk_accel_group_new();
388 	gtk_window_add_accel_group(GTK_WINDOW(window), group);
389 
390 	treeview = GTK_WIDGET(gtk_builder_get_object(builder, "treeview"));
391 
392 	store = GTK_LIST_STORE(gtk_builder_get_object(builder, "infodata"));
393 
394 	tmp = g_strdup(PACKAGE_VERSION);
395 	loc = strchr(tmp, '-');
396 	*loc = '\0';
397 	gtk_list_store_append(store, &iter);
398 	gtk_list_store_set(store, &iter, 0, _("Middleware version"), 1, tmp, -1);
399 	gtk_list_store_append(store, &iter);
400 	gtk_list_store_set(store, &iter, 0, _("Middleware build ID"), 1, loc+1, -1);
401 
402 	gtk_list_store_append(store, &iter);
403 	gtk_list_store_set(store, &iter, 0, _("Middleware build date"), 1, EID_NOW_STRING, -1);
404 
405 	check_pcsc(window, store);
406 	do_viewer(window, store);
407 	do_distro(window, store);
408 	do_uname(window, store);
409 	do_files(window, store);
410 	do_gtk(window, store);
411 
412 	renderer = gtk_cell_renderer_text_new();
413 	col = gtk_tree_view_column_new_with_attributes(_("Item"), renderer, "text", 0, NULL);
414 	gtk_tree_view_append_column(GTK_TREE_VIEW(treeview), col);
415 	col = gtk_tree_view_column_new_with_attributes(_("Value"), renderer, "text", 1, NULL);
416 	gtk_tree_view_append_column(GTK_TREE_VIEW(treeview), col);
417 
418 	sel = GTK_TREE_SELECTION(gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview)));
419 	gtk_tree_selection_set_mode(sel, GTK_SELECTION_MULTIPLE);
420 	g_signal_connect(G_OBJECT(sel), "changed", G_CALLBACK(copy2prim), NULL);
421 
422 	g_signal_connect(G_OBJECT(window), "delete-event", gtk_main_quit, NULL);
423 	button = GTK_WIDGET(gtk_builder_get_object(builder, "quitbtn"));
424 	g_signal_connect(G_OBJECT(button), "clicked", gtk_main_quit, NULL);
425 	gtk_widget_add_accelerator(button, "clicked", group, GDK_KEY_q, GDK_CONTROL_MASK, GTK_ACCEL_VISIBLE);
426 	gtk_widget_add_accelerator(button, "clicked", group, GDK_KEY_w, GDK_CONTROL_MASK, GTK_ACCEL_VISIBLE);
427 	button = GTK_WIDGET(gtk_builder_get_object(builder, "copybtn"));
428 	g_signal_connect_swapped(G_OBJECT(button), "clicked", G_CALLBACK(copy2clip), treeview);
429 	gtk_widget_add_accelerator(button, "clicked", group, GDK_KEY_c, GDK_CONTROL_MASK, GTK_ACCEL_VISIBLE);
430 
431 	gtk_widget_show_all(window);
432 
433 	gtk_main();
434 
435 	return 0;
436 }
437