soa_static.c 37.7 KB
Newer Older
Pekka Pessi's avatar
Pekka Pessi committed
1 2 3
/*
 * This file is part of the Sofia-SIP package
 *
4
 * Copyright (C) 2006 Nokia Corporation.
Pekka Pessi's avatar
Pekka Pessi committed
5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
 *
 * Contact: Pekka Pessi <pekka.pessi@nokia.com>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public License
 * as published by the Free Software Foundation; either version 2.1 of
 * the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA
 *
 */

/**@CFILE soa_static.c
 *
 * @brief Static implementation of Sofia SDP Offer/Answer Engine
 *
 * @author Pekka Pessi <Pekka.Pessi@nokia.com>
 *
 * @date Created: Tue Aug 16 17:06:06 EEST 2005
32 33 34 35 36 37
 *
 * @par Use-cases
 *  1. no existing session
 *    a) generating offer (upgrade with user-SDP)
 *    b) generating answer (upgrade with remote-SDP, rejects with user-SDP)
 *  2. session exists
38
 *    a) generating offer:
39
 *       upgrades with user-SDP
40
 *    b) generating answer:
41
 *       upgrades with remote-SDP, rejects with user-SDP
42
 *    c) processing answer:
43 44 45
 *       rejects with user-SDP, no upgrades
 *
 * Upgrading session with user SDP:
Pekka Pessi's avatar
Pekka Pessi committed
46 47 48 49 50 51 52 53 54 55
 */

#include "config.h"

#include <stddef.h>
#include <stdlib.h>
#include <string.h>

#include <assert.h>

Pekka Pessi's avatar
Pekka Pessi committed
56 57 58 59
struct soa_static_complete;

#define SU_MSG_ARG_T struct soa_static_completed

60 61 62 63 64
#include <sofia-sip/su_wait.h>
#include <sofia-sip/su_tag_class.h>
#include <sofia-sip/su_tag_class.h>
#include <sofia-sip/su_tagarg.h>
#include <sofia-sip/su_strlst.h>
65
#include <sofia-sip/su_string.h>
66
#include <sofia-sip/bnf.h>
Pekka Pessi's avatar
Pekka Pessi committed
67

68 69 70
#include "sofia-sip/soa.h"
#include <sofia-sip/sdp.h>
#include "sofia-sip/soa_session.h"
Pekka Pessi's avatar
Pekka Pessi committed
71 72 73 74

#define NONE ((void *)-1)
#define XXX assert(!"implemented")

Pekka Pessi's avatar
Pekka Pessi committed
75
typedef struct soa_static_session
Pekka Pessi's avatar
Pekka Pessi committed
76
{
Pekka Pessi's avatar
Pekka Pessi committed
77
  soa_session_t sss_session[1];
78
  char *sss_audio_aux;
79 80 81 82 83 84 85
  int sss_ordered_user;  /**< User SDP is ordered */
  int sss_reuse_rejected; /**< Try to reuse rejected media line slots */

  /** Mapping from user SDP m= lines to session SDP m= lines */
  int  *sss_u2s;
  /** Mapping from session SDP m= lines to user SDP m= lines */
  int *sss_s2u;
86

87 88 89 90 91
  /** State kept from SDP before current offer */
  struct {
    int  *u2s, *s2u;
  } sss_previous;

92 93
  /** Our latest offer or answer */
  sdp_session_t *sss_latest;
Pekka Pessi's avatar
Pekka Pessi committed
94 95
}
soa_static_session_t;
Pekka Pessi's avatar
Pekka Pessi committed
96

97 98 99
#define U2S_NOT_USED (-1)
#define U2S_SENTINEL (-2)

Pekka Pessi's avatar
Pekka Pessi committed
100 101
static int soa_static_init(char const *, soa_session_t *, soa_session_t *);
static void soa_static_deinit(soa_session_t *);
102 103
static int soa_static_set_params(soa_session_t *ss, tagi_t const *tags);
static int soa_static_get_params(soa_session_t const *ss, tagi_t *tags);
104
static tagi_t *soa_static_get_paramlist(soa_session_t const *ss,
105
					tag_type_t tag, tag_value_t value,
106
					...);
107
static int soa_static_set_capability_sdp(soa_session_t *ss,
108
				       sdp_session_t *sdp,
109
				       char const *, isize_t);
110
static int soa_static_set_remote_sdp(soa_session_t *ss,
111 112
				   int new_version,
				   sdp_session_t *sdp,
113
				   char const *, isize_t);
114
static int soa_static_set_user_sdp(soa_session_t *ss,
115
				   sdp_session_t *sdp,
116
				   char const *, isize_t);
117 118 119 120 121
static int soa_static_generate_offer(soa_session_t *ss, soa_callback_f *);
static int soa_static_generate_answer(soa_session_t *ss, soa_callback_f *);
static int soa_static_process_answer(soa_session_t *ss, soa_callback_f *);
static int soa_static_process_reject(soa_session_t *ss, soa_callback_f *);

122 123
static int soa_static_activate(soa_session_t *ss, char const *option);
static int soa_static_deactivate(soa_session_t *ss, char const *option);
Pekka Pessi's avatar
Pekka Pessi committed
124
static void soa_static_terminate(soa_session_t *ss, char const *option);
Pekka Pessi's avatar
Pekka Pessi committed
125

126
struct soa_session_actions const soa_default_actions =
Pekka Pessi's avatar
Pekka Pessi committed
127
  {
128
    (sizeof soa_default_actions),
Pekka Pessi's avatar
Pekka Pessi committed
129
    sizeof (struct soa_static_session),
Pekka Pessi's avatar
Pekka Pessi committed
130
    "static",
Pekka Pessi's avatar
Pekka Pessi committed
131 132 133 134 135 136
    soa_static_init,
    soa_static_deinit,
    soa_static_set_params,
    soa_static_get_params,
    soa_static_get_paramlist,
    soa_base_media_features,
137 138
    soa_base_sip_require,
    soa_base_sip_supported,
Pekka Pessi's avatar
Pekka Pessi committed
139
    soa_base_remote_sip_features,
140 141
    soa_static_set_capability_sdp,
    soa_static_set_remote_sdp,
142
    soa_static_set_user_sdp,
Pekka Pessi's avatar
Pekka Pessi committed
143 144 145
    soa_static_generate_offer,
    soa_static_generate_answer,
    soa_static_process_answer,
146
    soa_static_process_reject,
Pekka Pessi's avatar
Pekka Pessi committed
147
    soa_static_activate,
148
    soa_static_deactivate,
Pekka Pessi's avatar
Pekka Pessi committed
149 150 151
    soa_static_terminate
  };

