Skip to content

Commit 41dc1be

Browse files
authored
Convert path from UTF-8 to UTF-16 before loading, for google services JSON. (#1383)
* Convert path to UTF-16 before loading, for google services JSON. * When testing on desktop, use a folder with i18l characters. * Add App Desktop test, and JSON parsing from international path. * Format code. * Clean up test code. * Remove debug code. * Update copyright year. * Add release note. * Whitespace. * Format. * Add missing header for rmdir, mkdir, etc. * Fix C system call syntax. * Add missing directory mode.
1 parent 76f1506 commit 41dc1be

File tree

4 files changed

+290
-8
lines changed

4 files changed

+290
-8
lines changed

app/src/app_desktop.cc

+24-8
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818

1919
#include <string.h>
2020

21+
#include <codecvt>
2122
#include <fstream>
2223
#include <memory>
2324
#include <string>
@@ -28,6 +29,7 @@
2829
#include "app/src/heartbeat/heartbeat_controller_desktop.h"
2930
#include "app/src/include/firebase/app.h"
3031
#include "app/src/include/firebase/internal/common.h"
32+
#include "app/src/include/firebase/internal/platform.h"
3133
#include "app/src/include/firebase/version.h"
3234
#include "app/src/log.h"
3335
#include "app/src/semaphore.h"
@@ -36,17 +38,30 @@
3638
namespace firebase {
3739
DEFINE_FIREBASE_VERSION_STRING(Firebase);
3840

39-
namespace {
40-
41-
// Size is arbitrary, just making sure that there is a sane limit.
42-
static const int kMaxBuffersize = 1024 * 500;
41+
namespace internal {
4342

4443
// Attempts to load a config file from the path specified, and use it to
4544
// populate the AppOptions pointer. Returns true on success, false otherwise.
46-
static bool LoadAppOptionsFromJsonConfigFile(const char* path,
47-
AppOptions* options) {
45+
bool LoadAppOptionsFromJsonConfigFile(const char* path, AppOptions* options) {
46+
// Size is arbitrary, just making sure that there is a sane limit.
47+
static const int kMaxBuffersize = 1024 * 500;
48+
4849
bool loaded_options = false;
50+
#if FIREBASE_PLATFORM_WINDOWS
51+
// Convert the path from UTF-8 to UTF-16 before opening on Windows.
52+
std::wstring path_utf16;
53+
std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>, wchar_t>
54+
utf16_converter;
55+
try {
56+
path_utf16 = utf16_converter.from_bytes(path);
57+
} catch (std::range_error& ex) {
58+
LogError("Can't convert path '%s' to UTF-16: %s", path, ex.what());
59+
return false;
60+
}
61+
std::ifstream infile(path_utf16.c_str(), std::ifstream::binary);
62+
#else
4963
std::ifstream infile(path, std::ifstream::binary);
64+
#endif // FIREBASE_PLATFORM_WINDOWS
5065
if (infile) {
5166
infile.seekg(0, infile.end);
5267
int file_length = infile.tellg();
@@ -71,7 +86,7 @@ static bool LoadAppOptionsFromJsonConfigFile(const char* path,
7186
return loaded_options;
7287
}
7388

74-
} // namespace
89+
} // namespace internal
7590

7691
// Searches internal::g_default_config_path for filenames matching
7792
// kDefaultGoogleServicesNames attempting to load the app options from each
@@ -92,7 +107,8 @@ AppOptions* AppOptions::LoadDefault(AppOptions* options) {
92107
for (size_t i = 0; i < number_of_config_filenames; i++) {
93108
std::string full_path =
94109
internal::g_default_config_path + kDefaultGoogleServicesNames[i];
95-
if (LoadAppOptionsFromJsonConfigFile(full_path.c_str(), options)) {
110+
if (internal::LoadAppOptionsFromJsonConfigFile(full_path.c_str(),
111+
options)) {
96112
return options;
97113
}
98114
config_files += full_path;

app/tests/CMakeLists.txt

+8
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,14 @@ firebase_cpp_cc_test_on_ios(firebase_app_test
9393
UIKit
9494
)
9595

96+
firebase_cpp_cc_test(firebase_app_desktop_test
97+
SOURCES
98+
${FIREBASE_SOURCE_DIR}/app/tests/app_desktop_test.cc
99+
DEPENDS
100+
firebase_app
101+
flatbuffers
102+
)
103+
96104
firebase_cpp_cc_test(firebase_app_log_test
97105
SOURCES
98106
${FIREBASE_SOURCE_DIR}/app/tests/log_test.cc

app/tests/app_desktop_test.cc

+255
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,255 @@
1+
/*
2+
* Copyright 2023 Google LLC
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 "app/src/app_desktop.h"
18+
19+
#include <stdio.h>
20+
21+
#include <codecvt>
22+
#include <cstdio>
23+
#include <fstream>
24+
#include <string>
25+
26+
#include "app/src/app_common.h"
27+
#include "app/src/app_identifier.h"
28+
#include "app/src/include/firebase/app.h"
29+
#include "app/src/include/firebase/internal/platform.h"
30+
#include "app/src/include/firebase/version.h"
31+
#include "gmock/gmock.h"
32+
#include "gtest/gtest.h"
33+
34+
#if FIREBASE_PLATFORM_WINDOWS
35+
#include <direct.h>
36+
#else
37+
#include <unistd.h>
38+
#endif
39+
40+
namespace firebase {
41+
42+
static bool RemoveDirectoryUtf8(std::string dir_utf8) {
43+
#if FIREBASE_PLATFORM_WINDOWS
44+
std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>, wchar_t> cvt;
45+
std::wstring dir_utf16 = cvt.from_bytes(dir_utf8);
46+
return (_wrmdir(dir_utf16.c_str()) == 0);
47+
#else
48+
return (rmdir(dir_utf8.c_str()) == 0);
49+
#endif
50+
}
51+
52+
static bool MakeDirectoryUtf8(std::string dir_utf8) {
53+
#if FIREBASE_PLATFORM_WINDOWS
54+
std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>, wchar_t> cvt;
55+
std::wstring dir_utf16 = cvt.from_bytes(dir_utf8);
56+
return (_wmkdir(dir_utf16.c_str()) == 0);
57+
#else
58+
return (mkdir(dir_utf8.c_str(), 0700) == 0);
59+
#endif
60+
}
61+
62+
static bool ChangeDirectoryUtf8(std::string dir_utf8) {
63+
#if FIREBASE_PLATFORM_WINDOWS
64+
std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>, wchar_t> cvt;
65+
std::wstring dir_utf16 = cvt.from_bytes(dir_utf8);
66+
return (_wchdir(dir_utf16.c_str()) == 0);
67+
#else
68+
return (chdir(dir_utf8.c_str()) == 0);
69+
#endif
70+
}
71+
72+
static std::string GetCurrentDirectoryUtf8() {
73+
#if FIREBASE_PLATFORM_WINDOWS
74+
wchar_t path_buffer[FILENAME_MAX];
75+
_wgetcwd(path_buffer, FILENAME_MAX);
76+
std::string path_utf8;
77+
std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>, wchar_t> cvt;
78+
path_utf8 = cvt.to_bytes(path_buffer);
79+
return path_utf8;
80+
#else
81+
char path_buffer[FILENAME_MAX];
82+
getcwd(path_buffer, FILENAME_MAX);
83+
return std::string(path_buffer);
84+
#endif
85+
}
86+
87+
TEST(AppDesktopTest, TestSetAppId) {
88+
AppOptions options;
89+
options.set_app_id("abc");
90+
EXPECT_STREQ("abc", options.app_id());
91+
}
92+
93+
TEST(AppDesktopTest, TestSetApiKey) {
94+
AppOptions options;
95+
options.set_api_key("AIzaSyDdVgKwhZl0sTTTLZ7iTmt1r3N2cJLnaDk");
96+
EXPECT_STREQ("AIzaSyDdVgKwhZl0sTTTLZ7iTmt1r3N2cJLnaDk", options.api_key());
97+
}
98+
99+
TEST(AppDesktopTest, TestSetMessagingSenderId) {
100+
AppOptions options;
101+
options.set_messaging_sender_id("012345678901");
102+
EXPECT_STREQ("012345678901", options.messaging_sender_id());
103+
}
104+
105+
TEST(AppDesktopTest, TestSetDatabaseUrl) {
106+
AppOptions options;
107+
options.set_database_url("http://abc-xyz-123.firebaseio.com");
108+
EXPECT_STREQ("http://abc-xyz-123.firebaseio.com", options.database_url());
109+
}
110+
111+
TEST(AppDesktopTest, TestSetGaTrackingId) {
112+
AppOptions options;
113+
options.set_ga_tracking_id("UA-12345678-1");
114+
EXPECT_STREQ("UA-12345678-1", options.ga_tracking_id());
115+
}
116+
117+
TEST(AppDesktopTest, TestSetStorageBucket) {
118+
AppOptions options;
119+
options.set_storage_bucket("abc-xyz-123.storage.firebase.com");
120+
EXPECT_STREQ("abc-xyz-123.storage.firebase.com", options.storage_bucket());
121+
}
122+
123+
TEST(AppDesktopTest, TestSetProjectId) {
124+
AppOptions options;
125+
options.set_project_id("myproject-123");
126+
EXPECT_STREQ("myproject-123", options.project_id());
127+
}
128+
129+
static const char kGoogleServicesJsonContent[] = R"""(
130+
{
131+
"client": [
132+
{
133+
"services": {
134+
"appinvite_service": {
135+
"status": 1
136+
},
137+
"analytics_service": {
138+
"status": 0
139+
}
140+
},
141+
"oauth_client": [
142+
{
143+
"client_id": "fake client id"
144+
}
145+
],
146+
"api_key": [
147+
{
148+
"current_key": "fake api key"
149+
}
150+
],
151+
"client_info": {
152+
"mobilesdk_app_id": "fake app id",
153+
"android_client_info": {
154+
"package_name": "com.testproject.packagename"
155+
}
156+
}
157+
}
158+
],
159+
"configuration_version": "1",
160+
"project_info": {
161+
"storage_bucket": "fake storage bucket",
162+
"project_id": "fake project id",
163+
"firebase_url": "fake database url",
164+
"project_number": "fake messaging sender id"
165+
}
166+
}
167+
)""";
168+
169+
namespace internal {
170+
// Defined in app_desktop.cc
171+
bool LoadAppOptionsFromJsonConfigFile(const char* path, AppOptions* options);
172+
} // namespace internal
173+
174+
TEST(AppDesktopTest, TestLoadAppOptionsFromJsonConfigFile) {
175+
// Write out a config file and then load it from a full path.
176+
#if FIREBASE_PLATFORM_WINDOWS
177+
std::wofstream outfile(L"fake-google-services-1.json");
178+
const char kPathSep[] = "\\";
179+
#else
180+
std::ofstream outfile("fake-google-services-1.json");
181+
const char kPathSep[] = "/";
182+
#endif // FIREBASE_PLATFORM_WINDOWS
183+
184+
outfile << kGoogleServicesJsonContent;
185+
outfile.close();
186+
187+
std::string json_path =
188+
GetCurrentDirectoryUtf8() + kPathSep + "fake-google-services-1.json";
189+
190+
LogInfo("JSON path: %s", json_path.c_str());
191+
192+
AppOptions options;
193+
EXPECT_TRUE(
194+
internal::LoadAppOptionsFromJsonConfigFile(json_path.c_str(), &options));
195+
196+
EXPECT_STREQ("fake app id", options.app_id());
197+
EXPECT_STREQ("fake api key", options.api_key());
198+
EXPECT_STREQ("fake project id", options.project_id());
199+
200+
#if FIREBASE_PLATFORM_WINDOWS
201+
EXPECT_EQ(_wremove(L"fake-google-services-1.json"), 0);
202+
#else
203+
EXPECT_EQ(remove("fake-google-services-1.json"), 0);
204+
#endif // FIREBASE_PLATFORM_WINDOWS
205+
}
206+
207+
TEST(AppDesktopTest, TestLoadAppOptionsFromJsonConfigFileInInternationalPath) {
208+
// Save the current directory.
209+
std::string previous_directory = GetCurrentDirectoryUtf8();
210+
EXPECT_NE(previous_directory, "");
211+
212+
// Make a new directory, with international characters.
213+
std::string new_dir = "téŝt_dir";
214+
if (!ChangeDirectoryUtf8(new_dir)) {
215+
// If the target directory doesn't exist, make it.
216+
LogInfo("Creating directory: %s", new_dir.c_str());
217+
ASSERT_TRUE(MakeDirectoryUtf8(new_dir));
218+
ASSERT_TRUE(ChangeDirectoryUtf8(new_dir));
219+
}
220+
// Write out a config file and then load it from a full path with
221+
// international characters.
222+
#if FIREBASE_PLATFORM_WINDOWS
223+
const char kPathSep[] = "\\";
224+
std::wofstream outfile(L"fake-google-services-2.json");
225+
#else
226+
const char kPathSep[] = "/";
227+
std::ofstream outfile("fake-google-services-2.json");
228+
#endif // FIREBASE_PLATFORM_WINDOWS
229+
230+
outfile << kGoogleServicesJsonContent;
231+
outfile.close();
232+
233+
std::string json_path =
234+
GetCurrentDirectoryUtf8() + kPathSep + "fake-google-services-2.json";
235+
236+
LogInfo("JSON path: %s", json_path.c_str());
237+
238+
AppOptions options;
239+
EXPECT_TRUE(
240+
internal::LoadAppOptionsFromJsonConfigFile(json_path.c_str(), &options));
241+
242+
EXPECT_STREQ("fake app id", options.app_id());
243+
EXPECT_STREQ("fake api key", options.api_key());
244+
EXPECT_STREQ("fake project id", options.project_id());
245+
246+
#if FIREBASE_PLATFORM_WINDOWS
247+
EXPECT_EQ(_wremove(L"fake-google-services-2.json"), 0);
248+
#else
249+
EXPECT_EQ(remove("fake-google-services-2.json"), 0);
250+
#endif // FIREBASE_PLATFORM_WINDOWS
251+
252+
EXPECT_TRUE(ChangeDirectoryUtf8(previous_directory));
253+
EXPECT_TRUE(RemoveDirectoryUtf8(new_dir));
254+
}
255+
} // namespace firebase

release_build_files/readme.md

+3
Original file line numberDiff line numberDiff line change
@@ -629,6 +629,9 @@ code.
629629
## Release Notes
630630
### Upcoming Release
631631
- Changes
632+
- General (Desktop): Fixed an error loading google-services.json and
633+
google-services-desktop.json from paths with international characters on
634+
Windows.
632635
- Auth (Android): Fixed an issue where VerifyPhoneNumber's internal
633636
builder failed to create PhoneAuthOptions with certain compiler settings.
634637
- Auth (iOS): Fixed an issue where functions that return AuthResult

0 commit comments

Comments
 (0)