GCC Code Coverage Report


Directory: src/gate/
File: src/gate/uris.c
Date: 2025-09-14 13:10:38
Exec Total Coverage
Lines: 185 213 86.9%
Functions: 11 11 100.0%
Branches: 109 174 62.6%

Line Branch Exec Source
1 /* GATE PROJECT LICENSE:
2 +----------------------------------------------------------------------------+
3 | Copyright(c) 2018-2025, Stefan Meislinger <sm@opengate.at> |
4 | All rights reserved. |
5 | |
6 | Redistribution and use in source and binary forms, with or without |
7 | modification, are permitted provided that the following conditions are met:|
8 | |
9 | 1. Redistributions of source code must retain the above copyright notice, |
10 | this list of conditions and the following disclaimer. |
11 | 2. Redistributions in binary form must reproduce the above copyright |
12 | notice, this list of conditions and the following disclaimer in the |
13 | documentation and/or other materials provided with the distribution. |
14 | |
15 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"|
16 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
17 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
18 | ARE DISCLAIMED.IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE |
19 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
20 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
21 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
22 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
23 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
24 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF |
25 | THE POSSIBILITY OF SUCH DAMAGE. |
26 +----------------------------------------------------------------------------+
27 */
28
29 #include "gate/uris.h"
30 #include "gate/results.h"
31
32 static gate_string_t const gate_uri_scheme_separator = GATE_STRING_INIT_STATIC(GATE_URI_SCHEME_SEPARATOR);
33
34 static gate_string_t const gate_uri_userinfo_separator = GATE_STRING_INIT_STATIC("@");
35 static gate_string_t const gate_uri_path_separator = GATE_STRING_INIT_STATIC("/");
36 static gate_string_t const gate_uri_query_separator = GATE_STRING_INIT_STATIC("?");
37
38
39 12 gate_result_t gate_uri_init(gate_uri_t* uri)
40 {
41
1/2
✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
12 if (uri)
42 {
43 12 gate_mem_clear(uri, sizeof(gate_uri_t));
44 12 return GATE_RESULT_OK;
45 }
46 else
47 {
48 return GATE_RESULT_NULLPOINTER;
49 }
50 }
51
52 2 gate_result_t gate_uri_copy(gate_uri_t* target, gate_uri_t const* src)
53 {
54 2 gate_result_t result = GATE_RESULT_OUTOFMEMORY;
55
56
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (!target)
57 {
58 return GATE_RESULT_NULLPOINTER;
59 }
60
61 do
62 {
63 2 gate_mem_clear(target, sizeof(gate_uri_t));
64
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 if (src)
65 {
66
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
2 if (NULL == gate_string_clone(&target->scheme, &src->scheme)) break;
67
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
2 if (NULL == gate_string_clone(&target->user_info, &src->user_info)) break;
68
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
2 if (NULL == gate_string_clone(&target->host, &src->host)) break;
69 2 target->port = src->port;
70
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
2 if (NULL == gate_string_clone(&target->absolute_path, &src->absolute_path)) break;
71
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
2 if (NULL == gate_string_clone(&target->query, &src->query)) break;
72 }
73
74 2 result = GATE_RESULT_OK;
75 } while (0);
76
77
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (GATE_FAILED(result))
78 {
79 gate_uri_destroy(target);
80 }
81
82 2 return result;
83 }
84
85
86 13 gate_result_t gate_uri_destroy(gate_uri_t* uri)
87 {
88
1/2
✓ Branch 0 taken 13 times.
✗ Branch 1 not taken.
13 if (uri)
89 {
90 13 gate_string_release(&uri->scheme);
91 13 gate_string_release(&uri->user_info);
92 13 gate_string_release(&uri->host);
93 13 uri->port = 0;
94 13 gate_string_release(&uri->absolute_path);
95 13 gate_string_release(&uri->query);
96 13 gate_mem_clear(uri, sizeof(gate_uri_t));
97 13 return GATE_RESULT_OK;
98 }
99 else
100 {
101 return GATE_RESULT_NULLPOINTER;
102 }
103 }
104
105 6 gate_result_t gate_uri_parse_path(gate_string_t const* path, gate_string_t* abs_path, gate_string_t* query_part)
106 {
107
2/2
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 5 times.
6 if (gate_string_is_empty(path))
108 {
109
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if (abs_path)
110 {
111 1 gate_string_create_static_len(abs_path, gate_uri_path_separator.str, gate_uri_path_separator.length);
112 }
113
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if (query_part)
114 {
115 1 gate_string_create_empty(query_part);
116 }
117 }
118 else
119 {
120
2/2
✓ Branch 1 taken 3 times.
✓ Branch 2 taken 2 times.
5 if (0 == gate_string_parse(path, &gate_uri_query_separator, 0, abs_path, query_part, true))
121 {
122
1/2
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
3 if (abs_path)
123 {
124 3 gate_string_duplicate(abs_path, path);
125 }
126
1/2
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
3 if (query_part)
127 {
128 3 gate_string_create_empty(query_part);
129 }
130 }
131 }
132 6 return GATE_RESULT_OK;
133 }
134
135
136 4 gate_result_t gate_uri_parse(gate_uri_t* uri, gate_string_t const* text)
137 {
138 4 gate_result_t ret = GATE_RESULT_FAILED;
139 gate_size_t pos;
140 4 gate_string_t source = GATE_STRING_INIT_EMPTY;
141 4 gate_string_t part = GATE_STRING_INIT_EMPTY;
142 4 gate_string_t address = GATE_STRING_INIT_EMPTY;
143 4 gate_string_t path = GATE_STRING_INIT_EMPTY;
144 4 gate_string_t hostport = GATE_STRING_INIT_EMPTY;
145 4 gate_int64_t num = 0;
146
147 do
148 {
149
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 4 times.
4 if (NULL == gate_string_clone(&source, text))
150 {
151 ret = GATE_RESULT_OUTOFMEMORY;
152 break;
153 }
154 4 gate_uri_init(uri);
155
156
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 4 times.
4 if (!gate_string_parse(&source, &gate_uri_scheme_separator, 0, &uri->scheme, &part, false))
157 {
158 break;
159 }
160
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 4 times.
4 if (!gate_string_parse(&part, &gate_uri_path_separator, 0, &address, &path, true))
161 {
162 gate_string_clone(&address, &part);
163 }
164
165 4 gate_uri_parse_path(&path, &uri->absolute_path, &uri->query);
166
167
2/2
✓ Branch 1 taken 3 times.
✓ Branch 2 taken 1 times.
4 if (!gate_string_parse(&address, &gate_uri_userinfo_separator, 0, &uri->user_info, &hostport, false))
168 {
169 3 gate_string_clone(&hostport, &address);
170 }
171 4 pos = gate_string_char_pos(&hostport, ':', 0);
172 4 uri->port = 0;
173
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 1 times.
4 if (pos == GATE_STR_NPOS)
174 {
175 3 gate_string_clone(&uri->host, &hostport);
176 }
177 else
178 {
179 1 gate_string_substr(&uri->host, &hostport, 0, pos);
180 1 gate_str_parse_int64(gate_string_ptr(&hostport, pos + 1),
181 1 gate_string_length(&hostport) - pos - 1,
182 &num);
183 1 uri->port = (gate_int32_t)num;
184 }
185 4 ret = GATE_RESULT_OK;
186
187 } while (0);
188
189 4 gate_string_release(&hostport);
190 4 gate_string_release(&path);
191 4 gate_string_release(&address);
192 4 gate_string_release(&part);
193 4 gate_string_release(&source);
194
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 if (GATE_FAILED(ret))
195 {
196 gate_uri_destroy(uri);
197 }
198 4 pos = gate_string_pos(text, &gate_uri_scheme_separator, 0);
199 4 return ret;
200 }
201 9 gate_result_t gate_uri_to_string(gate_uri_t const* uri, gate_string_t* text, gate_bool_t absolute_path_only)
202 {
203 9 gate_result_t ret = GATE_RESULT_OUTOFMEMORY;
204 gate_strbuilder_t builder;
205 do
206 {
207 9 gate_strbuilder_create(&builder, 128);
208
209
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 3 times.
9 if (!absolute_path_only)
210 {
211
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 6 times.
6 if (0 == gate_strbuilder_append_string(&builder, &uri->scheme)) break;
212
213
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 6 times.
6 if (0 == gate_strbuilder_append_string(&builder, &gate_uri_scheme_separator)) break;
214
2/2
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 4 times.
6 if (!gate_string_is_empty(&uri->user_info))
215 {
216
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
2 if (0 == gate_strbuilder_append_string(&builder, &uri->user_info)) break;
217
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
2 if (0 == gate_strbuilder_append_text(&builder, "@", 1)) break;
218 }
219
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 6 times.
6 if (0 == gate_strbuilder_append_string(&builder, &uri->host)) break;
220
1/2
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
6 if (uri->port != 0)
221 {
222
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 6 times.
6 if (0 == gate_strbuilder_append_text(&builder, ":", 1)) break;
223
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 6 times.
6 if (0 == gate_strbuilder_append_int32(&builder, uri->port)) break;
224 }
225 }
226
227
2/4
✓ Branch 1 taken 9 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 9 times.
9 if (gate_string_is_empty(&uri->absolute_path) || uri->absolute_path.str[0] != '/')
228 {
229 if (0 == gate_strbuilder_append_text(&builder, "/", 1)) break;
230 }
231
1/2
✓ Branch 1 taken 9 times.
✗ Branch 2 not taken.
9 if (!gate_string_is_empty(&uri->absolute_path))
232 {
233
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 9 times.
9 if (0 == gate_strbuilder_append_string(&builder, &uri->absolute_path)) break;
234 }
235
236
2/2
✓ Branch 1 taken 4 times.
✓ Branch 2 taken 5 times.
9 if (!gate_string_is_empty(&uri->query))
237 {
238
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
4 if ((uri->query.str[0] != '?') && (uri->query.str[0] != '#'))
239 {
240 if (0 == gate_strbuilder_append_text(&builder, "?", 1)) break;
241 }
242
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 4 times.
4 if (0 == gate_strbuilder_append_string(&builder, &uri->query)) break;
243 }
244
245
1/2
✓ Branch 1 taken 9 times.
✗ Branch 2 not taken.
9 if (gate_strbuilder_to_string(&builder, text))
246 {
247 9 ret = GATE_RESULT_OK;
248 }
249 } while (0);
250
251 9 gate_strbuilder_release(&builder);
252 9 return ret;
253 }
254
255 54 static gate_bool_t gate_uri_is_allowed_char(char c)
256 {
257 return (
258
1/2
✓ Branch 0 taken 31 times.
✗ Branch 1 not taken.
31 ((c >= 'A') && (c <= 'Z'))
259
4/4
✓ Branch 0 taken 27 times.
✓ Branch 1 taken 27 times.
✓ Branch 2 taken 3 times.
✓ Branch 3 taken 24 times.
54 || ((c >= 'a') && (c <= 'z'))
260
3/4
✓ Branch 0 taken 13 times.
✓ Branch 1 taken 17 times.
✓ Branch 2 taken 13 times.
✗ Branch 3 not taken.
30 || ((c >= '0') && (c <= '9'))
261
2/2
✓ Branch 0 taken 29 times.
✓ Branch 1 taken 1 times.
30 || (c == '_')
262
2/2
✓ Branch 0 taken 28 times.
✓ Branch 1 taken 1 times.
29 || (c == '-')
263
2/2
✓ Branch 0 taken 27 times.
✓ Branch 1 taken 1 times.
28 || (c == '.')
264
2/2
✓ Branch 0 taken 26 times.
✓ Branch 1 taken 1 times.
27 || (c == '*')
265
2/2
✓ Branch 0 taken 25 times.
✓ Branch 1 taken 1 times.
26 || (c == '!')
266
1/2
✓ Branch 0 taken 25 times.
✗ Branch 1 not taken.
25 || (c == '~')
267
2/2
✓ Branch 0 taken 24 times.
✓ Branch 1 taken 1 times.
25 || (c == '\'')
268
2/2
✓ Branch 0 taken 23 times.
✓ Branch 1 taken 1 times.
24 || (c == '(')
269
4/4
✓ Branch 0 taken 31 times.
✓ Branch 1 taken 23 times.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 22 times.
108 || (c == ')')
270 );
271 }
272
273 6 gate_result_t gate_uri_escape(gate_string_t const* src, gate_string_t* dest)
274 {
275 6 gate_result_t ret = GATE_RESULT_OUTOFMEMORY;
276 6 gate_strbuilder_t builder = GATE_INIT_EMPTY;
277 gate_size_t len;
278 char const* ptr;
279 6 char hexbuf[4] = "%__";
280 do
281 {
282 6 len = gate_string_length(src);
283
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 5 times.
6 if (len == 0)
284 {
285 1 gate_string_create_empty(dest);
286 1 ret = GATE_RESULT_OK;
287 1 break;
288 }
289 5 gate_strbuilder_create(&builder, len + len / 2 + 2);
290
291 5 ret = GATE_RESULT_OK;
292
2/2
✓ Branch 0 taken 54 times.
✓ Branch 1 taken 5 times.
59 for (ptr = src->str; len != 0; ++ptr, --len)
293 {
294
2/2
✓ Branch 1 taken 32 times.
✓ Branch 2 taken 22 times.
54 if (gate_uri_is_allowed_char(*ptr))
295 {
296
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 32 times.
32 if (0 == gate_strbuilder_append_text(&builder, ptr, 1))
297 {
298 ret = GATE_RESULT_OUTOFMEMORY;
299 break;
300 }
301 }
302 else
303 {
304 22 gate_str_print_hex_byte(&hexbuf[1], 2, (gate_uint8_t)*ptr, true);
305
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 22 times.
22 if (3 != gate_strbuilder_append_text(&builder, hexbuf, 3))
306 {
307 ret = GATE_RESULT_OUTOFMEMORY;
308 break;
309 }
310 }
311 }
312
1/2
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
5 if (GATE_SUCCEEDED(ret))
313 {
314
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 5 times.
5 if (!gate_strbuilder_to_string(&builder, dest))
315 {
316 ret = GATE_RESULT_OUTOFMEMORY;
317 }
318 }
319
320 } while (0);
321 6 gate_strbuilder_release(&builder);
322 6 return ret;
323 }
324 4 gate_result_t gate_uri_unescape(gate_string_t const* src, gate_string_t* dest)
325 {
326 4 gate_result_t ret = GATE_RESULT_OUTOFMEMORY;
327 4 gate_strbuilder_t builder = GATE_INIT_EMPTY;
328 gate_size_t len;
329 char const* ptr;
330 gate_uint8_t u8;
331 do
332 {
333 4 len = gate_string_length(src);
334
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 3 times.
4 if (len == 0)
335 {
336 1 gate_string_create_empty(dest);
337 1 ret = GATE_RESULT_OK;
338 1 break;
339 }
340 3 gate_strbuilder_create(&builder, len);
341
342 3 ret = GATE_RESULT_OK;
343
2/2
✓ Branch 0 taken 42 times.
✓ Branch 1 taken 3 times.
45 for (ptr = src->str; len != 0; ++ptr, --len)
344 {
345
2/2
✓ Branch 0 taken 22 times.
✓ Branch 1 taken 20 times.
42 if (*ptr == '%')
346 {
347
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 22 times.
22 if (len < 3)
348 {
349 ret = GATE_RESULT_INVALIDDATA;
350 break;
351 }
352
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 22 times.
22 if (!gate_str_parse_hex_byte(&ptr[1], &u8))
353 {
354 ret = GATE_RESULT_INVALIDDATA;
355 break;
356 }
357
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 22 times.
22 if (0 == gate_strbuilder_append_text(&builder, (char const*)&u8, 1))
358 {
359 ret = GATE_RESULT_OUTOFMEMORY;
360 break;
361 }
362 22 ptr += 2;
363 22 len -= 2;
364 }
365 else
366 {
367
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 20 times.
20 if (0 == gate_strbuilder_append_text(&builder, ptr, 1))
368 {
369 ret = GATE_RESULT_OUTOFMEMORY;
370 break;
371 }
372 }
373 }
374
1/2
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
3 if (GATE_SUCCEEDED(ret))
375 {
376
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 3 times.
3 if (!gate_strbuilder_to_string(&builder, dest))
377 {
378 ret = GATE_RESULT_OUTOFMEMORY;
379 }
380 }
381 } while (0);
382 4 gate_strbuilder_release(&builder);
383 4 return ret;
384 }
385
386 4 gate_result_t gate_uri_parse_user_info(gate_string_t const* src, gate_string_t* username, gate_string_t* password)
387 {
388 4 gate_result_t ret = GATE_RESULT_OUTOFMEMORY;
389 gate_size_t pos;
390 gate_string_t str_user, str_pass;
391 do
392 {
393
2/2
✓ Branch 1 taken 3 times.
✓ Branch 2 taken 1 times.
4 if (gate_string_is_empty(src))
394 {
395 3 gate_string_create_empty(username);
396 3 gate_string_create_empty(password);
397 3 ret = GATE_RESULT_OK;
398 }
399 else
400 {
401 1 pos = gate_string_char_pos(src, ':', 0);
402
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (pos == GATE_STR_NPOS)
403 {
404 if (gate_string_create(username, src->str, src->length))
405 {
406 ret = GATE_RESULT_OK;
407 }
408 }
409 else
410 {
411 1 gate_string_create_static_len(&str_user, src->str, pos);
412 1 gate_string_create_static_len(&str_pass, src->str + pos + 1, src->length - pos - 1);
413 1 ret = gate_uri_unescape(&str_user, username);
414
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 GATE_BREAK_IF_FAILED(ret);
415 1 ret = gate_uri_unescape(&str_pass, password);
416
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (GATE_FAILED(ret))
417 {
418 gate_string_release(username);
419 }
420 }
421 }
422 } while (0);
423 4 return ret;
424 }
425 1 gate_result_t gate_uri_build_user_info(gate_string_t const* username, gate_string_t const* password, gate_string_t* user_info)
426 {
427 1 gate_result_t ret = GATE_RESULT_OUTOFMEMORY;
428 gate_strbuilder_t builder;
429 gate_size_t len;
430 1 gate_string_t str_user = GATE_STRING_INIT_EMPTY;
431 1 gate_string_t str_pass = GATE_STRING_INIT_EMPTY;
432 do
433 {
434 1 gate_strbuilder_create(&builder, gate_string_length(username) + gate_string_length(password) + 3);
435
436 1 len = gate_string_length(username);
437
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if (len > 0)
438 {
439
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
1 GATE_BREAK_IF_FAILED(gate_uri_escape(username, &str_user));
440
441
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
1 if (0 == gate_strbuilder_append_string(&builder, &str_user)) break;
442 }
443 1 len = gate_string_length(password);
444
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if (len != 0)
445 {
446
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
1 if (0 == gate_strbuilder_append_text(&builder, ":", 1)) break;
447
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
1 GATE_BREAK_IF_FAILED(gate_uri_escape(password, &str_pass));
448
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
1 if (0 == gate_strbuilder_append_string(&builder, &str_pass)) break;
449 }
450
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 if (gate_strbuilder_to_string(&builder, user_info))
451 {
452 1 ret = GATE_RESULT_OK;
453 }
454 } while (0);
455
456 1 gate_strbuilder_release(&builder);
457 1 gate_string_release(&str_user);
458 1 gate_string_release(&str_pass);
459 1 return ret;
460 }
461