Coverage Report

Created: 2026-02-09 06:53

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/kamailio/src/core/strutils.c
Line
Count
Source
1
/*
2
 * Copyright (C) 2001-2003 FhG Fokus
3
 *
4
 * This file is part of Kamailio, a free SIP server.
5
 *
6
 * SPDX-License-Identifier: GPL-2.0-or-later
7
 *
8
 * Kamailio is free software; you can redistribute it and/or modify
9
 * it under the terms of the GNU General Public License as published by
10
 * the Free Software Foundation; either version 2 of the License, or
11
 * (at your option) any later version
12
 *
13
 * Kamailio is distributed in the hope that it will be useful,
14
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16
 * GNU General Public License for more details.
17
 *
18
 * You should have received a copy of the GNU General Public License
19
 * along with this program; if not, write to the Free Software
20
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
21
 */
22
23
#include <sys/types.h>
24
#include <string.h>
25
#include <stdlib.h>
26
#include <regex.h>
27
#include <ctype.h>
28
29
#include "parser/parse_uri.h"
30
#include "parser/parse_param.h"
31
#include "parser/parse_hname2.h"
32
33
#include "dprint.h"
34
#include "ut.h"
35
#include "trim.h"
36
#include "strutils.h"
37
38
/*! \brief
39
 * add backslashes to special characters
40
 */
41
int escape_common(char *dst, char *src, int src_len)
42
0
{
43
0
  int i, j;
44
45
0
  if(dst == 0 || src == 0 || src_len <= 0)
46
0
    return 0;
47
0
  j = 0;
48
0
  for(i = 0; i < src_len; i++) {
49
0
    switch(src[i]) {
50
0
      case '\'':
51
0
        dst[j++] = '\\';
52
0
        dst[j++] = src[i];
53
0
        break;
54
0
      case '"':
55
0
        dst[j++] = '\\';
56
0
        dst[j++] = src[i];
57
0
        break;
58
0
      case '\\':
59
0
        dst[j++] = '\\';
60
0
        dst[j++] = src[i];
61
0
        break;
62
0
      case '\0':
63
0
        dst[j++] = '\\';
64
0
        dst[j++] = '0';
65
0
        break;
66
0
      default:
67
0
        dst[j++] = src[i];
68
0
    }
69
0
  }
70
0
  return j;
71
0
}
72
73
/*! \brief
74
 * remove backslashes to special characters
75
 */
76
int unescape_common(char *dst, char *src, int src_len)
77
0
{
78
0
  int i, j;
79
80
0
  if(dst == 0 || src == 0 || src_len <= 0)
81
0
    return 0;
82
0
  j = 0;
83
0
  i = 0;
84
0
  while(i < src_len) {
85
0
    if(src[i] == '\\' && i + 1 < src_len) {
86
0
      switch(src[i + 1]) {
87
0
        case '\'':
88
0
          dst[j++] = '\'';
89
0
          i++;
90
0
          break;
91
0
        case '"':
92
0
          dst[j++] = '"';
93
0
          i++;
94
0
          break;
95
0
        case '\\':
96
0
          dst[j++] = '\\';
97
0
          i++;
98
0
          break;
99
0
        case '0':
100
0
          dst[j++] = '\0';
101
0
          i++;
102
0
          break;
103
0
        default:
104
0
          dst[j++] = src[i];
105
0
      }
106
0
    } else {
107
0
      dst[j++] = src[i];
108
0
    }
109
0
    i++;
110
0
  }
111
0
  return j;
112
0
}
113
114
/*! \brief
115
 * add backslashes for CR LF
116
 */
117
int escape_crlf(str *sin, str *sout)
118
0
{
119
0
  int i, j;
120
121
0
  if(sout == 0 || sin == 0 || sin->len <= 0)
122
0
    return -1;
123
0
  j = 0;
124
0
  for(i = 0; i < sin->len; i++) {
125
0
    switch(sin->s[i]) {
126
0
      case '\n':
127
0
        sout->s[j++] = '\\';
128
0
        sout->s[j++] = 'n';
129
0
        break;
130
0
      case '\r':
131
0
        sout->s[j++] = '\\';
132
0
        sout->s[j++] = 'r';
133
0
        break;
134
0
      default:
135
0
        sout->s[j++] = sin->s[i];
136
0
    }
137
0
  }
138
0
  sout->len = j;
139
0
  return 0;
140
0
}
141
142
/*! \brief
143
 * remove backslashes for CR LF
144
 */