Pekka Pessi's avatar
Pekka Pessi committed
152
/* Initialize session */
Pekka Pessi's avatar
Pekka Pessi committed
153 154
static int soa_static_init(char const *name,
			   soa_session_t *ss,
Pekka Pessi's avatar
Pekka Pessi committed
155 156
			   soa_session_t *parent)
{
Pekka Pessi's avatar
Pekka Pessi committed
157
  return soa_base_init(name, ss, parent);
Pekka Pessi's avatar
Pekka Pessi committed
158 159 160 161
}

static void soa_static_deinit(soa_session_t *ss)
{
Pekka Pessi's avatar
Pekka Pessi committed
162
  soa_base_deinit(ss);
Pekka Pessi's avatar
Pekka Pessi committed
163 164
}

165
static int soa_static_set_params(soa_session_t *ss, tagi_t const *tags)
Pekka Pessi's avatar
Pekka Pessi committed
166
{
167 168
  soa_static_session_t *sss = (soa_static_session_t *)ss;
  char const *audio_aux = sss->sss_audio_aux;
169 170
  int ordered_user = sss->sss_ordered_user;
  int reuse_rejected = sss->sss_reuse_rejected;
171
  int n, m;
172 173 174

  n = tl_gets(tags,
	      SOATAG_AUDIO_AUX_REF(audio_aux),
175 176
	      SOATAG_ORDERED_USER_REF(ordered_user),
	      SOATAG_REUSE_REJECTED_REF(reuse_rejected),
177 178
	      TAG_END());

179
  if (n > 0 && !su_casematch(audio_aux, sss->sss_audio_aux)) {
180 181 182 183 184 185 186 187
    char *s = su_strdup(ss->ss_home, audio_aux), *tbf = sss->sss_audio_aux;
    if (s == NULL && audio_aux != NULL)
      return -1;
    sss->sss_audio_aux = s;
    if (tbf)
      su_free(ss->ss_home, tbf);
  }

188 189 190
  sss->sss_ordered_user = ordered_user != 0;
  sss->sss_reuse_rejected = reuse_rejected != 0;

191 192 193 194 195
  m = soa_base_set_params(ss, tags);
  if (m < 0)
    return m;

  return n + m;
Pekka Pessi's avatar
Pekka Pessi committed
196 197
}

198
static int soa_static_get_params(soa_session_t const *ss, tagi_t *tags)
Pekka Pessi's avatar
Pekka Pessi committed
199
{
200 201
  soa_static_session_t *sss = (soa_static_session_t *)ss;

202
  int n, m;
203 204 205

  n = tl_tgets(tags,
	       SOATAG_AUDIO_AUX(sss->sss_audio_aux),
206 207
	       SOATAG_ORDERED_USER(sss->sss_ordered_user),
	       SOATAG_REUSE_REJECTED(sss->sss_reuse_rejected),
208 209 210 211 212 213
	       TAG_END());
  m = soa_base_get_params(ss, tags);
  if (m < 0)
    return m;

  return n + m;
Pekka Pessi's avatar
Pekka Pessi committed
214 215
}

216
static tagi_t *soa_static_get_paramlist(soa_session_t const *ss,
217
					tag_type_t tag, tag_value_t value,
218
					...)
Pekka Pessi's avatar
Pekka Pessi committed
219
{
220 221
  soa_static_session_t *sss = (soa_static_session_t *)ss;

222 223 224 225 226
  ta_list ta;
  tagi_t *tl;

  ta_start(ta, tag, value);

227 228 229
  tl = soa_base_get_paramlist(ss,
			      TAG_IF(sss->sss_audio_aux,
				     SOATAG_AUDIO_AUX(sss->sss_audio_aux)),
230 231 232 233
			      TAG_IF(sss->sss_ordered_user,
				     SOATAG_ORDERED_USER(1)),
			      TAG_IF(sss->sss_reuse_rejected,
				     SOATAG_REUSE_REJECTED(1)),
234
			      TAG_NEXT(ta_args(ta)));
235 236 237 238

  ta_end(ta);

  return tl;
Pekka Pessi's avatar
Pekka Pessi committed
239 240
}

241
static int soa_static_set_capability_sdp(soa_session_t *ss,
242
					 sdp_session_t *sdp,
243
					 char const *sdp_str,
244
					 isize_t sdp_len)
245 246 247 248 249
{
  return soa_base_set_capability_sdp(ss, sdp, sdp_str, sdp_len);
}


250
static int soa_static_set_remote_sdp(soa_session_t *ss,
251 252
				     int new_version,
				     sdp_session_t *sdp,
253
				     char const *sdp_str,
254
				     isize_t sdp_len)
255 256 257 258 259
{
  return soa_base_set_remote_sdp(ss, new_version, sdp, sdp_str, sdp_len);
}


260
static int soa_static_set_user_sdp(soa_session_t *ss,
261
				   sdp_session_t *sdp,
262
				   char const *sdp_str,
263
				   isize_t sdp_len)
264
{
265
  return soa_base_set_user_sdp(ss, sdp, sdp_str, sdp_len);
266 267
}

268
/** Generate a rejected m= line */
269
static
270
sdp_media_t *soa_sdp_make_rejected_media(su_home_t *home,
271
					 sdp_media_t const *m,
272 273
					 sdp_session_t *sdp,
					 int include_all_codecs)
Pekka Pessi's avatar
Pekka Pessi committed
274
{
275 276 277 278 279 280 281
  sdp_media_t rejected[1] = {{ sizeof (rejected) }};

  rejected->m_type = m->m_type;
  rejected->m_type_name = m->m_type_name;
  rejected->m_port = 0;
  rejected->m_proto = m->m_proto;
  rejected->m_proto_name = m->m_proto_name;
282 283 284

  if (include_all_codecs) {
    rejected->m_rtpmaps = m->m_rtpmaps;
285
  }
286

287
  rejected->m_rejected = 1;
288

289 290 291 292 293
  return sdp_media_dup(home, rejected, sdp);
}

