Bug Summary

File:libsofia-sip-ua/url/url.c
Warning:line 989, column 19
Although the value stored to 'do_copy' is used in the enclosing expression, the value is never actually read from 'do_copy'

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-pc-linux-gnu -analyze -disable-free -disable-llvm-verifier -discard-value-names -main-file-name url.c -analyzer-store=region -analyzer-opt-analyze-nested-blocks -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model pic -pic-level 2 -mframe-pointer=all -fmath-errno -fno-rounding-math -mconstructor-aliases -munwind-tables -target-cpu x86-64 -fno-split-dwarf-inlining -debugger-tuning=gdb -resource-dir /usr/lib/llvm-11/lib/clang/11.0.1 -D HAVE_CONFIG_H -I . -I ../.. -I ../../libsofia-sip-ua/su/sofia-sip -I ./../bnf -I ../bnf -I ./../ipt -I ../ipt -I ./../su -I ../su -D SU_DEBUG=0 -D PIC -internal-isystem /usr/local/include -internal-isystem /usr/lib/llvm-11/lib/clang/11.0.1/include -internal-externc-isystem /usr/include/x86_64-linux-gnu -internal-externc-isystem /include -internal-externc-isystem /usr/include -fdebug-compilation-dir /drone/src/libsofia-sip-ua/url -ferror-limit 19 -fgnuc-version=4.2.1 -analyzer-output=html -faddrsig -o /drone/src/scan-build/2022-06-23-181620-12-1 -x c url.c
1/*
2 * This file is part of the Sofia-SIP package
3 *
4 * Copyright (C) 2005 Nokia Corporation.
5 *
6 * Contact: Pekka Pessi <pekka.pessi@nokia.com>
7 *
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public License
10 * as published by the Free Software Foundation; either version 2.1 of
11 * the License, or (at your option) any later version.
12 *
13 * This library is distributed in the hope that it will be useful, but
14 * WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
17 *
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
21 * 02110-1301 USA
22 *
23 */
24
25/**@CFILE url.c
26 *
27 * Implementation of basic URL parsing and handling.
28 *
29 * @author Pekka Pessi <Pekka.Pessi@nokia.com>
30 *
31 * @date Created: Thu Jun 29 22:44:37 2000 ppessi
32 */
33
34#include "config.h"
35
36#include <sofia-sip/su_alloc.h>
37#include <sofia-sip/bnf.h>
38#include <sofia-sip/hostdomain.h>
39#include <sofia-sip/url.h>
40
41#include <sofia-sip/string0.h>
42
43#include <stdio.h>
44#include <string.h>
45#include <stdlib.h>
46#include <assert.h>
47#include <ctype.h>
48#include <limits.h>
49
50/**@def URL_PRINT_FORMAT
51 * Format string used when printing url with printf().
52 *
53 * The macro URL_PRINT_FORMAT is used in format string of printf() or
54 * similar printing functions. A URL can be printed like this:
55 * @code
56 * printf("%s received URL " URL_PRINT_FORMAT "\n",
57 * my_name, URL_PRINT_ARGS(url));
58 * @endcode
59 */
60
61/** @def URL_PRINT_ARGS(u)
62 * Argument list used when printing url with printf().
63 *
64 * The macro URL_PRINT_ARGS() is used to create a stdarg list for printf()
65 * or similar printing functions. Using it, a URL can be printed like this:
66 *
67 * @code
68 * printf("%s received URL " URL_PRINT_FORMAT "\n",
69 * my_name, URL_PRINT_ARGS(url));
70 * @endcode
71 */
72
73#define RESERVED";/?:@&=+$," ";/?:@&=+$,"
74#define DELIMS"<>#%\"" "<>#%\""
75#define UNWISE"{}|\\^[]`" "{}|\\^[]`"
76
77#define EXCLUDED";/?:@&=+$," "<>#%\"" "{}|\\^[]`" RESERVED";/?:@&=+$," DELIMS"<>#%\"" UNWISE"{}|\\^[]`"
78
79#define UNRESERVED"ABCDEFGHIJKLMNOPQRSTUVWXYZ" "abcdefghijklmnopqrstuvwxyz" "0123456789"
"-_.!~*'()"
"ABCDEFGHIJKLMNOPQRSTUVWXYZ" \
80 "abcdefghijklmnopqrstuvwxyz" \
81 "0123456789" \
82 "-_.!~*'()"
83
84#define IS_EXCLUDED(u, m32, m64, m96)(u <= ' ' || u >= '\177' || (u < 64 ? (m32 & (1 <<
(63 - u))) : (u < 96 ? (m64 & (1 << (95 - u))) :
(m96 & (1 << (127 - u))))) != 0)
\
85 (u <= ' ' \
86 || u >= '\177' \
87 || (u < 64 ? (m32 & (1 << (63 - u))) \
88 : (u < 96 ? (m64 & (1 << (95 - u))) \
89 : /*u < 128*/ (m96 & (1 << (127 - u))))) != 0)
90
91#define MASKS_WITH_RESERVED(reserved, m32, m64, m96)if (reserved == ((void*)0)) { m32 = 0xbe19003f, m64 = 0x8000001e
, m96 = 0x8000001d; } else do { m32 = 0xb400000a, m64 = 0x0000001e
, m96 = 0x8000001d; for (;reserved[0]; reserved++) { unsigned
r = reserved[0]; if (r < 32) ; else if (r < 64) m32 |=
1U << (63 - r); else if (r < 96) m64 |= 1U <<
(95 - r); else if (r < 128) m96 |= 1U << (127 - r);
} } while (0)
\
92 if (reserved == NULL((void*)0)) { \
93 m32 = 0xbe19003f, m64 = 0x8000001e, m96 = 0x8000001d; \
94 } else do { \
95 m32 = 0xb400000a, m64 = 0x0000001e, m96 = 0x8000001d; \
96 \
97 for (;reserved[0]; reserved++) { \
98 unsigned r = reserved[0]; \
99 RESERVE(r, m32, m64, m96)if (r < 32) ; else if (r < 64) m32 |= 1U << (63 -
r); else if (r < 96) m64 |= 1U << (95 - r); else if
(r < 128) m96 |= 1U << (127 - r)
; \
100 } \
101 } while (0)
102
103#define RESERVE(reserved, m32, m64, m96)if (r < 32) ; else if (r < 64) m32 |= 1U << (63 -
r); else if (r < 96) m64 |= 1U << (95 - r); else if
(r < 128) m96 |= 1U << (127 - r)
\
104 if (r < 32) \
105 ; \
106 else if (r < 64) \
107 m32 |= 1U << (63 - r); \
108 else if (r < 96) \
109 m64 |= 1U << (95 - r); \
110 else if (r < 128) \
111 m96 |= 1U << (127 - r)
112
113#define MASKS_WITH_ALLOWED(allowed, mask32, mask64, mask96)do { if (allowed) { for (;allowed[0]; allowed++) { unsigned a
= allowed[0]; if (a < 32) ; else if (a < 64) mask32 &=
~(1U << (63 - a)); else if (a < 96) mask64 &= ~
(1U << (95 - a)); else if (a < 128) mask96 &= ~(
1U << (127 - a)); } } } while (0)
\
114 do { \
115 if (allowed) { \
116 for (;allowed[0]; allowed++) { \
117 unsigned a = allowed[0]; \
118 ALLOW(a, mask32, mask64, mask96)if (a < 32) ; else if (a < 64) mask32 &= ~(1U <<
(63 - a)); else if (a < 96) mask64 &= ~(1U << (
95 - a)); else if (a < 128) mask96 &= ~(1U << (127
- a))
; \
119 } \
120 } \
121 } while (0)
122
123#define ALLOW(a, mask32, mask64, mask96)if (a < 32) ; else if (a < 64) mask32 &= ~(1U <<
(63 - a)); else if (a < 96) mask64 &= ~(1U << (
95 - a)); else if (a < 128) mask96 &= ~(1U << (127
- a))
\
124 if (a < 32) \
125 ; \
126 else if (a < 64) \
127 mask32 &= ~(1U << (63 - a)); \
128 else if (a < 96) \
129 mask64 &= ~(1U << (95 - a)); \
130 else if (a < 128) \
131 mask96 &= ~(1U << (127 - a))
132
133#define NUL'\0' '\0'
134#define NULNULNUL'\0', '\0', '\0' '\0', '\0', '\0'
135
136#define RMASK10xbe19003f 0xbe19003f
137#define RMASK20x8000001e 0x8000001e
138#define RMASK30x8000001d 0x8000001d
139
140#define RESERVED_MASK0xbe19003f, 0x8000001e, 0x8000001d 0xbe19003f, 0x8000001e, 0x8000001d
141#define URIC_MASK0xb400000a, 0x0000001e, 0x8000001d 0xb400000a, 0x0000001e, 0x8000001d
142
143#define IS_EXCLUDED_MASK(u, m)IS_EXCLUDED IS_EXCLUDED(u, m)
144
145/* Internal prototypes */
146static char *url_canonize(char *d, char const *s, size_t n,
147 unsigned syn33,
148 char const allowed[]);
149static char *url_canonize2(char *d, char const *s, size_t n,
150 unsigned syn33,
151 unsigned m32, unsigned m64, unsigned m96);
152static int url_tel_cmp_numbers(char const *A, char const *B);
153
154/**Test if string contains excluded or url-reserved characters.
155 *
156 *
157 *
158 * @param s string to be searched
159 *
160 * @retval 0 if no reserved characters were found.
161 * @retval l if a reserved character was found.
162 */
163int url_reserved_p(char const *s)
164{
165 if (s)
166 while (*s) {
167 unsigned char u = *s++;
168
169 if (IS_EXCLUDED(u, RMASK1, RMASK2, RMASK3)(u <= ' ' || u >= '\177' || (u < 64 ? (0xbe19003f &
(1 << (63 - u))) : (u < 96 ? (0x8000001e & (1 <<
(95 - u))) : (0x8000001d & (1 << (127 - u))))) != 0
)
)
170 return 1;
171 }
172
173 return 0;
174}
175
176/** Calculate length of string when escaped with %-notation.
177 *
178 * Calculate the length of string @a s when the excluded or reserved
179 * characters in it have been escaped.
180 *
181 * @param s String with reserved URL characters. [IN
182 * @param reserved Optional array of reserved characters [IN]
183 *
184 * @return
185 * The number of characters in corresponding but escaped string.
186 *
187 * You can handle a part of URL with reserved characters like this:
188 * @code
189 * if (url_reserved_p(s)) {
190 * n = malloc(url_esclen(s, NULL) + 1);
191 * if (n) url_escape(n, s);
192 * } else {
193 * n = malloc(strlen(s) + 1);
194 * if (n) strcpy(n, s);
195 * }
196 * @endcode
197 */
198isize_t url_esclen(char const *s, char const reserved[])
199{
200 size_t n;
201 unsigned mask32, mask64, mask96;
202
203 MASKS_WITH_RESERVED(reserved, mask32, mask64, mask96)if (reserved == ((void*)0)) { mask32 = 0xbe19003f, mask64 = 0x8000001e
, mask96 = 0x8000001d; } else do { mask32 = 0xb400000a, mask64
= 0x0000001e, mask96 = 0x8000001d; for (;reserved[0]; reserved
++) { unsigned r = reserved[0]; if (r < 32) ; else if (r <
64) mask32 |= 1U << (63 - r); else if (r < 96) mask64
|= 1U << (95 - r); else if (r < 128) mask96 |= 1U <<
(127 - r); } } while (0)
;
204
205 for (n = 0; s && *s; n++) {
206 unsigned char u = *s++;
207
208 if (IS_EXCLUDED(u, mask32, mask64, mask96)(u <= ' ' || u >= '\177' || (u < 64 ? (mask32 & (
1 << (63 - u))) : (u < 96 ? (mask64 & (1 <<
(95 - u))) : (mask96 & (1 << (127 - u))))) != 0)
)
209 n += 2;
210 }
211
212 return (isize_t)n;
213}
214
215/** Escape a string.
216 *
217 * The function url_escape() copies the string pointed by @a s to the array
218 * pointed by @a d, @b excluding the terminating \\0 character. All reserved
219 * characters in @a s are copied in hexadecimal format, for instance, @c
220 * "$%#" is copied as @c "%24%25%23". The destination array @a d must be
221 * large enough to receive the escaped copy.
222 *
223 * @param d Destination buffer [OUT]
224 * @param s String to be copied [IN]
225 * @param reserved Array of reserved characters [IN]
226 *
227 * @return Pointer to the destination array.
228 */
229char *url_escape(char *d, char const *s, char const reserved[])
230{
231 char *retval = d;
232 unsigned mask32, mask64, mask96;
233
234 MASKS_WITH_RESERVED(reserved, mask32, mask64, mask96)if (reserved == ((void*)0)) { mask32 = 0xbe19003f, mask64 = 0x8000001e
, mask96 = 0x8000001d; } else do { mask32 = 0xb400000a, mask64
= 0x0000001e, mask96 = 0x8000001d; for (;reserved[0]; reserved
++) { unsigned r = reserved[0]; if (r < 32) ; else if (r <
64) mask32 |= 1U << (63 - r); else if (r < 96) mask64
|= 1U << (95 - r); else if (r < 128) mask96 |= 1U <<
(127 - r); } } while (0)
;
235
236 while (s && *s) {
237 unsigned char u = *s++;
238
239 if (IS_EXCLUDED(u, mask32, mask64, mask96)(u <= ' ' || u >= '\177' || (u < 64 ? (mask32 & (
1 << (63 - u))) : (u < 96 ? (mask64 & (1 <<
(95 - u))) : (mask96 & (1 << (127 - u))))) != 0)
) {
240# define URL_HEXIFY(u) ((u) + '0' + ((u) >= 10 ? 'A' - '0' - 10 : 0))
241
242 *d++ = '%';
243 *d++ = URL_HEXIFY(u >> 4);
244 *d++ = URL_HEXIFY(u & 15);
245
246# undef URL_HEXIFY
247 }
248 else {
249 *d++ = u;
250 }
251 }
252
253 *d = '\0';
254
255 return retval;
256}
257
258
259/**Unescape url-escaped string fragment.
260 *
261 * Unescape @a n characters from string @a s to the buffer @a d, including
262 * the terminating \\0 character. All %-escaped triplets in @a s are
263 * unescaped, for instance, @c "%40%25%23" is copied as @c "@%#". The
264 * destination array @a d must be large enough to receive the escaped copy
265 * (@a n bytes is always enough).
266 *
267 * @param d destination buffer
268 * @param s string to be unescaped
269 * @param n maximum number of characters to unescape
270 *
271 * @return Length of unescaped string
272 *
273 * @NEW_1_12_4.
274 */
275size_t url_unescape_to(char *d, char const *s, size_t n)
276{
277 size_t i = 0, j = 0;
278
279 if (s == NULL((void*)0))
280 return 0;
281
282 i = j = strncspn(s, n, "%");
283
284 if (d && d != s)
285 memmove(d, s, i);
286
287 for (; i < n;) {
288 char c = s[i++];
289
290 if (c == '\0')
291 break;
292
293 if (c == '%' && i + 1 < n && IS_HEX(s[i])(((s[i]) >= '0' && (s[i]) <= '9') || ((s[i]) >=
'A' && (s[i]) <= 'F') || ((s[i]) >= 'a' &&
(s[i]) <= 'f'))
&& IS_HEX(s[i + 1])(((s[i + 1]) >= '0' && (s[i + 1]) <= '9') || ((
s[i + 1]) >= 'A' && (s[i + 1]) <= 'F') || ((s[i
+ 1]) >= 'a' && (s[i + 1]) <= 'f'))
) {
294#define UNHEX(a) (a - (a >= 'a' ? 'a' - 10 : (a >= 'A' ? 'A' - 10 : '0')))
295 c = (UNHEX(s[i]) << 4) | UNHEX(s[i + 1]);
296#undef UNHEX
297 i += 2;
298 }
299
300 if (d)
301 d[j] = c;
302 j++;
303 }
304
305 return j;
306}
307
308/**Unescape url-escaped string.
309 *
310 * Unescape string @a s to the buffer @a d, including the terminating \\0
311 * character. All %-escaped triplets in @a s are unescaped, for instance, @c
312 * "%40%25%23" is copied as @c "@%#". The destination array @a d must be
313 * large enough to receive the escaped copy.
314 *
315 * @param d destination buffer
316 * @param s string to be copied
317 *
318 * @return Pointer to the destination buffer.
319 */
320char *url_unescape(char *d, char const *s)
321{
322 size_t n = url_unescape_to(d, s, SIZE_MAX(18446744073709551615UL));
323 if (d)
324 d[n] = '\0';
325 return d;
326}
327
328/** Canonize a URL component */
329static
330char *url_canonize(char *d, char const *s, size_t n,
331 unsigned syn33,
332 char const allowed[])
333{
334 unsigned mask32 = 0xbe19003f, mask64 = 0x8000001e, mask96 = 0x8000001d;
335
336 MASKS_WITH_ALLOWED(allowed, mask32, mask64, mask96)do { if (allowed) { for (;allowed[0]; allowed++) { unsigned a
= allowed[0]; if (a < 32) ; else if (a < 64) mask32 &=
~(1U << (63 - a)); else if (a < 96) mask64 &= ~
(1U << (95 - a)); else if (a < 128) mask96 &= ~(
1U << (127 - a)); } } } while (0)
;
337
338 return url_canonize2(d, s, n, syn33, mask32, mask64, mask96);
339}
340
341#define SYN33(c)(1U << (c - 33)) (1U << (c - 33))
342#define IS_SYN33(syn33, c)((syn33 & (1U << (c - 33))) != 0) ((syn33 & (1U << (c - 33))) != 0)
343
344/** Canonize a URL component (with precomputed mask) */
345static
346char *url_canonize2(char *d, char const * const s, size_t n,
347 unsigned syn33,
348 unsigned m32, unsigned m64, unsigned m96)
349{
350 size_t i = 0;
351
352 if (d == s)
353 for (;s[i] && i < n; d++, i++)
354 if (s[i] == '%')
355 break;
356
357 for (;s[i] && i < n; d++, i++) {
358 unsigned char c = s[i], h1, h2;
359
360 if (c != '%') {
361 if (!IS_SYN33(syn33, c)((syn33 & (1U << (c - 33))) != 0) && IS_EXCLUDED(c, m32, m64, m96)(c <= ' ' || c >= '\177' || (c < 64 ? (m32 & (1 <<
(63 - c))) : (c < 96 ? (m64 & (1 << (95 - c))) :
(m96 & (1 << (127 - c))))) != 0)
)
362 return NULL((void*)0);
363 *d = c;
364 continue;
365 }
366
367 h1 = s[i + 1];
368 if (!h1) {
369 *d = '\0';
370 return NULL((void*)0);
371 }
372 h2 = s[i + 2];
373
374 if (!IS_HEX(h1)(((h1) >= '0' && (h1) <= '9') || ((h1) >= 'A'
&& (h1) <= 'F') || ((h1) >= 'a' && (h1
) <= 'f'))
|| !IS_HEX(h2)(((h2) >= '0' && (h2) <= '9') || ((h2) >= 'A'
&& (h2) <= 'F') || ((h2) >= 'a' && (h2
) <= 'f'))
) {
375 *d = '\0';
376 return NULL((void*)0);
377 }
378
379#define UNHEX(a) (a - (a >= 'a' ? 'a' - 10 : (a >= 'A' ? 'A' - 10 : '0')))
380 c = (UNHEX(h1) << 4) | UNHEX(h2);
381
382 if (!IS_EXCLUDED(c, m32, m64, m96)(c <= ' ' || c >= '\177' || (c < 64 ? (m32 & (1 <<
(63 - c))) : (c < 96 ? (m64 & (1 << (95 - c))) :
(m96 & (1 << (127 - c))))) != 0)
) {
383 /* Convert hex to normal character */
384 *d = c, i += 2;
385 continue;
386 }
387
388 /* Convert hex to uppercase */
389 if (h1 >= 'a' /* && h1 <= 'f' */)
390 h1 = h1 - 'a' + 'A';
391 if (h2 >= 'a' /* && h2 <= 'f' */)
392 h2 = h2 - 'a' + 'A';
393
394 d[0] = '%', d[1] = h1, d[2] = h2;
395
396 d +=2, i += 2;
397#undef UNHEX
398 }
399
400 *d = '\0';
401
402 return d;
403}
404
405
406/** Canonize a URL component (with precomputed mask).
407 *
408 * This version does not flag error if *s contains character that should
409 * be escaped.
410 */
411static
412char *url_canonize3(char *d, char const * const s, size_t n,
413 unsigned m32, unsigned m64, unsigned m96)
414{
415 size_t i = 0;
416
417 if (d == s)
418 for (;s[i] && i < n; d++, i++)
419 if (s[i] == '%')
420 break;
421
422 for (;s[i] && i < n; d++, i++) {
423 unsigned char c = s[i], h1, h2;
424
425 if (c != '%') {
426 *d = c;
427 continue;
428 }
429
430 h1 = s[i + 1];
431 if (!h1) {
432 *d = '\0';
433 return NULL((void*)0);
434 }
435 h2 = s[i + 2];
436
437 if (!IS_HEX(h1)(((h1) >= '0' && (h1) <= '9') || ((h1) >= 'A'
&& (h1) <= 'F') || ((h1) >= 'a' && (h1
) <= 'f'))
|| !IS_HEX(h2)(((h2) >= '0' && (h2) <= '9') || ((h2) >= 'A'
&& (h2) <= 'F') || ((h2) >= 'a' && (h2
) <= 'f'))
) {
438 *d = '\0';
439 return NULL((void*)0);
440 }
441
442#define UNHEX(a) (a - (a >= 'a' ? 'a' - 10 : (a >= 'A' ? 'A' - 10 : '0')))
443 c = (UNHEX(h1) << 4) | UNHEX(h2);
444
445 if (!IS_EXCLUDED(c, m32, m64, m96)(c <= ' ' || c >= '\177' || (c < 64 ? (m32 & (1 <<
(63 - c))) : (c < 96 ? (m64 & (1 << (95 - c))) :
(m96 & (1 << (127 - c))))) != 0)
) {
446 *d = c, i += 2;
447 continue;
448 }
449
450 /* Convert hex to uppercase */
451 if (h1 >= 'a' /* && h1 <= 'f' */)
452 h1 = h1 - 'a' + 'A';
453 if (h2 >= 'a' /* && h2 <= 'f' */)
454 h2 = h2 - 'a' + 'A';
455
456 d[0] = '%', d[1] = h1, d[2] = h2;
457
458 d +=2, i += 2;
459#undef UNHEX
460 }
461
462 *d = '\0';
463
464 return d;
465}
466
467
468/** Get URL scheme. */
469char const* url_scheme(enum url_type_e url_type)
470{
471 switch (url_type) {
472 case url_any: return "*";
473 case url_sip: return "sip";
474 case url_sips: return "sips";
475 case url_tel: return "tel";
476 case url_fax: return "fax";
477 case url_modem: return "modem";
478 case url_http: return "http";
479 case url_https: return "https";
480 case url_ftp: return "ftp";
481 case url_file: return "file";
482 case url_rtsp: return "rtsp";
483 case url_rtspu: return "rtspu";
484 case url_mailto: return "mailto";
485 case url_im: return "im";
486 case url_pres: return "pres";
487 case url_cid: return "cid";
488 case url_msrp: return "msrp";
489 case url_msrps: return "msrps";
490 case url_urn: return "urn";
491 case url_wv: return "wv";
492 default:
493 assert(url_type == url_unknown)((void) sizeof ((url_type == url_unknown) ? 1 : 0), __extension__
({ if (url_type == url_unknown) ; else __assert_fail ("url_type == url_unknown"
, "url.c", 493, __extension__ __PRETTY_FUNCTION__); }))
;
494 return NULL((void*)0);
495 }
496}
497
498su_inlinestatic inline
499int url_type_is_opaque(enum url_type_e url_type)
500{
501 return
502 url_type == url_invalid ||
503 url_type == url_tel ||
504 url_type == url_modem ||
505 url_type == url_fax ||
506 url_type == url_cid;
507}
508
509/** Init an url as given type */
510void url_init(url_t *url, enum url_type_e type)
511{
512 memset(url, 0, sizeof(*url));
513 url->url_type = type;
514 if (type > url_unknown) {
515 char const *scheme = url_scheme((enum url_type_e)url->url_type);
516 if (scheme)
517 url->url_scheme = scheme;
518 }
519}
520
521/** Get url type */
522su_inlinestatic inline
523enum url_type_e url_get_type(char const *scheme, size_t len)
524{
525#define test_scheme(s) \
526 if (len == strlen(#s) && !strncasecmp(scheme, #s, len)) return url_##s
527
528 switch (scheme[0]) {
529 case '*': if (strcmp(scheme, "*") == 0) return url_any;
530 case 'c': case 'C':
531 test_scheme(cid); break;
532 case 'f': case 'F':
533 test_scheme(ftp); test_scheme(file); test_scheme(fax); break;
534 case 'h': case 'H':
535 test_scheme(http); test_scheme(https); break;
536 case 'i': case 'I':
537 test_scheme(im); break;
538 case 'm': case 'M':
539 test_scheme(mailto); test_scheme(modem);
540 test_scheme(msrp); test_scheme(msrps); break;
541 case 'p': case 'P':
542 test_scheme(pres); break;
543 case 'r': case 'R':
544 test_scheme(rtsp); test_scheme(rtspu); break;
545 case 's': case 'S':
546 test_scheme(sip); test_scheme(sips); break;
547 case 't': case 'T':
548 test_scheme(tel); break;
549 case 'u': case 'U':
550 test_scheme(urn); break;
551 case 'w': case 'W':
552 test_scheme(wv); break;
553
554
555 default: break;
556 }
557
558#undef test_scheme
559
560 if (len != span_unreserved(scheme))
561 return url_invalid;
562 else
563 return url_unknown;
564}
565
566/**
567 * Decode a URL.
568 *
569 * This function decodes a (SIP) URL string to a url_t structure.
570 *
571 * @param url structure to store the parsing result
572 * @param s NUL-terminated string to be parsed
573 *
574 * @note The parsed string @a s will be modified when parsing it.
575 *
576 * @retval 0 if successful,
577 * @retval -1 otherwise.
578 */
579static
580int _url_d(url_t *url, char *s)
581{
582 size_t n, p;
583 char rest_c, *host, *user;
584 int have_authority = 1;
585
586 memset(url, 0, sizeof(*url));
587
588 if (strcmp(s, "*") == 0) {
589 url->url_type = url_any;
590 url->url_scheme = "*";
591 return 0;
592 }
593
594 n = strcspn(s, ":/?#");
595
596 if (n && s[n] == ':') {
597 char *scheme;
598 url->url_scheme = scheme = s; s[n] = '\0'; s = s + n + 1;
599
600 if (!(scheme = url_canonize(scheme, scheme, SIZE_MAX(18446744073709551615UL), 0, "+")))
601 return -1;
602
603 n = scheme - url->url_scheme;
604
605 url->url_type = url_get_type(url->url_scheme, n);
606
607 have_authority = !url_type_is_opaque((enum url_type_e)url->url_type);
608 }
609 else {
610 url->url_type = url_unknown;
611 }
612
613 user = NULL((void*)0), host = s;
614
615 if (url->url_type == url_sip || url->url_type == url_sips) {
616 /* SIP URL may have /;? in user part but no path */
617 /* user-unreserved = "&" / "=" / "+" / "$" / "," / ";" / "?" / "/" */
618 /* Some #*@#* phones include unescaped # there, too */
619 n = strcspn(s, "@/;?#");
620 p = strcspn(s + n, "@");
621 if (s[n + p] == '@') {
622 n += p;
623 user = s;
624 host = s + n + 1;
625 }
626
627 n += strcspn(s + n, "/;?#");
628 }
629 else if (have_authority) {
630 if (url->url_type == url_wv) {
631 /* WV URL may have / in user part */
632 n = strcspn(s, "@#?;");
633 if (s[n] == '@') {
634 user = s;
635 host = s + n + 1;
636 n += strcspn(s + n, ";?#");
637 }
638 }
639 else if (host[0] == '/' && host[1] != '/') {
640 /* foo:/bar or /bar - no authority, just path */
641 url->url_root = '/'; /* Absolute path */
642 host = NULL((void*)0), n = 0;
643 }
644 else {
645 if (host[0] == '/' && host[1] == '/') {
646 /* We have authority, / / foo or foo */
647 host += 2; s += 2, url->url_root = '/';
648 n = strcspn(s, "/?#@[]");
649 }
650 else
651 n = strcspn(s, "@;/?#");
652
653 if (s[n] == '@')
654 user = host, host = user + n + 1;
655
656 n += strcspn(s + n, ";/?#"); /* Find path, query and/or fragment */
657 }
658 }
659 else /* !have_authority */ {
660 user = host, host = NULL((void*)0);
661 if (url->url_type != url_invalid)
662 n = strcspn(s, "/;?#"); /* Find params, query and/or fragment */
663 else
664 n = strcspn(s, "#");
665 }
666
667 rest_c = s[n]; s[n] = 0; s = rest_c ? s + n + 1 : NULL((void*)0);
668
669 if (user) {
670 if (host) host[-1] = '\0';
671 url->url_user = user;
672 if (url->url_type != url_unknown) {
673 n = strcspn(user, ":");
674 if (user[n]) {
675 user[n] = '\0';
676 url->url_password = user + n + 1;
677 }
678 }
679 }
680
681 if (host) {
682 url->url_host = host;
683 /* IPv6 (and in some cases, IPv4) addresses are quoted with [] */
684 if (host[0] == '[') {
685 n = strcspn(host, "]");
686 if (host[n] && (host[n + 1] == '\0' || host[n + 1] == ':'))
687 n++;
688 else
689 n = 0;
690 }
691 else {
692 n = strcspn(host, ":");
693 }
694
695 /* We allow empty host by default */
696 if (n == 0) switch (url->url_type) {
697 case url_sip:
698 case url_sips:
699 case url_im:
700 case url_pres:
701 return -1;
702 default:
703 break;
704 }
705
706 if (host[n] == ':') {
707 char *port = host + n + 1;
708 url->url_port = port;
709 switch (url->url_type) {
710 case url_any:
711 case url_sip:
712 case url_sips:
713 case url_http:
714 case url_https:
715 case url_ftp:
716 case url_file:
717 case url_rtsp:
718 case url_rtspu:
719 if (!url_canonize2(port, port, SIZE_MAX(18446744073709551615UL), 0, RESERVED_MASK0xbe19003f, 0x8000001e, 0x8000001d))
720 return -1;
721
722 /* Check that port is really numeric or wildcard */
723 /* Port can be *digit, empty string or "*" */
724 while (*port >= '0' && *port <= '9')
725 port++;
726
727 if (port != url->url_port) {
728 if (port[0] != '\0')
729 return -1;
730 }
731 else if (port[0] == '\0')
732 /* empty string */;
733 else if (port[0] == '*' && port[1] == '\0')
734 /* wildcard */;
735 else
736 return -1;
737 }
738 host[n] = 0;
739 }
740 }
741
742 if (rest_c == '/') {
743 url->url_path = s; n = strcspn(s, "?#");
744 rest_c = s[n]; s[n] = 0; s = rest_c ? s + n + 1 : NULL((void*)0);
745 }
746 if (rest_c == ';') {
747 url->url_params = s; n = strcspn(s, "?#");
748 rest_c = s[n]; s[n] = 0; s = rest_c ? s + n + 1 : NULL((void*)0);
749 }
750 if (rest_c == '?') {
751 url->url_headers = s; n = strcspn(s, "#");
752 rest_c = s[n]; s[n] = 0; s = rest_c ? s + n + 1 : NULL((void*)0);
753 }
754 if (rest_c == '#') {
755 url->url_fragment = s;
756 rest_c = '\0';
757 }
758 if (rest_c)
759 return -1;
760
761 return 0;
762}
763
764/* Unreserved things */
765
766/**
767 * Decode a URL.
768 *
769 * This function decodes a URL string to a url_t structure.
770 *
771 * @param url structure to store the parsing result
772 * @param s NUL-terminated string to be parsed
773 *
774 * @note The parsed string @a s will be modified when parsing it.
775 *
776 * @retval 0 if successful,
777 * @retval -1 otherwise.
778 */
779int url_d(url_t *url, char *s)
780{
781 if (url == NULL((void*)0) || _url_d(url, s) < 0)
782 return -1;
783
784 /* Canonize URL */
785 /* scheme is canonized by _url_d() */
786 if (url->url_type == url_sip || url->url_type == url_sips) {
787
788# define SIP_USER_UNRESERVED"&=+$,;?/" "&=+$,;?/"
789 s = (char *)url->url_user;
790 if (s && !url_canonize(s, s, SIZE_MAX(18446744073709551615UL), 0, SIP_USER_UNRESERVED"&=+$,;?/"))
791 return -1;
792
793 /* Having different charset in user and password does not make sense */
794 /* but that is how it is defined in RFC 3261 */
795# define SIP_PASS_UNRESERVED"&=+$," "&=+$,"
796 s = (char *)url->url_password;
797 if (s && !url_canonize(s, s, SIZE_MAX(18446744073709551615UL), 0, SIP_PASS_UNRESERVED"&=+$,"))
798 return -1;
799
800 }
801 else {
802
803# define USER_UNRESERVED"&=+$,;" "&=+$,;"
804 s = (char *)url->url_user;
805 if (s && !url_canonize(s, s, SIZE_MAX(18446744073709551615UL), 0, USER_UNRESERVED"&=+$,;"))
806 return -1;
807
808# define PASS_UNRESERVED"&=+$,;:" "&=+$,;:"
809 s = (char *)url->url_password;
810 if (s && !url_canonize(s, s, SIZE_MAX(18446744073709551615UL), 0, PASS_UNRESERVED"&=+$,;:"))
811 return -1;
812 }
813
814 s = (char *)url->url_host;
815 if (s && !url_canonize2(s, s, SIZE_MAX(18446744073709551615UL), 0, RESERVED_MASK0xbe19003f, 0x8000001e, 0x8000001d))
816 return -1;
817
818 /* port is canonized by _url_d() */
819 s = (char *)url->url_path;
820 if (s && !url_canonize(s, s, SIZE_MAX(18446744073709551615UL),
821 /* Allow all URI characters but ? */
822 /* Allow unescaped /;?@, - but do not convert */
823 SYN33('/')(1U << ('/' - 33)) | SYN33(';')(1U << (';' - 33)) | SYN33('=')(1U << ('=' - 33)) | SYN33('@')(1U << ('@' - 33)) |
824 SYN33(',')(1U << (',' - 33)),
825 /* Convert escaped :&+$ to unescaped */
826 ":&+$"))
827 return -1;
828
829 s = (char *)url->url_params;
830 if (s && !url_canonize(s, s, SIZE_MAX(18446744073709551615UL),
831 /* Allow all URI characters but ? */
832 /* Allow unescaped ;=@, - but do not convert */
833 SYN33(';')(1U << (';' - 33)) | SYN33('=')(1U << ('=' - 33)) | SYN33('@')(1U << ('@' - 33)) | SYN33(',')(1U << (',' - 33)),
834 /* Convert escaped /:&+$ to unescaped */
835 "/:&+$"))
836 return -1;
837
838 /* Unhex alphanumeric and unreserved URI characters */
839 s = (char *)url->url_headers;
840 if (s && !url_canonize3(s, s, SIZE_MAX(18446744073709551615UL), RESERVED_MASK0xbe19003f, 0x8000001e, 0x8000001d))
841 return -1;
842
843 /* Allow all URI characters (including reserved ones) */
844 s = (char *)url->url_fragment;
845 if (s && !url_canonize2(s, s, SIZE_MAX(18446744073709551615UL), 0, URIC_MASK0xb400000a, 0x0000001e, 0x8000001d))
846 return -1;
847
848 return 0;
849}
850
851/** Encode an URL.
852 *
853 * The function url_e() combines a URL from substrings in url_t structure
854 * according the @ref url_syntax "URL syntax" presented above. The encoded
855 * @a url is stored in a @a buffer of @a n bytes.
856 *
857 * @param buffer memory area to store the encoded @a url.
858 * @param n size of @a buffer.
859 * @param url URL to be encoded.
860 *
861 * @return
862 * Return the number of bytes in the encoding.
863 *
864 * @note The function follows the convention set by C99 snprintf(). Even if
865 * the result does not fit into the @a buffer and it is truncated, the
866 * function returns the number of bytes in an untruncated encoding.
867 */
868issize_t url_e(char buffer[], isize_t n, url_t const *url)
869{
870 size_t i;
871 char *b = buffer;
872 size_t m = n;
873 int do_copy = n > 0;
874
875 if (url == NULL((void*)0))
876 return -1;
877
878 if (URL_STRING_P(url)((url) && *((url_string_t*)(url))->us_str != 0)) {
879 char const *u = (char *)url;
880 i = strlen(u);
881 if (!buffer)
882 return i;
883
884 if (i >= n) {
885 memcpy(buffer, u, n - 2);
886 buffer[n - 1] = '\0';
887 } else {
888 memcpy(buffer, u, i + 1);
889 }
890
891 return i;
892 }
893
894
895 if (url->url_type == url_any) {
896 if (b && m > 0) {
897 if (m > 1) strcpy(b, "*"); else b[0] = '\0';
898 }
899 return 1;
900 }
901
902 if (url->url_scheme && url->url_scheme[0]) {
903 i = strlen(url->url_scheme) + 1;
904 if (do_copy && (do_copy = i <= n)) {
905 memcpy(b, url->url_scheme, i - 1);
906 b[i - 1] = ':';
907 }
908 b += i; n -= i;
909 }
910
911 if (url->url_root && (url->url_host || url->url_user)) {
912 if (do_copy && (do_copy = 2 <= n))
913 memcpy(b, "//", 2);
914 b += 2; n -= 2;
915 }
916
917 if (url->url_user) {
918 i = strlen(url->url_user);
919 if (do_copy && (do_copy = i <= n))
920 memcpy(b, url->url_user, i);
921 b += i; n -= i;
922
923 if (url->url_password) {
924 if (do_copy && (do_copy = 1 <= n))
925 *b = ':';
926 b++; n--;
927 i = strlen(url->url_password);
928 if (do_copy && (do_copy = i <= n))
929 memcpy(b, url->url_password, i);
930 b += i; n -= i;
931 }
932
933 if (url->url_host) {
934 if (do_copy && (do_copy = 1 <= n))
935 *b = '@';
936 b++; n--;
937 }
938 }
939
940 if (url->url_host) {
941 i = strlen(url->url_host);
942 if (do_copy && (do_copy = i <= n))
943 memcpy(b, url->url_host, i);
944 b += i; n -= i;
945
946 if (url->url_port) {
947 i = strlen(url->url_port) + 1;
948 if (do_copy && (do_copy = i <= n)) {
949 b[0] = ':';
950 memcpy(b + 1, url->url_port, i - 1);
951 }
952 b += i; n -= i;
953 }
954 }
955
956 if (url->url_path) {
957 if (url->url_root) {
958 if (do_copy && (do_copy = 1 <= n))
959 b[0] = '/';
960 b++, n--;
961 }
962 i = strlen(url->url_path);
963 if (do_copy && (do_copy = i < n))
964 memcpy(b, url->url_path, i);
965 b += i; n -= i;
966 }
967
968 {
969 static char const sep[] = ";?#";
970 char const *pp[3];
971 size_t j;
972
973 pp[0] = url->url_params;
974 pp[1] = url->url_headers;
975 pp[2] = url->url_fragment;
976
977 for (j = 0; j < 3; j++) {
978 char const *p = pp[j];
979 if (!p) continue;
980 i = strlen(p) + 1;
981 if (do_copy && (do_copy = i <= n)) {
982 *b = sep[j];
983 memcpy(b + 1, p, i - 1);
984 }
985 b += i; n -= i;
986 }
987 }
988
989 if (do_copy && (do_copy = 1 <= n))
Although the value stored to 'do_copy' is used in the enclosing expression, the value is never actually read from 'do_copy'
990 *b = '\0';
991 else if (buffer && m > 0)
992 buffer[m - 1] = '\0';
993
994 assert((size_t)(b - buffer) == (size_t)(m - n))((void) sizeof (((size_t)(b - buffer) == (size_t)(m - n)) ? 1
: 0), __extension__ ({ if ((size_t)(b - buffer) == (size_t)(
m - n)) ; else __assert_fail ("(size_t)(b - buffer) == (size_t)(m - n)"
, "url.c", 994, __extension__ __PRETTY_FUNCTION__); }))
;
995
996 /* This follows the snprintf(C99) return value,
997 * Number of characters written (excluding NUL)
998 */
999 return b - buffer;
1000}
1001
1002
1003/** Calculate the length of URL when encoded.
1004 *
1005 */
1006isize_t url_len(url_t const * url)
1007{
1008 size_t rv = 0;
1009
1010 if (url->url_scheme) rv += strlen(url->url_scheme) + 1; /* plus ':' */
1011 if (url->url_user) {
1012 rv += strlen(url->url_user);
1013 if (url->url_password)
1014 rv += strlen(url->url_password) + 1; /* plus ':' */
1015 rv += url->url_host != NULL((void*)0); /* plus '@' */
1016 }
1017 if (url->url_host) rv += strlen(url->url_host);
1018 if (url->url_port) rv += strlen(url->url_port) + 1; /* plus ':' */
1019 if (url->url_path) rv += strlen(url->url_path) + 1; /* plus initial / */
1020 if (url->url_params) rv += strlen(url->url_params) + 1; /* plus initial ; */
1021 if (url->url_headers) rv += strlen(url->url_headers) + 1; /* plus '?' */
1022 if (url->url_fragment) rv += strlen(url->url_fragment) + 1; /* plus '#' */
1023
1024 return rv;
1025}
1026
1027/**@def URL_E(buf, end, url)
1028 * Encode an URL: use @a buf up to @a end.
1029 * @hideinitializer
1030 */
1031
1032/**
1033 * Calculate the size of strings associated with a #url_t sructure.
1034 *
1035 * @param url pointer to a #url_t structure or string
1036 * @return Number of bytes for URL
1037 */
1038isize_t url_xtra(url_t const *url)
1039{
1040 size_t xtra;
1041
1042 if (URL_STRING_P(url)((url) && *((url_string_t*)(url))->us_str != 0)) {
1043 xtra = strlen((char const *)url) + 1;
1044 }
1045 else {
1046 size_t len_scheme, len_user, len_password,
1047 len_host, len_port, len_path, len_params,
1048 len_headers, len_fragment;
1049
1050 len_scheme = (url->url_type <= url_unknown && url->url_scheme) ?
1051 strlen(url->url_scheme) + 1 : 0;
1052 len_user = url->url_user ? strlen(url->url_user) + 1 : 0;
1053 len_password = url->url_password ? strlen(url->url_password) + 1 : 0;
1054 len_host = url->url_host ? strlen(url->url_host) + 1 : 0;
1055 len_port = url->url_port ? strlen(url->url_port) + 1 : 0;
1056 len_path = url->url_path ? strlen(url->url_path) + 1 : 0;
1057 len_params = url->url_params ? strlen(url->url_params) + 1 : 0;
1058 len_headers = url->url_headers ? strlen(url->url_headers) + 1 : 0;
1059 len_fragment = url->url_fragment ? strlen(url->url_fragment) + 1 : 0;
1060
1061 xtra =
1062 len_scheme + len_user + len_password + len_host + len_port +
1063 len_path + len_params + len_headers + len_fragment;
1064 }
1065
1066 return xtra;
1067}
1068
1069su_inlinestatic inline
1070char *copy(char *buf, char *end, char const *src)
1071{
1072#if HAVE_MEMCCPY1
1073 char *b = memccpy(buf, src, '\0', end - buf);
1074 if (b)
1075 return b;
1076 else
1077 return end + strlen(src + (end - buf)) + 1;
1078#else
1079 for (; buf < end && (*buf = *src); buf++, src++)
1080 ;
1081
1082 if (buf >= end)
1083 while (*src++)
1084 buf++;
1085
1086 return buf + 1;
1087#endif
1088}
1089
1090/**
1091 * Duplicate the url.
1092 *
1093 * The function url_dup() copies the url structure @a src and the strings
1094 * attached to it to @a url. The non-constant strings in @a src are copied
1095 * to @a buf. If the size of duplicated strings exceed @a bufsize, the
1096 * corresponding string fields in @a url are set to NULL.
1097 *
1098 * The calling function can calculate the size of buffer required by calling
1099 * url_dup() with zero as @a bufsize and NULL as @a dst.
1100
1101 * @param buf Buffer for non-constant strings copied from @a src.
1102 * @param bufsize Size of @a buf.
1103 * @param dst Destination URL structure.
1104 * @param src Source URL structure.
1105 *
1106 * @return Number of characters required for
1107 * duplicating the strings in @a str, or -1 if an error
1108 * occurred.
1109 */
1110issize_t url_dup(char *buf, isize_t bufsize, url_t *dst, url_t const *src)
1111{
1112 if (!src && !dst)
1113 return -1;
1114 else if (URL_STRING_P(src)((src) && *((url_string_t*)(src))->us_str != 0)) {
1115 size_t n = strlen((char *)src) + 1;
1116 if (n > bufsize || dst == NULL((void*)0))
1117 return n;
1118
1119 strcpy(buf, (char *)src);
1120 memset(dst, 0, sizeof(*dst));
1121 if (url_d(dst, buf) < 0)
1122 return -1;
1123
1124 return n;
1125 }
1126 else {
1127 char *b = buf;
1128 char *end = b + bufsize;
1129 char const **dstp;
1130 char const * const *srcp;
1131 url_t dst0[1];
1132
1133 if (dst == NULL((void*)0))
1134 dst = dst0;
1135
1136 memset(dst, 0, sizeof(*dst));
1137
1138 if (!src)
1139 return 0;
1140
1141 memset(dst->url_pad, 0, sizeof dst->url_pad);
1142 dst->url_type = src->url_type;
1143 dst->url_root = src->url_root;
1144
1145 dstp = &dst->url_scheme;
1146 srcp = &src->url_scheme;
1147
1148 if (dst->url_type > url_unknown)
1149 *dstp = url_scheme((enum url_type_e)dst->url_type);
1150
1151 if (*dstp != NULL((void*)0))
1152 dstp++, srcp++; /* Skip scheme if it is constant */
1153
1154 if (dst != dst0 && buf != NULL((void*)0) && bufsize != 0)
1155 for (; srcp <= &src->url_fragment; srcp++, dstp++)
1156 if (*srcp) {
1157 char *next = copy(b, end, *srcp);
1158
1159 if (next > end)
1160 break;
1161
1162 *dstp = b, b = next;
1163 }
1164
1165 for (; srcp <= &src->url_fragment; srcp++)
1166 if (*srcp) {
1167 b += strlen(*srcp) + 1;
1168 }
1169
1170 return b - buf;
1171 }
1172}
1173
1174/**@def URL_DUP(buf, end, dst, src)
1175 * Duplicate the url: use @a buf up to @a end. @HI
1176 *
1177 * The macro URL_DUP() duplicates the url. The non-constant strings in @a
1178 * src are copied to @a buf. However, no strings are copied past @a end.
1179 * In other words, the size of buffer is @a end - @a buf.
1180 *
1181 * The macro updates the buffer pointer @a buf, so that it points to the
1182 * first unused byte in the buffer. The buffer pointer @a buf is updated,
1183 * even if the buffer is too small for the duplicated strings.
1184 *
1185 * @param buf Buffer for non-constant strings copied from @a src.
1186 * @param end End of @a buf.
1187 * @param dst Destination URL structure.
1188 * @param src Source URL structure.
1189 *
1190 * @return
1191 * The macro URL_DUP() returns pointer to first unused byte in the
1192 * buffer @a buf.
1193 */
1194
1195/** Duplicate the url to memory allocated via home.
1196 *
1197 * The function url_hdup() duplicates (deep copies) an #url_t structure.
1198 * Alternatively, it can be passed a string; string is then copied and
1199 * parsed to the #url_t structure.
1200 *
1201 * The function url_hdup() allocates the destination structure from @a home
1202 * as a single memory block. It is possible to free the copied url structure
1203 * and all the associated strings using a single call to su_free().
1204 *
1205 * @param home memory home used to allocate new url object
1206 * @param src pointer to URL (or string)
1207 *
1208 * @return
1209 * The function url_hdup() returns a pointer to the newly allocated #url_t
1210 * structure, or NULL upon an error.
1211 */
1212url_t *url_hdup(su_home_t *home, url_t const *src)
1213{
1214 if (src) {
1215 size_t len = sizeof(*src) + url_xtra(src);
1216 url_t *dst = su_alloc(home, len);
1217 if (dst) {
1218 ssize_t actual;
1219 actual = url_dup((char *)(dst + 1), len - sizeof(*src), dst, src);
1220 if (actual < 0)
1221 su_free(home, dst), dst = NULL((void*)0);
1222 else
1223 assert(len == sizeof(*src) + actual)((void) sizeof ((len == sizeof(*src) + actual) ? 1 : 0), __extension__
({ if (len == sizeof(*src) + actual) ; else __assert_fail ("len == sizeof(*src) + actual"
, "url.c", 1223, __extension__ __PRETTY_FUNCTION__); }))
;
1224 }
1225 return dst;
1226 }
1227 else
1228 return NULL((void*)0);
1229}
1230
1231
1232/** Convert an string to an url */
1233url_t *url_make(su_home_t *h, char const *str)
1234{
1235 return url_hdup(h, URL_STRING_MAKE(str)((url_string_t *)((str) && *((char *)(str)) ? (str) :
((void*)0)))
->us_url);
1236}
1237
1238/** Print an URL */
1239url_t *url_format(su_home_t *h, char const *fmt, ...)
1240{
1241 url_t *url;
1242 char *us;
1243 va_list ap;
1244
1245 va_start(ap, fmt)__builtin_va_start(ap, fmt);
1246
1247 us = su_vsprintf(h, fmt, ap);
1248
1249 va_end(ap)__builtin_va_end(ap);
1250
1251 if (us == NULL((void*)0))
1252 return NULL((void*)0);
1253
1254 url = url_hdup(h, URL_STRING_MAKE(us)((url_string_t *)((us) && *((char *)(us)) ? (us) : ((
void*)0)))
->us_url);
1255
1256 su_free(h, us);
1257
1258 return url;
1259}
1260
1261
1262/** Convert @a url to a string allocated from @a home.
1263 *
1264 * @param home memory home to allocate the new string
1265 * @param url url to convert to string
1266 *
1267 * The @a url can be a string, too.
1268 *
1269 * @return Newly allocated conversion result, or NULL upon an error.
1270 */
1271char *url_as_string(su_home_t *home, url_t const *url)
1272{
1273 if (url) {
1274 int len = url_e(NULL((void*)0), 0, url);
1275 char *b = su_alloc(home, len + 1);
1276 url_e(b, len + 1, url);
1277 return b;
1278 } else {
1279 return NULL((void*)0);
1280 }
1281}
1282
1283
1284/** Test if param @a tag matches to parameter string @a p.
1285 */
1286#define URL_PARAM_MATCH(p, tag)(strncasecmp(p, tag, strlen(tag)) == 0 && (p[strlen(tag
)] == '\0' || p[strlen(tag)] == ';' || p[strlen(tag)] == '=')
)
\
1287 (strncasecmp(p, tag, strlen(tag)) == 0 && \
1288 (p[strlen(tag)] == '\0' || p[strlen(tag)] == ';' || p[strlen(tag)] == '='))
1289
1290/**
1291 * Search for a parameter.
1292 *
1293 * This function searches for a parameter from a parameter list.
1294 *
1295 * If you want to test if there is parameter @b user=phone,
1296 * call this function like
1297 * @code if (url_param(url->url_param, "user=phone", NULL, 0))
1298 * @endcode
1299 *
1300 * @param params URL parameter string (excluding first semicolon)
1301 * @param tag parameter name
1302 * @param value string to which the parameter value is copied
1303 * @param vlen length of string reserved for value
1304 *
1305 * @retval positive length of parameter value (including final NUL) if found
1306 * @retval zero if not found.
1307 */
1308isize_t url_param(char const *params,
1309 char const *tag,
1310 char value[], isize_t vlen)
1311{
1312 size_t n, tlen, flen;
1313 char *p;
1314
1315 if (!params)
1316 return 0;
1317
1318 tlen = strlen(tag);
1319 if (tlen && tag[tlen - 1] == '=')
1320 tlen--;
1321
1322 for (p = (char *)params; *p; p += n + 1) {
1323 n = strcspn(p, ";");
1324 if (n < tlen) {
1325 if (p[n]) continue; else break;
1326 }
1327 if (strncasecmp(p, tag, tlen) == 0) {
1328 if (n == tlen) {
1329 if (vlen > 0)
1330 value[0] = '\0';
1331 return 1;
1332 }
1333 if (p[tlen] != '=')
1334 continue;
1335 flen = n - tlen - 1;
1336 if (flen >= (size_t)vlen)
1337 return flen + 1;
1338 memcpy(value, p + tlen + 1, flen);
1339 value[flen] = '\0';
1340 return flen + 1;
1341 }
1342 if (!p[n])
1343 break;
1344 }
1345
1346 return 0;
1347}
1348
1349/** Check for a parameter.
1350 *
1351 * @deprecated
1352 * Bad grammar. Use url_has_param().
1353 */
1354isize_t url_have_param(char const *params, char const *tag)
1355{
1356 return url_param(params, tag, NULL((void*)0), 0);
1357}
1358
1359/** Check for a parameter. */
1360int url_has_param(url_t const *url, char const *tag)
1361{
1362 return url && url->url_params && url_param(url->url_params, tag, NULL((void*)0), 0);
1363}
1364
1365/** Add an parameter. */
1366int url_param_add(su_home_t *h, url_t *url, char const *param)
1367{
1368 /* XXX - should remove existing parameters with same name? */
1369 size_t n = url->url_params ? strlen(url->url_params) + 1: 0;
1370 size_t nn = strlen(param) + 1;
1371 char *s = su_alloc(h, n + nn);
1372
1373 if (!s)
1374 return -1;
1375
1376 if (url->url_params)
1377 strcpy(s, url->url_params)[n - 1] = ';';
1378 strcpy(s + n, param);
1379 url->url_params = s;
1380
1381 return 0;
1382}
1383
1384/** Remove a named parameter from url_param string.
1385 *
1386 * Remove a named parameter and its possible value from the URL parameter
1387 * string (url_s##url_param).
1388 *
1389 * @return Pointer to modified string, or NULL if nothing is left in there.
1390 */
1391char *url_strip_param_string(char *params, char const *name)
1392{
1393 if (params && name) {
1394 size_t i, n = strlen(name), remove, rest;
1395
1396 for (i = 0; params[i];) {
1397 if (strncasecmp(params + i, name, n) ||
1398 (params[i + n] != '=' && params[i + n] != ';' && params[i + n])) {
1399 i = i + strcspn(params + i, ";");
1400 if (!params[i++])
1401 break;
1402 continue;
1403 }
1404 remove = n + strcspn(params + i + n, ";");
1405 if (params[i + remove] == ';')
1406 remove++;
1407
1408 if (i == 0) {
1409 params += remove;
1410 continue;
1411 }
1412
1413 rest = strlen(params + i + remove);
1414 if (!rest) {
1415 if (i == 0)
1416 return NULL((void*)0); /* removed everything */
1417 params[i - 1] = '\0';
1418 break;
1419 }
1420 memmove(params + i, params + i + remove, rest + 1);
1421 }
1422
1423 if (!params[0])
1424 return NULL((void*)0);
1425 }
1426
1427 return params;
1428}
1429
1430int url_string_p(url_string_t const *url)
1431{
1432 return URL_STRING_P(url)((url) && *((url_string_t*)(url))->us_str != 0);
1433}
1434
1435int url_is_string(url_string_t const *url)
1436{
1437 return URL_IS_STRING(url)((url) && *((url_string_t*)(url))->us_str != 0);
1438}
1439
1440/** Strip transport-specific stuff. */
1441static
1442int url_strip_transport2(url_t *url, int modify)
1443{
1444 char *p, *d;
1445 size_t n;
1446 int semi;
1447
1448 if (url->url_type != url_sip && url->url_type != url_sips)
1449 return 0;
1450
1451 if (url->url_port != NULL((void*)0)) {
1452 if (!modify)
1453 return 1;
1454 url->url_port = NULL((void*)0);
1455 }
1456
1457 if (!url->url_params)
1458 return 0;
1459
1460 for (d = p = (char *)url->url_params; *p; p += n + semi) {
1461 n = strcspn(p, ";");
1462 semi = (p[n] != '\0');
1463
1464 if (modify && n == 0)
1465 continue;
1466 if (URL_PARAM_MATCH(p, "method")(strncasecmp(p, "method", strlen("method")) == 0 && (
p[strlen("method")] == '\0' || p[strlen("method")] == ';' || p
[strlen("method")] == '='))
)
1467 continue;
1468 if (URL_PARAM_MATCH(p, "maddr")(strncasecmp(p, "maddr", strlen("maddr")) == 0 && (p[
strlen("maddr")] == '\0' || p[strlen("maddr")] == ';' || p[strlen
("maddr")] == '='))
)
1469 continue;
1470 if (URL_PARAM_MATCH(p, "ttl")(strncasecmp(p, "ttl", strlen("ttl")) == 0 && (p[strlen
("ttl")] == '\0' || p[strlen("ttl")] == ';' || p[strlen("ttl"
)] == '='))
)
1471 continue;
1472 if (URL_PARAM_MATCH(p, "transport")(strncasecmp(p, "transport", strlen("transport")) == 0 &&
(p[strlen("transport")] == '\0' || p[strlen("transport")] ==
';' || p[strlen("transport")] == '='))
)
1473 continue;
1474
1475 if (p != d) {
1476 if (d != url->url_params)
1477 d++;
1478 if (p != d) {
1479 if (!modify)
1480 return 1;
1481 memmove(d, p, n + 1);
1482 }
1483 }
1484 d += n;
1485 }
1486
1487 if (d == p)
1488 return 0;
1489 else if (d + 1 == p) /* empty param */
1490 return 0;
1491 else if (!modify)
1492 return 1;
1493
1494 if (d != url->url_params)
1495 *d = '\0';
1496 else
1497 url->url_params = NULL((void*)0);
1498
1499 return 1;
1500}
1501
1502/** Strip transport-specific stuff.
1503 *
1504 * The function url_strip_transport() removes transport-specific parameters
1505 * from a SIP or SIPS URI. These parameters include:
1506 * - the port number
1507 * - "maddr=" parameter
1508 * - "transport=" parameter
1509 * - "ttl=" parameter
1510 * - "method=" parameter
1511 *
1512 * @note
1513 * The @a url must be a pointer to a URL structure. It is stripped in-place.
1514 *
1515 * @note
1516 * If the parameter string contains empty parameters, they are stripped, too.
1517 *
1518 * @return
1519 * The function url_strip_transport() returns @e true, if the URL was
1520 * modified, @e false otherwise.
1521 */
1522int url_strip_transport(url_t *url)
1523{
1524 return url_strip_transport2(url, 1);
1525}
1526
1527/** Check for transport-specific stuff.
1528 *
1529 * The function url_have_transport() tests if there are transport-specific
1530 * parameters in a SIP or SIPS URI. These parameters include:
1531 * - the port number
1532 * - "maddr=" parameters
1533 * - "transport=" parameters
1534 *
1535 * @note
1536 * The @a url must be a pointer to a URL structure.
1537 *
1538 * @return The function url_have_transport() returns @e true, if the URL
1539 * contains transport parameters, @e false otherwise.
1540 */
1541int url_have_transport(url_t const *url)
1542{
1543 return url_strip_transport2((url_t *)url, 0);
1544}
1545
1546/**Lazily compare two URLs.
1547 *
1548 * Compare essential parts of URLs: schema, host, port, and username.
1549 *
1550 * any_url compares 0 with any other URL.
1551 *
1552 * pres: and im: URIs compares 0 with SIP URIs.
1553 *
1554 * @note
1555 * The @a a and @a b must be pointers to URL structures.
1556 *
1557 * @note Currently, the url parameters are not compared. This is because the
1558 * url_cmp() is used to sort URLs: taking parameters into account makes that
1559 * impossible.
1560 */
1561int url_cmp(url_t const *a, url_t const *b)
1562{
1563 int rv;
1564 int url_type;
1565
1566 if ((a && a->url_type == url_any) || (b && b->url_type == url_any))
1567 return 0;
1568
1569 if (!a || !b)
1570 return (a != NULL((void*)0)) - (b != NULL((void*)0));
1571
1572 if ((rv = a->url_type - b->url_type)) {
1573#if 0
1574 /* presence and instant messaging URLs match magically with SIP */
1575 enum url_type_e a_type = a->url_type;
1576 enum url_type_e b_type = b->url_type;
1577
1578 if (a_type == url_im || a_type == url_pres)
1579 a_type = url_sip;
1580
1581 if (b_type == url_im || b_type == url_pres)
1582 b_type = url_sip;
1583
1584 if (a_type != b_type)
1585#endif
1586 return rv;
1587 }
1588
1589 url_type = a->url_type; /* Or b->url_type, they are equal! */
1590
1591 if (url_type <= url_unknown &&
1592 ((rv = !a->url_scheme - !b->url_scheme) ||
1593 (a->url_scheme && b->url_scheme &&
1594 (rv = strcasecmp(a->url_scheme, b->url_scheme)))))
1595 return rv;
1596
1597 if ((rv = host_cmp(a->url_host, b->url_host)))
1598 return rv;
1599
1600 if (a->url_port != b->url_port) {
1601 char const *a_port;
1602 char const *b_port;
1603
1604 if (url_type != url_sip && url_type != url_sips)
1605 a_port = b_port = url_port_default((enum url_type_e)url_type);
1606 else if (host_is_ip_address(a->url_host))
1607 a_port = b_port = url_port_default((enum url_type_e)url_type);
1608 else
1609 a_port = b_port = "";
1610
1611 if (a->url_port) a_port = a->url_port;
1612 if (b->url_port) b_port = b->url_port;
1613
1614 if ((rv = strcmp(a_port, b_port)))
1615 return rv;
1616 }
1617
1618 if (a->url_user != b->url_user) {
1619 if (a->url_user == NULL((void*)0)) return -1;
1620 if (b->url_user == NULL((void*)0)) return +1;
1621 switch (url_type) {
1622 case url_tel: case url_modem: case url_fax:
1623 rv = url_tel_cmp_numbers(a->url_user, b->url_user);
1624 break;
1625 default:
1626 rv = strcmp(a->url_user, b->url_user);
1627 break;
1628 }
1629 if (rv)
1630 return rv;
1631 }
1632
1633#if 0
1634 if (a->url_path != b->url_path) {
1635 if (a->url_path == NULL((void*)0)) return -1;
1636 if (b->url_path == NULL((void*)0)) return +1;
1637 if ((rv = strcmp(a->url_path, b->url_path)))
1638 return rv;
1639 }
1640#endif
1641
1642 return 0;
1643}
1644
1645static
1646int url_tel_cmp_numbers(char const *A, char const *B)
1647{
1648 short a, b;
1649 int rv;
1650
1651 while (*A && *B) {
1652 #define UNHEX(a) (a - (a >= 'a' ? 'a' - 10 : (a >= 'A' ? 'A' - 10 : '0')))
1653 /* Skip visual-separators */
1654 do {
1655 a = *A++;
1656 if (a == '%' && IS_HEX(A[0])(((A[0]) >= '0' && (A[0]) <= '9') || ((A[0]) >=
'A' && (A[0]) <= 'F') || ((A[0]) >= 'a' &&
(A[0]) <= 'f'))
&& IS_HEX(A[1])(((A[1]) >= '0' && (A[1]) <= '9') || ((A[1]) >=
'A' && (A[1]) <= 'F') || ((A[1]) >= 'a' &&
(A[1]) <= 'f'))
)
1657 a = (UNHEX(A[0]) << 4) | UNHEX(A[1]), A +=2;
1658 } while (a == ' ' || a == '-' || a == '.' || a == '(' || a == ')');
1659
1660 if (isupper(a)((*__ctype_b_loc ())[(int) ((a))] & (unsigned short int) _ISupper
)
)
1661 a = tolower(a);
1662
1663 do {
1664 b = *B++;
1665 if (b == '%' && IS_HEX(B[0])(((B[0]) >= '0' && (B[0]) <= '9') || ((B[0]) >=
'A' && (B[0]) <= 'F') || ((B[0]) >= 'a' &&
(B[0]) <= 'f'))
&& IS_HEX(B[1])(((B[1]) >= '0' && (B[1]) <= '9') || ((B[1]) >=
'A' && (B[1]) <= 'F') || ((B[1]) >= 'a' &&
(B[1]) <= 'f'))
)
1666 b = (UNHEX(B[0]) << 4) | UNHEX(B[1]), B +=2;
1667 } while (b == ' ' || b == '-' || b == '.' || b == '(' || b == ')');
1668
1669 if (isupper(b)((*__ctype_b_loc ())[(int) ((b))] & (unsigned short int) _ISupper
)
)
1670 b = tolower(b);
1671
1672 if ((rv = a - b))
1673 return rv;
1674 }
1675
1676 return (int)*A - (int)*B;
1677}
1678
1679/**Conservative comparison of urls.
1680 *
1681 * Compare all parts of URLs.
1682 *
1683 * @note
1684 * The @a a and @a b must be pointers to URL structures.
1685 *
1686 */
1687int url_cmp_all(url_t const *a, url_t const *b)
1688{
1689 int rv, url_type;
1690
1691 if (!a || !b)
1692 return (a != NULL((void*)0)) - (b != NULL((void*)0));
1693
1694 if ((rv = a->url_type - b->url_type))
1695 return rv;
1696
1697 url_type = a->url_type; /* Or b->url_type, they are equal! */
1698
1699 if (url_type <= url_unknown &&
1700 ((rv = !a->url_scheme - !b->url_scheme) ||
1701 (a->url_scheme && b->url_scheme &&
1702 (rv = strcasecmp(a->url_scheme, b->url_scheme)))))
1703 return rv;
1704
1705 if ((rv = a->url_root - b->url_root))
1706 return rv;
1707
1708 if ((rv = host_cmp(a->url_host, b->url_host)))
1709 return rv;
1710
1711 if (a->url_port != b->url_port) {
1712 char const *a_port;
1713 char const *b_port;
1714
1715 if (url_type != url_sip && url_type != url_sips)
1716 a_port = b_port = url_port_default((enum url_type_e)url_type);
1717 else if (host_is_ip_address(a->url_host))
1718 a_port = b_port = url_port_default((enum url_type_e)url_type);
1719 else
1720 a_port = b_port = "";
1721
1722 if (a->url_port) a_port = a->url_port;
1723 if (b->url_port) b_port = b->url_port;
1724
1725 if ((rv = strcmp(a_port, b_port)))
1726 return rv;
1727 }
1728
1729 if (a->url_user != b->url_user) {
1730 if (a->url_user == NULL((void*)0)) return -1;
1731 if (b->url_user == NULL((void*)0)) return +1;
1732
1733 switch (url_type) {
1734 case url_tel: case url_modem: case url_fax:
1735 rv = url_tel_cmp_numbers(a->url_user, b->url_user);
1736 break;
1737 default:
1738 rv = strcmp(a->url_user, b->url_user);
1739 break;
1740 }
1741 if (rv)
1742 return rv;
1743 }
1744
1745 if (a->url_path != b->url_path) {
1746 if (a->url_path == NULL((void*)0)) return -1;
1747 if (b->url_path == NULL((void*)0)) return +1;
1748 if ((rv = strcmp(a->url_path, b->url_path)))
1749 return rv;
1750 }
1751
1752 if (a->url_params != b->url_params) {
1753 if (a->url_params == NULL((void*)0)) return -1;
1754 if (b->url_params == NULL((void*)0)) return +1;
1755 if ((rv = strcmp(a->url_params, b->url_params)))
1756 return rv;
1757 }
1758
1759 if (a->url_headers != b->url_headers) {
1760 if (a->url_headers == NULL((void*)0)) return -1;
1761 if (b->url_headers == NULL((void*)0)) return +1;
1762 if ((rv = strcmp(a->url_headers, b->url_headers)))
1763 return rv;
1764 }
1765
1766 if (a->url_headers != b->url_headers) {
1767 if (a->url_headers == NULL((void*)0)) return -1;
1768 if (b->url_headers == NULL((void*)0)) return +1;
1769 if ((rv = strcmp(a->url_headers, b->url_headers)))
1770 return rv;
1771 }
1772
1773 if (a->url_fragment != b->url_fragment) {
1774 if (a->url_fragment == NULL((void*)0)) return -1;
1775 if (b->url_fragment == NULL((void*)0)) return +1;
1776 if ((rv = strcmp(a->url_fragment, b->url_fragment)))
1777 return rv;
1778 }
1779
1780 return 0;
1781}
1782
1783/** Return default port number corresponding to the url type */
1784char const *url_port_default(enum url_type_e url_type)
1785{
1786 switch (url_type) {
1787 case url_sip: /* "sip:" */
1788 return "5060";
1789 case url_sips: /* "sips:" */
1790 return "5061";
1791 case url_http: /* "http:" */
1792 return "80";
1793 case url_https: /* "https:" */
1794 return "443";
1795 case url_ftp: /* "ftp:" */
1796 case url_file: /* "file:" */
1797 return "21";
1798 case url_rtsp: /* "rtsp:" */
1799 case url_rtspu: /* "rtspu:" */
1800 return "554";
1801 case url_mailto: /* "mailto:" */
1802 return "25";
1803
1804 case url_any: /* "*" */
1805 return "*";
1806
1807 case url_msrp:
1808 case url_msrps:
1809 return "9999"; /* XXXX */
1810
1811 case url_tel:
1812 case url_urn:
1813 case url_fax:
1814 case url_modem:
1815 case url_im:
1816 case url_pres:
1817 case url_cid:
1818 case url_wv:
1819
1820 default: /* Unknown scheme */
1821 return "";
1822 }
1823}
1824
1825/** Return default transport name corresponding to the url type */
1826char const *url_tport_default(enum url_type_e url_type)
1827{
1828 switch (url_type) {
1829 case url_sip:
1830 return "*";
1831 case url_sips:
1832 return "tls";
1833 case url_http:
1834 return "tcp";
1835 case url_https:
1836 return "tls";
1837 case url_ftp:
1838 case url_file:
1839 return "tcp";
1840 case url_rtsp:
1841 return "tcp";
1842 case url_rtspu:
1843 return "udp";
1844 case url_mailto:
1845 return "tcp";
1846 case url_msrp:
1847 return "tcp";
1848 case url_msrps:
1849 return "tls";
1850
1851 case url_any: /* "*" */
1852 case url_tel:
1853 case url_fax:
1854 case url_modem:
1855 case url_im:
1856 case url_pres:
1857 case url_cid:
1858 case url_urn:
1859 case url_wv:
1860
1861 default: /* Unknown scheme */
1862 return "*";
1863 }
1864}
1865
1866
1867/** Return the URL port string */
1868char const *url_port(url_t const *u)
1869{
1870 if (!u)
1871 return "";
1872 else if (u->url_port && u->url_port[0])
1873 return u->url_port;
1874
1875 if (u->url_type == url_sips || u->url_type == url_sip)
1876 if (!host_is_ip_address(u->url_host))
1877 return "";
1878
1879 return url_port_default((enum url_type_e)u->url_type);
1880}
1881
1882/** Sanitize URL.
1883 *
1884 * The function url_sanitize() adds a scheme to an incomplete URL. It
1885 * modifies its parameter structure @a url. Currently, the function follows
1886 * simple heuristics:
1887 *
1888 * - URL with host name starting with @c ftp. is an FTP URL
1889 * - URL with host name starting with @c www. is an HTTP URL
1890 * - URL with host and path, e.g., @c host/foo;bar, is an HTTP URL
1891 * - URL with host name, no path is a SIP URL.
1892 *
1893 * @param url pointer to URL struct to be sanitized (IN/OUT)
1894 *
1895 * @return
1896 * The function url_sanitize() returns 0 if it considers URL to be
1897 * sane, and -1 otherwise.
1898 */
1899int url_sanitize(url_t *url)
1900{
1901 if (!url)
1902 return -1;
1903 else if (url->url_scheme != NULL((void*)0))
1904 /* xyzzy */;
1905 else if (url->url_host == NULL((void*)0))
1906 return -1;
1907 else if (strncasecmp(url->url_host, "ftp.", strlen("ftp.")) == 0)
1908 url->url_type = url_ftp, url->url_scheme = "ftp", url->url_root = '/';
1909 else if (strncasecmp(url->url_host, "www.", strlen("www.")) == 0
1910 || url->url_path)
1911 url->url_type = url_http, url->url_scheme = "http", url->url_root = '/';
1912 else
1913 url->url_type = url_sip, url->url_scheme = "sip";
1914
1915 return 0;
1916}
1917
1918#include <sofia-sip/su_md5.h>
1919
1920static
1921void canon_update(su_md5_t *md5, char const *s, size_t n, char const *allow)
1922{
1923 size_t i, j;
1924
1925 for (i = 0, j = 0; i < n && s[i]; i++) {
1926 char c;
1927
1928 if (s[i] == '%' && i + 2 < n && IS_HEX(s[i+1])(((s[i+1]) >= '0' && (s[i+1]) <= '9') || ((s[i+
1]) >= 'A' && (s[i+1]) <= 'F') || ((s[i+1]) >=
'a' && (s[i+1]) <= 'f'))
&& IS_HEX(s[i+2])(((s[i+2]) >= '0' && (s[i+2]) <= '9') || ((s[i+
2]) >= 'A' && (s[i+2]) <= 'F') || ((s[i+2]) >=
'a' && (s[i+2]) <= 'f'))
) {
1929#define UNHEX(a) (a - (a >= 'a' ? 'a' - 10 : (a >= 'A' ? 'A' - 10 : '0')))
1930 c = (UNHEX(s[i+1]) << 4) | UNHEX(s[i+2]);
1931#undef UNHEX
1932 if (c != '%' && c > ' ' && c < '\177' &&
1933 (!strchr(EXCLUDED";/?:@&=+$," "<>#%\"" "{}|\\^[]`", c) || strchr(allow, c))) {
1934 if (i != j)
1935 su_md5_iupdate(md5, s + j, i - j);
1936 su_md5_iupdate(md5, &c, 1);
1937 j = i + 3;
1938 }
1939 i += 2;
1940 }
1941 }
1942
1943 if (i != j)
1944 su_md5_iupdate(md5, s + j, i - j);
1945}
1946
1947/** Update MD5 sum with url-string contents */
1948static
1949void url_string_update(su_md5_t *md5, char const *s)
1950{
1951 size_t n, p;
1952 int have_authority = 1;
1953 enum url_type_e type = url_any;
1954 char const *at, *colon;
1955 char schema[48];
1956
1957 if (s == NULL((void*)0) || strlen(s) == 0 || strcmp(s, "*") == 0) {
1958 su_md5_update(md5, "*\0\0*", 4);
1959 return;
1960 }
1961
1962 n = strcspn(s, ":/?#");
1963 if (n >= sizeof schema) {
1964 su_md5_update(md5, ":", 1);
1965 }
1966 else if (n && s[n] == ':' ) {
1967 at = url_canonize(schema, s, n, 0, "+");
1968
1969 type = url_get_type(schema, at - schema);
1970 su_md5_iupdate(md5, schema, at - schema);
1971
1972 have_authority = !url_type_is_opaque(type);
1973 s += n + 1;
1974 }
1975 else {
1976 su_md5_update(md5, "", 1);
1977 }
1978
1979 if (type == url_sip || type == url_sips) {
1980 /* SIP URL may have /;? in user part but no path */
1981 /* user-unreserved = "&" / "=" / "+" / "$" / "," / ";" / "?" / "/" */
1982 /* Some #*@#* phones include unescaped # there, too */
1983 n = strcspn(s, "@/;?#");
1984 p = strcspn(s + n, "@");
1985 if (s[n + p] == '@') {
1986 n += p;
1987 /* Ignore password in hash */
1988 colon = memchr(s, ':', n);
1989 p = colon ? (size_t)(colon - s) : n;
1990 canon_update(md5, s, p, SIP_USER_UNRESERVED"&=+$,;?/");
1991 s += n + 1; n = 0;
1992 }
1993 else
1994 su_md5_iupdate(md5, "", 1); /* user */
1995 n += strcspn(s + n, "/;?#");
1996 }
1997 else if (have_authority) {
1998 if (type == url_wv) { /* WV URL may have / in user part */
1999 n = strcspn(s, "@;?#");
2000 }
2001 else if (type != url_wv && s[0] == '/' && s[1] != '/') {
2002 /* foo:/bar */
2003 su_md5_update(md5, "\0\0", 2); /* user, host */
2004 su_md5_striupdate(md5, url_port_default(type));
2005 return;
2006 }
2007 else if (s[0] == '/' && s[1] == '/') {
2008 /* We have authority, / / foo or foo */
2009 s += 2;
2010 n = strcspn(s, "/?#@[]");
2011 }
2012 else
2013 n = strcspn(s, "@;/?#");
2014
2015 if (s[n] == '@') {
2016 /* Ignore password in hash */
2017 colon = type != url_unknown ? memchr(s, ':', n) : NULL((void*)0);
2018 p = colon ? (size_t)(colon - s) : n;
2019 canon_update(md5, s, p, SIP_USER_UNRESERVED"&=+$,;?/");
2020 s += n + 1;
2021 n = strcspn(s, "/;?#"); /* Until path, query or fragment */
2022 }
2023 else {
2024 su_md5_iupdate(md5, "", 1); /* user */
2025 n += strcspn(s + n, "/;?#"); /* Until path, query or fragment */
2026 }
2027 }
2028 else /* if (!have_authority) */ {
2029 n = strcspn(s, ":/;?#"); /* Until pass, path, query or fragment */
2030
2031 canon_update(md5, s, n, ""); /* user */
2032 su_md5_update(md5, "\0", 1); /* host, no port */
2033 su_md5_striupdate(md5, url_port_default(type));
2034 return;
2035 }
2036
2037 if (n > 0 && s[0] == '[') { /* IPv6reference */
2038 colon = memchr(s, ']', n);
2039 if (colon == NULL((void*)0) || ++colon == s + n || *colon != ':')
2040 colon = NULL((void*)0);
2041 }
2042 else
2043 colon = memchr(s, ':', n);
2044
2045 if (colon) {
2046 canon_update(md5, s, colon - s, ""); /* host */
2047 canon_update(md5, colon + 1, (s + n) - (colon + 1), "");
2048 }
2049 else {
2050 canon_update(md5, s, n, ""); /* host */
2051 su_md5_strupdate(md5, url_port_default(type)); /* port */
2052 }
2053
2054 /* ignore parameters/path/headers.... */
2055}
2056
2057
2058/** Update md5 digest with contents of URL.
2059 *
2060 */
2061void url_update(su_md5_t *md5, url_t const *url)
2062{
2063 if (url_string_p((url_string_t *)url)) {
2064 url_string_update(md5, (char const *)url);
2065 }
2066 else {
2067 SU_MD5_STRI0UPDATE(md5, url->url_scheme)su_md5_iupdate(md5, (url->url_scheme) ? (url->url_scheme
) : "", (url->url_scheme) ? strlen(url->url_scheme) : 1
)
;
2068 SU_MD5_STRI0UPDATE(md5, url->url_user)su_md5_iupdate(md5, (url->url_user) ? (url->url_user) :
"", (url->url_user) ? strlen(url->url_user) : 1)
;
2069 SU_MD5_STRI0UPDATE(md5, url->url_host)su_md5_iupdate(md5, (url->url_host) ? (url->url_host) :
"", (url->url_host) ? strlen(url->url_host) : 1)
;
2070 su_md5_striupdate(md5, URL_PORT(url)((url) && (url)->url_port ? (url)->url_port : url_port_default
((url) ? (enum url_type_e)(url)->url_type : url_any))
);
2071 /* XXX - parameters/path.... */
2072 /* SU_MD5_STRI0UPDATE(md5, url->url_path); */
2073 }
2074}
2075
2076/** Calculate a digest from URL contents. */
2077void url_digest(void *hash, int hsize, url_t const *url, char const *key)
2078{
2079 su_md5_t md5[1];
2080 uint8_t digest[SU_MD5_DIGEST_SIZE16];
2081
2082 su_md5_init(md5);
2083 if (key) su_md5_strupdate(md5, key);
2084 url_update(md5, url);
2085 su_md5_digest(md5, digest);
2086
2087 if (hsize > SU_MD5_DIGEST_SIZE16) {
2088 memset((char *)hash + SU_MD5_DIGEST_SIZE16, 0, hsize - SU_MD5_DIGEST_SIZE16);
2089 hsize = SU_MD5_DIGEST_SIZE16;
2090 }
2091
2092 memcpy(hash, digest, hsize);
2093}
2094
2095/** Convert a URL query to a header string.
2096 *
2097 * URL query is converted by replacing each "=" in header name "=" value
2098 * pair with semicolon (":"), and the "&" separating header-name-value pairs
2099 * with line feed ("\n"). The "body" pseudoheader is moved last in the
2100 * string. The %-escaping is removed. Note that if the @a query contains %00,
2101 * the resulting string will be truncated.
2102 *
2103 * @param home memory home used to alloate string (if NULL, malloc() it)
2104 * @param query query part from SIP URL
2105 *
2106 * The result string is allocated from @a home, and it can be used as
2107 * argument to msg_header_parse_str(), msg_header_add_str() or
2108 * SIPTAG_HEADER_STR().
2109 *
2110 * @sa msg_header_add_str(), SIPTAG_HEADER_STR(),
2111 * sip_headers_as_url_query(), sip_url_query_as_taglist(),
2112 * @RFC3261 section 19.1.1 "Headers", #url_t, url_s#url_headers,
2113 * url_unescape(), url_unescape_to()
2114 *
2115 * @since New in @VERSION_1_12_4.
2116 */
2117char *url_query_as_header_string(su_home_t *home,
2118 char const *query)
2119{
2120 size_t i, j, n, b_start = 0, b_len = 0;
2121 char *s = su_strdup(home, query);
2122
2123 if (!s)
2124 return NULL((void*)0);
2125
2126 for (i = 0, j = 0; s[i];) {
2127 n = strcspn(s + i, "=");
2128 if (!s[i + n])
2129 break;
2130 if (n == 4 && strncasecmp(s + i, "body", 4) == 0) {
2131 if (b_start)
2132 break;
2133 b_start = i + n + 1, b_len = strcspn(s + b_start, "&");
2134 i = b_start + b_len;
2135 if (s[i] != '\0') i += 1;
2136 continue;
2137 }
2138 if (i != j)
2139 memmove(s + j, s + i, n);
2140 s[j + n] = ':';
2141 i += n + 1, j += n + 1;
2142 n = strcspn(s + i, "&");
2143 j += url_unescape_to(s + j, s + i, n);
2144 i += n;
2145 if (s[i]) {
2146 s[j++] = '\n', i++;
2147 }
2148 }
2149
2150 if (s[i])
2151 return (void)su_free(home, s), NULL((void*)0);
2152
2153 if (b_start) {
2154 s[j++] = '\n', s[j++] = '\n';
2155 j += url_unescape_to(s + j, query + b_start, b_len);
2156 }
2157 s[j] = '\0'; assert(j <= i)((void) sizeof ((j <= i) ? 1 : 0), __extension__ ({ if (j <=
i) ; else __assert_fail ("j <= i", "url.c", 2157, __extension__
__PRETTY_FUNCTION__); }))
;
2158
2159 return s;
2160}