Skip to content

Commit 36d80d0

Browse files
committed
File: don't distinguish between quoted and unquoted URLs.
1 parent c4161e3 commit 36d80d0

File tree

11 files changed

+118
-2
lines changed

11 files changed

+118
-2
lines changed

src/file/file.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ File::Impl::Impl(const std::string &pFileNameOrUrl, bool pRetrieveContents)
2727
{
2828
// Check whether we are dealing with a local file or a URL.
2929

30-
auto [isLocalFile, fileNameOrUrl] = retrieveFileInfo(pFileNameOrUrl);
30+
auto [isLocalFile, fileNameOrUrl] = retrieveFileInfo(decodeUrl(pFileNameOrUrl));
3131

3232
if (isLocalFile) {
3333
mFilePath = stringToPath(fileNameOrUrl);

src/misc/utils.cpp

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,37 @@ bool fuzzyCompare(double pNb1, double pNb2)
145145
return std::fabs(pNb1 - pNb2) * ONE_TRILLION <= std::fmin(std::fabs(pNb1), std::fabs(pNb2));
146146
}
147147

148+
std::string encodeUrl(const std::string &pUrl)
149+
{
150+
std::ostringstream res;
151+
152+
for (char c : pUrl) {
153+
if (isalnum(c) || std::string("!#$&'()*+,-./:;=?@_~").find(c) != std::string::npos) {
154+
res << c;
155+
} else {
156+
res << '%' << std::uppercase << std::hex << std::setw(2) << std::setfill('0') << static_cast<int>(static_cast<unsigned char>(c));
157+
}
158+
}
159+
return res.str();
160+
}
161+
162+
std::string decodeUrl(const std::string &pUrl)
163+
{
164+
std::string res;
165+
166+
for (size_t i = 0; i < pUrl.size(); ++i) {
167+
if ((pUrl[i] == '%') && (i + 2 < pUrl.size()) && std::isxdigit(pUrl[i + 1]) && std::isxdigit(pUrl[i + 2])) {
168+
res += static_cast<char>(std::stoi(pUrl.substr(i + 1, 2), nullptr, 16));
169+
170+
i += 2;
171+
} else {
172+
res += pUrl[i];
173+
}
174+
}
175+
176+
return res;
177+
}
178+
148179
#ifdef BUILDING_USING_MSVC
149180
std::string forwardSlashPath(const std::string &pPath)
150181
{
@@ -436,7 +467,7 @@ std::tuple<bool, std::filesystem::path> downloadFile(const std::string &pUrl)
436467

437468
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1);
438469
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0);
439-
curl_easy_setopt(curl, CURLOPT_URL, pUrl.c_str());
470+
curl_easy_setopt(curl, CURLOPT_URL, encodeUrl(pUrl).c_str());
440471
curl_easy_setopt(curl, CURLOPT_WRITEDATA, static_cast<void *>(&file));
441472
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curlWriteFunction);
442473

src/misc/utils.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,9 @@ void LIBOPENCOR_UNIT_TESTING_EXPORT printArray(const std::string &pName, const D
7878

7979
bool LIBOPENCOR_UNIT_TESTING_EXPORT fuzzyCompare(double pNb1, double pNb2);
8080

81+
std::string LIBOPENCOR_UNIT_TESTING_EXPORT encodeUrl(const std::string &pUrl);
82+
std::string LIBOPENCOR_UNIT_TESTING_EXPORT decodeUrl(const std::string &pUrl);
83+
8184
#ifdef BUILDING_USING_MSVC
8285
std::string LIBOPENCOR_UNIT_TESTING_EXPORT forwardSlashPath(const std::string &pPath);
8386
#endif

tests/api/file/basictests.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,17 @@ TEST(BasicFileTest, remoteFile)
104104
EXPECT_FALSE(file->contents().empty());
105105
}
106106