/** Expand a @a truncated SDP.
 */
294
static
295 296 297 298 299 300 301 302 303 304 305 306 307 308 309
sdp_session_t *soa_sdp_expand_media(su_home_t *home,
				    sdp_session_t const *truncated,
				    sdp_session_t const *complete)
{
  sdp_session_t *expanded;
  sdp_media_t **m0;
  sdp_media_t * const *m1;

  expanded = sdp_session_dup(home, truncated);

  if (expanded) {
    for (m0 = &expanded->sdp_media, m1 = &complete->sdp_media;
	 *m1;
	 m1 = &(*m1)->m_next) {
      if (!*m0) {
310
	*m0 = soa_sdp_make_rejected_media(home, *m1, expanded, 0);
311 312 313 314 315 316 317 318 319 320
	if (!*m0)
	  return NULL;
      }
      m0 = &(*m0)->m_next;
    }
  }

  return expanded;
}

321
/** Check if @a session should be upgraded with @a remote */
322
int soa_sdp_upgrade_is_needed(sdp_session_t const *session,
323
			      sdp_session_t const *remote)
324
{
325
  sdp_media_t const *rm, *lm;
326

327
  if (!remote)
328 329 330 331
    return 0;
  if (!session)
    return 1;

332
  for (rm = remote->sdp_media, lm = session->sdp_media;
333 334 335 336 337
       rm && lm ; rm = rm->m_next, lm = lm->m_next) {
    if (rm->m_rejected)
      continue;
    if (lm->m_rejected)
      break;
338 339
  }

340
  return rm != NULL;
341 342
}

343
/** Check if codec is in auxiliary list */
344
static
345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361
int soa_sdp_is_auxiliary_codec(sdp_rtpmap_t const *rm, char const *auxiliary)
{
  char const *codec;
  size_t clen, alen;
  char const *match;

  if (!rm || !rm->rm_encoding || !auxiliary)
    return 0;

  codec = rm->rm_encoding;

  clen = strlen(codec), alen = strlen(auxiliary);

  if (clen > alen)
    return 0;

  for (match = auxiliary;
362
       (match = su_strcasestr(match, codec));
363 364 365 366 367 368 369 370 371 372 373 374
       match = match + 1) {
    if (IS_ALPHANUM(match[clen]) || match[clen] == '-')
      continue;
    if (match != auxiliary &&
	(IS_ALPHANUM(match[-1]) || match[-1] == '-'))
      continue;
    return 1;
  }

  return 0;
}

375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394
static
sdp_rtpmap_t *soa_sdp_media_matching_rtpmap(sdp_rtpmap_t const *from,
					    sdp_rtpmap_t const *anylist,
					    char const *auxiliary)
{
  sdp_rtpmap_t const *rm;

  for (rm = anylist; rm; rm = rm->rm_next) {
    /* Ignore auxiliary codecs */
    if (auxiliary && soa_sdp_is_auxiliary_codec(rm, auxiliary))
      continue;

    if (sdp_rtpmap_find_matching(from, rm))
      return (sdp_rtpmap_t *)rm;
  }

  return NULL;
}

#define SDP_MEDIA_NONE ((sdp_media_t *)-1)
395

396 397
/** Find first matching media in table @a mm.
 *
398
 * - if allow_rtp_mismatch == 0, search for a matching codec
399 400 401 402
 * - if allow_rtp_mismatch == 1, prefer m=line with matching codec
 * - if allow_rtp_mismatch > 1, ignore codecs
 */
static
403
int soa_sdp_matching_mindex(soa_session_t *ss,
404 405 406
			    sdp_media_t *mm[],
			    sdp_media_t const *with,
			    int *return_codec_mismatch)
407
{
408
  int i, j = -1;
409
  soa_static_session_t *sss = (soa_static_session_t *)ss;
410 411
  int rtp = sdp_media_uses_rtp(with), dummy;
  char const *auxiliary = NULL;
412

413 414
  if (return_codec_mismatch == NULL)
    return_codec_mismatch = &dummy;
415

416 417 418 419 420 421
  if (with->m_type == sdp_media_audio) {
    auxiliary = sss->sss_audio_aux;
    /* Looking for a single codec */
    if (with->m_rtpmaps && with->m_rtpmaps->rm_next == NULL)
      auxiliary = NULL;
  }
Pekka Pessi's avatar
Pekka Pessi committed
422

423
  for (i = 0; mm[i]; i++) {
424 425 426
    if (mm[i] == SDP_MEDIA_NONE)
      continue;

427 428
    if (!sdp_media_match_with(mm[i], with))
      continue;
429

430
    if (!rtp)
431 432
      break;

433
    if (soa_sdp_media_matching_rtpmap(with->m_rtpmaps,
434 435
				      mm[i]->m_rtpmaps,
				      auxiliary))
436 437 438 439 440 441
      break;

    if (j == -1)
      j = i;
  }

442 443 444 445
  if (mm[i])
    return *return_codec_mismatch = 0, i;
  else
    return *return_codec_mismatch = 1, j;
446 447 448
}

/** Set payload types in @a l_m according to the values in @a r_m.
449
 *
450 451
 * @retval number of common codecs
 */
