1: -module(jingle_SUITE). 2: -compile([export_all, nowarn_export_all]). 3: 4: -include_lib("common_test/include/ct.hrl"). 5: -include_lib("exml/include/exml.hrl"). 6: -include_lib("eunit/include/eunit.hrl"). 7: 8: -import(distributed_helper, [mim/0, mim2/0, 9: require_rpc_nodes/1, 10: rpc/4]). 11: 12: -import(jingle_helper, [content/1, 13: content_group/1]). 14: 15: -import(domain_helper, [domain/0]). 16: 17: %%-------------------------------------------------------------------- 18: %% Suite configuration 19: %%-------------------------------------------------------------------- 20: 21: all() -> 22: [{group, all}]. 23: 24: groups() -> 25: G = [{all, [parallel], test_cases()}], 26: ct_helper:repeat_all_until_all_ok(G). 27: 28: test_cases() -> 29: [jingle_session_is_established_for_full_jids, 30: jingle_session_is_established_for_full_jids_on_different_nodes, 31: resp_4xx_from_sip_proxy_results_in_session_terminate, 32: jingle_session_is_established_when_calling_a_number, 33: jingle_session_is_established_and_terminated_by_initiator, 34: jingle_session_is_established_and_terminated_by_receiver, 35: jingle_session_is_established_and_terminated_by_receiver_on_different_node, 36: jingle_session_is_intiated_and_canceled_by_initiator, 37: jingle_session_is_intiated_and_canceled_by_receiver, 38: jingle_session_is_intiated_and_canceled_by_receiver_on_different_node, 39: jingle_session_is_established_with_a_conference_room, 40: jingle_session_is_terminated_on_other_receivers_devices, 41: jingle_session_initiate_is_resent_on_demand, 42: mongoose_replies_with_480_when_invitee_is_offline, 43: mongoose_returns_404_when_not_authorized_user_tires_to_accept_a_session, 44: mongoose_returns_404_when_nto_authorized_user_tries_to_cancel_a_session, 45: mongoose_sends_reINVITE_on_source_remove_action, 46: mongoose_sends_reINVITE_on_source_add_action, 47: mongoose_sends_reINVITE_on_source_update_action 48: 49: ]. 50: 51: suite() -> 52: require_rpc_nodes([mim]) ++ escalus:suite(). 53: 54: %%-------------------------------------------------------------------- 55: %% Init & teardown 56: %%-------------------------------------------------------------------- 57: 58: init_per_suite(Config) -> 59: case rpc(mim(), application, get_application, [nksip]) of 60: {ok, nksip} -> 61: distributed_helper:add_node_to_cluster(mim2(), Config), 62: start_nksip_in_mim_nodes(), 63: application:ensure_all_started(esip), 64: spawn(fun() -> ets:new(jingle_sip_translator, [public, named_table]), 65: ets:new(jingle_sip_translator_bindings, [public, named_table]), 66: receive stop -> ok end end), 67: esip:add_listener(12345, tcp, []), 68: esip:set_config_value(module, jingle_sip_translator), 69: escalus:init_per_suite(Config); 70: undefined -> 71: {skip, build_was_not_configured_with_jingle_sip} 72: end. 73: 74: start_nksip_in_mim_nodes() -> 75: Pid1 = start_nskip_in_parallel(mim(), []), 76: Pid2 = start_nskip_in_parallel(mim2(), [{listen_port, 12346}]), 77: wait_for_process_to_stop(Pid1), 78: wait_for_process_to_stop(Pid2). 79: 80: wait_for_process_to_stop(Pid) -> 81: erlang:monitor(process, Pid), 82: receive 83: {'DOWN', _, process, Pid, _} -> ok 84: after timer:seconds(60) -> 85: ct:fail(wait_for_process_to_stop_timeout) 86: end. 87: 88: start_nskip_in_parallel(NodeSpec, ExtraOpts) -> 89: Domain = domain(), 90: Opts = [{proxy_host, "localhost"}, 91: {proxy_port, 12345}, 92: {username_to_phone,[{<<"2000006168">>, <<"+919177074440">>}]} 93: | ExtraOpts], 94: RPCSpec = NodeSpec#{timeout => timer:seconds(60)}, 95: proc_lib:spawn_link(dynamic_modules, start, [RPCSpec, Domain, mod_jingle_sip, Opts]). 96: 97: end_per_suite(Config) -> 98: escalus_fresh:clean(), 99: Domain = domain(), 100: dynamic_modules:stop(mim(), Domain, mod_jingle_sip), 101: dynamic_modules:stop(mim2(), Domain, mod_jingle_sip), 102: distributed_helper:remove_node_from_cluster(mim2(), Config), 103: escalus:end_per_suite(Config). 104: 105: init_per_group(_GroupName, Config) -> 106: Config. 107: 108: end_per_group(_GroupName, Config) -> 109: Config. 110: 111: init_per_testcase(CaseName, Config) -> 112: escalus:init_per_testcase(CaseName, Config). 113: 114: end_per_testcase(CaseName, Config) -> 115: escalus:end_per_testcase(CaseName, Config). 116: 117: %%-------------------------------------------------------------------- 118: %% Cases 119: %%-------------------------------------------------------------------- 120: 121: jingle_session_is_established_for_full_jids(Config) -> 122: escalus:fresh_story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) -> 123: {InviteStanza, InviteRequest} = initiate_jingle_session(Alice, Bob), 124: 125: accept_jingle_session(Alice, Bob, InviteStanza, InviteRequest) 126: end). 127: 128: jingle_session_is_established_for_full_jids_on_different_nodes(Config) -> 129: escalus:fresh_story(Config, [{alice, 1}, {clusterguy, 1}], fun(Alice, Bob) -> 130: {InviteStanza, InviteRequest} = initiate_jingle_session(Alice, Bob), 131: 132: accept_jingle_session(Alice, Bob, InviteStanza, InviteRequest) 133: end). 134: 135: resp_4xx_from_sip_proxy_results_in_session_terminate(Config) -> 136: escalus:fresh_story(Config, [{alice, 1}], fun(Alice) -> 137: {_InviteStanza, FirstIQSet} = send_initiate_and_wait_for_first_iq_set(Alice, <<"error.480@localhost">>), 138: assert_is_session_terminate(FirstIQSet, <<"general-error">>) 139: 140: end). 141: 142: jingle_session_is_established_when_calling_a_number(Config) -> 143: escalus:fresh_story(Config, [{alice, 1}], fun(Alice) -> 144: {InviteStanza, FirstIQSet} = send_initiate_and_wait_for_first_iq_set(Alice, <<"+488790@numbers.localhost">>), 145: assert_ringing(InviteStanza, FirstIQSet), 146: AcceptInfo = escalus:wait_for_stanza(Alice, timer:seconds(5)), 147: assert_accept_response(InviteStanza, AcceptInfo), 148: 149: ok 150: 151: end). 152: 153: jingle_session_is_established_with_a_conference_room(Config) -> 154: escalus:fresh_story(Config, [{alice, 1}], fun(Alice) -> 155: {InviteStanza, FirstIQSet} = send_initiate_and_wait_for_first_iq_set(Alice, <<"*901@numbers.localhost">>), 156: assert_ringing(InviteStanza, FirstIQSet), 157: AcceptInfo = escalus:wait_for_stanza(Alice, timer:seconds(5)), 158: assert_accept_response(InviteStanza, AcceptInfo), 159: 160: TransportInfo = escalus:wait_for_stanza(Alice), 161: assert_transport_info(InviteStanza, TransportInfo), 162: 163: ok 164: end). 165: 166: jingle_session_initiate_is_resent_on_demand(Config) -> 167: escalus:fresh_story(Config, [{alice, 1}, {bob, 2}], fun(Alice, Bob, Bob2) -> 168: %% Bob2 becomes unavailalbe 169: push_helper:become_unavailable(Bob2), 170: escalus:wait_for_stanza(Bob), %%Bob first device gets unavailable form the other 171: {InviteStanza, InviteRequest} = initiate_jingle_session(Alice, Bob), 172: 173: escalus_assert:has_no_stanzas(Bob2), 174: 175: SID = exml_query:path(InviteRequest, path_to_jingle_sid()), 176: 177: ResendSessionInitiateIQ = iq_get(jingle_element(SID, <<"existing-session-initiate">>, [])), 178: 179: %% Bob2 becomes available 180: escalus:send(Bob2, escalus_stanza:presence(<<"available">>)), 181: escalus:wait_for_stanzas(Bob2, 2), %% 2 presence stansa from Bob2 and Bob 182: %% and asks for the session-initiate received by the other device 183: %% this is to get the invite in new session (new browser window) 184: escalus:send(Bob2, ResendSessionInitiateIQ), 185: ResendResult = escalus:wait_for_stanza(Bob2), 186: escalus:assert(is_iq_result, [ResendSessionInitiateIQ], ResendResult), 187: InviteRequest2 = escalus:wait_for_stanza(Bob2), 188: assert_same_sid(InviteRequest, InviteRequest2), 189: assert_invite_request(InviteStanza, InviteRequest2), 190: AliceShortJID = escalus_client:short_jid(Alice), 191: escalus:assert(is_stanza_from, [AliceShortJID], InviteRequest2) 192: 193: 194: end). 195: 196: jingle_session_is_terminated_on_other_receivers_devices(Config) -> 197: escalus:fresh_story(Config, [{alice, 1}, {bob, 2}], fun(Alice, Bob, Bob2) -> 198: {InviteStanza, InviteRequest} = initiate_jingle_session(Alice, Bob), 199: %% The other Bob's device also gets the invite 200: InviteRequest2 = escalus:wait_for_stanza(Bob2), 201: 202: %% then bob accepts the call on one of the devices 203: assert_same_sid(InviteRequest, InviteRequest2), 204: accept_jingle_session(Alice, Bob2, InviteStanza, InviteRequest2), 205: 206: %% then Bob's first device gets cancel request 207: Terminate = escalus:wait_for_stanza(Bob), 208: assert_is_session_terminate(Terminate, <<"cancel">>) 209: 210: end). 211: 212: 213: mongoose_replies_with_480_when_invitee_is_offline(Config) -> 214: escalus:fresh_story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) -> 215: %% Bob becomes unavailalbe 216: push_helper:become_unavailable(Bob), 217: jingle_sip_translator:send_invite(Alice, Bob, self()), 218: 219: receive 220: {sip_resp, 480} -> 221: ok; 222: {sip_resp, Other} -> 223: ct:fail("Received SIP resp: ~p", [Other]) 224: after timer:seconds(5) -> 225: ct:fail(timeout_waiting_for_sip_resp) 226: end 227: 228: end). 229: 230: mongoose_returns_404_when_not_authorized_user_tires_to_accept_a_session(Config) -> 231: escalus:fresh_story(Config, [{alice, 1}, {bob, 1}, {kate, 1}], fun(Alice, Bob, Kate) -> 232: {InviteStanza, _InviteRequest} = initiate_jingle_session(Alice, Bob), 233: %% Then Kate tries to accept Alice's session 234: AcceptStanza = escalus_stanza:to(jingle_accept(InviteStanza), Bob), 235: escalus:send(Kate, AcceptStanza), 236: AcceptResult = escalus:wait_for_stanza(Kate, timer:seconds(5)), 237: escalus:assert(is_iq_error, AcceptResult), 238: escalus:assert(is_error, [<<"cancel">>, <<"item-not-found">>], AcceptResult) 239: 240: 241: end). 242: 243: mongoose_returns_404_when_nto_authorized_user_tries_to_cancel_a_session(Config) -> 244: escalus:fresh_story(Config, [{alice, 1}, {bob, 1}, {kate, 1}], fun(Alice, Bob, Kate) -> 245: {_InviteStanza, InviteRequest} = initiate_jingle_session(Alice, Bob), 246: %% Then Kate tries to cancel Bob's session 247: 248: Result = send_session_terminate_request(Kate, Alice, InviteRequest, <<"decline">>), 249: 250: escalus:assert(is_iq_error, Result), 251: escalus:assert(is_error, [<<"cancel">>, <<"item-not-found">>], Result) 252: 253: end). 254: 255: jingle_session_is_established_and_terminated_by_initiator(Config) -> 256: escalus:fresh_story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) -> 257: {InviteStanza, InviteRequest} = initiate_jingle_session(Alice, Bob), 258: 259: accept_jingle_session(Alice, Bob, InviteStanza, InviteRequest), 260: timer:sleep(1000), 261: 262: %% Then Alice (who initiated) terminates the call 263: terminate_jingle_session(Alice, Bob, InviteStanza) 264: 265: end). 266: 267: jingle_session_is_established_and_terminated_by_receiver(Config) -> 268: escalus:fresh_story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) -> 269: {InviteStanza, InviteRequest} = initiate_jingle_session(Alice, Bob), 270: 271: accept_jingle_session(Alice, Bob, InviteStanza, InviteRequest), 272: 273: timer:sleep(timer:seconds(5)), 274: %% then Bob (who was invited to the call) terminates the call 275: %% it's important that bob terminates the call from the invite he got 276: terminate_jingle_session(Bob, Alice, InviteRequest) 277: 278: end). 279: 280: jingle_session_is_established_and_terminated_by_receiver_on_different_node(Config) -> 281: escalus:fresh_story(Config, [{alice, 1}, {clusterguy, 1}], fun(Alice, Bob) -> 282: {InviteStanza, InviteRequest} = initiate_jingle_session(Alice, Bob), 283: 284: accept_jingle_session(Alice, Bob, InviteStanza, InviteRequest), 285: 286: timer:sleep(timer:seconds(5)), 287: %% then Bob (who was invited to the call) terminates the call 288: %% it's important that bob terminates the call from the invite he got 289: terminate_jingle_session(Bob, Alice, InviteRequest) 290: 291: end). 292: 293: 294: jingle_session_is_intiated_and_canceled_by_initiator(Config) -> 295: escalus:fresh_story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) -> 296: {InviteStanza, _InviteRequest} = initiate_jingle_session(Alice, Bob), 297: timer:sleep(1000), 298: %% then Bob (who was invited to the call) terminates the call 299: terminate_jingle_session(Alice, Bob, InviteStanza, <<"decline">>) 300: 301: end). 302: 303: jingle_session_is_intiated_and_canceled_by_receiver(Config) -> 304: escalus:fresh_story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) -> 305: {_InviteStanza, InviteRequest} = initiate_jingle_session(Alice, Bob), 306: 307: timer:sleep(1000), 308: %% then Bob (who was invited to the call) terminates the call 309: terminate_jingle_session(Bob, Alice, InviteRequest, <<"decline">>) 310: end). 311: 312: jingle_session_is_intiated_and_canceled_by_receiver_on_different_node(Config) -> 313: escalus:fresh_story(Config, [{alice, 1}, {clusterguy, 1}], fun(Alice, Bob) -> 314: {_InviteStanza, InviteRequest} = initiate_jingle_session(Alice, Bob), 315: 316: timer:sleep(1000), 317: %% then Bob (who was invited to the call) terminates the call 318: terminate_jingle_session(Bob, Alice, InviteRequest, <<"decline">>) 319: end). 320: 321: mongoose_sends_reINVITE_on_source_remove_action(Config) -> 322: escalus:fresh_story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) -> 323: {InviteStanza, InviteRequest} = initiate_jingle_session(Alice, Bob), 324: accept_jingle_session(Alice, Bob, InviteStanza, InviteRequest), 325: 326: %% then Alice sends source-remove 327: SourceRemoveStanza = escalus_stanza:to(jingle_source_remove(InviteStanza), Bob), 328: escalus:send(Alice, SourceRemoveStanza), 329: SourceRemoveResult = escalus:wait_for_stanza(Alice), 330: escalus:assert(is_iq_result, [SourceRemoveStanza], SourceRemoveResult), 331: SourceRemoveToBob = escalus:wait_for_stanza(Bob), 332: assert_source_remove_action(SourceRemoveToBob, InviteRequest) 333: end). 334: 335: mongoose_sends_reINVITE_on_source_add_action(Config) -> 336: escalus:fresh_story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) -> 337: {InviteStanza, InviteRequest} = initiate_jingle_session(Alice, Bob), 338: accept_jingle_session(Alice, Bob, InviteStanza, InviteRequest), 339: 340: %% then Alice sends source-remove 341: SourceRemoveStanza = escalus_stanza:to(jingle_source_add(InviteStanza), Bob), 342: escalus:send(Alice, SourceRemoveStanza), 343: SourceRemoveResult = escalus:wait_for_stanza(Alice), 344: escalus:assert(is_iq_result, [SourceRemoveStanza], SourceRemoveResult), 345: SourceRemoveToBob = escalus:wait_for_stanza(Bob), 346: assert_source_add_action(SourceRemoveToBob, InviteRequest) 347: end). 348: 349: mongoose_sends_reINVITE_on_source_update_action(Config) -> 350: escalus:fresh_story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) -> 351: {InviteStanza, InviteRequest} = initiate_jingle_session(Alice, Bob), 352: accept_jingle_session(Alice, Bob, InviteStanza, InviteRequest), 353: 354: %% then Alice sends source-remove 355: SourceRemoveStanza = escalus_stanza:to(jingle_source_update(InviteStanza), Bob), 356: escalus:send(Alice, SourceRemoveStanza), 357: SourceRemoveResult = escalus:wait_for_stanza(Alice), 358: escalus:assert(is_iq_result, [SourceRemoveStanza], SourceRemoveResult), 359: SourceRemoveToBob = escalus:wait_for_stanza(Bob), 360: assert_source_update_action(SourceRemoveToBob, InviteRequest) 361: end). 362: %%-------------------------------------------------------------------- 363: %% Helpers 364: %%-------------------------------------------------------------------- 365: 366: accept_jingle_session(Alice, Bob, InviteStanza, InviteRequest) -> 367: AcceptStanza = escalus_stanza:to(jingle_accept(InviteRequest), Alice), 368: escalus:send(Bob, AcceptStanza), 369: AcceptResult = escalus:wait_for_stanza(Bob, timer:seconds(5)), 370: escalus:assert(is_iq_result, AcceptResult), 371: 372: AcceptInfo = escalus:wait_for_stanza(Alice, timer:seconds(5)), 373: assert_accept_response(InviteStanza, AcceptInfo). 374: 375: initiate_jingle_session(Alice, Bob) -> 376: {InviteStanza, _FirstIQSet} = send_initiate_and_wait_for_first_iq_set(Alice, Bob), 377: 378: %jingle_sip_translator:send_invite(Alice, Bob), 379: 380: InviteRequest = escalus:wait_for_stanza(Bob, timer:seconds(5)), 381: escalus:assert(is_iq_set, InviteRequest), 382: assert_invite_request(InviteStanza, InviteRequest), 383: {InviteStanza, InviteRequest}. 384: 385: send_initiate_and_wait_for_first_iq_set(Alice, Bob) -> 386: InviteStanza = escalus_stanza:to(jingle_initiate(), Bob), 387: escalus:send(Alice, InviteStanza), 388: SessionInitiateResult = escalus:wait_for_stanza(Alice, timer:seconds(5)), 389: escalus:assert(is_iq_result, SessionInitiateResult), 390: RingingStanza = escalus:wait_for_stanza(Alice, timer:seconds(5)), 391: escalus:assert(is_iq_set, RingingStanza), 392: {InviteStanza, RingingStanza}. 393: 394: terminate_jingle_session(Terminator, Other, InviteStanza) -> 395: terminate_jingle_session(Terminator, Other, InviteStanza, <<"success">>). 396: 397: terminate_jingle_session(Terminator, Other, InviteStanza, Reason) -> 398: TerminateResult = send_session_terminate_request(Terminator, Other, 399: InviteStanza, Reason), 400: escalus:assert(is_iq_result, TerminateResult), 401: 402: TerminateInfo = escalus:wait_for_stanza(Other, timer:seconds(5)), 403: assert_is_session_terminate(TerminateInfo, Reason). 404: 405: send_session_terminate_request(Terminator, Other, InviteStanza, Reason) -> 406: TerminateStanza = escalus_stanza:to(jingle_terminate(InviteStanza, Reason), Other), 407: escalus:send(Terminator, TerminateStanza), 408: escalus:wait_for_stanza(Terminator, timer:seconds(5)). 409: 410: assert_invite_request(InviteStanza, InviteRequest) -> 411: JingleEl = exml_query:subelement(InviteRequest, <<"jingle">>), 412: ?assertEqual(<<"session-initiate">>, 413: (exml_query:attr(JingleEl, <<"action">>))), 414: 415: assert_different_sid(InviteStanza, InviteRequest), 416: assert_session_description(JingleEl). 417: 418: assert_ringing(InviteStanza, RingingStanza) -> 419: JingleEl = exml_query:subelement(RingingStanza, <<"jingle">>), 420: ?assertEqual(<<"session-info">>, 421: (exml_query:attr(JingleEl, <<"action">>))), 422: Ringing = exml_query:subelement(JingleEl, <<"ringing">>), 423: ?assertMatch(#xmlel{}, Ringing), 424: ?assertEqual(<<"urn:xmpp:jingle:apps:rtp:info:1">>, exml_query:attr(Ringing, <<"xmlns">>)), 425: 426: assert_same_sid(InviteStanza, RingingStanza). 427: 428: 429: assert_session_description(JingleEl) -> 430: Contents = exml_query:subelements(JingleEl, <<"content">>), 431: ?assertMatch([#xmlel{} | _], Contents), 432: 433: [assert_transport_with_candidate(Content) || Content <- Contents], 434: 435: case Contents of 436: [_, _ | _ ] -> %% For at least 2 contents 437: ?assertMatch((#xmlel{}), (exml_query:subelement(JingleEl, <<"group">>))); 438: _ -> 439: ok 440: end. 441: 442: assert_transport_with_candidate(Content) -> 443: TransportEl = exml_query:subelement(Content, <<"transport">>), 444: ?assertMatch((#xmlel{}), TransportEl), 445: ?assertMatch([#xmlel{} | _], (exml_query:subelements(TransportEl, <<"candidate">>))). 446: 447: assert_different_sid(Sent, Received) -> 448: SIDPath = path_to_jingle_sid(), 449: ?assertNotEqual((exml_query:path(Sent, SIDPath)), 450: (exml_query:path(Received, SIDPath))). 451: assert_same_sid(Sent, Received) -> 452: SIDPath = path_to_jingle_sid(), 453: ?assertEqual((exml_query:path(Sent, SIDPath)), 454: (exml_query:path(Received, SIDPath))). 455: 456: path_to_jingle_sid() -> [{element, <<"jingle">>}, {attr, <<"sid">>}]. 457: 458: assert_accept_response(InviteStanza, AcceptResponse) -> 459: JingleEl = exml_query:subelement(AcceptResponse, <<"jingle">>), 460: ?assertEqual(<<"session-accept">>, 461: (exml_query:attr(JingleEl, <<"action">>))), 462: 463: assert_same_sid(InviteStanza, AcceptResponse), 464: assert_session_description(JingleEl). 465: 466: assert_is_session_terminate(FirstIQSet, ReasonName) -> 467: JingleEl = exml_query:subelement(FirstIQSet, <<"jingle">>), 468: ?assertEqual(<<"session-terminate">>, 469: (exml_query:attr(JingleEl, <<"action">>))), 470: ?assertMatch((#xmlel{}), 471: exml_query:path(JingleEl, 472: [{element, <<"reason">>}, {element, ReasonName}])). 473: 474: assert_transport_info(InviteStanza, TransportInfo) -> 475: JingleEl = exml_query:subelement(TransportInfo, <<"jingle">>), 476: ?assertEqual(<<"transport-info">>, 477: (exml_query:attr(JingleEl, <<"action">>))), 478: assert_same_sid(InviteStanza, TransportInfo). 479: 480: assert_source_remove_action(SourceRemoveRequest, InviteRequest) -> 481: assert_same_sid(InviteRequest, SourceRemoveRequest), 482: JingleEl = exml_query:subelement(SourceRemoveRequest, <<"jingle">>), 483: ?assertEqual(<<"source-remove">>, 484: (exml_query:attr(JingleEl, <<"action">>))). 485: 486: assert_source_add_action(SourceRemoveRequest, InviteRequest) -> 487: assert_same_sid(InviteRequest, SourceRemoveRequest), 488: JingleEl = exml_query:subelement(SourceRemoveRequest, <<"jingle">>), 489: ?assertEqual(<<"source-add">>, 490: (exml_query:attr(JingleEl, <<"action">>))). 491: 492: assert_source_update_action(SourceRemoveRequest, InviteRequest) -> 493: assert_same_sid(InviteRequest, SourceRemoveRequest), 494: JingleEl = exml_query:subelement(SourceRemoveRequest, <<"jingle">>), 495: ?assertEqual(<<"source-update">>, 496: (exml_query:attr(JingleEl, <<"action">>))). 497: 498: jingle_stanza_addressed_to_bare_jid_is_delivered(Config) -> 499: escalus:fresh_story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) -> 500: BobBareJID = escalus_client:short_jid(Bob), 501: Stanza = escalus_stanza:to(jingle_initiate(), BobBareJID), 502: escalus:send(Alice, Stanza), 503: R = escalus:wait_for_stanza(Bob), 504: escalus:assert(is_iq_set, R), 505: ?assertEqual(exml_query:attr(Stanza, <<"id">>), exml_query:attr(R, <<"id">>)) 506: end). 507: 508: jingle_stanza_addressed_to_own_bare_jid_is_rejected(Config) -> 509: escalus:fresh_story(Config, [{alice, 1}], fun(Alice) -> 510: AliceBareJID = escalus_client:short_jid(Alice), 511: Stanza = escalus_stanza:to(jingle_initiate(), AliceBareJID), 512: escalus:send(Alice, Stanza), 513: R = escalus:wait_for_stanza(Alice), 514: escalus:assert(is_error, [<<"modify">>, <<"bad-request">>], R) 515: end). 516: 517: 518: other_iq_stanza_addressed_to_bare_jid_are_not_routed(Config) -> 519: escalus:fresh_story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) -> 520: BobBareJID = escalus_client:short_jid(Bob), 521: Stanza = escalus_stanza:to(escalus_stanza:iq_set(<<"urn:unknown:iq:ns">>, []), BobBareJID), 522: escalus:send(Alice, Stanza), 523: Reply = escalus:wait_for_stanza(Alice), 524: escalus:assert(is_error, [<<"cancel">>, <<"service-unavailable">>], Reply) 525: 526: end). 527: 528: 529: jingle_initiate() -> 530: Audio = content(audio), 531: Video = content(video), 532: I = jingle_element(<<"session-initiate">>, [Audio, Video, content_group([Audio, Video])]), 533: iq_set(I). 534: 535: iq_set(I) -> 536: Stanza = escalus_stanza:iq_set_nonquery(<<"jabber:client">>, [I]), 537: iq_with_id(Stanza). 538: 539: iq_with_id(#xmlel{attrs = Attrs} = Stanza) -> 540: NewAttrs = lists:keystore(<<"id">>, 1, Attrs, {<<"id">>, uuid:uuid_to_string(uuid:get_v4(), binary_standard)}), 541: Stanza#xmlel{attrs = NewAttrs}. 542: 543: iq_get(I) -> 544: Stanza = #xmlel{name = <<"iq">>, 545: attrs = [{<<"xmlns">>, <<"jabber:client">>}, 546: {<<"type">>, <<"get">>}], 547: children = [I]}, 548: iq_with_id(Stanza). 549: 550: jingle_element(Action, Children) -> 551: SID = uuid:uuid_to_string(uuid:get_v4(), binary_standard), 552: jingle_element(SID, Action, Children). 553: 554: jingle_element(SID, Action, Children) -> 555: #xmlel{name = <<"jingle">>, 556: attrs = [{<<"action">>, Action}, 557: {<<"sid">>, SID}, 558: {<<"xmlns">>, <<"urn:xmpp:jingle:1">>}], 559: children = Children}. 560: 561: jingle_accept(InviteRequest) -> 562: SID = exml_query:path(InviteRequest, path_to_jingle_sid()), 563: Audio = content(audio), 564: Video = content(video_disabled), 565: I = jingle_element(SID, <<"session-accept">>, [Audio, Video, content_group([Audio])]), 566: iq_set(I). 567: 568: 569: jingle_source_remove(InviteRequest) -> 570: SID = exml_query:path(InviteRequest, path_to_jingle_sid()), 571: Audio = content(audio_source_remove), 572: Video = content(video_source_remove), 573: I = jingle_element(SID, <<"source-remove">>, [Audio, Video, 574: content_group([Audio, Video])]), 575: iq_set(I). 576: 577: jingle_source_add(InviteRequest) -> 578: SID = exml_query:path(InviteRequest, path_to_jingle_sid()), 579: Audio = content(audio_source_remove), 580: Video = content(video_source_remove), 581: I = jingle_element(SID, <<"source-add">>, [Audio, Video, 582: content_group([Audio, Video])]), 583: iq_set(I). 584: 585: jingle_source_update(InviteRequest) -> 586: SID = exml_query:path(InviteRequest, path_to_jingle_sid()), 587: Audio = content(audio_source_remove), 588: Video = content(video_source_remove), 589: I = jingle_element(SID, <<"source-update">>, [Audio, Video, 590: content_group([Audio, Video])]), 591: iq_set(I). 592: 593: jingle_transport_info(InviteRequest, Creator, Media, TransportAttrs) -> 594: SID = exml_query:path(InviteRequest, path_to_jingle_sid()), 595: iq_set(jingle_element(SID, <<"transport-info">>, [trickle_ice_candidate(Creator, Media, TransportAttrs)])). 596: 597: jingle_terminate(InviteRequest, Reason) -> 598: SID = exml_query:path(InviteRequest, path_to_jingle_sid()), 599: ReasonEl = #xmlel{name = <<"reason">>, 600: children = [#xmlel{name = Reason}]}, 601: iq_set(jingle_element(SID, <<"session-terminate">>, [ReasonEl])). 602: 603: get_ice_candidates() -> 604: [ 605: [{<<"foundation">>, <<"1293499931">>}, {<<"component">>, <<"1">>}, {<<"protocol">>, <<"udp">>}, {<<"priority">>, <<"2122260223">>}, {<<"ip">>, <<"172.86.160.16">>}, {<<"port">>, <<"46515">>}, {<<"type">>, <<"host">>}, {<<"generation">>, <<"0">>}, {<<"network">>, <<"1">>}, {<<"id">>, <<"1.1947885zlx">>}] 606: ]. 607: 608: trickle_ice_candidate(Creator, Content, TransportAttrs) -> 609: Candidate = #xmlel{name = <<"candidate">>, 610: attrs = TransportAttrs}, 611: Transport = #xmlel{name = <<"transport">>, 612: attrs = [{<<"xmlns">>, <<"urn:xmpp:jingle:transports:ice-udp:1">>}, 613: {<<"ufrag">>, <<"7Gpn">>}, 614: {<<"pwd">>, <<"MUOzzatqL2qP7n1uRC7msD+c">>}], 615: children = [Candidate]}, 616: #xmlel{name = <<"content">>, 617: attrs = [{<<"name">>, Content}, 618: {<<"creator">>, Creator}], 619: children = [Transport]}.