Bug Summary

File: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 -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-eagerly-assume -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 -mrelocation-model pic -pic-level 2 -mthread-model posix -mdisable-fp-elim -fmath-errno -masm-verbose -mconstructor-aliases -munwind-tables -fuse-init-array -target-cpu x86-64 -dwarf-column-info -debugger-tuning=gdb -resource-dir /usr/lib/llvm-7/lib/clang/7.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-7/lib/clang/7.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 -fmessage-length 0 -fobjc-runtime=gcc -fdiagnostics-show-option -analyzer-output=html -o /drone/src/scan-build/2021-08-31-093522-363-1 -x c url.c -faddrsig

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

./../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)))
24
Loop condition is true. Entering loop body
25
Within the expansion of the macro 'IS_UNRESERVED':
a
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 */