Coverage Report

Created: 2025-07-18 06:32

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