You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
458 lines
13 KiB
458 lines
13 KiB
/* |
|
* greeter.c |
|
* |
|
* Copyright © 2014-2016 Antergos Developers <dev@antergos.com> |
|
* |
|
* Includes Code Contributed By: |
|
* Copyright © 2016 Scott Balneaves <sbalneav@ltsp.org> |
|
* |
|
* Based on code from lightdm-webkit-greeter: |
|
* Copyright © 2010-2015 Robert Ancell <robert.ancell@canonical.com> |
|
* |
|
* This file is part of lightdm-webkit2-greeter. |
|
* |
|
* lightdm-webkit2-greeter is free software; you can redistribute it and/or modify |
|
* it under the terms of the GNU General Public License as published by |
|
* the Free Software Foundation; either version 3 of the License, or |
|
* (at your option) any later version. |
|
* |
|
* lightdm-webkit2-greeter is distributed in the hope that it will be useful, |
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
* GNU General Public License for more details. |
|
* |
|
* The following additional terms are in effect as per Section 7 of the license: |
|
* |
|
* The preservation of all legal notices and author attributions in |
|
* the material or in the Appropriate Legal Notices displayed |
|
* by works containing it is required. |
|
* |
|
* You should have received a copy of the GNU General Public License |
|
* along with lightdm-webkit2-greeter; If not, see <http://www.gnu.org/licenses/>. |
|
* |
|
*/ |
|
|
|
#include <stdlib.h> |
|
#include <glib.h> |
|
#include <gtk/gtk.h> |
|
#include <gdk/gdkx.h> |
|
#include <glib/gprintf.h> |
|
#include <glib-unix.h> |
|
#include <webkit2/webkit2.h> |
|
#include <JavaScriptCore/JavaScript.h> |
|
#include <glib/gi18n.h> |
|
#include <sys/mman.h> |
|
|
|
#include "config.h" |
|
#include "greeter-resources.h" |
|
|
|
/* Work-around CLion bug */ |
|
#ifndef CONFIG_DIR |
|
#include "../build/src/config.h" |
|
#include "../build/src/greeter-resources.h" |
|
#endif |
|
|
|
|
|
static GtkWidget *web_view; |
|
static GtkWidget *window; |
|
static WebKitSettings *webkit_settings; |
|
static GdkDisplay *default_display; |
|
static GResource *greeter_resources; |
|
|
|
/* Screensaver values */ |
|
static int |
|
timeout, |
|
interval, |
|
prefer_blanking, |
|
allow_exposures; |
|
|
|
static gint config_timeout; |
|
|
|
static gboolean |
|
debug_mode, |
|
heartbeat, |
|
heartbeat_exit; |
|
|
|
|
|
static void |
|
initialize_web_extensions_cb(WebKitWebContext *context, gpointer user_data) { |
|
|
|
webkit_web_context_set_web_extensions_directory(context, WEBEXT_DIR); |
|
|
|
} |
|
|
|
|
|
static void |
|
create_new_webkit_settings_object(void) { |
|
webkit_settings = webkit_settings_new_with_settings( |
|
"enable-developer-extras", TRUE, |
|
"javascript-can-open-windows-automatically", TRUE, |
|
"allow-file-access-from-file-urls", TRUE, |
|
"enable-write-console-messages-to-stdout", TRUE, |
|
#ifdef HAS_WEBKITGTK_2_14 |
|
"allow-universal-access-from-file-urls", TRUE, |
|
#endif |
|
NULL |
|
); |
|
} |
|
|
|
|
|
static gboolean |
|
context_menu_cb(WebKitWebView *view, |
|
WebKitContextMenu *context_menu, |
|
GdkEvent *event, |
|
WebKitHitTestResult *hit_test_result, |
|
gpointer user_data) { |
|
|
|
/* Returning true without creating a custom context menu results in no context |
|
* menu being shown. Thus, we are returning the opposite of debug_mode to get |
|
* desired result (which is only show menu when debug_mode is enabled. |
|
*/ |
|
return (! debug_mode); |
|
} |
|
|
|
|
|
static gboolean |
|
check_theme_heartbeat(void) { |
|
if (! heartbeat && ! heartbeat_exit) { |
|
/* Theme heartbeat not received. We assume that an error has occurred |
|
* which broke script execution. We will fallback to the simple theme |
|
* so the user won't be stuck with a broken login screen. |
|
*/ |
|
g_warning("[ERROR] :: A problem was detected with the current theme. Falling back to simple theme..."); |
|
webkit_web_view_load_uri( |
|
WEBKIT_WEB_VIEW(web_view), |
|
g_strdup_printf("file://%ssimple/index.html", THEME_DIR) |
|
); |
|
} |
|
|
|
heartbeat = FALSE; |
|
|
|
return heartbeat; |
|
} |
|
|
|
|
|
/** |
|
* Callback for Theme Heartbeat. |
|
*/ |
|
static void |
|
theme_heartbeat_handler(void) { |
|
if (! heartbeat) { |
|
/* Setup g_timeout callback for theme heartbeat check */ |
|
g_timeout_add_seconds(8, (GSourceFunc) check_theme_heartbeat, NULL); |
|
heartbeat = TRUE; |
|
heartbeat_exit = FALSE; |
|
} |
|
} |
|
|
|
|
|
/** |
|
* Before starting the user's session, the web process will send a signal to exit the hearbeat |
|
* in order to prevent a race condition while the greeter is shutting down. |
|
*/ |
|
static void |
|
theme_heartbeat_exit_handler(void) { |
|
heartbeat_exit = TRUE; |
|
} |
|
|
|
|
|
static void |
|
script_loaded_cb(GObject *object, |
|
GAsyncResult *result, |
|
gpointer user_data) { |
|
|
|
WebKitJavascriptResult *js_result; |
|
GError *error = NULL; |
|
|
|
js_result = webkit_web_view_run_javascript_from_gresource_finish( |
|
WEBKIT_WEB_VIEW(object), |
|
result, |
|
&error |
|
); |
|
|
|
if (!js_result) { |
|
g_error_free(error); |
|
} else { |
|
webkit_javascript_result_unref(js_result); |
|
} |
|
} |
|
|
|
|
|
/** |
|
* Makes the greeter behave a bit more like a screensaver if it was launched as |
|
* a lock-screen by blanking the screen. |
|
*/ |
|
static void |
|
lock_hint_enabled_handler(void) { |
|
Display *display = gdk_x11_display_get_xdisplay(default_display); |
|
config_timeout = (0 != config_timeout) ? config_timeout : 300; |
|
|
|
XGetScreenSaver(display, &timeout, &interval, &prefer_blanking, &allow_exposures); |
|
XForceScreenSaver(display, ScreenSaverActive); |
|
XSetScreenSaver(display, config_timeout, 0, PreferBlanking, DefaultExposures); |
|
} |
|
|
|
|
|
static void |
|
load_script(char *script) { |
|
webkit_web_view_run_javascript_from_gresource( |
|
WEBKIT_WEB_VIEW(web_view), |
|
script, |
|
NULL, |
|
(GAsyncReadyCallback) script_loaded_cb, |
|
NULL |
|
); |
|
} |
|
|
|
|
|
static void |
|
greeter_loaded_handler(void) { |
|
load_script(GRESOURCE_PATH "/js/_vendor/moment-with-locales.min.js"); |
|
load_script(GRESOURCE_PATH "/js/Greeter.js"); |
|
load_script(GRESOURCE_PATH "/js/GreeterConfig.js"); |
|
load_script(GRESOURCE_PATH "/js/ThemeUtils.js"); |
|
load_script(GRESOURCE_PATH "/js/ThemeHeartbeat.js"); |
|
} |
|
|
|
|
|
/** |
|
* Message received callback. |
|
* |
|
* Receives messages from our web extension process and calls appropriate handlers. |
|
* |
|
* @param manager The user content manager instance that was created in #main. |
|
* @param message The message sent from web extension process. |
|
* @param user_data Data that is private to the current user. |
|
*/ |
|
static void |
|
message_received_cb(WebKitUserContentManager *manager, |
|
WebKitJavascriptResult *message, |
|
gpointer user_data) { |
|
|
|
gchar *message_str; |
|
JSGlobalContextRef context; |
|
JSValueRef message_val; |
|
JSStringRef js_str_val; |
|
gsize message_str_length; |
|
|
|
context = webkit_javascript_result_get_global_context(message); |
|
message_val = webkit_javascript_result_get_value(message); |
|
|
|
if (JSValueIsString(context, message_val)) { |
|
js_str_val = JSValueToStringCopy(context, message_val, NULL); |
|
message_str_length = JSStringGetMaximumUTF8CStringSize(js_str_val); |
|
message_str = (gchar *)g_malloc (message_str_length); |
|
JSStringGetUTF8CString(js_str_val, message_str, message_str_length); |
|
JSStringRelease(js_str_val); |
|
|
|
} else { |
|
message_str = ""; |
|
printf("Error running javascript: unexpected return value"); |
|
} |
|
|
|
if (strcmp(message_str, "GreeterLoaded") == 0) { |
|
greeter_loaded_handler(); |
|
|
|
} else if (strcmp(message_str, "LockHint") == 0) { |
|
lock_hint_enabled_handler(); |
|
|
|
} else if (strcmp(message_str, "Heartbeat") == 0) { |
|
theme_heartbeat_handler(); |
|
|
|
} else if (strcmp(message_str, "Heartbeat::Exit") == 0) { |
|
theme_heartbeat_exit_handler(); |
|
} else { |
|
printf("UI PROCESS - message_received_cb(): no match!"); |
|
} |
|
|
|
g_free(message_str); |
|
|
|
} |
|
|
|
|
|
static void |
|
quit_cb(void) { |
|
gtk_widget_destroy(window); |
|
gtk_main_quit(); |
|
} |
|
|
|
|
|
static gchar * |
|
rtrim_comments(gchar *str) { |
|
gchar *ptr = NULL; |
|
|
|
ptr = strchr(str, '#'); |
|
|
|
if (NULL != ptr) { |
|
*ptr = '\0'; |
|
} |
|
|
|
return g_strstrip(str); |
|
} |
|
|
|
|
|
int |
|
main(int argc, char **argv) { |
|
GdkScreen *screen; |
|
GdkWindow *root_window; |
|
GdkRectangle geometry; |
|
GKeyFile *keyfile; |
|
gchar *theme; |
|
GError *err = NULL; |
|
GdkRGBA bg_color; |
|
WebKitUserContentManager *manager; |
|
WebKitWebContext *context; |
|
GtkCssProvider *css_provider; |
|
WebKitCookieManager *cookie_manager; |
|
|
|
/* Prevent memory from being swapped out, since we see unencrypted passwords. */ |
|
mlockall (MCL_CURRENT | MCL_FUTURE); |
|
|
|
/* https://goo.gl/vDFwFe */ |
|
g_setenv ("GDK_CORE_DEVICE_EVENTS", "1", TRUE); |
|
|
|
/* Initialize i18n */ |
|
bindtextdomain(GETTEXT_PACKAGE, LOCALE_DIR); |
|
bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8"); |
|
textdomain(GETTEXT_PACKAGE); |
|
|
|
gtk_init(&argc, &argv); |
|
|
|
g_unix_signal_add(SIGTERM, (GSourceFunc) quit_cb, NULL); |
|
g_unix_signal_add(SIGINT, (GSourceFunc) quit_cb, NULL); |
|
g_unix_signal_add(SIGHUP, (GSourceFunc) quit_cb, NULL); |
|
|
|
/* BEGIN Greeter Config File */ |
|
keyfile = g_key_file_new(); |
|
|
|
g_key_file_load_from_file( |
|
keyfile, |
|
CONFIG_DIR "/lightdm-webkit2-greeter.conf", |
|
G_KEY_FILE_NONE, |
|
NULL |
|
); |
|
|
|
/* TODO: Handle config values and fallbacks some other way, this is garbage! */ |
|
theme = g_key_file_get_string(keyfile, "greeter", "webkit_theme", &err); |
|
|
|
if ( NULL != err) { |
|
g_clear_error(&err); |
|
theme = g_key_file_get_string(keyfile, "greeter", "webkit-theme", &err); |
|
|
|
if ( NULL != err) { |
|
g_clear_error(&err); |
|
theme = "antergos"; |
|
} |
|
} |
|
|
|
theme = rtrim_comments(theme); |
|
config_timeout = g_key_file_get_integer(keyfile, "greeter", "screensaver_timeout", &err); |
|
|
|
if ( NULL != err) { |
|
g_clear_error(&err); |
|
config_timeout = g_key_file_get_integer(keyfile, "greeter", "screensaver-timeout", &err); |
|
|
|
if ( NULL != err) { |
|
g_error_free(err); |
|
config_timeout = 300; |
|
} |
|
} |
|
|
|
debug_mode = g_key_file_get_boolean(keyfile, "greeter", "debug_mode", NULL); |
|
|
|
if ( NULL != err) { |
|
g_clear_error(&err); |
|
debug_mode = FALSE; |
|
} |
|
/* END Greeter Config File */ |
|
|
|
/* Set default cursor */ |
|
root_window = gdk_get_default_root_window(); |
|
default_display = gdk_display_get_default(); |
|
|
|
gdk_window_set_cursor(root_window, gdk_cursor_new_for_display(default_display, GDK_LEFT_PTR)); |
|
|
|
/* Set the GTK theme to Adwaita */ |
|
g_object_set(gtk_settings_get_default(), "gtk-theme-name", "Adwaita", NULL); |
|
|
|
/* Setup the main window */ |
|
window = gtk_window_new(GTK_WINDOW_TOPLEVEL); |
|
screen = gtk_window_get_screen(GTK_WINDOW(window)); |
|
|
|
gtk_window_set_decorated(GTK_WINDOW(window), FALSE); |
|
|
|
#ifdef HAS_GTK_3_22 |
|
GdkMonitor *monitor = gdk_display_get_primary_monitor(default_display); |
|
gdk_monitor_get_geometry(monitor, &geometry); |
|
#else |
|
gdk_screen_get_monitor_geometry(screen, gdk_screen_get_primary_monitor(screen), &geometry); |
|
#endif |
|
|
|
gtk_window_set_default_size(GTK_WINDOW(window), geometry.width, geometry.height); |
|
gtk_window_move(GTK_WINDOW(window), geometry.x, geometry.y); |
|
|
|
/* Setup CSS provider. We use CSS to set the window background to black instead |
|
* of default white so the screen doesnt flash during startup. |
|
*/ |
|
greeter_resources = greeter_resources_get_resource(); |
|
css_provider = gtk_css_provider_new(); |
|
|
|
g_resources_register(greeter_resources); |
|
gtk_css_provider_load_from_resource( |
|
GTK_CSS_PROVIDER(css_provider), |
|
"/com/antergos/lightdm-webkit2-greeter/css/style.css" |
|
); |
|
gtk_style_context_add_provider_for_screen( |
|
screen, |
|
GTK_STYLE_PROVIDER(css_provider), |
|
GTK_STYLE_PROVIDER_PRIORITY_APPLICATION |
|
); |
|
|
|
/* Register and connect handler that will set the web extensions directory |
|
* so webkit can find our extension. |
|
*/ |
|
context = webkit_web_context_get_default(); |
|
g_signal_connect(WEBKIT_WEB_CONTEXT(context), |
|
"initialize-web-extensions", |
|
G_CALLBACK(initialize_web_extensions_cb), NULL); |
|
|
|
/* Set cookie policy */ |
|
cookie_manager = webkit_web_context_get_cookie_manager(context); |
|
webkit_cookie_manager_set_accept_policy(cookie_manager, WEBKIT_COOKIE_POLICY_ACCEPT_ALWAYS); |
|
|
|
/* Register and connect handler of any messages we send from our web extension process. */ |
|
manager = webkit_user_content_manager_new(); |
|
g_signal_connect(manager, "script-message-received::GreeterBridge", G_CALLBACK(message_received_cb), NULL); |
|
webkit_user_content_manager_register_script_message_handler(manager, "GreeterBridge"); |
|
|
|
/* Create the web_view */ |
|
web_view = webkit_web_view_new_with_user_content_manager(manager); |
|
|
|
/* Set the web_view's settings. */ |
|
create_new_webkit_settings_object(); |
|
webkit_web_view_set_settings(WEBKIT_WEB_VIEW(web_view), WEBKIT_SETTINGS(webkit_settings)); |
|
|
|
/* The default background color of the web_view is white which causes a flash effect when the greeter starts. |
|
* We make it black instead. This only applies when the theme hasn't set the body background via CSS. |
|
*/ |
|
gdk_rgba_parse(&bg_color, "#000000"); |
|
webkit_web_view_set_background_color(WEBKIT_WEB_VIEW(web_view), gdk_rgba_copy(&bg_color)); |
|
|
|
/* Maybe disable the context (right-click) menu. */ |
|
g_signal_connect(WEBKIT_WEB_VIEW(web_view), "context-menu", G_CALLBACK(context_menu_cb), NULL); |
|
|
|
/* There's no turning back now, let's go! */ |
|
gtk_container_add(GTK_CONTAINER(window), web_view); |
|
webkit_web_view_load_uri(WEBKIT_WEB_VIEW(web_view), |
|
g_strdup_printf("file://%s/%s/index.html", THEME_DIR, theme)); |
|
|
|
gtk_widget_show_all(window); |
|
gtk_widget_set_can_focus(GTK_WIDGET(web_view), TRUE); |
|
gtk_widget_grab_focus(GTK_WIDGET(web_view)); |
|
|
|
g_debug("Entering Gtk loop..."); |
|
gtk_main(); |
|
g_debug("Exited Gtk loop."); |
|
|
|
return 0; |
|
}
|
|
|