| File: | soa_static.c |
| Warning: | line 329, column 15 Value stored to 'mT' is never read |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
| 1 | /* |
| 2 | * This file is part of the Sofia-SIP package |
| 3 | * |
| 4 | * Copyright (C) 2006 Nokia Corporation. |
| 5 | * |
| 6 | * Contact: Pekka Pessi <pekka.pessi@nokia.com> |
| 7 | * |
| 8 | * This library is free software; you can redistribute it and/or |
| 9 | * modify it under the terms of the GNU Lesser General Public License |
| 10 | * as published by the Free Software Foundation; either version 2.1 of |
| 11 | * the License, or (at your option) any later version. |
| 12 | * |
| 13 | * This library is distributed in the hope that it will be useful, but |
| 14 | * WITHOUT ANY WARRANTY; without even the implied warranty of |
| 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| 16 | * Lesser General Public License for more details. |
| 17 | * |
| 18 | * You should have received a copy of the GNU Lesser General Public |
| 19 | * License along with this library; if not, write to the Free Software |
| 20 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA |
| 21 | * 02110-1301 USA |
| 22 | * |
| 23 | */ |
| 24 | |
| 25 | /**@CFILE soa_static.c |
| 26 | * |
| 27 | * @brief Static implementation of Sofia SDP Offer/Answer Engine |
| 28 | * |
| 29 | * @author Pekka Pessi <Pekka.Pessi@nokia.com> |
| 30 | * |
| 31 | * @date Created: Tue Aug 16 17:06:06 EEST 2005 |
| 32 | * |
| 33 | * @par Use-cases |
| 34 | * 1. no existing session |
| 35 | * a) generating offer (upgrade with user-SDP) |
| 36 | * b) generating answer (upgrade with remote-SDP, rejects with user-SDP) |
| 37 | * 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 | * rejects with user-SDP, no upgrades |
| 44 | * |
| 45 | * Upgrading session with user SDP: |
| 46 | */ |
| 47 | |
| 48 | #include "config.h" |
| 49 | |
| 50 | #include <stddef.h> |
| 51 | #include <stdlib.h> |
| 52 | #include <string.h> |
| 53 | |
| 54 | #include <assert.h> |
| 55 | |
| 56 | struct soa_static_complete; |
| 57 | |
| 58 | #define SU_MSG_ARG_Tstruct soa_static_completed struct soa_static_completed |
| 59 | |
| 60 | #include <sofia-sip/su_wait.h> |
| 61 | #include <sofia-sip/su_tag_class.h> |
| 62 | #include <sofia-sip/su_tag_class.h> |
| 63 | #include <sofia-sip/su_tagarg.h> |
| 64 | #include <sofia-sip/su_strlst.h> |
| 65 | #include <sofia-sip/su_string.h> |
| 66 | #include <sofia-sip/bnf.h> |
| 67 | |
| 68 | #include "sofia-sip/soa.h" |
| 69 | #include <sofia-sip/sdp.h> |
| 70 | #include "sofia-sip/soa_session.h" |
| 71 | |
| 72 | #define NONE((void *)-1) ((void *)-1) |
| 73 | #define XXX((void) sizeof ((!"implemented") ? 1 : 0), __extension__ ({ if (!"implemented") ; else __assert_fail ("!\"implemented\"", "soa_static.c" , 73, __extension__ __PRETTY_FUNCTION__); })) assert(!"implemented")((void) sizeof ((!"implemented") ? 1 : 0), __extension__ ({ if (!"implemented") ; else __assert_fail ("!\"implemented\"", "soa_static.c" , 73, __extension__ __PRETTY_FUNCTION__); })) |
| 74 | |
| 75 | typedef struct soa_static_session |
| 76 | { |
| 77 | soa_session_t sss_session[1]; |
| 78 | char *sss_audio_aux; |
| 79 | int sss_ordered_user; /**< User SDP is ordered */ |
| 80 | int sss_reuse_rejected; /**< Try to reuse rejected media line slots */ |
| 81 | |
| 82 | /** Mapping from user SDP m= lines to session SDP m= lines */ |
| 83 | int *sss_u2s; |
| 84 | /** Mapping from session SDP m= lines to user SDP m= lines */ |
| 85 | int *sss_s2u; |
| 86 | |
| 87 | /** State kept from SDP before current offer */ |
| 88 | struct { |
| 89 | int *u2s, *s2u; |
| 90 | } sss_previous; |
| 91 | |
| 92 | /** Our latest offer or answer */ |
| 93 | sdp_session_t *sss_latest; |
| 94 | } |
| 95 | soa_static_session_t; |
| 96 | |
| 97 | #define U2S_NOT_USED(-1) (-1) |
| 98 | #define U2S_SENTINEL(-2) (-2) |
| 99 | |
| 100 | static int soa_static_init(char const *, soa_session_t *, soa_session_t *); |
| 101 | static void soa_static_deinit(soa_session_t *); |
| 102 | static int soa_static_set_params(soa_session_t *ss, tagi_t const *tags); |
| 103 | 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 | int new_version, |
| 112 | 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 | static int soa_static_generate_offer(soa_session_t *ss, soa_callback_f *); |
| 118 | static int soa_static_generate_answer(soa_session_t *ss, soa_callback_f *); |
| 119 | static int soa_static_process_answer(soa_session_t *ss, soa_callback_f *); |
| 120 | static int soa_static_process_reject(soa_session_t *ss, soa_callback_f *); |
| 121 | |
| 122 | static int soa_static_activate(soa_session_t *ss, char const *option); |
| 123 | static int soa_static_deactivate(soa_session_t *ss, char const *option); |
| 124 | static void soa_static_terminate(soa_session_t *ss, char const *option); |
| 125 | |
| 126 | struct soa_session_actions const soa_default_actions = |
| 127 | { |
| 128 | (sizeof soa_default_actions), |
| 129 | sizeof (struct soa_static_session), |
| 130 | "static", |
| 131 | soa_static_init, |
| 132 | soa_static_deinit, |
| 133 | soa_static_set_params, |
| 134 | soa_static_get_params, |
| 135 | soa_static_get_paramlist, |
| 136 | soa_base_media_features, |
| 137 | soa_base_sip_require, |
| 138 | soa_base_sip_supported, |
| 139 | soa_base_remote_sip_features, |
| 140 | soa_static_set_capability_sdp, |
| 141 | soa_static_set_remote_sdp, |
| 142 | soa_static_set_user_sdp, |
| 143 | soa_static_generate_offer, |
| 144 | soa_static_generate_answer, |
| 145 | soa_static_process_answer, |
| 146 | soa_static_process_reject, |
| 147 | soa_static_activate, |
| 148 | soa_static_deactivate, |
| 149 | soa_static_terminate |
| 150 | }; |
| 151 | |
| 152 | /* Initialize session */ |
| 153 | static int soa_static_init(char const *name, |
| 154 | soa_session_t *ss, |
| 155 | soa_session_t *parent) |
| 156 | { |
| 157 | return soa_base_init(name, ss, parent); |
| 158 | } |
| 159 | |
| 160 | static void soa_static_deinit(soa_session_t *ss) |
| 161 | { |
| 162 | soa_base_deinit(ss); |
| 163 | } |
| 164 | |
| 165 | static int soa_static_set_params(soa_session_t *ss, tagi_t const *tags) |
| 166 | { |
| 167 | soa_static_session_t *sss = (soa_static_session_t *)ss; |
| 168 | char const *audio_aux = sss->sss_audio_aux; |
| 169 | int ordered_user = sss->sss_ordered_user; |
| 170 | int reuse_rejected = sss->sss_reuse_rejected; |
| 171 | int n, m; |
| 172 | |
| 173 | n = tl_gets(tags, |
| 174 | SOATAG_AUDIO_AUX_REF(audio_aux)soatag_audio_aux_ref, tag_str_vr(&(audio_aux)), |
| 175 | SOATAG_ORDERED_USER_REF(ordered_user)soatag_ordered_user_ref, tag_bool_vr(&(ordered_user)), |
| 176 | SOATAG_REUSE_REJECTED_REF(reuse_rejected)soatag_reuse_rejected_ref, tag_bool_vr(&(reuse_rejected)), |
| 177 | TAG_END()(tag_type_t)0, (tag_value_t)0); |
| 178 | |
| 179 | if (n > 0 && !su_casematch(audio_aux, sss->sss_audio_aux)) { |
| 180 | char *s = su_strdup(ss->ss_home, audio_aux), *tbf = sss->sss_audio_aux; |
| 181 | if (s == NULL((void*)0) && audio_aux != NULL((void*)0)) |
| 182 | return -1; |
| 183 | sss->sss_audio_aux = s; |
| 184 | if (tbf) |
| 185 | su_free(ss->ss_home, tbf); |
| 186 | } |
| 187 | |
| 188 | sss->sss_ordered_user = ordered_user != 0; |
| 189 | sss->sss_reuse_rejected = reuse_rejected != 0; |
| 190 | |
| 191 | m = soa_base_set_params(ss, tags); |
| 192 | if (m < 0) |
| 193 | return m; |
| 194 | |
| 195 | return n + m; |
| 196 | } |
| 197 | |
| 198 | static int soa_static_get_params(soa_session_t const *ss, tagi_t *tags) |
| 199 | { |
| 200 | soa_static_session_t *sss = (soa_static_session_t *)ss; |
| 201 | |
| 202 | int n, m; |
| 203 | |
| 204 | n = tl_tgets(tags, |
| 205 | SOATAG_AUDIO_AUX(sss->sss_audio_aux)soatag_audio_aux, tag_str_v(sss->sss_audio_aux), |
| 206 | SOATAG_ORDERED_USER(sss->sss_ordered_user)soatag_ordered_user, tag_bool_v(sss->sss_ordered_user), |
| 207 | SOATAG_REUSE_REJECTED(sss->sss_reuse_rejected)soatag_reuse_rejected, tag_bool_v(sss->sss_reuse_rejected), |
| 208 | TAG_END()(tag_type_t)0, (tag_value_t)0); |
| 209 | m = soa_base_get_params(ss, tags); |
| 210 | if (m < 0) |
| 211 | return m; |
| 212 | |
| 213 | return n + m; |
| 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 | ...) |
| 219 | { |
| 220 | soa_static_session_t *sss = (soa_static_session_t *)ss; |
| 221 | |
| 222 | ta_list ta; |
| 223 | tagi_t *tl; |
| 224 | |
| 225 | ta_start(ta, tag, value)do { tag_type_t ta_start__tag = (tag); tag_value_t ta_start__value = (value); __builtin_va_start((ta).ap, (value)); while ((ta_start__tag ) == tag_next && (ta_start__value) != 0) { ta_start__tag = ((tagi_t *)ta_start__value)->t_tag; if (ta_start__tag == tag_null || ta_start__tag == ((void*)0)) break; if (ta_start__tag == tag_next) { ta_start__value = ((tagi_t *)ta_start__value) ->t_value; } else { ta_start__tag = tag_next; break; } } ( ta).tl->t_tag = ta_start__tag; (ta).tl->t_value = ta_start__value ; if (ta_start__tag != ((void*)0) && ta_start__tag != tag_null && ta_start__tag != tag_next) { va_list ta_start__ap ; __builtin_va_copy((ta_start__ap), ((ta).ap)); (ta).tl[1].t_tag = tag_next; (ta).tl[1].t_value = (tag_value_t)tl_vlist(ta_start__ap ); __builtin_va_end(ta_start__ap); } else { (ta).tl[1].t_value = 0; (ta).tl[1].t_value = (tag_value_t)0; } } while(0); |
| 226 | |
| 227 | tl = soa_base_get_paramlist(ss, |
| 228 | TAG_IF(sss->sss_audio_aux,!(sss->sss_audio_aux) ? tag_skip : soatag_audio_aux, tag_str_v (sss->sss_audio_aux) |
| 229 | SOATAG_AUDIO_AUX(sss->sss_audio_aux))!(sss->sss_audio_aux) ? tag_skip : soatag_audio_aux, tag_str_v (sss->sss_audio_aux), |
| 230 | TAG_IF(sss->sss_ordered_user,!(sss->sss_ordered_user) ? tag_skip : soatag_ordered_user, tag_bool_v(1) |
| 231 | SOATAG_ORDERED_USER(1))!(sss->sss_ordered_user) ? tag_skip : soatag_ordered_user, tag_bool_v(1), |
| 232 | TAG_IF(sss->sss_reuse_rejected,!(sss->sss_reuse_rejected) ? tag_skip : soatag_reuse_rejected , tag_bool_v(1) |
| 233 | SOATAG_REUSE_REJECTED(1))!(sss->sss_reuse_rejected) ? tag_skip : soatag_reuse_rejected , tag_bool_v(1), |
| 234 | TAG_NEXT(ta_args(ta))tag_next, (tag_value_t)((ta).tl)); |
| 235 | |
| 236 | ta_end(ta)((((ta).tl[1].t_value) ? (tl_vfree((tagi_t *)((ta).tl[1].t_value ))) : (void)0), (ta).tl[1].t_value = 0, __builtin_va_end((ta) .ap)); |
| 237 | |
| 238 | return tl; |
| 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 | return soa_base_set_capability_sdp(ss, sdp, sdp_str, sdp_len); |
| 247 | } |
| 248 | |
| 249 | |
| 250 | static int soa_static_set_remote_sdp(soa_session_t *ss, |
| 251 | int new_version, |
| 252 | sdp_session_t *sdp, |
| 253 | char const *sdp_str, |
| 254 | isize_t sdp_len) |
| 255 | { |
| 256 | return soa_base_set_remote_sdp(ss, new_version, sdp, sdp_str, sdp_len); |
| 257 | } |
| 258 | |
| 259 | |
| 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 | sdp_session_t *sdp, |
| 273 | int include_all_codecs) |
| 274 | { |
| 275 | sdp_media_t rejected[1] = {{ sizeof (rejected) }}; |
| 276 | |
| 277 | rejected->m_type = m->m_type; |
| 278 | rejected->m_type_name = m->m_type_name; |
| 279 | rejected->m_port = 0; |
| 280 | rejected->m_proto = m->m_proto; |
| 281 | rejected->m_proto_name = m->m_proto_name; |
| 282 | |
| 283 | if (include_all_codecs) { |
| 284 | if (m->m_rtpmaps) { |
| 285 | rejected->m_rtpmaps = m->m_rtpmaps; |
| 286 | } |
| 287 | else if (m->m_format) { |
| 288 | rejected->m_format = m->m_format; |
| 289 | } |
| 290 | } |
| 291 | |
| 292 | rejected->m_rejected = 1; |
| 293 | |
| 294 | return sdp_media_dup(home, rejected, sdp); |
| 295 | } |
| 296 | |
| 297 | /** Expand a @a truncated SDP. |
| 298 | */ |
| 299 | static |
| 300 | sdp_session_t *soa_sdp_expand_media(su_home_t *home, |
| 301 | sdp_session_t const *truncated, |
| 302 | sdp_session_t const *complete) |
| 303 | { |
| 304 | sdp_session_t *tmp_truncated; |
| 305 | sdp_session_t *expanded; |
| 306 | sdp_media_t **mE; |
| 307 | sdp_media_t **mT; |
| 308 | sdp_media_t * const *mC; |
| 309 | |
| 310 | /* Truncated list that we will be reducing */ |
| 311 | tmp_truncated = sdp_session_dup(home, truncated); |
| 312 | /* New resulting list */ |
| 313 | expanded = sdp_session_dup(home, truncated); |
| 314 | |
| 315 | if (expanded) { |
| 316 | expanded->sdp_media = NULL((void*)0); /* Empty the list of medias */ |
| 317 | mE = &expanded->sdp_media; /* Pointer to the beginning of the new list */ |
| 318 | |
| 319 | /* Loop through all items in the complete list to preserve complete list's order */ |
| 320 | for (mC = &complete->sdp_media; *mC; mC = &(*mC)->m_next) { |
| 321 | /* Find the corresponding media in the truncated list */ |
| 322 | if ((mT = sdp_media_exists(tmp_truncated, *mC))) { |
| 323 | /* Copy the corresponding media from the truncated list to the new list */ |
| 324 | *mE = sdp_media_dup(home, *mT, expanded); |
| 325 | if (!*mE) |
| 326 | return NULL((void*)0); |
| 327 | |
| 328 | /* Remove corresponding media from the truncated list */ |
| 329 | mT = &(*mT)->m_next; |
Value stored to 'mT' is never read | |
| 330 | } else { |
| 331 | /* If the corresponding media was not found in the truncated list, add a rejected media */ |
| 332 | *mE = soa_sdp_make_rejected_media(home, *mC, expanded, 0); |
| 333 | if (!*mE) |
| 334 | return NULL((void*)0); |
| 335 | } |
| 336 | |
| 337 | /* Prepare pointer of the new list for a new item */ |
| 338 | mE = &(*mE)->m_next; |
| 339 | } |
| 340 | } |
| 341 | |
| 342 | return expanded; |
| 343 | } |
| 344 | |
| 345 | /** Check if @a session should be upgraded with @a remote */ |
| 346 | int soa_sdp_upgrade_is_needed(sdp_session_t const *session, |
| 347 | sdp_session_t const *remote) |
| 348 | { |
| 349 | sdp_media_t const *rm, *lm; |
| 350 | |
| 351 | if (!remote) |
| 352 | return 0; |
| 353 | if (!session) |
| 354 | return 1; |
| 355 | |
| 356 | for (rm = remote->sdp_media, lm = session->sdp_media; |
| 357 | rm && lm ; rm = rm->m_next, lm = lm->m_next) { |
| 358 | if (rm->m_rejected) |
| 359 | continue; |
| 360 | if (lm->m_rejected) |
| 361 | break; |
| 362 | } |
| 363 | |
| 364 | return rm != NULL((void*)0); |
| 365 | } |
| 366 | |
| 367 | /** Check if codec is in auxiliary list */ |
| 368 | static |
| 369 | int soa_sdp_is_auxiliary_codec(sdp_rtpmap_t const *rm, char const *auxiliary) |
| 370 | { |
| 371 | char const *codec; |
| 372 | size_t clen, alen; |
| 373 | char const *match; |
| 374 | |
| 375 | if (!rm || !rm->rm_encoding || !auxiliary) |
| 376 | return 0; |
| 377 | |
| 378 | codec = rm->rm_encoding; |
| 379 | |
| 380 | clen = strlen(codec), alen = strlen(auxiliary); |
| 381 | |
| 382 | if (clen > alen) |
| 383 | return 0; |
| 384 | |
| 385 | for (match = auxiliary; |
| 386 | (match = su_strcasestr(match, codec)); |
| 387 | match = match + 1) { |
| 388 | if (IS_ALPHANUM(match[clen])(match[clen] && (((match[clen]) >= '0' && ( match[clen]) <= '9') || (match[clen] && ((_bnf_table [(unsigned char)match[clen]] & bnf_alpha))))) || match[clen] == '-') |
| 389 | continue; |
| 390 | if (match != auxiliary && |
| 391 | (IS_ALPHANUM(match[-1])(match[-1] && (((match[-1]) >= '0' && (match [-1]) <= '9') || (match[-1] && ((_bnf_table[(unsigned char)match[-1]] & bnf_alpha))))) || match[-1] == '-')) |
| 392 | continue; |
| 393 | return 1; |
| 394 | } |
| 395 | |
| 396 | return 0; |
| 397 | } |
| 398 | |
| 399 | static |
| 400 | sdp_rtpmap_t *soa_sdp_media_matching_rtpmap(sdp_rtpmap_t const *from, |
| 401 | sdp_rtpmap_t const *anylist, |
| 402 | char const *auxiliary) |
| 403 | { |
| 404 | sdp_rtpmap_t const *rm; |
| 405 | |
| 406 | for (rm = anylist; rm; rm = rm->rm_next) { |
| 407 | /* Ignore auxiliary codecs */ |
| 408 | if (auxiliary && soa_sdp_is_auxiliary_codec(rm, auxiliary)) |
| 409 | continue; |
| 410 | |
| 411 | if (sdp_rtpmap_find_matching(from, rm)) |
| 412 | return (sdp_rtpmap_t *)rm; |
| 413 | } |
| 414 | |
| 415 | return NULL((void*)0); |
| 416 | } |
| 417 | |
| 418 | #ifndef _MSC_VER |
| 419 | #define SDP_MEDIA_NONE((sdp_media_t *)-1) ((sdp_media_t *)-1) |
| 420 | #else |
| 421 | #define SDP_MEDIA_NONE((sdp_media_t *)-1) ((sdp_media_t *)(INT_PTR)-1) |
| 422 | #endif |
| 423 | |
| 424 | /** Find first matching media in table @a mm. |
| 425 | * |
| 426 | * - if allow_rtp_mismatch == 0, search for a matching codec |
| 427 | * - if allow_rtp_mismatch == 1, prefer m=line with matching codec |
| 428 | * - if allow_rtp_mismatch > 1, ignore codecs |
| 429 | */ |
| 430 | static |
| 431 | int soa_sdp_matching_mindex(soa_session_t *ss, |
| 432 | sdp_media_t *mm[], |
| 433 | sdp_media_t const *with, |
| 434 | int *return_codec_mismatch) |
| 435 | { |
| 436 | int i, j = -1; |
| 437 | soa_static_session_t *sss = (soa_static_session_t *)ss; |
| 438 | int rtp = sdp_media_uses_rtp(with), dummy; |
| 439 | char const *auxiliary = NULL((void*)0); |
| 440 | |
| 441 | if (return_codec_mismatch == NULL((void*)0)) |
| 442 | return_codec_mismatch = &dummy; |
| 443 | |
| 444 | if (with->m_type == sdp_media_audio) { |
| 445 | auxiliary = sss->sss_audio_aux; |
| 446 | /* Looking for a single codec */ |
| 447 | if (with->m_rtpmaps && with->m_rtpmaps->rm_next == NULL((void*)0)) |
| 448 | auxiliary = NULL((void*)0); |
| 449 | } |
| 450 | |
| 451 | for (i = 0; mm[i]; i++) { |
| 452 | if (mm[i] == SDP_MEDIA_NONE((sdp_media_t *)-1)) |
| 453 | continue; |
| 454 | |
| 455 | if (!sdp_media_match_with(mm[i], with)) |
| 456 | continue; |
| 457 | |
| 458 | if (!rtp) |
| 459 | break; |
| 460 | |
| 461 | if (soa_sdp_media_matching_rtpmap(with->m_rtpmaps, |
| 462 | mm[i]->m_rtpmaps, |
| 463 | auxiliary)) |
| 464 | break; |
| 465 | |
| 466 | if (j == -1) |
| 467 | j = i; |
| 468 | } |
| 469 | |
| 470 | if (mm[i]) |
| 471 | return *return_codec_mismatch = 0, i; |
| 472 | else |
| 473 | return *return_codec_mismatch = 1, j; |
| 474 | } |
| 475 | |
| 476 | /** Set payload types in @a l_m according to the values in @a r_m. |
| 477 | * |
| 478 | * @retval number of common codecs |
| 479 | */ |
| 480 | static |
| 481 | int soa_sdp_set_rtpmap_pt(sdp_media_t *l_m, |
| 482 | sdp_media_t const *r_m) |
| 483 | { |
| 484 | sdp_rtpmap_t *lrm, **next_lrm; |
| 485 | sdp_rtpmap_t const *rrm; |
| 486 | |
| 487 | int local_codecs = 0, common_codecs = 0; |
| 488 | |
| 489 | unsigned char dynamic_pt[128]; |
| 490 | unsigned pt; |
| 491 | |
| 492 | for (next_lrm = &l_m->m_rtpmaps; (lrm = *next_lrm); ) { |
| 493 | if (lrm->rm_any) { |
| 494 | /* Remove codecs known only by pt number */ |
| 495 | *next_lrm = lrm->rm_next; |
| 496 | continue; |
| 497 | } |
| 498 | else { |
| 499 | next_lrm = &lrm->rm_next; |
| 500 | } |
| 501 | |
| 502 | local_codecs++; |
| 503 | |
| 504 | rrm = sdp_rtpmap_find_matching(r_m->m_rtpmaps, lrm); |
| 505 | |
| 506 | /* XXX - do fmtp comparison */ |
| 507 | |
| 508 | if (rrm) { |
| 509 | #if 0 |
| 510 | /* Use same payload type as remote */ |
| 511 | if (lrm->rm_pt != rrm->rm_pt) { |
| 512 | lrm->rm_predef = 0; |
| 513 | lrm->rm_pt = rrm->rm_pt; |
| 514 | |
| 515 | } |
| 516 | #endif |
| 517 | common_codecs++; |
| 518 | } |
| 519 | else { |
| 520 | /* Determine payload type later */ |
| 521 | lrm->rm_any = 1; |
| 522 | } |
| 523 | } |
| 524 | |
| 525 | if (local_codecs == common_codecs) |
| 526 | return common_codecs; |
| 527 | |
| 528 | /* Select unique dynamic payload type for each payload */ |
| 529 | |
| 530 | memset(dynamic_pt, 0, sizeof dynamic_pt); |
| 531 | |
| 532 | for (lrm = l_m->m_rtpmaps; lrm; lrm = lrm->rm_next) { |
| 533 | if (!lrm->rm_any) |
| 534 | dynamic_pt[lrm->rm_pt] = 1; |
| 535 | } |
| 536 | |
| 537 | for (rrm = r_m->m_rtpmaps; rrm; rrm = rrm->rm_next) { |
| 538 | dynamic_pt[rrm->rm_pt] = 1; |
| 539 | } |
| 540 | |
| 541 | for (next_lrm = &l_m->m_rtpmaps; (lrm = *next_lrm); ) { |
| 542 | if (!lrm->rm_any) { |
| 543 | next_lrm = &lrm->rm_next; |
| 544 | continue; |
| 545 | } |
| 546 | |
| 547 | lrm->rm_any = 0; |
| 548 | |
| 549 | pt = lrm->rm_pt; |
| 550 | |
| 551 | if (dynamic_pt[pt]) { |
| 552 | for (pt = 96; pt < 128; pt++) |
| 553 | if (!dynamic_pt[pt]) |
| 554 | break; |
| 555 | |
| 556 | if (pt == 128) { |
| 557 | for (pt = 0; pt < 128; pt++) |
| 558 | if (!sdp_rtpmap_well_known[pt] && !dynamic_pt[pt]) |
| 559 | break; |
| 560 | } |
| 561 | |
| 562 | if (pt == 128) { |
| 563 | for (pt = 0; pt < 128; pt++) |
| 564 | if (!dynamic_pt[pt]) |
| 565 | break; |
| 566 | } |
| 567 | |
| 568 | if (pt == 128) { |
| 569 | /* Too many payload types */ |
| 570 | *next_lrm = lrm->rm_next; |
| 571 | continue; |
| 572 | } |
| 573 | |
| 574 | lrm->rm_pt = pt; |
| 575 | lrm->rm_predef = 0; |
| 576 | } |
| 577 | |
| 578 | dynamic_pt[pt] = 1; |
| 579 | |
| 580 | next_lrm = &lrm->rm_next; |
| 581 | } |
| 582 | |
| 583 | return common_codecs; |
| 584 | } |
| 585 | |
| 586 | |
| 587 | /** Sort rtpmaps in @a inout_list according to the values in @a rrm. |
| 588 | * |
| 589 | * @return Number of common codecs |
| 590 | */ |
| 591 | static |
| 592 | int soa_sdp_sort_rtpmap(sdp_rtpmap_t **inout_list, |
| 593 | sdp_rtpmap_t const *rrm, |
| 594 | char const *auxiliary) |
| 595 | { |
| 596 | sdp_rtpmap_t *sorted = NULL((void*)0), **next = &sorted, **left; |
| 597 | sdp_rtpmap_t *aux = NULL((void*)0), **next_aux = &aux; |
| 598 | |
| 599 | int common_codecs = 0; |
| 600 | |
| 601 | assert(inout_list)((void) sizeof ((inout_list) ? 1 : 0), __extension__ ({ if (inout_list ) ; else __assert_fail ("inout_list", "soa_static.c", 601, __extension__ __PRETTY_FUNCTION__); })); |
| 602 | if (!inout_list) |
| 603 | return 0; |
| 604 | |
| 605 | /* If remote has only single codec, ignore list of auxiliary codecs */ |
| 606 | if (rrm && !rrm->rm_next) |
| 607 | auxiliary = NULL((void*)0); |
| 608 | |
| 609 | /* Insertion sort from *inout_list to sorted */ |
| 610 | for (; rrm && *inout_list; rrm = rrm->rm_next) { |
| 611 | for (left = inout_list; *left; left = &(*left)->rm_next) { |
| 612 | if (sdp_rtpmap_match(rrm, (*left))) |
| 613 | break; |
| 614 | } |
| 615 | if (!*left) |
| 616 | continue; |
| 617 | |
| 618 | if (auxiliary && soa_sdp_is_auxiliary_codec(rrm, auxiliary)) { |
| 619 | *next_aux = *left, next_aux = &(*next_aux)->rm_next; |
| 620 | } |
| 621 | else { |
| 622 | common_codecs++; |
| 623 | *next = *left; next = &(*next)->rm_next; |
| 624 | } |
| 625 | *left = (*left)->rm_next; |
| 626 | } |
| 627 | |
| 628 | /* Append common auxiliary codecs */ |
| 629 | if (aux) |
| 630 | *next = aux, next = next_aux; |
| 631 | |
| 632 | /* Append leftover codecs */ |
| 633 | *next = *inout_list; |
| 634 | |
| 635 | *inout_list = sorted; |
| 636 | |
| 637 | return common_codecs; |
| 638 | } |
| 639 | |
| 640 | |
| 641 | /** Select rtpmaps in @a inout_list according to the values in @a rrm. |
| 642 | * |
| 643 | * @return Number of common codecs |
| 644 | */ |
| 645 | static |
| 646 | int soa_sdp_select_rtpmap(sdp_rtpmap_t **inout_list, |
| 647 | sdp_rtpmap_t const *rrm, |
| 648 | char const *auxiliary, |
| 649 | int select_single) |
| 650 | { |
| 651 | sdp_rtpmap_t **left; |
| 652 | sdp_rtpmap_t *aux = NULL((void*)0), **next_aux = &aux; |
| 653 | |
| 654 | int common_codecs = 0; |
| 655 | |
| 656 | assert(inout_list)((void) sizeof ((inout_list) ? 1 : 0), __extension__ ({ if (inout_list ) ; else __assert_fail ("inout_list", "soa_static.c", 656, __extension__ __PRETTY_FUNCTION__); })); |
| 657 | if (!inout_list) |
| 658 | return 0; |
| 659 | |
| 660 | for (left = inout_list; *left; ) { |
| 661 | if (auxiliary && soa_sdp_is_auxiliary_codec(*left, auxiliary)) |
| 662 | /* Insert into list of auxiliary codecs */ |
| 663 | *next_aux = *left, *left = (*left)->rm_next, |
| 664 | next_aux = &(*next_aux)->rm_next; |
| 665 | else if (!(select_single && common_codecs > 0) |
| 666 | && sdp_rtpmap_find_matching(rrm, (*left))) |
| 667 | /* Select */ |
| 668 | left = &(*left)->rm_next, common_codecs++; |
| 669 | else |
| 670 | /* Remove */ |
| 671 | *left = (*left)->rm_next; |
| 672 | } |
| 673 | |
| 674 | *left = aux, *next_aux = NULL((void*)0); |
| 675 | |
| 676 | return common_codecs; |
| 677 | } |
| 678 | |
| 679 | |
| 680 | /** Sort and select rtpmaps */ |
| 681 | static |
| 682 | int soa_sdp_media_upgrade_rtpmaps(soa_session_t *ss, |
| 683 | sdp_media_t *sm, |
| 684 | sdp_media_t const *rm) |
| 685 | { |
| 686 | soa_static_session_t *sss = (soa_static_session_t *)ss; |
| 687 | char const *auxiliary = NULL((void*)0); |
| 688 | int common_codecs; |
| 689 | |
| 690 | common_codecs = soa_sdp_set_rtpmap_pt(sm, rm); |
| 691 | |
| 692 | if (rm->m_type == sdp_media_audio) |
| 693 | auxiliary = sss->sss_audio_aux; |
| 694 | |
| 695 | if (ss->ss_rtp_sort == SOA_RTP_SORT_REMOTE || |
| 696 | (ss->ss_rtp_sort == SOA_RTP_SORT_DEFAULT && |
| 697 | rm->m_mode == sdp_recvonly)) { |
| 698 | soa_sdp_sort_rtpmap(&sm->m_rtpmaps, rm->m_rtpmaps, auxiliary); |
| 699 | } |
| 700 | |
| 701 | if (common_codecs == 0) |
| 702 | ; |
| 703 | else if (ss->ss_rtp_select == SOA_RTP_SELECT_SINGLE) { |
| 704 | soa_sdp_select_rtpmap(&sm->m_rtpmaps, rm->m_rtpmaps, auxiliary, 1); |
| 705 | } |
| 706 | else if (ss->ss_rtp_select == SOA_RTP_SELECT_COMMON) { |
| 707 | soa_sdp_select_rtpmap(&sm->m_rtpmaps, rm->m_rtpmaps, auxiliary, 0); |
| 708 | } |
| 709 | |
| 710 | return common_codecs; |
| 711 | } |
| 712 | |
| 713 | |
| 714 | /** Sort and select rtpmaps within session */ |
| 715 | static |
| 716 | int soa_sdp_session_upgrade_rtpmaps(soa_session_t *ss, |
| 717 | sdp_session_t *session, |
| 718 | sdp_session_t const *remote) |
| 719 | { |
| 720 | sdp_media_t *sm; |
| 721 | sdp_media_t const *rm; |
| 722 | |
| 723 | for (sm = session->sdp_media, rm = remote->sdp_media; |
| 724 | sm && rm; |
| 725 | sm = sm->m_next, rm = rm->m_next) { |
| 726 | if (!sm->m_rejected && sdp_media_uses_rtp(sm)) |
| 727 | soa_sdp_media_upgrade_rtpmaps(ss, sm, rm); |
| 728 | } |
| 729 | |
| 730 | return 0; |
| 731 | } |
| 732 | |
| 733 | /** Upgrade m= lines within session */ |
| 734 | static |
| 735 | int soa_sdp_upgrade(soa_session_t *ss, |
| 736 | su_home_t *home, |
| 737 | sdp_session_t *session, |
| 738 | sdp_session_t const *user, |
| 739 | sdp_session_t const *remote, |
| 740 | int **return_u2s, |
| 741 | int **return_s2u) |
| 742 | { |
| 743 | soa_static_session_t *sss = (soa_static_session_t *)ss; |
| 744 | |
| 745 | int Ns, Nu, Nr, Nmax, n, i, j; |
| 746 | sdp_media_t *m, **mm, *um; |
| 747 | sdp_media_t **s_media, **o_media, **u_media; |
| 748 | sdp_media_t const *rm, **r_media; |
| 749 | int *u2s = NULL((void*)0), *s2u = NULL((void*)0); |
| 750 | |
| 751 | if (session == NULL((void*)0) || user == NULL((void*)0)) |
| 752 | return (errno(*__errno_location ()) = EFAULT14), -1; |
| 753 | |
| 754 | #ifdef __clang__1 |
| 755 | #pragma clang diagnostic push |
| 756 | #pragma clang diagnostic ignored "-Wnon-literal-null-conversion" |
| 757 | #endif |
| 758 | |
| 759 | Ns = sdp_media_count(session, sdp_media_any, (sdp_text_t)0, (sdp_proto_e)0, (sdp_text_t)0); |
| 760 | Nu = sdp_media_count(user, sdp_media_any, (sdp_text_t)0, (sdp_proto_e)0, (sdp_text_t)0); |
| 761 | Nr = sdp_media_count(remote, sdp_media_any, (sdp_text_t)0, (sdp_proto_e)0, (sdp_text_t)0); |
| 762 | |
| 763 | #ifdef __clang__1 |
| 764 | #pragma clang diagnostic pop |
| 765 | #endif |
| 766 | |
| 767 | if (remote == NULL((void*)0)) |
| 768 | Nmax = Ns + Nu; |
| 769 | else if (Ns < Nr) |
| 770 | Nmax = Nr; |
| 771 | else |
| 772 | Nmax = Ns; |
| 773 | |
| 774 | s_media = su_zalloc(home, (Nmax + 1) * (sizeof *s_media)); |
| 775 | o_media = su_zalloc(home, (Ns + 1) * (sizeof *o_media)); |
| 776 | u_media = su_zalloc(home, (Nu + 1) * (sizeof *u_media)); |
| 777 | r_media = su_zalloc(home, (Nr + 1) * (sizeof *r_media)); |
| 778 | if (!s_media || !o_media || !u_media || !r_media) |
| 779 | return -1; |
| 780 | |
| 781 | um = sdp_media_dup_all(home, user->sdp_media, session); |
| 782 | if (!um && user->sdp_media) |
| 783 | return -1; |
| 784 | |
| 785 | u2s = su_alloc(home, (Nu + 1) * sizeof(*u2s)); |
| 786 | s2u = su_alloc(home, (Nmax + 1) * sizeof(*s2u)); |
| 787 | if (!u2s || !s2u) |
| 788 | return -1; |
| 789 | |
| 790 | for (i = 0; i < Nu; i++) |
| 791 | u2s[i] = U2S_NOT_USED(-1); |
| 792 | u2s[Nu] = U2S_SENTINEL(-2); |
| 793 | |
| 794 | for (i = 0; i < Nmax; i++) |
| 795 | s2u[i] = U2S_NOT_USED(-1); |
| 796 | s2u[Nmax] = U2S_SENTINEL(-2); |
| 797 | |
| 798 | for (i = 0, m = session->sdp_media; m && i < Ns; m = m->m_next) |
| 799 | o_media[i++] = m; |
| 800 | assert(i == Ns)((void) sizeof ((i == Ns) ? 1 : 0), __extension__ ({ if (i == Ns) ; else __assert_fail ("i == Ns", "soa_static.c", 800, __extension__ __PRETTY_FUNCTION__); })); |
| 801 | for (i = 0, m = um; m && i < Nu; m = m->m_next) |
| 802 | u_media[i++] = m; |
| 803 | assert(i == Nu)((void) sizeof ((i == Nu) ? 1 : 0), __extension__ ({ if (i == Nu) ; else __assert_fail ("i == Nu", "soa_static.c", 803, __extension__ __PRETTY_FUNCTION__); })); |
| 804 | m = remote ? remote->sdp_media : NULL((void*)0); |
| 805 | for (i = 0; m && i < Nr; m = m->m_next) |
| 806 | r_media[i++] = m; |
| 807 | assert(i == Nr)((void) sizeof ((i == Nr) ? 1 : 0), __extension__ ({ if (i == Nr) ; else __assert_fail ("i == Nr", "soa_static.c", 807, __extension__ __PRETTY_FUNCTION__); })); |
| 808 | |
| 809 | if (sss->sss_ordered_user && sss->sss_u2s) { /* User SDP is ordered */ |
| 810 | for (j = 0; sss->sss_u2s[j] != U2S_SENTINEL(-2); j++) { |
| 811 | i = sss->sss_u2s[j]; |
| 812 | if (i == U2S_NOT_USED(-1)) |
| 813 | continue; |
| 814 | if (j >= Nu) /* lines removed from user SDP */ |
| 815 | continue; |
| 816 | if (i >= Ns) /* I should never be called but somehow i and Ns are 0 here sometimes */ |
| 817 | continue; |
| 818 | s_media[i] = u_media[j], u_media[j] = SDP_MEDIA_NONE((sdp_media_t *)-1); |
| 819 | u2s[j] = i, s2u[i] = j; |
| 820 | } |
| 821 | } |
| 822 | |
| 823 | if (remote) { |
| 824 | /* Update session according to remote */ |
| 825 | for (i = 0; i < Nr; i++) { |
| 826 | rm = r_media[i]; |
| 827 | m = s_media[i]; |
| 828 | |
| 829 | if (!m) { |
| 830 | int codec_mismatch = 0; |
| 831 | |
| 832 | if (!rm->m_rejected) |
| 833 | j = soa_sdp_matching_mindex(ss, u_media, rm, &codec_mismatch); |
| 834 | else |
| 835 | j = -1; |
| 836 | |
| 837 | if (j == -1) { |
| 838 | s_media[i] = soa_sdp_make_rejected_media(home, rm, session, 0); |
| 839 | continue; |
| 840 | } |
| 841 | else if (codec_mismatch && !ss->ss_rtp_mismatch) { |
| 842 | m = soa_sdp_make_rejected_media(home, u_media[j], session, 1); |
| 843 | soa_sdp_set_rtpmap_pt(s_media[i] = m, rm); |
| 844 | continue; |
| 845 | } |
| 846 | |
| 847 | s_media[i] = m = u_media[j]; u_media[j] = SDP_MEDIA_NONE((sdp_media_t *)-1); |
| 848 | u2s[j] = i, s2u[i] = j; |
| 849 | } |
| 850 | |
| 851 | if (sdp_media_uses_rtp(rm)) |
| 852 | soa_sdp_media_upgrade_rtpmaps(ss, m, rm); |
| 853 | } |
| 854 | } |
| 855 | else { |
| 856 | |
| 857 | if (sss->sss_ordered_user) { |
| 858 | /* Update session with unused media in u_media */ |
| 859 | |
| 860 | if (!sss->sss_reuse_rejected) { |
| 861 | /* Mark previously used slots */ |
| 862 | for (i = 0; i < Ns; i++) { |
| 863 | if (s_media[i]) |
| 864 | continue; |
| 865 | s_media[i] = |
| 866 | soa_sdp_make_rejected_media(home, o_media[i], session, 0); |
| 867 | } |
| 868 | } |
| 869 | |
| 870 | for (j = 0; j < Nu; j++) { |
| 871 | if (u_media[j] == SDP_MEDIA_NONE((sdp_media_t *)-1)) |
| 872 | continue; |
| 873 | |
| 874 | for (i = 0; i < Nmax; i++) { |
| 875 | if (s_media[i] == NULL((void*)0)) { |
| 876 | s_media[i] = u_media[j], u_media[j] = SDP_MEDIA_NONE((sdp_media_t *)-1); |
| 877 | u2s[j] = i, s2u[i] = j; |
| 878 | break; |
| 879 | } |
| 880 | } |
| 881 | |
| 882 | assert(i != Nmax)((void) sizeof ((i != Nmax) ? 1 : 0), __extension__ ({ if (i != Nmax) ; else __assert_fail ("i != Nmax", "soa_static.c", 882 , __extension__ __PRETTY_FUNCTION__); })); |
| 883 | } |
| 884 | } |
| 885 | |
| 886 | /* Match unused user media by media types with the existing session */ |
| 887 | for (i = 0; i < Ns; i++) { |
| 888 | if (s_media[i]) |
| 889 | continue; |
| 890 | |
| 891 | j = soa_sdp_matching_mindex(ss, u_media, o_media[i], NULL((void*)0)); |
| 892 | if (j == -1) { |
| 893 | s_media[i] = soa_sdp_make_rejected_media(home, o_media[i], session, 0); |
| 894 | continue; |
| 895 | } |
| 896 | |
| 897 | s_media[i] = u_media[j], u_media[j] = SDP_MEDIA_NONE((sdp_media_t *)-1); |
| 898 | u2s[j] = i, s2u[i] = j; |
| 899 | } |
| 900 | |
| 901 | /* Here we just append new media at the end */ |
| 902 | for (j = 0; j < Nu; j++) { |
| 903 | if (u_media[j] != SDP_MEDIA_NONE((sdp_media_t *)-1)) { |
| 904 | s_media[i] = u_media[j], u_media[j] = SDP_MEDIA_NONE((sdp_media_t *)-1); |
| 905 | u2s[j] = i, s2u[i] = j; |
| 906 | i++; |
| 907 | } |
| 908 | } |
| 909 | assert(i <= Nmax)((void) sizeof ((i <= Nmax) ? 1 : 0), __extension__ ({ if ( i <= Nmax) ; else __assert_fail ("i <= Nmax", "soa_static.c" , 909, __extension__ __PRETTY_FUNCTION__); })); |
| 910 | } |
| 911 | |
| 912 | mm = &session->sdp_media; |
| 913 | for (i = 0; s_media[i]; i++) { |
| 914 | m = s_media[i]; *mm = m; mm = &m->m_next; |
| 915 | } |
| 916 | *mm = NULL((void*)0); |
| 917 | |
| 918 | s2u[n = i] = U2S_SENTINEL(-2); |
| 919 | *return_u2s = u2s; |
| 920 | *return_s2u = s2u; |
| 921 | |
| 922 | #ifndef NDEBUG /* X check */ |
| 923 | for (j = 0; j < Nu; j++) { |
| 924 | i = u2s[j]; |
| 925 | assert(i == U2S_NOT_USED || s2u[i] == j)((void) sizeof ((i == (-1) || s2u[i] == j) ? 1 : 0), __extension__ ({ if (i == (-1) || s2u[i] == j) ; else __assert_fail ("i == U2S_NOT_USED || s2u[i] == j" , "soa_static.c", 925, __extension__ __PRETTY_FUNCTION__); }) ); |
| 926 | } |
| 927 | for (i = 0; i < n; i++) { |
| 928 | j = s2u[i]; |
| 929 | assert(j == U2S_NOT_USED || u2s[j] == i)((void) sizeof ((j == (-1) || u2s[j] == i) ? 1 : 0), __extension__ ({ if (j == (-1) || u2s[j] == i) ; else __assert_fail ("j == U2S_NOT_USED || u2s[j] == i" , "soa_static.c", 929, __extension__ __PRETTY_FUNCTION__); }) ); |
| 930 | } |
| 931 | #endif |
| 932 | |
| 933 | return 0; |
| 934 | } |
| 935 | |
| 936 | static |
| 937 | int *u2s_alloc(su_home_t *home, int const *u2s) |
| 938 | { |
| 939 | if (u2s) { |
| 940 | int i, *a; |
| 941 | for (i = 0; u2s[i] != U2S_SENTINEL(-2); i++) |
| 942 | ; |
| 943 | a = su_alloc(home, (i + 1) * (sizeof *u2s)); |
| 944 | if (a) |
| 945 | memcpy(a, u2s, (i + 1) * (sizeof *u2s)); |
| 946 | return a; |
| 947 | } |
| 948 | |
| 949 | return NULL((void*)0); |
| 950 | } |
| 951 | |
| 952 | /** Check if @a session contains media that are rejected by @a remote. */ |
| 953 | static |
| 954 | int soa_sdp_reject_is_needed(sdp_session_t const *session, |
| 955 | sdp_session_t const *remote) |
| 956 | { |
| 957 | sdp_media_t const *sm, *rm; |
| 958 | |
| 959 | if (!remote) |
| 960 | return 1; |
| 961 | if (!session) |
| 962 | return 0; |
| 963 | |
| 964 | for (sm = session->sdp_media, rm = remote->sdp_media; |
| 965 | sm && rm; sm = sm->m_next, rm = rm->m_next) { |
| 966 | if (rm->m_rejected) { |
| 967 | if (!sm->m_rejected) |
| 968 | return 1; |
| 969 | } |
| 970 | else { |
| 971 | /* Mode bits do not match */ |
| 972 | if (((rm->m_mode & sdp_recvonly) == sdp_recvonly) |
| 973 | != ((sm->m_mode & sdp_sendonly) == sdp_sendonly)) |
| 974 | return 1; |
| 975 | } |
| 976 | } |
| 977 | |
| 978 | if (sm) |
| 979 | return 1; |
| 980 | |
| 981 | return 0; |
| 982 | } |
| 983 | |
| 984 | /** If m= line is rejected by remote mark m= line rejected within session */ |
| 985 | static |
| 986 | int soa_sdp_reject(su_home_t *home, |
| 987 | sdp_session_t *session, |
| 988 | sdp_session_t const *remote) |
| 989 | { |
| 990 | sdp_media_t *sm; |
| 991 | sdp_media_t const *rm; |
| 992 | |
| 993 | if (!session || !session->sdp_media || !remote) |
| 994 | return 0; |
| 995 | |
| 996 | rm = remote->sdp_media; |
| 997 | |
| 998 | for (sm = session->sdp_media; sm; sm = sm->m_next) { |
| 999 | if (!rm || rm->m_rejected) { |
| 1000 | sm->m_rejected = 1; |
| 1001 | sm->m_mode = 0; |
| 1002 | sm->m_port = 0; |
| 1003 | sm->m_number_of_ports = 1; |
| 1004 | if (sm->m_format) |
| 1005 | sm->m_format->l_next = NULL((void*)0); |
| 1006 | if (sm->m_rtpmaps) |
| 1007 | sm->m_rtpmaps->rm_next = NULL((void*)0); |
| 1008 | sm->m_information = NULL((void*)0); |
| 1009 | if (sm->m_connections) |
| 1010 | sm->m_connections->c_next = NULL((void*)0); |
| 1011 | sm->m_bandwidths = NULL((void*)0); |
| 1012 | sm->m_key = NULL((void*)0); |
| 1013 | sm->m_attributes = NULL((void*)0); |
| 1014 | sm->m_user = NULL((void*)0); |
| 1015 | } |
| 1016 | |
| 1017 | if (rm) |
| 1018 | rm = rm->m_next; |
| 1019 | } |
| 1020 | |
| 1021 | return 0; |
| 1022 | } |
| 1023 | |
| 1024 | |
| 1025 | /** Update mode within session. |
| 1026 | * |
| 1027 | * @sa soatag_hold |
| 1028 | * |
| 1029 | * @retval 1 if session was changed (or to be changed, if @a dryrun is nonzero) |
| 1030 | */ |
| 1031 | static |
| 1032 | int soa_sdp_mode_set(sdp_session_t const *user, |
| 1033 | int const *s2u, |
| 1034 | sdp_session_t *session, |
| 1035 | sdp_session_t const *remote, |
| 1036 | char const *hold, |
| 1037 | int dryrun) |
| 1038 | { |
| 1039 | sdp_media_t *sm; |
| 1040 | sdp_media_t const *rm, *rm_next, *um; |
| 1041 | int retval = 0, i, j; |
| 1042 | int hold_all; |
| 1043 | int inactive_all; |
| 1044 | char const *hold_media = NULL((void*)0); |
| 1045 | sdp_mode_t send_mode, recv_mode; |
| 1046 | |
| 1047 | SU_DEBUG_7(("soa_sdp_mode_set(%p, %p, \"%s\"): called\n",(((soa_log != ((void*)0) && soa_log->log_init) == 0 ? 9 : ((soa_log != ((void*)0) && soa_log->log_init > 1) ? soa_log->log_level : su_log_default->log_level )) >= 7 ? (_su_llog(soa_log, 7, "soa_static.c", (const char *)__func__, 1048, "soa_sdp_mode_set(%p, %p, \"%s\"): called\n" , (void *)session, (void *)remote, hold ? hold : "")) : (void )0) |
| 1048 | (void *)session, (void *)remote, hold ? hold : ""))(((soa_log != ((void*)0) && soa_log->log_init) == 0 ? 9 : ((soa_log != ((void*)0) && soa_log->log_init > 1) ? soa_log->log_level : su_log_default->log_level )) >= 7 ? (_su_llog(soa_log, 7, "soa_static.c", (const char *)__func__, 1048, "soa_sdp_mode_set(%p, %p, \"%s\"): called\n" , (void *)session, (void *)remote, hold ? hold : "")) : (void )0); |
| 1049 | |
| 1050 | if (!session || !session->sdp_media) |
| 1051 | return 0; |
| 1052 | |
| 1053 | rm = remote ? remote->sdp_media : NULL((void*)0), rm_next = NULL((void*)0); |
| 1054 | |
| 1055 | hold_all = su_strmatch(hold, "*"); |
| 1056 | inactive_all = su_strmatch(hold, "#"); |
| 1057 | |
| 1058 | i = 0; |
| 1059 | |
| 1060 | for (sm = session->sdp_media; sm; sm = sm->m_next, rm = rm_next, i++) { |
| 1061 | rm_next = rm ? rm->m_next : NULL((void*)0); |
| 1062 | |
| 1063 | if (sm->m_rejected) |
| 1064 | continue; |
| 1065 | |
| 1066 | assert(s2u)((void) sizeof ((s2u) ? 1 : 0), __extension__ ({ if (s2u) ; else __assert_fail ("s2u", "soa_static.c", 1066, __extension__ __PRETTY_FUNCTION__ ); })); |
| 1067 | |
| 1068 | for (j = 0, um = user->sdp_media; j != s2u[i]; um = um->m_next, j++) { |
| 1069 | if (!um) break; |
| 1070 | } |
| 1071 | |
| 1072 | if (um == NULL((void*)0)) { |
| 1073 | if (dryrun) |
| 1074 | return 1; |
| 1075 | else |
| 1076 | retval = 1; |
| 1077 | sm->m_rejected = 1; |
| 1078 | sm->m_mode = sdp_inactive; |
| 1079 | sm->m_port = 0; |
| 1080 | continue; |
| 1081 | } |
| 1082 | |
| 1083 | if (um->m_mode) { /* when its inactive, keep it inactive */ |
| 1084 | send_mode = (sdp_mode_t)(um->m_mode & sdp_sendonly); |
| 1085 | if (rm) |
| 1086 | send_mode = (rm->m_mode & sdp_recvonly) ? sdp_sendonly : 0; |
| 1087 | } else send_mode = um->m_mode; |
| 1088 | |
| 1089 | recv_mode = (sdp_mode_t)(um->m_mode & sdp_recvonly); |
| 1090 | |
| 1091 | if (rm && rm->m_mode == sdp_inactive) { |
| 1092 | send_mode = recv_mode = (sdp_mode_t)0; |
| 1093 | } |
| 1094 | else if (inactive_all) { |
| 1095 | send_mode = recv_mode = (sdp_mode_t)0; |
| 1096 | } |
| 1097 | else if (hold_all) { |
| 1098 | recv_mode = (sdp_mode_t)0; |
| 1099 | } |
| 1100 | else if (hold && (hold_media = su_strcasestr(hold, sm->m_type_name))) { |
| 1101 | recv_mode = (sdp_mode_t)0; |
| 1102 | hold_media += strlen(sm->m_type_name); |
| 1103 | hold_media += strspn(hold_media, " \t"); |
| 1104 | if (hold_media[0] == '=') { |
| 1105 | hold_media += strspn(hold, " \t"); |
| 1106 | if (su_casenmatch(hold_media, "inactive", strlen("inactive"))) |
| 1107 | recv_mode = send_mode = (sdp_mode_t)0; |
| 1108 | } |
| 1109 | } |
| 1110 | |
| 1111 | if (sm->m_mode != (unsigned)(recv_mode | send_mode)) { |
| 1112 | if (dryrun) |
| 1113 | return 1; |
| 1114 | else |
| 1115 | retval = 1; |
| 1116 | sm->m_mode = recv_mode | send_mode; |
| 1117 | } |
| 1118 | } |
| 1119 | |
| 1120 | return retval; |
| 1121 | } |
| 1122 | |
| 1123 | enum offer_answer_action { |
| 1124 | generate_offer, |
| 1125 | generate_answer, |
| 1126 | process_answer |
| 1127 | }; |
| 1128 | |
| 1129 | /** |
| 1130 | * Updates the modified copy of local SDP based |
| 1131 | * on application provided local SDP and remote SDP. |
| 1132 | */ |
| 1133 | static int offer_answer_step(soa_session_t *ss, |
| 1134 | enum offer_answer_action action, |
| 1135 | char const *by) |
| 1136 | { |
| 1137 | soa_static_session_t *sss = (soa_static_session_t *)ss; |
| 1138 | |
| 1139 | sdp_session_t *local = ss->ss_local->ssd_sdp; |
| 1140 | sdp_session_t local0[1]; |
| 1141 | |
| 1142 | sdp_session_t *user = ss->ss_user->ssd_sdp; |
| 1143 | unsigned user_version = ss->ss_user_version; |
| 1144 | |
| 1145 | sdp_session_t *remote = ss->ss_remote->ssd_sdp; |
| 1146 | unsigned remote_version = ss->ss_remote_version; |
| 1147 | |
| 1148 | int fresh = 0; |
| 1149 | |
| 1150 | sdp_origin_t o[1] = {{ sizeof(o) }}; |
| 1151 | sdp_connection_t *c, c0[1] = {{ sizeof(c0) }}; |
| 1152 | char c0_buffer[64]; |
| 1153 | |
| 1154 | sdp_time_t t[1] = {{ sizeof(t) }}; |
| 1155 | |
| 1156 | int *u2s = NULL((void*)0), *s2u = NULL((void*)0), *tbf; |
| 1157 | |
| 1158 | sdp_session_t *latest = NULL((void*)0), *previous = NULL((void*)0); |
| 1159 | |
| 1160 | char const *phrase = "Internal Media Error"; |
| 1161 | |
| 1162 | su_home_t tmphome[SU_HOME_AUTO_SIZE(8192)(((8192) + ((sizeof(su_home_t) + 7) & (size_t)~8) + ((3 * sizeof (void *) + 4 * sizeof(unsigned) + 7 * (sizeof (long) + sizeof(void *)) + 7) & (size_t)~8)) / sizeof(su_home_t))]; |
| 1163 | |
| 1164 | su_home_auto(tmphome, sizeof tmphome); |
| 1165 | |
| 1166 | SU_DEBUG_7(("soa_static_offer_answer_action(%p, %s): called\n",(((soa_log != ((void*)0) && soa_log->log_init) == 0 ? 9 : ((soa_log != ((void*)0) && soa_log->log_init > 1) ? soa_log->log_level : su_log_default->log_level )) >= 7 ? (_su_llog(soa_log, 7, "soa_static.c", (const char *)__func__, 1167, "soa_static_offer_answer_action(%p, %s): called\n" , (void *)ss, by)) : (void)0) |
| 1167 | (void *)ss, by))(((soa_log != ((void*)0) && soa_log->log_init) == 0 ? 9 : ((soa_log != ((void*)0) && soa_log->log_init > 1) ? soa_log->log_level : su_log_default->log_level )) >= 7 ? (_su_llog(soa_log, 7, "soa_static.c", (const char *)__func__, 1167, "soa_static_offer_answer_action(%p, %s): called\n" , (void *)ss, by)) : (void)0); |
| 1168 | |
| 1169 | if (user == NULL((void*)0)) |
| 1170 | return soa_set_status(ss, 500, "No session set by user"); |
| 1171 | |
| 1172 | if (action == generate_offer) |
| 1173 | remote = NULL((void*)0); |
| 1174 | else if (remote == NULL((void*)0)) |
| 1175 | return soa_set_status(ss, 500, "No remote SDP"); |
| 1176 | |
| 1177 | #ifdef __clang__1 |
| 1178 | #pragma clang diagnostic push |
| 1179 | #pragma clang diagnostic ignored "-Wnon-literal-null-conversion" |
| 1180 | #endif |
| 1181 | |
| 1182 | /* Pre-negotiation Step: Expand truncated remote SDP */ |
| 1183 | if (local && remote) switch (action) { |
| 1184 | case generate_answer: |
| 1185 | case process_answer: |
| 1186 | if (sdp_media_count(remote, sdp_media_any, "*", (sdp_proto_e)0, (sdp_text_t)0) < |
| 1187 | sdp_media_count(local, sdp_media_any, "*", (sdp_proto_e)0, (sdp_text_t)0)) { |
| 1188 | SU_DEBUG_5(("%s: remote %s is truncated: expanding\n",(((soa_log != ((void*)0) && soa_log->log_init) == 0 ? 9 : ((soa_log != ((void*)0) && soa_log->log_init > 1) ? soa_log->log_level : su_log_default->log_level )) >= 5 ? (_su_llog(soa_log, 5, "soa_static.c", (const char *)__func__, 1189, "%s: remote %s is truncated: expanding\n", by, action == generate_answer ? "offer" : "answer")) : (void )0) |
| 1189 | by, action == generate_answer ? "offer" : "answer"))(((soa_log != ((void*)0) && soa_log->log_init) == 0 ? 9 : ((soa_log != ((void*)0) && soa_log->log_init > 1) ? soa_log->log_level : su_log_default->log_level )) >= 5 ? (_su_llog(soa_log, 5, "soa_static.c", (const char *)__func__, 1189, "%s: remote %s is truncated: expanding\n", by, action == generate_answer ? "offer" : "answer")) : (void )0); |
| 1190 | remote = soa_sdp_expand_media(tmphome, remote, local); |
| 1191 | if (remote == NULL((void*)0)) |
| 1192 | return soa_set_status(ss, 500, "Cannot expand remote session"); |
| 1193 | } |
| 1194 | default: |
| 1195 | break; |
| 1196 | } |
| 1197 | |
| 1198 | #ifdef __clang__1 |
| 1199 | #pragma clang diagnostic pop |
| 1200 | #endif |
| 1201 | |
| 1202 | |
| 1203 | /* Step A: Create local SDP session (based on user-supplied SDP) */ |
| 1204 | if (local == NULL((void*)0)) switch (action) { |
| 1205 | case generate_offer: |
| 1206 | case generate_answer: |
| 1207 | SU_DEBUG_7(("soa_static(%p, %s): %s\n", (void *)ss, by,(((soa_log != ((void*)0) && soa_log->log_init) == 0 ? 9 : ((soa_log != ((void*)0) && soa_log->log_init > 1) ? soa_log->log_level : su_log_default->log_level )) >= 7 ? (_su_llog(soa_log, 7, "soa_static.c", (const char *)__func__, 1208, "soa_static(%p, %s): %s\n", (void *)ss, by , "generating local description")) : (void)0) |
| 1208 | "generating local description"))(((soa_log != ((void*)0) && soa_log->log_init) == 0 ? 9 : ((soa_log != ((void*)0) && soa_log->log_init > 1) ? soa_log->log_level : su_log_default->log_level )) >= 7 ? (_su_llog(soa_log, 7, "soa_static.c", (const char *)__func__, 1208, "soa_static(%p, %s): %s\n", (void *)ss, by , "generating local description")) : (void)0); |
| 1209 | |
| 1210 | fresh = 1; |
| 1211 | local = local0; |
| 1212 | *local = *user, local->sdp_media = NULL((void*)0); |
| 1213 | |
| 1214 | o->o_username = "-"; |
| 1215 | o->o_address = c0; |
| 1216 | c0->c_address = c0_buffer; |
| 1217 | |
| 1218 | if (!local->sdp_origin) |
| 1219 | local->sdp_origin = o; |
| 1220 | break; |
| 1221 | |
| 1222 | case process_answer: |
| 1223 | default: |
| 1224 | goto internal_error; |
| 1225 | } |
| 1226 | |
| 1227 | /* Step B: upgrade local SDP (add m= lines to it) */ |
| 1228 | switch (action) { |
| 1229 | case generate_offer: |
| 1230 | /* Upgrade local SDP based on user SDP */ |
| 1231 | if (local != local0 && ss->ss_local_user_version == user_version) |
| 1232 | break; |
| 1233 | if (local != local0) |
| 1234 | *local0 = *local, local = local0; |
| 1235 | SU_DEBUG_7(("soa_static(%p, %s): %s\n", (void *)ss, by,(((soa_log != ((void*)0) && soa_log->log_init) == 0 ? 9 : ((soa_log != ((void*)0) && soa_log->log_init > 1) ? soa_log->log_level : su_log_default->log_level )) >= 7 ? (_su_llog(soa_log, 7, "soa_static.c", (const char *)__func__, 1236, "soa_static(%p, %s): %s\n", (void *)ss, by , "upgrade with local description")) : (void)0) |
| 1236 | "upgrade with local description"))(((soa_log != ((void*)0) && soa_log->log_init) == 0 ? 9 : ((soa_log != ((void*)0) && soa_log->log_init > 1) ? soa_log->log_level : su_log_default->log_level )) >= 7 ? (_su_llog(soa_log, 7, "soa_static.c", (const char *)__func__, 1236, "soa_static(%p, %s): %s\n", (void *)ss, by , "upgrade with local description")) : (void)0); |
| 1237 | if (soa_sdp_upgrade(ss, tmphome, local, user, NULL((void*)0), &u2s, &s2u) < 0) |
| 1238 | goto internal_error; |
| 1239 | break; |
| 1240 | case generate_answer: |
| 1241 | /* Upgrade local SDP based on remote SDP */ |
| 1242 | if (ss->ss_local_user_version == user_version && |
| 1243 | ss->ss_local_remote_version == remote_version) |
| 1244 | break; |
| 1245 | if (1) { |
| 1246 | if (local != local0) |
| 1247 | *local0 = *local, local = local0; |
| 1248 | SU_DEBUG_7(("soa_static(%p, %s): %s\n", (void *)ss, by,(((soa_log != ((void*)0) && soa_log->log_init) == 0 ? 9 : ((soa_log != ((void*)0) && soa_log->log_init > 1) ? soa_log->log_level : su_log_default->log_level )) >= 7 ? (_su_llog(soa_log, 7, "soa_static.c", (const char *)__func__, 1249, "soa_static(%p, %s): %s\n", (void *)ss, by , "upgrade with remote description")) : (void)0) |
| 1249 | "upgrade with remote description"))(((soa_log != ((void*)0) && soa_log->log_init) == 0 ? 9 : ((soa_log != ((void*)0) && soa_log->log_init > 1) ? soa_log->log_level : su_log_default->log_level )) >= 7 ? (_su_llog(soa_log, 7, "soa_static.c", (const char *)__func__, 1249, "soa_static(%p, %s): %s\n", (void *)ss, by , "upgrade with remote description")) : (void)0); |
| 1250 | if (soa_sdp_upgrade(ss, tmphome, local, user, remote, &u2s, &s2u) < 0) |
| 1251 | goto internal_error; |
| 1252 | } |
| 1253 | break; |
| 1254 | case process_answer: |
| 1255 | default: |
| 1256 | break; |
| 1257 | } |
| 1258 | |
| 1259 | |
| 1260 | /* Step C: reject media */ |
| 1261 | switch (action) { |
| 1262 | case generate_offer: |
| 1263 | /* Local media is marked as rejected already in upgrade phase */ |
| 1264 | break; |
| 1265 | case generate_answer: |
| 1266 | case process_answer: |
| 1267 | if (ss->ss_local_remote_version == remote_version) |
| 1268 | break; |
| 1269 | if (soa_sdp_reject_is_needed(local, remote)) { |
| 1270 | if (local != local0) { |
| 1271 | *local0 = *local, local = local0; |
| 1272 | #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) \ |
| 1273 | do { \ |
| 1274 | if (!local->sdp_media) break; \ |
| 1275 | local->sdp_media = \ |
| 1276 | sdp_media_dup_all(tmphome, local->sdp_media, local); \ |
| 1277 | if (!local->sdp_media) \ |
| 1278 | goto internal_error; \ |
| 1279 | } while (0) |
| 1280 | 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); |
| 1281 | } |
| 1282 | SU_DEBUG_7(("soa_static(%p, %s): %s\n", (void *)ss, by,(((soa_log != ((void*)0) && soa_log->log_init) == 0 ? 9 : ((soa_log != ((void*)0) && soa_log->log_init > 1) ? soa_log->log_level : su_log_default->log_level )) >= 7 ? (_su_llog(soa_log, 7, "soa_static.c", (const char *)__func__, 1283, "soa_static(%p, %s): %s\n", (void *)ss, by , "marking rejected media")) : (void)0) |
| 1283 | "marking rejected media"))(((soa_log != ((void*)0) && soa_log->log_init) == 0 ? 9 : ((soa_log != ((void*)0) && soa_log->log_init > 1) ? soa_log->log_level : su_log_default->log_level )) >= 7 ? (_su_llog(soa_log, 7, "soa_static.c", (const char *)__func__, 1283, "soa_static(%p, %s): %s\n", (void *)ss, by , "marking rejected media")) : (void)0); |
| 1284 | soa_sdp_reject(tmphome, local, remote); |
| 1285 | } |
| 1286 | break; |
| 1287 | default: |
| 1288 | break; |
| 1289 | } |
| 1290 | |
| 1291 | /* Step D: Set media mode bits */ |
| 1292 | switch (action) { |
| 1293 | int const *s2u_; |
| 1294 | |
| 1295 | case generate_offer: |
| 1296 | case generate_answer: |
| 1297 | case process_answer: |
| 1298 | s2u_ = s2u; |
| 1299 | |
| 1300 | if (!s2u_) s2u_ = sss->sss_s2u; |
| 1301 | |
| 1302 | if (soa_sdp_mode_set(user, s2u_, local, remote, ss->ss_hold, 1)) { |
| 1303 | if (local != local0) { |
| 1304 | *local0 = *local, local = local0; |
| 1305 | 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); |
| 1306 | } |
| 1307 | |
| 1308 | soa_sdp_mode_set(user, s2u_, local, remote, ss->ss_hold, 0); |
| 1309 | } |
| 1310 | break; |
| 1311 | default: |
| 1312 | break; |
| 1313 | } |
| 1314 | |
| 1315 | /* Step E: Upgrade codecs by answer. */ |
| 1316 | switch (action) { |
| 1317 | case process_answer: |
| 1318 | /* Upgrade local SDP based on remote SDP */ |
| 1319 | if (ss->ss_local_remote_version == remote_version) |
| 1320 | break; |
| 1321 | if (1 /* We don't have good test for codecs */) { |
| 1322 | SU_DEBUG_7(("soa_static(%p, %s): %s\n", (void *)ss, by,(((soa_log != ((void*)0) && soa_log->log_init) == 0 ? 9 : ((soa_log != ((void*)0) && soa_log->log_init > 1) ? soa_log->log_level : su_log_default->log_level )) >= 7 ? (_su_llog(soa_log, 7, "soa_static.c", (const char *)__func__, 1323, "soa_static(%p, %s): %s\n", (void *)ss, by , "upgrade codecs with remote description")) : (void)0) |
| 1323 | "upgrade codecs with remote description"))(((soa_log != ((void*)0) && soa_log->log_init) == 0 ? 9 : ((soa_log != ((void*)0) && soa_log->log_init > 1) ? soa_log->log_level : su_log_default->log_level )) >= 7 ? (_su_llog(soa_log, 7, "soa_static.c", (const char *)__func__, 1323, "soa_static(%p, %s): %s\n", (void *)ss, by , "upgrade codecs with remote description")) : (void)0); |
| 1324 | if (local != local0) { |
| 1325 | *local0 = *local, local = local0; |
| 1326 | 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); |
| 1327 | } |
| 1328 | soa_sdp_session_upgrade_rtpmaps(ss, local, remote); |
| 1329 | } |
| 1330 | break; |
| 1331 | case generate_offer: |
| 1332 | case generate_answer: |
| 1333 | default: |
| 1334 | break; |
| 1335 | } |
| 1336 | |
| 1337 | /* Step F0: Initialize o= line */ |
| 1338 | if (fresh) { |
| 1339 | if (user->sdp_origin) { |
| 1340 | o->o_username = user->sdp_origin->o_username; |
| 1341 | |
| 1342 | if (user->sdp_origin->o_address) |
| 1343 | o->o_address = user->sdp_origin->o_address; |
| 1344 | |
| 1345 | if (user->sdp_origin->o_id) |
| 1346 | o->o_id = user->sdp_origin->o_id; |
| 1347 | |
| 1348 | if (user->sdp_origin->o_version && user->sdp_origin->o_version != o->o_version) { |
| 1349 | o->o_version = user->sdp_origin->o_version; |
| 1350 | o->o_version--; |
| 1351 | } |
| 1352 | } |
| 1353 | |
| 1354 | if (soa_init_sdp_origin_with_session(ss, o, c0_buffer, local) < 0) { |
| 1355 | phrase = "Cannot Get IP Address for Session Description"; |
| 1356 | goto internal_error; |
| 1357 | } |
| 1358 | |
| 1359 | local->sdp_origin = o; |
| 1360 | } |
| 1361 | |
| 1362 | /* Step F: Update c= line(s) */ |
| 1363 | switch (action) { |
| 1364 | sdp_connection_t *user_c, *local_c; |
| 1365 | |
| 1366 | case generate_offer: |
| 1367 | case generate_answer: |
| 1368 | user_c = user->sdp_connection; |
| 1369 | if (!soa_check_sdp_connection(user_c)) |
| 1370 | user_c = NULL((void*)0); |
| 1371 | |
| 1372 | local_c = local->sdp_connection; |
| 1373 | if (!soa_check_sdp_connection(local_c)) |
| 1374 | local_c = NULL((void*)0); |
| 1375 | |
| 1376 | if (ss->ss_local_user_version != user_version || |
| 1377 | local_c == NULL((void*)0) || |
| 1378 | (user_c != NULL((void*)0) && sdp_connection_cmp(local_c, user_c))) { |
| 1379 | sdp_media_t *m; |
| 1380 | |
| 1381 | if (user_c) |
| 1382 | c = user_c; |
| 1383 | else |
| 1384 | c = local->sdp_origin->o_address; |
| 1385 | |
| 1386 | /* Every m= line (even rejected one) must have a c= line |
| 1387 | * or there must be a c= line at session level |
| 1388 | */ |
| 1389 | for (m = local->sdp_media; m; m = m->m_next) |
| 1390 | if (m->m_connections == NULL((void*)0)) |
| 1391 | break; |
| 1392 | |
| 1393 | if (m) { |
| 1394 | if (local != local0) { |
| 1395 | *local0 = *local, local = local0; |
| 1396 | 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); |
| 1397 | } |
| 1398 | local->sdp_connection = c; |
| 1399 | } |
| 1400 | } |
| 1401 | break; |
| 1402 | |
| 1403 | default: |
| 1404 | break; |
| 1405 | } |
| 1406 | |
| 1407 | soa_description_free(ss, ss->ss_previous); |
| 1408 | su_free(ss->ss_home, sss->sss_previous.u2s), sss->sss_previous.u2s = NULL((void*)0); |
| 1409 | su_free(ss->ss_home, sss->sss_previous.s2u), sss->sss_previous.s2u = NULL((void*)0); |
| 1410 | |
| 1411 | if (u2s) { |
| 1412 | u2s = u2s_alloc(ss->ss_home, u2s); |
| 1413 | s2u = u2s_alloc(ss->ss_home, s2u); |
| 1414 | if (!u2s || !s2u) |
| 1415 | goto internal_error; |
| 1416 | } |
| 1417 | |
| 1418 | if (ss->ss_local->ssd_sdp != local && |
| 1419 | sdp_session_cmp(ss->ss_local->ssd_sdp, local)) { |
| 1420 | int bump; |
| 1421 | |
| 1422 | switch (action) { |
| 1423 | case generate_offer: |
| 1424 | bump = sdp_session_cmp(local, sss->sss_latest); |
| 1425 | break; |
| 1426 | case generate_answer: |
| 1427 | bump = 1; |
| 1428 | break; |
| 1429 | case process_answer: |
| 1430 | default: |
| 1431 | bump = 0; |
| 1432 | break; |
| 1433 | } |
| 1434 | |
| 1435 | if (bump) { |
| 1436 | /* Upgrade the version number */ |
| 1437 | if (local->sdp_origin != o) |
| 1438 | *o = *local->sdp_origin, local->sdp_origin = o; |
| 1439 | o->o_version++; |
| 1440 | } |
| 1441 | |
| 1442 | /* Do sanity checks for the created SDP */ |
| 1443 | if (!local->sdp_subject) /* s= is mandatory */ |
| 1444 | local->sdp_subject = "-"; |
| 1445 | if (!local->sdp_time) /* t= is mandatory */ |
| 1446 | local->sdp_time = t; |
| 1447 | |
| 1448 | if (action == generate_offer) { |
| 1449 | /* Keep a copy of previous session state */ |
| 1450 | int *previous_u2s = u2s_alloc(ss->ss_home, sss->sss_u2s); |
| 1451 | int *previous_s2u = u2s_alloc(ss->ss_home, sss->sss_s2u); |
| 1452 | |
| 1453 | if ((sss->sss_u2s && !previous_u2s) || (sss->sss_s2u && !previous_s2u)) |
| 1454 | goto internal_error; |
| 1455 | |
| 1456 | *ss->ss_previous = *ss->ss_local; |
| 1457 | memset(ss->ss_local, 0, (sizeof *ss->ss_local)); |
| 1458 | ss->ss_previous_user_version = ss->ss_local_user_version; |
| 1459 | ss->ss_previous_remote_version = ss->ss_local_remote_version; |
| 1460 | sss->sss_previous.u2s = previous_u2s; |
| 1461 | sss->sss_previous.s2u = previous_s2u; |
| 1462 | } |
| 1463 | |
| 1464 | SU_DEBUG_7(("soa_static(%p, %s): %s\n", (void *)ss, by,(((soa_log != ((void*)0) && soa_log->log_init) == 0 ? 9 : ((soa_log != ((void*)0) && soa_log->log_init > 1) ? soa_log->log_level : su_log_default->log_level )) >= 7 ? (_su_llog(soa_log, 7, "soa_static.c", (const char *)__func__, 1465, "soa_static(%p, %s): %s\n", (void *)ss, by , "storing local description")) : (void)0) |
| 1465 | "storing local description"))(((soa_log != ((void*)0) && soa_log->log_init) == 0 ? 9 : ((soa_log != ((void*)0) && soa_log->log_init > 1) ? soa_log->log_level : su_log_default->log_level )) >= 7 ? (_su_llog(soa_log, 7, "soa_static.c", (const char *)__func__, 1465, "soa_static(%p, %s): %s\n", (void *)ss, by , "storing local description")) : (void)0); |
| 1466 | |
| 1467 | /* Update the unparsed and pretty-printed descriptions */ |
| 1468 | if (soa_description_set(ss, ss->ss_local, local, NULL((void*)0), 0) < 0) { |
| 1469 | if (action == generate_offer) { |
| 1470 | /* Remove 2nd reference to local session state */ |
| 1471 | memset(ss->ss_previous, 0, (sizeof *ss->ss_previous)); |
| 1472 | ss->ss_previous_user_version = 0; |
| 1473 | ss->ss_previous_remote_version = 0; |
| 1474 | su_free(ss->ss_home, sss->sss_previous.u2s), sss->sss_previous.u2s = NULL((void*)0); |
| 1475 | su_free(ss->ss_home, sss->sss_previous.s2u), sss->sss_previous.s2u = NULL((void*)0); |
| 1476 | } |
| 1477 | |
| 1478 | su_free(ss->ss_home, u2s), su_free(ss->ss_home, s2u); |
| 1479 | |
| 1480 | goto internal_error; |
| 1481 | } |
| 1482 | |
| 1483 | if (bump) { |
| 1484 | latest = sdp_session_dup(ss->ss_home, ss->ss_local->ssd_sdp); |
| 1485 | previous = sss->sss_latest; |
| 1486 | } |
| 1487 | } |
| 1488 | |
| 1489 | if (u2s) { |
| 1490 | tbf = sss->sss_u2s, sss->sss_u2s = u2s, su_free(ss->ss_home, tbf); |
| 1491 | tbf = sss->sss_s2u, sss->sss_s2u = s2u, su_free(ss->ss_home, tbf); |
| 1492 | } |
| 1493 | |
| 1494 | /* Update version numbers */ |
| 1495 | switch (action) { |
| 1496 | case generate_offer: |
| 1497 | ss->ss_local_user_version = user_version; |
| 1498 | sss->sss_latest = latest; |
| 1499 | break; |
| 1500 | case generate_answer: |
| 1501 | ss->ss_local_user_version = user_version; |
| 1502 | ss->ss_local_remote_version = remote_version; |
| 1503 | sss->sss_latest = latest; |
| 1504 | break; |
| 1505 | case process_answer: |
| 1506 | ss->ss_local_remote_version = remote_version; |
| 1507 | default: |
| 1508 | break; |
| 1509 | } |
| 1510 | |
| 1511 | if (previous) |
| 1512 | su_free(ss->ss_home, previous); |
| 1513 | |
| 1514 | su_home_deinit(tmphome); |
| 1515 | return 0; |
| 1516 | |
| 1517 | internal_error: |
| 1518 | su_home_deinit(tmphome); |
| 1519 | return soa_set_status(ss, 500, phrase); |
| 1520 | } |
| 1521 | |
| 1522 | /** |
| 1523 | * Generates offer based on local SDP. |
| 1524 | */ |
| 1525 | static int soa_static_generate_offer(soa_session_t *ss, |
| 1526 | soa_callback_f *completed) |
| 1527 | { |
| 1528 | if (!ss->ss_user->ssd_sdp) |
| 1529 | return soa_set_status(ss, 500, "No session set by user"); |
| 1530 | |
| 1531 | if (offer_answer_step(ss, generate_offer, "soa_generate_offer") < 0) |
| 1532 | return -1; |
| 1533 | |
| 1534 | return soa_base_generate_offer(ss, NULL((void*)0)); |
| 1535 | } |
| 1536 | |
| 1537 | static int soa_static_generate_answer(soa_session_t *ss, |
| 1538 | soa_callback_f *completed) |
| 1539 | { |
| 1540 | /* NOTE: |
| 1541 | * - local SDP might have changed |
| 1542 | * - remote SDP might have been updated |
| 1543 | */ |
| 1544 | |
| 1545 | if (offer_answer_step(ss, generate_answer, "soa_generate_answer") < 0) |
| 1546 | return -1; |
| 1547 | |
| 1548 | return soa_base_generate_answer(ss, NULL((void*)0)); |
| 1549 | } |
| 1550 | |
| 1551 | static int soa_static_process_answer(soa_session_t *ss, |
| 1552 | soa_callback_f *completed) |
| 1553 | { |
| 1554 | /* NOTE: |
| 1555 | * - both local and remote information is available |
| 1556 | * - local SDP might have changed |
| 1557 | * - remote SDP might have been updated |
| 1558 | */ |
| 1559 | if (offer_answer_step(ss, process_answer, "soa_process_answer") < 0) |
| 1560 | return -1; |
| 1561 | |
| 1562 | return soa_base_process_answer(ss, NULL((void*)0)); |
| 1563 | } |
| 1564 | |
| 1565 | /** Process rejected offer */ |
| 1566 | static int soa_static_process_reject(soa_session_t *ss, |
| 1567 | soa_callback_f *completed) |
| 1568 | { |
| 1569 | soa_static_session_t *sss = (soa_static_session_t *)ss; |
| 1570 | struct soa_description d[1]; |
| 1571 | |
| 1572 | soa_base_process_reject(ss, NULL((void*)0)); |
| 1573 | |
| 1574 | *d = *ss->ss_local; |
| 1575 | *ss->ss_local = *ss->ss_previous; |
| 1576 | ss->ss_local_user_version = ss->ss_previous_user_version; |
| 1577 | ss->ss_local_remote_version = ss->ss_previous_remote_version; |
| 1578 | |
| 1579 | memset(ss->ss_previous, 0, (sizeof *ss->ss_previous)); |
| 1580 | soa_description_free(ss, d); |
| 1581 | su_free(ss->ss_home, sss->sss_previous.u2s), sss->sss_previous.u2s = NULL((void*)0); |
| 1582 | su_free(ss->ss_home, sss->sss_previous.s2u), sss->sss_previous.s2u = NULL((void*)0); |
| 1583 | ss->ss_previous_user_version = 0; |
| 1584 | ss->ss_previous_remote_version = 0; |
| 1585 | |
| 1586 | su_free(ss->ss_home, sss->sss_latest), sss->sss_latest = NULL((void*)0); |
| 1587 | |
| 1588 | return 0; |
| 1589 | } |
| 1590 | |
| 1591 | static int soa_static_activate(soa_session_t *ss, char const *option) |
| 1592 | { |
| 1593 | return soa_base_activate(ss, option); |
| 1594 | } |
| 1595 | |
| 1596 | static int soa_static_deactivate(soa_session_t *ss, char const *option) |
| 1597 | { |
| 1598 | return soa_base_deactivate(ss, option); |
| 1599 | } |
| 1600 | |
| 1601 | static void soa_static_terminate(soa_session_t *ss, char const *option) |
| 1602 | { |
| 1603 | soa_static_session_t *sss = (soa_static_session_t *)ss; |
| 1604 | |
| 1605 | soa_description_free(ss, ss->ss_local); |
| 1606 | su_free(ss->ss_home, sss->sss_u2s), sss->sss_u2s = NULL((void*)0); |
| 1607 | su_free(ss->ss_home, sss->sss_s2u), sss->sss_s2u = NULL((void*)0); |
| 1608 | |
| 1609 | soa_description_free(ss, ss->ss_previous); |
| 1610 | ss->ss_previous_user_version = 0; |
| 1611 | ss->ss_previous_remote_version = 0; |
| 1612 | su_free(ss->ss_home, sss->sss_previous.u2s), sss->sss_previous.u2s = NULL((void*)0); |
| 1613 | su_free(ss->ss_home, sss->sss_previous.s2u), sss->sss_previous.s2u = NULL((void*)0); |
| 1614 | |
| 1615 | su_free(ss->ss_home, sss->sss_latest), sss->sss_latest = NULL((void*)0); |
| 1616 | |
| 1617 | soa_base_terminate(ss, option); |
| 1618 | } |