452
static
453
int soa_sdp_set_rtpmap_pt(sdp_media_t *l_m,
454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492
			  sdp_media_t const *r_m)
{
  sdp_rtpmap_t *lrm, **next_lrm;
  sdp_rtpmap_t const *rrm;

  int local_codecs = 0, common_codecs = 0;

  unsigned char dynamic_pt[128];
  unsigned pt;

  for (next_lrm = &l_m->m_rtpmaps; (lrm = *next_lrm); ) {
    if (lrm->rm_any) {
      /* Remove codecs known only by pt number */
      *next_lrm = lrm->rm_next;
      continue;
    }
    else {
      next_lrm = &lrm->rm_next;
    }

    local_codecs++;

    rrm = sdp_rtpmap_find_matching(r_m->m_rtpmaps, lrm);

    /* XXX - do fmtp comparison */

    if (rrm) {
      /* Use same payload type as remote */
      if (lrm->rm_pt != rrm->rm_pt) {
	lrm->rm_predef = 0;
	lrm->rm_pt = rrm->rm_pt;
      }
      common_codecs++;
    }
    else {
      /* Determine payload type later */
      lrm->rm_any = 1;
    }
  }
493

494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514
  if (local_codecs == common_codecs)
    return common_codecs;

  /* Select unique dynamic payload type for each payload */

  memset(dynamic_pt, 0, sizeof dynamic_pt);

  for (lrm = l_m->m_rtpmaps; lrm; lrm = lrm->rm_next) {
    if (!lrm->rm_any)
      dynamic_pt[lrm->rm_pt] = 1;
  }

  for (rrm = r_m->m_rtpmaps; rrm; rrm = rrm->rm_next) {
    dynamic_pt[rrm->rm_pt] = 1;
  }

  for (next_lrm = &l_m->m_rtpmaps; (lrm = *next_lrm); ) {
    if (!lrm->rm_any) {
      next_lrm = &lrm->rm_next;
      continue;
    }
515

516 517 518 519 520 521 522 523
    lrm->rm_any = 0;

    pt = lrm->rm_pt;

    if (dynamic_pt[pt]) {
      for (pt = 96; pt < 128; pt++)
        if (!dynamic_pt[pt])
          break;
524

525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547
      if (pt == 128) {
        for (pt = 0; pt < 128; pt++)
          if (!sdp_rtpmap_well_known[pt] && !dynamic_pt[pt])
            break;
      }

      if (pt == 128)  {
        for (pt = 0; pt < 128; pt++)
          if (!dynamic_pt[pt])
            break;
      }

      if (pt == 128) {
        /* Too many payload types */
        *next_lrm = lrm->rm_next;
        continue;
      }

      lrm->rm_pt = pt;
      lrm->rm_predef = 0;
    }

    dynamic_pt[pt] = 1;
548

549 550 551 552 553 554 555
    next_lrm = &lrm->rm_next;
  }

  return common_codecs;
}


556
/** Sort rtpmaps in @a inout_list according to the values in @a rrm.
557 558 559
 *
 * @return Number of common codecs
 */
560
static
561
int soa_sdp_sort_rtpmap(sdp_rtpmap_t **inout_list,
562 563
			sdp_rtpmap_t const *rrm,
			char const *auxiliary)
564 565
{
  sdp_rtpmap_t *sorted = NULL, **next = &sorted, **left;
566
  sdp_rtpmap_t *aux = NULL, **next_aux = &aux;
567 568 569 570 571 572 573

  int common_codecs = 0;

  assert(inout_list);
  if (!inout_list)
    return 0;

574 575 576 577
  /* If remote has only single codec, ignore list of auxiliary codecs */
  if (rrm && !rrm->rm_next)
    auxiliary = NULL;

578 579 580 581 582
  /* Insertion sort from *inout_list to sorted */
  for (; rrm && *inout_list; rrm = rrm->rm_next) {
    for (left = inout_list; *left; left = &(*left)->rm_next) {
      if (sdp_rtpmap_match(rrm, (*left)))
	break;
583
    }
584 585
    if (!*left)
      continue;
586 587 588 589 590 591 592 593

    if (auxiliary && soa_sdp_is_auxiliary_codec(rrm, auxiliary)) {
      *next_aux = *left, next_aux = &(*next_aux)->rm_next;
    }
    else {
      common_codecs++;
      *next = *left; next = &(*next)->rm_next;
    }
594
    *left = (*left)->rm_next;
595
  }
596

597 598 599 600
  /* Append common auxiliary codecs */
  if (aux)
    *next = aux, next = next_aux;

601 602 603 604 605 606
  /* Append leftover codecs */
  *next = *inout_list;

  *inout_list = sorted;

  return common_codecs;
607 608
}

609

610
/** Select rtpmaps in @a inout_list according to the values in @a rrm.
611 612 613
 *
 * @return Number of common codecs
 */
614
static
615
int soa_sdp_select_rtpmap(sdp_rtpmap_t **inout_list,
616 617 618
			  sdp_rtpmap_t const *rrm,
			  char const *auxiliary,
			  int select_single)
619 620
{
  sdp_rtpmap_t **left;
621
  sdp_rtpmap_t *aux = NULL, **next_aux = &aux;
622 623 624 625 626 627 628 629

  int common_codecs = 0;

  assert(inout_list);
  if (!inout_list)
    return 0;

  for (left = inout_list; *left; ) {
630 631
    if (auxiliary && soa_sdp_is_auxiliary_codec(*left, auxiliary))
      /* Insert into list of auxiliary codecs */
632
      *next_aux = *left, *left = (*left)->rm_next,
633 634 635
	next_aux = &(*next_aux)->rm_next;
    else if (!(select_single && common_codecs > 0)
	     && sdp_rtpmap_find_matching(rrm, (*left)))
636 637 638 639 640 641 642
      /* Select */
      left = &(*left)->rm_next, common_codecs++;
    else
      /* Remove */
      *left = (*left)->rm_next;
  }

643 644
  *left = aux, *next_aux = NULL;

645 646 647
  return common_codecs;
}

648

649
/** Sort and select rtpmaps  */
650 651 652 653
static
int soa_sdp_media_upgrade_rtpmaps(soa_session_t *ss,
				  sdp_media_t *sm,
				  sdp_media_t const *rm)
654
{
655
  soa_static_session_t *sss = (soa_static_session_t *)ss;
656 657 658 659 660 661 662 663
  char const *auxiliary = NULL;
  int common_codecs;

  common_codecs = soa_sdp_set_rtpmap_pt(sm, rm);

  if (rm->m_type == sdp_media_audio)
    auxiliary = sss->sss_audio_aux;

664
  if (ss->ss_rtp_sort == SOA_RTP_SORT_REMOTE ||
665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682
      (ss->ss_rtp_sort == SOA_RTP_SORT_DEFAULT &&
       rm->m_mode == sdp_recvonly)) {
    soa_sdp_sort_rtpmap(&sm->m_rtpmaps, rm->m_rtpmaps, auxiliary);
  }

  if (common_codecs == 0)
    ;
  else if (ss->ss_rtp_select == SOA_RTP_SELECT_SINGLE) {
    soa_sdp_select_rtpmap(&sm->m_rtpmaps, rm->m_rtpmaps, auxiliary, 1);
  }
  else if (ss->ss_rtp_select == SOA_RTP_SELECT_COMMON) {
    soa_sdp_select_rtpmap(&sm->m_rtpmaps, rm->m_rtpmaps, auxiliary, 0);
  }

  return common_codecs;
}