145
int unescape_crlf(str *sin, str *sout)
146
0
{
147
0
  int i, j;
148
149
0
  if(sout == 0 || sin == 0 || sin->len <= 0)
150
0
    return -1;
151
0
  j = 0;
152
0
  i = 0;
153
0
  while(i < sin->len) {
154
0
    if(sin->s[i] == '\\' && i + 1 < sin->len) {
155
0
      switch(sin->s[i + 1]) {
156
0
        case 'n':
157
0
          sout->s[j++] = '\n';
158
0
          i++;
159
0
          break;
160
0
        case 'r':
161
0
          sout->s[j++] = '\r';
162
0
          i++;
163
0
          break;
164
0
        default:
165
0
          sout->s[j++] = sin->s[i];
166
0
      }
167
0
    } else {
168
0
      sout->s[j++] = sin->s[i];
169
0
    }
170
0
    i++;
171
0
  }
172
0
  sout->len = j;
173
0
  return 0;
174
0
}
175
176
/*! \brief Unscape all printable ASCII characters */
177
int unescape_user(str *sin, str *sout)
178
0
{
179
0
  char *at, *p, c;
180
181
0
  if(sin == NULL || sout == NULL || sin->s == NULL || sout->s == NULL
182
0
      || sin->len < 0 || sout->len < sin->len + 1)
183
0
    return -1;
184
185
0
  at = sout->s;
186
0
  p = sin->s;
187
0
  while(p < sin->s + sin->len) {
188
0
    if(*p == '%') {
189
0
      p++;
190
0
      switch(*p) {
191
0
        case '0':
192
0
        case '1':
193
0
        case '2':
194
0
        case '3':
195
0
        case '4':
196
0
        case '5':
197
0
        case '6':
198
0
        case '7':
199
0
        case '8':
200
0
        case '9':
201
0
          c = (*p - '0') << 4;
202
0
          break;
203
0
        case 'a':
204
0
        case 'b':
205
0
        case 'c':
206
0
        case 'd':
207
0
        case 'e':
208
0
        case 'f':
209
0
          c = (*p - 'a' + 10) << 4;
210
0
          break;
211
0
        case 'A':
212
0
        case 'B':
213
0
        case 'C':
214
0
        case 'D':
215
0
        case 'E':
216
0
        case 'F':
217
0
          c = (*p - 'A' + 10) << 4;
218
0
          break;
219
0
        default:
220
0
          LM_ERR("invalid hex digit <%u>\n", (unsigned int)*p);
221
0
          return -1;
222
0
      }
223
0
      p++;
224
0
      switch(*p) {
225
0
        case '0':
226
0
        case '1':
227
0
        case '2':
228
0
        case '3':
229
0
        case '4':
230
0
        case '5':
231
0
        case '6':
232
0
        case '7':
233
0
        case '8':
234
0
        case '9':
235
0
          c = c + (*p - '0');
236
0
          break;
237
0
        case 'a':
238
0
        case 'b':
239
0
        case 'c':
240
0
        case 'd':
241
0
        case 'e':
242
0
        case 'f':
243
0
          c = c + (*p - 'a' + 10);
244
0
          break;
245
0
        case 'A':
246
0
        case 'B':
247
0
        case 'C':
248
0
        case 'D':
249
0
        case 'E':
250
0
        case 'F':
251
0
          c = c + (*p - 'A' + 10);
252
0
          break;
253
0
        default:
254
0
          LM_ERR("invalid hex digit <%u>\n", (unsigned int)*p);
255
0
          return -1;
256
0
      }
257
0
      if((c < 32) || (c > 126)) {
258
0
        LM_ERR("invalid escaped character <%u>\n", (unsigned int)c);
259
0
        return -1;
260
0
      }
261
0
      *at++ = c;
262
0
    } else {
263
0
      *at++ = *p;
264
0
    }
265
0
    p++;
266
0
  }
267
268
0
  *at = 0;
269
0
  sout->len = at - sout->s;
270
271
0
  LM_DBG("unescaped string is <%s>\n", sout->s);
272
0
  return 0;
273
0
}
274
275
/*! \brief
276
 * Escape all printable characters that are not valid in user
277
 * part of request uri
278
 * no_need_to_escape = unreserved | user-unreserved
279
 * unreserved = aplhanum | mark
280
 * mark = - | _ | . | ! | ~ | * | ' | ( | )
281
 * user-unreserved = & | = | + | $ | , | ; | ? | /
282
 */
