/src/FreeRDP/libfreerdp/utils/helpers.c
Line | Count | Source |
1 | | /** |
2 | | * FreeRDP: A Remote Desktop Protocol Implementation |
3 | | * common helper utilities |
4 | | * |
5 | | * Copyright 2024 Armin Novak <anovak@thincast.com> |
6 | | * Copyright 2024 Thincast Technologies GmbH |
7 | | * |
8 | | * Licensed under the Apache License, Version 2.0 (the "License"); |
9 | | * you may not use this file except in compliance with the License. |
10 | | * You may obtain a copy of the License at |
11 | | * |
12 | | * http://www.apache.org/licenses/LICENSE-2.0 |
13 | | * |
14 | | * Unless required by applicable law or agreed to in writing, software |
15 | | * distributed under the License is distributed on an "AS IS" BASIS, |
16 | | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
17 | | * See the License for the specific language governing permissions and |
18 | | * limitations under the License. |
19 | | */ |
20 | | |
21 | | #include <ctype.h> |
22 | | |
23 | | #include <freerdp/utils/helpers.h> |
24 | | |
25 | | #include <winpr/path.h> |
26 | | #include <winpr/file.h> |
27 | | #include <winpr/build-config.h> |
28 | | #include <freerdp/version.h> |
29 | | #include <freerdp/build-config.h> |
30 | | |
31 | | #include "../core/utils.h" |
32 | | |
33 | | static INIT_ONCE s_freerdp_app_details_once = INIT_ONCE_STATIC_INIT; |
34 | | static char s_freerdp_vendor_string[MAX_PATH] = WINPR_C_ARRAY_INIT; |
35 | | static char s_freerdp_product_string[MAX_PATH] = WINPR_C_ARRAY_INIT; |
36 | | static char s_freerdp_details_string[3ull * MAX_PATH] = WINPR_C_ARRAY_INIT; |
37 | | static WCHAR s_freerdp_details_string_w[3ull * MAX_PATH] = WINPR_C_ARRAY_INIT; |
38 | | static SSIZE_T s_freerdp_version = -1; |
39 | | static BOOL s_freerdp_app_details_are_custom = FALSE; |
40 | | |
41 | | static void updateDetailsString(void) |
42 | 1 | { |
43 | 1 | const char* vendor = s_freerdp_vendor_string; |
44 | 1 | const char* product = s_freerdp_product_string; |
45 | 1 | const SSIZE_T version = s_freerdp_version; |
46 | | |
47 | 1 | WINPR_ASSERT(vendor); |
48 | 1 | WINPR_ASSERT(product); |
49 | 1 | if (s_freerdp_app_details_are_custom) |
50 | 0 | { |
51 | 0 | if (version < 0) |
52 | 0 | (void)_snprintf(s_freerdp_details_string, sizeof(s_freerdp_details_string) - 1, "%s-%s", |
53 | 0 | vendor, product); |
54 | 0 | else |
55 | 0 | (void)_snprintf(s_freerdp_details_string, sizeof(s_freerdp_details_string) - 1, |
56 | 0 | "%s-%s%" PRIdz, vendor, product, version); |
57 | 0 | } |
58 | 1 | else if (version < 0) |
59 | 1 | { |
60 | 1 | (void)_snprintf(s_freerdp_details_string, sizeof(s_freerdp_details_string) - 1, "%s", |
61 | 1 | product); |
62 | 1 | } |
63 | 0 | else |
64 | 0 | (void)_snprintf(s_freerdp_details_string, sizeof(s_freerdp_details_string) - 1, "%s%" PRIdz, |
65 | 0 | product, version); |
66 | | |
67 | 1 | (void)ConvertUtf8NToWChar(s_freerdp_details_string, sizeof(s_freerdp_details_string), |
68 | 1 | s_freerdp_details_string_w, sizeof(s_freerdp_details_string_w) - 1); |
69 | 1 | } |
70 | | |
71 | | static BOOL CALLBACK init_app_details(WINPR_ATTR_UNUSED PINIT_ONCE once, |
72 | | WINPR_ATTR_UNUSED PVOID param, |
73 | | WINPR_ATTR_UNUSED PVOID* context) |
74 | 1 | { |
75 | 1 | const size_t vlen = sizeof(FREERDP_VENDOR_STRING); |
76 | 1 | const size_t plen = sizeof(FREERDP_PRODUCT_STRING); |
77 | 1 | const char* rvlen = strncpy(s_freerdp_vendor_string, FREERDP_VENDOR_STRING, vlen); |
78 | 1 | const char* rplen = strncpy(s_freerdp_product_string, FREERDP_PRODUCT_STRING, plen); |
79 | 1 | if (!rvlen || !rplen) |
80 | 0 | return FALSE; |
81 | | |
82 | | #if defined(WITH_RESOURCE_VERSIONING) |
83 | | s_freerdp_version = FREERDP_VERSION_MAJOR; |
84 | | #else |
85 | 1 | s_freerdp_version = -1; |
86 | 1 | #endif |
87 | 1 | updateDetailsString(); |
88 | 1 | return TRUE; |
89 | 1 | } |
90 | | |
91 | | WINPR_ATTR_NODISCARD |
92 | | static BOOL initializeApplicationDetails(void) |
93 | 660 | { |
94 | 660 | return InitOnceExecuteOnce(&s_freerdp_app_details_once, init_app_details, nullptr, nullptr); |
95 | 660 | } |
96 | | |
97 | | BOOL freerdp_setApplicationDetails(const char* vendor, const char* product, SSIZE_T version) |
98 | 0 | { |
99 | 0 | if (!initializeApplicationDetails()) |
100 | 0 | return -1; |
101 | | |
102 | 0 | if (!vendor || !product) |
103 | 0 | return FALSE; |
104 | 0 | const size_t vlen = strnlen(vendor, MAX_PATH); |
105 | 0 | const size_t plen = strnlen(product, MAX_PATH); |
106 | 0 | if ((vlen == MAX_PATH) || (plen == MAX_PATH)) |
107 | 0 | return FALSE; |
108 | | |
109 | 0 | if (!strncpy(s_freerdp_vendor_string, vendor, vlen + 1)) |
110 | 0 | return FALSE; |
111 | | |
112 | 0 | if (!strncpy(s_freerdp_product_string, product, plen + 1)) |
113 | 0 | return FALSE; |
114 | | |
115 | 0 | s_freerdp_version = version; |
116 | 0 | s_freerdp_app_details_are_custom = TRUE; |
117 | |
|
118 | 0 | const char separator = PathGetSeparatorA(PATH_STYLE_NATIVE); |
119 | 0 | char* str = freerdp_getApplicatonDetailsCombined(separator); |
120 | 0 | if (!str) |
121 | 0 | return FALSE; |
122 | | |
123 | 0 | const BOOL rc = winpr_setApplicationDetails(str, "WinPR", -1); |
124 | 0 | free(str); |
125 | 0 | updateDetailsString(); |
126 | 0 | return rc; |
127 | 0 | } |
128 | | |
129 | | const char* freerdp_getApplicationDetailsVendor(void) |
130 | 260 | { |
131 | 260 | if (!initializeApplicationDetails()) |
132 | 0 | return nullptr; |
133 | 260 | return s_freerdp_vendor_string; |
134 | 260 | } |
135 | | |
136 | | const char* freerdp_getApplicationDetailsProduct(void) |
137 | 260 | { |
138 | 260 | if (!initializeApplicationDetails()) |
139 | 0 | return nullptr; |
140 | 260 | return s_freerdp_product_string; |
141 | 260 | } |
142 | | |
143 | | #if !defined(WITHOUT_FREERDP_3x_DEPRECATED) |
144 | | char* freerdp_getApplicatonDetailsRegKey(const char* fmt) |
145 | 100 | { |
146 | 100 | char* val = freerdp_getApplicatonDetailsCombined('\\'); |
147 | 100 | if (!val) |
148 | 0 | return nullptr; |
149 | | |
150 | 100 | char* str = nullptr; |
151 | 100 | size_t slen = 0; |
152 | 100 | (void)winpr_asprintf(&str, &slen, fmt, val); |
153 | 100 | free(val); |
154 | 100 | return str; |
155 | 100 | } |
156 | | #endif |
157 | | |
158 | | char* freerdp_getApplicatonDetailsCombined(char separator) |
159 | 100 | { |
160 | 100 | const SSIZE_T version = freerdp_getApplicationDetailsVersion(); |
161 | 100 | const char* vendor = freerdp_getApplicationDetailsVendor(); |
162 | 100 | const char* product = freerdp_getApplicationDetailsProduct(); |
163 | | |
164 | 100 | size_t slen = 0; |
165 | 100 | char* str = nullptr; |
166 | 100 | if (version < 0) |
167 | 100 | { |
168 | 100 | (void)winpr_asprintf(&str, &slen, "%s%c%s", vendor, separator, product); |
169 | 100 | } |
170 | 0 | else |
171 | 0 | { |
172 | 0 | (void)winpr_asprintf(&str, &slen, "%s%c%s%" PRIdz, vendor, separator, product, version); |
173 | 0 | } |
174 | | |
175 | 100 | return str; |
176 | 100 | } |
177 | | |
178 | | SSIZE_T freerdp_getApplicationDetailsVersion(void) |
179 | 140 | { |
180 | 140 | if (!initializeApplicationDetails()) |
181 | 0 | return -1; |
182 | 140 | return s_freerdp_version; |
183 | 140 | } |
184 | | |
185 | | const char* freerdp_getApplicationDetailsString(void) |
186 | 80 | { |
187 | 80 | return s_freerdp_details_string; |
188 | 80 | } |
189 | | |
190 | | const WCHAR* freerdp_getApplicationDetailsStringW(void) |
191 | 0 | { |
192 | 0 | return s_freerdp_details_string_w; |
193 | 0 | } |
194 | | |
195 | | BOOL freerdp_areApplicationDetailsCustomized(void) |
196 | 40 | { |
197 | 40 | return s_freerdp_app_details_are_custom; |
198 | 40 | } |
199 | | |
200 | | #if !defined(WITH_FULL_CONFIG_PATH) |
201 | | WINPR_ATTR_MALLOC(free, 1) |
202 | | WINPR_ATTR_NODISCARD |
203 | | static char* freerdp_settings_get_legacy_config_path(const char* filename) |
204 | 20 | { |
205 | 20 | char product[sizeof(FREERDP_PRODUCT_STRING)] = WINPR_C_ARRAY_INIT; |
206 | | |
207 | 180 | for (size_t i = 0; i < sizeof(product); i++) |
208 | 160 | product[i] = (char)tolower(FREERDP_PRODUCT_STRING[i]); |
209 | | |
210 | 20 | char* path = GetKnownSubPath(KNOWN_PATH_XDG_CONFIG_HOME, product); |
211 | | |
212 | 20 | if (!path) |
213 | 0 | return nullptr; |
214 | | |
215 | 20 | char* filepath = GetCombinedPath(path, filename); |
216 | 20 | free(path); |
217 | 20 | return filepath; |
218 | 20 | } |
219 | | #endif |
220 | | |
221 | | WINPR_ATTR_NODISCARD |
222 | | WINPR_ATTR_MALLOC(free, 1) static char* getCustomConfigPath(BOOL system, const char* filename) |
223 | 0 | { |
224 | 0 | eKnownPathTypes id = system ? KNOWN_PATH_SYSTEM_CONFIG_HOME : KNOWN_PATH_XDG_CONFIG_HOME; |
225 | |
|
226 | 0 | const char* vendor = freerdp_getApplicationDetailsVendor(); |
227 | 0 | const char* product = freerdp_getApplicationDetailsProduct(); |
228 | 0 | const SSIZE_T version = freerdp_getApplicationDetailsVersion(); |
229 | |
|
230 | 0 | if (!vendor || !product) |
231 | 0 | return nullptr; |
232 | | |
233 | 0 | char* config = GetKnownSubPathV(id, "%s", vendor); |
234 | 0 | if (!config) |
235 | 0 | return nullptr; |
236 | | |
237 | 0 | char* base = nullptr; |
238 | 0 | if (version < 0) |
239 | 0 | base = GetCombinedPathV(config, "%s", product); |
240 | 0 | else |
241 | 0 | base = GetCombinedPathV(config, "%s%" PRIdz, product, version); |
242 | 0 | free(config); |
243 | |
|
244 | 0 | if (!base) |
245 | 0 | return nullptr; |
246 | | |
247 | 0 | if (!filename) |
248 | 0 | return base; |
249 | | |
250 | 0 | char* path = GetCombinedPathV(base, "%s", filename); |
251 | 0 | free(base); |
252 | 0 | return path; |
253 | 0 | } |
254 | | |
255 | | char* freerdp_GetConfigFilePath(BOOL system, const char* filename) |
256 | 40 | { |
257 | | #if defined(FREERDP_USE_VENDOR_PRODUCT_CONFIG_DIR) |
258 | | const BOOL customized = TRUE; |
259 | | #else |
260 | 40 | const BOOL customized = freerdp_areApplicationDetailsCustomized(); |
261 | 40 | #endif |
262 | 40 | if (customized) |
263 | 0 | return getCustomConfigPath(system, filename); |
264 | | |
265 | 40 | eKnownPathTypes id = system ? KNOWN_PATH_SYSTEM_CONFIG_HOME : KNOWN_PATH_XDG_CONFIG_HOME; |
266 | | |
267 | 40 | const char* vendor = freerdp_getApplicationDetailsVendor(); |
268 | 40 | const char* product = freerdp_getApplicationDetailsProduct(); |
269 | 40 | const SSIZE_T version = freerdp_getApplicationDetailsVersion(); |
270 | | |
271 | 40 | if (!vendor || !product) |
272 | 0 | return nullptr; |
273 | | |
274 | 40 | #if !defined(WITH_FULL_CONFIG_PATH) |
275 | 40 | if (!system && (_stricmp(vendor, product) == 0)) |
276 | 20 | return freerdp_settings_get_legacy_config_path(filename); |
277 | 20 | #endif |
278 | | |
279 | 20 | char* config = GetKnownPath(id); |
280 | 20 | if (!config) |
281 | 0 | return nullptr; |
282 | | |
283 | 20 | char* base = nullptr; |
284 | 20 | if (version < 0) |
285 | 20 | base = GetCombinedPathV(config, "%s", product); |
286 | 0 | else |
287 | 0 | base = GetCombinedPathV(config, "%s%" PRIdz, product, version); |
288 | 20 | free(config); |
289 | | |
290 | 20 | if (!base) |
291 | 0 | return nullptr; |
292 | | |
293 | 20 | if (!filename) |
294 | 0 | return base; |
295 | | |
296 | 20 | char* path = GetCombinedPathV(base, "%s", filename); |
297 | 20 | free(base); |
298 | 20 | return path; |
299 | 20 | } |
300 | | |
301 | | WINPR_JSON* freerdp_GetJSONConfigFile(BOOL system, const char* filename) |
302 | 20 | { |
303 | 20 | char* path = freerdp_GetConfigFilePath(system, filename); |
304 | 20 | if (!path) |
305 | 0 | return nullptr; |
306 | | |
307 | 20 | WINPR_JSON* json = WINPR_JSON_ParseFromFile(path); |
308 | 20 | free(path); |
309 | 20 | return json; |
310 | 20 | } |