/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 | 3 | { |
43 | 3 | const char* vendor = s_freerdp_vendor_string; |
44 | 3 | const char* product = s_freerdp_product_string; |
45 | 3 | const SSIZE_T version = s_freerdp_version; |
46 | | |
47 | 3 | WINPR_ASSERT(vendor); |
48 | 3 | WINPR_ASSERT(product); |
49 | 3 | 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 | 3 | else if (version < 0) |
59 | 3 | { |
60 | 3 | (void)_snprintf(s_freerdp_details_string, sizeof(s_freerdp_details_string) - 1, "%s", |
61 | 3 | product); |
62 | 3 | } |
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 | 3 | (void)ConvertUtf8NToWChar(s_freerdp_details_string, sizeof(s_freerdp_details_string), |
68 | 3 | s_freerdp_details_string_w, sizeof(s_freerdp_details_string_w) - 1); |
69 | 3 | } |
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 | 3 | { |
75 | 3 | const size_t vlen = sizeof(FREERDP_VENDOR_STRING); |
76 | 3 | const size_t plen = sizeof(FREERDP_PRODUCT_STRING); |
77 | 3 | const char* rvlen = strncpy(s_freerdp_vendor_string, FREERDP_VENDOR_STRING, vlen); |
78 | 3 | const char* rplen = strncpy(s_freerdp_product_string, FREERDP_PRODUCT_STRING, plen); |
79 | 3 | if (!rvlen || !rplen) |
80 | 0 | return FALSE; |
81 | | |
82 | | #if defined(WITH_RESOURCE_VERSIONING) |
83 | | s_freerdp_version = FREERDP_VERSION_MAJOR; |
84 | | #else |
85 | 3 | s_freerdp_version = -1; |
86 | 3 | #endif |
87 | 3 | updateDetailsString(); |
88 | 3 | return TRUE; |
89 | 3 | } |
90 | | |
91 | | WINPR_ATTR_NODISCARD |
92 | | static BOOL initializeApplicationDetails(void) |
93 | 1.07M | { |
94 | 1.07M | InitOnceExecuteOnce(&s_freerdp_app_details_once, init_app_details, NULL, NULL); |
95 | 1.07M | return TRUE; |
96 | 1.07M | } |
97 | | |
98 | | BOOL freerdp_setApplicationDetails(const char* vendor, const char* product, SSIZE_T version) |
99 | 0 | { |
100 | 0 | if (!initializeApplicationDetails()) |
101 | 0 | return -1; |
102 | | |
103 | 0 | if (!vendor || !product) |
104 | 0 | return FALSE; |
105 | 0 | const size_t vlen = strnlen(vendor, MAX_PATH); |
106 | 0 | const size_t plen = strnlen(product, MAX_PATH); |
107 | 0 | if ((vlen == MAX_PATH) || (plen == MAX_PATH)) |
108 | 0 | return FALSE; |
109 | | |
110 | 0 | if (!strncpy(s_freerdp_vendor_string, vendor, vlen + 1)) |
111 | 0 | return FALSE; |
112 | | |
113 | 0 | if (!strncpy(s_freerdp_product_string, product, plen + 1)) |
114 | 0 | return FALSE; |
115 | | |
116 | 0 | s_freerdp_version = version; |
117 | 0 | s_freerdp_app_details_are_custom = TRUE; |
118 | |
|
119 | 0 | const char separator = PathGetSeparatorA(PATH_STYLE_NATIVE); |
120 | 0 | char* str = freerdp_getApplicatonDetailsCombined(separator); |
121 | 0 | if (!str) |
122 | 0 | return FALSE; |
123 | | |
124 | 0 | const BOOL rc = winpr_setApplicationDetails(str, "WinPR", -1); |
125 | 0 | free(str); |
126 | 0 | updateDetailsString(); |
127 | 0 | return rc; |
128 | 0 | } |
129 | | |
130 | | const char* freerdp_getApplicationDetailsVendor(void) |
131 | 415k | { |
132 | 415k | if (!initializeApplicationDetails()) |
133 | 0 | return NULL; |
134 | 415k | return s_freerdp_vendor_string; |
135 | 415k | } |
136 | | |
137 | | const char* freerdp_getApplicationDetailsProduct(void) |
138 | 415k | { |
139 | 415k | if (!initializeApplicationDetails()) |
140 | 0 | return NULL; |
141 | 415k | return s_freerdp_product_string; |
142 | 415k | } |
143 | | |
144 | | char* freerdp_getApplicatonDetailsRegKey(const char* fmt) |
145 | 198k | { |
146 | 198k | char* val = freerdp_getApplicatonDetailsCombined('\\'); |
147 | 198k | if (!val) |
148 | 0 | return NULL; |
149 | | |
150 | 198k | char* str = NULL; |
151 | 198k | size_t slen = 0; |
152 | 198k | (void)winpr_asprintf(&str, &slen, fmt, val); |
153 | 198k | free(val); |
154 | 198k | return str; |
155 | 198k | } |
156 | | |
157 | | char* freerdp_getApplicatonDetailsCombined(char separator) |
158 | 198k | { |
159 | 198k | const SSIZE_T version = freerdp_getApplicationDetailsVersion(); |
160 | 198k | const char* vendor = freerdp_getApplicationDetailsVendor(); |
161 | 198k | const char* product = freerdp_getApplicationDetailsProduct(); |
162 | | |
163 | 198k | size_t slen = 0; |
164 | 198k | char* str = NULL; |
165 | 198k | if (version < 0) |
166 | 198k | { |
167 | 198k | (void)winpr_asprintf(&str, &slen, "%s%c%s", vendor, separator, product); |
168 | 198k | } |
169 | 0 | else |
170 | 0 | { |
171 | 0 | (void)winpr_asprintf(&str, &slen, "%s%c%s%" PRIdz, vendor, separator, product, version); |
172 | 0 | } |
173 | | |
174 | 198k | return str; |
175 | 198k | } |
176 | | |
177 | | SSIZE_T freerdp_getApplicationDetailsVersion(void) |
178 | 241k | { |
179 | 241k | if (!initializeApplicationDetails()) |
180 | 0 | return -1; |
181 | 241k | return s_freerdp_version; |
182 | 241k | } |
183 | | |
184 | | const char* freerdp_getApplicationDetailsString(void) |
185 | 115k | { |
186 | 115k | return s_freerdp_details_string; |
187 | 115k | } |
188 | | |
189 | | const WCHAR* freerdp_getApplicationDetailsStringW(void) |
190 | 0 | { |
191 | 0 | return s_freerdp_details_string_w; |
192 | 0 | } |
193 | | |
194 | | BOOL freerdp_areApplicationDetailsCustomized(void) |
195 | 43.2k | { |
196 | 43.2k | return s_freerdp_app_details_are_custom; |
197 | 43.2k | } |
198 | | |
199 | | #if !defined(WITH_FULL_CONFIG_PATH) |
200 | | WINPR_ATTR_MALLOC(free, 1) |
201 | | WINPR_ATTR_NODISCARD |
202 | | static char* freerdp_settings_get_legacy_config_path(const char* filename) |
203 | 43.2k | { |
204 | 43.2k | char product[sizeof(FREERDP_PRODUCT_STRING)] = WINPR_C_ARRAY_INIT; |
205 | | |
206 | 389k | for (size_t i = 0; i < sizeof(product); i++) |
207 | 346k | product[i] = (char)tolower(FREERDP_PRODUCT_STRING[i]); |
208 | | |
209 | 43.2k | char* path = GetKnownSubPath(KNOWN_PATH_XDG_CONFIG_HOME, product); |
210 | | |
211 | 43.2k | if (!path) |
212 | 0 | return NULL; |
213 | | |
214 | 43.2k | char* filepath = GetCombinedPath(path, filename); |
215 | 43.2k | free(path); |
216 | 43.2k | return filepath; |
217 | 43.2k | } |
218 | | #endif |
219 | | |
220 | | WINPR_ATTR_NODISCARD |
221 | | WINPR_ATTR_MALLOC(free, 1) static char* getCustomConfigPath(BOOL system, const char* filename) |
222 | 0 | { |
223 | 0 | eKnownPathTypes id = system ? KNOWN_PATH_SYSTEM_CONFIG_HOME : KNOWN_PATH_XDG_CONFIG_HOME; |
224 | |
|
225 | 0 | const char* vendor = freerdp_getApplicationDetailsVendor(); |
226 | 0 | const char* product = freerdp_getApplicationDetailsProduct(); |
227 | 0 | const SSIZE_T version = freerdp_getApplicationDetailsVersion(); |
228 | |
|
229 | 0 | if (!vendor || !product) |
230 | 0 | return NULL; |
231 | | |
232 | 0 | char* config = GetKnownSubPathV(id, "%s", vendor); |
233 | 0 | if (!config) |
234 | 0 | return NULL; |
235 | | |
236 | 0 | char* base = NULL; |
237 | 0 | if (version < 0) |
238 | 0 | base = GetCombinedPathV(config, "%s", product); |
239 | 0 | else |
240 | 0 | base = GetCombinedPathV(config, "%s%" PRIdz, product, version); |
241 | 0 | free(config); |
242 | |
|
243 | 0 | if (!base) |
244 | 0 | return NULL; |
245 | | |
246 | 0 | if (!filename) |
247 | 0 | return base; |
248 | | |
249 | 0 | char* path = GetCombinedPathV(base, "%s", filename); |
250 | 0 | free(base); |
251 | 0 | return path; |
252 | 0 | } |
253 | | |
254 | | char* freerdp_GetConfigFilePath(BOOL system, const char* filename) |
255 | 43.2k | { |
256 | | #if defined(FREERDP_USE_VENDOR_PRODUCT_CONFIG_DIR) |
257 | | const BOOL customized = TRUE; |
258 | | #else |
259 | 43.2k | const BOOL customized = freerdp_areApplicationDetailsCustomized(); |
260 | 43.2k | #endif |
261 | 43.2k | if (customized) |
262 | 0 | return getCustomConfigPath(system, filename); |
263 | | |
264 | 43.2k | eKnownPathTypes id = system ? KNOWN_PATH_SYSTEM_CONFIG_HOME : KNOWN_PATH_XDG_CONFIG_HOME; |
265 | | |
266 | 43.2k | const char* vendor = freerdp_getApplicationDetailsVendor(); |
267 | 43.2k | const char* product = freerdp_getApplicationDetailsProduct(); |
268 | 43.2k | const SSIZE_T version = freerdp_getApplicationDetailsVersion(); |
269 | | |
270 | 43.2k | if (!vendor || !product) |
271 | 0 | return NULL; |
272 | | |
273 | 43.2k | #if !defined(WITH_FULL_CONFIG_PATH) |
274 | 43.2k | if (!system && (_stricmp(vendor, product) == 0)) |
275 | 43.2k | return freerdp_settings_get_legacy_config_path(filename); |
276 | 0 | #endif |
277 | | |
278 | 0 | char* config = GetKnownPath(id); |
279 | 0 | if (!config) |
280 | 0 | return NULL; |
281 | | |
282 | 0 | char* base = NULL; |
283 | 0 | if (version < 0) |
284 | 0 | base = GetCombinedPathV(config, "%s", product); |
285 | 0 | else |
286 | 0 | base = GetCombinedPathV(config, "%s%" PRIdz, product, version); |
287 | 0 | free(config); |
288 | |
|
289 | 0 | if (!base) |
290 | 0 | return NULL; |
291 | | |
292 | 0 | if (!filename) |
293 | 0 | return base; |
294 | | |
295 | 0 | char* path = GetCombinedPathV(base, "%s", filename); |
296 | 0 | free(base); |
297 | 0 | return path; |
298 | 0 | } |
299 | | |
300 | | WINPR_JSON* freerdp_GetJSONConfigFile(BOOL system, const char* filename) |
301 | 0 | { |
302 | 0 | char* path = freerdp_GetConfigFilePath(system, filename); |
303 | 0 | if (!path) |
304 | 0 | return NULL; |
305 | | |
306 | 0 | WINPR_JSON* json = WINPR_JSON_ParseFromFile(path); |
307 | 0 | free(path); |
308 | 0 | return json; |
309 | 0 | } |