283
int escape_user(str *sin, str *sout)
284
0
{
285
286
0
  char *at, *p;
287
0
  unsigned char x;
288
289
0
  if(sin == NULL || sout == NULL || sin->s == NULL || sout->s == NULL
290
0
      || sin->len < 0 || sout->len < 3 * sin->len + 1)
291
0
    return -1;
292
293
294
0
  at = sout->s;
295
0
  p = sin->s;
296
0
  while(p < sin->s + sin->len) {
297
0
    if(*p < 32 || *p > 126) {
298
0
      LM_ERR("invalid escaped character <%u>\n", (unsigned int)*p);
299
0
      return -1;
300
0
    }
301
0
    if(isdigit((int)*p) || ((*p >= 'A') && (*p <= 'Z'))
302
0
        || ((*p >= 'a') && (*p <= 'z'))) {
303
0
      *at = *p;
304
0
    } else {
305
0
      switch(*p) {
306
0
        case '-':
307
0
        case '_':
308
0
        case '.':
309
0
        case '!':
310
0
        case '~':
311
0
        case '*':
312
0
        case '\'':
313
0
        case '(':
314
0
        case ')':
315
0
        case '&':
316
0
        case '=':
317
0
        case '+':
318
0
        case '$':
319
0
        case ',':
320
0
        case ';':
321
0
        case '?':
322
0
          *at = *p;
323
0
          break;
324
0
        default:
325
0
          *at++ = '%';
326
0
          x = (unsigned char)(*p) >> 4;
327
0
          if(x < 10) {
328
0
            *at++ = x + '0';
329
0
          } else {
330
0
            *at++ = x - 10 + 'a';
331
0
          }
332
0
          x = (*p) & 0x0f;
333
0
          if(x < 10) {
334
0
            *at = x + '0';
335
0
          } else {
336
0
            *at = x - 10 + 'a';
337
0
          }
338
0
      }
339
0
    }
340
0
    at++;
341
0
    p++;
342
0
  }
343
0
  *at = 0;
344
0
  sout->len = at - sout->s;
345
0
  LM_DBG("escaped string is <%s>\n", sout->s);
346
0
  return 0;
347
0
}
348
349
350
int unescape_param(str *sin, str *sout)
351
0
{
352
0
  return unescape_user(sin, sout);
353
0
}
354
355
356
/*! \brief
357
 * Escape all printable characters that are not valid in
358
 * a param part of request uri
359
 * no_need_to_escape = unreserved | param-unreserved
360
 * unreserved = alphanum | mark
361
 * mark = - | _ | . | ! | ~ | * | ' | ( | )
362
 * param-unreserved = [ | ] | / | : | & | + | $
363
 */
364
int escape_param(str *sin, str *sout)
365
0
{
366
0
  char *at, *p;
367
0
  unsigned char x;
368
369
0
  if(sin == NULL || sout == NULL || sin->s == NULL || sout->s == NULL
370
0
      || sin->len < 0 || sout->len < 3 * sin->len + 1)
371
0
    return -1;
372
373
0
  at = sout->s;
374
0
  p = sin->s;
375
0
  while(p < sin->s + sin->len) {
376
0
    if(*p < 32 || *p > 126) {
377
0
      LM_ERR("invalid escaped character <%u>\n", (unsigned int)*p);
378
0
      return -1;
379
0
    } else if(isdigit((int)*p) || ((*p >= 'A') && (*p <= 'Z'))
380
0
          || ((*p >= 'a') && (*p <= 'z'))) {
381
0
      *at = *p;
382
0
    } else {
383
0
      switch(*p) {
384
0
        case '-':
385
0
        case '_':
386
0
        case '.':
387
0
        case '!':
388
0
        case '~':
389
0
        case '*':
390
0
        case '\'':
391
0
        case '(':
392
0
        case ')':
393
0
        case '[':
394
0
        case ']':
395
0
        case '/':
396
0
        case ':':
397
0
        case '&':
398
0
        case '+':
399
0
        case '$':
400
0
          *at = *p;
401
0
          break;
402
0
        default:
403
404
0
          *at++ = '%';
405
0
          x = (unsigned char)(*p) >> 4;
406
0
          if(x < 10) {
407
0
            *at++ = x + '0';
408
0
          } else {
409
0
            *at++ = x - 10 + 'a';
410
0
          }
411
0
          x = (*p) & 0x0f;
412
0
          if(x < 10) {
413
0
            *at = x + '0';
414
0
          } else {
415
0
            *at = x - 10 + 'a';
416
0
          }
417
0
          break;
418
0
      }
419
0
    }
420
0
    at++;
421
0
    p++;
422
0
  }
423
0
  *at = 0;
424
0
  sout->len = at - sout->s;
425
0
  LM_DBG("escaped string is <%s>\n", sout->s);
426
427
0
  return 0;
428
0
}
429
430
/*! \brief
431
 * escapes a string to use as a CSV field, as specified in RFC4180:
432
 * - enclose string in double quotes
433
 * - escape double quotes with a second double quote
434
 */
