Coverage Report

Created: 2026-08-31 06:36

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/opensips/sdp_ops.c
Line
Count
Source
1
/*
2
 * Copyright (C) 2024-2025 OpenSIPS Solutions
3
 *
4
 * This file is part of opensips, a free SIP server.
5
 *
6
 * opensips is free software; you can redistribute it and/or modify
7
 * it under the terms of the GNU General Public License as published by
8
 * the Free Software Foundation; either version 2 of the License, or
9
 * (at your option) any later version
10
 *
11
 * opensips is distributed in the hope that it will be useful,
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
 * GNU General Public License for more details.
15
 *
16
 * You should have received a copy of the GNU General Public License
17
 * along with this program; if not, write to the Free Software
18
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19
 */
20
21
#include "pvar.h"
22
#include "sdp_ops.h"
23
#include "ut.h"
24
#include "parser/sdp/sdp.h"
25
26
enum sdp_pv_name {
27
  SDP_PV_NAME_P1_NAME,
28
  SDP_PV_NAME_P2_STREAM,
29
  SDP_PV_NAME_P3_LINE,
30
  SDP_PV_NAME_P4_TOKEN,
31
};
32
33
0
#define SDP_PV_IDX_INSERT  1
34
0
#define SDP_PV_IDX_AINSERT 2
35
36
void free_sdp_ops_lines(struct sdp_body_part_ops *ops);
37
38
struct sdp_body_part_ops *mk_sdp_ops(void)
39
0
{
40
0
  struct sdp_body_part_ops *ops;
41
42
0
  ops = pkg_malloc(sizeof *ops);
43
0
  if (!ops) {
44
0
    LM_ERR("oom\n");
45
0
    return NULL;
46
0
  }
47
0
  memset(ops, 0, sizeof *ops);
48
49
0
  return ops;
50
0
}
51
52
53
/* fetch the value of a static/dynamic index */
54
static inline int IDX(struct sip_msg *msg, struct sdp_pv_idx *idx)
55
0
{
56
0
  pv_value_t val;
57
58
0
  if (!idx->is_pv_idx)
59
0
    return idx->idx;
60
61
0
  if (pv_get_spec_value(msg, &idx->idx_pv, &val) != 0) {
62
0
    LM_ERR("failed to get idx spec value\n");
63
0
    return -1;
64
0
  }
65
66
0
  if (!(val.flags & PV_VAL_INT)) {
67
0
    LM_ERR("SDP idx spec contains non-INT value ('%.*s')\n",
68
0
            val.rs.len, val.rs.s);
69
0
    return -1;
70
0
  }
71
72
0
  return val.ri;
73
0
}
74
75
76
/* returns a string representation of the index (useful for debugging) */
77
static inline char *IDX_STR(struct sdp_pv_idx *idx)
78
0
{
79
0
#define idx_buf_cnt 4
80
0
  static char buf[idx_buf_cnt][20];
81
0
  static int buf_idx;
82
0
  char *p;
83
84
0
  p = buf[buf_idx];
85
0
  buf_idx = (buf_idx+1) % idx_buf_cnt;
86
87
0
  if (!idx->is_pv_idx)
88
0
    sprintf(p, "%d", idx->idx);
89
0
  else
90
0
    sprintf(p, "pv type %d", idx->idx_pv.type);
91
92
0
  return p;
93
0
}
94
95
96
static inline int sdp_detect_line_sep(str *sdp, char *sep, int *sep_len)
97
0
{
98
0
  char *p, *lim = sdp->s + sdp->len;
99
100
  /* detect separator using first line
101
   *   (avoid checking last line -- some UAs omit the LF) */
102
0
  for (p = sdp->s; p < lim && *p != '\n' && *p != '\r'; p++) {};
103
0
  if (p == lim)
104
0
    return 0;
105
106
0
  sep[0] = *p;
107
0
  if (*p == '\r' && (p+1)<lim && *(p+1) == '\n') {
108
0
    sep[1] = '\n';
109
0
    *sep_len = 2;
110
0
  } else {
111
0
    *sep_len = 1;
112
0
  }
113
114
0
  return 1;
115
0
}
116
117
118
int pv_set_sdp(struct sip_msg *msg, pv_param_t *param,
119
      int op, pv_value_t *val)
