diff options
-rw-r--r-- | CMakeLists.txt | 37 | ||||
-rw-r--r-- | FindPAM.cmake | 16 | ||||
-rw-r--r-- | config.h.in | 8 | ||||
-rw-r--r-- | data/bg.svg | 208 | ||||
-rw-r--r-- | data/siegel.png | bin | 0 -> 42599 bytes | |||
-rw-r--r-- | data/unlock.ui | 166 | ||||
-rw-r--r-- | etc/ulock.conf | 4 | ||||
-rw-r--r-- | po/POTFILES.in | 3 | ||||
-rw-r--r-- | po/de.po | 49 | ||||
-rw-r--r-- | po/ulock.pot | 49 | ||||
-rw-r--r-- | ulock.c | 315 |
11 files changed, 851 insertions, 4 deletions
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 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- Created with Inkscape (http://www.inkscape.org/) --> + +<svg + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + xmlns:xlink="http://www.w3.org/1999/xlink" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + version="1.1" + width="1680" + height="1050" + viewBox="0 0 1680 1050" + id="svg2" + xml:space="preserve" + inkscape:version="0.48.2 r9819" + sodipodi:docname="bg.svg"><sodipodi:namedview + pagecolor="#ffffff" + bordercolor="#666666" + borderopacity="1" + objecttolerance="10" + gridtolerance="10" + guidetolerance="10" + inkscape:pageopacity="0" + inkscape:pageshadow="2" + inkscape:window-width="1280" + inkscape:window-height="776" + id="namedview3016" + showgrid="false" + inkscape:zoom="0.23166667" + inkscape:cx="349.5177" + inkscape:cy="787.53453" + inkscape:window-x="0" + inkscape:window-y="24" + inkscape:window-maximized="0" + inkscape:current-layer="layer1" /><metadata + id="metadata129"><rdf:RDF><cc:Work + rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title /></cc:Work></rdf:RDF></metadata><defs + id="defs127"> + + + +<linearGradient + x1="960.00049" + y1="878.5" + x2="960.00049" + y2="291.50049" + id="linearGradient3223" + xlink:href="#SVGID_1_" + gradientUnits="userSpaceOnUse" /> +<linearGradient + inkscape:collect="always" + xlink:href="#SVGID_1_" + id="linearGradient3811" + gradientUnits="userSpaceOnUse" + x1="960.00049" + y1="878.5" + x2="960.00049" + y2="291.50049" /> + <linearGradient + x1="960.00049" + y1="878.5" + x2="960.00049" + y2="291.50049" + id="SVGID_1_" + gradientUnits="userSpaceOnUse"> + <stop + id="stop20" + style="stop-color:#50acc5;stop-opacity:0.80000001" + offset="0" /> + <stop + id="stop22" + style="stop-color:#388da2;stop-opacity:0.81980002" + offset="0.0148" /> + <stop + id="stop24" + style="stop-color:#247385;stop-opacity:0.84420002" + offset="0.0331" /> + <stop + id="stop26" + style="stop-color:#166071;stop-opacity:0.87110001" + offset="0.0533" /> + <stop + id="stop28" + style="stop-color:#0b5363;stop-opacity:0.9016" + offset="0.0762" /> + <stop + id="stop30" + style="stop-color:#044d5c;stop-opacity:0.93849999" + offset="0.1039" /> + <stop + id="stop32" + style="stop-color:#104b5a;stop-opacity:1;" + offset="0.15000001" /> + <stop + id="stop34" + style="stop-color:#104b5a;stop-opacity:1;" + offset="0.85000002" /> + <stop + id="stop36" + style="stop-color:#044d5c;stop-opacity:0.93529999" + offset="0.89859998" /> + <stop + id="stop38" + style="stop-color:#0b5363;stop-opacity:0.8987" + offset="0.926" /> + <stop + id="stop40" + style="stop-color:#165f70;stop-opacity:0.86919999" + offset="0.94809997" /> + <stop + id="stop42" + style="stop-color:#237184;stop-opacity:0.84350002" + offset="0.96740001" /> + <stop + id="stop44" + style="stop-color:#368ba0;stop-opacity:0.8204" + offset="0.98470002" /> + <stop + id="stop46" + style="stop-color:#50acc5;stop-opacity:0.80000001" + offset="1" /> + </linearGradient> + + + <linearGradient + inkscape:collect="always" + xlink:href="#SVGID_1_" + id="linearGradient3830" + gradientUnits="userSpaceOnUse" + x1="960.00049" + y1="878.5" + x2="960.00049" + y2="291.50049" + gradientTransform="matrix(1.1474185,0,0,0.85490494,-261.52174,174.07496)" /></defs> + + + + +<g + id="Hilfslinien_Center" + style="display:none" + transform="translate(0,-150)"> +</g> +<g + inkscape:groupmode="layer" + id="layer1" + inkscape:label="Layer" + transform="translate(0,-150)"><rect + style="fill:#104b5a;fill-opacity:1" + id="rect5" + y="150" + x="0" + height="1050" + width="1680" /><path + d="m 1326.2174,920.83442 c 0,2.35099 -2.5816,4.27453 -5.7371,4.27453 H 359.51967 c -3.1554,0 -5.73709,-1.92354 -5.73709,-4.27453 V 427.55427 c 0,-2.35098 2.58169,-4.27452 5.73709,-4.27452 h 960.96063 c 3.1555,0 5.7371,1.92354 5.7371,4.27452 v 493.28015 z" + id="path48" + style="opacity:0.5;fill:url(#linearGradient3830)" + inkscape:connector-curvature="0" /><path + d="m 1326.2174,920.83442 c 0,2.35099 -2.5816,4.27453 -5.7371,4.27453 H 359.51967 c -3.1554,0 -5.73709,-1.92354 -5.73709,-4.27453 V 427.55427 c 0,-2.35098 2.58169,-4.27452 5.73709,-4.27452 h 960.96063 c 3.1555,0 5.7371,1.92354 5.7371,4.27452 v 493.28015 z" + id="path50" + style="fill:none;stroke:#50acc5;stroke-width:0.4952105;stroke-linejoin:round;stroke-miterlimit:10" + inkscape:connector-curvature="0" /><g + id="Hilfslinien_Login" + transform="translate(-120,75)"> +</g><text + xml:space="preserve" + style="font-size:14px;font-style:normal;font-weight:normal;line-height:129.99999523%;letter-spacing:0px;word-spacing:0px;fill:#ffffff;fill-opacity:1;stroke:none;font-family:Sans" + x="839.86035" + y="1006.295" + id="text3011" + sodipodi:linespacing="130%"><tspan + sodipodi:role="line" + id="tspan3013" + x="839.86035" + y="1006.295" + style="font-size:22px;font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;text-align:center;line-height:129.99999523%;writing-mode:lr-tb;text-anchor:middle;font-family:Sans;-inkscape-font-specification:Sans Bold">Bei Problemen melden Sie sich bitte per E-Mail an</tspan><tspan + sodipodi:role="line" + x="839.86035" + y="1034.895" + id="tspan3015" + style="font-size:22px;font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;text-align:center;line-height:129.99999523%;writing-mode:lr-tb;text-anchor:middle;font-family:Sans;-inkscape-font-specification:Sans Bold" /><tspan + sodipodi:role="line" + x="839.86035" + y="1063.495" + id="tspan3017" + style="font-size:22px;font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;text-align:center;line-height:129.99999523%;writing-mode:lr-tb;text-anchor:middle;font-family:Sans;-inkscape-font-specification:Sans Bold">pool-hotline@itsc.uni-luebeck.de</tspan><tspan + sodipodi:role="line" + x="839.86035" + y="1092.095" + id="tspan3019" + style="font-size:22px;font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;text-align:center;line-height:129.99999523%;writing-mode:lr-tb;text-anchor:middle;font-family:Sans;-inkscape-font-specification:Sans Bold" /><tspan + sodipodi:role="line" + x="839.86035" + y="1120.6949" + id="tspan3021" + style="font-size:22px;font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;text-align:center;line-height:129.99999523%;writing-mode:lr-tb;text-anchor:middle;font-family:Sans;-inkscape-font-specification:Sans Bold">oder telefonisch unter der</tspan><tspan + sodipodi:role="line" + x="839.86035" + y="1149.2949" + id="tspan3023" + style="font-size:22px;font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;text-align:center;line-height:129.99999523%;writing-mode:lr-tb;text-anchor:middle;font-family:Sans;-inkscape-font-specification:Sans Bold">internen Nummer 5004.</tspan></text> + +</g></svg>
\ No newline at end of file diff --git a/data/siegel.png b/data/siegel.png Binary files differnew file mode 100644 index 0000000..8b46a10 --- /dev/null +++ b/data/siegel.png 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 @@ +<?xml version="1.0" encoding="UTF-8"?> +<interface> + <requires lib="gtk+" version="2.24"/> + <object class="GtkNotebook" id="unlock_window"> + <property name="width_request">700</property> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="halign">center</property> + <property name="valign">center</property> + <property name="show_tabs">False</property> + <child> + <object class="GtkHBox" id="box1"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="spacing">40</property> + <child> + <object class="GtkImage" id="image1"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="pixbuf">siegel.png</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">0</property> + </packing> + </child> + <child> + <object class="GtkVBox" id="vbox2"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="border_width">12</property> + <property name="spacing">12</property> + <child> + <object class="GtkLabel" id="lock_label"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="label" comments="This is a placeholder string and will be replaced with the hostname of the system">[lock]</property> + </object> + <packing> + <property name="expand">True</property> + <property name="fill">True</property> + <property name="position">1</property> + </packing> + </child> + <child> + <object class="GtkVBox" id="login_box"> + <property name="can_focus">False</property> + <property name="spacing">6</property> + <child> + <object class="GtkHBox" id="prompt_box"> + <property name="can_focus">False</property> + <property name="spacing">6</property> + <child> + <object class="GtkLabel" id="prompt_label"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="label" translatable="yes">Password:</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">0</property> + </packing> + </child> + <child> + <object class="GtkEntry" id="prompt_entry"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="visibility">False</property> + <property name="invisible_char">•</property> + <property name="invisible_char_set">True</property> + <property name="primary_icon_activatable">False</property> + <property name="secondary_icon_activatable">False</property> + <signal name="activate" handler="login_cb" swapped="no"/> + </object> + <packing> + <property name="expand">True</property> + <property name="fill">True</property> + <property name="position">1</property> + </packing> + </child> + </object> + <packing> + <property name="expand">True</property> + <property name="fill">True</property> + <property name="position">0</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="message_label"> + <property name="can_focus">False</property> + <property name="yalign">0</property> + <property name="label" comments="This is a placeholder string and will be replaced with a message from PAM">[message]</property> + </object> + <packing> + <property name="expand">True</property> + <property name="fill">True</property> + <property name="position">1</property> + </packing> + </child> + <child> + <object class="GtkHBox" id="hbox2"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="spacing">20</property> + <child> + <object class="GtkButton" id="unlock_button"> + <property name="label" translatable="yes">Unlock</property> + <property name="use_action_appearance">False</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">True</property> + <property name="use_action_appearance">False</property> + </object> + <packing> + <property name="expand">True</property> + <property name="fill">True</property> + <property name="pack_type">end</property> + <property name="position">1</property> + </packing> + </child> + <child> + <object class="GtkButton" id="logout_button"> + <property name="label" translatable="yes">Logout</property> + <property name="use_action_appearance">False</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">True</property> + <property name="use_action_appearance">False</property> + </object> + <packing> + <property name="expand">True</property> + <property name="fill">True</property> + <property name="pack_type">end</property> + <property name="position">2</property> + </packing> + </child> + </object> + <packing> + <property name="expand">True</property> + <property name="fill">True</property> + <property name="position">2</property> + </packing> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">3</property> + </packing> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">1</property> + </packing> + </child> + </object> + </child> + <child type="tab"> + <placeholder/> + </child> + </object> +</interface> 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 <mschiffer@universe-factory.net>, 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 <mschiffer@universe-factory.net>\n" +"Language-Team: German <de@li.org>\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 <i>%s</i>." +msgstr "Dieser Computer ist zur Zeit vom Benutzer <i>%s</i> gesperrt." + +#: ulock.c:92 +msgid "" +"This computer is currently locked by the user <i>%s</i>.\n" +"The user can be logged out in %02u:%02u minutes." +msgstr "" +"Dieser Computer ist zur Zeit vom Benutzer <i>%s</i> gesperrt.\n" +"Der Benutzer kann in %02u:%02u Minuten ausgeloggt werden." + +#: ulock.c:98 +msgid "" +"This computer is currently locked by the user <i>%s</i>.\n" +"The user can be logged out now." +msgstr "" +"Dieser Computer ist zur Zeit vom Benutzer <i>%s</i> 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 <EMAIL@ADDRESS>, 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 <EMAIL@ADDRESS>\n" +"Language-Team: LANGUAGE <LL@li.org>\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 <i>%s</i>." +msgstr "" + +#: ulock.c:92 +#, c-format +msgid "" +"This computer is currently locked by the user <i>%s</i>.\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 <i>%s</i>.\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 "" @@ -0,0 +1,315 @@ +#include <config.h> + +#include <sys/types.h> + +#include <locale.h> +#include <libintl.h> +#include <pthread.h> +#include <pwd.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <syslog.h> +#include <unistd.h> + +#include <security/pam_appl.h> + +#include <gdk/gdk.h> +#include <gdk/gdkx.h> +#include <gtk/gtk.h> + +#include <X11/Xlib.h> + + +#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 <i>%s</i>."), escaped_username); + enableLogoutButton = FALSE; + } + else if (logoutTime > 0) { + message = g_strdup_printf(_("This computer is currently locked by the user <i>%s</i>.\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 <i>%s</i>.\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; +} |