Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/wolfpld/tracy.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/nfd
diff options
context:
space:
mode:
authorBartosz Taudul <wolf@nereid.pl>2022-07-17 17:22:27 +0300
committerBartosz Taudul <wolf@nereid.pl>2022-07-17 17:22:27 +0300
commitd6a607b9f0202ae852a645552037c10c952b8eab (patch)
treed40b88e78ff6f6dcdfc1d4bca5c4292fdeb79420 /nfd
parent85102f7a194b5b0a3438a06072e0d23f99a40499 (diff)
Update nativefiledialog-extended to 3311592.
Diffstat (limited to 'nfd')
-rw-r--r--nfd/nfd_cocoa.m49
-rw-r--r--nfd/nfd_portal.cpp171
2 files changed, 141 insertions, 79 deletions
diff --git a/nfd/nfd_cocoa.m b/nfd/nfd_cocoa.m
index a2968133..f7024356 100644
--- a/nfd/nfd_cocoa.m
+++ b/nfd/nfd_cocoa.m
@@ -9,17 +9,23 @@
#include <Availability.h>
#include "nfd.h"
-// At least one of NFD_NEEDS_ALLOWEDCONTENTTYPES and NFD_NEEDS_ALLOWEDFILETYPES will be defined
-#if defined(__MAC_OS_X_VERSION_MAX_ALLOWED) && defined(__MAC_12_0) && \
- __MAC_OS_X_VERSION_MAX_ALLOWED >= __MAC_12_0
-#include <UniformTypeIdentifiers/UniformTypeIdentifiers.h>
-#define NFD_NEEDS_ALLOWEDCONTENTTYPES
-#if !defined(__MAC_OS_X_VERSION_MIN_ALLOWED) || !defined(__MAC_12_0) || \
- __MAC_OS_X_VERSION_MIN_ALLOWED < __MAC_12_0
-#define NFD_NEEDS_ALLOWEDFILETYPES
-#endif
+// MacOS is deprecating the allowedFileTypes property in favour of allowedContentTypes, so we have
+// to introduce this breaking change. Define NFD_MACOS_ALLOWEDCONTENTTYPES to 1 to have it set the
+// allowedContentTypes property of the SavePanel or OpenPanel. Define
+// NFD_MACOS_ALLOWEDCONTENTTYPES to 0 to have it set the allowedFileTypes property of the SavePanel
+// or OpenPanel. If NFD_MACOS_ALLOWEDCONTENTTYPES is undefined, then it will set it to 1 if
+// __MAC_OS_X_VERSION_MIN_REQUIRED >= 11.0, and 0 otherwise.
+#if !defined(NFD_MACOS_ALLOWEDCONTENTTYPES)
+#if !defined(__MAC_OS_X_VERSION_MIN_REQUIRED) || !defined(__MAC_11_0) || \
+ __MAC_OS_X_VERSION_MIN_REQUIRED < __MAC_11_0
+#define NFD_MACOS_ALLOWEDCONTENTTYPES 0
#else
-#define NFD_NEEDS_ALLOWEDFILETYPES
+#define NFD_MACOS_ALLOWEDCONTENTTYPES 1
+#endif
+#endif
+
+#if NFD_MACOS_ALLOWEDCONTENTTYPES == 1
+#include <UniformTypeIdentifiers/UniformTypeIdentifiers.h>
#endif
static const char* g_errorstr = NULL;
@@ -40,7 +46,7 @@ static void NFDi_Free(void* ptr) {
free(ptr);
}
-#if defined(NFD_NEEDS_ALLOWEDCONTENTTYPES)
+#if NFD_MACOS_ALLOWEDCONTENTTYPES == 1
// Returns an NSArray of UTType representing the content types.
static NSArray* BuildAllowedContentTypes(const nfdnfilteritem_t* filterList,
nfdfiltersize_t filterCount) {
@@ -81,9 +87,7 @@ static NSArray* BuildAllowedContentTypes(const nfdnfilteritem_t* filterList,
return returnArray;
}
-#endif
-
-#if defined(NFD_NEEDS_ALLOWEDFILETYPES)
+#else
// Returns an NSArray of NSString representing the file types.
static NSArray* BuildAllowedFileTypes(const nfdnfilteritem_t* filterList,
nfdfiltersize_t filterCount) {
@@ -130,21 +134,8 @@ static void AddFilterListToDialog(NSSavePanel* dialog,
assert(filterList);
// Make NSArray of file types and set it on the dialog
-// We use setAllowedFileTypes or setAllowedContentTypes depending on the OS version
-#if defined(NFD_NEEDS_ALLOWEDCONTENTTYPES) && defined(NFD_NEEDS_ALLOWEDFILETYPES)
- // If both are needed, it means we have to do a runtime check
- if (@available(macOS 12.0, *)) {
- NSArray* allowedContentTypes = BuildAllowedContentTypes(filterList, filterCount);
- [dialog setAllowedContentTypes:allowedContentTypes];
- } else {
- NSArray* allowedFileTypes = BuildAllowedFileTypes(filterList, filterCount);
- // Unfortunately @available doesn't silence deprecation warnings so we need these pragmas
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wdeprecated-declarations"
- [dialog setAllowedFileTypes:allowedFileTypes];
-#pragma clang diagnostic pop
- }
-#elif defined(NFD_NEEDS_ALLOWEDCONTENTTYPES)
+// We use setAllowedFileTypes or setAllowedContentTypes depending on the deployment target
+#if NFD_MACOS_ALLOWEDCONTENTTYPES == 1
NSArray* allowedContentTypes = BuildAllowedContentTypes(filterList, filterCount);
[dialog setAllowedContentTypes:allowedContentTypes];
#else
diff --git a/nfd/nfd_portal.cpp b/nfd/nfd_portal.cpp
index f5a9302f..8ef9158f 100644
--- a/nfd/nfd_portal.cpp
+++ b/nfd/nfd_portal.cpp
@@ -20,13 +20,12 @@
#include "nfd.h"
/*
-Define NFD_PORTAL_AUTO_APPEND_FILE_EXTENSION to 0 if you don't want the file extension to be
-appended when missing. Linux programs usually doesn't append the file extension, but for consistency
-with other OSes we append it by default.
+Define NFD_APPEND_EXTENSION if you want the file extension to be appended when missing. Linux
+programs usually don't append the file extension, but for consistency with other OSes you might want
+to append it. However, when using portals, the file overwrite prompt and the Flatpak sandbox won't
+know that we appended an extension, so they will not check or whitelist the correct file. Enabling
+NFD_APPEND_EXTENSION is not recommended for portals.
*/
-#ifndef NFD_PORTAL_AUTO_APPEND_FILE_EXTENSION
-#define NFD_PORTAL_AUTO_APPEND_FILE_EXTENSION 1
-#endif
namespace {
@@ -720,14 +719,14 @@ nfdresult_t ReadResponseUrisSingle(DBusMessage* msg, const char*& file) {
const nfdresult_t res = ReadResponseUris(msg, uri_iter);
if (res != NFD_OKAY) return res; // can be NFD_CANCEL or NFD_ERROR
if (dbus_message_iter_get_arg_type(&uri_iter) != DBUS_TYPE_STRING) {
- NFDi_SetError("D-Bus response signal URI sub iter is not an string.");
+ NFDi_SetError("D-Bus response signal URI sub iter is not a string.");
return NFD_ERROR;
}
dbus_message_iter_get_basic(&uri_iter, &file);
return NFD_OKAY;
}
-#if NFD_PORTAL_AUTO_APPEND_FILE_EXTENSION == 1
+#ifdef NFD_APPEND_EXTENSION
// Read the response URI and selected extension (in the form "*.abc" or "*") (if any). If response
// was okay, then returns NFD_OKAY and set file and extn to them (the pointer is set to some string
// owned by msg, so you should not manually free it). `file` is the user-entered file name, and
@@ -775,7 +774,7 @@ nfdresult_t ReadResponseUrisSingleAndCurrentExtension(DBusMessage* msg,
}
if (dbus_message_iter_get_arg_type(&current_filter_struct_iter) !=
DBUS_TYPE_ARRAY) {
- // NFDi_SetError("D-Bus response signal URI sub iter is not an string.");
+ // NFDi_SetError("D-Bus response signal URI sub iter is not a string.");
return NFD_OKAY;
}
DBusMessageIter current_filter_array_iter;
@@ -788,7 +787,7 @@ nfdresult_t ReadResponseUrisSingleAndCurrentExtension(DBusMessage* msg,
DBusMessageIter current_filter_extn_iter;
dbus_message_iter_recurse(&current_filter_array_iter, &current_filter_extn_iter);
if (dbus_message_iter_get_arg_type(&current_filter_extn_iter) != DBUS_TYPE_UINT32) {
- // NFDi_SetError("D-Bus response signal URI sub iter is not an string.");
+ // NFDi_SetError("D-Bus response signal URI sub iter is not a string.");
return NFD_OKAY;
}
dbus_uint32_t type;
@@ -803,7 +802,7 @@ nfdresult_t ReadResponseUrisSingleAndCurrentExtension(DBusMessage* msg,
return NFD_OKAY;
}
if (dbus_message_iter_get_arg_type(&current_filter_extn_iter) != DBUS_TYPE_STRING) {
- // NFDi_SetError("D-Bus response signal URI sub iter is not an string.");
+ // NFDi_SetError("D-Bus response signal URI sub iter is not a string.");
return NFD_OKAY;
}
dbus_message_iter_get_basic(&current_filter_extn_iter, &tmp_extn);
@@ -942,12 +941,69 @@ class DBusSignalSubscriptionHandler {
}
};
+// Returns true if ch is in [0-9A-Za-z], false otherwise.
+bool IsHex(char ch) {
+ return ('0' <= ch && ch <= '9') || ('A' <= ch && ch <= 'F') || ('a' <= ch && ch <= 'f');
+}
+
+// Returns the hexadecimal value contained in the char. Precondition: IsHex(ch)
+char ParseHexUnchecked(char ch) {
+ if ('0' <= ch && ch <= '9') return ch - '0';
+ if ('A' <= ch && ch <= 'F') return ch - ('A' - 10);
+ if ('a' <= ch && ch <= 'f') return ch - ('a' - 10);
+#if defined(__GNUC__)
+ __builtin_unreachable();
+#endif
+}
+
+// Returns true if the given file URI is decodable (i.e. not malformed), and false otherwise.
+// If this function returns true, then `out` will be populated with the length of the decoded URI
+// and `fileUriEnd` will point to the trailing null byte of `fileUri`. Otherwise, `out` and
+// `fileUriEnd` will be unmodified.
+bool TryUriDecodeLen(const char* fileUri, size_t& out, const char*& fileUriEnd) {
+ size_t len = 0;
+ while (*fileUri) {
+ if (*fileUri != '%') {
+ ++fileUri;
+ } else {
+ if (*(fileUri + 1) == '\0' || *(fileUri + 2) == '\0') {
+ return false;
+ }
+ if (!IsHex(*(fileUri + 1)) || !IsHex(*(fileUri + 2))) {
+ return false;
+ }
+ fileUri += 3;
+ }
+ ++len;
+ }
+ out = len;
+ fileUriEnd = fileUri;
+ return true;
+}
+
+// Decodes the given URI and writes it to `outPath`. The caller must ensure that the given URI is
+// not malformed (typically with a prior call to `TryUriDecodeLen`). This function does not write
+// any trailing null character.
+char* UriDecodeUnchecked(const char* fileUri, const char* fileUriEnd, char* outPath) {
+ while (fileUri != fileUriEnd) {
+ if (*fileUri != '%') {
+ *outPath++ = *fileUri++;
+ } else {
+ ++fileUri;
+ const char high_nibble = ParseHexUnchecked(*fileUri++);
+ const char low_nibble = ParseHexUnchecked(*fileUri++);
+ *outPath++ = (high_nibble << 4) | low_nibble;
+ }
+ }
+ return outPath;
+}
+
constexpr const char FILE_URI_PREFIX[] = "file://";
constexpr size_t FILE_URI_PREFIX_LEN = sizeof(FILE_URI_PREFIX) - 1;
-// If fileUri starts with "file://", strips that prefix and copies it to a new buffer, and make
-// outPath point to it, and returns NFD_OKAY. Otherwise, does not modify outPath and returns
-// NFD_ERROR (with the correct error set)
+// If fileUri starts with "file://", strips that prefix and URI-decodes the remaining part to a new
+// buffer, and make outPath point to it, and returns NFD_OKAY. Otherwise, does not modify outPath
+// and returns NFD_ERROR (with the correct error set)
nfdresult_t AllocAndCopyFilePath(const char* fileUri, char*& outPath) {
const char* prefix_begin = FILE_URI_PREFIX;
const char* const prefix_end = FILE_URI_PREFIX + FILE_URI_PREFIX_LEN;
@@ -957,14 +1013,20 @@ nfdresult_t AllocAndCopyFilePath(const char* fileUri, char*& outPath) {
return NFD_ERROR;
}
}
- size_t len = strlen(fileUri);
- char* path_without_prefix = NFDi_Malloc<char>(len + 1);
- copy(fileUri, fileUri + (len + 1), path_without_prefix);
+ size_t decoded_len;
+ const char* file_uri_end;
+ if (!TryUriDecodeLen(fileUri, decoded_len, file_uri_end)) {
+ NFDi_SetError("D-Bus freedesktop portal returned a malformed URI.");
+ return NFD_ERROR;
+ }
+ char* const path_without_prefix = NFDi_Malloc<char>(decoded_len + 1);
+ char* const out_end = UriDecodeUnchecked(fileUri, file_uri_end, path_without_prefix);
+ *out_end = '\0';
outPath = path_without_prefix;
return NFD_OKAY;
}
-#if NFD_PORTAL_AUTO_APPEND_FILE_EXTENSION == 1
+#ifdef NFD_APPEND_EXTENSION
bool TryGetValidExtension(const char* extn,
const char*& trimmed_extn,
const char*& trimmed_extn_end) {
@@ -994,10 +1056,18 @@ nfdresult_t AllocAndCopyFilePathWithExtn(const char* fileUri, const char* extn,
}
}
- const char* file_end = fileUri;
- for (; *file_end != '\0'; ++file_end)
- ;
- const char* file_it = file_end;
+ size_t decoded_len;
+ const char* file_uri_end;
+ if (!TryUriDecodeLen(fileUri, decoded_len, file_uri_end)) {
+ NFDi_SetError("D-Bus freedesktop portal returned a malformed URI.");
+ return NFD_ERROR;
+ }
+
+ const char* file_it = file_uri_end;
+ // The following loop condition is safe because `FILE_URI_PREFIX` ends with '/',
+ // so we won't iterate past the beginning of the URI.
+ // Also in UTF-8 all non-ASCII code points are encoded using bytes 128-255 so every '.' or '/'
+ // is also '.' or '/' in UTF-8.
do {
--file_it;
} while (*file_it != '/' && *file_it != '.');
@@ -1005,16 +1075,17 @@ nfdresult_t AllocAndCopyFilePathWithExtn(const char* fileUri, const char* extn,
const char* trimmed_extn_end; // includes the '\0'
if (*file_it == '.' || !TryGetValidExtension(extn, trimmed_extn, trimmed_extn_end)) {
// has file extension already or no valid extension in `extn`
- ++file_end; // includes the '\0'
- char* path_without_prefix = NFDi_Malloc<char>(file_end - fileUri);
- copy(fileUri, file_end, path_without_prefix);
+ char* const path_without_prefix = NFDi_Malloc<char>(decoded_len + 1);
+ char* const out_end = UriDecodeUnchecked(fileUri, file_uri_end, path_without_prefix);
+ *out_end = '\0';
outPath = path_without_prefix;
} else {
// no file extension and we have a valid extension
- char* path_without_prefix =
- NFDi_Malloc<char>((file_end - fileUri) + (trimmed_extn_end - trimmed_extn));
- char* out = copy(fileUri, file_end, path_without_prefix);
- copy(trimmed_extn, trimmed_extn_end, out);
+ char* const path_without_prefix =
+ NFDi_Malloc<char>(decoded_len + (trimmed_extn_end - trimmed_extn));
+ char* const out_mid = UriDecodeUnchecked(fileUri, file_uri_end, path_without_prefix);
+ char* const out_end = copy(trimmed_extn, trimmed_extn_end, out_mid);
+ *out_end = '\0';
outPath = path_without_prefix;
}
return NFD_OKAY;
@@ -1244,15 +1315,15 @@ nfdresult_t NFD_OpenDialogN(nfdnchar_t** outPath,
}
DBusMessage_Guard msg_guard(msg);
- const char* file;
+ const char* uri;
{
- const nfdresult_t res = ReadResponseUrisSingle(msg, file);
+ const nfdresult_t res = ReadResponseUrisSingle(msg, uri);
if (res != NFD_OKAY) {
return res;
}
}
- return AllocAndCopyFilePath(file, *outPath);
+ return AllocAndCopyFilePath(uri, *outPath);
}
nfdresult_t NFD_OpenDialogMultipleN(const nfdpathset_t** outPaths,
@@ -1295,27 +1366,27 @@ nfdresult_t NFD_SaveDialogN(nfdnchar_t** outPath,
}
DBusMessage_Guard msg_guard(msg);
-#if NFD_PORTAL_AUTO_APPEND_FILE_EXTENSION == 1
- const char* file;
+#ifdef NFD_APPEND_EXTENSION
+ const char* uri;
const char* extn;
{
- const nfdresult_t res = ReadResponseUrisSingleAndCurrentExtension(msg, file, extn);
+ const nfdresult_t res = ReadResponseUrisSingleAndCurrentExtension(msg, uri, extn);
if (res != NFD_OKAY) {
return res;
}
}
- return AllocAndCopyFilePathWithExtn(file, extn, *outPath);
+ return AllocAndCopyFilePathWithExtn(uri, extn, *outPath);
#else
- const char* file;
+ const char* uri;
{
- const nfdresult_t res = ReadResponseUrisSingle(msg, file);
+ const nfdresult_t res = ReadResponseUrisSingle(msg, uri);
if (res != NFD_OKAY) {
return res;
}
}
- return AllocAndCopyFilePath(file, *outPath);
+ return AllocAndCopyFilePath(uri, *outPath);
#endif
}
@@ -1331,15 +1402,15 @@ nfdresult_t NFD_PickFolderN(nfdnchar_t** outPath, const nfdnchar_t* defaultPath)
}
DBusMessage_Guard msg_guard(msg);
- const char* file;
+ const char* uri;
{
- const nfdresult_t res = ReadResponseUrisSingle(msg, file);
+ const nfdresult_t res = ReadResponseUrisSingle(msg, uri);
if (res != NFD_OKAY) {
return res;
}
}
- return AllocAndCopyFilePath(file, *outPath);
+ return AllocAndCopyFilePath(uri, *outPath);
}
nfdresult_t NFD_PathSet_GetCount(const nfdpathset_t* pathSet, nfdpathsetsize_t* count) {
@@ -1364,12 +1435,12 @@ nfdresult_t NFD_PathSet_GetPathN(const nfdpathset_t* pathSet,
}
}
if (dbus_message_iter_get_arg_type(&uri_iter) != DBUS_TYPE_STRING) {
- NFDi_SetError("D-Bus response signal URI sub iter is not an string.");
+ NFDi_SetError("D-Bus response signal URI sub iter is not a string.");
return NFD_ERROR;
}
- const char* file;
- dbus_message_iter_get_basic(&uri_iter, &file);
- return AllocAndCopyFilePath(file, *outPath);
+ const char* uri;
+ dbus_message_iter_get_basic(&uri_iter, &uri);
+ return AllocAndCopyFilePath(uri, *outPath);
}
void NFD_PathSet_FreePathN(const nfdnchar_t* filePath) {
@@ -1402,12 +1473,12 @@ nfdresult_t NFD_PathSet_EnumNextN(nfdpathsetenum_t* enumerator, nfdnchar_t** out
return NFD_OKAY;
}
if (arg_type != DBUS_TYPE_STRING) {
- NFDi_SetError("D-Bus response signal URI sub iter is not an string.");
+ NFDi_SetError("D-Bus response signal URI sub iter is not a string.");
return NFD_ERROR;
}
- const char* file;
- dbus_message_iter_get_basic(&uri_iter, &file);
- const nfdresult_t res = AllocAndCopyFilePath(file, *outPath);
+ const char* uri;
+ dbus_message_iter_get_basic(&uri_iter, &uri);
+ const nfdresult_t res = AllocAndCopyFilePath(uri, *outPath);
if (res != NFD_OKAY) return res;
dbus_message_iter_next(&uri_iter);
return NFD_OKAY;