683
/** Sort and select rtpmaps within session */
684 685 686 687 688
static
int soa_sdp_session_upgrade_rtpmaps(soa_session_t *ss,
				    sdp_session_t *session,
				    sdp_session_t const *remote)
{
689 690 691
  sdp_media_t *sm;
  sdp_media_t const *rm;

692 693
  for (sm = session->sdp_media, rm = remote->sdp_media;
       sm && rm;
694
       sm = sm->m_next, rm = rm->m_next) {
695 696
    if (!sm->m_rejected && sdp_media_uses_rtp(sm))
      soa_sdp_media_upgrade_rtpmaps(ss, sm, rm);
697 698 699 700 701
  }

  return 0;
}

702
/** Upgrade m= lines within session */
703
static
704 705
int soa_sdp_upgrade(soa_session_t *ss,
		    su_home_t *home,
706
		    sdp_session_t *session,
707 708 709 710
		    sdp_session_t const *user,
		    sdp_session_t const *remote,
		    int **return_u2s,
		    int **return_s2u)
711
{
712 713
  soa_static_session_t *sss = (soa_static_session_t *)ss;

714 715 716 717 718 719 720 721
  int Ns, Nu, Nr, size, i, j;
  sdp_media_t *m, **mm, *um;
  sdp_media_t **s_media, **o_media, **u_media;
  sdp_media_t const *rm, **r_media;
  int *u2s = NULL, *s2u = NULL;

  if (session == NULL || user == NULL)
    return (errno = EFAULT), -1;
722 723

  Ns = sdp_media_count(session, sdp_media_any, 0, 0, 0);
724 725
  Nu = sdp_media_count(user, sdp_media_any, 0, 0, 0);
  Nr = sdp_media_count(remote, sdp_media_any, 0, 0, 0);
726

727 728 729 730
  if (remote == NULL)
    size = Ns + Nu + 1;
  else if (Ns < Nr)
    size = Nr + 1;
731 732 733 734
  else
    size = Ns + 1;

  s_media = su_zalloc(home, size * (sizeof *s_media));
735
  o_media = su_zalloc(home, (Ns + 1) * (sizeof *o_media));
736
  u_media = su_zalloc(home, (Nu + 1) * (sizeof *u_media));
737
  r_media = su_zalloc(home, (Nr + 1) * (sizeof *r_media));
738 739
  if (!s_media || !o_media || !u_media || !r_media)
    return -1;
740

741
  um = sdp_media_dup_all(home, user->sdp_media, session);
742
  if (!um && user->sdp_media)
743
    return -1;
744

745 746 747
  u2s = su_alloc(home, (Nu + 1) * sizeof(*u2s));
  s2u = su_alloc(home, size * sizeof(*s2u));
  if (!u2s || !s2u)
748 749
    return -1;

750 751 752 753 754 755 756 757
  for (i = 0; i < Nu; i++)
    u2s[i] = U2S_NOT_USED;
  u2s[i] = U2S_SENTINEL;

  for (i = 0; i <= size; i++)
    s2u[i] = U2S_NOT_USED;
  s2u[i] = U2S_SENTINEL;

758
  for (i = 0, m = session->sdp_media; m && i < Ns; m = m->m_next)
759
    o_media[i++] = m;
760
  assert(i == Ns);
761
  for (i = 0, m = um; m && i < Nu; m = m->m_next)
762 763
    u_media[i++] = m;
  assert(i == Nu);
764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779
  m = remote ? remote->sdp_media : NULL;
  for (i = 0; m && i < Nr; m = m->m_next)
      r_media[i++] = m;
  assert(i == Nr);

  if (sss->sss_ordered_user && sss->sss_u2s) {     /* User SDP is ordered */
    for (j = 0; sss->sss_u2s[j] != U2S_SENTINEL; j++) {
      i = sss->sss_u2s[j];
      if (i == U2S_NOT_USED)
	continue;
      if (j >= Nu) /* lines removed from user SDP */
	continue;
      s_media[i] = u_media[j], u_media[j] = SDP_MEDIA_NONE;
      u2s[j] = i, s2u[i] = j;
    }
  }
780

781
  if (remote) {
782
    /* Update session according to remote */
783 784 785
    for (i = 0; i < Nr; i++) {
      rm = r_media[i];
      m = s_media[i];
786

787 788
      if (!m) {
	int codec_mismatch = 0;
789

790 791 792 793
	if (!rm->m_rejected)
	  j = soa_sdp_matching_mindex(ss, u_media, rm, &codec_mismatch);
	else
	  j = -1;
794

795 796 797 798 799 800 801 802
	if (j == -1) {
	  s_media[i] = soa_sdp_make_rejected_media(home, rm, session, 0);
	  continue;
	}
	else if (codec_mismatch && !ss->ss_rtp_mismatch) {
	  m = soa_sdp_make_rejected_media(home, u_media[j], session, 1);
	  soa_sdp_set_rtpmap_pt(s_media[i] = m, rm);
	  continue;
803
	}
804 805 806

	s_media[i] = m = u_media[j]; u_media[j] = SDP_MEDIA_NONE;
	u2s[j] = i, s2u[i] = j;
807 808
      }

809 810 811 812
      if (sdp_media_uses_rtp(rm))
	soa_sdp_media_upgrade_rtpmaps(ss, m, rm);
    }
  }
813
  else {
814

815 816 817 818 819 820 821 822
    if (sss->sss_ordered_user) {
      /* Update session with unused media in u_media */

      if (!sss->sss_reuse_rejected) {
	/* Mark previously used slots */
	for (i = 0; i < Ns; i++) {
	  if (s_media[i])
	    continue;
823
	  s_media[i] =
824 825
	    soa_sdp_make_rejected_media(home, o_media[i], session, 0);
	}
826 827
      }

828 829 830
      for (j = 0; j < Nu; j++) {
	if (u_media[j] == SDP_MEDIA_NONE)
	  continue;
831

832 833 834 835 836 837
	for (i = 0; i < size - 1; i++) {
	  if (s_media[i] == NULL) {
	    s_media[i] = u_media[j], u_media[j] = SDP_MEDIA_NONE;
	    u2s[j] = i, s2u[i] = j;
	    break;
	  }
838
	}
839

840 841
	assert(i != size - 1);
      }
842
    }
843

844
    /* Match unused user media by media types with the existing session */
845
    for (i = 0; i < Ns; i++) {
846 847 848 849 850 851 852 853 854 855 856
      if (s_media[i])
	continue;

      j = soa_sdp_matching_mindex(ss, u_media, o_media[i], NULL);
      if (j == -1) {
	s_media[i] = soa_sdp_make_rejected_media(home, o_media[i], session, 0);
	continue;
      }

      s_media[i] = u_media[j], u_media[j] = SDP_MEDIA_NONE;
      u2s[j] = i, s2u[i] = j;
857
    }
858

859
    /* Here we just append new media at the end */
860 861 862 863 864 865 866
    for (j = 0; j < Nu; j++) {
      if (u_media[j] != SDP_MEDIA_NONE) {
	s_media[i] = u_media[j], u_media[j] = SDP_MEDIA_NONE;
	u2s[j] = i, s2u[i] = j;
	i++;
      }
    }
867 868 869 870 871
    assert(i <= size);
  }

  mm = &session->sdp_media;
  for (i = 0; s_media[i]; i++) {
872
    m = s_media[i]; *mm = m; mm = &m->m_next;
873
  }
874 875
  *mm = NULL;

876 877 878 879 880 881 882 883 884 885 886 887 888 889 890
  s2u[size = i] = U2S_SENTINEL;
  *return_u2s = u2s;
  *return_s2u = s2u;

#ifndef NDEBUG			/* X check */
  for (j = 0; j < Nu; j++) {
    i = u2s[j];
    assert(i == U2S_NOT_USED || s2u[i] == j);
  }
  for (i = 0; i < size; i++) {
    j = s2u[i];
    assert(j == U2S_NOT_USED || u2s[j] == i);
  }
#endif

891 892 893
  return 0;
}

