Bug Summary

File:libsofia-sip-ua/url/./../bnf/sofia-sip/bnf.h
Warning:line 266, column 10
Array subscript is undefined

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

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)
;
15
Taking true branch
16
Loop condition is true. Entering loop body
17
Taking false branch
18
Taking true branch
19
Loop condition is false. Execution continues on line 336
20
Loop condition is false. Exiting loop
337
338 return url_canonize2(d, s, n, syn33, mask32, mask64, mask96);
21
Calling 'url_canonize2'
24
Returning from 'url_canonize2'
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
21.1
'd' is not equal to 's'
21.1
'd' is not equal to 's'
== s)
22
Taking false branch
353 for (;s[i] && i < n; d++, i++)
354 if (s[i] == '%')
355 break;
356
357 for (;s[i] && i < n; d++, i++) {
23
Assuming the condition is false
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]) {
27
Control jumps to the 'default' case at line 555
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;
28
Execution continues on line 560
556 }
557
558#undef test_scheme
559
560 if (len != span_unreserved(scheme))
29
Calling 'span_unreserved'
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))
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
5.1
's' is not equal to NULL
5.1
's' is not equal to NULL
== NULL((void*)0) || strlen(s) == 0 || strcmp(s, "*") == 0) {
6
Assuming the condition is false
7
Assuming the condition is false
8
Taking false branch
1958 su_md5_update(md5, "*\0\0*", 4);
1959 return;
1960 }
1961
1962 n = strcspn(s, ":/?#");
1963 if (n >= sizeof schema) {
9
Assuming the condition is false
10
Taking false branch
1964 su_md5_update(md5, ":", 1);
1965 }
1966 else if (n && s[n] == ':' ) {
11
Assuming 'n' is not equal to 0
12
Assuming the condition is true
13
Taking true branch
1967 at = url_canonize(schema, s, n, 0, "+");
14
Calling 'url_canonize'
25
Returning from 'url_canonize'
1968
1969 type = url_get_type(schema, at - schema);
26
Calling 'url_get_type'
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)) {
4
Taking true branch
2064 url_string_update(md5, (char const *)url);
5
Calling 'url_string_update'
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);
1
Assuming 'key' is null
2
Taking false branch
2084 url_update(md5, url);
3
Calling 'url_update'
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}