120
0
{
121
0
  struct sdp_body_part_ops *ops;
122
0
  int null_before = 0;
123
124
0
  if (!msg || !param) {
125
0
    LM_ERR("bad parameters\n");
126
0
    return -1;
127
0
  }
128
129
0
  if (!msg->sdp_ops) {
130
0
    ops = msg->sdp_ops = mk_sdp_ops();
131
0
    if (!ops) {
132
0
      LM_ERR("oom\n");
133
0
      return -1;
134
0
    }
135
0
  } else {
136
0
    ops = msg->sdp_ops;
137
0
  }
138
139
0
  if (!val) {
140
0
    LM_ERR("sdp-set: NULL\n");
141
0
    ops->flags |= SDP_OPS_FL_NULL;
142
0
    ops->flags &= ~SDP_OPS_FL_DIRTY;
143
0
    if (msg->body) {
144
0
      free_sip_body(msg->body);
145
0
      msg->body = NULL;
146
0
    }
147
0
    free_sdp_ops_lines(ops);
148
0
    pkg_free(ops->rebuilt_sdp.s);
149
0
    ops->rebuilt_sdp.s = NULL;
150
151
0
  } else {
152
0
    LM_ERR("sdp-set: non-NULL!\n");
153
154
0
    if (!(val->flags & PV_VAL_STR) || val->rs.len <= 0) {
155
0
      LM_ERR("non-empty str value required to set SDP body\n");
156
0
      goto error;
157
0
    }
158
159
0
    if (pkg_str_sync(&ops->sdp, &val->rs) != 0) {
160
0
      LM_ERR("oom\n");
161
0
      return -1;
162
0
    }
163
164
0
    if (ops->flags & SDP_OPS_FL_NULL) {
165
0
      null_before = 1;
166
0
      ops->flags &= ~SDP_OPS_FL_NULL;
167
0
    }
168
169
0
    if (msg->body) {
170
0
      free_sip_body(msg->body);
171
0
      msg->body = NULL;
172
0
    }
173
174
0
    if (parse_sip_body(msg) != 0) {
175
0
      LM_ERR("bad body provided (%.*s ...), refusing to set in SIP msg\n",
176
0
              val->rs.len>=40 ? 40:val->rs.len, val->rs.s);
177
0
      pkg_free(ops->sdp.s);
178
0
      ops->sdp = STR_NULL;
179
0
      if (null_before)
180
0
        ops->flags |= SDP_OPS_FL_NULL;
181
0
      return -1;
182
0
    }
183
184
0
    if (!parse_sdp(msg)) {
185
0
      LM_ERR("bad SDP provided (%.*s ...), refusing to set in SIP msg\n",
186
0
              val->rs.len>=40 ? 40:val->rs.len, val->rs.s);
187
0
      free_sip_body(msg->body);
188
0
      msg->body = NULL;
189
0
      pkg_free(ops->sdp.s);
190
0
      ops->sdp = STR_NULL;
191
0
      if (null_before)
192
0
        ops->flags |= SDP_OPS_FL_NULL;
193
0
      return -1;
194
0
    }
195
196
0
    if (!sdp_detect_line_sep(&ops->sdp, ops->sep, &ops->sep_len)) {
197
0
      LM_ERR("failed to detect SDP separator, first 50B: '%.*s'\n",
198
0
              ops->sdp.len >= 50 ? 50:ops->sdp.len, ops->sdp.s);
199
0
      free_sip_body(msg->body);
200
0
      msg->body = NULL;
201
0
      pkg_free(ops->sdp.s);
202
0
      ops->sdp = STR_NULL;
203
0
      memset(ops->sep, 0, 2);
204
0
      if (null_before)
205
0
        ops->flags |= SDP_OPS_FL_NULL;
206
0
      return -1;
207
0
    }
208
209
0
    if (ops->sdp.s[ops->sdp.len-1] > '\r')
210
0
      ops->flags |= SDP_OPS_FL_NO_LLF;
211
212
0
    free_sdp_ops_lines(ops);
213
0
    if (ops->rebuilt_sdp.s) {
214
0
      pkg_free(ops->rebuilt_sdp.s);
215
0
      ops->rebuilt_sdp = STR_NULL;
216
0
    }
217
218
0
    LM_DBG("separator: %d %d (%d)\n", ops->sdp.s[ops->sdp.len-2],
219
0
            ops->sdp.s[ops->sdp.len-1], ops->sep_len);
220
0
  }
221
222
0
  return 0;
223
0
error:
224
0
  return -1;
225
0
}
226
227
228
static char *parse_sdp_pv_index(char *in, int len, struct sdp_pv_idx *idx)
229
0
{
230
0
  char *lim = in + len, *end;
231
0
  long val = -1;
232
233
0
  if (len <= 0)
234
0
    return NULL;
235
236
0
  if (in[0] == PV_MARKER) {
237
0
    str input = {.s = in, .len = len};
238
239
0
    if (!(end = pv_parse_spec(&input, &idx->idx_pv))) {
240
0
      LM_ERR("failed to parse spec idx!  input: '%.*s'\n", len, in);
241
0
      return NULL;
242
0
    }
243
244
0
    idx->is_pv_idx = 1;
245
0
    goto parse_bracket;
246
0
  }
247
248
0
  val = strtol(in, &end, 10);
249
0
  if (errno == ERANGE) {
250
0
    LM_ERR("failed to parse index: value too big\n");
251
0
    return NULL;
252
0
  }
253
254
0
  if (val == -1) {
255
0
    LM_ERR("failed to parse index, given input: ...[%.*s\n", len, in);
256
0
    return NULL;
257
0
  }
258
259
0
parse_bracket:
260
0
  while (end < lim && is_ws(*end))
261
0
    end++;
262
263
0
  if (end == lim || *end != ']') {
264
0
    LM_ERR("failed to parse index, given input: ...[%.*s\n", len, in);
265
0
    return NULL;
266
0
  }
267
268
0
  while (end+1 < lim && is_ws(*(end+1)))
269
0
    end++;
270
271
0
  idx->idx = val;
272
0
  return end;
273
0
}
274
275
276
int pv_parse_sdp_name(pv_spec_p sp, const str *_in)
277
0
{
278
  // TODO -- add support for custom SDP holders
279
0
  str in = *_in, tok;
280
0
  int escape = 0, i;
281
0
  enum sdp_pv_name nm = SDP_PV_NAME_P3_LINE;
282
0
  struct sdp_pv_param *param;
283
0
  char *p, *lim = in.s + in.len;
284
285
0
  if (!sp)
286
0
    return -1;
287
288
0
  LM_DBG("parse sdp name: '%.*s'\n", in.len, in.s);
289
0
  trim(&in);
290
0
  if (!in.s || in.len == 0)
291
0
    goto done;
292
293
0
  if (in.s[0] == PV_MARKER) {
294
0
    LM_ERR("no support for dynamic names in $sdp.line\n");
295
0
    return -1;
296
0
  } else if (in.s[0] == '@') {
297
    // TODO: impl custom SDP holders (perhaps using a map)
298
0
    return -1;
299
0
  }
300
301
0
  param = pkg_malloc(sizeof *param);
302
0
  if (!param) {
303
0
    LM_ERR("oom\n");
304
0
    return -1;
305
0
  }
306
0
  memset(param, 0, sizeof *param);
307
308
0
  tok.s = in.s;
309
0
  for (i = 0; i < in.len; i++) {
310
0
    struct sdp_pv_idx idx = {0};
311
312
0
    if (escape && (in.s[i] == '\\' || in.s[i] == '/')) {
313
0
      memmove(&in.s[i-1], &in.s[i], in.len - i);
314
0
      in.len--;
315
0
      i--;
316
0
      escape = 0;
317
0
      continue;
318
0
    }
319
320
0
    if (in.s[i] == '\\') {
321
0
      escape = 1;
322
0
      continue;
323
0
    }
324
0
    escape = 0;
325
326
0
    if (in.s[i] == '[') {
327
0
      p = parse_sdp_pv_index(in.s + i + 1, in.len - i - 1, &idx);
328
0
      if (!p) {
329
0
        LM_ERR("error while parsing index in $sdp name: '%.*s'\n", in.len, in.s);
330
0
        return -1;
331
0
      }
332
333
0
      if (p < lim && *p != '/') {
334
0
        LM_ERR("error after index part in $sdp name: '%.*s'\n", in.len, in.s);
335
0
        return -1;
336
0
      }
337
338
0
      i += p - (in.s + i);
339
0
      continue;
340
0
    }
341
342
0
    if (in.s[i] == '/' && nm <= SDP_PV_NAME_P4_TOKEN) {
343
0
      tok.len = i - (tok.s - in.s);
344
      // save tok
345
0
      switch (nm) {
346
0
      case SDP_PV_NAME_P1_NAME:
347
0
      case SDP_PV_NAME_P2_STREAM:
348
0
      case SDP_PV_NAME_P3_LINE:
349
0
        param->match_line.prefix = tok;
350
0
        tok.s = in.s + i + 1;
351
0
        break;
352
0
      case SDP_PV_NAME_P4_TOKEN:
353
0
        param->match_token.prefix = tok;
354
0
        break;
355
0
      }
356
357
0
      nm++;
358
0
      continue;
359
0
    }
360
0
  }
361
362
0
  LM_DBG("parse sdp name: '%.*s'\n", in.len, in.s);
363
364
0
  sp->pvp.pvn.type = PV_NAME_PVAR;
365
0
  sp->pvp.pvn.u.dname = param;
366
367
0
done:
368
0
  return 0;
369
0
}
370
371
372
int pv_parse_sdp_line_index(pv_spec_p sp, const str *in)
373
0
{
374
0
  #define SDP_INSERT_IDX  "insert"
375
0
  #define SDP_AINSERT_IDX "insertAfter"
376
377
0
  if (!in || !in->s || !sp)
378
0
    return -1;
379
380
0
  if (str_casematch(in, &str_init(SDP_INSERT_IDX))) {
381
0
    sp->pvp.pvi.type = SDP_PV_IDX_INSERT;
382
0
    return 0;
383
0
  }
384
385
0
  if (str_casematch(in, &str_init(SDP_AINSERT_IDX))) {
386
0
    sp->pvp.pvi.type = SDP_PV_IDX_AINSERT;
387
0
    return 0;
388
0
  }
389
390
0
  LM_ERR("unsupported SDP variable index: '%.*s'\n", in->len, in->s);
391
0
  return -1;
392
0
}
393
394
395
int sdp_ops_parse_lines(struct sdp_body_part_ops *ops, str *body)
396
0
{
397
0
  char *p, *lim = body->s + body->len, _sep[2], sep, *start;
398
0
  int sep_len = 1, i = 0;
399
400
0
  if (!sdp_detect_line_sep(body, _sep, &sep_len)) {
401
0
    LM_ERR("failed to detect SDP separator, first 50B: '%.*s'\n",
402
0
            body->len >= 50 ? 50:body->len, body->s);
403
0
    return -1;
404
0
  }
405
406
0
  sep = _sep[0];
407
0
  start = body->s;
408
0
  for (p = start; p < lim; p++) {
409
0
    if (*p != sep || (sep_len == 2 && p < (lim-1) && *(p+1) != '\n'))
410
0
      continue;
411
412
0
    if (i >= SDP_MAX_LINES) {
413
0
      LM_ERR("SDP body exceeds maximum line count (%d)\n",
414
0
          SDP_MAX_LINES);
415
0
      return -1;
416
0
    }
417
418
    /* lines are stored *without* the ending separator */
419
0
    ops->lines[i].line.s = start;
420
0
    ops->lines[i].line.len = p - start;
421
0
    ops->lines[i++].newbuf = 0;
422
423
0
    start = p + sep_len;
424
0
  }
425
426
  /* edge-case: the very last SDP line does not include a separator... */
427
0
  if (start < lim) {
428
0
    if (i >= SDP_MAX_LINES) {
429
0
      LM_ERR("SDP body exceeds maximum line count (%d)\n",
430
0
          SDP_MAX_LINES);
431
0
      return -1;
432
0
    }
433
434
0
    ops->lines[i].line.s = start;
435
0
    ops->lines[i].line.len = p - start;
436
0
    ops->lines[i++].newbuf = 0;
437
0
    ops->flags |= SDP_OPS_FL_NO_LLF;
438
0
  }
439
440
0
  LM_DBG("parsed %d SDP lines in total\n", i);
441
0
  ops->lines_sz = i;
442
443
0
  memcpy(ops->sep, _sep, sep_len);
444
0
  ops->sep_len = sep_len;
445
446
0
  return 0;
447
0
}
448
449
#define first_part_by_mime( _part_start, _part_end, _mime) \
450
0
  do {\
451
0
    _part_end = _part_start;\
452
0
    while( (_part_end) && \
453
0
    !(is_body_part_received(_part_end) && ((_mime)==0 || \
454
0
    (_mime)==(_part_end)->mime )) ) { \
455
0
      _part_end = (_part_end)->next; \
456
0
    } \
457
0
  }while(0)