894
static
895 896 897 898 899 900 901 902 903 904 905 906 907 908 909
int *u2s_alloc(su_home_t *home, int const *u2s)
{
  if (u2s) {
    int i, *a;
    for (i = 0; u2s[i] != U2S_SENTINEL; i++)
      ;
    a = su_alloc(home, (i + 1) * (sizeof *u2s));
    if (a)
      memcpy(a, u2s, (i + 1) * (sizeof *u2s));
    return a;
  }

  return NULL;
}

910
/** Check if @a session contains media that are rejected by @a remote. */
911
static
912 913
int soa_sdp_reject_is_needed(sdp_session_t const *session,
			     sdp_session_t const *remote)
914 915 916 917 918 919 920 921
{
  sdp_media_t const *sm, *rm;

  if (!remote)
    return 1;
  if (!session)
    return 0;

922
  for (sm = session->sdp_media, rm = remote->sdp_media;
923
       sm && rm; sm = sm->m_next, rm = rm->m_next) {
924 925 926 927 928
    if (rm->m_rejected) {
      if (!sm->m_rejected)
	return 1;
    }
    else {
929
      /* Mode bits do not match */
930 931
      if (((rm->m_mode & sdp_recvonly) == sdp_recvonly)
	  != ((sm->m_mode & sdp_sendonly) == sdp_sendonly))
932 933
	return 1;
    }
934 935 936 937 938 939 940 941
  }

  if (sm)
    return 1;

  return 0;
}

942
/** If m= line is rejected by remote mark m= line rejected within session */
943
static
944 945 946 947 948 949 950
int soa_sdp_reject(su_home_t *home,
		   sdp_session_t *session,
		   sdp_session_t const *remote)
{
  sdp_media_t *sm;
  sdp_media_t const *rm;

951
  if (!session || !session->sdp_media || !remote)
952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973
    return 0;

  rm = remote->sdp_media;

  for (sm = session->sdp_media; sm; sm = sm->m_next) {
    if (!rm || rm->m_rejected) {
      sm->m_rejected = 1;
      sm->m_mode = 0;
      sm->m_port = 0;
      sm->m_number_of_ports = 1;
      if (sm->m_format)
	sm->m_format->l_next = NULL;
      if (sm->m_rtpmaps)
	sm->m_rtpmaps->rm_next = NULL;
      sm->m_information = NULL;
      if (sm->m_connections)
	sm->m_connections->c_next = NULL;
      sm->m_bandwidths = NULL;
      sm->m_key = NULL;
      sm->m_attributes = NULL;
      sm->m_user = NULL;
    }
974

975 976 977 978 979 980 981
    if (rm)
      rm = rm->m_next;
  }

  return 0;
}

982

983 984 985
/** Update mode within session.
 *
 * @sa soatag_hold
986 987
 *
 * @retval 1 if session was changed (or to be changed, if @a dryrun is nonzero)
988
 */
989
static
990 991 992
int soa_sdp_mode_set(sdp_session_t const *user,
		     int const *s2u,
		     sdp_session_t *session,
993
		     sdp_session_t const *remote,
994 995
		     char const *hold,
		     int dryrun)
