WinPR::Clipboard: Add to convert uri to local file

URI is specified by RFC 8089: https://datatracker.ietf.org/doc/html/rfc8089

Local files:

   o  A traditional file URI for a local file with an empty authority.
      For example:

      *  "file:///path/to/file"

   o  The minimal representation of a local file with no authority field
      and an absolute path that begins with a slash "/".  For example:

      *  "file:/path/to/file"

   o  The minimal representation of a local file in a DOS- or Windows-
      based environment with no authority field and an absolute path
      that begins with a drive letter.  For example:

      *  "file:c:/path/to/file"

   o  Regular DOS or Windows file URIs with vertical line characters in
      the drive letter construct.  For example:

      *  "file:///c|/path/to/file"

      *  "file:/c|/path/to/file"

      *  "file:c|/path/to/file"
This commit is contained in:
Kang Lin 2022-09-16 11:23:21 +08:00 committed by akallabeth
parent 22bd0a43e6
commit 6a6e3340c7
3 changed files with 219 additions and 17 deletions

View File

@ -439,31 +439,158 @@ static BOOL process_file_name(wClipboard* clipboard, const char* local_name, wAr
return result;
}
static BOOL process_uri(wClipboard* clipboard, const char* uri, size_t uri_len)
static BOOL is_dos_driver(const char* path)
{
const char prefix[] = "file://";
BOOL result = FALSE;
char* name = NULL;
if ((path[1] == ':' || path[1] == '|') &&
((path[0] >= 'A' && path[0] <= 'Z') || (path[0] >= 'a' && path[0] <= 'z')))
return TRUE;
return FALSE;
}
#if !defined(BUILD_TESTING)
static
#endif
char*
parse_uri_to_local_file(const char* uri, size_t uri_len)
{
// URI is specified by RFC 8089: https://datatracker.ietf.org/doc/html/rfc8089
const char prefix[] = "file:";
const char prefixTraditional[] = "file://";
char* localName = NULL;
size_t localLen = 0;
char* buffer = NULL;
const size_t prefixLen = strnlen(prefix, sizeof(prefix));
WINPR_ASSERT(clipboard);
const size_t prefixTraditionalLen = strnlen(prefixTraditional, sizeof(prefixTraditional));
WLog_VRB(TAG, "processing URI: %.*s", uri_len, uri);
if ((uri_len < prefixLen) || strncmp(uri, prefix, prefixLen))
{
WLog_ERR(TAG, "non-'file://' URI schemes are not supported");
goto out;
WLog_ERR(TAG, "non-'file:' URI schemes are not supported");
return NULL;
}
name = decode_percent_encoded_string(uri + prefixLen, uri_len - prefixLen);
do
{
/* https://datatracker.ietf.org/doc/html/rfc8089#appendix-F
* - The minimal representation of a local file in a DOS- or Windows-
* based environment with no authority field and an absolute path
* that begins with a drive letter.
*
* "file:c:/path/to/file"
*
* - Regular DOS or Windows file URIs with vertical line characters in
* the drive letter construct.
*
* "file:c|/path/to/file"
*
*/
if (uri[prefixLen] != '/')
{
if (!name)
goto out;
if (is_dos_driver(uri + prefixLen))
{
// Dos and Windows file URI
localName = (char*)(uri + prefixLen);
localLen = uri_len - prefixLen;
break;
}
else
{
WLog_ERR(TAG, "URI format are not supported: %s", uri);
return NULL;
}
}
/*
* - The minimal representation of a local file with no authority field
* and an absolute path that begins with a slash "/". For example:
*
* "file:/path/to/file"
*
*/
if (uri[prefixLen] == '/' && uri[prefixLen + 1] != '/')
{
if (is_dos_driver(uri + prefixLen + 1))
{
// Dos and Windows file URI
localName = (char*)(uri + prefixLen + 1);
localLen = uri_len - prefixLen - 1;
}
else
{
localName = (char*)uri + prefixLen;
localLen = uri_len - prefixLen;
}
break;
}
/*
* - A traditional file URI for a local file with an empty authority.
*
* "file:///path/to/file"
*/
if ((uri_len < prefixTraditionalLen) ||
strncmp(uri, prefixTraditional, prefixTraditionalLen))
{
WLog_ERR(TAG, "non-'file:' URI schemes are not supported");
return NULL;
}
localName = (char*)(uri + prefixTraditionalLen);
localLen = uri_len - prefixTraditionalLen;
/*
* "file:///c:/path/to/file"
* "file:///c|/path/to/file"
*/
if (localName[0] != '/')
{
WLog_ERR(TAG, "URI format are not supported: %s", uri);
return NULL;
}
if (is_dos_driver(localName + 1))
{
localName++;
localLen--;
}
} while (0);
buffer = calloc(localLen + 1, sizeof(char));
if (buffer)
{
memcpy(buffer, localName, localLen);
if (buffer[1] == '|' &&
((buffer[0] >= 'A' && buffer[0] <= 'Z') || (buffer[0] >= 'a' && buffer[0] <= 'z')))
buffer[1] = ':';
return buffer;
}
return NULL;
}
static BOOL process_uri(wClipboard* clipboard, const char* uri, size_t uri_len)
{
// URI is specified by RFC 8089: https://datatracker.ietf.org/doc/html/rfc8089
BOOL result = FALSE;
char* name = NULL;
char* localName = NULL;
WINPR_ASSERT(clipboard);
localName = parse_uri_to_local_file(uri, uri_len);
if (localName)
{
name = decode_percent_encoded_string(localName, strlen(localName));
free(localName);
}
if (name)
{
result = process_file_name(clipboard, name, clipboard->localFiles);
free(name);
}
result = process_file_name(clipboard, name, clipboard->localFiles);
out:
free(name);
return result;
}