458
459
/* @until_line: non-inclusive (i.e. must be next-after last valid line) */
460
int _sdp_ops_find_line(struct sdp_body_part_ops *ops, int idx,
461
            str *prefix, str *token, int from_line, int until_line)
462
0
{
463
0
  int i;
464
465
0
  if ((from_line+idx) > until_line || (from_line+idx) >= SDP_MAX_LINES ||
466
0
          (token->s && (from_line+idx) == until_line)) {
467
0
    LM_DBG("index out of bounds (trying to fetch line %d, prefix: %.*s/%d"
468
0
                ", have %d lines)\n",
469
0
            idx, prefix->len, prefix->s, prefix->len, until_line);
470
0
    return -1;
471
0
  }
472
473
0
  if (prefix->len == 0)
474
0
    return from_line + idx;
475
476
0
  for (i = from_line; i < until_line; i++) {
477
    /* have prefix and doesn't match current line => skip it */
478
0
    if (prefix->len > ops->lines[i].line.len
479
0
            || strncasecmp(prefix->s, ops->lines[i].line.s, prefix->len))
480
0
      continue;
481
482
    /* have index, but still too high => skip line */
483
0
    if (idx > 0) {
484
0
      idx--;
485
0
      continue;
486
0
    }
487
488
    /* line found */
489
0
    return i;
490
0
  }
491
492
0
  return -1;
493
0
}
494
495
/* Note: MAY return the very "next last" line idx, to enable INSERT ops */
496
static inline int sdp_ops_find_line(struct sip_msg *msg, struct sdp_body_part_ops *ops, int idx,
497
        int by_session, struct sdp_chunk_match *by_stream, str *prefix, str *token)
498
0
{
499
0
  int i, j, have_stream = 0, stream_idx;
500
0
  struct sdp_ops_line *lines;
501
502
0
  if (!by_stream) {
503
0
    if (!by_session)
504
0
      return _sdp_ops_find_line(ops, idx, prefix, token, 0, ops->lines_sz);
505
506
    /* the SDP session ends at the first m= line */
507
0
    lines = ops->lines;
508
0
    for (i = 0; i < ops->lines_sz; i++) {
509
0
      if (lines[i].line.len < 2 || strncasecmp("m=", lines[i].line.s, 2))
510
0
        continue;
511
0
      break;
512
0
    }
513
514
0
    idx = _sdp_ops_find_line(ops, idx, prefix, token, 0, i);
515
0
    return (idx > i || (idx == i && by_session == 1)) ? -1 : idx;
516
0
  }
517
518
0
  lines = ops->lines;
519
0
  stream_idx = IDX(msg, &by_stream->idx);
520
0
  for (i = 0; i < ops->lines_sz; i++) {
521
0
    if (lines[i].line.len < 2 || strncasecmp("m=", lines[i].line.s, 2))
522
0
      continue;
523
524
0
    if (by_stream->prefix.len > (lines[i].line.len-2)
525
0
            || strncasecmp(by_stream->prefix.s, lines[i].line.s+2, by_stream->prefix.len))
526
0
      continue;
527
528
0
    if (stream_idx-- > 0)
529
0
      continue;
530
531
0
    have_stream = 1;
532
0
    break;
533
0
  }
534
535
0
  if (!have_stream) {
536
0
    LM_DBG("failed to locate a stream by prefix: '%.*s', index: %d\n",
537
0
            by_stream->prefix.len, by_stream->prefix.s, IDX(msg, &by_stream->idx));
538
0
    return -1;
539
0
  }
540
541
0
  for (j = i+1; j < ops->lines_sz; j++) {
542
0
    if (lines[j].line.len < 2 || strncasecmp("m=", lines[j].line.s, 2))
543
0
      continue;
544
0
    break;
545
0
  }
546
547
0
  LM_DBG("located stream by prefix: '%.*s', idx: %d; interval: [%d, %d)\n",
548
0
            by_stream->prefix.len, by_stream->prefix.s, IDX(msg, &by_stream->idx), i, j);
549
550
0
  return _sdp_ops_find_line(ops, idx, prefix, token, i, j);
551
0
}
552
553
554
int pv_get_sdp_line(struct sip_msg *msg, pv_param_t *param, pv_value_t *res)
555
0
{
556
0
  struct sdp_pv_param *pvp = (struct sdp_pv_param *)param->pvn.u.dname;
557
0
  struct sdp_body_part_ops *ops;
558
0
  struct sip_msg_body *sbody;
559
0
  struct body_part *body_part;
560
0
  str body, line = STR_NULL, pfx, token = STR_NULL;
561
0
  char *p, *lim, *start;
562
0
  int idx;
563
564
0
  if (!msg || !res)
565
0
    return pv_get_null(msg, param, res);
566
567
0
  if (msg->body_lumps) {
568
    /* TODO: rebuild SDP body, clear the body lumps; assert lines_sz == 0 */
569
0
  }
570
571
0
  if (!have_sdp_ops(msg) || msg->sdp_ops->lines_sz == 0) {
572
0
    if (parse_sip_body(msg)<0 || !(sbody=msg->body)) {
573
0
      LM_ERR("current SIP message has no SDP body!\n");
574
0
      return pv_get_null(msg, param, res);
575
0
    }
576
577
0
    first_part_by_mime( &sbody->first, body_part, (TYPE_APPLICATION<<16)+SUBTYPE_SDP );
578
0
    if (!body_part) {
579
0
      LM_ERR("current SIP message has a body, but no SDP part!\n");
580
0
      return pv_get_null(msg, param, res);
581
0
    }
582
583
0
    ops = msg->sdp_ops;
584
    /* first time working with SDP ops => allocate DS */
585
0
    if (!ops && !(ops = msg->sdp_ops = mk_sdp_ops())) {
586
0
      LM_ERR("oom\n");
587
0
      return pv_get_null(msg, param, res);
588
0
    }
589
590
0
    body = body_part->body;
591
0
    if (ops->lines_sz == 0 && sdp_ops_parse_lines(ops, &body) != 0) {
592
0
      LM_ERR("oom\n");
593
0
      return pv_get_null(msg, param, res);
594
0
    }
595
0
  } else {
596
0
    ops = msg->sdp_ops;
597
0
  }
598
599
0
  idx = sdp_ops_find_line(msg, ops, IDX(msg, &pvp->match_line.idx), 0, NULL,
600
0
          &pvp->match_line.prefix, &pvp->match_token.prefix);
601
0
  if (idx < 0 || idx >= ops->lines_sz)    // out of bounds
602
0
    return pv_get_null(msg, param, res);
603
604
0
  line = ops->lines[idx].line;
605
606
0
  if (!pvp->match_token.prefix.s)
607
0
    return pv_get_strval(msg, param, res, &line);
608
609
0
  idx = IDX(msg, &pvp->match_token.idx);
610
0
  pfx = pvp->match_token.prefix;
611
0
  start = line.s;
612
0
  lim = line.s + line.len;
613
0
  while (start < lim && is_ws(*start))
614
0
    start++;
615
616
0
  for (p = start; p <= lim; p++) {
617
0
    if (p < lim && !is_ws(*p))
618
0
      continue;
619
620
    /* have prefix and doesn't match current token => skip token */
621
0
    if (pfx.len && (pfx.len > (p-start) || strncasecmp(pfx.s, start, pfx.len))) {
622
0
      while (p < lim && is_ws(*p))
623
0
        p++;
624
0
      start = p;
625
0
      continue;
626
0
    }
627
628
0
    if (idx > 0) {
629
0
      idx--;
630
0
      start = p;
631
0
      while (start < lim && is_ws(*start))
632
0
        start++;
633
0
      continue;
634
0
    }
635
636
0
    token.s = start;
637
0
    token.len = p - start;
638
0
    break;
639
0
  }
640
641
0
  if (!token.s)
642
0
    return pv_get_null(msg, param, res);
643
644
0
  return pv_get_strval(msg, param, res, &token);
645
0
}
646
647
648
int pv_set_sdp_line(struct sip_msg *msg, pv_param_t *param,
649
      int op, pv_value_t *val)