./../bnf/sofia-sip/bnf.h

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#ifndef BNF_H /** Defined when <sofia-sip/bnf.h> has been included. */
26#define BNF_H
27
28/**@file sofia-sip/bnf.h
29 *
30 * Parsing macros and prototypes for HTTP-like protocols.
31 *
32 * @author Pekka Pessi <Pekka.Pessi@nokia.com>
33 *
34 * @date Created: Tue Jun 06 10:59:34 2000 ppessi
35 *
36 */
37
38#include <sofia-sip/su_types.h>
39
40#include <string.h>
41
42SOFIA_BEGIN_DECLS
43
44/* Parsing tokens */
45/** Control characters. */
46#define CTL"\001\002\003\004\005\006\007" "\010\011\012\013\014\015\016\017"
"\020\021\022\023\024\025\026\027" "\030\031\032\033\034\035\036\037"
"\177" "\0"
"\001\002\003\004\005\006\007" \
47 "\010\011\012\013\014\015\016\017" \
48 "\020\021\022\023\024\025\026\027" \
49 "\030\031\032\033\034\035\036\037" "\177" "\0"
50/** Space */
51#define SP" " " "
52/** Horizontal tab */
53#define HT"\t" "\t"
54/** Carriage return */
55/* CR conflicts with Windows SDK 10, so it is now _CR */
56#define _CR"\r" "\r"
57/** Line feed */
58#define LF"\n" "\n"
59/** Line-ending characters */
60#define CRLF"\r" "\n" _CR"\r" LF"\n"
61/** Whitespace */
62#define WS" " "\t" SP" " HT"\t"
63/** Linear whitespace */
64#define LWS" " "\t" "\r" "\n" SP" " HT"\t" _CR"\r" LF"\n"
65/** Lower-case alphabetic characters */
66#define LOALPHA"abcdefghijklmnopqrstuvwxyz" "abcdefghijklmnopqrstuvwxyz"
67/** Upper-case alphabetic characters */
68#define UPALPHA"ABCDEFGHIJKLMNOPQRSTUVWXYZ" "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
69/** Alphabetic characters */
70#define ALPHA"abcdefghijklmnopqrstuvwxyz" "ABCDEFGHIJKLMNOPQRSTUVWXYZ" LOALPHA"abcdefghijklmnopqrstuvwxyz" UPALPHA"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
71/** Digits */
72#define DIGIT"0123456789" "0123456789"
73/** RTSP safe characters */
74#define SAFE"$-_." "$-_." /* RTSP stuff */
75#define ALPHANUM"0123456789" "abcdefghijklmnopqrstuvwxyz" "ABCDEFGHIJKLMNOPQRSTUVWXYZ" DIGIT"0123456789" ALPHA"abcdefghijklmnopqrstuvwxyz" "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
76#define HEX"0123456789" "ABCDEF" "abcdef" DIGIT"0123456789" "ABCDEF" "abcdef"
77
78/** SIP token characters.
79 * @note $|&^# were token chars in RFC 2543, but no more in RFC 3261.
80 */
81#define SIP_TOKEN"0123456789" "abcdefghijklmnopqrstuvwxyz" "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"-.!%*_+`'~"
ALPHANUM"0123456789" "abcdefghijklmnopqrstuvwxyz" "ABCDEFGHIJKLMNOPQRSTUVWXYZ" "-.!%*_+`'~"
82/** SIP separator characters */
83#define SIP_SEPARATOR"()<>@,;:\\\"/[]?={}" " " "\t" "()<>@,;:\\\"/[]?={}" SP" " HT"\t"
84
85/** SIP Word characters (that are not token characters) */
86#define SIP_WORD"()<>:\\\"/[]?{}" "()<>:\\\"/[]?{}"
87
88/** Skip whitespace (SP HT) */
89#define skip_ws(ss)(*(ss) += strspn(*(ss), " " "\t")) (*(ss) += span_ws(*(ss))strspn(*(ss), " " "\t"))
90
91/** Skip linear whitespace (SP HT CR LF) */
92#define skip_lws(ss)(*(ss) += span_lws(*(ss))) (*(ss) += span_lws(*(ss)))
93
94/** Skip [a-zA-Z] */
95#define skip_alpha(ss)(*(ss) += span_alpha(*(ss))) (*(ss) += span_alpha(*(ss)))
96
97/** Skip digits */
98#define skip_digit(ss)(*(ss) += span_digit(*(ss))) (*(ss) += span_digit(*(ss)))
99
100/** Skip characters belonging to an RTSP token. */
101#define skip_alpha_digit_safe(ss)(*(ss) += span_alpha_digit_safe(*(ss))) (*(ss) += span_alpha_digit_safe(*(ss)))
102
103/** Skip characters belonging to a SIP token. */
104#define skip_token(ss)(*(ss) += span_token(*(ss))) (*(ss) += span_token(*(ss)))
105
106/** Skip characters belonging to a SIP parameter value. */
107#define skip_param(ss)(*(ss) += span_param(*(ss))) (*(ss) += span_param(*(ss)))
108
109/** Skip characters belonging to a SIP word. */
110#define skip_word(ss)(*(ss) += span_word(*(ss))) (*(ss) += span_word(*(ss)))
111
112/** Test if @c is CR or LF */
113#define IS_CRLF(c)((c) == '\r' || (c) == '\n') ((c) == '\r' || (c) == '\n')
114/** Test if @c is linear whitespace */
115#define IS_LWS(c)((c) == ' ' || (c) == '\t' || (c) == '\r' || (c) == '\n') ((c) == ' ' || (c) == '\t' || (c) == '\r' || (c) == '\n')
116/*#define IS_LWS(c) ((_bnf_table[(unsigned char)(c)] & bnf_lws))*/
117/** Test if @c is normal whitespace */
118#define IS_WS(c)((c) == ' ' || (c) == '\t') ((c) == ' ' || (c) == '\t')
119/** Test if @c is not whitespace (and not NUL). */
120#define IS_NON_WS(c)(c && !((c) == ' ' || (c) == '\t')) (c && !IS_WS(c)((c) == ' ' || (c) == '\t'))
121/*#define IS_NON_WS(c) (c && !(_bnf_table[(unsigned char)c] & bnf_ws))*/
122/** Test if @c is not linear whitespace (and not NUL). */
123#define IS_NON_LWS(c)(c && !((c) == ' ' || (c) == '\t' || (c) == '\r' || (
c) == '\n'))
(c && !IS_LWS(c)((c) == ' ' || (c) == '\t' || (c) == '\r' || (c) == '\n'))
124/*#define IS_NON_LWS(c) (c && !(_bnf_table[(unsigned char)c] & bnf_lws))*/
125/** Test if @c is a digit. */
126#define IS_DIGIT(c)((c) >= '0' && (c) <= '9') ((c) >= '0' && (c) <= '9')
127/** Test if @c is alphabetic. */
128#define IS_ALPHA(c)(c && ((_bnf_table[(unsigned char)c] & bnf_alpha)
))
(c && ((_bnf_table[(unsigned char)c] & bnf_alpha)))
129/** Test if @c is alphanumeric. */
130#define IS_ALPHANUM(c)(c && (((c) >= '0' && (c) <= '9') || (c
&& ((_bnf_table[(unsigned char)c] & bnf_alpha)))
))
(c && (IS_DIGIT(c)((c) >= '0' && (c) <= '9') || IS_ALPHA(c)(c && ((_bnf_table[(unsigned char)c] & bnf_alpha)
))
))
131/** Test if @c is URL-unreserved. */
132#define IS_UNRESERVED(c)((_bnf_table[(unsigned char)c] & bnf_unreserved)) ((_bnf_table[(unsigned char)c] & bnf_unreserved))
133/** Test if @c is URL-reserved. */
134#define IS_RESERVED(c)(c && !(_bnf_table[(unsigned char)c] & bnf_unreserved
))
(c && !(_bnf_table[(unsigned char)c] & bnf_unreserved))
135/** Test if @c is valid in tokens. */
136#define IS_TOKEN(c)((_bnf_table[(unsigned char)c] & bnf_token)) ((_bnf_table[(unsigned char)c] & bnf_token))
137/** Test if @c is valid for SIP parameter value. */
138#define IS_PARAM(c)((_bnf_table[(unsigned char)c] & (bnf_token|bnf_param))) ((_bnf_table[(unsigned char)c] & (bnf_token|bnf_param)))
139/** Test if @c is a hex digit. */
140#define IS_HEX(c)(((c) >= '0' && (c) <= '9') || ((c) >= 'A' &&
(c) <= 'F') || ((c) >= 'a' && (c) <= 'f'))
(((c) >= '0' && (c) <= '9') || ((c) >= 'A' && (c) <= 'F') || ((c) >= 'a' && (c) <= 'f'))
141/** Test if @c is a linear whitespace or valid in tokens. */
142#define IS_TOKENLWS(c)((_bnf_table[(unsigned char)c] & (bnf_token|bn_lws))) ((_bnf_table[(unsigned char)c] & (bnf_token|bn_lws)))
143
144enum {
145 bnf_ws = 1, /**< Whitespace character */
146 bnf_crlf = 2, /**< Line end character */
147 bnf_lws = 3, /**< Linear whitespace */
148 bnf_alpha = 4, /**< Alphabetic */
149 bnf_safe = 8, /**< RTSP safe */
150 bnf_mark = 16, /**< URL mark */
151 bnf_unreserved = bnf_alpha | bnf_mark, /**< URL unreserved */
152 bnf_separator = 32, /**< SIP separator */
153 /** SIP token, not alphabetic (0123456789-.!%*_+`'~) */
154 bnf_token0 = 64 | bnf_safe,
155 bnf_token = bnf_token0 | bnf_alpha, /**< SIP token */
156 bnf_param0 = 128, /**< SIP parameter, not token */
157 bnf_param = bnf_token | bnf_param0 /**< SIP/HTTP parameter */
158};
159
160/** Table for determining class of a character. */
161SOFIAPUBVARextern unsigned char const _bnf_table[256];
162
163/** Get number of characters before CRLF */
164#define span_non_crlf(s)strcspn(s, "\r" "\n") strcspn(s, _CR"\r" LF"\n")
165
166/** Get number of characters before whitespace */
167#define span_non_ws(s)strcspn(s, " " "\t") strcspn(s, WS" " "\t")
168
169/** Get number of whitespace characters */
170#define span_ws(s)strspn(s, " " "\t") strspn(s, WS" " "\t")
171
172/** Get number of characters before linear whitespace */
173#define span_non_lws(s)strcspn(s, " " "\t" "\r" "\n") strcspn(s, LWS" " "\t" "\r" "\n")
174
175/** Calculate span of a linear whitespace.
176 * LWS = [*WSP CRLF] 1*WSP
177 */
178su_inlinestatic inline isize_t span_lws(char const *s)
179{
180 char const *e = s;
181 int i = 0;
182 e += strspn(s, WS" " "\t");
183 if (e[i] == '\r') i++;
184 if (e[i] == '\n') i++;
185 if (IS_WS(e[i])((e[i]) == ' ' || (e[i]) == '\t'))
186 e += i + strspn(e + i, WS" " "\t");
187 return e - s;
188}
189
190/** Calculate span of a token or linear whitespace characters. */
191su_inlinestatic inline isize_t span_token_lws(char const *s)
192{
193 char const *e = s;
194 while (_bnf_table[(unsigned char)(*e)] & (bnf_token | bnf_lws))
195 e++;
196 return e - s;
197}
198
199/** Calculate span of a token characters. */
200su_inlinestatic inline isize_t span_token(char const *s)
201{
202 char const *e = s;
203 while (_bnf_table[(unsigned char)(*e)] & bnf_token)
204 e++;
205 return e - s;
206}
207
208/** Calculate span of a alphabetic characters. */
209su_inlinestatic inline isize_t span_alpha(char const *s)
210{
211 char const *e = s;
212 while (_bnf_table[(unsigned char)(*e)] & bnf_alpha)
213 e++;
214 return e - s;
215}
216
217/** Calculate span of a digits. */
218su_inlinestatic inline isize_t span_digit(char const *s)
219{
220 char const *e = s;
221 while (*e >= '0' && *e <= '9')
222 e++;
223 return e - s;
224}
225
226/** Calculate span of a hex. */
227su_inlinestatic inline isize_t span_hexdigit(char const *s)
228{
229 char const *e = s;
230 while (IS_HEX(*e)(((*e) >= '0' && (*e) <= '9') || ((*e) >= 'A'
&& (*e) <= 'F') || ((*e) >= 'a' && (*e
) <= 'f'))
)
231 e++;
232 return e - s;
233}
234
235/** Calculate span of characters belonging to an RTSP token */
236su_inlinestatic inline isize_t span_alpha_digit_safe(char const *s)
237{
238 char const *e = s;
239 while (_bnf_table[(unsigned char)(*e)] & (bnf_alpha | bnf_safe))
240 e++;
241 return e - s;
242}
243
244/** Calculate span of a characters valid in parameters. */
245su_inlinestatic inline isize_t span_param(char const *s)
246{
247 char const *e = s;
248 while (IS_PARAM(*e)((_bnf_table[(unsigned char)*e] & (bnf_token|bnf_param))))
249 e++;
250 return e - s;
251}
252
253/** Calculate span of a SIP word. */
254su_inlinestatic inline isize_t span_word(char const *s)
255{
256 char const *e = s;
257 while (*e && (IS_TOKEN(*e)((_bnf_table[(unsigned char)*e] & bnf_token)) || strchr(SIP_WORD"()<>:\\\"/[]?{}", *e)))
258 e++;
259 return e - s;
260}
261
262/** Calculate span of a unreserved characters. */
263su_inlinestatic inline isize_t span_unreserved(char const *s)
264{
265 char const *e = s;
266 while (IS_UNRESERVED(*e)((_bnf_table[(unsigned char)*e] & bnf_unreserved)))
30
Loop condition is true. Entering loop body
31
Array subscript is undefined
267 e++;
268 return e - s;
269}
270
271/** Calculate span of a double quoted string (with escaped chars inside) */
272su_inlinestatic inline isize_t span_quoted(char const *s)
273{
274 char const *b = s;
275
276 if (*s++ != '"')
277 return 0;
278
279 for (;;) {
280 s += strcspn(s, "\\\"");
281 if (!*s)
282 return 0;
283 if (*s++ == '"')
284 return s - b;
285 if (!*s++)
286 return 0;
287 }
288}
289
290/* RFC 2396 defines URL chars */
291/** Reserved in URLs */
292#define URL_RESERVED";/?:=+$," ";/?:=+$,"
293
294/** Non-alphanumeric characters without syntactical meaning. */
295#define URL_MARK"-_.!~*'()" "-_.!~*'()"
296
297/** Unreserved characters. */
298#define URL_UNRESERVED"0123456789" "abcdefghijklmnopqrstuvwxyz" "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"-_.!~*'()"
ALPHANUM"0123456789" "abcdefghijklmnopqrstuvwxyz" "ABCDEFGHIJKLMNOPQRSTUVWXYZ" URL_MARK"-_.!~*'()"
299
300/** URL hex escape. */
301#define URL_ESCAPED"%" "%"
302#define URL_DELIMS"<>#%\"" "<>#%\""
303#define URL_UNWISE"{}|\\^[]`" "{}|\\^[]`"
304#define URL_SCHEME"0123456789" "abcdefghijklmnopqrstuvwxyz" "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"+-."
ALPHANUM"0123456789" "abcdefghijklmnopqrstuvwxyz" "ABCDEFGHIJKLMNOPQRSTUVWXYZ" "+-."
305
306/** Get number of characters belonging to url scheme */
307#define span_url_scheme(s)strspn(s, "0123456789" "abcdefghijklmnopqrstuvwxyz" "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"+-.")
strspn(s, URL_SCHEME"0123456789" "abcdefghijklmnopqrstuvwxyz" "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"+-."
)
308
309SOFIAPUBFUN int span_ip4_address(char const *host);
310SOFIAPUBFUN int span_ip6_address(char const *host);
311SOFIAPUBFUN int span_ip6_reference(char const *host);
312SOFIAPUBFUN int span_ip_address(char const *host);
313SOFIAPUBFUN isize_t span_domain(char const *host);
314SOFIAPUBFUN isize_t span_host(char const *host);
315
316SOFIAPUBFUN int scan_ip4_address(char **inout_host);
317SOFIAPUBFUN int scan_ip6_address(char **inout_host);
318SOFIAPUBFUN int scan_ip6_reference(char **inout_host);
319SOFIAPUBFUN int scan_ip_address(char **inout_host);
320SOFIAPUBFUN issize_t scan_domain(char **inout_host);
321SOFIAPUBFUN issize_t scan_host(char **inout_host);
322
323SOFIA_END_DECLS
324
325#endif /* !defined BNF_H */