From c3cacb470430f3a19db8e2442f1ed61887044373 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Wed, 9 Sep 2020 18:44:49 +0200 Subject: [PATCH 1/4] selection: add selection__unset() These functions clear the current selection. --- selection.c | 41 +++++++++++++++++++++++++++++++++++++++++ selection.h | 3 +++ 2 files changed, 44 insertions(+) diff --git a/selection.c b/selection.c index ed8d061b..b423fcca 100644 --- a/selection.c +++ b/selection.c @@ -661,6 +661,47 @@ selection_cancel(struct terminal *term) term->selection.ongoing = false; } +void +selection_clipboard_unset(struct seat *seat) +{ + struct wl_clipboard *clipboard = &seat->clipboard; + + if (clipboard->data_source == NULL) + return; + + /* Kill previous data source */ + assert(clipboard->serial != 0); + wl_data_device_set_selection(seat->data_device, NULL, clipboard->serial); + wl_data_source_destroy(clipboard->data_source); + + clipboard->data_source = NULL; + clipboard->serial = 0; + + free(clipboard->text); + clipboard->text = NULL; +} + +void +selection_primary_unset(struct seat *seat) +{ + struct wl_primary *primary = &seat->primary; + + if (primary->data_source == NULL) + return; + + assert(primary->serial != 0); + zwp_primary_selection_device_v1_set_selection( + seat->primary_selection_device, NULL, primary->serial); + zwp_primary_selection_source_v1_destroy(primary->data_source); + free(primary->text); + + primary->data_source = NULL; + primary->serial = 0; + + free(primary->text); + primary->text = NULL; +} + void selection_mark_word(struct seat *seat, struct terminal *term, int col, int row, bool spaces_only, uint32_t serial) diff --git a/selection.h b/selection.h index 639c36e9..178a990b 100644 --- a/selection.h +++ b/selection.h @@ -30,6 +30,9 @@ void selection_mark_word( void selection_mark_row( struct seat *seat, struct terminal *term, int row, uint32_t serial); +void selection_clipboard_unset(struct seat *seat); +void selection_primary_unset(struct seat *seat); + char *selection_to_text(const struct terminal *term); void selection_to_clipboard( struct seat *seat, struct terminal *term, uint32_t serial); From dec6f963cbedb093370f8d9559e357e72a31df90 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Wed, 9 Sep 2020 18:45:10 +0200 Subject: [PATCH 2/4] selection: add selection__has_data() Returns true when there is data available for paste in the clipboard/primary selection. --- selection.c | 12 ++++++++++++ selection.h | 3 +++ 2 files changed, 15 insertions(+) diff --git a/selection.c b/selection.c index b423fcca..f1129b03 100644 --- a/selection.c +++ b/selection.c @@ -661,6 +661,18 @@ selection_cancel(struct terminal *term) term->selection.ongoing = false; } +bool +selection_clipboard_has_data(const struct seat *seat) +{ + return seat->clipboard.data_offer != NULL; +} + +bool +selection_primary_has_data(const struct seat *seat) +{ + return seat->primary.data_offer != NULL; +} + void selection_clipboard_unset(struct seat *seat) { diff --git a/selection.h b/selection.h index 178a990b..547da2fc 100644 --- a/selection.h +++ b/selection.h @@ -33,6 +33,9 @@ void selection_mark_row( void selection_clipboard_unset(struct seat *seat); void selection_primary_unset(struct seat *seat); +bool selection_clipboard_has_data(const struct seat *seat); +bool selection_primary_has_data(const struct seat *seat); + char *selection_to_text(const struct terminal *term); void selection_to_clipboard( struct seat *seat, struct terminal *term, uint32_t serial); From 776b831d89ff873ce1daf4e65e5eb13e808914a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Wed, 9 Sep 2020 18:46:39 +0200 Subject: [PATCH 3/4] osc52: unset (clear) selection when an invalid payload is received --- CHANGELOG.md | 2 ++ osc.c | 27 ++++++++++++++++----------- 2 files changed, 18 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 73028676..e1853345 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -112,6 +112,8 @@ * Handling of multi-column composed characters while reflowing. * Escape sequences sent for key combinations with `Return`, that did **not** include `Alt`. +* Clipboard (or primary selection) is now cleared when receiving an + OSC-52 command with an invalid base64 encoded payload. ### Security diff --git a/osc.c b/osc.c index 50d6ab3b..c04c8069 100644 --- a/osc.c +++ b/osc.c @@ -23,17 +23,6 @@ static void osc_to_clipboard(struct terminal *term, const char *target, const char *base64_data) { - char *decoded = base64_decode(base64_data); - if (decoded == NULL) { - if (errno == EINVAL) - LOG_WARN("OSC: invalid clipboard data: %s", base64_data); - else - LOG_ERRNO("base64_decode() failed"); - return; - } - - LOG_DBG("decoded: %s", decoded); - bool to_clipboard = false; bool to_primary = false; @@ -71,6 +60,22 @@ osc_to_clipboard(struct terminal *term, const char *target, return; } + char *decoded = base64_decode(base64_data); + if (decoded == NULL) { + if (errno == EINVAL) + LOG_WARN("OSC: invalid clipboard data: %s", base64_data); + else + LOG_ERRNO("base64_decode() failed"); + + if (to_clipboard) + selection_clipboard_unset(seat); + if (to_primary) + selection_primary_unset(seat); + return; + } + + LOG_DBG("decoded: %s", decoded); + if (to_clipboard) { char *copy = xstrdup(decoded); if (!text_to_clipboard(seat, term, copy, seat->kbd.serial)) From 419bd87098a068f398db10d9c5cab5e40a01a2ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Wed, 9 Sep 2020 18:46:58 +0200 Subject: [PATCH 4/4] osc52: use first source that actually *has* data --- osc.c | 52 ++++++++++++++++++++++++++++++++-------------------- 1 file changed, 32 insertions(+), 20 deletions(-) diff --git a/osc.c b/osc.c index c04c8069..7e22d7c1 100644 --- a/osc.c +++ b/osc.c @@ -162,20 +162,6 @@ from_clipboard_done(void *user) static void osc_from_clipboard(struct terminal *term, const char *source) { - /* Use clipboard if no source has been specified */ - char src = source[0] == '\0' ? 'c' : 0; - - for (const char *s = source; *s != '\0'; s++) { - if (*s == 'c' || *s == 'p' || *s == 's') { - src = *s; - break; - } else - LOG_WARN("unimplemented: clipboard source '%c'", *s); - } - - if (src == 0) - return; - /* Find a seat in which the terminal has focus */ struct seat *seat = NULL; tll_foreach(term->wl->seats, it) { @@ -190,6 +176,35 @@ osc_from_clipboard(struct terminal *term, const char *source) return; } + /* Use clipboard if no source has been specified */ + char src = source[0] == '\0' ? 'c' : 0; + bool from_clipboard = src == 'c'; + bool from_primary = false; + + for (const char *s = source; + *s != '\0' && !from_clipboard && !from_primary; + s++) + { + if (*s == 'c' || *s == 'p' || *s == 's') { + src = *s; + + switch (src) { + case 'c': + from_clipboard = selection_clipboard_has_data(seat); + break; + + case 's': + case 'p': + from_primary = selection_primary_has_data(seat); + break; + } + } else + LOG_WARN("unimplemented: clipboard source '%c'", *s); + } + + if (!from_clipboard && !from_primary) + return; + term_to_slave(term, "\033]52;", 5); term_to_slave(term, &src, 1); term_to_slave(term, ";", 1); @@ -197,17 +212,14 @@ osc_from_clipboard(struct terminal *term, const char *source) struct clip_context *ctx = xmalloc(sizeof(*ctx)); *ctx = (struct clip_context) {.seat = seat, .term = term}; - switch (src) { - case 'c': + if (from_clipboard) { text_from_clipboard( seat, term, &from_clipboard_cb, &from_clipboard_done, ctx); - break; + } - case 's': - case 'p': + if (from_primary) { text_from_primary( seat, term, &from_clipboard_cb, &from_clipboard_done, ctx); - break; } }