650
0
{
651
0
  struct sdp_pv_param *pvp = (struct sdp_pv_param *)param->pvn.u.dname;
652
0
  struct sip_msg_body *sbody;
653
0
  struct sdp_body_part_ops *ops;
654
0
  struct body_part *body_part;
655
0
  str body, dup_line, src_line;
656
0
  int idx, insert = 0;
657
658
0
  if (!msg)
659
0
    return -1;
660
661
0
  if (val && !(val->flags & PV_VAL_STR)) {
662
0
    LM_ERR("refusing to set SDP line to non-string value (val flags: %d)\n",
663
0
                val->flags);
664
0
    return -1;
665
0
  }
666
667
0
  if (msg->body_lumps) {
668
    /* TODO: rebuild SDP body, clear the body lumps; assert lines_sz == 0 */
669
0
  }
670
671
0
  if (!have_sdp_ops(msg) || msg->sdp_ops->lines_sz == 0) {
672
0
    if (parse_sip_body(msg)<0 || !(sbody=msg->body)) {
673
0
      LM_ERR("current SIP message has no SDP body!\n");
674
0
      return -1;
675
0
    }
676
677
0
    first_part_by_mime( &sbody->first, body_part, (TYPE_APPLICATION<<16)+SUBTYPE_SDP );
678
0
    if (!body_part) {
679
0
      LM_ERR("current SIP message has a body, but no SDP part!\n");
680
0
      return -1;
681
0
    }
682
683
0
    ops = msg->sdp_ops;
684
    /* first time working with SDP ops => allocate DS */
685
0
    if (!ops && !(ops = msg->sdp_ops = mk_sdp_ops())) {
686
0
      LM_ERR("oom\n");
687
0
      return -1;
688
0
    }
689
690
0
    body = body_part->body;
691
0
    if (ops->lines_sz == 0 && sdp_ops_parse_lines(ops, &body) != 0) {
692
0
      LM_ERR("oom\n");
693
0
      return -1;
694
0
    }
695
0
  } else {
696
0
    ops = msg->sdp_ops;
697
0
  }
698
699
0
  idx = IDX(msg, &pvp->match_line.idx);
700
0
  switch (param->pvi.type) {
701
0
  case SDP_PV_IDX_INSERT:
702
0
    insert = 1;
703
0
    break;
704
705
0
  case SDP_PV_IDX_AINSERT:
706
0
    insert = 1;
707
0
    idx++; /* convert it to "INSERT" operation */
708
0
    break;
709
710
0
  default:
711
0
    break;
712
0
  }
713
714
0
  idx = sdp_ops_find_line(msg, ops, idx, 0, NULL, &pvp->match_line.prefix,
715
0
                               &pvp->match_token.prefix);
716
0
  if (idx < 0) {
717
0
    LM_ERR("failed to locate SDP line for writing for line %d, match_token: "
718
0
            "'%.*s'\n", IDX(msg, &pvp->match_line.idx),
719
0
            pvp->match_line.prefix.len, pvp->match_line.prefix.s);
720
0
    return -1;
721
0
  }
722
723
0
  if (pvp->match_token.prefix.s)
724
0
    goto handle_token_edit;
725
726
  /* delete line operation -> ignore the index */
727
0
  if (!val) {
728
0
    if (idx == ops->lines_sz) {
729
0
      LM_ERR("index out of bounds (trying to delete SDP line %d, have %d lines)\n",
730
0
              idx, ops->lines_sz);
731
0
      return -1;
732
0
    }
733
734
0
    if (ops->lines[idx].newbuf)
735
0
      pkg_free(ops->lines[idx].line.s);
736
737
0
    memmove(&ops->lines[idx], &ops->lines[idx+1], (ops->lines_sz-idx-1)*sizeof *ops->lines);
738
0
    ops->lines[idx].have_gap = 1;
739
0
    ops->lines_sz--;
740
0
    goto out_success;
741
0
  }
742
743
  /* trim any trailing \n, \r or \r\n from the input */
744
0
  src_line = val->rs;
745
0
  if (src_line.len > 0 && (src_line.s[src_line.len-1] == '\n' || src_line.s[src_line.len-1] == '\r')) {
746
0
    src_line.len--;
747
0
    if (src_line.len > 0 && src_line.s[src_line.len] == '\n' && src_line.s[src_line.len-1] == '\r')
748
0
      src_line.len--;
749
0
  }
750
751
0
  if (pkg_str_dup(&dup_line, &src_line) != 0) {
752
0
    LM_ERR("oom\n");
753
0
    return -1;
754
0
  }
755
756
0
  if (insert) {
757
    /* insert line operation */
758
0
    memmove(&ops->lines[idx+1], &ops->lines[idx], (ops->lines_sz-idx)*sizeof *ops->lines);
759
0
    ops->lines_sz++;
760
0
  } else {
761
    /* edit line operation -> ignore the PV index */
762
0
    if (ops->lines[idx].newbuf)
763
0
      pkg_free(ops->lines[idx].line.s);
764
0
  }
765
766
0
  ops->lines[idx].line = dup_line;
767
0
  ops->lines[idx].newbuf = 1;
768
0
  goto out_success;
769
770
0
handle_token_edit:
771
  
772
0
out_success:
773
0
  ops->flags |= SDP_OPS_FL_DIRTY;
774
0
  return 0;
775
0
}
776
777
778
int pv_parse_sdp_line_name(pv_spec_p sp, const str *_in)
779
0
{
780
0
  str in = *_in, tok;
781
0
  int escape = 0, i, midx = 0;
782
0
  struct sdp_pv_param *param;
783
0
  struct sdp_chunk_match *matches[3];
784
0
  char *p;
785
786
0
  if (!sp)
787
0
    return -1;
788
789
0
  if (!in.s || in.len == 0)
790
0
    goto done;
791
792
0
  if (in.s[0] == PV_MARKER) {
793
0
    LM_ERR("no support for dynamic names in $sdp.line\n");
794
0
    return -1;
795
0
  } else if (in.s[0] == '@') {
796
    // TODO: impl custom SDP holders (perhaps using a map)
797
0
    return -1;
798
0
  }
799
800
0
  param = pkg_malloc(sizeof *param);
801
0
  if (!param) {
802
0
    LM_ERR("oom\n");
803
0
    return -1;
804
0
  }
805
0
  memset(param, 0, sizeof *param);
806
807
0
  matches[0] = &param->match_line;
808
0
  matches[1] = &param->match_token;
809
0
  matches[2] = NULL;
810
811
0
  tok.s = in.s;
812
0
  for (i = 0; i < in.len; i++) {
813
0
    if (!matches[midx])
814
0
      break;
815
816
0
    if (escape && (in.s[i] == '\\' || in.s[i] == '/')) {
817
0
      memmove(&in.s[i-1], &in.s[i], in.len - i);
818
0
      in.len--;
819
0
      i--;
820
0
      escape = 0;
821
0
      continue;
822
0
    }
823
824
0
    if (in.s[i] == '\\') {
825
0
      escape = 1;
826
0
      continue;
827
0
    }
828
0
    escape = 0;
829
830
0
    if (in.s[i] == '[' || in.s[i] == '/') {
831
0
      tok.len = i - (tok.s - in.s);
832
0
      trim_leading(&tok);
833
834
0
      if (in.s[i] == '[') {
835
0
        p = parse_sdp_pv_index(in.s + i + 1, in.len - i - 1, &matches[midx]->idx);
836
0
        if (!p) {
837
0
          LM_ERR("error while parsing index in $sdp name: '%.*s'\n", in.len, in.s);
838
0
          return -1;
839
0
        }
840
841
0
        p = q_memchr(p, '/', in.s + in.len - p);
842
0
        if (!p) {
843
0
          matches[midx++]->prefix = tok;
844
0
          break;
845
0
        }
846
0
      } else {
847
0
        p = in.s + i;
848
0
      }
849
850
      // slash here
851
0
      i = p - in.s;
852
0
      matches[midx++]->prefix = tok;
853
0
      tok.s = in.s + i+1;
854
0
    }
855
856
0
    if ((i+1) == in.len) {
857
0
      tok.len = i+1 - (tok.s - in.s);
858
0
      trim_leading(&tok);
859
0
      matches[midx++]->prefix = tok;
860
0
    }
861
0
  }
862
863
0
  LM_DBG("parse sdp.line name: '%.*s', c1: '%.*s/%p'[%s], c2: '%.*s/%p'[%s], c3: '%.*s/%p'[%s]\n",
864
0
          in.len, in.s,
865
0
      param->match_stream.prefix.len, param->match_stream.prefix.s, param->match_stream.prefix.s, IDX_STR(&param->match_stream.idx),
866
0
      param->match_line.prefix.len, param->match_line.prefix.s, param->match_line.prefix.s, IDX_STR(&param->match_line.idx),
867
0
      param->match_token.prefix.len, param->match_token.prefix.s, param->match_token.prefix.s, IDX_STR(&param->match_token.idx));
868
869
0
  sp->pvp.pvn.type = PV_NAME_PVAR;
870
0
  sp->pvp.pvn.u.dname = param;
871
872
0
done:
873
0
  return 0;
874
0
}
875
876
877
int pv_parse_sdp_stream_name(pv_spec_p sp, const str *_in)
878
0
{
879
0
  str in = *_in, tok;
880
0
  int escape = 0, i, midx = 0;
881
0
  struct sdp_pv_param *param;
882
0
  struct sdp_chunk_match *matches[4];
883
0
  char *p;
884
885
0
  if (!sp)
886
0
    return -1;
887
888
0
  if (!in.s || in.len == 0)
889
0
    goto done;
890
891
0
  if (in.s[0] == PV_MARKER) {
892
0
    LM_ERR("no support for dynamic names in $sdp.line\n");
893
0
    return -1;
894
0
  } else if (in.s[0] == '@') {
895
    // TODO: impl custom SDP holders (perhaps using a map)
896
0
    return -1;
897
0
  }
898
899
0
  param = pkg_malloc(sizeof *param);
900
0
  if (!param) {
901
0
    LM_ERR("oom\n");
902
0
    return -1;
903
0
  }
904
0
  memset(param, 0, sizeof *param);
905
906
0
  matches[0] = &param->match_stream;
907
0
  matches[1] = &param->match_line;
908
0
  matches[2] = &param->match_token;
909
0
  matches[3] = NULL;
910
911
0
  tok.s = in.s;
912
0
  for (i = 0; i < in.len; i++) {
913
0
    if (!matches[midx])
914
0
      break;
915
916
0
    if (escape && (in.s[i] == '\\' || in.s[i] == '/')) {
917
0
      memmove(&in.s[i-1], &in.s[i], in.len - i);
918
0
      in.len--;
919
0
      i--;
920
0
      escape = 0;
921
0
      continue;
922
0
    }
923
924
0
    if (in.s[i] == '\\') {
925
0
      escape = 1;
926
0
      continue;
927
0
    }
928
0
    escape = 0;
929
930
0
    if (in.s[i] == '[' || in.s[i] == '/') {
931
0
      tok.len = i - (tok.s - in.s);
932
0
      trim_leading(&tok);
933
934
0
      if (in.s[i] == '[') {
935
0
        p = parse_sdp_pv_index(in.s + i + 1, in.len - i - 1, &matches[midx]->idx);
936
0
        if (!p) {
937
0
          LM_ERR("error while parsing index in $sdp name: '%.*s'\n", in.len, in.s);
938
0
          return -1;
939
0
        }
940
941
0
        p = q_memchr(p, '/', in.s + in.len - p);
942
0
        if (!p) {
943
0
          matches[midx++]->prefix = tok;
944
0
          break;
945
0
        }
946
0
      } else {
947
0
        p = in.s + i;
948
0
      }
949
950
      // slash here
951
0
      i = p - in.s;
952
0
      matches[midx++]->prefix = tok;
953
0
      tok.s = in.s + i+1;
954
0
    }
955
956
0
    if ((i+1) == in.len) {
957
0
      tok.len = i+1 - (tok.s - in.s);
958
0
      trim_leading(&tok);
959
0
      matches[midx++]->prefix = tok;
960
0
    }
961
0
  }
962
963
0
  LM_DBG("parse sdp.stream name: '%.*s',"
964
0
              " c1: '%.*s/%p'[%s], c2: '%.*s/%p'[%s], c3: '%.*s/%p'[%s]\n",
965
0
          in.len, in.s,
966
0
      param->match_stream.prefix.len, param->match_stream.prefix.s,
967
0
        param->match_stream.prefix.s, IDX_STR(&param->match_stream.idx),
968
0
      param->match_line.prefix.len, param->match_line.prefix.s,
969
0
        param->match_line.prefix.s, IDX_STR(&param->match_line.idx),
970
0
      param->match_token.prefix.len, param->match_token.prefix.s,
971
0
        param->match_token.prefix.s, IDX_STR(&param->match_token.idx));
972
973
0
  sp->pvp.pvn.type = PV_NAME_PVAR;
974
0
  sp->pvp.pvn.u.dname = param;
975
976
0
done:
977
0
  return 0;
978
0
}
979
980
981
int pv_get_sdp_stream(struct sip_msg *msg, pv_param_t *param, pv_value_t *res)
982
0
{
983
0
  struct sdp_pv_param *pvp = (struct sdp_pv_param *)param->pvn.u.dname;
984
0
  struct sdp_body_part_ops *ops;
985
0
  struct sip_msg_body *sbody;
986
0
  struct body_part *body_part;
987
0
  str body, line = STR_NULL, pfx, token = STR_NULL;
988
0
  char *p, *lim, *start;
989
0
  int idx;
990
991
0
  if (!msg || !res)
992
0
    return pv_get_null(msg, param, res);
993
994
0
  if (msg->body_lumps) {
995
    /* TODO: rebuild SDP body, clear the body lumps; assert lines_sz == 0 */
996
0
  }
997
998
0
  if (!have_sdp_ops(msg) || msg->sdp_ops->lines_sz == 0) {
999
0
    if (parse_sip_body(msg)<0 || !(sbody=msg->body)) {
1000
0
      LM_ERR("current SIP message has no SDP body!\n");
1001
0
      return pv_get_null(msg, param, res);
1002
0
    }
1003
1004
0
    first_part_by_mime( &sbody->first, body_part, (TYPE_APPLICATION<<16)+SUBTYPE_SDP );
1005
0
    if (!body_part) {
1006
0
      LM_ERR("current SIP message has a body, but no SDP part!\n");
1007
0
      return pv_get_null(msg, param, res);
1008
0
    }
1009
1010
0
    ops = msg->sdp_ops;
1011
    /* first time working with SDP ops => allocate DS */
1012
0
    if (!ops && !(ops = msg->sdp_ops = mk_sdp_ops())) {
1013
0
      LM_ERR("oom\n");
1014
0
      return pv_get_null(msg, param, res);
1015
0
    }
1016
1017
0
    body = body_part->body;
1018
0
    if (ops->lines_sz == 0 && sdp_ops_parse_lines(ops, &body) != 0) {
1019
0
      LM_ERR("oom\n");
1020
0
      return pv_get_null(msg, param, res);
1021
0
    }
1022
0
  } else {
1023
0
    ops = msg->sdp_ops;
1024
0
  }
1025
1026
0
  idx = sdp_ops_find_line(msg, ops, IDX(msg, &pvp->match_line.idx), 0,
1027
0
        &pvp->match_stream, &pvp->match_line.prefix,
1028
0
              &pvp->match_token.prefix);
1029
0
  if (idx < 0 || idx >= ops->lines_sz)    // out of bounds
1030
0
    return pv_get_null(msg, param, res);
1031
1032
0
  line = ops->lines[idx].line;
1033
1034
0
  if (!pvp->match_token.prefix.s)
1035
0
    return pv_get_strval(msg, param, res, &line);
1036
1037
0
  idx = IDX(msg, &pvp->match_token.idx);
1038
0
  pfx = pvp->match_token.prefix;
1039
0
  start = line.s;
1040
0
  lim = line.s + line.len;
1041
0
  while (start < lim && is_ws(*start))
1042
0
    start++;
1043
1044
0
  for (p = start; p <= lim; p++) {
1045
0
    if (p < lim && !is_ws(*p))
1046
0
      continue;
1047
1048
    /* have prefix and doesn't match current token => skip token */
1049
0
    if (pfx.len && (pfx.len > (p-start) || strncasecmp(pfx.s, start, pfx.len))) {
1050
0
      while (p < lim && is_ws(*p))
1051
0
        p++;
1052
0
      start = p;
1053
0
      continue;
1054
0
    }
1055
1056
0
    if (idx > 0) {
1057
0
      idx--;
1058
0
      start = p;
1059
0
      while (start < lim && is_ws(*start))
1060
0
        start++;
1061
0
      continue;
1062
0
    }
1063
1064
0
    token.s = start;
1065
0
    token.len = p - start;
1066
0
    break;
1067
0
  }
1068
1069
0
  if (!token.s)
1070
0
    return pv_get_null(msg, param, res);
1071
1072
0
  return pv_get_strval(msg, param, res, &token);
1073
0
}
1074
1075
1076
int pv_set_sdp_stream(struct sip_msg *msg, pv_param_t *param,
1077
      int op, pv_value_t *val)
