Bug Summary

File:libsofia-sip-ua/msg/msg_parser_util.c
Warning:line 447, column 12
Although the value stored to 'N' is used in the enclosing expression, the value is never actually read from 'N'

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-pc-linux-gnu -analyze -disable-free -disable-llvm-verifier -discard-value-names -main-file-name msg_parser_util.c -analyzer-store=region -analyzer-opt-analyze-nested-blocks -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model pic -pic-level 2 -mframe-pointer=all -fmath-errno -fno-rounding-math -mconstructor-aliases -munwind-tables -target-cpu x86-64 -fno-split-dwarf-inlining -debugger-tuning=gdb -resource-dir /usr/lib/llvm-11/lib/clang/11.0.1 -D HAVE_CONFIG_H -I . -I ../.. -I ../../libsofia-sip-ua/su/sofia-sip -I ./../url -I ../url -I ./../bnf -I ../bnf -I ./../su -I ../su -D SU_DEBUG=0 -D PIC -internal-isystem /usr/local/include -internal-isystem /usr/lib/llvm-11/lib/clang/11.0.1/include -internal-externc-isystem /usr/include/x86_64-linux-gnu -internal-externc-isystem /include -internal-externc-isystem /usr/include -fdebug-compilation-dir /drone/src/libsofia-sip-ua/msg -ferror-limit 19 -fgnuc-version=4.2.1 -analyzer-output=html -faddrsig -o /drone/src/scan-build/2022-06-23-181620-12-1 -x c msg_parser_util.c
1/*
2 * This file is part of the Sofia-SIP package
3 *
4 * Copyright (C) 2005 Nokia Corporation.
5 *
6 * Contact: Pekka Pessi <pekka.pessi@nokia.com>
7 *
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public License
10 * as published by the Free Software Foundation; either version 2.1 of
11 * the License, or (at your option) any later version.
12 *
13 * This library is distributed in the hope that it will be useful, but
14 * WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
17 *
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
21 * 02110-1301 USA
22 *
23 */
24
25/**@ingroup msg_parser
26 * @CFILE msg_parser_util.c
27 *
28 * Text-message parser helper functions.
29 *
30 * @author Pekka Pessi <Pekka.Pessi@nokia.com>
31 *
32 * @date Created: Tue Aug 28 16:26:34 2001 ppessi
33 *
34 */
35
36#include "config.h"
37
38#include <stddef.h>
39#include <stdlib.h>
40#include <string.h>
41#include <stdio.h>
42#include <assert.h>
43#include <limits.h>
44
45#include <stdarg.h>
46#include <sofia-sip/su_tagarg.h>
47
48#include <sofia-sip/su.h>
49#include <sofia-sip/su_alloc.h>
50#include <sofia-sip/su_string.h>
51
52#include "msg_internal.h"
53#include "sofia-sip/msg_parser.h"
54#include "sofia-sip/bnf.h"
55
56#include "sofia-sip/url.h"
57
58static issize_t msg_comma_scanner(char *start);
59
60/**
61 * Parse first line.
62 *
63 * Splits the first line from a message into three whitespace-separated
64 * parts.
65 */
66int msg_firstline_d(char *s, char **return_part2, char **return_part3)
67{
68 char *s1 = s, *s2, *s3;
69 size_t n;
70
71 /* Split line into three segments separated by whitespace */
72 if (s1[n = span_non_ws(s1)strcspn(s1, " " "\t")]) {
73 s1[n] = '\0';
74 s2 = s1 + n + 1;
75 while (IS_WS(*s2)((*s2) == ' ' || (*s2) == '\t'))
76 s2++;
77 }
78 else {
79 /* Hopeless - no WS in first line */
80 return -1;
81 }
82
83 n = span_non_ws(s2)strcspn(s2, " " "\t");
84
85 if (s2[n]) {
86 s2[n++] = '\0';
87 while (IS_WS(s2[n])((s2[n]) == ' ' || (s2[n]) == '\t'))
88 n++;
89 }
90
91 s3 = s2 + n;
92
93 *return_part2 = s2;
94
95 *return_part3 = s3;
96
97 return 0;
98}
99
100/**Parse a token.
101 *
102 * Parses a token from string pointed by @a *ss. It stores the token value
103 * in @a return_token, and updates the @a ss to the end of token and
104 * possible whitespace.
105 */
106issize_t msg_token_d(char **ss, char const **return_token)
107{
108 char *s = *ss;
109 size_t n = span_token(s);
110 if (n) {
111 for (; IS_LWS(s[n])((s[n]) == ' ' || (s[n]) == '\t' || (s[n]) == '\r' || (s[n]) ==
'\n')
; n++)
112 s[n] = '\0';
113 *return_token = s;
114 *ss = s + n;
115 return n;
116 }
117 else
118 return -1;
119}
120
121/** Parse a 32-bit unsigned int.
122 *
123 * The function msg_uint32_d() parses a 32-bit unsigned integer in string
124 * pointed by @a *ss. It stores the value in @a return_token and updates the
125 * @a ss to the end of integer and possible whitespace.
126 *
127 * @retval length of parsed integer, or -1 upon an error.
128 */
129issize_t msg_uint32_d(char **ss, uint32_t *return_value)
130{
131 char const *s = *ss, *s0 = s;
132 uint32_t value;
133 unsigned digit;
134
135 if (!IS_DIGIT(*s)((*s) >= '0' && (*s) <= '9'))
136 return -1;
137
138 for (value = 0; IS_DIGIT(*s)((*s) >= '0' && (*s) <= '9'); s++) {
139 digit = *s - '0';
140 if (value > 429496729U)
141 return -1;
142 else if (value == 429496729U && digit > 5)
143 return -1;
144 value = 10 * value + digit;
145 }
146
147 if (*s) {
148 if (!IS_LWS(*s)((*s) == ' ' || (*s) == '\t' || (*s) == '\r' || (*s) == '\n'))
149 return (issize_t)-1;
150 skip_lws(&s)(*(&s) += span_lws(*(&s)));
151 }
152
153 *ss = (char *)s;
154 *return_value = value;
155
156 return s - s0;
157}
158
159
160/** Parse any list.
161 *
162 * Parses a list of items, separated by @a sep. The parsed string is passed
163 * in @a *ss, which is updated to point to the first non-linear-whitespace
164 * character after the list. The function modifies the string as it parses
165 * it.
166 *
167 * The parsed items are appended to the list @a *append_list. If there the
168 * list in @a *append_list is NULL, allocate a new list and return it in @a
169 * *append_list. Empty list items are ignored, and are not appended to the
170 * list.
171 *
172 * The function @b must be passed a scanning function @a scanner. The
173 * scanning function scans for a legitimate list item, for example, a token.
174 * It should also compact the list item, for instance, if the item consists
175 * of @c name=value parameter definitions it should remove whitespace around
176 * "=" sign. The scanning function returns the length of the scanned item,
177 * including any linear whitespace after it.
178 *
179 * @param[in] home memory home for allocating list pointers
180 * @param[in,out] ss pointer to pointer to string to be parsed
181 * @param[in,out] append_list pointer to list
182 * where parsed list items are appended
183 * @param[in] sep separator character
184 * @param[in] scanner pointer to function for scanning a single item
185 *
186 * @retval 0 if successful.
187 * @retval -1 upon an error.
188 */
189issize_t msg_any_list_d(su_home_t *home,
190 char **ss,
191 msg_param_t **append_list,
192 issize_t (*scanner)(char *s),
193 int sep)
194{
195 char const *stack[MSG_N_PARAMSmsg_n_params];
196 char const **list = stack, **re_list;
197 size_t N = MSG_N_PARAMSmsg_n_params, n = 0;
198 issize_t tlen;
199 char *s = *ss;
200 char const **start;
201
202 if (!scanner)
203 return -1;
204
205 if (*append_list) {
206 list = *append_list;
207 while (list[n])
208 n++;
209 N = MSG_PARAMS_NUM(n + 1)(((n + 1) + msg_n_params - 1) & (size_t)(0 - msg_n_params
))
;
210 }
211
212 start = &list[n];
213
214 skip_lws(&s)(*(&s) += span_lws(*(&s)));
215
216 while (*s) {
217 tlen = scanner(s);
218
219 if (tlen < 0 || (s[tlen] && s[tlen] != sep && s[tlen] != ','))
220 goto error;
221
222 if (tlen > 0) {
223 if (n + 1 == N) { /* Reallocate list? */
224 N = MSG_PARAMS_NUM(N + 1)(((N + 1) + msg_n_params - 1) & (size_t)(0 - msg_n_params
))
;
225 if (list == stack || list == *append_list) {
226 re_list = su_alloc(home, N * sizeof(*list));
227 if (re_list)
228 memcpy(re_list, list, n * sizeof(*list));
229 }
230 else
231 re_list = su_realloc(home, list, N * sizeof(*list));
232 if (!re_list)
233 goto error;
234 list = re_list;
235 }
236
237 list[n++] = s;
238 s += tlen;
239 }
240
241 if (*s == sep) {
242 *s++ = '\0';
243 skip_lws(&s)(*(&s) += span_lws(*(&s)));
244 }
245 else if (*s)
246 break;
247 }
248
249 *ss = s;
250
251 if (n == 0) {
252 *append_list = NULL((void*)0);
253 return 0;
254 }
255
256 if (list == stack) {
257 size_t size = sizeof(*list) * MSG_PARAMS_NUM(n + 1)(((n + 1) + msg_n_params - 1) & (size_t)(0 - msg_n_params
))
;
258 list = su_alloc(home, size);
259 if (!list) return -1;
260 memcpy((void *)list, stack, n * sizeof(*list));
261 }
262
263 list[n] = NULL((void*)0);
264 *append_list = list;
265 return 0;
266
267 error:
268 *start = NULL((void*)0);
269 if (list != stack && list != *append_list)
270 su_free(home, list);
271 return -1;
272}
273
274/** Scan an attribute (name [= value]) pair.
275 *
276 * The attribute consists of name (a token) and optional value, separated by
277 * equal sign. The value can be a token or quoted string.
278 *
279 * This function compacts the scanned value. It removes the
280 * whitespace around equal sign "=" by moving the equal sign character and
281 * value towards name.
282 *
283 * If there is whitespace within the scanned value or after it,
284 * NUL-terminates the scanned attribute.
285 *
286 * @retval > 0 number of characters scanned,
287 * including the whitespace within the value
288 * @retval -1 upon an error
289 */
290issize_t msg_attribute_value_scanner(char *s)
291{
292 char *p = s;
293 size_t tlen;
294
295 skip_token(&s)(*(&s) += span_token(*(&s)));
296
297 if (s == p) /* invalid parameter name */
298 return -1;
299
300 tlen = s - p;
301
302 if (IS_LWS(*s)((*s) == ' ' || (*s) == '\t' || (*s) == '\r' || (*s) == '\n')) { *s++ = '\0'; skip_lws(&s)(*(&s) += span_lws(*(&s))); }
303
304 if (*s == '=') {
305 char *v;
306 s++;
307 skip_lws(&s)(*(&s) += span_lws(*(&s)));
308
309 /* get value */
310 if (*s == '"') {
311 size_t qlen = span_quoted(s);
312 if (!qlen)
313 return -1;
314 v = s; s += qlen;
315 }
316 else {
317 v = s;
318 skip_param(&s)(*(&s) += span_param(*(&s)));
319 if (s == v)
320 return -1;
321 }
322
323 if (p + tlen + 1 != v) {
324 memmove(p + tlen + 1, v, s - v);
325 p[tlen] = '=';
326 p[tlen + 1 + (s - v)] = '\0';
327 }
328 }
329
330 if (IS_LWS(*s)((*s) == ' ' || (*s) == '\t' || (*s) == '\r' || (*s) == '\n')) { *s++ = '\0'; skip_lws(&s)(*(&s) += span_lws(*(&s))); }
331
332 return s - p;
333}
334
335/**Parse an attribute-value list.
336 *
337 * Parses an attribute-value list, which has syntax as follows:
338 * @code
339 * av-list = (av-pair *(";" av-pair)
340 * av-pair = token ["=" ( value / quoted-string) ] ; optional value
341 * @endcode
342 *
343 * @param[in] home pointer to a memory home
344 * @param[in,out] ss pointer to string at the start of parameter list
345 * @param[in,out] append_list pointer to list
346 * where parsed list items are appended
347 *
348 * @retval >= 0 if successful
349 * @retval -1 upon an error
350 */
351issize_t msg_avlist_d(su_home_t *home,
352 char **ss,
353 msg_param_t const **append_list)
354{
355 char const *stack[MSG_N_PARAMSmsg_n_params];
356 char const **params;
357 size_t n = 0, N;
358 char *s = *ss;
359
360 if (!*s)
361 return -1;
362
363 if (*append_list) {
364 params = (char const **)*append_list;
365 for (n = 0; params[n]; n++)
366 ;
367 N = MSG_PARAMS_NUM(n + 1)(((n + 1) + msg_n_params - 1) & (size_t)(0 - msg_n_params
))
;
368 }
369 else {
370 params = stack;
371 N = MSG_PARAMS_NUM(1)(((1) + msg_n_params - 1) & (size_t)(0 - msg_n_params));
372 }
373
374 for (;;) {
375 char *p;
376 size_t tlen;
377
378 /* XXX - we should handle also quoted parameters */
379
380 skip_lws(&s)(*(&s) += span_lws(*(&s)));
381 p = s;
382 skip_token(&s)(*(&s) += span_token(*(&s)));
383 tlen = s - p;
384 if (!tlen) /* invalid parameter name */
385 goto error;
386
387 if (IS_LWS(*s)((*s) == ' ' || (*s) == '\t' || (*s) == '\r' || (*s) == '\n')) { *s++ = '\0'; skip_lws(&s)(*(&s) += span_lws(*(&s))); }
388
389 if (*s == '=') {
390 char *v;
391 s++;
392 skip_lws(&s)(*(&s) += span_lws(*(&s)));
393
394 /* get value */
395 if (*s == '"') {
396 size_t qlen = span_quoted(s);
397 if (!qlen)
398 goto error;
399 v = s; s += qlen;
400 }
401 else {
402 v = s;
403 skip_param(&s)(*(&s) += span_param(*(&s)));
404 if (s == v)
405 goto error;
406 }
407 if (p + tlen + 1 != v) {
408 p = memmove(v - tlen - 1, p, tlen);
409 p[tlen] = '=';
410 }
411
412 }
413
414 if (IS_LWS(*s)((*s) == ' ' || (*s) == '\t' || (*s) == '\r' || (*s) == '\n')) { *s++ = '\0'; skip_lws(&s)(*(&s) += span_lws(*(&s))); }
415
416 if (n == N) {
417 /* Reallocate params */
418 char const **nparams = su_realloc(home, (void*)(params != stack ? params : NULL((void*)0)),
419 (N = MSG_PARAMS_NUM(N + 1)(((N + 1) + msg_n_params - 1) & (size_t)(0 - msg_n_params
))
) * sizeof(*params));
420 if (!nparams) {
421 goto error;
422 }
423 if (params == stack)
424 memcpy(nparams, stack, n * sizeof(*params));
425 params = nparams;
426 }
427
428 params[n++] = p;
429
430 if (*s != ';')
431 break;
432
433 *s++ = '\0';
434 }
435
436 *ss = s;
437
438 if (params == stack) {
439 size_t size = sizeof(*params) * MSG_PARAMS_NUM(n + 1)(((n + 1) + msg_n_params - 1) & (size_t)(0 - msg_n_params
))
;
440 params = su_alloc(home, size);
441 if (!params) return -1;
442 memcpy((void *)params, stack, n * sizeof(*params));
443 }
444 else if (n == N) {
445 /* Reallocate params */
446 char const **nparams = su_realloc(home, (void*)(params != stack ? params : NULL((void*)0)),
447 (N = MSG_PARAMS_NUM(N + 1)(((N + 1) + msg_n_params - 1) & (size_t)(0 - msg_n_params
))
) * sizeof(*params));
Although the value stored to 'N' is used in the enclosing expression, the value is never actually read from 'N'
448 if (!nparams) {
449 goto error;
450 }
451 if (params == stack)
452 memcpy(nparams, stack, n * sizeof(*params));
453 params = nparams;
454 }
455
456 params[n] = NULL((void*)0);
457
458 *append_list = params;
459
460 return 0;
461
462 error:
463 if (params != stack)
464 su_free(home, params);
465 return -1;
466}
467
468/**Parse a semicolon-separated parameter list starting with semicolon.
469 *
470 * Parse a parameter list, which has syntax as follows:
471 * @code
472 * *(";" token [ "=" (token | quoted-string)]).
473 * @endcode
474 *
475 * @param[in] home pointer to a memory home
476 * @param[in,out] ss pointer to string at the start of parameter list
477 * @param[in,out] append_list pointer to list
478 * where parsed list items are appended
479 *
480 * @retval >= 0 if successful
481 * @retval -1 upon an error
482 *
483 * @sa msg_avlist_d()
484 */
485issize_t msg_params_d(su_home_t *home,
486 char **ss,
487 msg_param_t const **append_list)
488{
489 if (**ss == ';') {
490 *(*ss)++ = '\0';
491 *append_list = NULL((void*)0);
492 return msg_avlist_d(home, ss, append_list);
493 }
494
495 if (IS_LWS(**ss)((**ss) == ' ' || (**ss) == '\t' || (**ss) == '\r' || (**ss) ==
'\n')
) {
496 *(*ss)++ = '\0'; skip_lws(ss)(*(ss) += span_lws(*(ss)));
497 }
498
499 return 0;
500}
501
502/** Encode a list of parameters */
503isize_t msg_params_e(char b[], isize_t bsiz, msg_param_t const pparams[])
504{
505 int i;
506 char *end = b + bsiz, *b0 = b;
507 msg_param_t p;
508
509 if (pparams)
510 for (i = 0; (p = pparams[i]); i++) {
511 if (p[0]) {
512 MSG_CHAR_E(b, end, ';')(++(b) < (end) ? ((b)[-1]=(';')) : (';'));
513 MSG_STRING_E(b, end, p)do { size_t _n = strlen(p); if (b + _n+1 < end) memcpy(b, p
, _n+1); b+= _n; } while(0)
;
514 }
515 }
516
517 return b - b0;
518}
519
520/** Duplicate a parameter list */
521char *msg_params_dup(msg_param_t const **d, msg_param_t const s[],
522 char *b, isize_t xtra)
523{
524 char *end = b + xtra;
525 char **pp;
526 int i;
527 isize_t n;
528
529 n = msg_params_count(s);
530
531 if (n == 0) {
532 *d = NULL((void*)0);
533 return b;
534 }
535
536 MSG_STRUCT_ALIGN(b)((b) = (void*)(((uintptr_t)(b) + (sizeof(void *)) - 1) & (
0 - (uintptr_t)(sizeof(void *)))))
;
537 pp = (char **)b;
538
539 b += sizeof(*pp) * MSG_PARAMS_NUM(n + 1)(((n + 1) + msg_n_params - 1) & (size_t)(0 - msg_n_params
))
;
540
541 for (i = 0; s[i]; i++) {
542 MSG_STRING_DUP(b, pp[i], s[i])(void)((s[i])?((b)=(char*)memccpy((void *)((pp[i])=(char*)b),
(s[i]),0,2147483647)) :((pp[i])=((void*)0)))
;
543 }
544 pp[i] = NULL((void*)0);
545
546 assert(b <= end)((void) sizeof ((b <= end) ? 1 : 0), __extension__ ({ if (
b <= end) ; else __assert_fail ("b <= end", "msg_parser_util.c"
, 546, __extension__ __PRETTY_FUNCTION__); }))
; (void)end;
547
548 *d = (msg_param_t const *)pp;
549
550 return b;
551}
552
553
554/** Parse a comma-separated list.
555 *
556 * Parses a comma-separated list. The parsed string is passed in @a *ss,
557 * which is updated to point to the first non-linear-whitespace character
558 * after the list. The function modifies the string as it parses it.
559 *
560 * A pointer to the resulting list is returned in @a *retval. If there
561 * already is a list in @a *retval, new items are appended. Empty list items
562 * are ignored, and are not included in the list.
563 *
564 * The function can be passed an optional scanning function. The scanning
565 * function scans for a legitimate list item, for example, a token. It also
566 * compacts the list item, for instance, if the item consists of @c
567 * name=value parameter definitions. The scanning function returns the
568 * length of the scanned item, including any linear whitespace after it.
569 *
570 * By default, the scanning function accepts tokens, quoted strings or
571 * separators (except comma, of course).
572 *
573 * @param[in] home memory home for allocating list pointers
574 * @param[in,out] ss pointer to pointer to string to be parsed
575 * @param[in,out] append_list pointer to list
576 * where parsed list items are appended
577 * @param[in] scanner pointer to function scanning a single item
578 * (optional)
579 *
580 * @retval 0 if successful.
581 * @retval -1 upon an error.
582 */
583issize_t msg_commalist_d(su_home_t *home,
584 char **ss,
585 msg_param_t **append_list,
586 issize_t (*scanner)(char *s))
587{
588 scanner = scanner ? scanner : msg_comma_scanner;
589 return msg_any_list_d(home, ss, append_list, scanner, ',');
590}
591
592/** Token scanner for msg_commalist_d() accepting also empty entries. */
593issize_t msg_token_scan(char *start)
594{
595 char *s = start;
596 skip_token(&s)(*(&s) += span_token(*(&s)));
597
598 if (IS_LWS(*s)((*s) == ' ' || (*s) == '\t' || (*s) == '\r' || (*s) == '\n'))
599 *s++ = '\0';
600 skip_lws(&s)(*(&s) += span_lws(*(&s)));
601
602 return s - start;
603}
604
605/** Scan and compact a comma-separated item */
606static
607issize_t msg_comma_scanner(char *start)
608{
609 size_t tlen;
610 char *s, *p;
611
612 s = p = start;
613
614 if (s[0] == ',')
615 return 0;
616
617 for (;;) {
618 /* Grab next section - token, quoted string, or separator character */
619 char c = *s;
620
621 if (IS_TOKEN(c)((_bnf_table[(unsigned char)c] & bnf_token)))
622 tlen = span_token(s);
623 else if (c == '"')
624 tlen = span_quoted(s);
625 else /* if (IS_SEPARATOR(c)) */
626 tlen = 1;
627
628 if (tlen == 0)
629 return -1;
630
631 if (p != s)
632 memmove(p, s, tlen); /* Move section to end of paramexter */
633 p += tlen; s += tlen;
634
635 skip_lws(&s)(*(&s) += span_lws(*(&s))); /* Skip possible LWS */
636
637 if (*s == '\0' || *s == ',') { /* Test for possible end */
638 if (p != s) *p = '\0';
639 return s - start;
640 }
641
642 if (IS_TOKEN(c)((_bnf_table[(unsigned char)c] & bnf_token)) && IS_TOKEN(*s)((_bnf_table[(unsigned char)*s] & bnf_token)))
643 *p++ = ' '; /* Two tokens must be separated by LWS */
644 }
645}
646
647/** Parse a comment.
648 *
649 * Parses a multilevel comment. The comment assigned to return-value
650 * parameter @a return_comment is NUL-terminated. The string at return-value
651 * parameter @a ss is updated to point to first non-linear-whitespace
652 * character after the comment.
653 */
654issize_t msg_comment_d(char **ss, char const **return_comment)
655{
656 /* skip comment */
657 int level = 1;
658 char *s = *ss;
659
660 assert(s[0] == '(')((void) sizeof ((s[0] == '(') ? 1 : 0), __extension__ ({ if (
s[0] == '(') ; else __assert_fail ("s[0] == '('", "msg_parser_util.c"
, 660, __extension__ __PRETTY_FUNCTION__); }))
;
661
662 if (*s != '(')
663 return -1;
664
665 *s++ = '\0';
666
667 if (return_comment)
668 *return_comment = s;
669
670 while (level) switch (*s++) {
671 case '(': level++; break;
672 case ')': level--; break;
673 case '\0': /* ERROR */ return -1;
674 }
675
676 assert(s[-1] == ')')((void) sizeof ((s[-1] == ')') ? 1 : 0), __extension__ ({ if (
s[-1] == ')') ; else __assert_fail ("s[-1] == ')'", "msg_parser_util.c"
, 676, __extension__ __PRETTY_FUNCTION__); }))
;
677
678 s[-1] = '\0';
679 skip_lws(&s)(*(&s) += span_lws(*(&s)));
680 *ss = s;
681
682 return 0;
683}
684
685/** Parse a quoted string */
686issize_t msg_quoted_d(char **ss, char **return_quoted)
687{
688 char *s= *ss, *s0 = s;
689 ssize_t n = span_quoted(s);
690
691 if (n <= 0)
692 return -1;
693
694 *return_quoted = s;
695 s += n;
696 if (IS_LWS(*s)((*s) == ' ' || (*s) == '\t' || (*s) == '\r' || (*s) == '\n')) {
697 *s++ = '\0';
698 skip_lws(&s)(*(&s) += span_lws(*(&s))); /* skip linear whitespace */
699 }
700
701 *ss = s;
702
703 return s - s0;
704}
705
706#if 0
707/** Calculate length of string when quoted. */
708int msg_quoted_len(char const *u)
709{
710 int rv;
711
712 if (!u)
713 return 0;
714
715 rv = span_token_lws(u);
716 if (u[rv]) {
717 /* We need to quote string */
718 int n;
719 int extra = 2; /* quote chars */
720
721 /* Find all characters to quote */
722 for (n = strcspn(u + rv, "\\\""); u[rv + n]; rv += n)
723 extra++;
724
725 rv += extra;
726 }
727
728 return rv;
729}
730#endif
731
732/**Parse @e host[":"port] pair.
733 *
734 * Parses a @e host[":"port] pair. The caller passes function a pointer to a
735 * string via @a ss, and pointers to which parsed host and port are assigned
736 * via @a hhost and @a pport, respectively. The value-result parameter @a
737 * *pport must be initialized to default value (e.g., NULL).
738 *
739 * @param ss pointer to pointer to string to be parsed
740 * @param return_host value-result parameter for @e host
741 * @param return_port value-result parameter for @e port
742
743 * @return
744 * Returns zero when successful, and a negative value upon an error. The
745 * parsed values for host and port are assigned via @a return_host and @a
746 * return_port, respectively. The function also updates the pointer @a *ss,
747 * so if call is successful, the @a *ss points to first
748 * non-linear-whitespace character after @e host[":"port] pair.
749 *
750 * @note
751 * If there is no whitespace after @e port, the value in @a *pport may not be
752 * NUL-terminated. The calling function @b must NUL terminate the value by
753 * setting the @a **ss to NUL after first examining its value.
754 */
755int msg_hostport_d(char **ss,
756 char const **return_host,
757 char const **return_port)
758{
759 char *host, *s = *ss;
760 char *port = NULL((void*)0);
761
762 /* Host name */
763 host = s;
764 if (s[0] != '[') {
765 skip_token(&s)(*(&s) += span_token(*(&s))); if (host == s) return -1;
766 }
767 else {
768 /* IPv6 */
769 size_t n = strspn(++s, HEX"0123456789" "ABCDEF" "abcdef" ":.");
770 if (s[n] != ']') return -1;
771 s += n + 1;
772 }
773
774 if (IS_LWS(*s)((*s) == ' ' || (*s) == '\t' || (*s) == '\r' || (*s) == '\n')) { *s++ = '\0'; skip_lws(&s)(*(&s) += span_lws(*(&s))); }
775
776 if (s[0] == ':') {
777 unsigned long nport;
778 *s++ = '\0'; skip_lws(&s)(*(&s) += span_lws(*(&s)));
779 if (!IS_DIGIT(*s)((*s) >= '0' && (*s) <= '9'))
780 return -1;
781 port = s;
782 nport = strtoul(s, &s, 10);
783 if (nport > 65535)
784 return -1;
785 if (IS_LWS(*s)((*s) == ' ' || (*s) == '\t' || (*s) == '\r' || (*s) == '\n')) {
786 *s++ = '\0';
787 skip_lws(&s)(*(&s) += span_lws(*(&s)));
788 }
789 }
790
791 *return_host = host;
792 *return_port = port;
793
794 *ss = s;
795
796 return 0;
797}
798
799/** Find a header parameter.
800 *
801 * Searches for given parameter @a name from the header. If parameter is
802 * found, it returns a non-NULL pointer to the parameter value. If there is
803 * no value for the name (in form "name" or "name=value"), the returned pointer
804 * points to a NUL character.
805 *
806 * @param h pointer to header structure
807 * @param name parameter name (with or without "=" sign)
808 *
809 * @return
810 * A pointer to parameter value, or NULL if parameter was not found.
811 */
812char const *msg_header_find_param(msg_common_t const *h, char const *name)
813{
814 if (h && h->h_class->hc_params) {
815 msg_param_t const **params = (msg_param_t const **)
816 ((char *)h + h->h_class->hc_params);
817 return msg_params_find(*params, name);
818 }
819
820 return NULL((void*)0);
821}
822
823/**Modify a parameter value or list item in a header.
824 *
825 * A header parameter @a param can be just a C-string (@a is_item > 0), or
826 * it can have internal format <i>name [ "=" value]</i>. In the latter case,
827 * the value part following = is ignored when replacing or removing the
828 * parameter.
829 *
830 * @param home memory home used to re-allocate parameter list in header
831 * @param h pointer to a header
832 * @param param parameter to be replaced or added
833 * @param is_item how to interpret @a param:
834 * - 1 case-sensitive, no structure
835 * - 0 case-insensitive, <i>name [ "=" value ]</i>
836 * @param remove_replace_add what operation to do:
837 * - -1 remove
838 * - 0 replace
839 * - 1 add
840 *
841 * @retval 1 if parameter was replaced or removed successfully
842 * @retval 0 if parameter was added successfully,
843 * or there was nothing to remove
844 * @retval -1 upon an error
845 */
846static
847int msg_header_param_modify(su_home_t *home, msg_common_t *h,
848 char const *param,
849 int is_item,
850 int remove_replace_add)
851{
852 msg_param_t *params, **pointer_to_params;
853 size_t plen, n;
854
855 if (!h || !h->h_class->hc_params || !param)
856 return -1;
857
858 pointer_to_params = (msg_param_t **)((char *)h + h->h_class->hc_params);
859 params = *pointer_to_params;
860
861 plen = is_item > 0 ? strlen(param) : strcspn(param, "=");
862 n = 0;
863
864 if (params) {
865 /* Existing list, try to replace or remove */
866 for (; params[n]; n++) {
867 char const *maybe = params[n];
868
869 if (remove_replace_add > 0)
870 continue;
871
872 if (is_item > 0) {
873 if (strcmp(maybe, param) == 0) {
874 if (remove_replace_add == 0)
875 return 1;
876 }
877 }
878 else {
879 if (su_casenmatch(maybe, param, plen) &&
880 (maybe[plen] == '=' || maybe[plen] == 0))
881 break;
882 }
883 }
884 }
885
886 /* Not found? */
887 if (!params || !params[n]) {
888 if (remove_replace_add < 0)
889 return 0; /* Nothing to remove */
890 else
891 remove_replace_add = 1; /* Add instead of replace */
892 }
893
894 if (remove_replace_add < 0) { /* Remove */
895 for (; params[n]; n++)
896 params[n] = params[n + 1];
897 }
898 else {
899 if (remove_replace_add > 0) { /* Add */
900 size_t m_before = MSG_PARAMS_NUM(n + 1)(((n + 1) + msg_n_params - 1) & (size_t)(0 - msg_n_params
))
;
901 size_t m_after = MSG_PARAMS_NUM(n + 2)(((n + 2) + msg_n_params - 1) & (size_t)(0 - msg_n_params
))
;
902
903 assert(!params || !params[n])((void) sizeof ((!params || !params[n]) ? 1 : 0), __extension__
({ if (!params || !params[n]) ; else __assert_fail ("!params || !params[n]"
, "msg_parser_util.c", 903, __extension__ __PRETTY_FUNCTION__
); }))
;
904
905 if (m_before != m_after || !params) {
906 msg_param_t *p;
907 /* XXX - we should know when to do realloc */
908 p = su_alloc(home, m_after * sizeof(*p));
909 if (!p) return -1;
910 if (n > 0)
911 memcpy(p, params, n * sizeof(p[0]));
912 *pointer_to_params = params = p;
913 }
914 params[n + 1] = NULL((void*)0);
915 }
916
917 params[n] = param; /* Add .. or replace */
918 }
919
920 msg_fragment_clear(h);
921
922 if (h->h_class->hc_update) {
923 /* Update shortcuts */
924 size_t namelen;
925 char const *name, *value;
926
927 name = param;
928 namelen = strcspn(name, "=");
929
930 if (remove_replace_add < 0)
931 value = NULL((void*)0);
932 else
933 value = param + namelen + (name[namelen] == '=');
934
935 h->h_class->hc_update(h, name, namelen, value);
936 }
937
938 return remove_replace_add <= 0; /* 0 when added, 1 otherwise */
939}
940
941/** Add a parameter to a header.
942 *
943 * You should use this function only when the header accepts multiple
944 * parameters (or list items) with the same name. If that is not the case,
945 * you should use msg_header_replace_param().
946 *
947 * @note This function @b does @b not duplicate @p param. The caller should
948 * have allocated the @a param from the memory home associated with header
949 * @a h.
950 *
951 * The possible shortcuts to parameter values are updated. For example, the
952 * "received" parameter in @Via header has shortcut in structure #sip_via_t,
953 * the @ref sip_via_s::v_received "v_received" field. The shortcut is usully
954 * a pointer to the parameter value. If the parameter was
955 * "received=127.0.0.1" the @ref sip_via_s::v_received "v_received" field
956 * would be a pointer to "127.0.0.1". If the parameter was "received=" or
957 * "received", the shortcut would be a pointer to an empty string, "".
958 *
959 * @param home memory home used to re-allocate parameter list in header
960 * @param h pointer to a header
961 * @param param parameter to be replaced or added
962 *
963 * @retval 0 if parameter was added
964 * @retval -1 upon an error
965 *
966 * @sa msg_header_replace_param(), msg_header_remove_param(),
967 * msg_header_update_params(),
968 * #msg_common_t, #msg_header_t,
969 * #msg_hclass_t, msg_hclass_t::hc_params, msg_hclass_t::hc_update
970 */
971int msg_header_add_param(su_home_t *home, msg_common_t *h, char const *param)
972{
973 return msg_header_param_modify(home, h, param,
974 0 /* case-insensitive name=value */,
975 1 /* add */);
976}
977
978
979
980/** Replace or add a parameter to a header.
981 *
982 * A header parameter @a param is a string of format <i>name "=" value</i>
983 * or just name. The value part following "=" is ignored when selecting a
984 * parameter to replace.
985 *
986 * @note This function @b does @b not duplicate @p param. The caller should
987 * have allocated the @a param from the memory home associated with header
988 * @a h.
989 *
990 * The possible shortcuts to parameter values are updated. For example, the
991 * "received" parameter in @Via header has shortcut in structure #sip_via_t,
992 * the @ref sip_via_s::v_received "v_received" field.
993 *
994 * @param home memory home used to re-allocate parameter list in header
995 * @param h pointer to a header
996 * @param param parameter to be replaced or added
997 *
998 * @retval 0 if parameter was added
999 * @retval 1 if parameter was replaced
1000 * @retval -1 upon an error
1001 *
1002 * @sa msg_header_add_param(), msg_header_remove_param(),
1003 * msg_header_update_params(),
1004 * #msg_common_t, #msg_header_t,
1005 * #msg_hclass_t, msg_hclass_t::hc_params, msg_hclass_t::hc_update
1006 */
1007int msg_header_replace_param(su_home_t *home,
1008 msg_common_t *h,
1009 char const *param)
1010{
1011 return msg_header_param_modify(home, h, param,
1012 0 /* case-insensitive name=value */,
1013 0 /* replace */);
1014}
1015
1016/** Remove a parameter from header.
1017 *
1018 * The parameter name is given as token optionally followed by "=" sign and
1019 * value. The "=" and value after it are ignored when selecting a parameter
1020 * to remove.
1021 *
1022 * The possible shortcuts to parameter values are updated. For example, the
1023 * "received" parameter in @Via header has shortcut in structure #sip_via_t,
1024 * the @ref sip_via_s::v_received "v_received" field. The shortcut to
1025 * removed parameter would be set to NULL.
1026 *
1027 * @param h pointer to a header
1028 * @param name name of parameter to be removed
1029 *
1030 * @retval 1 if a parameter was removed
1031 * @retval 0 if no parameter was not removed
1032 * @retval -1 upon an error
1033 *
1034 * @sa msg_header_add_param(), msg_header_replace_param(),
1035 * msg_header_update_params(),
1036 * #msg_common_t, #msg_header_t,
1037 * #msg_hclass_t, msg_hclass_t::hc_params, msg_hclass_t::hc_update
1038 */
1039int msg_header_remove_param(msg_common_t *h, char const *name)
1040{
1041 return msg_header_param_modify(NULL((void*)0), h, name,
1042 0 /* case-insensitive name=value */,
1043 -1 /* remove */);
1044}
1045
1046/** Update shortcuts to parameter values.
1047 *
1048 * Update the shortcuts to parameter values in parameter list. For example,
1049 * the "received" parameter in @Via header has shortcut in structure
1050 * #sip_via_t, the @ref sip_via_s::v_received "v_received" field. The
1051 * shortcut is usully a pointer to the parameter value. If the parameter was
1052 * "received=127.0.0.1" the @ref sip_via_s::v_received "v_received" field
1053 * would be a pointer to "127.0.0.1". If the parameter was "received=" or
1054 * "received", the shortcut would be a pointer to an empty string, "".
1055 *
1056 * @retval 0 when successful
1057 * @retval -1 upon an error
1058 *
1059 * @sa msg_header_add_param(), msg_header_replace_param(),
1060 * msg_header_update_params(),
1061 * #msg_common_t, #msg_header_t,
1062 * #msg_hclass_t, msg_hclass_t::hc_params, msg_hclass_t::hc_update
1063 */
1064int msg_header_update_params(msg_common_t *h, int clear)
1065{
1066 msg_hclass_t *hc;
1067 unsigned char offset;
1068 msg_update_f *update;
1069 int retval;
1070 msg_param_t const *params;
1071 size_t n;
1072 char const *p, *v;
1073
1074 if (h == NULL((void*)0))
1075 return errno(*__errno_location ()) = EFAULT14, -1;
1076
1077 hc = h->h_class; offset = hc->hc_params; update = hc->hc_update;
1078
1079 if (offset == 0 || update == NULL((void*)0))
1080 return 0;
1081
1082 if (clear)
1083 update(h, NULL((void*)0), 0, NULL((void*)0));
1084
1085 params = *(msg_param_t **)((char *)h + offset);
1086 if (params == NULL((void*)0))
1087 return 0;
1088
1089 retval = 0;
1090
1091 for (p = *params; p; p = *++params) {
1092 n = strcspn(p, "=");
1093 v = p + n + (p[n] == '=');
1094 if (update(h, p, n, v) < 0)
1095 retval = -1;
1096 }
1097
1098 return retval;
1099}
1100
1101
1102/** Find a header item.
1103 *
1104 * Searches for given item @a name from the header. If item is found, the
1105 * function returns a non-NULL pointer to the item.
1106 *
1107 * @param h pointer to header structure
1108 * @param item item
1109 *
1110 * @return
1111 * A pointer to item, or NULL if it was not found.
1112 *
1113 * @since New in @VERSION_1_12_4
1114 *
1115 * @sa msg_header_replace_item(), msg_header_remove_item(),
1116 * @Allow, @AllowEvents
1117 */
1118char const *msg_header_find_item(msg_common_t const *h, char const *item)
1119{
1120 if (h && h->h_class->hc_params) {
1121 char const * const * items =
1122 *(char const * const * const *)
1123 ((char *)h + h->h_class->hc_params);
1124
1125 if (items)
1126 for (; *items; items++) {
1127 if (strcmp(item, *items) == 0) {
1128 return *items;
1129 }
1130 }
1131 }
1132
1133 return NULL((void*)0);
1134}
1135
1136
1137/**Add an item to a header.
1138 *
1139 * This function treats a #msg_header_t as set of C strings. The @a item is
1140 * a C string. If no identical string is found from the list, it is added to
1141 * the list.
1142 *
1143 * The shortcuts, if any, to item values are updated accordingly.
1144 *
1145 * @param home memory home used to re-allocate list in header
1146 * @param h pointer to a header
1147 * @param item item to be removed
1148 *
1149 * @retval 0 if item was added
1150 * @retval 1 if item was replaced
1151 * @retval -1 upon an error
1152 *
1153 * @since New in @VERSION_1_12_4.
1154 *
1155 * @sa msg_header_remove_item(), @Allow, @AllowEvents,
1156 * msg_header_replace_param(), msg_header_remove_param(),
1157 * #msg_common_t, #msg_header_t, #msg_list_t
1158 * #msg_hclass_t, msg_hclass_t::hc_params
1159 */
1160int msg_header_replace_item(su_home_t *home,
1161 msg_common_t *h,
1162 char const *item)
1163{
1164 return msg_header_param_modify(home, h, item,
1165 1 /* string item */,
1166 0 /* replace */);
1167}
1168
1169/**Remove an item from a header.
1170 *
1171 * This function treats a #msg_header_t as set of C strings. The @a item is a
1172 * C string. If identical string is found from the list, it is removed.
1173 *
1174 * The shortcuts, if any, to item values are updated accordingly.
1175 *
1176 * @param h pointer to a header
1177 * @param name item to be removed
1178 *
1179 * @retval 0 if item was added
1180 * @retval 1 if item was replaced
1181 * @retval -1 upon an error
1182 *
1183 * @since New in @VERSION_1_12_4.
1184 *
1185 * @sa msg_header_replace_item(), @Allow, @AllowEvents,
1186 * msg_header_replace_param(), msg_header_remove_param(),
1187 * #msg_common_t, #msg_header_t, #msg_list_t
1188 * #msg_hclass_t, msg_hclass_t::hc_params
1189 */
1190int msg_header_remove_item(msg_common_t *h, char const *name)
1191{
1192 return msg_header_param_modify(NULL((void*)0), h, name,
1193 1 /* item */,
1194 -1 /* remove */);
1195}
1196
1197
1198/** Find a parameter from a parameter list.
1199 *
1200 * Searches for given parameter @a token from the parameter list. If
1201 * parameter is found, it returns a non-NULL pointer to the parameter value.
1202 * If there is no value for the parameter (the parameter is of form "name"
1203 * or "name="), the returned pointer points to a NUL character.
1204 *
1205 * @param params list (or vector) of parameters
1206 * @param token parameter name (with or without "=" sign)
1207 *
1208 * @return
1209 * A pointer to parameter value, or NULL if parameter was not found.
1210 */
1211msg_param_t msg_params_find(msg_param_t const params[], msg_param_t token)
1212{
1213 if (params && token) {
1214 size_t i, n = strcspn(token, "=");
1215
1216 assert(n > 0)((void) sizeof ((n > 0) ? 1 : 0), __extension__ ({ if (n >
0) ; else __assert_fail ("n > 0", "msg_parser_util.c", 1216
, __extension__ __PRETTY_FUNCTION__); }))
;
1217
1218 for (i = 0; params[i]; i++) {
1219 msg_param_t param = params[i];
1220 if (su_casenmatch(param, token, n)) {
1221 if (param[n] == '=')
1222 return param + n + 1;
1223 else if (param[n] == 0)
1224 return param + n;
1225 }
1226 }
1227 }
1228
1229 return NULL((void*)0);
1230}
1231
1232/** Find a slot for parameter from a parameter list.
1233 *
1234 * Searches for given parameter @a token from the parameter list. If
1235 * parameter is found, it returns a non-NULL pointer to the item containing
1236 * the parameter.
1237 *
1238 * @param params list (or vector) of parameters
1239 * @param token parameter name (with or without "=" sign)
1240 *
1241 * @return
1242 * A pointer to parameter slot, or NULL if parameter was not found.
1243 */
1244msg_param_t *msg_params_find_slot(msg_param_t params[], msg_param_t token)
1245{
1246 if (params && token) {
1247 int i;
1248 size_t n = strlen(token);
1249
1250 assert(n > 0)((void) sizeof ((n > 0) ? 1 : 0), __extension__ ({ if (n >
0) ; else __assert_fail ("n > 0", "msg_parser_util.c", 1250
, __extension__ __PRETTY_FUNCTION__); }))
;
1251
1252 for (i = 0; params[i]; i++) {
1253 msg_param_t param = params[i];
1254 if (su_casenmatch(param, token, n)) {
1255 if (param[n] == '=')
1256 return params + i;
1257 else if (param[n] == 0 || token[n - 1] == '=')
1258 return params + i;
1259 }
1260 }
1261
1262 }
1263
1264 return NULL((void*)0);
1265}
1266
1267/** Replace or add a parameter from a list.
1268 *
1269 * A non-NULL parameter list must have been created by msg_params_d()
1270 * or by msg_params_dup().
1271 *
1272 * @note This function does not duplicate @p param.
1273 *
1274 * @param home memory home
1275 * @param inout_params pointer to pointer to parameter list
1276 * @param param parameter to be replaced or added
1277 *
1278 * @retval 0 if parameter was added
1279 * @retval 1 if parameter was replaced
1280 * @retval -1 upon an error
1281 */
1282int msg_params_replace(su_home_t *home,
1283 msg_param_t **inout_params,
1284 msg_param_t param)
1285{
1286 msg_param_t *params;
1287 size_t i, n;
1288
1289 assert(inout_params)((void) sizeof ((inout_params) ? 1 : 0), __extension__ ({ if (
inout_params) ; else __assert_fail ("inout_params", "msg_parser_util.c"
, 1289, __extension__ __PRETTY_FUNCTION__); }))
;
1290
1291 if (param == NULL((void*)0) || param[0] == '=' || param[0] == '\0')
1292 return -1;
1293
1294 params = *inout_params;
1295
1296 n = strcspn(param, "=");
1297
1298 if (params) {
1299 /* Existing list, try to replace or remove */
1300 for (i = 0; params[i]; i++) {
1301 msg_param_t maybe = params[i];
1302
1303 if (su_casenmatch(maybe, param, n)) {
1304 if (maybe[n] == '=' || maybe[n] == 0) {
1305 params[i] = param;
1306 return 1;
1307 }
1308 }
1309 }
1310 }
1311
1312 /* Not found on list */
1313 return msg_params_add(home, inout_params, param);
1314}
1315
1316/** Remove a parameter from a list.
1317 *
1318 * @retval 1 if parameter was removed
1319 * @retval 0 if parameter was not found
1320 * @retval -1 upon an error
1321 */
1322int msg_params_remove(msg_param_t *params, msg_param_t param)
1323{
1324 size_t i, n;
1325
1326 if (!params || !param || !param[0])
1327 return -1;
1328
1329 n = strcspn(param, "=");
1330 assert(n > 0)((void) sizeof ((n > 0) ? 1 : 0), __extension__ ({ if (n >
0) ; else __assert_fail ("n > 0", "msg_parser_util.c", 1330
, __extension__ __PRETTY_FUNCTION__); }))
;
1331
1332 for (i = 0; params[i]; i++) {
1333 msg_param_t maybe = params[i];
1334
1335 if (su_casenmatch(maybe, param, n)) {
1336 if (maybe[n] == '=' || maybe[n] == 0) {
1337 /* Remove */
1338 do {
1339 params[i] = params[i + 1];
1340 } while (params[i++]);
1341 return 1;
1342 }
1343 }
1344 }
1345
1346 return 0;
1347}
1348
1349/** Calculate number of parameters in a parameter list */
1350size_t msg_params_length(char const * const * params)
1351{
1352 size_t len;
1353
1354 if (!params)
1355 return 0;
1356
1357 for (len = 0; params[len]; len++)
1358 ;
1359
1360 return len;
1361}
1362
1363
1364/**
1365 * Add a parameter to a list.
1366 *
1367 * Add a parameter to the list; the list must have been created by @c
1368 * msg_params_d() or by @c msg_params_dup() (or it may contain only @c
1369 * NULL).
1370 *
1371 * @note This function does not duplicate @p param.
1372 *
1373 * @param home memory home
1374 * @param inout_params pointer to pointer to parameter list
1375 * @param param parameter to be added
1376 *
1377 * @retval 0 if parameter was added
1378 * @retval -1 upon an error
1379 */
1380int msg_params_add(su_home_t *home,
1381 msg_param_t **inout_params,
1382 msg_param_t param)
1383{
1384 size_t n, m_before, m_after;
1385 msg_param_t *p = *inout_params;
1386
1387 if (param == NULL((void*)0))
1388 return -1;
1389
1390 /* Count number of parameters */
1391 for (n = 0; p && p[n]; n++)
1392 ;
1393
1394 m_before = MSG_PARAMS_NUM(n + 1)(((n + 1) + msg_n_params - 1) & (size_t)(0 - msg_n_params
))
;
1395 m_after = MSG_PARAMS_NUM(n + 2)(((n + 2) + msg_n_params - 1) & (size_t)(0 - msg_n_params
))
;
1396
1397 if (m_before != m_after || !p) {
1398 p = su_alloc(home, m_after * sizeof(*p));
1399 assert(p)((void) sizeof ((p) ? 1 : 0), __extension__ ({ if (p) ; else __assert_fail
("p", "msg_parser_util.c", 1399, __extension__ __PRETTY_FUNCTION__
); }))
; if (!p) return -1;
1400 if (n)
1401 memcpy(p, *inout_params, n * sizeof(*p));
1402 *inout_params = p;
1403 }
1404
1405 p[n] = param;
1406 p[n + 1] = NULL((void*)0);
1407
1408 return 0;
1409}
1410
1411static
1412int msg_param_prune(msg_param_t const d[], msg_param_t p, unsigned prune)
1413{
1414 size_t i, nlen;
1415
1416 if (prune == 1)
1417 nlen = strcspn(p, "=");
1418 else
1419 nlen = 0;
1420
1421 for (i = 0; d[i]; i++) {
1422 if ((prune == 1 &&
1423 su_casenmatch(p, d[i], nlen)
1424 && (d[i][nlen] == '=' || d[i][nlen] == '\0'))
1425 ||
1426 (prune == 2 && su_casematch(p, d[i]))
1427 ||
1428 (prune == 3 && strcmp(p, d[i]) == 0))
1429 return 1;
1430 }
1431
1432 return 0;
1433}
1434
1435/**Join two parameter lists.
1436 *
1437 * The function @c msg_params_join() joins two parameter lists; the
1438 * first list must have been created by @c msg_params_d() or by @c
1439 * msg_params_dup() (or it may contain only @c NULL).
1440 *
1441 * @param home memory home
1442 * @param dst pointer to pointer to destination parameter list
1443 * @param src source list
1444 * @param prune prune duplicates
1445 * @param dup duplicate parameters in src list
1446 *
1447 * @par Pruning
1448 * <table>
1449 * <tr><td>0<td>do not prune</tr>
1450 * <tr><td>1<td>prune parameters with identical names</tr>
1451 * <tr><td>2<td>case-insensitive values</tr>
1452 * <tr><td>3<td>case-sensitive values</tr>
1453 * </table>
1454 *
1455 * @return
1456 * @retval >= 0 when successful
1457 * @retval -1 upon an error
1458 */
1459issize_t msg_params_join(su_home_t *home,
1460 msg_param_t **dst,
1461 msg_param_t const *src,
1462 unsigned prune,
1463 int dup)
1464{
1465 size_t n, m, n_before, n_after, pruned, total = 0;
1466 msg_param_t *d = *dst;
1467
1468 if (prune > 3)
1469 return -1;
1470
1471 if (src == NULL((void*)0) || *src == NULL((void*)0))
1472 return 0;
1473
1474 /* Count number of parameters */
1475 for (n = 0; d && d[n]; n++)
1476 ;
1477
1478 n_before = MSG_PARAMS_NUM(n + 1)(((n + 1) + msg_n_params - 1) & (size_t)(0 - msg_n_params
))
;
1479
1480 for (m = 0, pruned = 0; src[m]; m++) {
1481 if (n > 0 && prune > 0 && msg_param_prune(d, src[m], prune)) {
1482 pruned++;
1483 if (prune > 1)
1484 continue;
1485 }
1486 if (dup)
1487 total += strlen(src[m]) + 1;
1488 }
1489
1490 n_after = MSG_PARAMS_NUM(n + m - pruned + 1)(((n + m - pruned + 1) + msg_n_params - 1) & (size_t)(0 -
msg_n_params))
;
1491
1492 if (n_before != n_after || !d) {
1493 d = su_alloc(home, n_after * sizeof(*d));
1494 assert(d)((void) sizeof ((d) ? 1 : 0), __extension__ ({ if (d) ; else __assert_fail
("d", "msg_parser_util.c", 1494, __extension__ __PRETTY_FUNCTION__
); }))
; if (!d) return -1;
1495 if (n)
1496 memcpy(d, *dst, n * sizeof(*d));
1497 *dst = d;
1498 }
1499
1500 for (m = 0; src[m]; m++) {
1501 if (pruned && msg_param_prune(d, src[m], prune)) {
1502 pruned--;
1503 if (prune > 1)
1504 continue;
1505 }
1506
1507 if (dup)
1508 d[n++] = su_strdup(home, src[m]); /* XXX */
1509 else
1510 d[n++] = src[m];
1511 }
1512
1513 d[n] = NULL((void*)0);
1514
1515 return 0;
1516}
1517
1518/**Join header item lists.
1519 *
1520 * Join items from a source header to the destination header. The item are
1521 * compared with the existing ones. If a match is found, it is not added to
1522 * the list. If @a duplicate is true, the entries are duplicated while they
1523 * are added to the list.
1524 *
1525 * @param home memory home
1526 * @param dst destination header
1527 * @param src source header
1528 * @param duplicate if true, allocate and copy items that are added
1529 *
1530 * @return
1531 * @retval >= 0 when successful
1532 * @retval -1 upon an error
1533 *
1534 * @NEW_1_12_5.
1535 */
1536int msg_header_join_items(su_home_t *home,
1537 msg_common_t *dst,
1538 msg_common_t const *src,
1539 int duplicate)
1540{
1541 size_t N, m, M, i, n_before, n_after, total;
1542 char *dup = NULL((void*)0);
1543 msg_param_t *d, **dd, *s;
1544 msg_param_t t, *temp, temp0[16];
1545 size_t *len, len0[(sizeof temp0)/(sizeof temp0[0])];
1546 msg_update_f *update = NULL((void*)0);
1547
1548 if (dst == NULL((void*)0) || dst->h_class->hc_params == 0 ||
1549 src == NULL((void*)0) || src->h_class->hc_params == 0)
1550 return -1;
1551
1552 s = *(msg_param_t **)((char *)src + src->h_class->hc_params);
1553 if (s == NULL((void*)0))
1554 return 0;
1555
1556 for (M = 0; s[M]; M++)
1557 {}
1558
1559 if (M == 0)
1560 return 0;
1561
1562 if (M <= (sizeof temp0) / (sizeof temp0[0])) {
1563 temp = temp0, len = len0;
1564 }
1565 else {
1566 temp = malloc(M * (sizeof *temp) + M * (sizeof *len));
1567 if (!temp) return -1;
1568 len = (void *)(temp + M);
1569 }
1570
1571 dd = (msg_param_t **)((char *)dst + dst->h_class->hc_params);
1572 d = *dd;
1573
1574 for (N = 0; d && d[N]; N++)
1575 {}
1576
1577 for (m = 0, M = 0, total = 0; s[m]; m++) {
1578 t = s[m];
1579 for (i = 0; i < N; i++)
1580 if (strcmp(t, d[i]) == 0)
1581 break;
1582 if (i < N)
1583 continue;
1584
1585 for (i = 0; i < M; i++)
1586 if (strcmp(t, temp[i]) == 0)
1587 break;
1588 if (i < M)
1589 continue;
1590
1591 temp[M] = t;
1592 len[M] = strlen(t);
1593 total += len[M++] + 1;
1594 }
1595
1596 if (M == 0)
1597 goto success;
1598
1599 dup = su_alloc(home, total); if (!dup) goto error;
1600
1601 n_before = MSG_PARAMS_NUM(N + 1)(((N + 1) + msg_n_params - 1) & (size_t)(0 - msg_n_params
))
;
1602 n_after = MSG_PARAMS_NUM(N + M + 1)(((N + M + 1) + msg_n_params - 1) & (size_t)(0 - msg_n_params
))
;
1603
1604 if (d == NULL((void*)0) || n_before != n_after) {
1605 d = su_alloc(home, n_after * sizeof(*d)); if (!d) goto error;
1606 if (N)
1607 memcpy(d, *dd, N * sizeof(*d));
1608 *dd = d;
1609 }
1610
1611 update = dst->h_class->hc_update;
1612
1613 for (m = 0; m < M; m++) {
1614 d[N++] = memcpy(dup, temp[m], len[m] + 1);
1615
1616 if (update)
1617 update(dst, dup, len[m], dup + len[m]);
1618
1619 dup += len[m] + 1;
1620 }
1621
1622 d[N] = NULL((void*)0);
1623
1624 success:
1625 if (temp != temp0)
1626 free(temp);
1627 return 0;
1628
1629 error:
1630 if (temp != temp0)
1631 free(temp);
1632 su_free(home, dup);
1633 return -1;
1634}
1635
1636/**Compare parameter lists.
1637 *
1638 * Compares parameter lists.
1639 *
1640 * @param a pointer to a parameter list
1641 * @param b pointer to a parameter list
1642 *
1643 * @retval an integer less than zero if @a is less than @a b
1644 * @retval an integer zero if @a match with @a b
1645 * @retval an integer greater than zero if @a is greater than @a b
1646 */
1647int msg_params_cmp(char const * const a[], char const * const b[])
1648{
1649 int c;
1650 size_t nlen;
1651
1652 if (a == NULL((void*)0) || b == NULL((void*)0))
1653 return (a != NULL((void*)0)) - (b != NULL((void*)0));
1654
1655 for (;;) {
1656 if (*a == NULL((void*)0) || *b == NULL((void*)0))
1657 return (*a != NULL((void*)0)) - (*b != NULL((void*)0));
1658 nlen = strcspn(*a, "=");
1659 if ((c = su_strncasecmp(*a, *b, nlen)))
1660 return c;
1661 if ((c = strcmp(*a + nlen, *b + nlen)))
1662 return c;
1663 a++, b++;
1664 }
1665}
1666
1667
1668/** Unquote string
1669 *
1670 * Duplicates the string @a q in unquoted form.
1671 */
1672char *msg_unquote_dup(su_home_t *home, char const *q)
1673{
1674 char *d;
1675 size_t total, n, m;
1676
1677 /* First, easy case */
1678 if (q[0] == '"')
1679 q++;
1680 n = strcspn(q, "\"\\");
1681 if (q[n] == '\0' || q[n] == '"')
1682 return su_strndup(home, q, n);
1683
1684 /* Hairy case - backslash-escaped chars */
1685 total = n;
1686 for (;;) {
1687 if (q[n] == '\0' || q[n] == '"' || q[n + 1] == '\0')
1688 break;
1689 m = strcspn(q + n + 2, "\"\\");
1690 total += m + 1;
1691 n += m + 2;
1692 }
1693
1694 if (!(d = su_alloc(home, total + 1)))
1695 return NULL((void*)0);
1696
1697 for (n = 0;;) {
1698 m = strcspn(q, "\"\\");
1699 memcpy(d + n, q, m);
1700 n += m, q += m;
1701 if (q[0] == '\0' || q[0] == '"' || q[1] == '\0')
1702 break;
1703 d[n++] = q[1];
1704 q += 2;
1705 }
1706 assert(total == n)((void) sizeof ((total == n) ? 1 : 0), __extension__ ({ if (total
== n) ; else __assert_fail ("total == n", "msg_parser_util.c"
, 1706, __extension__ __PRETTY_FUNCTION__); }))
;
1707 d[n] = '\0';
1708
1709 return d;
1710}
1711
1712/** Unquote string */
1713char *msg_unquote(char *dst, char const *s)
1714{
1715 int copy = dst != NULL((void*)0);
1716 char *d = dst;
1717
1718 if (*s++ != '"')
1719 return NULL((void*)0);
1720
1721 for (;;) {
1722 size_t n = strcspn(s, "\"\\");
1723 if (copy)
1724 memmove(d, s, n);
1725 s += n;
1726 d += n;
1727
1728 if (*s == '\0')
1729 return NULL((void*)0);
1730 else if (*s == '"') {
1731 if (copy) *d = '\0';
1732 return dst;
1733 }
1734 else {
1735 /* Copy quoted char */
1736 if ((copy ? (*d++ = *++s) : *++s) == '\0')
1737 return NULL((void*)0);
1738 s++;
1739 }
1740 }
1741}
1742
1743/** Quote string */
1744issize_t msg_unquoted_e(char *b, isize_t bsiz, char const *s)
1745{
1746 isize_t e = 0;
1747
1748 if (b == NULL((void*)0))
1749 bsiz = 0;
1750
1751 if (0 < bsiz)
1752 *b = '"';
1753 e++;
1754
1755 for (;*s;) {
1756 size_t n = strcspn(s, "\"\\");
1757
1758 if (n == 0) {
1759 if (b && e + 2 <= bsiz)
1760 b[e] = '\\', b[e + 1] = s[0];
1761 e += 2;
1762 s++;
1763 }
1764 else {
1765 if (b && (e + n <= bsiz))
1766 memcpy(b + e, s, n);
1767 e += n;
1768 s += n;
1769 }
1770 }
1771
1772 if (b && e < bsiz)
1773 b[e] = '"';
1774 e++;
1775
1776 return e;
1777}
1778
1779
1780/** Calculate a simple hash over a string. */
1781unsigned long msg_hash_string(char const *id)
1782{
1783 unsigned long hash = 0;
1784
1785 if (id)
1786 for (; *id; id++) {
1787 hash += (unsigned)*id;
1788 hash *= 38501U;
1789 }
1790 else
1791 hash *= 38501U;
1792
1793 if (hash == 0)
1794 hash = (unsigned long)-1;
1795
1796 return hash;
1797}
1798
1799
1800/** Calculate the size of a duplicate of a header structure. */
1801isize_t msg_header_size(msg_header_t const *h)
1802{
1803 if (h == NULL((void*)0) || h == MSG_HEADER_NONE((msg_header_t *)-1))
1804 return 0;
1805 else
1806 return h->sh_classsh_common->h_class->hc_dxtra(h, h->sh_classsh_common->h_class->hc_size);
1807}
1808
1809
1810/** Encode a message to the buffer.
1811 *
1812 * The function msg_encode_e encodes a message to a given buffer.
1813 * It returns the length of the message to be encoded, even if the
1814 * buffer is too small (just like snprintf() is supposed to do).
1815 *
1816 * @param b buffer (may be NULL)
1817 * @param size size of buffer
1818 * @param mo public message structure (#sip_t, #http_t)
1819 * @param flags see #
1820 */
1821issize_t msg_object_e(char b[], isize_t size, msg_pub_t const *mo, int flags)
1822{
1823 size_t rv = 0;
1824 ssize_t n;
1825 msg_header_t const *h;
1826
1827 if (mo->msg_request)
1828 h = mo->msg_request;
1829 else
1830 h = mo->msg_status;
1831
1832 for (; h; h = h->sh_succsh_common->h_succ) {
1833 n = msg_header_e(b, size, h, flags);
1834 if (n < 0)
1835 return -1;
1836 if ((size_t)n < size)
1837 b += n, size -= n;
1838 else
1839 b = NULL((void*)0), size = 0;
1840 rv += n;
1841 }
1842
1843 return rv;
1844}
1845
1846/** Encode header contents. */
1847issize_t msg_header_field_e(char b[], isize_t bsiz, msg_header_t const *h, int flags)
1848{
1849 assert(h)((void) sizeof ((h) ? 1 : 0), __extension__ ({ if (h) ; else __assert_fail
("h", "msg_parser_util.c", 1849, __extension__ __PRETTY_FUNCTION__
); }))
; assert(h->sh_class)((void) sizeof ((h->sh_common->h_class) ? 1 : 0), __extension__
({ if (h->sh_common->h_class) ; else __assert_fail ("h->sh_class"
, "msg_parser_util.c", 1849, __extension__ __PRETTY_FUNCTION__
); }))
;
1850
1851 return h->sh_classsh_common->h_class->hc_print(b, bsiz, h, flags);
1852}
1853
1854/** Get offset of header @a h from structure @a mo. */
1855msg_header_t **
1856msg_header_offset(msg_t const *msg, msg_pub_t const *mo, msg_header_t const *h)
1857{
1858 if (h == NULL((void*)0) || h->sh_classsh_common->h_class == NULL((void*)0))
1859 return NULL((void*)0);
1860
1861 return msg_hclass_offset(msg->m_class, mo, h->sh_classsh_common->h_class);
1862}
1863
1864/**Get a header from the public message structure.
1865 *
1866 * Gets a pointer to header from a message structure.
1867 *
1868 * @param pub public message structure from which header is obtained
1869 * @param hc header class
1870 */
1871msg_header_t *
1872msg_header_access(msg_pub_t const *pub, msg_hclass_t *hc)
1873{
1874 msg_header_t * const * hh;
1875
1876 if (pub == NULL((void*)0) || hc == NULL((void*)0))
1877 return NULL((void*)0);
1878
1879 hh = msg_hclass_offset((void *)pub->msg_identmsg_common->h_class, (msg_pub_t *)pub, hc);
1880
1881 if (hh)
1882 return *hh;
1883 else
1884 return NULL((void*)0);
1885}
1886
1887#include <sofia-sip/su_uniqueid.h>
1888
1889/** Generates a random token.
1890 *
1891 */
1892issize_t msg_random_token(char token[], isize_t tlen,
1893 void const *rmemp, isize_t rsize)
1894{
1895 uint32_t random = 0, rword;
1896 uint8_t rbyte;
1897 uint8_t const *rmem = rmemp;
1898 size_t i;
1899 ssize_t n;
1900
1901 static char const token_chars[33] =
1902 /* Create aesthetically pleasing raNDom capS LooK */
1903 "aBcDeFgHjKmNpQrStUvXyZ0123456789";
1904
1905 if (rmem == NULL((void*)0) && rsize == 0)
1906 rsize = UINT_MAX(2147483647 *2U +1U);
1907
1908 if (rsize == 0) {
1909 if (token && tlen > 0)
1910 strcpy(token, "+");
1911 return 1;
1912 }
1913
1914 if (token == NULL((void*)0)) {
1915 if (rsize >= tlen * 5 / 8)
1916 return tlen;
1917 else
1918 return rsize / 5 * 8;
1919 }
1920
1921 for (i = 0, n = 0; i < tlen;) {
1922 if (n < 5) {
1923 if (rsize == 0)
1924 ;
1925 else if (rmem) {
1926 rbyte = *rmem++, rsize--;
1927 random = random | (rbyte << n);
1928 n += 8;
1929 } else {
1930 rword = su_random();
1931 random = (rword >> 13) & 31;
1932 n = 6;
1933 }
1934 }
1935
1936 token[i] = token_chars[random & 31];
1937 random >>= 5;
1938 i++, n -= 5;
1939
1940 if (n < 0 || (n == 0 && rsize == 0))
1941 break;
1942 }
1943
1944 token[i] = 0;
1945
1946 return i;
1947}
1948
1949
1950/** Parse a message.
1951 *
1952 * Parse a text message with parser @a mc. The @a data is copied and it is
1953 * not modified or referenced by the parsed message.
1954 *
1955 * @par Example
1956 * Parse a SIP message fragment (e.g., payload of NOTIFY sent in response to
1957 * REFER):
1958 * @code
1959 * msg_t *m = msg_make(sip_default_mclass(), 0, pl->pl_data, pl->pl_len);
1960 * sip_t *frag = sip_object(m);
1961 * @endcode
1962 *
1963 * @param mc message class (parser table)
1964 * @param flags message flags (see #msg_flg_user)
1965 * @param data message text
1966 * @param len size of message text (if -1, use strlen(data))
1967 *
1968 * @retval A pointer to a freshly allocated and parsed message.
1969 *
1970 * Upon parsing error, the header structure may be left incomplete. The
1971 * #MSG_FLG_ERROR is set in @a msg_object(msg)->msg_flags.
1972 *
1973 * @since New in @VERSION_1_12_4
1974 *
1975 * @sa msg_as_string(), msg_extract()
1976 */
1977msg_t *msg_make(msg_mclass_t const *mc, int flags,
1978 void const *data, ssize_t len)
1979{
1980 msg_t *msg;
1981 msg_iovec_t iovec[2];
1982
1983 if (len == -1)
1984 len = strlen(data);
1985 if (len == 0)
1986 return NULL((void*)0);
1987
1988 msg = msg_create(mc, flags);
1989 if (msg == NULL((void*)0))
1990 return NULL((void*)0);
1991
1992 su_home_preload(msg_home(msg)((su_home_t*)(msg)), 1, len + 1024);
1993
1994 if (msg_recv_iovec(msg, iovec, 2, len, 1) < 0) {
1995 perror("msg_recv_iovec");
1996 }
1997 assert((ssize_t)iovec->mv_len == len)((void) sizeof (((ssize_t)iovec->siv_len == len) ? 1 : 0),
__extension__ ({ if ((ssize_t)iovec->siv_len == len) ; else
__assert_fail ("(ssize_t)iovec->mv_len == len", "msg_parser_util.c"
, 1997, __extension__ __PRETTY_FUNCTION__); }))
;
1998 memcpy(iovec->mv_basesiv_base, data, len);
1999 msg_recv_commit(msg, len, 1);
2000
2001 if (msg_extract(msg) < 0)
2002 msg->m_object->msg_flags |= MSG_FLG_ERROR;
2003
2004 return msg;
2005}