1 /* 2 * Copyright (C) 2012-2018 Doubango Telecom <http://www.doubango.org> 3 * License: BSD 4 * This file is part of Open Source sipML5 solution <http://www.sipml5.org> 5 */ 6 7 /** 8 @fileoverview This is SIPML5 "library" contains a lot of classes and functions. 9 10 @name sipML5 API 11 @author Doubango Telecom <http://www.doubango.org> 12 @version 2.1.4 13 */ 14 15 /** 16 @namespace 17 @description Root namesapce. 18 */ 19 SIPml = {}; 20 21 /** @private */SIPml.b_initialized = false; 22 /** @private */SIPml.b_initializing = false; 23 /** @private */SIPml.s_navigator_friendly_name = 'unknown'; 24 /** @private */SIPml.b_navigator_outdated = false; 25 /** @private */SIPml.s_navigator_version = 'unknown'; 26 /** @private */SIPml.s_system_friendly_name = 'unknown'; 27 /** @private */SIPml.b_webrtc4all_plugin_outdated = false; 28 /** @private */SIPml.b_webrtc4all_supported = false; 29 /** @private */SIPml.s_webrtc4all_version = 'unknown'; 30 /** @private */SIPml.b_have_media_stream = false; 31 /** @private */SIPml.b_webrtc_supported = false; 32 33 34 /** 35 Sets the debug level. 36 @since version 1.3.203 37 @param {String} level The level. Supported values: <i>info</i>, <i>warn</i>, <i>error</i> and <i>fatal</i>. 38 */ 39 SIPml.setDebugLevel = function (level) { 40 tsk_utils_log_set_level(level === 'fatal' ? 1 : (level === 'error' ? 2 : (level === 'warn' ? 3 : 4))); 41 } 42 43 /** 44 Starts debugging the native (C/C++) code. Requires webrt4all plugin. 45 On Windows, the output file should be at <b>C:\Users\<YOUR LOGIN>\AppData\Local\Temp\Low\webrtc4all.log</b>. 46 Starting the native debug isn't recommended and must be done to track issues only. 47 @since version 2.0.0 48 */ 49 SIPml.startNativeDebug = function () { 50 WebRtc4all_GetPlugin().startDebug(); 51 } 52 53 /** 54 Stops debugging the native (C/C++) code. Requires webrt4all plugin. 55 @since version 2.0.0 56 */ 57 SIPml.stopNativeDebug = function () { 58 WebRtc4all_GetPlugin().stopDebug(); 59 } 60 61 /** 62 Gets the current WebRTC type being used. 63 @since version 1.4.217 64 @returns {String} the WebRTC type. Possible values: <i>native</i>, <i>w4a</i>, <i>erisson</i> or <i>unknown</i>. 65 */ 66 SIPml.getWebRtcType = function () { 67 switch (WebRtc4all_GetType()) { 68 case WebRtcType_e.W4A: case WebRtcType_e.IE: case WebRtcType_e.NPAPI: return "w4a"; 69 case WebRtcType_e.ERICSSON: return "erisson"; 70 case WebRtcType_e.NATIVE: return "native"; 71 default: return "unknown"; 72 } 73 } 74 75 /** 76 Sets the default webrtc type. Must be called before <a href="#.init">initializing</a> the engine. 77 @since version 2.0.0 78 @param {String} type The type. Supported values: <i>native</i>, <i>w4a</i> and <i>erisson</i>. 79 @returns {Boolean} <i>true</i> if succeed; otherwise <i>false</i> 80 */ 81 SIPml.setWebRtcType = function (type) { 82 if (SIPml.isInitialized()) { 83 throw new Error("ERR_ALREADY_INITIALIZED: Engine already initialized."); 84 } 85 return WebRtc4all_SetType(type); 86 } 87 88 /** 89 Gets the list of running apps. Requires webrt4all plugin. 90 @since version 2.0.0 91 @returns {String} the the list of running apps. Format: <i>(base64($$WindowID$$=str(...)$$Description$$=str(...)$$IconData$$=base64(...)$$IconType$$=str(...)))*</i> 92 @throws {ERR_NOT_READY | ERR_NOT_SUPPORTED} <font color="red">ERR_NOT_READY</font> | <font color="red">ERR_NOT_SUPPORTED</font> 93 */ 94 SIPml.getRunningApps = function () { 95 if (SIPml.getWebRtcType() != 'w4a') { 96 throw new Error("ERR_NOT_SUPPORTED: requires webrtc4all plugin"); 97 } 98 return WebRtc4all_GetPlugin().runningApps(); 99 } 100 101 /** 102 Sets the video fps. Requires webrt4all plugin. 103 @since version 2.0.0 104 @param {Integer} fps fps value. 105 @returns {Integer} 0 if successful; otherwise nonzero 106 @throws {ERR_NOT_READY | ERR_NOT_SUPPORTED} <font color="red">ERR_NOT_READY</font> | <font color="red">ERR_NOT_SUPPORTED</font> 107 */ 108 SIPml.setFps = function (fps) { 109 if (SIPml.getWebRtcType() != 'w4a') { 110 throw new Error("ERR_NOT_SUPPORTED: Setting maximum video size requires webrtc4all plugin"); 111 } 112 WebRtc4all_GetPlugin().fps = fps; 113 return 0; 114 } 115 116 117 /** 118 Sets the maximum video size. Requires webrt4all plugin. 119 @since version 2.0.0 120 @param {String} maxVideoSize maxVideoSize value. Supported values: "sqcif", "qcif" "qvga" "cif" "hvga", "vga", "4cif", "svga", "480p", "720p", "16cif", "1080p", "2160p". 121 @returns {Integer} 0 if successful; otherwise nonzero 122 @throws {ERR_NOT_READY | ERR_NOT_SUPPORTED} <font color="red">ERR_NOT_READY</font> | <font color="red">ERR_NOT_SUPPORTED</font> 123 */ 124 SIPml.setMaxVideoSize = function (maxVideoSize) { 125 if (SIPml.getWebRtcType() != 'w4a') { 126 throw new Error("ERR_NOT_SUPPORTED: Setting FPS requires webrtc4all plugin"); 127 } 128 WebRtc4all_GetPlugin().maxVideoSize = maxVideoSize; 129 return 0; 130 } 131 132 /** 133 Sets the maximum bandwidth (upload). Requires webrt4all plugin. 134 @since version 2.0.0 135 @param {Integer} maxBandwidthUp maxBandwidthUp value (kbps). 136 @returns {Integer} 0 if successful; otherwise nonzero 137 @throws {ERR_NOT_READY | ERR_NOT_SUPPORTED} <font color="red">ERR_NOT_READY</font> | <font color="red">ERR_NOT_SUPPORTED</font> 138 */ 139 SIPml.setMaxBandwidthUp = function (maxBandwidthUp) { 140 if (SIPml.getWebRtcType() != 'w4a') { 141 throw new Error("ERR_NOT_SUPPORTED: Setting maximum bandwidth requires webrtc4all plugin"); 142 } 143 WebRtc4all_GetPlugin().maxBandwidthUp = maxBandwidthUp; 144 return 0; 145 } 146 147 /** 148 Sets the maximum bandwidth (down). Requires webrt4all plugin. 149 @since version 2.0.0 150 @param {Integer} maxBandwidthUp maxBandwidthUp value (kbps). 151 @returns {Integer} 0 if successful; otherwise nonzero 152 @throws {ERR_NOT_READY | ERR_NOT_SUPPORTED} <font color="red">ERR_NOT_READY</font> | <font color="red">ERR_NOT_SUPPORTED</font> 153 */ 154 SIPml.setMaxBandwidthDown = function (maxBandwidthDown) { 155 if (SIPml.getWebRtcType() != 'w4a') { 156 throw new Error("ERR_NOT_SUPPORTED: Setting maximum bandwidth requires webrtc4all plugin"); 157 } 158 WebRtc4all_GetPlugin().maxBandwidthDown = maxBandwidthDown; 159 return 0; 160 } 161 162 /** 163 Defines whether to enable "zero-artifacts" features. Requires webrt4all plugin. <br /> 164 More information about this option on Doubango's TelePresence wiki page: <a href="https://code.google.com/p/telepresence/wiki/Technical_Video_quality#Zero-artifacts">https://code.google.com/p/telepresence/wiki/Technical_Video_quality#Zero-artifacts</a> 165 @since version 2.0.0 166 @param {Boolean} zeroArtifacts New optional value. 167 @returns {Integer} 0 if successful; otherwise nonzero 168 @throws {ERR_NOT_READY | ERR_NOT_SUPPORTED} <font color="red">ERR_NOT_READY</font> | <font color="red">ERR_NOT_SUPPORTED</font> 169 */ 170 SIPml.setZeroArtifacts = function (zeroArtifacts) { 171 if (SIPml.getWebRtcType() != 'w4a') { 172 throw new Error("ERR_NOT_SUPPORTED: Setting maximum bandwidth requires webrtc4all plugin"); 173 } 174 WebRtc4all_GetPlugin().zeroArtifacts = zeroArtifacts; 175 return 0; 176 } 177 178 /** 179 Gets the version name of the installed <a href="http://code.google.com/p/webrtc4all/">webrtc4all plugin</a>. 180 You must <a href="#.init">initialize</a> the engine before calling this function. 181 @static 182 @returns {String} Version name (e.g. '1.12.756') 183 @throws {ERR_NOT_INITIALIZED} <font color="red">ERR_NOT_INITIALIZED</font> if the engine is not <a href="#.init">initialized</a>. 184 */ 185 SIPml.getWebRtc4AllVersion = function () { 186 if (!SIPml.isInitialized()) { 187 throw new Error("ERR_NOT_INITIALIZED: Engine not initialized yet. Please call 'SIPml.init()' first"); 188 } 189 return SIPml.s_webrtc4all_version; 190 }; 191 192 /** 193 Gets the web browser version (e.g. <i>'1.5.beta'</i>). 194 You must <a href="#.init">initialize</a> the engine before calling this function. 195 @static 196 @returns {String} The the web browser version. 197 @throws {ERR_NOT_INITIALIZED} <font color="red">ERR_NOT_INITIALIZED</font> if the engine is not <a href="#.init">initialized</a>. 198 */ 199 SIPml.getNavigatorVersion = function () { 200 if (!SIPml.isInitialized()) { 201 throw new Error("ERR_NOT_INITIALIZED: Engine not initialized yet. Please call 'SIPml.init()' first"); 202 } 203 return SIPml.s_navigator_version; 204 }; 205 206 /** 207 Gets the web browser friendly name (e.g. <i>'chrome'</i>, <i>'firefox'</i>, <i>'safari'</i>, <i>'opera'</i>, <i>'ie'</i> or <i>'netscape'</i>). 208 You must <a href="#.init">initialize</a> the engine before calling this function. 209 @static 210 @returns {String} The web browser friendly name. 211 @throws {ERR_NOT_INITIALIZED} <font color="red">ERR_NOT_INITIALIZED</font> if the engine is not <a href="#.init">initialized</a>. 212 */ 213 SIPml.getNavigatorFriendlyName = function () { 214 if (!SIPml.isInitialized()) { 215 throw new Error("ERR_NOT_INITIALIZED: Engine not initialized yet. Please call 'SIPml.init()' first"); 216 } 217 return SIPml.s_navigator_friendly_name; 218 }; 219 220 /** 221 Gets the Operating System friendly name (e.g. <i>'windows'</i>, <i>'mac'</i>, <i>'lunix'</i>, <i>'solaris'</i>, <i>'sunos'</i> or <i>'powerpc'</i>). 222 You must <a href="#.init">initialize</a> the engine before calling this function. 223 @static 224 @returns {String} The Operating System friendly name. 225 @throws {ERR_NOT_INITIALIZED} <font color="red">ERR_NOT_INITIALIZED</font> if the engine is not <a href="#.init">initialized</a>. 226 */ 227 SIPml.getSystemFriendlyName = function () { 228 if (!SIPml.isInitialized()) { 229 throw new Error("ERR_NOT_INITIALIZED: Engine not initialized yet. Please call 'SIPml.init()' first"); 230 } 231 return SIPml.s_system_friendly_name; 232 }; 233 234 /** 235 Checks whether the web browser supports WebRTC but is outdated. 236 You must <a href="#.init">initialize</a> the engine before calling this function. 237 @static 238 @returns {Boolean} <i>true</i> if outdated; otherwise <i>false</i> 239 @throws {ERR_NOT_INITIALIZED} <font color="red">ERR_NOT_INITIALIZED</font> if the engine is not <a href="#.init">initialized</a>. 240 */ 241 SIPml.isNavigatorOutdated = function () { 242 if (!SIPml.isInitialized()) { 243 throw new Error("ERR_NOT_INITIALIZED: Engine not initialized yet. Please call 'SIPml.init()' first"); 244 } 245 return SIPml.b_navigator_outdated; 246 } 247 248 /** 249 Checks whether the <a href="http://code.google.com/p/webrtc4all/">webrtc4all plugin</a> is outdated or not. 250 You must <a href="#.init">initialize</a> the engine before calling this function. 251 @static 252 @returns {Boolean} <i>true</i> if outdated; otherwise <i>false</i> 253 @throws {ERR_NOT_INITIALIZED} <font color="red">ERR_NOT_INITIALIZED</font> if the engine is not <a href="#.init">initialized</a>. 254 */ 255 SIPml.isWebRtc4AllPluginOutdated = function () { 256 if (!SIPml.isInitialized()) { 257 throw new Error("ERR_NOT_INITIALIZED: Engine not initialized yet. Please call 'SIPml.init()' first"); 258 } 259 return SIPml.b_webrtc4all_plugin_outdated; 260 } 261 262 /** 263 Checks whether the <a href="http://code.google.com/p/webrtc4all/">webrtc4all plugin</a> is installed or not. 264 You must <a href="#.init">initialize</a> the engine before calling this function. 265 @static 266 @returns {Boolean} <i>true</i> if supported; otherwise <i>false</i> 267 @throws {ERR_NOT_INITIALIZED} <font color="red">ERR_NOT_INITIALIZED</font> if the engine is not <a href="#.init">initialized</a>. 268 */ 269 SIPml.isWebRtc4AllSupported = function () { 270 if (!SIPml.isInitialized()) { 271 throw new Error("ERR_NOT_INITIALIZED: Engine not initialized yet. Please call 'SIPml.init()' first"); 272 } 273 return SIPml.b_webrtc4all_supported; 274 } 275 276 /** 277 Checks whether Screen share is supported on this browser. 278 You must <a href="#.init">initialize</a> the engine before calling this function. 279 @since version 1.3.203 280 @static 281 @returns {Boolean} <i>true</i> if supported; otherwise <i>false</i> 282 @throws {ERR_NOT_INITIALIZED} <font color="red">ERR_NOT_INITIALIZED</font> if the engine is not <a href="#.init">initialized</a>. 283 */ 284 SIPml.isScreenShareSupported = function () { 285 if (!SIPml.isInitialized()) { 286 throw new Error("ERR_NOT_INITIALIZED: Engine not initialized yet. Please call 'SIPml.init()' first"); 287 } 288 return (SIPml.getWebRtcType() === "w4a") || (navigator.userAgent.match('Chrome') && parseInt(navigator.userAgent.match(/Chrome\/(.*) /)[1]) >= 26); 289 } 290 291 /** 292 Checks whether WebRTC is supported or not. 293 You must <a href="#.init">initialize</a> the engine before calling this function. 294 @static 295 @returns {Boolean} <i>true</i> if supported; otherwise <i>false</i> 296 @throws {ERR_NOT_INITIALIZED} <font color="red">ERR_NOT_INITIALIZED</font> if the engine is not <a href="#.init">initialized</a>. 297 */ 298 SIPml.isWebRtcSupported = function () { 299 if (!SIPml.isInitialized()) { 300 throw new Error("ERR_NOT_INITIALIZED: Engine not initialized yet. Please call 'SIPml.init()' first"); 301 } 302 return SIPml.b_webrtc_supported; 303 } 304 305 /** 306 Checks whether WebSocket is supported or not. 307 @static 308 @returns {Boolean} <i>true</i> if supported; otherwise <i>false</i> 309 */ 310 SIPml.isWebSocketSupported = function () { 311 return tsk_utils_have_websocket(); 312 } 313 314 /** 315 Checks whether <a href="https://developer.mozilla.org/en-US/docs/WebRTC/navigator.getUserMedia">getUserMedia</a> is supported or not. The engined must be initialized before calling this function. 316 @static 317 @returns {Boolean} <i>true</i> if <a href="https://developer.mozilla.org/en-US/docs/WebRTC/navigator.getUserMedia">getUserMedia</a> is supported; otherwise <i>false</i> 318 */ 319 SIPml.haveMediaStream = function () { 320 if (!SIPml.isInitialized()) { 321 throw new Error("ERR_NOT_INITIALIZED: Engine not initialized yet. Please call 'SIPml.init()' first"); 322 } 323 return SIPml.b_have_media_stream; 324 } 325 326 /** 327 Checks whether the engine is ready to make/receive calls or not. <br /> 328 The engine is ready when: 329 <ul> 330 <li>engine is <a href="#.init">initialized</a></li> 331 <li>webrtc is supported</li> 332 <li>we got a valid media stream (from <a href="https://developer.mozilla.org/en-US/docs/WebRTC/navigator.getUserMedia">getUserMedia</a>)</li> 333 </ul> 334 @static 335 @returns {Boolean} <i>true</i> if the engine is ready; otherwise <i>false</i> 336 @throws {ERR_NOT_INITIALIZED} <font color="red">ERR_NOT_INITIALIZED</font> if the engine is not <a href="#.init">initialized</a>. 337 */ 338 SIPml.isReady = function () { 339 return (SIPml.isInitialized() && SIPml.isWebRtcSupported() && SIPml.haveMediaStream()); 340 } 341 342 /** 343 Checks whether the engine is initialized or not. To initialize the stack you must call <a href="#.init">init()</a> function. 344 @static 345 @returns {Boolean} <i>true</i> if the engine is initialized; otherwise <i>false</i> 346 */ 347 SIPml.isInitialized = function () { return SIPml.b_initialized; } 348 349 350 /** 351 Initialize the engine. <b>You must call this function before any other.</b>. 352 @param {CallbackFunction} [readyCallback] Optional callback function to call when the stack finish initializing and become ready. 353 @param {CallbackFunction} [errorCallback] Optional callback function to call when initialization fails. 354 355 @example 356 SIPml.init(function(e){ console.info('engine is ready'); }, function(e){ console.info('Error: ' + e.message); }); 357 @static 358 */ 359 SIPml.init = function (successCallback, errorCallback) { 360 if (!SIPml.b_initialized && !SIPml.b_initializing) { 361 SIPml.b_initializing = true; 362 tsk_utils_init_webrtc(); 363 364 tsk_utils_log_info('User-Agent=' + (navigator.userAgent || "unknown")); 365 366 SIPml.b_have_media_stream = tsk_utils_have_stream(); 367 SIPml.b_webrtc_supported = tsk_utils_have_webrtc(); 368 SIPml.b_webrtc4all_supported = tsk_utils_have_webrtc4all(); 369 SIPml.s_webrtc4all_version = tsk_utils_webrtc4all_get_version(); 370 SIPml.s_navigator_friendly_name = tsk_utils_get_navigator_friendly_name(); 371 SIPml.s_system_friendly_name = tsk_utils_get_system_friendly_name(); 372 373 // prints whether WebSocket is supported 374 tsk_utils_log_info("WebSocket supported = " + (SIPml.isWebSocketSupported() ? "yes" : "no")); 375 376 // check webrtc4all version 377 if (tsk_utils_have_webrtc4all()) { 378 tsk_utils_log_info("WebRTC type = " + WebRtc4all_GetType() + " version = " + tsk_utils_webrtc4all_get_version()); 379 if (SIPml.s_webrtc4all_version != '1.35.981') { 380 SIPml.b_webrtc4all_plugin_outdated = true; 381 } 382 } 383 384 // prints navigator friendly name 385 tsk_utils_log_info("Navigator friendly name = " + SIPml.s_navigator_friendly_name); 386 387 // gets navigator version 388 if (SIPml.s_navigator_friendly_name == 'ie') { 389 var re = new RegExp("MSIE ([0-9]{1,}[\.0-9]{0,})"); 390 if (re.exec(navigator.userAgent) != null) { 391 SIPml.s_navigator_version = RegExp.$1; 392 } 393 } 394 395 // prints OS friendly name 396 tsk_utils_log_info("OS friendly name = " + SIPml.s_system_friendly_name); 397 // prints support for WebRTC (native or plugin) 398 tsk_utils_log_info("Have WebRTC = " + (tsk_utils_have_webrtc() ? "yes" : "false")); 399 // prints support for getUserMedia 400 tsk_utils_log_info("Have GUM = " + (tsk_utils_have_stream() ? "yes" : "false")); 401 402 // checks for WebRTC support 403 if (!tsk_utils_have_webrtc()) { 404 // is it chrome? 405 if (SIPml.s_navigator_friendly_name == "chrome") { 406 SIPml.b_navigator_outdated = true; 407 return; 408 } 409 410 // for now the plugins (WebRTC4all only works on Windows) 411 if (SIPml.s_system_friendly_name == 'win' || SIPml.s_system_friendly_name == 'windows') { 412 // Internet explorer 413 if (SIPml.s_navigator_friendly_name == 'ie') { 414 // Check for IE version 415 var rv = -1; 416 var ua = navigator.userAgent; 417 var re = new RegExp("MSIE ([0-9]{1,}[\.0-9]{0,})"); 418 if (re.exec(ua) != null) { 419 rv = parseFloat(RegExp.$1); 420 } 421 if (rv < 9.0) { 422 SIPml.b_navigator_outdated = true; 423 return; 424 } 425 426 // break page loading ('window.location' won't stop JS execution) 427 if (!tsk_utils_have_webrtc4all()) { 428 return; 429 } 430 } 431 } 432 } 433 434 if (SIPml.b_webrtc_supported && SIPml.b_have_media_stream) { 435 SIPml.b_initialized = true; 436 SIPml.b_initializing = false; 437 tsk_utils_log_info("Engine initialized"); 438 if (successCallback) { 439 successCallback({}); 440 } 441 } 442 else { 443 if (errorCallback) { 444 var s_description = !SIPml.b_webrtc_supported ? "WebRTC not supported" : (!SIPml.b_have_media_stream ? "getUserMedia not supported" : "Internal error"); 445 errorCallback({ description: s_description }); 446 } 447 } 448 } 449 } 450 451 // ================================== SIPml.EventTarget ========================================== 452 453 /** 454 @constructor 455 Defines an event target. You sould never create an event target object. 456 */ 457 SIPml.EventTarget = function () { 458 this.ao_listeners = []; 459 } 460 461 /** 462 Adds an event listener to the target object. <br /><br /> 463 <table border="1"> 464 <tr> 465 <td><b>Target classes<b></td> 466 <td><b>Supported event types<b></td> 467 <td><b>Raised event object<b></td> 468 <td><b>Remarques<b></td> 469 </tr> 470 <tr> 471 <td><a href="SIPml.Stack.html" name="SIPml.EventTarget.Stack">SIPml.Stack</a></td> 472 <td> 473 <b>*</b><br/> starting<br/> started<br/> stopping<br/> stopped<br/> failed_to_start<br/> failed_to_stop<br/> i_new_call<br /> i_new_message<br /> 474 m_permission_requested<br/> m_permission_accepted<br/> m_permission_refused 475 </td> 476 <td><a href="SIPml.Stack.Event.html">SIPml.Stack.Event</a></td> 477 <td>'*' is used to listen for all events</td> 478 </tr> 479 <tr> 480 <td> 481 <a href="SIPml.Session.html" name="SIPml.EventTarget.Session">SIPml.Session</a> 482 <ul> 483 <li><a href="SIPml.Session.Call.html">SIPml.Session.Call</a></li> 484 <li><a href="SIPml.Session.Message.html">SIPml.Session.Message</a></li> 485 <li><a href="SIPml.Session.Message.html">SIPml.Session.Registration</a></li> 486 <li><a href="SIPml.Session.Message.html">SIPml.Session.Subscribe</a></li> 487 <li><a href="SIPml.Session.Message.html">SIPml.Session.Publish</a></li> 488 <ul> 489 </td> 490 <td><b>*</b><br/> connecting<br /> connected<br /> terminating<br /> terminated<br /> 491 i_ao_request<br /> 492 media_added<br/> media_removed<br/> 493 i_request<br/> o_request<br/> cancelled_request<br/> sent_request<br/> 494 transport_error<br/> global_error<br/> message_error<br/> webrtc_error 495 </td> 496 <td><a href="SIPml.Session.Event.html">SIPml.Session.Event</a></td> 497 <td>'*' is used to listen for all events<br /></td> 498 </tr> 499 <tr> 500 <td><a href="SIPml.Session.Call.html" name="SIPml.EventTarget.Session.Call">SIPml.Session.Call</a></td> 501 <td> 502 m_early_media<br/> m_local_hold_ok<br/> m_local_hold_nok<br/> m_local_resume_ok<br/> m_local_resume_nok<br/> m_remote_hold<br/> m_remote_resume<br/> 503 m_stream_video_local_added<br /> m_stream_video_local_removed<br/> m_stream_video_remote_added<br/> m_stream_video_remote_removed <br /> 504 m_stream_audio_local_added<br /> m_stream_audio_local_removed<br/> m_stream_audio_remote_added<br/> m_stream_audio_remote_removed <br /> 505 i_ect_new_call<br/> o_ect_trying<br/> o_ect_accepted<br/> o_ect_completed<br/> i_ect_completed<br/> o_ect_failed<br/> i_ect_failed<br/> o_ect_notify<br/> i_ect_notify<br/> i_ect_requested <br /> 506 m_bfcp_info<br /> 507 i_info 508 </td> 509 <td><a href="SIPml.Session.Event.html">SIPml.Session.Event</a></td> 510 <td>borrows all events supported by <a href="SIPml.Session.html">SIPml.Session</a></td> 511 </tr> 512 <tr> 513 <td><a href="SIPml.Session.Subscribe.html" name="SIPml.EventTarget.Session.Subscribe">SIPml.Session.Subscribe</a></td> 514 <td> 515 i_notify 516 </td> 517 <td><a href="SIPml.Session.Event.html">SIPml.Session.Event</a></td> 518 <td>borrows all events supported by <a href="SIPml.Session.html">SIPml.Session</a></td> 519 </tr> 520 </table> 521 @param {String|Array} type The event type/identifier. Must not be null or empty. Use <b>'*'</b> to listen for all events. 522 @param {function} listener The object that receives a notification when an event of the specified type occurs. This must be an object implementing the <a href="http://www.w3.org/TR/DOM-Level-2-Events/events.html#Events-EventListener">EventListener</a> interface, or simply a JavaScript function. 523 @example 524 // listen for a single event 525 this.addEventListener('started', function(e){ 526 console.info("'started' event fired"); 527 }); 528 // or listen for two or more events 529 this.addEventListener(['started', 'stopped'], function(e){ 530 console.info("'"+e.type+"' event fired"); 531 }); 532 // or listen for all events 533 this.addEventListener('*', function(e){ 534 console.info("'"+e.type+"' event fired"); 535 }); 536 @see <a href="#removeEventListener">removeEventListener</a> 537 @throws {ERR_INVALID_PARAMETER_VALUE|ERR_INVALID_PARAMETER_TYPE} <font color="red">ERR_INVALID_PARAMETER_VALUE</font> | <font color="red">ERR_INVALID_PARAMETER_TYPE</font> 538 */ 539 SIPml.EventTarget.prototype.addEventListener = function (o_type, o_listener) { 540 if (!o_listener) { 541 throw new Error("ERR_INVALID_PARAMETER_VALUE: 'listener' must not be null"); 542 } 543 if (!o_type) { 544 throw new Error("ERR_INVALID_PARAMETER_VALUE: 'type' must not be null"); 545 } 546 if (!(o_type instanceof String || typeof o_type == "string" || o_type instanceof Array)) { 547 throw new Error("ERR_INVALID_PARAMETER_TYPE: 'type' must be a string or array"); 548 } 549 550 if (o_type instanceof Array) { 551 var This = this; 552 o_type.forEach(function (s_type) { 553 if (!tsk_string_is_null_or_empty(s_type) && tsk_string_is_string(s_type)) { 554 This.ao_listeners[s_type] = o_listener; 555 } 556 }); 557 } 558 else { 559 this.ao_listeners[o_type] = o_listener; 560 } 561 } 562 563 /** 564 Removes an event listener from the target object. 565 @param {String} type The event type/identifier to stop listening for. 566 @see <a href="#addEventListener">addEventListener</a> 567 @exemple 568 this.removeEventListener('started'); 569 */ 570 SIPml.EventTarget.prototype.removeEventListener = function (s_type) { 571 if (tsk_string_is_string(s_type) && !tsk_string_is_null_or_empty(s_type)) { 572 this.ao_listeners[s_type] = undefined; 573 } 574 } 575 576 /** 577 @ignore 578 @private 579 @param {Object} event 580 */ 581 SIPml.EventTarget.prototype.dispatchEvent = function (o_event) { 582 var o_listener = (this.ao_listeners[o_event.s_type] || this.ao_listeners['*']); 583 if (o_listener) { 584 o_listener.call(this, o_event.o_value); 585 } 586 } 587 588 589 590 // ================================== SIPml.Event ========================================== 591 592 593 /** 594 SIP event object. You should never create an instance of this class by yourself. 595 @constructor 596 @param {String} type The event type or identifier. Please check <a href="SIPml.EventTarget.html#SIPml.EventTarget.Session">this link</a> for more information about all supported session event types. 597 @param {tsip_event} [event] Private wrapped session object. 598 @property {String} type The event <a href="SIPml.EventTarget.html#SIPml.EventTarget.Session">type or identifier</a> (e.g. <i>'connected'</i>). 599 @property {String} description User-friendly description in english (e.g. <i>'Session is now connected'</i>). 600 */ 601 SIPml.Event = function (s_type, o_event) { 602 this.type = s_type; 603 this.description = o_event ? o_event.s_phrase : s_type; 604 this.o_event = o_event; 605 } 606 607 /** 608 Gets the SIP response code. 609 @returns {Integer} The SIP response code (e.g. 404). 610 */ 611 SIPml.Event.prototype.getSipResponseCode = function () { 612 var o_message = this.o_event ? this.o_event.get_message() : null; 613 if (o_message && o_message.is_response()) { 614 return o_message.get_response_code(); 615 } 616 return -1; 617 } 618 619 /** 620 Gets the SIP content associated to this event. This function could be called to get the content of the incoming SIP message ('i_new_message' event). 621 @returns {Object} SIP content. 622 @see <a href="#getContentType">getContentType</a>, <a href="#getContentString">getContentString</a> 623 */ 624 SIPml.Event.prototype.getContent = function () { 625 var o_message = this.o_event ? this.o_event.get_message() : null; 626 if (o_message) { 627 return o_message.get_content(); 628 } 629 return null; 630 } 631 632 /** 633 Gets the SIP content associated to this event. This function could be called to get the content of the incoming SIP message ('i_new_message' event). 634 @returns {String} SIP content. 635 @see <a href="#getContentType">getContentType</a>, <a href="#getContent">getContent</a> 636 */ 637 SIPml.Event.prototype.getContentString = function () { 638 var o_message = this.o_event ? this.o_event.get_message() : null; 639 if (o_message) { 640 return o_message.get_content_as_string(); 641 } 642 return null; 643 } 644 645 /** 646 Gets the SIP content-type associated to this event. This function could be called to get the content-type of the incoming SIP message ('i_new_message' event). 647 @returns {Object} SIP content-type. 648 @see <a href="#getContent">getContent</a> 649 */ 650 SIPml.Event.prototype.getContentType = function () { 651 var o_message = this.o_event ? this.o_event.get_message() : null; 652 if (o_message) { 653 return o_message.get_content_type(); 654 } 655 return null; 656 } 657 658 659 660 // ================================== SIPml.Stack ========================================== 661 662 663 /** 664 Anonymous SIP Stack configuration object. 665 @namespace SIPml.Stack.Configuration 666 @name SIPml.Stack.Configuration 667 @property {String} realm The domain name. Required for stack <a href="SIPml.Stack.html#constructor">constructor</a> but optional when used with <a href="SIPml.Stack.html#setConfiguration">setConfiguration</a>. <br /> 668 Example: <i>example.org</i> 669 @property {String} impi The authentication name. Required for stack <a href="SIPml.Stack.html#constructor">constructor</a> but optional when used with <a href="SIPml.Stack.html#setConfiguration">setConfiguration</a>.<br /> 670 Example: <i>+33600000000</i> or <i>bob</i>. 671 @property {string} impu The full SIP uri address. Required for stack <a href="SIPml.Stack.html#constructor">constructor</a> but optional when used with <a href="SIPml.Stack.html#setConfiguration">setConfiguration</a>.<br /> 672 Example: <i>sip:+33600000000@example.com</i> or <i>tel:+33600000000</i> or <i>sip:bob@example.com</i> 673 @property {String} [password] The password to use for SIP authentication.<br /> 674 Example: <i>mysecret</i> 675 @property {String} [display_name] The display name to use in SIP requests. This is the String displayed by the called party for incoming calls. <br /> 676 Example: <i>I Am Legend</i> 677 @property {String} [websocket_proxy_url] The websocket proxy url to connect to (SIP server or gateway address). If unset the stack will use sipml5.org as host and a random port. You should not set this value unless you know what you're doing.<br /> 678 Example: <i>ws://sipml5.org:5060</i> 679 @property {String} [outbound_proxy_url] The outbound Proxy URL is used to set the destination IP address and Port to use for all outgoing requests regardless the <i>domain name</i> (a.k.a <i>realm</i>). <br /> 680 This is a good option for developers using a SIP domain name without valid DNS A/NAPTR/SRV records. You should not set this value unless you know what you're doing. <br /> 681 Example: <i>udp://192.168.0.12:5060</i> 682 @property {Array} [ice_servers] The list of the STUN/TURN servers to use. The format must be as explained at <a target=_blank href="http://www.w3.org/TR/webrtc/#rtciceserver-type">http://www.w3.org/TR/webrtc/#rtciceserver-type</a>. <br /> 683 To disable TURN/STUN to speedup ICE candidates gathering you can use an empty array. e.g. <i>[]</i>. <br /> 684 Example: <i>[{ url: 'stun:stun.l.google.com:19302'}, { url:'turn:user@numb.viagenie.ca', credential:'myPassword'}]</i> 685 @property {Object} [bandwidth] Defines the maximum audio and video bandwidth to use. This will change the outhoing SDP to include a "b:AS=" attribute. Use <i>0</i> to let the browser negotiates the right value using RTCP-REMB and congestion control. Same property could be used at session level to override this value.<br /> 686 <i>Available since version 1.3.203</i>. <br /> 687 Example: <i>{ audio:64, video:512 }</i> 688 @property {Object} [video_size] Defines the maximum and minimum video size to be used. All values are optional. The browser will try to find the best video size between <i>max</i> and <i>min</i> based on the camera capabilities. Same property could be used at session level to override this value.<br /> 689 <i>Available since version 1.3.203</i>. <br /> 690 Example: <i>{ minWidth:640, minHeight:480, maxWidth:1920, maxHeight:1080 }</i> 691 @property {Boolean} [enable_rtcweb_breaker] Whether to enable the <a href="http://webrtc2sip.org/#aRTCWebBreaker" target=_blank>RTCWeb Breaker</a> module to allow calling SIP-legacy networks. <br /> 692 Example: <i>true</i> 693 @property {Boolean} [enable_click2call] Whether to enable the <a href="http://click2dial.org" target=_blank>Click2Call / Click2Dial</a> service. 694 <i>Available since version 1.2.181</i>. <br /> 695 Example: <i>true</i> 696 @property {Boolean} [enable_early_ims] Whether to enable 3GGP Early IMS as per <a href="http://www.arib.or.jp/english/html/overview/doc/STD-T63v9_60/5_Appendix/Rel6/33/33978-660.pdf" target=_blank>TR 33.978</a>. Should be 'true' unless you're using a real IMS network. <br /> 697 <i>Available since version 1.3.203</i>. <br /> 698 Example: <i>true</i> 699 @property {Boolean} [enable_media_stream_cache] Whether to reuse the same media stream for all calls. If your website is <b>not using https</b> then, the browser will request access to the camera (or microphone) every time you try to make a call. Caching the media stream will avoid getting these notifications for each call. <br /> 700 <i>Available since version 1.3.203</i>. <br /> 701 Example: <i>true</i> 702 703 @property {Object} [events_listener] Object to subscribe to some events. 704 Example: 705 <ul> 706 <li><i>{ events: '*', listener: function(e){} }</i> </li> 707 <li><i>{ events: 'started', listener: function(e){} }</i></li> 708 <li><i>{ events: ['started', 'stopped'], listener: function(e){} }</i></li> 709 </ul> 710 You can also use <a href="#addEventListener">addEventListener</a> to add listeners to the stack. 711 @property {Array} [sip_headers] Stack-level SIP headers to add to all outgoing requests. Each header is an object with a <i>name</i> and <i>value</i> fields. <br /> 712 Example: <i>sip_headers: [{name: 'User-Agent', value: 'IM-client/OMA1.0 sipML5-v1.0.89.0'}, {name: 'Organization', value: 'Doubango Telecom'}]</i> 713 714 @example 715 var configuration = { 716 realm: 'example.org', 717 impi: 'bob', 718 impu: 'sip:bob@example.org', 719 password: 'mysecret', // optional 720 display_name: 'I Am Legend', // optional 721 websocket_proxy_url: 'ws://192.168.0.10:5060', // optional 722 outbound_proxy_url: 'udp://192.168.0.12:5060', // optional 723 ice_servers: [{ url: 'stun:stun.l.google.com:19302'}, { url:'turn:user@numb.viagenie.ca', credential:'myPassword'}], // optional 724 enable_rtcweb_breaker: true, // optional 725 enable_click2call: false, // optional 726 enable_early_ims: true, // optional 727 events_listener: { events: '*', listener: listenerFunc }, // optional 728 sip_headers: [ //optional 729 {name: 'User-Agent', value: 'IM-client/OMA1.0 sipML5-v1.0.89.0'}, 730 {name: 'Organization', value: 'Doubango Telecom'} 731 ] 732 }; 733 */ 734 735 736 /** 737 This is the root object used by any other object to make/receive calls, messages or manage presence. 738 You have to create an instance of this class before anything else. 739 @extends SIPml.EventTarget 740 @constructor 741 @class 742 @param {SIPml.Stack.Configuration} configuration Configuration object. Could be updated later using <a href="#setConfiguration">setConfiguration</a>. 743 @throws {ERR_INVALID_PARAMETER_VALUE|ERR_INVALID_PARAMETER_TYPE} <font color="red">ERR_INVALID_PARAMETER_VALUE</font> | <font color="red">ERR_INVALID_PARAMETER_TYPE</font> 744 @example 745 var listenerFunc = function(e){ 746 console.info('stack event = ' + e.type); 747 // Please check <a href="SIPml.EventTarget.html#SIPml.EventTarget.Stack">this link</a> for more information on all supported events. 748 } 749 750 var o_stack = new SIPml.Stack({ 751 realm: 'example.org', 752 impi: 'bob', 753 impu: 'sip:bob@example.org', 754 password: 'mysecret', // optional 755 display_name: 'I Am Legend', // optional 756 websocket_proxy_url: 'ws://192.168.0.10:5060', // optional 757 outbound_proxy_url: 'udp://192.168.0.12:5060', // optional 758 ice_servers: [{ url: 'stun:stun.l.google.com:19302'}, { url:'turn:user@numb.viagenie.ca', credential:'myPassword'}], // optional 759 bandwidth: { audio:64, video:512 }, // optional 760 video_size: { minWidth:640, minHeight:480, maxWidth:1920, maxHeight:1080 }, // optional 761 enable_rtcweb_breaker: true, // optional 762 enable_click2call: false, // optional 763 events_listener: { events: '*', listener: listenerFunc }, //optional 764 sip_headers: [ //optional 765 {name: 'User-Agent', value: 'IM-client/OMA1.0 sipML5-v1.0.89.0'}, 766 {name: 'Organization', value: 'Doubango Telecom'} 767 ] 768 } 769 ); 770 771 @see <a href="#setConfiguration">setConfiguration</a> 772 */ 773 SIPml.Stack = function (o_conf) { 774 SIPml.init(); 775 SIPml.EventTarget.call(this); 776 /* 777 members: 778 - o_stack {tsip_stack} 779 */ 780 781 if (!o_conf) { 782 throw new Error("ERR_INVALID_PARAMETER_VALUE: null configuration value"); 783 } 784 if (tsk_string_is_null_or_empty(o_conf.realm)) { 785 throw new Error("ERR_INVALID_PARAMETER_VALUE: '" + o_conf.realm + "' is not valid as realm value"); 786 } 787 if (tsk_string_is_null_or_empty(o_conf.impi)) { 788 throw new Error("ERR_INVALID_PARAMETER_VALUE: '" + o_conf.impi + "' is not valid as impi value"); 789 } 790 if (tsk_string_is_null_or_empty(o_conf.impu)) { 791 throw new Error("ERR_INVALID_PARAMETER_VALUE: '" + o_conf.impu + "' is not valid as impu value"); 792 } 793 // check IMPU validity 794 var o_impu = tsip_uri.prototype.Parse(o_conf.impu); 795 if (!o_impu || !o_impu.s_user_name || !o_impu.s_host) { 796 throw new Error("ERR_INVALID_PARAMETER_VALUE: '" + o_conf.impu + "' is not valid as SIP Uri"); 797 } 798 799 var i_port; 800 var s_proxy; 801 802 if (!SIPml.isWebSocketSupported()) { 803 // port and host will be updated using the result from DNS SRV(NAPTR(realm)) 804 i_port = 5060; 805 s_proxy = o_conf.realm; 806 } 807 else { 808 // there are at least 5 servers running on the cloud. 809 // we will connect to one of them and let the balancer to choose the right one (less connected sockets) 810 // each port can accept up to 65K connections which means that the cloud can manage 325K active connections 811 // the number of port will be increased or decreased based on the current trafic 812 813 // webrtc2sip 2.2+ (Doubango): 814 // WS: 10060, 11060, 12060, 13060, 14060 815 // WSS: 10062, 11062, 12062, 13062, 14062 816 // 817 818 i_port = ((o_conf.enable_rtcweb_breaker || (window.location && window.location.protocol == "https:")) ? 10062 : 10060) + (((new Date().getTime()) % 5) * 1000); 819 s_proxy = "ns313841.ovh.net"; 820 } 821 822 // create the stack 823 this.o_stack = new tsip_stack(o_conf.realm, o_conf.impi, o_conf.impu, s_proxy, i_port); 824 this.o_stack.oStack = this; 825 // set configurations 826 this.setConfiguration(o_conf); 827 828 // listen for stack events 829 this.o_stack.on_event_stack = function (e) { 830 var s_type; 831 switch (e.i_code) { 832 case tsip_event_code_e.STACK_STARTING: s_type = 'starting'; break; 833 case tsip_event_code_e.STACK_STARTED: s_type = 'started'; break; 834 case tsip_event_code_e.STACK_STOPPING: s_type = 'stopping'; break; 835 case tsip_event_code_e.STACK_STOPPED: s_type = 'stopped'; break; 836 case tsip_event_code_e.STACK_FAILED_TO_START: s_type = 'failed_to_start'; break; 837 case tsip_event_code_e.STACK_FAILED_TO_STOP: s_type = 'failed_to_stop'; break; 838 } 839 if (s_type) { 840 e.o_stack.oStack.dispatchEvent({ s_type: s_type, o_value: new SIPml.Stack.Event(s_type, e) }); 841 } 842 } 843 844 845 // listen for dialog events 846 this.o_stack.on_event_dialog = function (e) { 847 var s_type = null; 848 var i_session_id = e.o_session.i_id; 849 var oSession = e.o_session.o_stack.oStack.ao_sessions[i_session_id]; 850 if (!oSession) { 851 tsk_utils_log_warn('Cannot find session with id = ' + i_session_id); 852 return; 853 } 854 855 switch (e.i_code) { 856 case tsip_event_code_e.DIALOG_TRANSPORT_ERROR: s_type = 'transport_error'; break; 857 case tsip_event_code_e.DIALOG_GLOBAL_ERROR: s_type = 'global_error'; break; 858 case tsip_event_code_e.DIALOG_MESSAGE_ERROR: s_type = 'message_error'; break; 859 case tsip_event_code_e.DIALOG_WEBRTC_ERROR: s_type = 'webrtc_error'; break; 860 case tsip_event_code_e.DIALOG_REQUEST_INCOMING: s_type = 'i_request'; break; 861 case tsip_event_code_e.DIALOG_REQUEST_OUTGOING: s_type = 'o_request'; break; 862 case tsip_event_code_e.DIALOG_REQUEST_CANCELLED: s_type = 'cancelled_request'; break; 863 case tsip_event_code_e.DIALOG_REQUEST_SENT: s_type = 'sent_request'; break; 864 case tsip_event_code_e.DIALOG_MEDIA_ADDED: s_type = 'media_added'; break; 865 case tsip_event_code_e.DIALOG_MEDIA_REMOVED: s_type = 'media_removed'; break; 866 case tsip_event_code_e.DIALOG_CONNECTING: s_type = 'connecting'; break; 867 case tsip_event_code_e.DIALOG_CONNECTED: s_type = 'connected'; break; 868 case tsip_event_code_e.DIALOG_BFCP_INFO: s_type = 'bfcp_info'; break; 869 case tsip_event_code_e.DIALOG_TERMINATING: s_type = 'terminating'; break; 870 case tsip_event_code_e.DIALOG_TERMINATED: 871 { 872 s_type = 'terminated'; 873 e.o_session.o_stack.oStack.ao_sessions[i_session_id] = undefined; 874 break; 875 } 876 default: break; 877 } 878 879 if (s_type) { 880 oSession.dispatchEvent({ s_type: s_type, o_value: new SIPml.Session.Event(oSession, s_type, e) }); 881 } 882 } 883 884 // listen for MESSAGE events 885 this.o_stack.on_event_message = function (e) { 886 var s_type = null; 887 var i_session_id = e.o_session.i_id; 888 var oSession = e.o_session.o_stack.oStack.ao_sessions[i_session_id]; 889 890 switch (e.e_message_type) { 891 case tsip_event_message_type_e.I_MESSAGE: s_type = 'i_new_message'; break; 892 case tsip_event_message_type_e.AO_MESSAGE: s_type = 'i_ao_request'; break; 893 } 894 895 if (s_type) { 896 // 'i_new_call' is stack-level event 897 if (s_type == 'i_new_message') { 898 var oNewEvent = new SIPml.Stack.Event(s_type, e); 899 oNewEvent.newSession = new SIPml.Session.Message(e.o_session); 900 e.o_session.o_stack.oStack.ao_sessions[i_session_id] = oNewEvent.newSession; // save session 901 e.o_session.o_stack.oStack.dispatchEvent({ s_type: s_type, o_value: oNewEvent }); 902 } 903 else { 904 if (oSession) { 905 oSession.dispatchEvent({ s_type: s_type, o_value: new SIPml.Session.Event(oSession, s_type, e) }); 906 } 907 else { 908 tsk_utils_log_warn('Cannot find session with id = ' + i_session_id + ' and event = ' + e.e_invite_type); 909 } 910 } 911 } 912 }; 913 914 // listen for PUBLISH events 915 this.o_stack.on_event_publish = function (e) { 916 var s_type = null; 917 var i_session_id = e.o_session.i_id; 918 var oSession = e.o_session.o_stack.oStack.ao_sessions[i_session_id]; 919 if (!oSession) { 920 tsk_utils_log_warn('Cannot find session with id = ' + i_session_id + ' and event = ' + e.e_invite_type); 921 return; 922 } 923 924 switch (e.e_publish_type) { 925 case tsip_event_publish_type_e.I_PUBLISH: break; 926 case tsip_event_publish_type_e.I_UNPUBLISH: break; 927 case tsip_event_publish_type_e.AO_PUBLISH: 928 case tsip_event_publish_type_e.AO_UNPUBLISH: 929 { 930 s_type = 'i_ao_request'; 931 break; 932 } 933 } 934 if (s_type) { 935 oSession.dispatchEvent({ s_type: s_type, o_value: new SIPml.Session.Event(oSession, s_type, e) }); 936 } 937 } 938 939 // listen for SUBSCRIBE events 940 this.o_stack.on_event_subscribe = function (e) { 941 var s_type = null; 942 var i_session_id = e.o_session.i_id; 943 var oSession = e.o_session.o_stack.oStack.ao_sessions[i_session_id]; 944 if (!oSession) { 945 tsk_utils_log_warn('Cannot find session with id = ' + i_session_id + ' and event = ' + e.e_invite_type); 946 return; 947 } 948 949 switch (e.e_subscribe_type) { 950 case tsip_event_subscribe_type_e.I_SUBSCRIBE: break; 951 case tsip_event_subscribe_type_e.I_UNSUBSRIBE: break; 952 case tsip_event_subscribe_type_e.AO_SUBSCRIBE: 953 case tsip_event_subscribe_type_e.AO_UNSUBSCRIBE: 954 case tsip_event_subscribe_type_e.AO_NOTIFY: 955 { 956 s_type = 'i_ao_request'; 957 break; 958 } 959 case tsip_event_subscribe_type_e.I_NOTIFY: 960 { 961 s_type = 'i_notify'; 962 break; 963 } 964 } 965 if (s_type) { 966 oSession.dispatchEvent({ s_type: s_type, o_value: new SIPml.Session.Event(oSession, s_type, e) }); 967 } 968 } 969 970 971 // listen for INVITE events 972 this.o_stack.on_event_invite = function (e) { 973 var s_type = null; 974 var i_session_id = e.o_session.i_id; 975 var oSession = e.o_session.o_stack.oStack.ao_sessions[i_session_id]; 976 if (!oSession) { 977 switch (e.e_invite_type) { 978 case tsip_event_invite_type_e.I_NEW_CALL: 979 case tsip_event_invite_type_e.M_STREAM_LOCAL_REQUESTED: 980 case tsip_event_invite_type_e.M_STREAM_LOCAL_ACCEPTED: 981 case tsip_event_invite_type_e.M_STREAM_LOCAL_REFUSED: 982 case tsip_event_invite_type_e.M_BFCP_INFO: 983 break; 984 985 case tsip_event_invite_type_e.M_STREAM_LOCAL_ADDED: 986 case tsip_event_invite_type_e.M_STREAM_REMOTE_ADDED: 987 case tsip_event_invite_type_e.M_STREAM_LOCAL_REMOVED: 988 case tsip_event_invite_type_e.M_STREAM_REMOTE_REMOVED: 989 case tsip_event_invite_type_e.I_AO_REQUEST: 990 tsk_utils_log_info('Not notifying to session with id = ' + i_session_id + ' for event = ' + e.e_invite_type); 991 return; 992 993 default: 994 tsk_utils_log_warn('Cannot find session with id = ' + i_session_id + ' and event = ' + e.e_invite_type); 995 return; 996 } 997 } 998 999 1000 1001 var _setStream = function (o_view, o_stream, b_audio) { 1002 if (o_view) { 1003 attachMediaStream(o_view, o_stream); 1004 return (b_audio && o_stream && o_stream.getAudioTracks().length > 0) || (!b_audio && o_stream && o_stream.getVideoTracks().length > 0); 1005 } 1006 } 1007 1008 var attachStream = function (bLocal) { 1009 var o_stream = bLocal ? e.o_session.get_stream_local() : e.o_session.get_stream_remote(); 1010 if (_setStream((bLocal ? oSession.videoLocal : oSession.videoRemote), o_stream, false)) { 1011 dispatchEvent(bLocal ? 'm_stream_video_local_added' : 'm_stream_video_remote_added'); 1012 } 1013 if (_setStream((bLocal ? oSession.audioLocal : oSession.audioRemote), o_stream, true)) { 1014 dispatchEvent(bLocal ? 'm_stream_audio_local_added' : 'm_stream_audio_remote_added'); 1015 } 1016 } 1017 var deattachStream = function (bLocal) { 1018 if (_setStream((bLocal ? oSession.videoLocal : oSession.videoRemote), null, false)) { 1019 dispatchEvent(bLocal ? 'm_stream_video_local_removed' : 'm_stream_video_remote_removed'); 1020 } 1021 if (_setStream((bLocal ? oSession.audioLocal : oSession.audioRemote), null, true)) { 1022 dispatchEvent(bLocal ? 'm_stream_audio_local_removed' : 'm_stream_audio_remote_removed'); 1023 } 1024 } 1025 1026 var dispatchEvent = function (s_event_type) { 1027 if (s_event_type) { 1028 // 'i_new_call', 'm_permission_requested', 'm_permission_accepted' and 'm_permission_refused' are stack-level event 1029 switch (s_event_type) { 1030 case 'i_new_call': 1031 case 'm_permission_requested': 1032 case 'm_permission_accepted': 1033 case 'm_permission_refused': 1034 { 1035 var oNewEvent = new SIPml.Stack.Event(s_event_type, e); 1036 if (s_event_type == 'i_new_call') { 1037 oNewEvent.newSession = new SIPml.Session.Call(e.o_session); 1038 e.o_session.o_stack.oStack.ao_sessions[i_session_id] = oNewEvent.newSession; // save session 1039 } 1040 e.o_session.o_stack.oStack.dispatchEvent({ s_type: s_event_type, o_value: oNewEvent }); 1041 break; 1042 } 1043 default: 1044 { 1045 oSession.dispatchEvent({ s_type: s_event_type, o_value: new SIPml.Session.Event(oSession, s_event_type, e) }); 1046 break; 1047 } 1048 } 1049 } 1050 } 1051 1052 switch (e.e_invite_type) { 1053 case tsip_event_invite_type_e.I_NEW_CALL: s_type = 'i_new_call'; break; 1054 case tsip_event_invite_type_e.I_ECT_NEW_CALL: s_type = 'i_ect_new_call'; break; 1055 case tsip_event_invite_type_e.I_AO_REQUEST: s_type = 'i_ao_request'; break; 1056 case tsip_event_invite_type_e.M_EARLY_MEDIA: s_type = 'm_early_media'; break; 1057 case tsip_event_invite_type_e.M_STREAM_LOCAL_REQUESTED: s_type = 'm_permission_requested'; break; 1058 case tsip_event_invite_type_e.M_STREAM_LOCAL_ACCEPTED: s_type = 'm_permission_accepted'; break; 1059 case tsip_event_invite_type_e.M_STREAM_LOCAL_REFUSED: s_type = 'm_permission_refused'; break; 1060 case tsip_event_invite_type_e.M_STREAM_LOCAL_ADDED: 1061 { 1062 return attachStream(true); 1063 } 1064 case tsip_event_invite_type_e.M_STREAM_LOCAL_REMOVED: 1065 { 1066 return deattachStream(true); 1067 } 1068 case tsip_event_invite_type_e.M_STREAM_REMOTE_ADDED: 1069 { 1070 return attachStream(false); 1071 } 1072 case tsip_event_invite_type_e.M_STREAM_REMOTE_REMOVED: 1073 { 1074 return deattachStream(false); 1075 } 1076 case tsip_event_invite_type_e.M_LOCAL_HOLD_OK: s_type = 'm_local_hold_ok'; break; 1077 case tsip_event_invite_type_e.M_LOCAL_HOLD_NOK: s_type = 'm_local_hold_nok'; break; 1078 case tsip_event_invite_type_e.M_LOCAL_RESUME_OK: s_type = 'm_local_resume_ok'; break; 1079 case tsip_event_invite_type_e.M_LOCAL_RESUME_NOK: s_type = 'm_local_resume_nok'; break; 1080 case tsip_event_invite_type_e.M_REMOTE_HOLD: s_type = 'm_remote_hold'; break; 1081 case tsip_event_invite_type_e.M_REMOTE_RESUME: s_type = 'm_remote_resume'; break; 1082 case tsip_event_invite_type_e.M_BFCP_INFO: s_type = 'm_bfcp_info'; break; 1083 case tsip_event_invite_type_e.O_ECT_TRYING: s_type = 'o_ect_trying'; break; 1084 case tsip_event_invite_type_e.O_ECT_ACCEPTED: s_type = 'o_ect_accepted'; break; 1085 case tsip_event_invite_type_e.O_ECT_COMPLETED: s_type = 'o_ect_completed'; break; 1086 case tsip_event_invite_type_e.I_ECT_COMPLETED: s_type = 'i_ect_completed'; break; 1087 case tsip_event_invite_type_e.O_ECT_FAILED: s_type = 'o_ect_failed'; break; 1088 case tsip_event_invite_type_e.I_ECT_FAILED: s_type = 'i_ect_failed'; break; 1089 case tsip_event_invite_type_e.O_ECT_NOTIFY: s_type = 'o_ect_notify'; break; 1090 case tsip_event_invite_type_e.I_ECT_NOTIFY: s_type = 'i_ect_notify'; break; 1091 case tsip_event_invite_type_e.I_ECT_REQUESTED: s_type = 'i_ect_requested'; break; 1092 case tsip_event_invite_type_e.DIALOG_REQUEST_INCOMING: 1093 { 1094 if (e.o_message) { 1095 if (e.o_message.is_info()) { s_type = 'i_info'; } 1096 } 1097 break; 1098 } 1099 default: break; 1100 } 1101 1102 // dispatch event 1103 dispatchEvent(s_type); 1104 } 1105 } 1106 1107 SIPml.Stack.prototype = Object.create(SIPml.EventTarget.prototype); 1108 SIPml.Stack.prototype.ao_sessions = []; 1109 1110 /** 1111 Updates configuration values. 1112 @param {SIPml.Stack.Configuration} configuration Configuration object value. 1113 @returns {Integer} 0 if successful; otherwise nonzero 1114 @example 1115 // add two new headers and change the <i>proxy_url</i> 1116 o_stack.setConfiguration({ 1117 proxy_url: 'ws://192.168.0.10:5060', 1118 sip_headers: [ 1119 {name: 'User-Agent', value: 'IM-client/OMA1.0 sipML5-v1.0.89.0'}, 1120 {name: 'Organization', value: 'Doubango Telecom'} 1121 ] 1122 } 1123 ); 1124 */ 1125 SIPml.Stack.prototype.setConfiguration = function (o_conf) { 1126 if (o_conf.realm && !tsk_string_is_string(o_conf.realm)) { 1127 throw new Error("ERR_INVALID_PARAMETER_TYPE: '" + typeof o_conf.realm + "' not a valid type for realm. String is expected"); 1128 } 1129 if (o_conf.impi && !tsk_string_is_string(o_conf.impi)) { 1130 throw new Error("ERR_INVALID_PARAMETER_TYPE: '" + typeof o_conf.impi + "' not a valid type for impi. String is expected"); 1131 } 1132 if (o_conf.impu && !tsk_string_is_string(o_conf.impu)) { 1133 throw new Error("ERR_INVALID_PARAMETER_TYPE: '" + typeof o_conf.impu + "' not a valid type for impu. String is expected"); 1134 } 1135 if (o_conf.password && !tsk_string_is_string(o_conf.password)) { 1136 throw new Error("ERR_INVALID_PARAMETER_TYPE: '" + typeof o_conf.password + "' not a valid type for password. String is expected"); 1137 } 1138 if (o_conf.display_name && !tsk_string_is_string(o_conf.display_name)) { 1139 throw new Error("ERR_INVALID_PARAMETER_TYPE: '" + typeof o_conf.display_name + "' not a valid type for display_name. String is expected"); 1140 } 1141 if (o_conf.websocket_proxy_url && !tsk_string_is_string(o_conf.websocket_proxy_url)) { 1142 throw new Error("ERR_INVALID_PARAMETER_TYPE: '" + typeof o_conf.websocket_proxy_url + "' not a valid type for websocket_proxy_url. String is expected"); 1143 } 1144 if (o_conf.outbound_proxy_url && !tsk_string_is_string(o_conf.outbound_proxy_url)) { 1145 throw new Error("ERR_INVALID_PARAMETER_TYPE: '" + typeof o_conf.outbound_proxy_url + "' not a valid type for outbound_proxy_url. String is expected"); 1146 } 1147 if (o_conf.sip_headers && typeof o_conf.sip_headers != "Array" && !(o_conf.sip_headers instanceof Array)) { 1148 throw new Error("ERR_INVALID_PARAMETER_TYPE: '" + typeof o_conf.sip_headers + "' not a valid type for sip_headers. Array is expected"); 1149 } 1150 1151 // event-listener: must be first to be defined as other configs could raise events 1152 if (o_conf.events_listener) { 1153 this.addEventListener(o_conf.events_listener.events, o_conf.events_listener.listener); 1154 } 1155 1156 var b_rtcweb_breaker_enabled = !!o_conf.enable_rtcweb_breaker; 1157 var b_click2call_enabled = !!o_conf.enable_click2call; 1158 var b_early_ims = (o_conf.enable_early_ims == undefined) ? true : !!o_conf.enable_early_ims; // default value is true 1159 var b_enable_media_stream_cache = !!o_conf.enable_media_stream_cache; 1160 var o_bandwidth = o_conf.bandwidth ? o_conf.bandwidth : { audio: undefined, video: undefined }; 1161 var o_video_size = o_conf.video_size ? o_conf.video_size : { minWidth: undefined, minHeight: undefined, maxWidth: undefined, maxHeight: undefined }; 1162 var o_stack = this.o_stack; 1163 tsk_utils_log_info("s_websocket_server_url=" + (o_conf.websocket_proxy_url || "(null)")); 1164 tsk_utils_log_info("s_sip_outboundproxy_url=" + (o_conf.outbound_proxy_url || "(null)")); 1165 tsk_utils_log_info("b_rtcweb_breaker_enabled=" + (b_rtcweb_breaker_enabled ? "yes" : "no")); 1166 tsk_utils_log_info("b_click2call_enabled=" + (b_click2call_enabled ? "yes" : "no")); 1167 tsk_utils_log_info("b_early_ims=" + (b_early_ims ? "yes" : "no")); 1168 tsk_utils_log_info("b_enable_media_stream_cache=" + (b_enable_media_stream_cache ? "yes" : "no")); 1169 tsk_utils_log_info("o_bandwidth=" + JSON.stringify(o_bandwidth)); 1170 tsk_utils_log_info("o_video_size=" + JSON.stringify(o_video_size)); 1171 1172 o_stack.set(tsip_stack.prototype.SetPassword(o_conf.password), 1173 tsip_stack.prototype.SetDisplayName(o_conf.display_name), 1174 tsip_stack.prototype.SetProxyOutBoundUrl(o_conf.outbound_proxy_url), 1175 tsip_stack.prototype.SetRTCWebBreakerEnabled(b_rtcweb_breaker_enabled), 1176 tsip_stack.prototype.SetClick2CallEnabled(b_click2call_enabled), 1177 tsip_stack.prototype.SetSecureTransportEnabled((b_rtcweb_breaker_enabled || (window.location && window.location.protocol == "https:"))), // always use secure transport when RTCWebBreaker or https:// 1178 tsip_stack.prototype.SetEarlyIMSEnabled(b_early_ims), // should be 'true' unless you're using a real IMS network 1179 tsip_stack.prototype.SetWebsocketServerUrl(o_conf.websocket_proxy_url), 1180 tsip_stack.prototype.SetIceServers(o_conf.ice_servers), 1181 tsip_stack.prototype.SetMediaStreamCacheEnabled(b_enable_media_stream_cache), 1182 tsip_stack.prototype.SetBandwidth(o_bandwidth), 1183 tsip_stack.prototype.SetVideoSize(o_video_size)); 1184 1185 // add sip headers 1186 if (o_conf.sip_headers) { 1187 o_conf.sip_headers.forEach(function (o_header) { 1188 if (o_header && !tsk_string_is_null_or_empty(o_header.name) && (!o_header.value || tsk_string_is_string(o_header.value))) { 1189 o_stack.set( 1190 tsip_stack.prototype.SetHeader(o_header.name, o_header.value) 1191 ); 1192 } 1193 }); 1194 } 1195 1196 return 0; 1197 } 1198 1199 1200 /** 1201 Starts the SIP stack and connect the network transport to the WebSocket server without sending any SIP request. 1202 This function must be be called before any attempt to make or receive calls/messages. This function is asynchronous which means that the stack will not be immediately started after the call. 1203 Please check <a href="SIPml.EventTarget.html#SIPml.EventTarget.Stack">this link</a> for more information on all supported events. 1204 @returns {Integer} 0 if successful; otherwise nonzero 1205 @throws {ERR_INVALID_STATE} <font color="red">ERR_INVALID_STATE</font> 1206 */ 1207 SIPml.Stack.prototype.start = function () { 1208 return this.o_stack.start(); 1209 } 1210 1211 /** 1212 Stops the SIP stack and disconnect the network transport from the WebSocket server. This function will also hangup all calls and unregister the user from the SIP server. 1213 Please check <a href="SIPml.EventTarget.html#SIPml.EventTarget.Stack">this link</a> for more information on all supported events. 1214 @param {Integer} [timeout] Optional parameter used to defined maximum time in milliseconds to take to stop the stack. 1215 Default value: 2000 millis 1216 @returns {Integer} 0 if successful; otherwise nonzero 1217 @throws {ERR_INVALID_STATE} <font color="red">ERR_INVALID_STATE</font> 1218 */ 1219 SIPml.Stack.prototype.stop = function (i_timeout) { 1220 return this.o_stack.stop(i_timeout); 1221 } 1222 1223 /** 1224 Create new SIP session. 1225 @param {String} type Session type. Supported values: <b>'register'</b>, <b>'call-audio'</b>, <b>'call-audiovideo'</b>, <b>'call-video'</b>, <b>'call-screenshare'</b>, <b>'message'</b>, <b>'subscribe'</b> or <b>'publish'</b>. 1226 @param {SIPml.Session.Configuration} [configuration] Anonymous object used to configure the newly created session. 1227 @throws {ERR_INVALID_PARAMETER_VALUE} <font color="red">ERR_INVALID_PARAMETER_VALUE</font> <br> 1228 @returns {SIPml.Session} New session if successful; otherwise null.<br> The session type would be <a href="SIPml.Session.Registration.html">SIPml.Session.Registration</a>, <a href="SIPml.Session.Call.html">SIPml.Session.Call</a> or <a href="SIPml.Session.Message.html">SIPml.Session.Message</a> <br> 1229 1230 @example 1231 var <a href="SIPml.Session.Registration.html">o_registration</a> = this.<a href="#newSession">newSession</a>('register', { 1232 expires: 200, 1233 sip_caps: [ 1234 {name: '+g.oma.sip-im'}, 1235 {name: '+audio'}, 1236 {name: 'language', value: '\"en,fr\"'} 1237 ], 1238 sip_headers: [ 1239 {name: 'What', value: 'Registration', session: false}, 1240 {name: 'Organization', value: 'Doubango Telecom', session: true} 1241 ] 1242 }); 1243 o_registration.<a href="SIPml.Session.Registration.html#register">register</a>(); 1244 1245 // or 1246 var <a href="SIPml.Session.Call.html">o_audiovideo</a> = this.<a href="#newSession">newSession</a>('call-audiovideo', { 1247 video_local: document.getElementById('video_local'), // <video id="video_local" .../> 1248 video_remote: document.getElementById('video_remote'), // <video id="video_remote" .../> 1249 audio_remote: document.getElementById('audio_remote'), // <audio id="audio_remote" .../> 1250 sip_caps: [ 1251 {name: '+g.oma.sip-im'}, 1252 {name: '+sip.ice'}, 1253 {name: 'language', value: '\"en,fr\"'} 1254 ], 1255 sip_headers: [ 1256 {name: 'What', value: 'Audio/Video call', session: false}, 1257 {name: 'Organization', value: 'Doubango Telecom', session: false} 1258 ] 1259 }); 1260 o_audiovideo.<a href="SIPml.Session.Call.html#call">call</a>('alice'); // call alice 1261 */ 1262 SIPml.Stack.prototype.newSession = function (s_type, o_conf) { 1263 var o_session; 1264 var cls; 1265 if (s_type == 'register') { 1266 o_session = new tsip_session_register(this.o_stack); 1267 cls = SIPml.Session.Registration; 1268 } 1269 else if (s_type == 'message') { 1270 o_session = new tsip_session_message(this.o_stack); 1271 cls = SIPml.Session.Message; 1272 } 1273 else if (s_type == 'publish') { 1274 o_session = new tsip_session_publish(this.o_stack); 1275 cls = SIPml.Session.Publish; 1276 } 1277 else if (s_type == 'subscribe') { 1278 o_session = new tsip_session_subscribe(this.o_stack); 1279 cls = SIPml.Session.Subscribe; 1280 } 1281 else if (s_type == 'call-audio' || s_type == 'call-audiovideo' || s_type == 'call-video' || s_type == 'call-screenshare') { 1282 o_session = new tsip_session_invite(this.o_stack); 1283 o_session.s_type = s_type; 1284 cls = SIPml.Session.Call; 1285 } 1286 else { 1287 throw new Error("ERR_INVALID_PARAMETER_VALUE: '" + s_type + "' not valid as session type"); 1288 } 1289 1290 o_session.b_local = true; // locally created session 1291 var oSession = new cls(o_session, o_conf); 1292 this.ao_sessions[oSession.getId()] = oSession; 1293 return oSession; 1294 } 1295 1296 // ================================== SIPml.Stack.Event ========================================== 1297 1298 /** 1299 SIP Stack event object. You should never create an instance of this object. 1300 @constructor 1301 @extends SIPml.Event 1302 @param {String} type The event type/identifier. 1303 @param {tsip_event} [event] The wrapped event object. 1304 @property {String} type The event type or identifier (e.g. <i>'started'</i> or <i>'i_new_call'</i>). Please check <a href="SIPml.EventTarget.html#SIPml.EventTarget.Stack">this link</a> for more information on all supported events. 1305 @property {String} description User-friendly description in english (e.g. <i>'Stack started'</i> or <i>'<b>I</b>ncoming <b>new</b> <b>call</b>'</i>). 1306 @property {SIPml.Session} [newSession] Optional session object only defined when the event is about a new session creation (e.g. <i>'i_new_call'</i>). 1307 The session type would be <a href="SIPml.Session.Call.html">SIPml.Session.Call</a> or <a href="SIPml.Session.Message.html">SIPml.Session.Message</a> 1308 */ 1309 SIPml.Stack.Event = function (s_type, o_event) { 1310 SIPml.Event.call(this, s_type, o_event); 1311 } 1312 1313 SIPml.Stack.Event.prototype = Object.create(SIPml.Event.prototype); 1314 1315 // ================================== SIPml.Session ========================================== 1316 1317 /** 1318 Anonymous SIP Session configuration object. 1319 @namespace SIPml.Session.Configuration 1320 @name SIPml.Session.Configuration 1321 @property {Integer} [expires] Session timeout in seconds. 1322 @property {HTMLVideoElement} [video_local] <a href="https://developer.mozilla.org/en-US/docs/DOM/HTMLVideoElement">HTMLVideoElement<a> where to display the local video preview. This propety should only be used for <a href="SIPml.Session.Call.html">video sessions</a>. 1323 @property {HTMLVideoElement} [video_remote] <a href="https://developer.mozilla.org/en-US/docs/DOM/HTMLVideoElement">HTMLVideoElement<a> where to display the remote video stream. This propety should only be used for <a href="SIPml.Session.Call.html">video sessions</a>. 1324 @property {HTMLAudioElement} [audio_remote] <a href="https://developer.mozilla.org/en-US/docs/DOM/HTMLAudioElement">HTMLAudioElement<a> used to playback the remote audio stream. This propety should only be used for <a href="SIPml.Session.Call.html">audio sessions</a>. 1325 @property {Integer} [screencast_window_id] Windows identifer from which to grab frames for application or desktop share. Use #0 to share your entire desktop. This property should only be used when webrt4all with support fot BFCP is installed. <br /> 1326 <i>Available since version 2.0.0</i>. <br /> 1327 @property {Array} [sip_caps] <i>{name,value}</i> pairs defining the SIP capabilities associated to this session. The capabilities are added to the Contact header. Please refer to <a href="http://tools.ietf.org/html/rfc3840">rfc3840</a> and <a href="http://tools.ietf.org/html/rfc3841">rfc3841</a> for more information. 1328 @property {String} [from] Set the source uri string to be used in the <i>From</i> header (available since API version 1.2.170). 1329 @property {Object} [bandwidth] Defines the maximum audio and video bandwidth to use. This will change the outhoing SDP to include a "b:AS=" attribute. Use <i>0</i> to let the browser negotiates the right value using RTCP-REMB and congestion control. A default value for all sessions could be defined at stack level.<br /> 1330 <i>Available since version 1.3.203</i>. <br /> 1331 Example: <i>{ audio:64, video:512 }</i> 1332 @property {Object} [video_size] Defines the maximum and minimum video size to be used. All values are optional. The browser will try to find the best video size between <i>max</i> and <i>min</i> based on the camera capabilities. A default value for all sessions could be defined at stack level.<br /> 1333 <i>Available since version 1.3.203</i>. <br /> 1334 Example: <i>{ minWidth:640, minHeight:480, maxWidth:1920, maxHeight:1080 }</i> 1335 @property {Array} [sip_headers] <i>{name,value,session}</i> trios defining the SIP headers associated to this session. <i>session</i> is a boolean defining whether the header have to be added to all outgoing request or not (initial only). 1336 @example 1337 var configuration = 1338 { 1339 expires: 200, 1340 audio_remote: document.getElementById('audio_remote'), // <audio id="audio_remote" .../> 1341 video_local: document.getElementById('video_local'), // <video id="video_local" .../> 1342 video_remote: document.getElementById('video_remote'), // <video id="video_remote" .../> 1343 sip_caps: [ 1344 {name: '+g.oma.sip-im'}, 1345 {name: '+sip.ice'}, 1346 {name: 'language', value: '\"en,fr\"'} 1347 ], 1348 sip_headers: [ 1349 {name: 'What', value: 'Audio/Video call', session: false}, 1350 {name: 'Organization', value: 'Doubango Telecom', session: false} 1351 ] 1352 } 1353 */ 1354 1355 /** 1356 Base (abstract) SIP session. You should never create an instance of this class by yourself. You have to use <a href="SIPml.Stack.html#newSession"> newSession()</a> to create a new instance. 1357 This is a base class for <a href="SIPml.Session.Registration.html">SIPml.Session.Registration</a>, <a href="SIPml.Session.Call.html">SIPml.Session.Call</a> and <a href="SIPml.Session.Message.html">SIPml.Session.Message</a>. 1358 @constructor 1359 @extends SIPml.EventTarget 1360 @param {tsip_session_t} session Private wrapped session object. 1361 @param {SIPml.Session.Configuration} [configuration] Optional configuration object. 1362 @throws {ERR_INVALID_PARAMETER_VALUE|ERR_INVALID_PARAMETER_TYPE} <font color="red">ERR_INVALID_PARAMETER_VALUE</font> | <font color="red">ERR_INVALID_PARAMETER_TYPE</font> 1363 */ 1364 SIPml.Session = function (o_session, o_conf) { 1365 SIPml.EventTarget.call(this); 1366 /* 1367 - o_configuration: [] 1368 - o_session: tsip_session_xxx 1369 */ 1370 if (!o_session) { 1371 throw new Error("ERR_INVALID_PARAMETER_VALUE: Invalid session value"); 1372 } 1373 1374 this.o_session = o_session; 1375 this.setConfiguration(o_conf); 1376 } 1377 1378 SIPml.Session.prototype = Object.create(SIPml.EventTarget.prototype); 1379 SIPml.Session.prototype.o_session = null; 1380 SIPml.Session.prototype.o_session = null; 1381 SIPml.Session.prototype.o_configuration = null; 1382 1383 /** 1384 Gets the session unique identifier. 1385 @returns {Integer} Read-only session unique identifier. 1386 */ 1387 SIPml.Session.prototype.getId = function () { 1388 return this.o_session.get_id(); 1389 } 1390 1391 /** 1392 Updates or sets the session configuration. 1393 @param {SIPml.Session.Configuration} configuration 1394 */ 1395 SIPml.Session.prototype.setConfiguration = function (o_conf) { 1396 if (!o_conf) { 1397 return; 1398 } 1399 1400 var o_session = this.o_session; 1401 1402 // event-listener: must be first to be defined as other configs could raise events 1403 if (o_conf.events_listener) { 1404 this.addEventListener(o_conf.events_listener.events, o_conf.events_listener.listener); 1405 } 1406 1407 if (this instanceof SIPml.Session.Call) { 1408 // Bandwidth and video size 1409 o_session.set( 1410 tsip_session.prototype.SetBandwidth(o_conf.bandwidth ? o_conf.bandwidth : { audio: undefined, video: undefined }), 1411 tsip_session.prototype.SetVideoSize(o_conf.video_size ? o_conf.video_size : { minWidth: undefined, minHeight: undefined, maxWidth: undefined, maxHeight: undefined }), 1412 tsip_session.prototype.SetScreencastWindowID(o_conf.screencast_window_id ? o_conf.screencast_window_id : 0) 1413 ); 1414 // Do not change the views if not defined in new config. User must use "null" to unset the views 1415 this.videoLocal = (o_conf.video_local === undefined) ? this.videoLocal : o_conf.video_local; 1416 this.videoRemote = (o_conf.video_remote === undefined) ? this.videoRemote : o_conf.video_remote; 1417 this.audioRemote = (o_conf.audio_remote === undefined) ? this.audioRemote : o_conf.audio_remote; 1418 this.audioLocal = (o_conf.audio_local === undefined) ? this.audioLocal : o_conf.audio_local; 1419 1420 var _addStream = function (o_view, o_stream, b_audio) { 1421 if (o_view) { 1422 attachMediaStream(o_view, o_stream); 1423 return (b_audio && o_stream && o_stream.getAudioTracks().length > 0) || (!b_audio && o_stream && o_stream.getVideoTracks().length > 0); 1424 } 1425 } 1426 1427 if (_addStream(this.videoLocal, o_session.get_stream_local(), false)) { 1428 this.dispatchEvent({ s_type: 'm_stream_video_local_added', o_value: new SIPml.Session.Event(this, 'm_stream_video_local_added') }); 1429 } 1430 if (_addStream(this.videoRemote, o_session.get_stream_remote(), false)) { 1431 this.dispatchEvent({ s_type: 'm_stream_video_remote_added', o_value: new SIPml.Session.Event(this, 'm_stream_video_remote_added') }); 1432 } 1433 if (_addStream(this.audioLocal, o_session.get_stream_local(), true)) { 1434 this.dispatchEvent({ s_type: 'm_stream_audio_local_added', o_value: new SIPml.Session.Event(this, 'm_stream_audio_local_added') }); 1435 } 1436 if (_addStream(this.audioRemote, o_session.get_stream_remote(), true)) { 1437 this.dispatchEvent({ s_type: 'm_stream_audio_remote_added', o_value: new SIPml.Session.Event(this, 'm_stream_audio_remote_added') }); 1438 } 1439 } 1440 1441 1442 // headers 1443 if (o_conf.sip_headers) { 1444 o_conf.sip_headers.forEach(function (o_header) { 1445 if (o_header && !tsk_string_is_null_or_empty(o_header.name) && (!o_header.value || tsk_string_is_string(o_header.value))) { 1446 o_session.set( 1447 tsip_session.prototype.SetHeader(o_header.name, o_header.value) 1448 ); 1449 } 1450 }); 1451 } 1452 // caps 1453 if (o_conf.sip_caps) { 1454 o_conf.sip_caps.forEach(function (o_cap) { 1455 if (o_cap && !tsk_string_is_null_or_empty(o_cap.name) && (!o_cap.value || tsk_string_is_string(o_cap.value))) { 1456 o_session.set( 1457 tsip_session.prototype.SetCaps(o_cap.name, o_cap.value) 1458 ); 1459 } 1460 }); 1461 } 1462 // expires 1463 if (o_conf.expires) { 1464 o_session.set(tsip_session.prototype.SetExpires(o_conf.expires)); 1465 } 1466 // from 1467 if (o_conf.from) { 1468 o_session.set(tsip_session.prototype.SetFromStr(o_conf.from)); 1469 } 1470 } 1471 1472 /** 1473 Gets the remote party SIP Uri (e.g. sip:john.doe@example.com). This Uri could be used a match an incoming call to a contact from your address book. 1474 @returns {String} The remote party SIP Uri. 1475 @see <a href="#getRemoteFriendlyName">getRemoteFriendlyName</a> 1476 */ 1477 SIPml.Session.prototype.getRemoteUri = function () { 1478 return (this.o_session.b_local ? this.o_session.o_uri_to : this.o_session.o_uri_from).toString(); 1479 } 1480 1481 /** 1482 Gets the remote party friendly name (e.g. 'John Doe'). 1483 @returns {String} The remote party friendly name. 1484 @see <a href="#getRemoteUri">getRemoteUri</a> 1485 */ 1486 SIPml.Session.prototype.getRemoteFriendlyName = function () { 1487 var o_uri = this.o_session.b_local ? this.o_session.o_uri_to : this.o_session.o_uri_from; 1488 return o_uri.s_display_name ? o_uri.s_display_name : o_uri.s_user_name; 1489 } 1490 1491 /** 1492 Rejects an incoming SIP MESSAGE (SMS-like) or audio/video call. 1493 @param {SIPml.Session.Configuration} [configuration] Configuration value. 1494 @returns {Integer} 0 if successful; otherwise nonzero 1495 @throws {ERR_NOT_READY} <font color="red">ERR_NOT_READY</font> 1496 */ 1497 SIPml.Session.prototype.reject = function (o_conf) { 1498 this.setConfiguration(o_conf); 1499 return this.o_session.reject(); 1500 } 1501 1502 /** 1503 Accepts an incoming SIP MESSAGE (SMS-like) or audio/video call. 1504 @param {SIPml.Session.Configuration} [configuration] Configuration value. 1505 @returns {Integer} 0 if successful; otherwise nonzero 1506 @throws {ERR_NOT_READY} <font color="red">ERR_NOT_READY</font> 1507 */ 1508 SIPml.Session.prototype.accept = function (o_conf) { 1509 this.setConfiguration(o_conf); 1510 return this.o_session.accept(); 1511 } 1512 1513 1514 1515 // ================================== SIPml.Session.Event ================================== 1516 1517 1518 /** 1519 SIP session event object. You should never create an instance of this class by yourself. 1520 @constructor 1521 @extends SIPml.Event 1522 @param {SIPml.Session} [session] The session type would be <a href="SIPml.Session.Registration.html">SIPml.Session.Registration</a>, <a href="SIPml.Session.Call.html">SIPml.Session.Call</a> or <a href="SIPml.Session.Message.html">SIPml.Session.Message</a>. 1523 @param {String} type The event type or identifier. Please check <a href="SIPml.EventTarget.html#SIPml.EventTarget.Session">this link</a> for more information about all supported session event types. 1524 @param {tsip_event} [event] Private wrapped session object. 1525 @property {SIPml.Session} session Session associated to this event. Would be <a href="SIPml.Session.Registration.html">SIPml.Session.Registration</a>, <a href="SIPml.Session.Call.html">SIPml.Session.Call</a> or <a href="SIPml.Session.Message.html">SIPml.Session.Message</a>. 1526 */ 1527 SIPml.Session.Event = function (o_session, s_type, o_event) { 1528 SIPml.Event.call(this, s_type, o_event); 1529 this.session = o_session; 1530 } 1531 1532 SIPml.Session.Event.prototype = Object.create(SIPml.Event.prototype); 1533 1534 1535 /** 1536 Gets the name of destination for the current call transfer. 1537 @returns {String} The name of destination for the current call transfer (e.g. 'John Doe'). 1538 */ 1539 SIPml.Session.Event.prototype.getTransferDestinationFriendlyName = function () { 1540 var o_message = this.o_event ? this.o_event.get_message() : null; 1541 if (o_message) { 1542 var o_hdr_Refer_To = o_message.get_header(tsip_header_type_e.Refer_To); 1543 if (o_hdr_Refer_To && o_hdr_Refer_To.o_uri) { 1544 return (o_hdr_Refer_To.s_display_name ? o_hdr_Refer_To.s_display_name : o_hdr_Refer_To.o_uri.s_user_name); 1545 } 1546 } 1547 return null; 1548 } 1549 1550 // ================================== SIPml.Registration ================================== 1551 1552 /** 1553 SIP session registration class. You should never create an instance of this class by yourself. 1554 Please use <a href="SIPml.Stack.html#newSession">stack.newSession()</a> function to create a new registration session. 1555 @constructor 1556 @extends SIPml.Session 1557 @param {tsip_session} session Private wrapped session object 1558 @param {SIPml.Session.Configuration} [configuration] Configuration value. 1559 */ 1560 SIPml.Session.Registration = function (o_session, o_configuration) { 1561 SIPml.Session.call(this, o_session, o_configuration); 1562 } 1563 1564 SIPml.Session.Registration.prototype = Object.create(SIPml.Session.prototype); 1565 1566 /** 1567 Sends SIP REGISTER request to login the user. Refreshing requests will be automatically done based on the expiration time. 1568 @param {SIPml.Session.Configuration} [configuration] Optional configuration value. 1569 @example 1570 var session = <a href="SIPml.Stack.html#newSession">stack.newSession</a>('register', { 1571 expires: 200, 1572 events_listener: { events: '*', listener: onSipEventSession }, 1573 sip_caps: [ 1574 { name: '+g.oma.sip-im', value: null }, 1575 { name: '+audio', value: null }, 1576 { name: 'language', value: '\"en,fr\"' } 1577 ] 1578 }); 1579 session.register(); 1580 1581 @see <a href="#unregister">unregister</a> 1582 @throws {ERR_INVALID_STATE} <font color="red">ERR_INVALID_STATE</font> 1583 */ 1584 SIPml.Session.Registration.prototype.register = function (o_conf) { 1585 // FIXME: apply o_configuration 1586 // FIXME: raise error if stack not started 1587 this.setConfiguration(o_conf); 1588 return this.o_session.register(); 1589 } 1590 1591 /** 1592 Sends SIP REGISTER (expires=0) request to logout the user. 1593 @param {SIPml.Session.Configuration} [configuration] Configuration value. 1594 @see <a href="#register">register</a> 1595 @throws {ERR_INVALID_STATE} <font color="red">ERR_INVALID_STATE</font> 1596 */ 1597 SIPml.Session.Registration.prototype.unregister = function (o_conf) { 1598 // FIXME: raise error if stack not started 1599 this.setConfiguration(o_conf); 1600 return this.o_session.unregister(); 1601 } 1602 1603 // ================================== SIPml.Call ========================================== 1604 1605 /** 1606 SIP audio/video/screenshare call session class. You should never create an instance of this class by yourself. 1607 Please use <a href="SIPml.Stack.html#newSession">stack.newSession()</a> function to create a new audio/video/screenshare session. 1608 @constructor 1609 @extends SIPml.Session 1610 @param {tsip_session} session Private wrapped session object 1611 @param {SIPml.Session.Configuration} [configuration] Configuration value. 1612 @example 1613 var listenerFunc = function(e){ 1614 console.info('session event = ' + e.type); 1615 } 1616 var session = <a href="SIPml.Stack.html#newSession">stack.newSession</a>('call-audiovideo', { 1617 video_local: document.getElementById('video-local'), // <video id="video-local" .../> 1618 video_remote: document.getElementById('video-remote'), // <video id="video-remote" .../> 1619 audio_remote: document.getElementById('audio-remote'), // <audio id="audio-remote" .../> 1620 events_listener: { events: '*', listener: listenerFunc }, 1621 sip_caps: [ 1622 { name: '+g.oma.sip-im' }, 1623 { name: '+sip.ice' }, 1624 { name: 'language', value: '\"en,fr\"' } 1625 ] 1626 }); 1627 @throws {ERR_INVALID_PARAMETER_VALUE} <font color="red">ERR_INVALID_PARAMETER_VALUE</font> 1628 */ 1629 SIPml.Session.Call = function (o_session, o_conf) { 1630 SIPml.Session.call(this, o_session, o_conf); 1631 1632 switch (o_session.s_type) { 1633 case 'call-audio': this.mediaType = tmedia_type_e.AUDIO; break; 1634 case 'call-audiovideo': this.mediaType = tmedia_type_e.AUDIO_VIDEO; break; 1635 case 'call-video': this.mediaType = tmedia_type_e.VIDEO; break; 1636 case 'call-screenshare': this.mediaType = tmedia_type_e.SCREEN_SHARE; break; 1637 } 1638 } 1639 1640 SIPml.Session.Call.prototype = Object.create(SIPml.Session.prototype); 1641 SIPml.Session.Call.prototype.videoLocal = null; 1642 SIPml.Session.Call.prototype.videoRemote = null; 1643 1644 /** 1645 Makes audio/video call. 1646 @param {String} to Destination name, uri, phone number or identifier (e.g. 'sip:johndoe@example.com' or 'johndoe' or '+33600000000'). 1647 @param {SIPml.Session.Configuration} [configuration] Configuration value. 1648 @returns {Integer} 0 if successful; otherwise nonzero 1649 @example 1650 var listenerFunc = function(e){ 1651 console.info('session event = ' + e.type); 1652 } 1653 var session = <a href="SIPml.Stack.html#newSession">stack.newSession</a>('call-audiovideo'); 1654 session.call('johndoe', { 1655 video_local: document.getElementById('video-local'), // <video id="video-local" .../> 1656 video_remote: document.getElementById('video-remote'), // <video id="video-remote" .../> 1657 audio_remote: document.getElementById('audio-remote'), // <audio id="audio-remote" .../> 1658 events_listener: { events: '*', listener: listenerFunc }, 1659 sip_caps: [ 1660 { name: '+g.oma.sip-im' }, 1661 { name: '+sip.ice' }, 1662 { name: 'language', value: '\"en,fr\"' } 1663 ] 1664 }); 1665 1666 @throws {ERR_INVALID_PARAMETER_VALUE | ERR_NOT_READY} <font color="red">ERR_INVALID_PARAMETER_VALUE</font> | <font color="red">ERR_NOT_READY</font> 1667 */ 1668 SIPml.Session.Call.prototype.call = function (s_to, o_conf) { 1669 if (tsk_string_is_null_or_empty(s_to)) { 1670 throw new Error("ERR_INVALID_PARAMETER_VALUE: 'to' must not be null"); 1671 } 1672 if (!SIPml.haveMediaStream()) { 1673 throw new Error("ERR_NOT_READY: Media engine not ready yet"); 1674 } 1675 // set destination 1676 this.o_session.set(tsip_session.prototype.SetToStr(s_to)); 1677 // set conf 1678 this.setConfiguration(o_conf); 1679 // make call 1680 return this.o_session.call(this.mediaType); 1681 } 1682 1683 /** 1684 Terminates the audio/video call. 1685 @param {SIPml.Session.Configuration} [configuration] Configuration value. 1686 @returns {Integer} 0 if successful; otherwise nonzero 1687 @throws {ERR_NOT_READY} <font color="red">ERR_NOT_READY</font> 1688 */ 1689 SIPml.Session.Call.prototype.hangup = function (o_conf) { 1690 this.setConfiguration(o_conf); 1691 return this.o_session.hangup(); 1692 } 1693 1694 /** 1695 Holds the audio/video call. 1696 @param {SIPml.Session.Configuration} [configuration] Configuration value. 1697 @returns {Integer} 0 if successful; otherwise nonzero 1698 @throws {ERR_NOT_READY} <font color="red">ERR_NOT_READY</font> 1699 */ 1700 SIPml.Session.Call.prototype.hold = function (o_conf) { 1701 this.setConfiguration(o_conf); 1702 return this.o_session.hold(this.mediaType); 1703 } 1704 1705 /** 1706 Resumes the audio/video call. 1707 @param {SIPml.Session.Configuration} [configuration] Configuration value. 1708 @returns {Integer} 0 if successful; otherwise nonzero 1709 @throws {ERR_NOT_READY} <font color="red">ERR_NOT_READY</font> 1710 */ 1711 SIPml.Session.Call.prototype.resume = function (o_conf) { 1712 this.setConfiguration(o_conf); 1713 return this.o_session.resume(this.mediaType); 1714 } 1715 1716 /** 1717 Sends SIP INFO message. 1718 @param {Object|String} [content] SIP INFO request content. 1719 @param {String} [contentType] Content Type. 1720 @param {SIPml.Session.Configuration} [configuration] Configuration value. 1721 @returns {Integer} 0 if successful; otherwise nonzero 1722 @example 1723 session.info('Device orientation: portrait', 'doubango/device-orientation.xml'); 1724 @throws {ERR_NOT_READY} <font color="red">ERR_NOT_READY</font> 1725 */ 1726 SIPml.Session.Call.prototype.info = function (o_content, s_content_type, o_conf) { 1727 this.setConfiguration(o_conf); 1728 return this.o_session.info(o_content, s_content_type); 1729 } 1730 1731 /** 1732 Sends a SIP DTMF digit. 1733 @param {Char} [digit] The digit to send. 1734 @param {SIPml.Session.Configuration} [configuration] Configuration value. 1735 @returns {Integer} 0 if successful; otherwise nonzero 1736 @example 1737 session.dtmf('#'); 1738 @throws {ERR_INVALID_PARAMETER_VALUE | ERR_NOT_READY} <font color="red">ERR_INVALID_PARAMETER_VALUE</font> | <font color="red">ERR_NOT_READY</font> 1739 */ 1740 SIPml.Session.Call.prototype.dtmf = function (c_digit, o_conf) { 1741 this.setConfiguration(o_conf); 1742 return this.o_session.dtmf(c_digit); 1743 } 1744 1745 /** 1746 Transfers the call to a new destination. 1747 @param {String} to Transfer destination name, uri, phone number or identifier (e.g. 'sip:johndoe@example.com' or 'johndoe' or '+33600000000'). 1748 @param {SIPml.Session.Configuration} [configuration] Configuration value. 1749 @returns {Integer} 0 if successful; otherwise nonzero 1750 @example 1751 session.transfer('johndoe'); 1752 @throws {ERR_INVALID_PARAMETER_VALUE | ERR_NOT_READY} <font color="red">ERR_INVALID_PARAMETER_VALUE</font> | <font color="red">ERR_NOT_READY</font> 1753 */ 1754 SIPml.Session.Call.prototype.transfer = function (s_to, o_conf) { 1755 this.setConfiguration(o_conf); 1756 return this.o_session.transfer(s_to); 1757 } 1758 1759 /** 1760 Accepts incoming transfer request. 1761 @param {SIPml.Session.Configuration} [configuration] Configuration value. 1762 @returns {Integer} 0 if successful; otherwise nonzero 1763 @throws {ERR_NOT_READY} <font color="red">ERR_NOT_READY</font> 1764 */ 1765 SIPml.Session.Call.prototype.acceptTransfer = function (o_conf) { 1766 this.setConfiguration(o_conf); 1767 return this.o_session.transfer_accept(); 1768 } 1769 1770 /** 1771 Rejects incoming transfer request. 1772 @param {SIPml.Session.Configuration} [configuration] Configuration value. 1773 @returns {Integer} 0 if successful; otherwise nonzero 1774 @throws {ERR_NOT_READY} <font color="red">ERR_NOT_READY</font> 1775 */ 1776 SIPml.Session.Call.prototype.rejectTransfer = function (o_conf) { 1777 this.setConfiguration(o_conf); 1778 return this.o_session.transfer_reject(); 1779 } 1780 1781 /** 1782 Starts sharing your entire desktop or an App using BFCP(<a href="https://tools.ietf.org/html/rfc4582">rfc4582</a>). Requires webrt4all plugin. 1783 @since version 2.0.0 1784 @param {SIPml.Session.Configuration} [configuration] Configuration value. 1785 @returns {Integer} 0 if successful; otherwise nonzero 1786 @throws {ERR_NOT_READY | ERR_NOT_SUPPORTED} <font color="red">ERR_NOT_READY</font> | <font color="red">ERR_NOT_SUPPORTED</font> 1787 */ 1788 SIPml.Session.Call.prototype.startBfcpShare = function (o_conf) { 1789 if (SIPml.getWebRtcType() != 'w4a') { 1790 throw new Error("ERR_NOT_SUPPORTED: BFCP sharing requires webrtc4all plugin"); 1791 } 1792 this.setConfiguration(o_conf); 1793 return this.o_session.start_bfcp_share(); 1794 } 1795 1796 /** 1797 Stops sharing your entire desktop or an App using BFCP(<a href="https://tools.ietf.org/html/rfc4582">rfc4582</a>). Requires webrt4all plugin. 1798 @since version 2.0.0 1799 @param {SIPml.Session.Configuration} [configuration] Configuration value. 1800 @returns {Integer} 0 if successful; otherwise nonzero 1801 @throws {ERR_NOT_READY | ERR_NOT_SUPPORTED} <font color="red">ERR_NOT_READY</font> | <font color="red">ERR_NOT_SUPPORTED</font> 1802 */ 1803 SIPml.Session.Call.prototype.stopBfcpShare = function (o_conf) { 1804 if (SIPml.getWebRtcType() != 'w4a') { 1805 throw new Error("ERR_NOT_SUPPORTED: BFCP sharing requires webrtc4all plugin"); 1806 } 1807 this.setConfiguration(o_conf); 1808 return this.o_session.stop_bfcp_share(); 1809 } 1810 1811 /** 1812 Mutes or unmutes a media. 1813 @since version 2.0.0 1814 @param {String} media Media to mute. Must be <i>audio</i>, <i>video</i>. 1815 @param {Boolean} mute Whether to mute (true) or unmute (false) the media. 1816 @returns {Integer} 0 if successful; otherwise nonzero 1817 @throws {ERR_INVALID_ARGUMENT | ERR_NOT_SUPPORTED} <font color="red">ERR_INVALID_ARGUMENT</font> | <font color="red">ERR_NOT_SUPPORTED</font> 1818 */ 1819 SIPml.Session.Call.prototype.mute = function (s_media, b_mute) { 1820 if ((s_media !== 'audio' && s_media !== 'video') || typeof b_mute != "boolean") { 1821 throw new Error("ERR_INVALID_ARGUMENT"); 1822 } 1823 return this.o_session.set_mute(s_media, b_mute); 1824 } 1825 1826 // ================================== SIPml.Session.Message ========================================== 1827 1828 /** 1829 SIP MESSAGE (SMS) session class. You should never create an instance of this class by yourself. 1830 Please use <a href="SIPml.Stack.html#newSession">stack.newSession()</a> function to create a messaging/IM session. 1831 @constructor 1832 @param {tsip_session} session Private session object. 1833 @param {SIPml.Session.Configuration} [configuration] Configuration value. 1834 @example 1835 var session = <a href="SIPml.Stack.html#newSession">stack.newSession</a>('message'); 1836 */ 1837 SIPml.Session.Message = function (o_session, o_conf) { 1838 SIPml.Session.call(this, o_session, o_conf); 1839 1840 } 1841 1842 SIPml.Session.Message.prototype = Object.create(SIPml.Session.prototype); 1843 1844 /** 1845 Sends a SIP MESSAGE (SMS-like) request. 1846 @param {String} to Destination name, uri, phone number or identifier (e.g. 'sip:johndoe@example.com' or 'johndoe' or '+33600000000'). 1847 @param {Object|String} [content] The message content. 1848 @param {String} [contentType] The content type. 1849 @param {SIPml.Session.Configuration} [configuration] Configuration value. 1850 @returns {Integer} 0 if successful; otherwise nonzero 1851 @example 1852 var session = <a href="SIPml.Stack.html#newSession">stack.newSession</a>('message'); 1853 session.send('johndoe', 'Pêche à la moule', 'text/plain;charset=utf8',{ 1854 sip_caps: [ 1855 { name: '+g.oma.sip-im' }, 1856 { name: '+sip.ice' }, 1857 { name: 'language', value: '\"en,fr\"' } 1858 ], 1859 sip_headers: [ 1860 { name: 'What', value: 'Sending SMS' }, 1861 { name: 'My-Organization', value: 'Doubango Telecom' } 1862 ] 1863 }); 1864 @throws {ERR_INVALID_PARAMETER_VALUE | ERR_NOT_READY} <font color="red">ERR_INVALID_PARAMETER_VALUE</font> | <font color="red">ERR_NOT_READY</font> 1865 */ 1866 SIPml.Session.Message.prototype.send = function (s_to, o_content, s_content_type, o_conf) { 1867 if (tsk_string_is_null_or_empty(s_to)) { 1868 throw new Error("ERR_INVALID_PARAMETER_VALUE: 'to' must not be null"); 1869 } 1870 1871 // apply configuration values 1872 this.setConfiguration(o_conf); 1873 // set destination 1874 this.o_session.set(tsip_session.prototype.SetToStr(s_to)); 1875 // sends the message 1876 return this.o_session.send(o_content, s_content_type); 1877 } 1878 1879 1880 1881 // ================================== SIPml.Session.Publish ========================================== 1882 1883 1884 /** 1885 SIP PUBLISH (for presence status publication) session class.You should never create an instance of this class by yourself. 1886 Please use <a href="SIPml.Stack.html#newSession">stack.newSession()</a> function to create a new presence publication session. 1887 @constructor 1888 @extends SIPml.Session 1889 @since version 1.1.0 1890 @param {tsip_session} session Private session object. 1891 @param {SIPml.Session.Configuration} [configuration] Configuration value. 1892 @example 1893 var session = <a href="SIPml.Stack.html#newSession">stack.newSession</a>('publish', { 1894 expires: 200, 1895 events_listener: { events: '*', listener: function(e){} }, 1896 sip_headers: [ 1897 { name: 'Event', value: 'presence' } // very important 1898 ], 1899 sip_caps: [ 1900 { name: '+g.oma.sip-im', value: null }, 1901 { name: '+audio', value: null }, 1902 { name: 'language', value: '\"en,fr\"' } 1903 ] 1904 }); 1905 */ 1906 SIPml.Session.Publish = function (o_session, o_conf) { 1907 SIPml.Session.call(this, o_session, o_conf); 1908 // set destination to ourself (https://groups.google.com/forum/#!topic/doubango/XKWTQ9TgjPU) 1909 o_session.set(tsip_session.prototype.SetToUri(o_session.get_stack().identity.o_uri_impu)); 1910 } 1911 1912 SIPml.Session.Publish.prototype = Object.create(SIPml.Session.prototype); 1913 1914 /** 1915 Sends a SIP PUBLISH (for presence status publication) request. 1916 @since version 1.1.0 1917 @param {Object|String} [content] The request content. 1918 @param {String} [contentType] The content type. 1919 @param {SIPml.Session.Configuration} [configuration] Configuration value. 1920 @returns {Integer} 0 if successful; otherwise nonzero 1921 @example 1922 var session = <a href="SIPml.Stack.html#newSession">stack.newSession</a>('publish'); 1923 var contentType = 'application/pidf+xml'; 1924 var content = '<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n' + 1925 '<presence xmlns=\"urn:ietf:params:xml:ns:pidf\"\n' + 1926 ' xmlns:im=\"urn:ietf:params:xml:ns:pidf:im\"' + 1927 ' entity=\"sip:bob@example.com\">\n' + 1928 '<tuple id=\"s8794\">\n' + 1929 '<status>\n'+ 1930 ' <basic>open</basic>\n' + 1931 ' <im:im>away</im:im>\n' + 1932 '</status>\n' + 1933 '<contact priority=\"0.8\">tel:+33600000000</contact>\n' + 1934 '<note xml:lang=\"fr\">Bonjour de Paris :)</note>\n' + 1935 '</tuple>\n' + 1936 '</presence>'; 1937 1938 session.publish(content, contentType,{ 1939 expires: 200, 1940 sip_caps: [ 1941 { name: '+g.oma.sip-im' }, 1942 { name: '+sip.ice' }, 1943 { name: 'language', value: '\"en,fr\"' } 1944 ], 1945 sip_headers: [ 1946 { name: 'Event', value: 'presence' }, 1947 { name: 'Organization', value: 'Doubango Telecom' } 1948 ] 1949 }); 1950 @returns {Integer} 0 if successful; otherwise nonzero 1951 @throws {ERR_INVALID_PARAMETER_VALUE | ERR_NOT_READY} <font color="red">ERR_INVALID_PARAMETER_VALUE</font> | <font color="red">ERR_NOT_READY</font> 1952 @see <a href="#unpublish">unpublish</a> 1953 */ 1954 SIPml.Session.Publish.prototype.publish = function (o_content, s_content_type, o_conf) { 1955 // apply configuration values 1956 this.setConfiguration(o_conf); 1957 // sends the PUBLISH request 1958 return this.o_session.publish(o_content, s_content_type); 1959 } 1960 1961 /** 1962 Remove/unpublish presence data from the server. 1963 @since version 1.1.0 1964 @param {SIPml.Session.Configuration} [configuration] Configuration value. 1965 @throws {ERR_INVALID_PARAMETER_VALUE | ERR_NOT_READY} <font color="red">ERR_INVALID_PARAMETER_VALUE</font> | <font color="red">ERR_NOT_READY</font> 1966 @see <a href="#publish">publish</a> 1967 */ 1968 SIPml.Session.Publish.prototype.unpublish = function (o_conf) { 1969 // apply configuration values 1970 this.setConfiguration(o_conf); 1971 // sends the PUBLISH request (expires = 0) 1972 return this.o_session.unpublish(); 1973 } 1974 1975 1976 1977 1978 1979 // ================================== SIPml.Session.Subscribe ========================================== 1980 1981 1982 /** 1983 SIP SUBSCRIBE (for presence status subscription) session class.You should never create an instance of this class by yourself. 1984 Please use <a href="SIPml.Stack.html#newSession">stack.newSession()</a> function to create a new presence subscription session. 1985 @constructor 1986 @extends SIPml.Session 1987 @since version 1.1.0 1988 @param {tsip_session} session Private session object. 1989 @param {SIPml.Session.Configuration} [configuration] Configuration value. 1990 @example 1991 var session = <a href="SIPml.Stack.html#newSession">stack.newSession</a>('subscribe', { 1992 expires: 200, 1993 events_listener: { events: '*', listener: function(e){} }, 1994 sip_headers: [ 1995 { name: 'Event', value: 'presence' }, 1996 { name: 'Accept', value: 'application/pidf+xml' } 1997 ], 1998 sip_caps: [ 1999 { name: '+g.oma.sip-im', value: null }, 2000 { name: '+audio', value: null }, 2001 { name: 'language', value: '\"en,fr\"' } 2002 ] 2003 }); 2004 */ 2005 SIPml.Session.Subscribe = function (o_session, o_conf) { 2006 SIPml.Session.call(this, o_session, o_conf); 2007 2008 } 2009 2010 SIPml.Session.Subscribe.prototype = Object.create(SIPml.Session.prototype); 2011 2012 /** 2013 Sends a SIP SUBSCRIBE (for presence status subscription) request. 2014 @since version 1.1.0 2015 @param {String} to Destination name, uri, phone number or identifier (e.g. 'sip:johndoe@example.com' or 'johndoe' or '+33600000000'). 2016 @param {SIPml.Session.Configuration} [configuration] Configuration value. 2017 @returns {Integer} 0 if successful; otherwise nonzero 2018 @example 2019 var onEvent = function(e){ 2020 if(e.type == 'i_notify'){ 2021 // process incoming NOTIFY request 2022 } 2023 } 2024 var session = <a href="SIPml.Stack.html#newSession">stack.newSession</a>('subscribe', { 2025 expires: 200, 2026 events_listener: { events: '*', listener: onEvent }, 2027 sip_headers: [ 2028 { name: 'Event', value: 'presence' }, 2029 { name: 'Accept', value: 'application/pidf+xml' } 2030 ], 2031 sip_caps: [ 2032 { name: '+g.oma.sip-im', value: null }, 2033 { name: '+audio', value: null }, 2034 { name: 'language', value: '\"en,fr\"' } 2035 ] 2036 }); 2037 session.subscribe('johndoe'); // watch for johndoe's presence status 2038 2039 @throws {ERR_INVALID_PARAMETER_VALUE | ERR_NOT_READY} <font color="red">ERR_INVALID_PARAMETER_VALUE</font> | <font color="red">ERR_NOT_READY</font> 2040 @see <a href="#unsubscribe">unsubscribe</a> 2041 */ 2042 SIPml.Session.Subscribe.prototype.subscribe = function (s_to, o_conf) { 2043 if (tsk_string_is_null_or_empty(s_to)) { 2044 throw new Error("ERR_INVALID_PARAMETER_VALUE: 'to' must not be null"); 2045 } 2046 // set destination 2047 this.o_session.set(tsip_session.prototype.SetToStr(s_to)); 2048 // apply configuration values 2049 this.setConfiguration(o_conf); 2050 // sends the PUBLISH request 2051 return this.o_session.subscribe(); 2052 } 2053 2054 /** 2055 Unsubscribe. 2056 @since version 1.1.0 2057 @param {SIPml.Session.Configuration} [configuration] Configuration value. 2058 @returns {Integer} 0 if successful; otherwise nonzero 2059 @throws {ERR_INVALID_PARAMETER_VALUE | ERR_NOT_READY} <font color="red">ERR_INVALID_PARAMETER_VALUE</font> | <font color="red">ERR_NOT_READY</font> 2060 @see <a href="#subscribe">subscribe</a> 2061 */ 2062 SIPml.Session.Subscribe.prototype.unsubscribe = function (o_conf) { 2063 // apply configuration values 2064 this.setConfiguration(o_conf); 2065 // sends the SUBSCRIBE request (expires = 0) 2066 return this.o_session.unsubscribe(); 2067 } 2068