View File

@ -12,10 +12,10 @@ create_test_sourcelist(${MODULE_PREFIX}_SRCS
${${MODULE_PREFIX}_TESTS})
add_executable(${MODULE_NAME} ${${MODULE_PREFIX}_SRCS})
target_link_libraries(${MODULE_NAME} winpr)
set_target_properties(${MODULE_NAME} PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${TESTING_OUTPUT_DIRECTORY}")
set_target_properties(${MODULE_NAME} PROPERTIES RUNTIME_OUTPUT_DIRECTORY
"${TESTING_OUTPUT_DIRECTORY}")
foreach(test ${${MODULE_PREFIX}_TESTS})
get_filename_component(TestName ${test} NAME_WE)
@ -23,3 +23,11 @@ foreach(test ${${MODULE_PREFIX}_TESTS})
endforeach()
set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "WinPR/Test")
if(NOT WIN32)
add_executable(TestUri TestUri.c)
target_link_libraries(TestUri winpr)
set_target_properties(TestUri PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${TESTING_OUTPUT_DIRECTORY}")
add_test(TestUri ${TESTING_OUTPUT_DIRECTORY}/TestUri)
set_property(TARGET TestUri PROPERTY FOLDER "WinPR/Test")
endif()

View File

@ -0,0 +1,67 @@
#include <stdio.h>
#include <string.h>
#include <memory.h>
#include <stdlib.h>
#include <winpr/winpr.h>
#include "winpr/wlog.h"
#define WINPR_TAG(tag) "com.winpr." tag
#define TAG WINPR_TAG("clipboard.posix")
char* parse_uri_to_local_file(const char* uri, size_t uri_len);
int main(void)
{
int nRet = 0;
const char* input[] = { /*uri, file or NULL*/
"file://root/a.txt",
NULL,
"file:a.txt",
NULL,
"file:///c:/windows/a.txt",
"c:/windows/a.txt",
"file:c:/windows/a.txt",
"c:/windows/a.txt",
"file:c|/windows/a.txt",
"c:/windows/a.txt",
"file:///root/a.txt",
"/root/a.txt",
"file:/root/a.txt",
"/root/a.txt"
};
const size_t nLen = ARRAYSIZE(input);
printf("input length:%" PRIuz "\n", nLen / 2);
WLog_SetLogLevel(WLog_Get(TAG), WLOG_ERROR);
for (size_t i = 0; i < nLen; i += 2)
{
int bTest = 0;
char* name = parse_uri_to_local_file(input[i], strlen(input[i]));
if (name)
{
bTest = !strcmp(name, input[i + 1]);
if (!bTest)
{
printf("Test error: input: %s; Expected value: %s; output: %s\n", input[i],
input[i + 1], name);
nRet++;
}
free(name);
}
else
{
if (input[i + 1])
{
printf("Test error: input: %s; Expected value: %s; output: %s\n", input[i],
input[i + 1], name);
nRet++;
}
}
}
printf("TestUri return value: %d\n", nRet);
return nRet;
}