996 997
{
  sdp_media_t *sm;
998 999
  sdp_media_t const *rm, *rm_next, *um;
  int retval = 0, i, j;
1000
  int hold_all;
1001 1002
  int inactive_all;
  int inactive = 0;
1003
  char const *hold_media = NULL;
1004 1005
  sdp_mode_t send_mode, recv_mode;

Pekka Pessi's avatar
Pekka Pessi committed
1006
  SU_DEBUG_7(("soa_sdp_mode_set(%p, %p, \"%s\"): called\n",
1007
	      (void *)session, (void *)remote, hold ? hold : ""));
Pekka Pessi's avatar
Pekka Pessi committed
1008

1009 1010 1011 1012 1013
  if (!session || !session->sdp_media)
    return 0;

  rm = remote ? remote->sdp_media : NULL, rm_next = NULL;

1014 1015
  hold_all = su_strmatch(hold, "*");
  inactive_all = su_strmatch(hold, "#");
1016

1017 1018 1019
  i = 0;

  for (sm = session->sdp_media; sm; sm = sm->m_next, rm = rm_next, i++) {
1020
    rm_next = rm ? rm->m_next : NULL;
1021
    inactive = 0;
1022 1023 1024 1025

    if (sm->m_rejected)
      continue;

1026 1027 1028 1029
    assert(s2u);

    for (j = 0, um = user->sdp_media; j != s2u[i]; um = um->m_next, j++)
      assert(um);
Pekka Pessi's avatar
Pekka Pessi committed
1030
    if (um == NULL) {
1031 1032 1033 1034
      if (dryrun)
	return 1;
      else
	retval = 1;
Pekka Pessi's avatar
Pekka Pessi committed
1035 1036
      sm->m_rejected = 1;
      sm->m_mode = sdp_inactive;
1037
      sm->m_port = 0;
Pekka Pessi's avatar
Pekka Pessi committed
1038 1039
      continue;
    }
1040 1041

    send_mode = um->m_mode & sdp_sendonly;
1042 1043 1044
    if (rm)
      send_mode = (rm->m_mode & sdp_recvonly) ? sdp_sendonly : 0;

1045
    recv_mode = um->m_mode & sdp_recvonly;
1046 1047 1048 1049 1050 1051 1052 1053 1054 1055

    if (rm && rm->m_mode == sdp_inactive) {
      send_mode = recv_mode = 0;
    }
    else if (inactive_all) {
      send_mode = recv_mode = 0;
    }
    else if (hold_all) {
      recv_mode = 0;
    }
1056
    else if (hold && (hold_media = su_strcasestr(hold, sm->m_type_name))) {
1057 1058 1059 1060 1061
      recv_mode = 0;
      hold_media += strlen(sm->m_type_name);
      hold_media += strspn(hold_media, " \t");
      if (hold_media[0] == '=') {
	hold_media += strspn(hold, " \t");
1062
	if (su_casenmatch(hold_media, "inactive", strlen("inactive")))
1063
	  recv_mode = send_mode = 0;
1064 1065
      }
    }
1066

1067 1068 1069 1070 1071
    if (sm->m_mode != (unsigned)(recv_mode | send_mode)) {
      if (dryrun)
	return 1;
      else
	retval = 1;
1072 1073
      sm->m_mode = recv_mode | send_mode;
    }
1074 1075
  }

1076
  return retval;
1077 1078
}

1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092
enum offer_answer_action {
  generate_offer,
  generate_answer,
  process_answer
};

/**
 * Updates the modified copy of local SDP based
 * on application provided local SDP and remote SDP.
 */
