From 9a2bbd8acbebcde56dd2f89ebca3b196c2b38914 Mon Sep 17 00:00:00 2001 From: Ethan Lee Date: Wed, 12 Jan 2022 13:01:05 -0500 Subject: [PATCH] wayland: Convert URI to local path for DropFile --- src/video/wayland/SDL_waylandevents.c | 149 ++++++++++++++++++++++---- 1 file changed, 129 insertions(+), 20 deletions(-) diff --git a/src/video/wayland/SDL_waylandevents.c b/src/video/wayland/SDL_waylandevents.c index dfa62ec69..bf2aeea5b 100644 --- a/src/video/wayland/SDL_waylandevents.c +++ b/src/video/wayland/SDL_waylandevents.c @@ -1312,36 +1312,145 @@ data_device_handle_motion(void *data, struct wl_data_device *wl_data_device, { } +/* 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 == NULL || 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 != NULL) { + 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; - void *buffer = NULL; - size_t length = 0; - - const char *current_uri = NULL; - const char *last_char = NULL; - char *current_char = NULL; if (data_device->drag_offer != NULL) { /* TODO: SDL Support more mime types */ - buffer = Wayland_data_offer_receive(data_device->drag_offer, - &length, FILE_MIME, SDL_FALSE); - - /* uri-list */ - current_uri = (const char *)buffer; - last_char = (const char *)buffer + length; - for (current_char = buffer; current_char < last_char; ++current_char) { - if (*current_char == '\n' || *current_char == 0) { - if (*current_uri != 0 && *current_uri != '#') { - *current_char = 0; - SDL_SendDropFile(NULL, current_uri); + size_t length; + void *buffer = Wayland_data_offer_receive(data_device->drag_offer, + &length, FILE_MIME, SDL_FALSE); + if (buffer) { + char *saveptr = NULL; + char *token = SDL_strtokr((char *) buffer, "\r\n", &saveptr); + while (token != NULL) { + char *fn = Wayland_URIToLocal(token); + if (fn) { + SDL_SendDropFile(NULL, fn); /* FIXME: Window? */ } - current_uri = (const char *)current_char + 1; + token = SDL_strtokr(NULL, "\r\n", &saveptr); } + SDL_SendDropComplete(NULL); /* FIXME: Window? */ + SDL_free(buffer); } - - SDL_free(buffer); } }