435
int escape_csv(str *sin, str *sout)
436
0
{
437
0
  char *at, *p;
438
439
0
  if(sin == NULL || sout == NULL || sin->s == NULL || sout->s == NULL
440
0
      || sin->len < 0 || sout->len < 2 * sin->len + 3)
441
0
    return -1;
442
443
0
  at = sout->s;
444
0
  p = sin->s;
445
0
  *at++ = '"';
446
0
  while(p < sin->s + sin->len) {
447
0
    if(*p == '"') {
448
0
      *at++ = '"';
449
0
    }
450
0
    *at++ = *p++;
451
0
  }
452
0
  *at++ = '"';
453
0
  *at = 0;
454
0
  sout->len = at - sout->s;
455
0
  LM_DBG("escaped string is <%s>\n", sout->s);
456
457
0
  return 0;
458
0
}
459
460
int cmp_str(str *s1, str *s2)
461
0
{
462
0
  int ret = 0;
463
0
  int len = 0;
464
0
  if(s1->len == 0 && s2->len == 0)
465
0
    return 0;
466
0
  if(s1->len == 0)
467
0
    return -1;
468
0
  if(s2->len == 0)
469
0
    return 1;
470
0
  len = (s1->len < s2->len) ? s1->len : s2->len;
471
0
  ret = strncmp(s1->s, s2->s, len);
472
0
  if(ret == 0) {
473
0
    if(s1->len == s2->len)
474
0
      return 0;
475
0
    if(s1->len < s2->len)
476
0
      return -1;
477
0
    return 1;
478
0
  }
479
0
  return ret;
480
0
}
481
482
int cmpi_str(str *s1, str *s2)
483
0
{
484
0
  int ret = 0;
485
0
  int len = 0;
486
0
  if(s1->len == 0 && s2->len == 0)
487
0
    return 0;
488
0
  if(s1->len == 0)
489
0
    return -1;
490
0
  if(s2->len == 0)
491
0
    return 1;
492
0
  len = (s1->len < s2->len) ? s1->len : s2->len;
493
0
  ret = strncasecmp(s1->s, s2->s, len);
494
0
  if(ret == 0) {
495
0
    if(s1->len == s2->len)
496
0
      return 0;
497
0
    if(s1->len < s2->len)
498
0
      return -1;
499
0
    return 1;
500
0
  }
501
0
  return ret;
502
0
}
503
504
int cmp_hdrname_str(str *s1, str *s2)
505
0
{
506
0
  str n1, n2;
507
0
  hdr_field_t hf1, hf2;
508
509
0
  n1 = *s1;
510
0
  n2 = *s2;
511
0
  trim_trailing(&n1);
512
0
  trim_trailing(&n2);
513
514
0
  parse_hname2_str(&n1, &hf1);
515
0
  parse_hname2_str(&n2, &hf2);
516
0
  if(hf1.type == HDR_ERROR_T || hf2.type == HDR_ERROR_T) {
517
0
    LM_ERR("error parsing header names [%.*s] [%.*s]\n", n1.len, n1.s,
518
0
        n2.len, n2.s);
519
0
    return -4;
520
0
  }
521
522
0
  if(hf1.type != HDR_OTHER_T) {
523
0
    if(hf1.type == hf2.type) {
524
0
      return 0;
525
0
    } else {
526
0
      return 2;
527
0
    }
528
0
  } else if(hf2.type != HDR_OTHER_T) {
529
0
    return 2;
530
0
  }
531
0
  return cmpi_str(&n1, &n2);
532
0
}
533
534
int cmp_hdrname_strzn(str *s1, char *s2, size_t len)
535
0
{
536
0
  str n1, n2;
537
0
  hdr_field_t hf1, hf2;
538
539
0
  n1 = *s1;
540
0
  n2.s = s2;
541
0
  n2.len = len;
542
0
  trim_trailing(&n1);
543
0
  trim_trailing(&n2);
544
545
0
  parse_hname2_str(&n1, &hf1);
546
0
  parse_hname2_str(&n2, &hf2);
547
0
  if(hf1.type == HDR_ERROR_T || hf2.type == HDR_ERROR_T) {
548
0
    LM_ERR("error parsing header names [%.*s] [%.*s]\n", n1.len, n1.s,
549
0
        n2.len, n2.s);
550
0
    return -4;
551
0
  }
552
553
0
  if(hf1.type != HDR_OTHER_T) {
554
0
    if(hf1.type == hf2.type) {
555
0
      return 0;
556
0
    } else {
557
0
      return 2;
558
0
    }
559
0
  } else if(hf2.type != HDR_OTHER_T) {
560
0
    return 2;
561
0
  }
562
0
  return cmpi_str(&n1, &n2);
563
0
}
564
565
int cmp_str_params(str *s1, str *s2)
566
0
{
567
0
  param_t *pl1 = NULL;
568
0
  param_hooks_t phooks1;
569
0
  param_t *pit1 = NULL;
570
0
  param_t *pl2 = NULL;
571
0
  param_hooks_t phooks2;
572
0
  param_t *pit2 = NULL;
573
574
0
  if(parse_params(s1, CLASS_ANY, &phooks1, &pl1) < 0)
575
0
    return -1;
576
0
  if(parse_params(s2, CLASS_ANY, &phooks2, &pl2) < 0)
577
0
    return -1;
578
0
  for(pit1 = pl1; pit1; pit1 = pit1->next) {
579
0
    for(pit2 = pl2; pit2; pit2 = pit2->next) {
580
0
      if(pit1->name.len == pit2->name.len
581
0
          && strncasecmp(pit1->name.s, pit2->name.s, pit2->name.len)
582
0
                 == 0) {
583
0
        if(pit1->body.len != pit2->body.len
584
0
            || strncasecmp(
585
0
                   pit1->body.s, pit2->body.s, pit2->body.len)
586
0
                   != 0)
587
0
          return 1;
588
0
      }
589
0
    }
590
0
  }
591
0
  return 0;
592
0
}
593
594
/**
595
 * Compare SIP URI in light mode or as per RFC3261, 19.1.4
596
 * return:
597
 *  - 0: match
598
 *  - >0: no match
599
 *  - <0: error
600
 */
