Coverage Report

Created: 2025-10-10 06:06

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