static int offer_answer_step(soa_session_t *ss,
			     enum offer_answer_action action,
			     char const *by)
{
1093 1094
  soa_static_session_t *sss = (soa_static_session_t *)ss;

1095
  char c_address[64];
1096
  sdp_session_t *local = ss->ss_local->ssd_sdp;
1097
  sdp_session_t local0[1];
Pekka Pessi's avatar
Pekka Pessi committed
1098

1099 1100
  sdp_session_t *user = ss->ss_user->ssd_sdp;
  unsigned user_version = ss->ss_user_version;
Pekka Pessi's avatar
Pekka Pessi committed
1101

1102 1103 1104 1105 1106 1107
  sdp_session_t *remote = ss->ss_remote->ssd_sdp;
  unsigned remote_version = ss->ss_remote_version;

  sdp_origin_t o[1] = {{ sizeof(o) }};
  sdp_connection_t *c, c0[1] = {{ sizeof(c0) }};
  sdp_time_t t[1] = {{ sizeof(t) }};
1108

1109 1110
  int *u2s = NULL, *s2u = NULL, *tbf;

1111 1112
  sdp_session_t *latest = NULL, *previous = NULL;

1113 1114 1115 1116 1117
  char const *phrase = "Internal Media Error";

  su_home_t tmphome[SU_HOME_AUTO_SIZE(8192)];

  su_home_auto(tmphome, sizeof tmphome);
1118

1119 1120
  SU_DEBUG_7(("soa_static_offer_answer_action(%p, %s): called\n",
	      (void *)ss, by));
Pekka Pessi's avatar
Pekka Pessi committed
1121

1122 1123 1124
  if (user == NULL)
    return soa_set_status(ss, 500, "No session set by user");

1125 1126
  if (action == generate_offer)
    remote = NULL;
1127 1128
  else if (remote == NULL)
    return soa_set_status(ss, 500, "No remote SDP");
1129 1130 1131

  /* Pre-negotiation Step: Expand truncated remote SDP */
  if (local && remote) switch (action) {
1132 1133
  case generate_answer:
  case process_answer:
1134
    if (sdp_media_count(remote, sdp_media_any, "*", 0, 0) <
1135
	sdp_media_count(local, sdp_media_any, "*", 0, 0)) {
Pekka Pessi's avatar
Pekka Pessi committed
1136
      SU_DEBUG_5(("%s: remote %s is truncated: expanding\n",
1137 1138
		  by, action == generate_answer ? "offer" : "answer"));
      remote = soa_sdp_expand_media(tmphome, remote, local);
1139 1140
      if (remote == NULL)
	return soa_set_status(ss, 500, "Cannot expand remote session");
1141
    }
1142 1143 1144
  default:
    break;
  }
1145

1146
  /* Step A: Create local SDP session (based on user-supplied SDP) */
1147
  if (local == NULL) switch (action) {
1148 1149
  case generate_offer:
  case generate_answer:
1150 1151
    SU_DEBUG_7(("soa_static(%p, %s): %s\n", (void *)ss, by,
		"generating local description"));
Pekka Pessi's avatar
Pekka Pessi committed
1152

1153 1154 1155 1156 1157 1158 1159
    local = local0;
    *local = *user, local->sdp_media = NULL;

    if (local->sdp_origin) {
      o->o_username = local->sdp_origin->o_username;
      /* o->o_address = local->sdp_origin->o_address; */
    }
1160
    if (!o->o_address)
1161
      o->o_address = c0;
1162
    local->sdp_origin = o;
1163 1164

    if (soa_init_sdp_origin(ss, o, c_address) < 0) {
1165 1166
      phrase = "Cannot Get IP Address for Media";
      goto internal_error;
1167
    }
Pekka Pessi's avatar
Pekka Pessi committed
1168

1169
    break;
1170

1171 1172
  case process_answer:
  default:
1173
    goto internal_error;
1174
  }
1175

1176 1177 1178 1179
  /* Step B: upgrade local SDP (add m= lines to it)  */
  switch (action) {
  case generate_offer:
    /* Upgrade local SDP based on user SDP */
1180
    if (local != local0 && ss->ss_local_user_version == user_version)
1181
      break;
1182 1183
    if (local != local0)
      *local0 = *local, local = local0;
1184
    SU_DEBUG_7(("soa_static(%p, %s): %s\n", (void *)ss, by,
1185
		"upgrade with local description"));
1186 1187
    if (soa_sdp_upgrade(ss, tmphome, local, user, NULL, &u2s, &s2u) < 0)
      goto internal_error;
1188 1189 1190
    break;
  case generate_answer:
    /* Upgrade local SDP based on remote SDP */
1191 1192
    if (ss->ss_local_user_version == user_version &&
	ss->ss_local_remote_version == remote_version)
1193
      break;
1194
    if (1) {
1195 1196
      if (local != local0)
	*local0 = *local, local = local0;
1197
      SU_DEBUG_7(("soa_static(%p, %s): %s\n", (void *)ss, by,
1198
		  "upgrade with remote description"));
1199 1200
      if (soa_sdp_upgrade(ss, tmphome, local, user, remote, &u2s, &s2u) < 0)
	goto internal_error;
1201 1202
    }
    break;
1203
  case process_answer:
1204 1205
  default:
    break;
1206
  }
1207

1208

1209 1210 1211 1212 1213 1214 1215 1216 1217
  /* Step C: reject media */
  switch (action) {
  case generate_offer:
    /* Local media is marked as rejected already in upgrade phase */
    break;
  case generate_answer:
  case process_answer:
    if (ss->ss_local_remote_version == remote_version)
      break;
1218 1219
    if (soa_sdp_reject_is_needed(local, remote)) {
      if (local != local0) {
1220
	*local0 = *local, local = local0;
1221 1222 1223 1224 1225 1226 1227 1228 1229 1230
#define DUP_LOCAL(local)					 \
	do {							 \
	  if (!local->sdp_media) break;				 \
	  local->sdp_media =					 \
	    sdp_media_dup_all(tmphome, local->sdp_media, local); \
	  if (!local->sdp_media)				 \
	    goto internal_error;				 \
	} while (0)
	DUP_LOCAL(local);
      }
1231
      SU_DEBUG_7(("soa_static(%p, %s): %s\n", (void *)ss, by,
1232
		  "marking rejected media"));
1233 1234
      soa_sdp_reject(tmphome, local, remote);
    }
1235 1236 1237 1238 1239 1240 1241
    break;
  default:
    break;
  }

  /* Step D: Set media mode bits */
  switch (action) {
1242 1243
    int const *s2u_;

1244 1245 1246
  case generate_offer:
  case generate_answer:
  case process_answer:
1247 1248 1249 1250 1251
    s2u_ = s2u;

    if (!s2u_) s2u_ = sss->sss_s2u;

    if (soa_sdp_mode_set(user, s2u_, local, remote, ss->ss_hold, 1)) {
1252
      if (local != local0) {
1253
	*local0 = *local, local = local0;
1254 1255 1256
	DUP_LOCAL(local);
      }

1257
      soa_sdp_mode_set(user, s2u_, local, remote, ss->ss_hold, 0);
1258 1259
    }
    break;
1260 1261
  default:
    break;
1262
  }
1263

1264
  /* Step E: Upgrade codecs by answer. */
1265 1266 1267 1268 1269 1270
  switch (action) {
  case process_answer:
    /* Upgrade local SDP based on remote SDP */
    if (ss->ss_local_remote_version == remote_version)
      break;
    if (1 /* We don't have good test for codecs */) {
1271
      SU_DEBUG_7(("soa_static(%p, %s): %s\n", (void *)ss, by,
1272 1273
		  "upgrade codecs with remote description"));
      if (local != local0) {
1274
	*local0 = *local, local = local0;
1275 1276
	DUP_LOCAL(local);
      }
1277
      soa_sdp_session_upgrade_rtpmaps(ss, local, remote);
1278 1279 1280
    }
    break;
  case generate_offer:
1281
  case generate_answer:
1282 1283 1284 1285
  default:
    break;
  }

1286
  /* Step F: Update c= line */
Pekka Pessi's avatar
Pekka Pessi committed
1287 1288 1289 1290 1291 1292 1293 1294
  switch (action) {
  case generate_offer:
  case generate_answer:
    /* Upgrade local SDP based of user SDP */
    if (ss->ss_local_user_version == user_version &&
	local->sdp_connection)
      break;

1295 1296
    if (local->sdp_connection == NULL ||
	(user->sdp_connection != NULL &&
Pekka Pessi's avatar
Pekka Pessi committed
1297 1298 1299
	 sdp_connection_cmp(local->sdp_connection, user->sdp_connection))) {
      sdp_media_t *m;

1300
      /* Every m= line (even rejected one) must have a c= line
Pekka Pessi's avatar
Pekka Pessi committed
1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313
       * or there must be a c= line at session level
       */
      if (user->sdp_connection)
	c = user->sdp_connection;
      else
	c = local->sdp_origin->o_address;

      for (m = local->sdp_media; m; m = m->m_next)
	if (m->m_connections == NULL)
	  break;

      if (m) {
	if (local != local0) {
1314
	  *local0 = *local, local = local0;
Pekka Pessi's avatar
Pekka Pessi committed
1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325
	  DUP_LOCAL(local);
	}
	local->sdp_connection = c;
      }
    }
    break;

  default:
    break;
  }

1326
  soa_description_free(ss, ss->ss_previous);
1327 1328
  su_free(ss->ss_home, sss->sss_previous.u2s), sss->sss_previous.u2s = NULL;
  su_free(ss->ss_home, sss->sss_previous.s2u), sss->sss_previous.s2u = NULL;
1329

1330
  if (u2s) {
1331
    u2s = u2s_alloc(ss->ss_home, u2s);
1332 1333 1334 1335 1336
    s2u = u2s_alloc(ss->ss_home, s2u);
    if (!u2s || !s2u)
      goto internal_error;
  }

1337 1338
  if (ss->ss_local->ssd_sdp != local &&
      sdp_session_cmp(ss->ss_local->ssd_sdp, local)) {