601
int cmp_uri_mode(struct sip_uri *uri1, struct sip_uri *uri2, int cmode)
602
0
{
603
0
  if(uri1->type != uri2->type)
604
0
    return 1;
605
  /* quick check for length */
606
0
  if(uri1->user.len != uri2->user.len || uri1->host.len != uri2->host.len
607
0
      || uri1->port.len != uri2->port.len
608
0
      || uri1->passwd.len != uri2->passwd.len)
609
0
    return 1;
610
0
  if(cmp_str(&uri1->user, &uri2->user) != 0)
611
0
    return 1;
612
0
  if(cmp_str(&uri1->port, &uri2->port) != 0)
613
0
    return 1;
614
0
  if(cmp_str(&uri1->passwd, &uri2->passwd) != 0)
615
0
    return 1;
616
0
  if(cmpi_str(&uri1->host, &uri2->host) != 0)
617
0
    return 1;
618
0
  if(cmode == 1) {
619
    /* compare mode light - proto should be the same for match */
620
0
    if(uri1->proto == uri2->proto) {
621
0
      return 0;
622
0
    }
623
0
    return 1;
624
0
  }
625
  /* if no params, we are done */
626
0
  if(uri1->params.len == 0 && uri2->params.len == 0)
627
0
    return 0;
628
0
  if(uri1->params.len == 0) {
629
0
    if(uri2->user_param.len != 0)
630
0
      return 1;
631
0
    if(uri2->ttl.len != 0)
632
0
      return 1;
633
0
    if(uri2->method.len != 0)
634
0
      return 1;
635
0
    if(uri2->maddr.len != 0)
636
0
      return 1;
637
0
  }
638
0
  if(uri2->params.len == 0) {
639
0
    if(uri1->user_param.len != 0)
640
0
      return 1;
641
0
    if(uri1->ttl.len != 0)
642
0
      return 1;
643
0
    if(uri1->method.len != 0)
644
0
      return 1;
645
0
    if(uri1->maddr.len != 0)
646
0
      return 1;
647
0
  }
648
0
  return cmp_str_params(&uri1->params, &uri2->params);
649
0
}
650
651
/**
652
 * Compare SIP URI as per RFC3261, 19.1.4 (match also params)
653
 * return:
654
 *  - 0: match
655
 *  - >0: no match
656
 *  - <0: error
657
 */
