GHOST/Wayland: support URL decoding for file drag & drop

Paths that contained characters that needed escaping as URL's failed
to import.

Move URL decoding to a new file (GHOST_PathUtils), shared with X11 but
maybe be useful for other platforms too.
This commit is contained in:
Campbell Barton 2022-08-04 22:33:23 +10:00
parent 727cc426bc
commit a95103f6f4
Notes: blender-bot 2023-02-14 07:18:54 +01:00
Referenced by commit c67d4117d4, Fix broken build due to missing include.
Referenced by issue #99737, Drag and drop does not work with the wayland backend
6 changed files with 130 additions and 90 deletions

View File

@ -26,6 +26,7 @@ set(SRC
intern/GHOST_ISystemPaths.cpp
intern/GHOST_ModifierKeys.cpp
intern/GHOST_Path-api.cpp
intern/GHOST_PathUtils.cpp
intern/GHOST_Rect.cpp
intern/GHOST_System.cpp
intern/GHOST_TimerManager.cpp
@ -60,6 +61,7 @@ set(SRC
intern/GHOST_EventTrackpad.h
intern/GHOST_EventWheel.h
intern/GHOST_ModifierKeys.h
intern/GHOST_PathUtils.h
intern/GHOST_System.h
intern/GHOST_SystemPaths.h
intern/GHOST_TimerManager.h

View File

@ -97,89 +97,10 @@ GHOST_DropTargetX11::~GHOST_DropTargetX11()
}
}
/* Based on: https://stackoverflow.com/a/2766963/432509 */
using DecodeState_e = enum DecodeState_e {
/** Searching for an ampersand to convert. */
STATE_SEARCH = 0,
/** Convert the two proceeding characters from hex. */
STATE_CONVERTING
};
void GHOST_DropTargetX11::UrlDecode(char *decodedOut, int bufferSize, const char *encodedIn)
{
unsigned int i;
unsigned int len = strlen(encodedIn);
DecodeState_e state = STATE_SEARCH;
int j;
unsigned int asciiCharacter;
char tempNumBuf[3] = {0};
bool bothDigits = true;
memset(decodedOut, 0, bufferSize);
for (i = 0; i < len; ++i) {
switch (state) {
case STATE_SEARCH:
if (encodedIn[i] != '%') {
strncat(decodedOut, &encodedIn[i], 1);
assert((int)strlen(decodedOut) < bufferSize);
break;
}
/* We are now converting */
state = STATE_CONVERTING;
break;
case STATE_CONVERTING:
bothDigits = true;
/* Create a buffer to hold the hex. For example, if %20, this
* buffer would hold 20 (in ASCII) */
memset(tempNumBuf, 0, sizeof(tempNumBuf));
/* Conversion complete (i.e. don't convert again next iter) */
state = STATE_SEARCH;
strncpy(tempNumBuf, &encodedIn[i], 2);
/* Ensure both characters are hexadecimal */
for (j = 0; j < 2; ++j) {
if (!isxdigit(tempNumBuf[j])) {
bothDigits = false;
}
}
if (!bothDigits) {
break;
}
/* Convert two hexadecimal characters into one character */
sscanf(tempNumBuf, "%x", &asciiCharacter);
/* Ensure we aren't going to overflow */
assert((int)strlen(decodedOut) < bufferSize);
/* Concatenate this character onto the output */
strncat(decodedOut, (char *)&asciiCharacter, 1);
/* Skip the next character */
i++;
break;
}
}
}
char *GHOST_DropTargetX11::FileUrlDecode(char *fileUrl)
{
if (strncmp(fileUrl, "file://", 7) == 0) {
/* assume one character of encoded URL can be expanded to 4 chars max */
int decodedSize = 4 * strlen(fileUrl) + 1;
char *decodedPath = (char *)malloc(decodedSize);
UrlDecode(decodedPath, decodedSize, fileUrl + 7);
return decodedPath;
return GHOST_URL_decode_alloc(fileUrl + 7);
}
return nullptr;

View File

@ -64,14 +64,6 @@ class GHOST_DropTargetX11 {
*/
void *getURIListGhostData(unsigned char *dropBuffer, int dropBufferSize);
/**
* Decode URL (i.e. converts `file:///a%20b/test` to `file:///a b/test`)
* \param decodedOut: - buffer for decoded URL.
* \param bufferSize: - size of output buffer.
* \param encodedIn: - input encoded buffer to be decoded.
*/
void UrlDecode(char *decodedOut, int bufferSize, const char *encodedIn);
/**
* Fully decode file URL (i.e. converts `file:///a%20b/test` to `/a b/test`)
* \param fileUrl: - file path URL to be fully decoded.

View File

@ -0,0 +1,101 @@
/* SPDX-License-Identifier: GPL-2.0-or-later
* Copyright 2010 Blender Foundation. All rights reserved. */
/** \file
* \ingroup GHOST
*/
#include <cassert>
#include <cctype>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include "GHOST_PathUtils.h"
#include "GHOST_Types.h"
/* Based on: https://stackoverflow.com/a/2766963/432509 */
using DecodeState_e = enum DecodeState_e {
/** Searching for an ampersand to convert. */
STATE_SEARCH = 0,
/** Convert the two proceeding characters from hex. */
STATE_CONVERTING
};
void GHOST_URL_decode(char *buf_dst, int buf_dst_size, const char *buf_src)
{
const unsigned int buf_src_len = strlen(buf_src);
DecodeState_e state = STATE_SEARCH;
unsigned int ascii_character;
char temp_num_buf[3] = {0};
memset(buf_dst, 0, buf_dst_size);
for (unsigned int i = 0; i < buf_src_len; i++) {
switch (state) {
case STATE_SEARCH: {
if (buf_src[i] != '%') {
strncat(buf_dst, &buf_src[i], 1);
assert((int)strlen(buf_dst) < buf_dst_size);
break;
}
/* We are now converting. */
state = STATE_CONVERTING;
break;
}
case STATE_CONVERTING: {
bool both_digits = true;
/* Create a buffer to hold the hex. For example, if `%20`,
* this buffer would hold 20 (in ASCII). */
memset(temp_num_buf, 0, sizeof(temp_num_buf));
/* Conversion complete (i.e. don't convert again next iteration). */
state = STATE_SEARCH;
strncpy(temp_num_buf, &buf_src[i], 2);
/* Ensure both characters are hexadecimal. */
for (int j = 0; j < 2; j++) {
if (!isxdigit(temp_num_buf[j])) {
both_digits = false;
}
}
if (!both_digits) {
break;
}
/* Convert two hexadecimal characters into one character. */
sscanf(temp_num_buf, "%x", &ascii_character);
/* Ensure we aren't going to overflow. */
assert((int)strlen(buf_dst) < buf_dst_size);
/* Concatenate this character onto the output. */
strncat(buf_dst, (char *)&ascii_character, 1);
/* Skip the next character. */
i++;
break;
}
}
}
}
char *GHOST_URL_decode_alloc(const char *buf_src)
{
/* Assume one character of encoded URL can be expanded to 4 chars max. */
const size_t decoded_size_max = 4 * strlen(buf_src) + 1;
char *buf_dst = (char *)malloc(decoded_size_max);
GHOST_URL_decode(buf_dst, decoded_size_max, buf_src);
const size_t decoded_size = strlen(buf_dst) + 1;
if (decoded_size != decoded_size_max) {
char *buf_dst_trim = (char *)malloc(decoded_size);
memcpy(buf_dst_trim, buf_dst, decoded_size);
free(buf_dst);
buf_dst = buf_dst_trim;
}
return buf_dst;
}

View File

@ -0,0 +1,24 @@
/* SPDX-License-Identifier: GPL-2.0-or-later
* Copyright 2010 Blender Foundation. All rights reserved. */
/** \file
* \ingroup GHOST
*/
#pragma once
/**
* Decode URL (i.e. converts `file:///a%20b/test` to `file:///a b/test`)
*
* \param buf_dst: Buffer for decoded URL.
* \param buf_dst_maxlen: Size of output buffer.
* \param buf_src: Input encoded buffer to be decoded.
*/
void GHOST_URL_decode(char *buf_dst, int buf_dst_size, const char *buf_src);
/**
* A version of #GHOST_URL_decode that allocates the string & returns it.
*
* \param buf_src: Input encoded buffer to be decoded.
* \return The decoded output buffer.
*/
char *GHOST_URL_decode_alloc(const char *buf_src);

View File

@ -11,6 +11,7 @@
#include "GHOST_EventDragnDrop.h"
#include "GHOST_EventKey.h"
#include "GHOST_EventWheel.h"
#include "GHOST_PathUtils.h"
#include "GHOST_TimerManager.h"
#include "GHOST_WindowManager.h"
#include "GHOST_utildefines.h"
@ -1257,8 +1258,7 @@ static void data_device_handle_drop(void *data, struct wl_data_device * /*wl_dat
flist->count = int(uris.size());
flist->strings = static_cast<uint8_t **>(malloc(uris.size() * sizeof(uint8_t *)));
for (size_t i = 0; i < uris.size(); i++) {
flist->strings[i] = static_cast<uint8_t *>(malloc((uris[i].size() + 1) * sizeof(uint8_t)));
memcpy(flist->strings[i], uris[i].data(), uris[i].size() + 1);
flist->strings[i] = (uint8_t *)GHOST_URL_decode_alloc(uris[i].c_str());
}
CLOG_INFO(LOG, 2, "drop_read_uris_fn file_count=%d", flist->count);