107+
TEST(BasicFileTest, encodedRemoteFile)
108+
{
109+
auto file = libOpenCOR::File::create(libOpenCOR::ENCODED_REMOTE_FILE);
110+
111+
EXPECT_EQ(file->type(), libOpenCOR::File::Type::CELLML_FILE);
112+
EXPECT_NE(file->fileName(), "");
113+
EXPECT_EQ(file->url(), libOpenCOR::NON_ENCODED_REMOTE_FILE);
114+
EXPECT_EQ(file->path(), libOpenCOR::NON_ENCODED_REMOTE_FILE);
115+
EXPECT_FALSE(file->contents().empty());
116+
}
117+
107118
TEST(BasicFileTest, localVirtualFile)
108119
{
109120
auto file = libOpenCOR::File::create(libOpenCOR::resourcePath(libOpenCOR::UNKNOWN_FILE), false);

tests/bindings/javascript/file.basic.test.js

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,23 @@ test.describe('File basic tests', () => {
7777
assertIssues(loc, file, expectedUnknownFileIssues);
7878
});
7979

80+
test('Encoded remote file', () => {
81+
const file = new loc.File(utils.ENCODED_REMOTE_FILE);
82+
83+
assert.strictEqual(file.type.value, loc.File.Type.UNKNOWN_FILE.value);
84+
assert.strictEqual(file.fileName, '/some/path/file');
85+
assert.strictEqual(file.url, utils.NON_ENCODED_REMOTE_FILE);
86+
assert.strictEqual(file.path, utils.NON_ENCODED_REMOTE_FILE);
87+
assert.deepStrictEqual(file.contents(), utils.NO_CONTENTS);
88+
assertIssues(loc, file, expectedNoIssues);
89+
90+
file.setContents(unknownContentsPtr, utils.UNKNOWN_CONTENTS.length);
91+
92+
assert.strictEqual(file.type.value, loc.File.Type.UNKNOWN_FILE.value);
93+
assert.deepStrictEqual(file.contents(), utils.UNKNOWN_CONTENTS);
94+
assertIssues(loc, file, expectedUnknownFileIssues);
95+
});
96+
8097
test('File manager', () => {
8198
const fileManager = loc.FileManager.instance();
8299

tests/bindings/javascript/utils.in.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,10 @@ export const HTTP_REMOTE_COMBINE_ARCHIVE =
3535
'http://raw.githubusercontent.com/opencor/libopencor/master/tests/res/cellml_2.omex';
3636
export const REMOTE_BASE_PATH = 'https://raw.githubusercontent.com/opencor/libopencor/master/tests/res';
3737
export const REMOTE_FILE = 'https://raw.githubusercontent.com/opencor/libopencor/master/tests/res/cellml_2.cellml';
38+
export const ENCODED_REMOTE_FILE =
39+
'https://models.physiomeproject.org/workspace/aed/@@rawfile/d4accf8429dbf5bdd5dfa1719790f361f5baddbe/FAIRDO%20BG%20example%203.1.cellml';
40+
export const NON_ENCODED_REMOTE_FILE =
41+
'https://models.physiomeproject.org/workspace/aed/@@rawfile/d4accf8429dbf5bdd5dfa1719790f361f5baddbe/FAIRDO BG example 3.1.cellml';
3842

3943
export const NO_CONTENTS = stringToArrayBuffer('');
4044
export const NULL_CHARACTER_CONTENTS = stringToArrayBuffer('\0');

tests/bindings/python/test_file_basic.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,16 @@ def test_remote_file():
104104
assert file.contents != []
105105

106106

107+
def test_encoded_remote_file():
108+
file = loc.File(utils.EncodedRemoteFile)
109+
110+
assert file.type == loc.File.Type.CellmlFile
111+
assert file.file_name != ""
112+
assert file.url == utils.NonEncodedRemoteFile
113+
assert file.path == utils.NonEncodedRemoteFile
114+
assert file.contents != []
115+
116+
107117
def test_local_virtual_file():
108118
file = loc.File(utils.resource_path(utils.UnknownFile), False)
109119

tests/bindings/python/utils.in.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,8 @@
5252
)
5353
RemoteBasePath = "https://raw.githubusercontent.com/opencor/libopencor/master/tests/res"
5454
RemoteFile = "https://raw.githubusercontent.com/opencor/libopencor/master/tests/res/cellml_2.cellml"
55+
EncodedRemoteFile = "https://models.physiomeproject.org/workspace/aed/@@rawfile/d4accf8429dbf5bdd5dfa1719790f361f5baddbe/FAIRDO%20BG%20example%203.1.cellml"
56+
NonEncodedRemoteFile = "https://models.physiomeproject.org/workspace/aed/@@rawfile/d4accf8429dbf5bdd5dfa1719790f361f5baddbe/FAIRDO BG example 3.1.cellml"
5557
UnknownRemoteFile = "https://raw.githubusercontent.com/opencor/libopencor/master/tests/res/unknown_file.txt"
5658
IrretrievableRemoteFile = "https://some.domain.com/irretrievable_file.txt"
5759

tests/misc/tests.cmake

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,4 +20,5 @@ set(${TEST}_CATEGORY)
2020
set(${TEST}_SOURCE_FILES
2121
${CMAKE_CURRENT_LIST_DIR}/compilertests.cpp
2222
${CMAKE_CURRENT_LIST_DIR}/failingsimulationtests.cpp
23+
${CMAKE_CURRENT_LIST_DIR}/utils.cpp
2324
)

tests/misc/utils.cpp

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
/*
2+
Copyright libOpenCOR contributors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
#include "utils.h"
18+
19+
#include "tests/utils.h"
20+
21+
TEST(UtilsTest, encodeUrl)
22+
{
23+
EXPECT_EQ(libOpenCOR::encodeUrl(" !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~"),
24+
"%20!%22#$%25&'()*+,-./0123456789:;%3C=%3E?@ABCDEFGHIJKLMNOPQRSTUVWXYZ%5B%5C%5D%5E_%60abcdefghijklmnopqrstuvwxyz%7B%7C%7D~");
25+
}
26+
27+
TEST(UtilsTest, decodeUrl)
28+
{
29+
EXPECT_EQ(libOpenCOR::decodeUrl("%20!%22#$%25&'()*+,-./0123456789:;%3C=%3E?@ABCDEFGHIJKLMNOPQRSTUVWXYZ%5B%5C%5D%5E_%60abcdefghijklmnopqrstuvwxyz%7B%7C%7D~"),
30+
" !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~");
31+
EXPECT_EQ(libOpenCOR::decodeUrl("%"), "%");
32+
EXPECT_EQ(libOpenCOR::decodeUrl("%X"), "%X");
33+
EXPECT_EQ(libOpenCOR::decodeUrl("%XX"), "%XX");
34+
EXPECT_EQ(libOpenCOR::decodeUrl("%2X"), "%2X");
35+
}

0 commit comments

Comments
 (0)