658
int cmp_uri(struct sip_uri *uri1, struct sip_uri *uri2)
659
0
{
660
0
  return cmp_uri_mode(uri1, uri2, 0);
661
0
}
662
663
/**
664
 * Compare SIP URI light - uri type, user, host, port and proto match
665
 * return:
666
 *  - 0: match
667
 *  - >0: no match
668
 *  - <0: error
669
 */
670
int cmp_uri_light(struct sip_uri *uri1, struct sip_uri *uri2)
671
0
{
672
0
  return cmp_uri_mode(uri1, uri2, 1);
673
0
}
674
675
/**
676
 * return:
677
 *  - 0: match
678
 *  - >0: no match
679
 *  - <0: error
680
 */
681
int cmp_uri_str(str *s1, str *s2)
682
0
{
683
0
  struct sip_uri uri1;
684
0
  struct sip_uri uri2;
685
686
  /* todo: parse uri and compare the parts */
687
0
  if(parse_uri(s1->s, s1->len, &uri1) != 0)
688
0
    return -1;
689
0
  if(parse_uri(s2->s, s2->len, &uri2) != 0)
690
0
    return -1;
691
0
  return cmp_uri(&uri1, &uri2);
692
0
}
693
694
/**
695
 * return:
696
 *  - 0: match
697
 *  - >0: no match
698
 *  - <0: error
699
 */
700
int cmp_uri_light_str(str *s1, str *s2)
701
0
{
702
0
  struct sip_uri uri1;
703
0
  struct sip_uri uri2;
704
705
  /* todo: parse uri and compare the parts */
706
0
  if(parse_uri(s1->s, s1->len, &uri1) != 0)
707
0
    return -1;
708
0
  if(parse_uri(s2->s, s2->len, &uri2) != 0)
709
0
    return -1;
710
0
  return cmp_uri_light(&uri1, &uri2);
711
0
}
712
713
/**
714
 * Compare SIP AoR
715
 * - match user, host and port (if port missing, assume 5060)
716
 * return:
717
 *  - 0: match
718
 *  - >0: no match
719
 *  - <0: error
720
 */
721
int cmp_aor(struct sip_uri *uri1, struct sip_uri *uri2)
722
0
{
723
  /* quick check for length */
724
0
  if(uri1->user.len != uri2->user.len || uri1->host.len != uri2->host.len)
725
0
    return 1;
726
0
  if(cmp_str(&uri1->user, &uri2->user) != 0)
727
0
    return 1;
728
0
  if(cmp_str(&uri1->port, &uri2->port) != 0) {
729
0
    if(uri1->port.len == 0 && uri2->port_no != 5060)
730
0
      return 1;
731
0
    if(uri2->port.len == 0 && uri1->port_no != 5060)
732
0
      return 1;
733
0
  }
734
0
  if(cmpi_str(&uri1->host, &uri2->host) != 0)
735
0
    return 1;
736
0
  return 0;
737
0
}
738
739
/**
740
 * return:
741
 *  - 0: match
742
 *  - >0: no match
743
 *  - <0: error
744
 */
