File: | url/./../bnf/sofia-sip/bnf.h |
Warning: | line 266, column 10 Array subscript is undefined |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
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 */ | |||
146 | static char *url_canonize(char *d, char const *s, size_t n, | |||
147 | unsigned syn33, | |||
148 | char const allowed[]); | |||
149 | static char *url_canonize2(char *d, char const *s, size_t n, | |||
150 | unsigned syn33, | |||
151 | unsigned m32, unsigned m64, unsigned m96); | |||
152 | static 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 | */ | |||
163 | int 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 | */ | |||
198 | isize_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 | */ | |||
229 | char *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 | */ | |||
275 | size_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 | */ | |||
320 | char *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 */ | |||
329 | static | |||
330 | char *url_canonize(char *d, char const *s, size_t n, | |||
331 | unsigned syn33, | |||
332 | char const allowed[]) | |||
333 | { | |||
334 | unsigned mask32 = 0xbe19003f, mask64 = 0x8000001e, mask96 = 0x8000001d; | |||
335 | ||||
336 | MASKS_WITH_ALLOWED(allowed, mask32, mask64, mask96)do { if (allowed) { for (;allowed[0]; allowed++) { unsigned a = allowed[0]; if (a < 32) ; else if (a < 64) mask32 &= ~(1U << (63 - a)); else if (a < 96) mask64 &= ~ (1U << (95 - a)); else if (a < 128) mask96 &= ~( 1U << (127 - a)); } } } while (0); | |||
337 | ||||
338 | return url_canonize2(d, s, n, syn33, mask32, mask64, mask96); | |||
339 | } | |||
340 | ||||
341 | #define SYN33(c)(1U << (c - 33)) (1U << (c - 33)) | |||
342 | #define IS_SYN33(syn33, c)((syn33 & (1U << (c - 33))) != 0) ((syn33 & (1U << (c - 33))) != 0) | |||
343 | ||||
344 | /** Canonize a URL component (with precomputed mask) */ | |||
345 | static | |||
346 | char *url_canonize2(char *d, char const * const s, size_t n, | |||
347 | unsigned syn33, | |||
348 | unsigned m32, unsigned m64, unsigned m96) | |||
349 | { | |||
350 | size_t i = 0; | |||
351 | ||||
352 | if (d == s) | |||
353 | for (;s[i] && i < n; d++, i++) | |||
354 | if (s[i] == '%') | |||
355 | break; | |||
356 | ||||
357 | for (;s[i] && i < n; d++, i++) { | |||
358 | unsigned char c = s[i], h1, h2; | |||
359 | ||||
360 | if (c != '%') { | |||
361 | if (!IS_SYN33(syn33, c)((syn33 & (1U << (c - 33))) != 0) && IS_EXCLUDED(c, m32, m64, m96)(c <= ' ' || c >= '\177' || (c < 64 ? (m32 & (1 << (63 - c))) : (c < 96 ? (m64 & (1 << (95 - c))) : (m96 & (1 << (127 - c))))) != 0)) | |||
362 | return NULL((void*)0); | |||
363 | *d = c; | |||
364 | continue; | |||
365 | } | |||
366 | ||||
367 | h1 = s[i + 1], 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 | */ | |||
406 | static | |||
407 | char *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. */ | |||
459 | char 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 | ||||
488 | su_inlinestatic inline | |||
489 | int 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 */ | |||
500 | void 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 */ | |||
512 | su_inlinestatic inline | |||
513 | enum 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]) { | |||
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; | |||
546 | } | |||
547 | ||||
548 | #undef test_scheme | |||
549 | ||||
550 | if (len != span_unreserved(scheme)) | |||
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 | */ | |||
569 | static | |||
570 | int _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 | */ | |||
769 | int 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 | */ | |||
858 | issize_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 | */ | |||
996 | isize_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 | */ | |||
1028 | isize_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 | ||||
1059 | su_inlinestatic inline | |||
1060 | char *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 | */ | |||
1100 | issize_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 | */ | |||
1202 | url_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 */ | |||
1223 | url_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 */ | |||
1229 | url_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 | */ | |||
1261 | char *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 | */ | |||
1298 | isize_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 | */ | |||
1344 | isize_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. */ | |||
1350 | int 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. */ | |||
1356 | int 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 | */ | |||
1381 | char *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 | ||||
1420 | int 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 | ||||
1425 | int 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. */ | |||
1431 | static | |||
1432 | int 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 | */ | |||
1512 | int 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 | */ | |||
1531 | int 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 | */ | |||
1551 | int 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 | ||||
1635 | static | |||
1636 | int 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 | */ | |||
1677 | int 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 */ | |||
1774 | char 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 */ | |||
1816 | char 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 */ | |||
1858 | char 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 | */ | |||
1889 | int 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 | ||||
1910 | static | |||
1911 | void 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 */ | |||
1938 | static | |||
1939 | void 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) { | |||
1948 | su_md5_update(md5, "*\0\0*", 4); | |||
1949 | return; | |||
1950 | } | |||
1951 | ||||
1952 | n = strcspn(s, ":/?#"); | |||
1953 | if (n >= sizeof schema) { | |||
1954 | su_md5_update(md5, ":", 1); | |||
1955 | } | |||
1956 | else if (n && s[n] == ':' ) { | |||
1957 | at = url_canonize(schema, s, n, 0, "+"); | |||
1958 | ||||
1959 | type = url_get_type(schema, at - schema); | |||
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 | */ | |||
2051 | void url_update(su_md5_t *md5, url_t const *url) | |||
2052 | { | |||
2053 | if (url_string_p((url_string_t *)url)) { | |||
2054 | url_string_update(md5, (char const *)url); | |||
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. */ | |||
2067 | void 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); | |||
| ||||
2074 | url_update(md5, url); | |||
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 | */ | |||
2107 | char *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 | } |
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 | ||||||
42 | SOFIA_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 | ||||||
144 | enum { | |||||
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. */ | |||||
161 | SOFIAPUBVARextern 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 | */ | |||||
178 | su_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. */ | |||||
191 | su_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. */ | |||||
200 | su_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. */ | |||||
209 | su_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. */ | |||||
218 | su_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. */ | |||||
227 | su_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 */ | |||||
236 | su_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. */ | |||||
245 | su_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. */ | |||||
254 | su_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. */ | |||||
263 | su_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))) | |||||
| ||||||
267 | e++; | |||||
268 | return e - s; | |||||
269 | } | |||||
270 | ||||||
271 | /** Calculate span of a double quoted string (with escaped chars inside) */ | |||||
272 | su_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 | ||||||
309 | SOFIAPUBFUN int span_ip4_address(char const *host); | |||||
310 | SOFIAPUBFUN int span_ip6_address(char const *host); | |||||
311 | SOFIAPUBFUN int span_ip6_reference(char const *host); | |||||
312 | SOFIAPUBFUN int span_ip_address(char const *host); | |||||
313 | SOFIAPUBFUN isize_t span_domain(char const *host); | |||||
314 | SOFIAPUBFUN isize_t span_host(char const *host); | |||||
315 | ||||||
316 | SOFIAPUBFUN int scan_ip4_address(char **inout_host); | |||||
317 | SOFIAPUBFUN int scan_ip6_address(char **inout_host); | |||||
318 | SOFIAPUBFUN int scan_ip6_reference(char **inout_host); | |||||
319 | SOFIAPUBFUN int scan_ip_address(char **inout_host); | |||||
320 | SOFIAPUBFUN issize_t scan_domain(char **inout_host); | |||||
321 | SOFIAPUBFUN issize_t scan_host(char **inout_host); | |||||
322 | ||||||
323 | SOFIA_END_DECLS | |||||
324 | ||||||
325 | #endif /* !defined BNF_H */ |