1078
0
{
1079
0
  struct sdp_pv_param *pvp = (struct sdp_pv_param *)param->pvn.u.dname;
1080
0
  struct sip_msg_body *sbody;
1081
0
  struct sdp_body_part_ops *ops;
1082
0
  struct body_part *body_part;
1083
0
  str body, dup_line, src_line;
1084
0
  int idx, insert = 0;
1085
1086
0
  if (!msg)
1087
0
    return -1;
1088
1089
0
  if (val && !(val->flags & PV_VAL_STR)) {
1090
0
    LM_ERR("refusing to set SDP line to non-string value (val flags: %d)\n",
1091
0
                val->flags);
1092
0
    return -1;
1093
0
  }
1094
1095
0
  if (msg->body_lumps) {
1096
    /* TODO: rebuild SDP body, clear the body lumps; assert lines_sz == 0 */
1097
0
  }
1098
1099
0
  if (!have_sdp_ops(msg) || msg->sdp_ops->lines_sz == 0) {
1100
0
    if (parse_sip_body(msg)<0 || !(sbody=msg->body)) {
1101
0
      LM_ERR("current SIP message has no SDP body!\n");
1102
0
      return -1;
1103
0
    }
1104
1105
0
    first_part_by_mime( &sbody->first, body_part, (TYPE_APPLICATION<<16)+SUBTYPE_SDP );
1106
0
    if (!body_part) {
1107
0
      LM_ERR("current SIP message has a body, but no SDP part!\n");
1108
0
      return -1;
1109
0
    }
1110
1111
0
    ops = msg->sdp_ops;
1112
    /* first time working with SDP ops => allocate DS */
1113
0
    if (!ops && !(ops = msg->sdp_ops = mk_sdp_ops())) {
1114
0
      LM_ERR("oom\n");
1115
0
      return -1;
1116
0
    }
1117
1118
0
    body = body_part->body;
1119
0
    if (ops->lines_sz == 0 && sdp_ops_parse_lines(ops, &body) != 0) {
1120
0
      LM_ERR("oom\n");
1121
0
      return -1;
1122
0
    }
1123
0
  } else {
1124
0
    ops = msg->sdp_ops;
1125
0
  }
1126
1127
0
  idx = IDX(msg, &pvp->match_line.idx);
1128
0
  switch (param->pvi.type) {
1129
0
  case SDP_PV_IDX_INSERT:
1130
0
    insert = 1;
1131
0
    break;
1132
1133
0
  case SDP_PV_IDX_AINSERT:
1134
0
    insert = 1;
1135
0
    idx++; /* convert it to "INSERT" operation */
1136
0
    break;
1137
1138
0
  default:
1139
0
    break;
1140
0
  }
1141
1142
0
  idx = sdp_ops_find_line(msg, ops, idx, 0, &pvp->match_stream,
1143
0
        &pvp->match_line.prefix, &pvp->match_token.prefix);
1144
0
  if (idx < 0) {
1145
0
    LM_ERR("failed to locate SDP line for writing for line %d, match_token: "
1146
0
            "'%.*s'\n", IDX(msg, &pvp->match_line.idx), pvp->match_line.prefix.len,
1147
0
            pvp->match_line.prefix.s);
1148
0
    return -1;
1149
0
  }
1150
1151
0
  if (pvp->match_token.prefix.s)
1152
0
    goto handle_token_edit;
1153
1154
  /* delete line operation -> ignore the index */
1155
0
  if (!val) {
1156
0
    if (idx == ops->lines_sz) {
1157
0
      LM_ERR("index out of bounds (trying to delete SDP line %d, have %d lines)\n",
1158
0
              idx, ops->lines_sz);
1159
0
      return -1;
1160
0
    }
1161
1162
0
    if (ops->lines[idx].newbuf) {
1163
0
      pkg_free(ops->lines[idx].line.s);
1164
0
      memset(&ops->lines[idx], 0, sizeof ops->lines[idx]);
1165
0
    }
1166
1167
0
    memmove(&ops->lines[idx], &ops->lines[idx+1], (ops->lines_sz-idx-1)*sizeof *ops->lines);
1168
0
    ops->lines[idx].have_gap = 1;
1169
0
    ops->lines_sz--;
1170
0
    goto out_success;
1171
0
  }
1172
1173
  /* trim any trailing \n, \r or \r\n from the input */
1174
0
  src_line = val->rs;
1175
0
  if (src_line.len > 0 && (src_line.s[src_line.len-1] == '\n' || src_line.s[src_line.len-1] == '\r')) {
1176
0
    src_line.len--;
1177
0
    if (src_line.len > 0 && src_line.s[src_line.len] == '\n' && src_line.s[src_line.len-1] == '\r')
1178
0
      src_line.len--;
1179
0
  }
1180
1181
0
  if (pkg_str_dup(&dup_line, &src_line) != 0) {
1182
0
    LM_ERR("oom\n");
1183
0
    return -1;
1184
0
  }
1185
1186
0
  if (insert) {
1187
    /* insert line operation */
1188
0
    memmove(&ops->lines[idx+1], &ops->lines[idx], (ops->lines_sz-idx)*sizeof *ops->lines);
1189
0
    ops->lines_sz++;
1190
0
  } else {
1191
    /* edit line operation -> ignore the PV index */
1192
0
    if (ops->lines[idx].newbuf)
1193
0
      pkg_free(ops->lines[idx].line.s);
1194
0
  }
1195
1196
0
  ops->lines[idx].line = dup_line;
1197
0
  ops->lines[idx].newbuf = 1;
1198
0
  goto out_success;
1199
1200
0
handle_token_edit:
1201
  
1202
0
out_success:
1203
0
  ops->flags |= SDP_OPS_FL_DIRTY;
1204
0
  return 0;
1205
0
}
1206
1207
1208
int pv_get_sdp_stream_idx(struct sip_msg *msg, pv_param_t *param, pv_value_t *res)
1209
0
{
1210
0
  struct sdp_pv_param *pvp = (struct sdp_pv_param *)param->pvn.u.dname;
1211
0
  struct sdp_body_part_ops *ops;
1212
0
  struct sip_msg_body *sbody;
1213
0
  struct body_part *body_part;
1214
0
  str body;
1215
0
  int idx;
1216
1217
0
  if (!msg || !res)
1218
0
    return pv_get_null(msg, param, res);
1219
1220
0
  if (msg->body_lumps) {
1221
    /* TODO: rebuild SDP body, clear the body lumps; assert lines_sz == 0 */
1222
0
  }
1223
1224
0
  if (!have_sdp_ops(msg) || msg->sdp_ops->lines_sz == 0) {
1225
0
    if (parse_sip_body(msg)<0 || !(sbody=msg->body)) {
1226
0
      LM_ERR("current SIP message has no SDP body!\n");
1227
0
      return pv_get_null(msg, param, res);
1228
0
    }
1229
1230
0
    first_part_by_mime( &sbody->first, body_part, (TYPE_APPLICATION<<16)+SUBTYPE_SDP );
1231
0
    if (!body_part) {
1232
0
      LM_ERR("current SIP message has a body, but no SDP part!\n");
1233
0
      return pv_get_null(msg, param, res);
1234
0
    }
1235
1236
0
    ops = msg->sdp_ops;
1237
    /* first time working with SDP ops => allocate DS */
1238
0
    if (!ops && !(ops = msg->sdp_ops = mk_sdp_ops())) {
1239
0
      LM_ERR("oom\n");
1240
0
      return pv_get_null(msg, param, res);
1241
0
    }
1242
1243
0
    body = body_part->body;
1244
0
    if (ops->lines_sz == 0 && sdp_ops_parse_lines(ops, &body) != 0) {
1245
0
      LM_ERR("oom\n");
1246
0
      return pv_get_null(msg, param, res);
1247
0
    }
1248
0
  } else {
1249
0
    ops = msg->sdp_ops;
1250
0
  }
1251
1252
0
  idx = sdp_ops_find_line(msg, ops, IDX(msg, &pvp->match_line.idx), 0,
1253
0
        &pvp->match_stream, &pvp->match_line.prefix,
1254
0
              &pvp->match_token.prefix);
1255
0
  if (idx < 0 || idx >= ops->lines_sz)    // out of bounds
1256
0
    return pv_get_null(msg, param, res);
1257
1258
0
  return pv_get_sintval(msg, param, res, idx);
1259
0
}
1260
1261
1262
int pv_get_sdp_session(struct sip_msg *msg, pv_param_t *param, pv_value_t *res)
1263
0
{
1264
0
  struct sdp_pv_param *pvp = (struct sdp_pv_param *)param->pvn.u.dname;
1265
0
  struct sdp_body_part_ops *ops;
1266
0
  struct sip_msg_body *sbody;
1267
0
  struct body_part *body_part;
1268
0
  str body, line = STR_NULL, pfx, token = STR_NULL;
1269
0
  char *p, *lim, *start;
1270
0
  int idx;
1271
1272
0
  if (!msg || !res)
1273
0
    return pv_get_null(msg, param, res);
1274
1275
0
  if (msg->body_lumps) {
1276
    /* TODO: rebuild SDP body, clear the body lumps; assert lines_sz == 0 */
1277
0
  }
1278
1279
0
  if (!have_sdp_ops(msg) || msg->sdp_ops->lines_sz == 0) {
1280
0
    if (parse_sip_body(msg)<0 || !(sbody=msg->body)) {
1281
0
      LM_ERR("current SIP message has no SDP body!\n");
1282
0
      return pv_get_null(msg, param, res);
1283
0
    }
1284
1285
0
    first_part_by_mime( &sbody->first, body_part, (TYPE_APPLICATION<<16)+SUBTYPE_SDP );
1286
0
    if (!body_part) {
1287
0
      LM_ERR("current SIP message has a body, but no SDP part!\n");
1288
0
      return pv_get_null(msg, param, res);
1289
0
    }
1290
1291
0
    ops = msg->sdp_ops;
1292
    /* first time working with SDP ops => allocate DS */
1293
0
    if (!ops && !(ops = msg->sdp_ops = mk_sdp_ops())) {
1294
0
      LM_ERR("oom\n");
1295
0
      return pv_get_null(msg, param, res);
1296
0
    }
1297
1298
0
    body = body_part->body;
1299
0
    if (ops->lines_sz == 0 && sdp_ops_parse_lines(ops, &body) != 0) {
1300
0
      LM_ERR("oom\n");
1301
0
      return pv_get_null(msg, param, res);
1302
0
    }
1303
0
  } else {
1304
0
    ops = msg->sdp_ops;
1305
0
  }
1306
1307
0
  idx = sdp_ops_find_line(msg, ops, IDX(msg, &pvp->match_line.idx), 1,
1308
0
        NULL, &pvp->match_line.prefix, &pvp->match_token.prefix);
1309
0
  if (idx < 0 || idx >= ops->lines_sz)    // out of bounds
1310
0
    return pv_get_null(msg, param, res);
1311
1312
0
  line = ops->lines[idx].line;
1313
1314
0
  if (!pvp->match_token.prefix.s)
1315
0
    return pv_get_strval(msg, param, res, &line);
1316
1317
0
  idx = IDX(msg, &pvp->match_token.idx);
1318
0
  pfx = pvp->match_token.prefix;
1319
0
  start = line.s;
1320
0
  lim = line.s + line.len;
1321
0
  while (start < lim && is_ws(*start))
1322
0
    start++;
1323
1324
0
  for (p = start; p <= lim; p++) {
1325
0
    if (p < lim && !is_ws(*p))
1326
0
      continue;
1327
1328
    /* have prefix and doesn't match current token => skip token */
1329
0
    if (pfx.len && (pfx.len > (p-start) || strncasecmp(pfx.s, start, pfx.len))) {
1330
0
      while (p < lim && is_ws(*p))
1331
0
        p++;
1332
0
      start = p;
1333
0
      continue;
1334
0
    }
1335
1336
0
    if (idx > 0) {
1337
0
      idx--;
1338
0
      start = p;
1339
0
      while (start < lim && is_ws(*start))
1340
0
        start++;
1341
0
      continue;
1342
0
    }
1343
1344
0
    token.s = start;
1345
0
    token.len = p - start;
1346
0
    break;
1347
0
  }
1348
1349
0
  if (!token.s)
1350
0
    return pv_get_null(msg, param, res);
1351
1352
0
  return pv_get_strval(msg, param, res, &token);
1353
0
}
1354
1355
1356
int pv_set_sdp_session(struct sip_msg *msg, pv_param_t *param,
1357
      int op, pv_value_t *val)