745
int cmp_aor_str(str *s1, str *s2)
746
0
{
747
0
  struct sip_uri uri1;
748
0
  struct sip_uri uri2;
749
750
  /* todo: parse uri and compare the parts */
751
0
  if(parse_uri(s1->s, s1->len, &uri1) != 0)
752
0
    return -1;
753
0
  if(parse_uri(s2->s, s2->len, &uri2) != 0)
754
0
    return -1;
755
0
  return cmp_aor(&uri1, &uri2);
756
0
}
757
758
/*! \brief Replace in replacement tokens \\d with substrings of string pointed by
759
 * pmatch.
760
 */
761
int replace(regmatch_t *pmatch, char *string, char *replacement, str *result)
762
0
{
763
0
  int len, i, j, digit, size;
764
765
0
  len = strlen(replacement);
766
0
  j = 0;
767
768
0
  for(i = 0; i < len; i++) {
769
0
    if(replacement[i] == '\\') {
770
0
      if(i < len - 1) {
771
0
        if(isdigit((unsigned char)replacement[i + 1])) {
772
0
          digit = replacement[i + 1] - '0';
773
0
          if(pmatch[digit].rm_so != -1) {
774
0
            size = pmatch[digit].rm_eo - pmatch[digit].rm_so;
775
0
            if(j + size < result->len) {
776
0
              memcpy(&(result->s[j]),
777
0
                  string + pmatch[digit].rm_so, size);
778
0
              j = j + size;
779
0
            } else {
780
0
              return -1;
781
0
            }
782
0
          } else {
783
0
            return -2;
784
0
          }
785
0
          i = i + 1;
786
0
          continue;
787
0
        } else {
788
0
          i = i + 1;
789
0
        }
790
0
      } else {
791
0
        return -3;
792
0
      }
793
0
    }
794
0
    if(j + 1 < result->len) {
795
0
      result->s[j] = replacement[i];
796
0
      j = j + 1;
797
0
    } else {
798
0
      return -4;
799
0
    }
800
0
  }
801
0
  result->len = j;
802
0
  return 1;
803
0
}
804
805
806
0
#define SR_RE_MAX_MATCH 6
807
808
/*! \brief Match pattern against string and store result in pmatch */
809
int reg_match(char *pattern, char *string, regmatch_t *pmatch)
810
0
{
811
0
  regex_t preg;
812
813
0
  if(regcomp(&preg, pattern, REG_EXTENDED | REG_NEWLINE)) {
814
0
    return -1;
815
0
  }
816
0
  if(preg.re_nsub > SR_RE_MAX_MATCH) {
817
0
    regfree(&preg);
818
0
    return -2;
819
0
  }
820
0
  if(regexec(&preg, string, SR_RE_MAX_MATCH, pmatch, 0)) {
821
0
    regfree(&preg);
822
0
    return -3;
823
0
  }
824
0
  regfree(&preg);
825
0
  return 0;
826
0
}
827
828
829
/*! \brief Match pattern against string and, if match succeeds, and replace string
830
 * with replacement substituting tokens \\d with matched substrings.
831
 */
832
int reg_replace(char *pattern, char *replacement, char *string, str *result)
833
0
{
834
0
  regmatch_t pmatch[SR_RE_MAX_MATCH];
835
836
0
  LM_DBG("pattern: '%s', replacement: '%s', string: '%s'\n", pattern,
837
0
      replacement, string);
838
839
0
  if(reg_match(pattern, string, &(pmatch[0]))) {
840
0
    return -1;
841
0
  }
842
843
0
  return replace(&pmatch[0], string, replacement, result);
844
0
}
845
846
/* Converts a hex character to its integer value */
847
char hex_to_char(char hex_code)
848
0
{
849
0
  return isdigit((unsigned char)hex_code)
850
0
           ? hex_code - '0'
851
0
           : tolower((unsigned char)hex_code) - 'a' + 10;
852
0
}
853
854
/* Converts an integer value to its hex character */
855
char char_to_hex(char char_code)
856
0
{
857
0
  static char hex[] = "0123456789abcdef";
858
0
  return hex[char_code & 15];
859
0
}
860
861
/*! \brief
862
 *  URL Encodes a string
863
 */
