From d8ba203f6eab29af6f178183c55315b6514299a8 Mon Sep 17 00:00:00 2001 From: lbonn Date: Wed, 6 Mar 2024 09:56:02 +0100 Subject: [PATCH] [Wayland] Handle clipboard pasting more securely Only receive clipboard offers when pasting instead of storing the data indefinitely. This is also more performant by default as it is not doing unnecessary work. --- include/display-internal.h | 3 +- include/display.h | 3 +- include/wayland-internal.h | 7 ++- source/display.c | 6 +-- source/view.c | 22 +++++--- source/wayland/display.c | 101 ++++++++++++++++--------------------- 6 files changed, 70 insertions(+), 72 deletions(-) diff --git a/include/display-internal.h b/include/display-internal.h index 5d8001b4b..9cf4f08ef 100644 --- a/include/display-internal.h +++ b/include/display-internal.h @@ -28,6 +28,7 @@ #ifndef ROFI_DISPLAY_INTERNAL_H #define ROFI_DISPLAY_INTERNAL_H +#include "display.h" #include "helper.h" #include "nkutils-bindings.h" #include @@ -48,7 +49,7 @@ typedef struct _display_proxy { void (*set_input_focus)(guint window); void (*revert_input_focus)(void); - char *(*get_clipboard_data)(int type); + void (*get_clipboard_data)(int type, ClipboardCb callback, void *user_data); void (*set_fullscreen_mode)(void); guint (*scale)(void); diff --git a/include/display.h b/include/display.h index 4c1c185f3..146240a50 100644 --- a/include/display.h +++ b/include/display.h @@ -127,7 +127,8 @@ enum clipboard_type { CLIPBOARD_PRIMARY, }; -char *display_get_clipboard_data(enum clipboard_type); +typedef void (* ClipboardCb)(char *clipboard_data, void *user_data); +void display_get_clipboard_data(enum clipboard_type, ClipboardCb callback, void* user_data); void display_set_fullscreen_mode(void); diff --git a/include/wayland-internal.h b/include/wayland-internal.h index e6d248cec..2a5e173d3 100644 --- a/include/wayland-internal.h +++ b/include/wayland-internal.h @@ -34,6 +34,10 @@ typedef struct { typedef struct _wayland_seat wayland_seat; +typedef struct { + void *offer; +} clipboard_data; + typedef struct { GMainLoop *main_loop; GWaterWaylandSource *main_loop_source; @@ -77,8 +81,7 @@ typedef struct { int32_t scale; NkBindingsSeat *bindings_seat; - char *clipboard_default_data; - char *clipboard_primary_data; + clipboard_data clipboards[2]; uint32_t layer_width; uint32_t layer_height; diff --git a/source/display.c b/source/display.c index cd290e059..8d69f4173 100644 --- a/source/display.c +++ b/source/display.c @@ -1,8 +1,8 @@ #include "keyb.h" #include -#include "display-internal.h" #include "display.h" +#include "display-internal.h" #include "view.h" @@ -41,8 +41,8 @@ void display_startup_notification(RofiHelperExecuteContext *context, guint display_scale(void) { return proxy->scale(); } -char *display_get_clipboard_data(enum clipboard_type type) { - return proxy->get_clipboard_data(type); +void display_get_clipboard_data(enum clipboard_type type, ClipboardCb callback, void* user_data) { + proxy->get_clipboard_data(type, callback, user_data); } void display_set_fullscreen_mode(void) { proxy->set_fullscreen_mode(); } diff --git a/source/view.c b/source/view.c index 559ada990..5294be0f1 100644 --- a/source/view.c +++ b/source/view.c @@ -921,6 +921,18 @@ static void rofi_view_input_changed(void) { } } +#ifdef ENABLE_WAYLAND +static void rofi_view_clipboard_callback(char *clipboard_data, G_GNUC_UNUSED void *user_data) { + RofiViewState *state = rofi_view_get_active(); + if (clipboard_data != NULL) { + if (state != NULL) { + rofi_view_handle_text(state, clipboard_data); + } + g_free(clipboard_data); + } +} +#endif + static void rofi_view_trigger_global_action(KeyBindingAction action) { RofiViewState *state = rofi_view_get_active(); switch (action) { @@ -936,10 +948,7 @@ static void rofi_view_trigger_global_action(KeyBindingAction action) { #endif #ifdef ENABLE_WAYLAND if (config.backend == DISPLAY_WAYLAND) { - char *d = display_get_clipboard_data(CLIPBOARD_PRIMARY); - if (d != NULL) { - rofi_view_handle_text(current_active_menu, d); - } + display_get_clipboard_data(CLIPBOARD_PRIMARY, rofi_view_clipboard_callback, NULL); } #endif break; @@ -954,10 +963,7 @@ static void rofi_view_trigger_global_action(KeyBindingAction action) { #endif #ifdef ENABLE_WAYLAND if (config.backend == DISPLAY_WAYLAND) { - char *d = display_get_clipboard_data(CLIPBOARD_DEFAULT); - if (d != NULL) { - rofi_view_handle_text(current_active_menu, d); - } + display_get_clipboard_data(CLIPBOARD_DEFAULT, rofi_view_clipboard_callback, NULL); } #endif break; diff --git a/source/wayland/display.c b/source/wayland/display.c index 6cdc7d2f4..65058d12e 100644 --- a/source/wayland/display.c +++ b/source/wayland/display.c @@ -885,13 +885,12 @@ static void wayland_keyboard_release(wayland_seat *self) { #define CLIPBOARD_READ_INCREMENT 1024 -typedef void (*clipboard_read_callback)(char *data); - struct clipboard_read_info { char *buffer; size_t size; int fd; - clipboard_read_callback callback; + ClipboardCb callback; + void *user_data; }; static gboolean clipboard_read_glib_callback(GIOChannel *channel, @@ -900,8 +899,9 @@ static gboolean clipboard_read_glib_callback(GIOChannel *channel, struct clipboard_read_info *info = opaque; gsize read; - switch (g_io_channel_read_chars(channel, info->buffer + info->size, - CLIPBOARD_READ_INCREMENT, &read, NULL)) { + GIOStatus status = g_io_channel_read_chars(channel, info->buffer + info->size, + CLIPBOARD_READ_INCREMENT, &read, NULL); + switch (status) { case G_IO_STATUS_AGAIN: return TRUE; @@ -921,7 +921,12 @@ static gboolean clipboard_read_glib_callback(GIOChannel *channel, default: info->buffer[info->size] = '\0'; - info->callback(info->buffer); + if (status == G_IO_STATUS_EOF) { + info->callback(info->buffer, info->user_data); + } else { // G_IO_STATUS_ERROR + g_warning("Could not read data from clipboard"); + g_free(info->buffer); + } g_io_channel_shutdown(channel, FALSE, NULL); g_io_channel_unref(channel); close(info->fd); @@ -930,7 +935,7 @@ static gboolean clipboard_read_glib_callback(GIOChannel *channel, } } -static gboolean clipboard_read_data(int fd, clipboard_read_callback callback) { +static gboolean clipboard_read_data(int fd, ClipboardCb callback, void *user_data) { GIOChannel *channel = g_io_channel_unix_new(fd); struct clipboard_read_info *info = g_malloc(sizeof *info); @@ -943,6 +948,7 @@ static gboolean clipboard_read_data(int fd, clipboard_read_callback callback) { info->fd = fd; info->size = 0; info->callback = callback; + info->user_data = user_data; info->buffer = g_malloc(CLIPBOARD_READ_INCREMENT); if (info->buffer == NULL) { @@ -980,32 +986,24 @@ static void data_device_handle_data_offer(void *data, wl_data_offer_add_listener(offer, &data_offer_listener, NULL); } -static void clipboard_default_callback(char *data) { - if (wayland->clipboard_default_data != NULL) { - g_free(wayland->clipboard_default_data); +static void clipboard_handle_selection(enum clipboard_type cb_type, void *offer) { + clipboard_data *clipboard = &wayland->clipboards[cb_type]; + + if (clipboard->offer != NULL) { + if (cb_type == CLIPBOARD_DEFAULT) { + wl_data_offer_destroy(clipboard->offer); + } else { + zwp_primary_selection_offer_v1_destroy(clipboard->offer); + } } - wayland->clipboard_default_data = data; + clipboard->offer = offer; + } static void data_device_handle_selection(void *data, struct wl_data_device *data_device, struct wl_data_offer *offer) { - if (offer == NULL) { - // clipboard is empty - return; - } - - int fds[2]; - if (pipe(fds) < 0) { - return; - } - wl_data_offer_receive(offer, "text/plain", fds[1]); - close(fds[1]); - - wl_display_roundtrip(wayland->display); - - clipboard_read_data(fds[0], clipboard_default_callback); - wl_data_offer_destroy(offer); + clipboard_handle_selection(CLIPBOARD_DEFAULT, offer); } static const struct wl_data_device_listener data_device_listener = { @@ -1030,32 +1028,10 @@ static void primary_selection_device_handle_data_offer( offer, &primary_selection_offer_listener, NULL); } -static void clipboard_primary_callback(char *data) { - if (wayland->clipboard_primary_data != NULL) { - g_free(wayland->clipboard_primary_data); - } - wayland->clipboard_primary_data = data; -} - static void primary_selection_device_handle_selection( void *data, struct zwp_primary_selection_device_v1 *data_device, struct zwp_primary_selection_offer_v1 *offer) { - if (offer == NULL) { - // clipboard is empty - return; - } - - int fds[2]; - if (pipe(fds) < 0) { - return; - } - zwp_primary_selection_offer_v1_receive(offer, "text/plain", fds[1]); - close(fds[1]); - - wl_display_roundtrip(wayland->display); - - clipboard_read_data(fds[0], clipboard_primary_callback); - zwp_primary_selection_offer_v1_destroy(offer); + clipboard_handle_selection(CLIPBOARD_PRIMARY, offer); } static const struct zwp_primary_selection_device_v1_listener @@ -1748,15 +1724,26 @@ static const struct _view_proxy *wayland_display_view_proxy(void) { static guint wayland_display_scale(void) { return wayland->scale; } -static char *wayland_get_clipboard_data(int type) { - switch (type) { - case CLIPBOARD_DEFAULT: - return wayland->clipboard_default_data; - case CLIPBOARD_PRIMARY: - return wayland->clipboard_primary_data; +static void wayland_get_clipboard_data(int cb_type, ClipboardCb callback, void *user_data) { + clipboard_data *clipboard = &wayland->clipboards[cb_type]; + + if (clipboard->offer == NULL) { + return; } - return NULL; + int fds[2]; + if (pipe(fds) < 0) { + return; + } + + if (cb_type == CLIPBOARD_DEFAULT) { + wl_data_offer_receive(clipboard->offer, "text/plain", fds[1]); + } else { + zwp_primary_selection_offer_v1_receive(clipboard->offer, "text/plain", fds[1]); + } + close(fds[1]); + + clipboard_read_data(fds[0], callback, user_data); } static void wayland_set_fullscreen_mode(void) {