1358
0
{
1359
0
  struct sdp_pv_param *pvp = (struct sdp_pv_param *)param->pvn.u.dname;
1360
0
  struct sip_msg_body *sbody;
1361
0
  struct sdp_body_part_ops *ops;
1362
0
  struct body_part *body_part;
1363
0
  str body, dup_line, src_line;
1364
0
  int idx, insert = 0;
1365
1366
0
  if (!msg)
1367
0
    return -1;
1368
1369
0
  if (val && !(val->flags & PV_VAL_STR)) {
1370
0
    LM_ERR("refusing to set SDP line to non-string value (val flags: %d)\n",
1371
0
                val->flags);
1372
0
    return -1;
1373
0
  }
1374
1375
0
  if (msg->body_lumps) {
1376
    /* TODO: rebuild SDP body, clear the body lumps; assert lines_sz == 0 */
1377
0
  }
1378
1379
0
  if (!have_sdp_ops(msg) || msg->sdp_ops->lines_sz == 0) {
1380
0
    if (parse_sip_body(msg)<0 || !(sbody=msg->body)) {
1381
0
      LM_ERR("current SIP message has no SDP body!\n");
1382
0
      return -1;
1383
0
    }
1384
1385
0
    first_part_by_mime( &sbody->first, body_part, (TYPE_APPLICATION<<16)+SUBTYPE_SDP );
1386
0
    if (!body_part) {
1387
0
      LM_ERR("current SIP message has a body, but no SDP part!\n");
1388
0
      return -1;
1389
0
    }
1390
1391
0
    ops = msg->sdp_ops;
1392
    /* first time working with SDP ops => allocate DS */
1393
0
    if (!ops && !(ops = msg->sdp_ops = mk_sdp_ops())) {
1394
0
      LM_ERR("oom\n");
1395
0
      return -1;
1396
0
    }
1397
1398
0
    body = body_part->body;
1399
0
    if (ops->lines_sz == 0 && sdp_ops_parse_lines(ops, &body) != 0) {
1400
0
      LM_ERR("oom\n");
1401
0
      return -1;
1402
0
    }
1403
0
  } else {
1404
0
    ops = msg->sdp_ops;
1405
0
  }
1406
1407
0
  idx = IDX(msg, &pvp->match_line.idx);
1408
0
  switch (param->pvi.type) {
1409
0
  case SDP_PV_IDX_INSERT:
1410
0
    insert = 1;
1411
0
    break;
1412
1413
0
  case SDP_PV_IDX_AINSERT:
1414
0
    insert = 1;
1415
0
    idx++; /* convert it to "INSERT" operation */
1416
0
    break;
1417
1418
0
  default:
1419
0
    break;
1420
0
  }
1421
1422
0
  idx = sdp_ops_find_line(msg, ops, idx, 2, NULL,
1423
0
        &pvp->match_line.prefix, &pvp->match_token.prefix);
1424
0
  if (idx < 0) {
1425
0
    LM_ERR("failed to locate SDP line for writing for line %d, match_token: "
1426
0
            "'%.*s'\n", IDX(msg, &pvp->match_line.idx), pvp->match_line.prefix.len,
1427
0
            pvp->match_line.prefix.s);
1428
0
    return -1;
1429
0
  }
1430
1431
0
  if (pvp->match_token.prefix.s)
1432
0
    goto handle_token_edit;
1433
1434
  /* delete line operation -> ignore the index */
1435
0
  if (!val) {
1436
0
    if (idx == ops->lines_sz) {
1437
0
      LM_ERR("index out of bounds (trying to delete SDP line %d, have %d lines)\n",
1438
0
              idx, ops->lines_sz);
1439
0
      return -1;
1440
0
    }
1441
1442
0
    if (ops->lines[idx].newbuf)
1443
0
      pkg_free(ops->lines[idx].line.s);
1444
1445
0
    memmove(&ops->lines[idx], &ops->lines[idx+1], (ops->lines_sz-idx-1)*sizeof *ops->lines);
1446
0
    ops->lines[idx].have_gap = 1;
1447
0
    ops->lines_sz--;
1448
0
    goto out_success;
1449
0
  }
1450
1451
  /* trim any trailing \n, \r or \r\n from the input */
1452
0
  src_line = val->rs;
1453
0
  if (src_line.len > 0 && (src_line.s[src_line.len-1] == '\n' || src_line.s[src_line.len-1] == '\r')) {
1454
0
    src_line.len--;
1455
0
    if (src_line.len > 0 && src_line.s[src_line.len] == '\n' && src_line.s[src_line.len-1] == '\r')
1456
0
      src_line.len--;
1457
0
  }
1458
1459
0
  if (pkg_str_dup(&dup_line, &src_line) != 0) {
1460
0
    LM_ERR("oom\n");
1461
0
    return -1;
1462
0
  }
1463
1464
0
  if (insert) {
1465
    /* insert line operation */
1466
0
    memmove(&ops->lines[idx+1], &ops->lines[idx], (ops->lines_sz-idx)*sizeof *ops->lines);
1467
0
    ops->lines_sz++;
1468
0
  } else {
1469
    /* edit line operation -> ignore the PV index */
1470
0
    if (ops->lines[idx].newbuf)
1471
0
      pkg_free(ops->lines[idx].line.s);
1472
0
  }
1473
1474
0
  ops->lines[idx].line = dup_line;
1475
0
  ops->lines[idx].newbuf = 1;
1476
0
  goto out_success;
1477
1478
0
handle_token_edit:
1479
  
1480
0
out_success:
1481
0
  ops->flags |= SDP_OPS_FL_DIRTY;
1482
0
  return 0;
1483
0
}
1484
1485
1486
int pv_parse_sdp_session_name(pv_spec_p sp, const str *_in)
1487
0
{
1488
0
  str in = *_in, tok;
1489
0
  int escape = 0, i;
1490
0
  enum sdp_pv_name nm = SDP_PV_NAME_P3_LINE;
1491
0
  struct sdp_pv_param *param;
1492
1493
0
  if (!sp)
1494
0
    return -1;
1495
1496
0
  LM_DBG("parse sdp name: '%.*s'\n", in.len, in.s);
1497
0
  trim(&in);
1498
0
  if (!in.s || in.len == 0)
1499
0
    goto done;
1500
1501
0
  if (in.s[0] == PV_MARKER) {
1502
0
    LM_ERR("no support for dynamic names in $sdp.line\n");
1503
0
    return -1;
1504
0
  } else if (in.s[0] == '@') {
1505
    // TODO: impl custom SDP holders (perhaps using a map)
1506
0
    return -1;
1507
0
  }
1508
1509
0
  param = pkg_malloc(sizeof *param);
1510
0
  if (!param) {
1511
0
    LM_ERR("oom\n");
1512
0
    return -1;
1513
0
  }
1514
0
  memset(param, 0, sizeof *param);
1515
1516
0
  tok.s = in.s;
1517
0
  for (i = 0; i < in.len; i++) {
1518
0
    if (escape && (in.s[i] == '\\' || in.s[i] == '/')) {
1519
0
      memmove(&in.s[i-1], &in.s[i], in.len - i);
1520
0
      in.len--;
1521
0
      i--;
1522
0
      escape = 0;
1523
0
      continue;
1524
0
    }
1525
1526
0
    if (in.s[i] == '\\') {
1527
0
      escape = 1;
1528
0
      continue;
1529
0
    }
1530
0
    escape = 0;
1531
1532
0
    if (in.s[i] == '/' && nm <= SDP_PV_NAME_P4_TOKEN) {
1533
0
      tok.len = i - (tok.s - in.s);
1534
      // save tok
1535
0
      switch (nm) {
1536
0
      case SDP_PV_NAME_P1_NAME:
1537
0
      case SDP_PV_NAME_P2_STREAM:
1538
0
      case SDP_PV_NAME_P3_LINE:
1539
0
        param->match_line.prefix = tok;
1540
0
        tok.s = in.s + i + 1;
1541
0
        nm++;
1542
0
        break;
1543
0
      case SDP_PV_NAME_P4_TOKEN:
1544
0
        param->match_token.prefix = tok;
1545
0
        break;
1546
0
      }
1547
1548
0
      continue;
1549
0
    }
1550
0
  }
1551
1552
0
  LM_DBG("parse sdp name: '%.*s'\n", in.len, in.s);
1553
1554
0
  sp->pvp.pvn.type = PV_NAME_PVAR;
1555
0
  sp->pvp.pvn.u.dname = param;
1556
1557
0
done:
1558
0
  return 0;
1559
0
}
1560
1561
1562
int sdp_get_custom_body(struct sip_msg *msg, str *body)
1563
0
{
1564
0
  struct sdp_body_part_ops *ops = msg->sdp_ops;
1565
0
  struct sdp_ops_line *lines;
1566
0
  int i, len = 0, sep_len, rem, cpy_len = 0;
1567
0
  char *p, *start_cpy = NULL;
1568
0
  char *newbuf;
1569
1570
0
  if (!ops)
1571
0
    return -1;
1572
1573
0
  if (ops->flags & SDP_OPS_FL_NULL) {
1574
0
    *body = STR_NULL;
1575
0
    LM_DBG("SDP has been explicitly cleared, returning NULL\n");
1576
0
    return 0;
1577
0
  }
1578
1579
0
  if (!(ops->flags & SDP_OPS_FL_DIRTY)) {
1580
0
    if (!ops->rebuilt_sdp.s && !ops->sdp.s) {
1581
0
      LM_DBG("SDP has been ops-READ, with no changes => using msg SDP\n");
1582
0
      return -1;
1583
0
    }
1584
1585
0
    LM_DBG("found previously re-built custom SDP => quick-return (%d / %d)\n",
1586
0
            !ops->rebuilt_sdp.s, !ops->sdp.s);
1587
0
    goto out;
1588
0
  }
1589
1590
  /* DIRTY flag is on => need to do a full rebuild */
1591
0
  LM_DBG("DIRTY flag detected => rebuild SDP\n");
1592
1593
0
  sep_len = ops->sep_len;
1594
0
  lines = ops->lines;
1595
0
  for (i = 0; i < ops->lines_sz; i++)
1596
0
    len += lines[i].line.len;
1597
0
  len += sep_len * ops->lines_sz;
1598
1599
0
  if (!(newbuf = pkg_malloc(len))) {
1600
0
    LM_ERR("oom\n");
1601
0
    return -1;
1602
0
  }
1603
1604
0
  p = newbuf;
1605
0
  rem = len;
1606
1607
0
  for (i = 0; i < ops->lines_sz; i++) {
1608
0
    if (lines[i].newbuf || lines[i].have_gap) {
1609
0
      if (start_cpy) {
1610
0
        memcpy(p, start_cpy, cpy_len);
1611
0
        p += cpy_len;
1612
0
        rem -= cpy_len;
1613
0
        start_cpy = NULL;
1614
0
        cpy_len = 0;
1615
0
      }
1616
1617
0
      memcpy(p, lines[i].line.s, lines[i].line.len);
1618
0
      p += lines[i].line.len;
1619
0
      rem -= lines[i].line.len;
1620
1621
0
      memcpy(p, ops->sep, sep_len);
1622
0
      p += sep_len;
1623
0
      rem -= sep_len;
1624
1625
0
    } else if (!start_cpy) {
1626
0
      start_cpy = lines[i].line.s;
1627
0
      cpy_len += lines[i].line.len + sep_len;
1628
0
    } else {
1629
0
      cpy_len += lines[i].line.len + sep_len;
1630
0
    }
1631
0
  }
1632
1633
0
  if (start_cpy) {
1634
0
    memcpy(p, start_cpy, cpy_len);
1635
0
    p += cpy_len;
1636
0
    rem -= cpy_len;
1637
0
  }
1638
1639
0
  if (rem != 0) {
1640
0
    LM_BUG("SDP rebuild line mismatch (%d vs. %d), in buffer: '%.*s ...'\n",
1641
0
            len, rem, len > 100 ? 100 : len, ops->sdp.s);
1642
0
    ops->sdp.len = len;
1643
0
  }
1644
1645
0
  ops->flags &= ~SDP_OPS_FL_DIRTY;
1646
1647
0
  pkg_free(ops->rebuilt_sdp.s);
1648
0
  ops->rebuilt_sdp.s = newbuf;
1649
0
  ops->rebuilt_sdp.len = len;
1650
1651
0
out:
1652
0
  if (ops->rebuilt_sdp.s)
1653
0
    *body = ops->rebuilt_sdp;
1654
0
  else
1655
0
    *body = ops->sdp;
1656
0
  return 0;
1657
0
}
1658
1659
1660
void free_sdp_ops_lines(struct sdp_body_part_ops *ops)
1661
0
{
1662
0
  int i;
1663
1664
0
  for (i = 0; i < ops->lines_sz; i++)
1665
0
    if (ops->lines[i].newbuf) {
1666
0
      pkg_free(ops->lines[i].line.s);
1667
0
      ops->lines[i].newbuf = 0;
1668
0
      ops->lines[i].have_gap = 0;
1669
0
    }
1670
1671
0
  ops->lines_sz = 0;
1672
0
  ops->flags &= ~SDP_OPS_FL_NO_LLF;
1673
0
  ops->flags &= ~SDP_OPS_FL_PARSED; /* TODO - optimize with lazy parsing */
1674
0
}
1675
1676
1677
void free_sdp_ops(struct sdp_body_part_ops *ops)
1678
0
{
1679
0
  if (!ops)
1680
0
    return;
1681
1682
0
  free_sdp_ops_lines(ops);
1683
0
  pkg_free(ops->sdp.s);
1684
0
  pkg_free(ops->rebuilt_sdp.s);
1685
0
  pkg_free(ops);
1686
0
}