864
int urlencode(str *sin, str *sout)
865
0
{
866
0
  char *at, *p;
867
868
0
  if(sin == NULL || sout == NULL || sin->s == NULL || sout->s == NULL
869
0
      || sin->len < 0 || sout->len < 3 * sin->len + 1)
870
0
    return -1;
871
872
0
  at = sout->s;
873
0
  p = sin->s;
874
875
0
  while(p < sin->s + sin->len) {
876
0
    if(isalnum((unsigned char)(*p)) || *p == '-' || *p == '_' || *p == '.'
877
0
        || *p == '~')
878
0
      *at++ = *p;
879
0
    else
880
0
      *at++ = '%', *at++ = char_to_hex((unsigned char)(*p) >> 4),
881
0
      *at++ = char_to_hex(*p & 15);
882
0
    p++;
883
0
  }
884
885
0
  *at = 0;
886
0
  sout->len = at - sout->s;
887
0
  LM_DBG("urlencoded string is <%s>\n", sout->s);
888
889
0
  return 0;
890
0
}
891
892
/*! \brief
893
 *  URL Decodes a string
894
 */
895
int urldecode(str *sin, str *sout)
896
0
{
897
0
  char *at, *p;
898
899
0
  at = sout->s;
900
0
  p = sin->s;
901
902
0
  while(p < sin->s + sin->len) {
903
0
    if(*p == '%') {
904
0
      if(p[1] && p[2]) {
905
0
        *at++ = hex_to_char(p[1]) << 4 | hex_to_char(p[2]);
906
0
        p += 2;
907
0
      }
908
0
    } else if(*p == '+') {
909
0
      *at++ = ' ';
910
0
    } else {
911
0
      *at++ = *p;
912
0
    }
913
0
    p++;
914
0
  }
915
916
0
  *at = 0;
917
0
  sout->len = at - sout->s;
918
919
0
  LM_DBG("urldecoded string is <%s>\n", sout->s);
920
0
  return 0;
921
0
}
922
923
/*! \brief
924
 *  escape input string to prepare it for use as json value
925
 */
926
void ksr_str_json_escape(str *s_in, str *s_out, int *emode)
927
0
{
928
0
  char *p1, *p2;
929
0
  int len = 0;
930
0
  char token;
931
0
  int i;
932
933
0
  s_out->len = 0;
934
0
  if(!s_in || !s_in->s) {
935
0
    s_out->s = strdup("");
936
0
    *emode = 1;
937
0
    return;
938
0
  }
939
0
  for(i = 0; i < s_in->len; i++) {
940
0
    if(strchr("\"\\\b\f\n\r\t", s_in->s[i])) {
941
0
      len += 2;
942
0
    } else if(s_in->s[i] < 32) {
943
0
      len += 6;
944
0
    } else {
945
0
      len++;
946
0
    }
947
0
  }
948
0
  if(len == s_in->len) {
949
0
    s_out->s = s_in->s;
950
0
    s_out->len = s_in->len;
951
0
    *emode = 0;
952
0
    return;
953
0
  }
954
955
0
  s_out->s = (char *)malloc(len + 2);
956
0
  if(!s_out->s) {
957
0
    return;
958
0
  }
959
0
  *emode = 1;
960
961
0
  p2 = s_out->s;
962
0
  p1 = s_in->s;
963
0
  while(p1 < s_in->s + s_in->len) {
964
0
    if((unsigned char)*p1 > 31 && *p1 != '\"' && *p1 != '\\') {
965
0
      *p2++ = *p1++;
966
0
    } else {
967
0
      *p2++ = '\\';
968
0
      switch(token = *p1++) {
969
0
        case '\\':
970
0
          *p2++ = '\\';
971
0
          break;
972
0
        case '\"':
973
0
          *p2++ = '\"';
974
0
          break;
975
0
        case '\b':
976
0
          *p2++ = 'b';
977
0
          break;
978
0
        case '\f':
979
0
          *p2++ = 'f';
980
0
          break;
981
0
        case '\n':
982
0
          *p2++ = 'n';
983
0
          break;
984
0
        case '\r':
985
0
          *p2++ = 'r';
986
0
          break;
987
0
        case '\t':
988
0
          *p2++ = 't';
989
0
          break;
990
0
        default:
991
          /* escape and print */
992
0
          snprintf(p2, 6, "u%04x", token);
993
0
          p2 += 5;
994
0
          break;
995
0
      }
996
0
    }
997
0
  }
998
0
  *p2++ = 0;
999
0
  s_out->len = len;
1000
0
  return;
1001
0
}