From aff9ec19a2622905c4cb36c66794db916c90ca89 Mon Sep 17 00:00:00 2001 From: Matthias Schiffer Date: Thu, 19 Sep 2013 19:41:24 +0200 Subject: Implement ulock --- CMakeLists.txt | 37 ++++++- FindPAM.cmake | 16 +++ config.h.in | 8 ++ data/bg.svg | 208 +++++++++++++++++++++++++++++++++++++ data/siegel.png | Bin 0 -> 42599 bytes data/unlock.ui | 166 +++++++++++++++++++++++++++++ etc/ulock.conf | 4 + po/POTFILES.in | 3 + po/de.po | 49 +++++++++ po/ulock.pot | 49 +++++++++ ulock.c | 315 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 11 files changed, 851 insertions(+), 4 deletions(-) create mode 100644 FindPAM.cmake create mode 100644 config.h.in create mode 100644 data/bg.svg create mode 100644 data/siegel.png create mode 100644 data/unlock.ui create mode 100644 etc/ulock.conf create mode 100644 po/POTFILES.in create mode 100644 po/de.po create mode 100644 po/ulock.pot diff --git a/CMakeLists.txt b/CMakeLists.txt index 14fab01..da56d2a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,12 +1,41 @@ project(ULOCK C) cmake_minimum_required(VERSION 2.8) +set(CMAKE_MODULE_PATH ${ULOCK_SOURCE_DIR}) + +find_package(Gettext REQUIRED) + +find_package(PAM REQUIRED) + find_package(PkgConfig REQUIRED) pkg_check_modules(GTK3 REQUIRED gtk+-x11-3.0) +pkg_check_modules(GDK3 REQUIRED gdk-x11-3.0) +pkg_check_modules(X11 REQUIRED x11) + +set(CMAKE_THREAD_PREFER_PTHREAD TRUE) +find_package(Threads) +if(NOT CMAKE_USE_PTHREADS_INIT) + MESSAGE(FATAL_ERROR "No pthread support found.") +endif(NOT CMAKE_USE_PTHREADS_INIT) + + +set(CONFIG_DIR "/etc" CACHE STRING "Path to install ulock's configuration file to") +set(DATA_DIR "share/ulock" CACHE STRING "Path to install ulock's data files to") -include_directories(${GTK3_INCLUDE_DIRS}) + +include_directories(${ULOCK_BINARY_DIR} ${GTK3_INCLUDE_DIRS} ${GDK3_INCLUDE_DIRS} ${X11_INCLUDE_DIRS} ${PAM_INCLUDE_DIR}) + +configure_file(${ULOCK_SOURCE_DIR}/config.h.in ${ULOCK_BINARY_DIR}/config.h) add_executable(ulock ulock.c) -set_property(TARGET ulock PROPERTY COMPILE_FLAGS "-Wall ${GTK3_CFLAGS_OTHER}") -set_property(TARGET ulock PROPERTY LINK_FLAGS "${GTK3_LDFLAGS_OTHER}") -target_link_libraries(ulock ${GTK3_LIBRARIES}) +set_property(TARGET ulock PROPERTY COMPILE_FLAGS "-Wall -pthread ${GTK3_CFLAGS_OTHER} ${GDK3_CFLAGS_OTHER} ${X11_CFLAGS_OTHER}") +set_property(TARGET ulock PROPERTY LINK_FLAGS "-pthread ${GTK3_LDFLAGS_OTHER} ${GDK3_LDFLAGS_OTHER} ${X11_LDFLAGS_OTHER}") +target_link_libraries(ulock ${GTK3_LIBRARIES} ${GDK3_LIBRARIES} ${X11_LIBRARIES} ${PAM_LIBRARY}) + +GETTEXT_PROCESS_PO_FILES(de ALL PO_FILES po/de.po) + +install(TARGETS ulock RUNTIME DESTINATION bin) +install(DIRECTORY data/ DESTINATION ${DATA_DIR}) +install(FILES etc/ulock.conf DESTINATION ${CONFIG_DIR}) + +install(FILES ${ULOCK_BINARY_DIR}/de.gmo DESTINATION share/locale/de/LC_MESSAGES RENAME ulock.mo) diff --git a/FindPAM.cmake b/FindPAM.cmake new file mode 100644 index 0000000..f324670 --- /dev/null +++ b/FindPAM.cmake @@ -0,0 +1,16 @@ +FIND_PATH(PAM_INCLUDE_DIR security/pam_appl.h) +FIND_LIBRARY(PAM_LIBRARY NAMES pam) + +IF (PAM_INCLUDE_DIR AND PAM_LIBRARY) + SET(PAM_FOUND TRUE) +ENDIF (PAM_INCLUDE_DIR AND PAM_LIBRARY) + +IF (PAM_FOUND) + IF (NOT PAM_FIND_QUIETLY) + MESSAGE(STATUS "Found PAM: ${PAM_LIBRARY}; include path: ${PAM_INCLUDE_DIR}") + ENDIF (NOT PAM_FIND_QUIETLY) +ELSE (PAM_FOUND) + IF (PAM_FIND_REQUIRED) + MESSAGE(FATAL_ERROR "Could not find PAM") + ENDIF (PAM_FIND_REQUIRED) +ENDIF (PAM_FOUND) diff --git a/config.h.in b/config.h.in new file mode 100644 index 0000000..6f5e541 --- /dev/null +++ b/config.h.in @@ -0,0 +1,8 @@ +#ifndef _ULOCK_CONFIG_H_ +#define _ULOCK_CONFIG_H_ + +#define CONFIG_FILE "@CONFIG_DIR@/ulock.conf" +#define PREFIX "@CMAKE_INSTALL_PREFIX@" +#define DATA_DIR "@CMAKE_INSTALL_PREFIX@/@DATA_DIR@" + +#endif /* _ULOCK_CONFIG_H_ */ diff --git a/data/bg.svg b/data/bg.svg new file mode 100644 index 0000000..78e3972 --- /dev/null +++ b/data/bg.svg @@ -0,0 +1,208 @@ + + + +image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Bei Problemen melden Sie sich bitte per E-Mail anpool-hotline@itsc.uni-luebeck.deoder telefonisch unter derinternen Nummer 5004. + + \ No newline at end of file diff --git a/data/siegel.png b/data/siegel.png new file mode 100644 index 0000000..8b46a10 Binary files /dev/null and b/data/siegel.png differ diff --git a/data/unlock.ui b/data/unlock.ui new file mode 100644 index 0000000..89bc3b8 --- /dev/null +++ b/data/unlock.ui @@ -0,0 +1,166 @@ + + + + + 700 + True + False + center + center + False + + + True + False + 40 + + + True + False + siegel.png + + + False + True + 0 + + + + + True + False + 12 + 12 + + + True + False + [lock] + + + True + True + 1 + + + + + False + 6 + + + False + 6 + + + True + False + Password: + + + False + True + 0 + + + + + True + True + False + + True + False + False + + + + True + True + 1 + + + + + True + True + 0 + + + + + False + 0 + [message] + + + True + True + 1 + + + + + True + False + 20 + + + Unlock + False + True + True + True + False + + + True + True + end + 1 + + + + + Logout + False + True + True + True + False + + + True + True + end + 2 + + + + + True + True + 2 + + + + + False + True + 3 + + + + + False + True + 1 + + + + + + + + + diff --git a/etc/ulock.conf b/etc/ulock.conf new file mode 100644 index 0000000..920e278 --- /dev/null +++ b/etc/ulock.conf @@ -0,0 +1,4 @@ +[ulock] +#theme=Adwaita +#logout_timeout=600 +#logout_command=xfce4-session-logout -l diff --git a/po/POTFILES.in b/po/POTFILES.in new file mode 100644 index 0000000..def4168 --- /dev/null +++ b/po/POTFILES.in @@ -0,0 +1,3 @@ +[encoding: UTF-8] +pylock.c +[type: gettext/glade]data/unlock.ui diff --git a/po/de.po b/po/de.po new file mode 100644 index 0000000..108462a --- /dev/null +++ b/po/de.po @@ -0,0 +1,49 @@ +# ulock german translation +# Copyright (C) 2013 Matthias Schiffer +# This file is distributed under the same license as the ulock package. +# Matthias Schiffer , 2013. +# +msgid "" +msgstr "" +"Project-Id-Version: pylock 0.1\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2013-09-19 18:53+0200\n" +"PO-Revision-Date: 2012-02-01 19:30+0100\n" +"Last-Translator: Matthias Schiffer \n" +"Language-Team: German \n" +"Language: de\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: data/unlock.ui:125 +msgid "Logout" +msgstr "Ausloggen" + +#: data/unlock.ui:58 +msgid "Password:" +msgstr "Passwort:" + +#: ulock.c:88 +msgid "This computer is currently locked by the user %s." +msgstr "Dieser Computer ist zur Zeit vom Benutzer %s gesperrt." + +#: ulock.c:92 +msgid "" +"This computer is currently locked by the user %s.\n" +"The user can be logged out in %02u:%02u minutes." +msgstr "" +"Dieser Computer ist zur Zeit vom Benutzer %s gesperrt.\n" +"Der Benutzer kann in %02u:%02u Minuten ausgeloggt werden." + +#: ulock.c:98 +msgid "" +"This computer is currently locked by the user %s.\n" +"The user can be logged out now." +msgstr "" +"Dieser Computer ist zur Zeit vom Benutzer %s gesperrt.\n" +"Der Benutzer kann jetzt ausgeloggt werden." + +#: data/unlock.ui:109 +msgid "Unlock" +msgstr "Entsperren" diff --git a/po/ulock.pot b/po/ulock.pot new file mode 100644 index 0000000..697554b --- /dev/null +++ b/po/ulock.pot @@ -0,0 +1,49 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2013-09-19 18:53+0200\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=CHARSET\n" +"Content-Transfer-Encoding: 8bit\n" + +#: ulock.c:88 +#, c-format +msgid "This computer is currently locked by the user %s." +msgstr "" + +#: ulock.c:92 +#, c-format +msgid "" +"This computer is currently locked by the user %s.\n" +"The user can be logged out in %02u:%02u minutes." +msgstr "" + +#: ulock.c:98 +#, c-format +msgid "" +"This computer is currently locked by the user %s.\n" +"The user can be logged out now." +msgstr "" + +#: data/unlock.ui:58 +msgid "Password:" +msgstr "" + +#: data/unlock.ui:109 +msgid "Unlock" +msgstr "" + +#: data/unlock.ui:125 +msgid "Logout" +msgstr "" diff --git a/ulock.c b/ulock.c index e69de29..aaa1ecd 100644 --- a/ulock.c +++ b/ulock.c @@ -0,0 +1,315 @@ +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +#include + + +#define _(s) gettext(s) +#define DEBUG(args...) syslog(LOG_NOTICE, args) + + +enum lock_state { + LOCKED, UNLOCKING, LOGOUT +}; + + +static enum lock_state state = LOCKED; + +static GtkWidget *window, *unlockWindow, *lockLabel, *promptEntry, *messageLabel, *logoutButton, *unlockButton; +static gboolean enablePromptEntry = TRUE, enableLogoutButton = FALSE, enableUnlockButton = TRUE; + +static char *username; +static char *logoutCommand = NULL; +static int logoutTime = 0; + + +static gboolean do_grab_devices(gpointer user_data) { + DEBUG("do_grab_devices"); + + gtk_window_present(GTK_WINDOW(window)); + + GdkDeviceManager *deviceManager = gdk_display_get_device_manager(gdk_display_get_default()); + GList *devices = gdk_device_manager_list_devices(deviceManager, GDK_DEVICE_TYPE_MASTER); + + gboolean error = FALSE; + + for (; devices; devices = g_list_next(devices)) { + GdkDevice *device = devices->data; + + if (gdk_device_grab(device, gtk_widget_get_window(window), GDK_OWNERSHIP_APPLICATION, TRUE, GDK_ALL_EVENTS_MASK, NULL, GDK_CURRENT_TIME) != GDK_GRAB_SUCCESS) + error = TRUE; + } + + g_list_free(devices); + + return error; +} + +static void grab_devices(void) { + if (do_grab_devices(NULL)) + g_timeout_add_seconds(1, do_grab_devices, NULL); +} + +static void update(void) { + gboolean locked = (state == LOCKED); + + gtk_widget_set_sensitive(promptEntry, enablePromptEntry && locked); + gtk_widget_set_sensitive(logoutButton, enableLogoutButton && locked); + gtk_widget_set_sensitive(unlockButton, enableUnlockButton && locked); +} + +static gboolean do_update_logout_time(gpointer user_data) { + gboolean ret = FALSE; + char *escaped_username = g_markup_escape_text(username, -1); + char *message; + + if (!logoutCommand) { + message = g_strdup_printf(_("This computer is currently locked by the user %s."), escaped_username); + enableLogoutButton = FALSE; + } + else if (logoutTime > 0) { + message = g_strdup_printf(_("This computer is currently locked by the user %s.\nThe user can be logged out in %02u:%02u minutes."), escaped_username, logoutTime/60, logoutTime%60); + enableLogoutButton = FALSE; + ret = TRUE; + logoutTime--; + } + else { + message = g_strdup_printf(_("This computer is currently locked by the user %s.\nThe user can be logged out now."), escaped_username); + enableLogoutButton = TRUE; + } + + gtk_label_set_markup(GTK_LABEL(lockLabel), message); + g_free(message); + + update(); + + return ret; +} + +static void update_logout_time() { + if (do_update_logout_time(NULL)) + g_timeout_add_seconds(1, do_update_logout_time, NULL); +} + +static void lock(void) { + if (state != LOCKED) + return; + + DEBUG("lock"); + + update(); + grab_devices(); + gtk_widget_grab_focus(promptEntry); +} + +static int ulock_conv(int num_msg, const struct pam_message **msg, struct pam_response **resp, void *appdata_ptr) { + char *password = appdata_ptr; + + *resp = calloc(num_msg, sizeof(struct pam_response)); + int i; + for (i = 0; i < num_msg; i++) { + if (msg[i]->msg_style == PAM_PROMPT_ECHO_OFF) + resp[i]->resp = strdup(password); + } + + return 0; +} + +static gboolean try_unlock_return(gpointer message) { + DEBUG("try_unlock_return"); + + if (message) { + state = LOCKED; + gtk_entry_set_text(GTK_ENTRY(promptEntry), ""); + gtk_label_set_label(GTK_LABEL(messageLabel), message); + lock(); + } + else { + gtk_main_quit(); + } + + return FALSE; +} + +/* this thread must not access any GTK resources */ +static void* try_unlock_thread(void *arg) { + const struct pam_conv conv = {.conv = ulock_conv, .appdata_ptr = arg}; + + pam_handle_t *handle = NULL; + int ret = pam_start("login", username, &conv, &handle); + + if (!ret) + ret = pam_authenticate(handle, 0); + + const char *message = NULL; + if (ret) + message = pam_strerror(handle, ret); + + pam_end(handle, ret); + + g_idle_add(try_unlock_return, (gpointer)message); + + free(arg); + return NULL; +} + +static void try_unlock(void) { + if (state != LOCKED) + return; + + DEBUG("try_unlock"); + + state = UNLOCKING; + update(); + gtk_label_set_label(GTK_LABEL(messageLabel), ""); + + pthread_t thread; + pthread_create(&thread, NULL, try_unlock_thread, strdup((char*)gtk_entry_get_text(GTK_ENTRY(promptEntry)))); + pthread_detach(thread); +} + +static void logout(void) { + if (state != LOCKED) + return; + + if (!logoutCommand) + return; + + if (logoutTime > 0) + return; + + DEBUG("logout"); + + state = LOGOUT; + update(); + + system(logoutCommand); +} + +static GdkFilterReturn xevent_filter(GdkXEvent *xevent, GdkEvent *event, gpointer data) { + XEvent *ev = xevent; + + switch (ev->xany.type) { + case MapNotify: + case ConfigureNotify: + if (ev->xany.window != gdk_x11_window_get_xid(gtk_widget_get_window(window))) + gtk_window_present(GTK_WINDOW(window)); + break; + + default: + break; + } + + return GDK_FILTER_CONTINUE; +} + + +static void create_lock_window(void) { + window = gtk_window_new(GTK_WINDOW_TOPLEVEL); + + gtk_window_set_decorated(GTK_WINDOW(window), FALSE); + gtk_window_set_skip_taskbar_hint(GTK_WINDOW(window), TRUE); + gtk_window_set_skip_pager_hint(GTK_WINDOW(window), TRUE); + gtk_window_set_keep_above(GTK_WINDOW(window), TRUE); + gtk_window_fullscreen(GTK_WINDOW(window)); + + GtkWidget *overlay = gtk_overlay_new(); + gtk_container_add(GTK_CONTAINER(window), overlay); + + GtkWidget *bg = gtk_image_new_from_file(DATA_DIR "/bg.svg"); + gtk_container_add(GTK_CONTAINER(overlay), bg); + + GtkBuilder *builder = gtk_builder_new(); + + if (!gtk_builder_add_from_file(builder, DATA_DIR "/unlock.ui", NULL)) + exit(1); + + unlockWindow = GTK_WIDGET(gtk_builder_get_object(builder, "unlock_window")); + lockLabel = GTK_WIDGET(gtk_builder_get_object(builder, "lock_label")); + promptEntry = GTK_WIDGET(gtk_builder_get_object(builder, "prompt_entry")); + messageLabel = GTK_WIDGET(gtk_builder_get_object(builder, "message_label")); + logoutButton = GTK_WIDGET(gtk_builder_get_object(builder, "logout_button")); + unlockButton = GTK_WIDGET(gtk_builder_get_object(builder, "unlock_button")); + + gtk_overlay_add_overlay(GTK_OVERLAY(overlay), unlockWindow); + + gtk_widget_show_all(overlay); + + gtk_entry_set_text(GTK_ENTRY(promptEntry), ""); + gtk_label_set_label(GTK_LABEL(messageLabel), ""); + + g_signal_connect(promptEntry, "activate", G_CALLBACK(try_unlock), NULL); + g_signal_connect(unlockButton, "clicked", G_CALLBACK(try_unlock), NULL); + g_signal_connect(logoutButton, "clicked", G_CALLBACK(logout), NULL); + g_signal_connect(window, "map-event", G_CALLBACK(lock), NULL); + g_signal_connect(window, "delete-event", G_CALLBACK(gtk_true), NULL); + + XSelectInput(gdk_x11_get_default_xdisplay(), gdk_x11_get_default_root_xwindow(), SubstructureNotifyMask); + gdk_window_add_filter(NULL, (GdkFilterFunc)xevent_filter, NULL); + + update_logout_time(); +} + +static void get_username(void) { + struct passwd *passwd = getpwuid(getuid()); + if (!passwd) + exit(2); + + username = strdup(passwd->pw_name); +} + +static void load_config(void) { + GKeyFile *keyFile = g_key_file_new(); + if (!g_key_file_load_from_file(keyFile, CONFIG_FILE, G_KEY_FILE_NONE, NULL)) + return; + + gchar *theme = g_key_file_get_string(keyFile, "ulock", "theme", NULL); + if (theme) + gtk_settings_set_string_property(gtk_settings_get_default(), "gtk-theme-name", theme, "ulock"); + + logoutTime = g_key_file_get_integer(keyFile, "ulock", "logout_timeout", NULL); + + gchar *logoutCommandConfig = g_key_file_get_string(keyFile, "ulock", "logout_string", NULL); + if (logoutCommandConfig) + logoutCommand = strdup(logoutCommandConfig); + + g_key_file_free(keyFile); +} + +int main(int argc, char *argv[]) { + get_username(); + logoutCommand = strdup("echo foo"); + + setlocale(LC_ALL, ""); + bindtextdomain("ulock", PREFIX "/share/locale"); + textdomain("ulock"); + + openlog("ulock", LOG_PID, LOG_DAEMON); + + gtk_init(&argc, &argv); + load_config(); + + create_lock_window(); + gtk_widget_show(window); + + gtk_main(); + + return 0; +} -- cgit v1.2.3