diff --git a/src/core/unix/SDL_uri_decode.c b/src/core/unix/SDL_uri_decode.c new file mode 100644 index 0000000000..dd60595864 --- /dev/null +++ b/src/core/unix/SDL_uri_decode.c @@ -0,0 +1,123 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2024 Sam Lantinga + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ + +#include "SDL_internal.h" + +#include "SDL_uri_decode.h" +#include +#include + +int SDL_URIDecode(const char *src, char *dst, int len) +{ + int ri, wi, di; + char decode = '\0'; + if (!src || !dst || len < 0) { + errno = EINVAL; + return -1; + } + if (len == 0) { + len = SDL_strlen(src); + } + for (ri = 0, wi = 0, di = 0; ri < len && wi < len; ri += 1) { + if (di == 0) { + /* start decoding */ + if (src[ri] == '%') { + decode = '\0'; + di += 1; + continue; + } + /* normal write */ + dst[wi] = src[ri]; + wi += 1; + continue; + } else if (di == 1 || di == 2) { + char off = '\0'; + char isa = src[ri] >= 'a' && src[ri] <= 'f'; + char isA = src[ri] >= 'A' && src[ri] <= 'F'; + char isn = src[ri] >= '0' && src[ri] <= '9'; + if (!(isa || isA || isn)) { + /* not a hexadecimal */ + int sri; + for (sri = ri - di; sri <= ri; sri += 1) { + dst[wi] = src[sri]; + wi += 1; + } + di = 0; + continue; + } + /* itsy bitsy magicsy */ + if (isn) { + off = 0 - '0'; + } else if (isa) { + off = 10 - 'a'; + } else if (isA) { + off = 10 - 'A'; + } + decode |= (src[ri] + off) << (2 - di) * 4; + if (di == 2) { + dst[wi] = decode; + wi += 1; + di = 0; + } else { + di += 1; + } + continue; + } + } + dst[wi] = '\0'; + return wi; +} + +int SDL_URIToLocal(const char *src, char *dst) +{ + if (SDL_memcmp(src, "file:/", 6) == 0) { + src += 6; /* local file? */ + } else if (SDL_strstr(src, ":/") != NULL) { + return -1; /* wrong scheme */ + } + + SDL_bool local = src[0] != '/' || (src[0] != '\0' && src[1] == '/'); + + /* got a hostname? */ + if (!local && src[0] == '/' && src[2] != '/') { + char *hostname_end = SDL_strchr(src + 1, '/'); + if (hostname_end) { + char hostname[257]; + if (gethostname(hostname, 255) == 0) { + hostname[256] = '\0'; + if (SDL_memcmp(src + 1, hostname, hostname_end - (src + 1)) == 0) { + src = hostname_end + 1; + local = SDL_TRUE; + } + } + } + } + if (local) { + /* Convert URI escape sequences to real characters */ + if (src[0] == '/') { + src++; + } else { + src--; + } + return SDL_URIDecode(src, dst, 0); + } + return -1; +} diff --git a/src/core/unix/SDL_uri_decode.h b/src/core/unix/SDL_uri_decode.h new file mode 100644 index 0000000000..fbe33f12b8 --- /dev/null +++ b/src/core/unix/SDL_uri_decode.h @@ -0,0 +1,56 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2024 Sam Lantinga + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ + +#include "SDL_internal.h" + +#ifndef SDL_uri_decode_h_ +#define SDL_uri_decode_h_ + +/* Decodes URI escape sequences in string src of len bytes + * (excluding the terminating NULL byte) into the dst buffer. + * Since URI-encoded characters take three times the space of + * normal characters, src and dst can safely point to the same + * buffer for in situ conversion. + * + * The buffer is guaranteed to be NULL-terminated, but + * may contain embedded NULL bytes. + * + * Returns the number of decoded bytes that wound up in + * the destination buffer, excluding the terminating NULL byte. + * + * On error, -1 is returned. + */ +int SDL_URIDecode(const char *src, char *dst, int len); + +/* Convert URI to a local filename, stripping the "file://" + * preamble and hostname if present, and writes the result + * to the dst buffer. Since URI-encoded characters take + * three times the space of normal characters, src and dst + * can safely point to the same buffer for in situ conversion. + * + * Returns the number of decoded bytes that wound up in + * the destination buffer, excluding the terminating NULL byte. + * + * On error, -1 is returned; + */ +int SDL_URIToLocal(const char *src, char *dst); + +#endif /* SDL_uri_decode_h_ */ diff --git a/src/video/wayland/SDL_waylandevents.c b/src/video/wayland/SDL_waylandevents.c index 802ba01ab2..693655b399 100644 --- a/src/video/wayland/SDL_waylandevents.c +++ b/src/video/wayland/SDL_waylandevents.c @@ -24,6 +24,7 @@ #ifdef SDL_VIDEO_DRIVER_WAYLAND #include "../../core/unix/SDL_poll.h" +#include "../../core/unix/SDL_uri_decode.h" #include "../../events/SDL_events_c.h" #include "../../events/SDL_scancode_tables_c.h" #include "../../core/linux/SDL_system_theme.h" @@ -89,8 +90,6 @@ struct SDL_WaylandTouchPoint static struct wl_list touch_points; -static char *Wayland_URIToLocal(char *uri); - static void touch_add(SDL_TouchID id, wl_fixed_t fx, wl_fixed_t fy, struct wl_surface *surface) { struct SDL_WaylandTouchPoint *tp = SDL_malloc(sizeof(struct SDL_WaylandTouchPoint)); @@ -2060,127 +2059,6 @@ static void data_device_handle_motion(void *data, struct wl_data_device *wl_data } } -/* Decodes URI escape sequences in string buf of len bytes - * (excluding the terminating NULL byte) in-place. Since - * URI-encoded characters take three times the space of - * normal characters, this should not be an issue. - * - * Returns the number of decoded bytes that wound up in - * the buffer, excluding the terminating NULL byte. - * - * The buffer is guaranteed to be NULL-terminated but - * may contain embedded NULL bytes. - * - * On error, -1 is returned. - * - * FIXME: This was shamelessly copied from SDL_x11events.c - */ -static int Wayland_URIDecode(char *buf, int len) -{ - int ri, wi, di; - char decode = '\0'; - if (!buf || len < 0) { - errno = EINVAL; - return -1; - } - if (len == 0) { - len = SDL_strlen(buf); - } - for (ri = 0, wi = 0, di = 0; ri < len && wi < len; ri += 1) { - if (di == 0) { - /* start decoding */ - if (buf[ri] == '%') { - decode = '\0'; - di += 1; - continue; - } - /* normal write */ - buf[wi] = buf[ri]; - wi += 1; - continue; - } else if (di == 1 || di == 2) { - char off = '\0'; - char isa = buf[ri] >= 'a' && buf[ri] <= 'f'; - char isA = buf[ri] >= 'A' && buf[ri] <= 'F'; - char isn = buf[ri] >= '0' && buf[ri] <= '9'; - if (!(isa || isA || isn)) { - /* not a hexadecimal */ - int sri; - for (sri = ri - di; sri <= ri; sri += 1) { - buf[wi] = buf[sri]; - wi += 1; - } - di = 0; - continue; - } - /* itsy bitsy magicsy */ - if (isn) { - off = 0 - '0'; - } else if (isa) { - off = 10 - 'a'; - } else if (isA) { - off = 10 - 'A'; - } - decode |= (buf[ri] + off) << (2 - di) * 4; - if (di == 2) { - buf[wi] = decode; - wi += 1; - di = 0; - } else { - di += 1; - } - continue; - } - } - buf[wi] = '\0'; - return wi; -} - -/* Convert URI to local filename - * return filename if possible, else NULL - * - * FIXME: This was shamelessly copied from SDL_x11events.c - */ -static char *Wayland_URIToLocal(char *uri) -{ - char *file = NULL; - SDL_bool local; - - if (SDL_memcmp(uri, "file:/", 6) == 0) { - uri += 6; /* local file? */ - } else if (SDL_strstr(uri, ":/") != NULL) { - return file; /* wrong scheme */ - } - - local = uri[0] != '/' || (uri[0] != '\0' && uri[1] == '/'); - - /* got a hostname? */ - if (!local && uri[0] == '/' && uri[2] != '/') { - char *hostname_end = SDL_strchr(uri + 1, '/'); - if (hostname_end) { - char hostname[257]; - if (gethostname(hostname, 255) == 0) { - hostname[256] = '\0'; - if (SDL_memcmp(uri + 1, hostname, hostname_end - (uri + 1)) == 0) { - uri = hostname_end + 1; - local = SDL_TRUE; - } - } - } - } - if (local) { - file = uri; - /* Convert URI escape sequences to real characters */ - Wayland_URIDecode(file, 0); - if (uri[1] == '/') { - file++; - } else { - file--; - } - } - return file; -} - static void data_device_handle_drop(void *data, struct wl_data_device *wl_data_device) { SDL_WaylandDataDevice *data_device = data; @@ -2226,9 +2104,8 @@ static void data_device_handle_drop(void *data, struct wl_data_device *wl_data_d char *saveptr = NULL; char *token = SDL_strtok_r((char *)buffer, "\r\n", &saveptr); while (token) { - char *fn = Wayland_URIToLocal(token); - if (fn) { - SDL_SendDropFile(data_device->dnd_window, NULL, fn); + if (SDL_URIToLocal(token, token) >= 0) { + SDL_SendDropFile(data_device->dnd_window, NULL, token); } token = SDL_strtok_r(NULL, "\r\n", &saveptr); } diff --git a/src/video/x11/SDL_x11events.c b/src/video/x11/SDL_x11events.c index decb47a93b..560d316ca2 100644 --- a/src/video/x11/SDL_x11events.c +++ b/src/video/x11/SDL_x11events.c @@ -36,6 +36,7 @@ #include "SDL_x11settings.h" #include "../SDL_clipboard_c.h" #include "../../core/unix/SDL_poll.h" +#include "../../core/unix/SDL_uri_decode.h" #include "../../events/SDL_events_c.h" #include "../../events/SDL_mouse_c.h" #include "../../events/SDL_touch_c.h" @@ -207,123 +208,6 @@ static SDL_bool X11_IsWheelEvent(Display *display, int button, int *xticks, int return SDL_FALSE; } -/* Decodes URI escape sequences in string buf of len bytes - (excluding the terminating NULL byte) in-place. Since - URI-encoded characters take three times the space of - normal characters, this should not be an issue. - - Returns the number of decoded bytes that wound up in - the buffer, excluding the terminating NULL byte. - - The buffer is guaranteed to be NULL-terminated but - may contain embedded NULL bytes. - - On error, -1 is returned. - */ -static int X11_URIDecode(char *buf, int len) -{ - int ri, wi, di; - char decode = '\0'; - if (!buf || len < 0) { - errno = EINVAL; - return -1; - } - if (len == 0) { - len = SDL_strlen(buf); - } - for (ri = 0, wi = 0, di = 0; ri < len && wi < len; ri += 1) { - if (di == 0) { - /* start decoding */ - if (buf[ri] == '%') { - decode = '\0'; - di += 1; - continue; - } - /* normal write */ - buf[wi] = buf[ri]; - wi += 1; - continue; - } else if (di == 1 || di == 2) { - char off = '\0'; - char isa = buf[ri] >= 'a' && buf[ri] <= 'f'; - char isA = buf[ri] >= 'A' && buf[ri] <= 'F'; - char isn = buf[ri] >= '0' && buf[ri] <= '9'; - if (!(isa || isA || isn)) { - /* not a hexadecimal */ - int sri; - for (sri = ri - di; sri <= ri; sri += 1) { - buf[wi] = buf[sri]; - wi += 1; - } - di = 0; - continue; - } - /* itsy bitsy magicsy */ - if (isn) { - off = 0 - '0'; - } else if (isa) { - off = 10 - 'a'; - } else if (isA) { - off = 10 - 'A'; - } - decode |= (buf[ri] + off) << (2 - di) * 4; - if (di == 2) { - buf[wi] = decode; - wi += 1; - di = 0; - } else { - di += 1; - } - continue; - } - } - buf[wi] = '\0'; - return wi; -} - -/* Convert URI to local filename - return filename if possible, else NULL -*/ -static char *X11_URIToLocal(char *uri) -{ - char *file = NULL; - SDL_bool local; - - if (SDL_memcmp(uri, "file:/", 6) == 0) { - uri += 6; /* local file? */ - } else if (SDL_strstr(uri, ":/") != NULL) { - return file; /* wrong scheme */ - } - - local = uri[0] != '/' || (uri[0] != '\0' && uri[1] == '/'); - - /* got a hostname? */ - if (!local && uri[0] == '/' && uri[2] != '/') { - char *hostname_end = SDL_strchr(uri + 1, '/'); - if (hostname_end) { - char hostname[257]; - if (gethostname(hostname, 255) == 0) { - hostname[256] = '\0'; - if (SDL_memcmp(uri + 1, hostname, hostname_end - (uri + 1)) == 0) { - uri = hostname_end + 1; - local = SDL_TRUE; - } - } - } - } - if (local) { - file = uri; - /* Convert URI escape sequences to real characters */ - X11_URIDecode(file, 0); - if (uri[1] == '/') { - file++; - } else { - file--; - } - } - return file; -} - /* An X11 event hook */ static SDL_X11EventHook g_X11EventHook = NULL; static void *g_X11EventHookData = NULL; @@ -1886,9 +1770,8 @@ static void X11_DispatchEvent(SDL_VideoDevice *_this, XEvent *xevent) if (SDL_strcmp("text/plain", name) == 0) { SDL_SendDropText(data->window, token); } else if (SDL_strcmp("text/uri-list", name) == 0) { - char *fn = X11_URIToLocal(token); - if (fn) { - SDL_SendDropFile(data->window, NULL, fn); + if (SDL_URIToLocal(token, token) >= 0) { + SDL_SendDropFile(data->window, NULL, token); } } token = SDL_strtok_r(NULL, "\r\n", &saveptr);