Fork of FusionPBX but with LDAP kinda working
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

11859 lines
367 KiB

  1. /*
  2. * SIP version 0.7.8
  3. * Copyright (c) 2014-2017 Junction Networks, Inc <http://www.onsip.com>
  4. * Homepage: http://sipjs.com
  5. * License: http://sipjs.com/license/
  6. *
  7. *
  8. * ~~~SIP.js contains substantial portions of JsSIP under the following license~~~
  9. * Homepage: http://jssip.net
  10. * Copyright (c) 2012-2013 José Luis Millán - Versatica <http://www.versatica.com>
  11. *
  12. * Permission is hereby granted, free of charge, to any person obtaining
  13. * a copy of this software and associated documentation files (the
  14. * "Software"), to deal in the Software without restriction, including
  15. * without limitation the rights to use, copy, modify, merge, publish,
  16. * distribute, sublicense, and/or sell copies of the Software, and to
  17. * permit persons to whom the Software is furnished to do so, subject to
  18. * the following conditions:
  19. *
  20. * The above copyright notice and this permission notice shall be
  21. * included in all copies or substantial portions of the Software.
  22. *
  23. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  24. * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  25. * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  26. * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
  27. * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
  28. * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  29. * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  30. *
  31. * ~~~ end JsSIP license ~~~
  32. */
  33. (function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.SIP = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
  34. // Copyright Joyent, Inc. and other Node contributors.
  35. //
  36. // Permission is hereby granted, free of charge, to any person obtaining a
  37. // copy of this software and associated documentation files (the
  38. // "Software"), to deal in the Software without restriction, including
  39. // without limitation the rights to use, copy, modify, merge, publish,
  40. // distribute, sublicense, and/or sell copies of the Software, and to permit
  41. // persons to whom the Software is furnished to do so, subject to the
  42. // following conditions:
  43. //
  44. // The above copyright notice and this permission notice shall be included
  45. // in all copies or substantial portions of the Software.
  46. //
  47. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
  48. // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  49. // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
  50. // NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
  51. // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
  52. // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
  53. // USE OR OTHER DEALINGS IN THE SOFTWARE.
  54. function EventEmitter() {
  55. this._events = this._events || {};
  56. this._maxListeners = this._maxListeners || undefined;
  57. }
  58. module.exports = EventEmitter;
  59. // Backwards-compat with node 0.10.x
  60. EventEmitter.EventEmitter = EventEmitter;
  61. EventEmitter.prototype._events = undefined;
  62. EventEmitter.prototype._maxListeners = undefined;
  63. // By default EventEmitters will print a warning if more than 10 listeners are
  64. // added to it. This is a useful default which helps finding memory leaks.
  65. EventEmitter.defaultMaxListeners = 10;
  66. // Obviously not all Emitters should be limited to 10. This function allows
  67. // that to be increased. Set to zero for unlimited.
  68. EventEmitter.prototype.setMaxListeners = function(n) {
  69. if (!isNumber(n) || n < 0 || isNaN(n))
  70. throw TypeError('n must be a positive number');
  71. this._maxListeners = n;
  72. return this;
  73. };
  74. EventEmitter.prototype.emit = function(type) {
  75. var er, handler, len, args, i, listeners;
  76. if (!this._events)
  77. this._events = {};
  78. // If there is no 'error' event listener then throw.
  79. if (type === 'error') {
  80. if (!this._events.error ||
  81. (isObject(this._events.error) && !this._events.error.length)) {
  82. er = arguments[1];
  83. if (er instanceof Error) {
  84. throw er; // Unhandled 'error' event
  85. }
  86. throw TypeError('Uncaught, unspecified "error" event.');
  87. }
  88. }
  89. handler = this._events[type];
  90. if (isUndefined(handler))
  91. return false;
  92. if (isFunction(handler)) {
  93. switch (arguments.length) {
  94. // fast cases
  95. case 1:
  96. handler.call(this);
  97. break;
  98. case 2:
  99. handler.call(this, arguments[1]);
  100. break;
  101. case 3:
  102. handler.call(this, arguments[1], arguments[2]);
  103. break;
  104. // slower
  105. default:
  106. len = arguments.length;
  107. args = new Array(len - 1);
  108. for (i = 1; i < len; i++)
  109. args[i - 1] = arguments[i];
  110. handler.apply(this, args);
  111. }
  112. } else if (isObject(handler)) {
  113. len = arguments.length;
  114. args = new Array(len - 1);
  115. for (i = 1; i < len; i++)
  116. args[i - 1] = arguments[i];
  117. listeners = handler.slice();
  118. len = listeners.length;
  119. for (i = 0; i < len; i++)
  120. listeners[i].apply(this, args);
  121. }
  122. return true;
  123. };
  124. EventEmitter.prototype.addListener = function(type, listener) {
  125. var m;
  126. if (!isFunction(listener))
  127. throw TypeError('listener must be a function');
  128. if (!this._events)
  129. this._events = {};
  130. // To avoid recursion in the case that type === "newListener"! Before
  131. // adding it to the listeners, first emit "newListener".
  132. if (this._events.newListener)
  133. this.emit('newListener', type,
  134. isFunction(listener.listener) ?
  135. listener.listener : listener);
  136. if (!this._events[type])
  137. // Optimize the case of one listener. Don't need the extra array object.
  138. this._events[type] = listener;
  139. else if (isObject(this._events[type]))
  140. // If we've already got an array, just append.
  141. this._events[type].push(listener);
  142. else
  143. // Adding the second element, need to change to array.
  144. this._events[type] = [this._events[type], listener];
  145. // Check for listener leak
  146. if (isObject(this._events[type]) && !this._events[type].warned) {
  147. var m;
  148. if (!isUndefined(this._maxListeners)) {
  149. m = this._maxListeners;
  150. } else {
  151. m = EventEmitter.defaultMaxListeners;
  152. }
  153. if (m && m > 0 && this._events[type].length > m) {
  154. this._events[type].warned = true;
  155. console.error('(node) warning: possible EventEmitter memory ' +
  156. 'leak detected. %d listeners added. ' +
  157. 'Use emitter.setMaxListeners() to increase limit.',
  158. this._events[type].length);
  159. if (typeof console.trace === 'function') {
  160. // not supported in IE 10
  161. console.trace();
  162. }
  163. }
  164. }
  165. return this;
  166. };
  167. EventEmitter.prototype.on = EventEmitter.prototype.addListener;
  168. EventEmitter.prototype.once = function(type, listener) {
  169. if (!isFunction(listener))
  170. throw TypeError('listener must be a function');
  171. var fired = false;
  172. function g() {
  173. this.removeListener(type, g);
  174. if (!fired) {
  175. fired = true;
  176. listener.apply(this, arguments);
  177. }
  178. }
  179. g.listener = listener;
  180. this.on(type, g);
  181. return this;
  182. };
  183. // emits a 'removeListener' event iff the listener was removed
  184. EventEmitter.prototype.removeListener = function(type, listener) {
  185. var list, position, length, i;
  186. if (!isFunction(listener))
  187. throw TypeError('listener must be a function');
  188. if (!this._events || !this._events[type])
  189. return this;
  190. list = this._events[type];
  191. length = list.length;
  192. position = -1;
  193. if (list === listener ||
  194. (isFunction(list.listener) && list.listener === listener)) {
  195. delete this._events[type];
  196. if (this._events.removeListener)
  197. this.emit('removeListener', type, listener);
  198. } else if (isObject(list)) {
  199. for (i = length; i-- > 0;) {
  200. if (list[i] === listener ||
  201. (list[i].listener && list[i].listener === listener)) {
  202. position = i;
  203. break;
  204. }
  205. }
  206. if (position < 0)
  207. return this;
  208. if (list.length === 1) {
  209. list.length = 0;
  210. delete this._events[type];
  211. } else {
  212. list.splice(position, 1);
  213. }
  214. if (this._events.removeListener)
  215. this.emit('removeListener', type, listener);
  216. }
  217. return this;
  218. };
  219. EventEmitter.prototype.removeAllListeners = function(type) {
  220. var key, listeners;
  221. if (!this._events)
  222. return this;
  223. // not listening for removeListener, no need to emit
  224. if (!this._events.removeListener) {
  225. if (arguments.length === 0)
  226. this._events = {};
  227. else if (this._events[type])
  228. delete this._events[type];
  229. return this;
  230. }
  231. // emit removeListener for all listeners on all events
  232. if (arguments.length === 0) {
  233. for (key in this._events) {
  234. if (key === 'removeListener') continue;
  235. this.removeAllListeners(key);
  236. }
  237. this.removeAllListeners('removeListener');
  238. this._events = {};
  239. return this;
  240. }
  241. listeners = this._events[type];
  242. if (isFunction(listeners)) {
  243. this.removeListener(type, listeners);
  244. } else {
  245. // LIFO order
  246. while (listeners.length)
  247. this.removeListener(type, listeners[listeners.length - 1]);
  248. }
  249. delete this._events[type];
  250. return this;
  251. };
  252. EventEmitter.prototype.listeners = function(type) {
  253. var ret;
  254. if (!this._events || !this._events[type])
  255. ret = [];
  256. else if (isFunction(this._events[type]))
  257. ret = [this._events[type]];
  258. else
  259. ret = this._events[type].slice();
  260. return ret;
  261. };
  262. EventEmitter.listenerCount = function(emitter, type) {
  263. var ret;
  264. if (!emitter._events || !emitter._events[type])
  265. ret = 0;
  266. else if (isFunction(emitter._events[type]))
  267. ret = 1;
  268. else
  269. ret = emitter._events[type].length;
  270. return ret;
  271. };
  272. function isFunction(arg) {
  273. return typeof arg === 'function';
  274. }
  275. function isNumber(arg) {
  276. return typeof arg === 'number';
  277. }
  278. function isObject(arg) {
  279. return typeof arg === 'object' && arg !== null;
  280. }
  281. function isUndefined(arg) {
  282. return arg === void 0;
  283. }
  284. },{}],2:[function(require,module,exports){
  285. module.exports={
  286. "name": "sip.js",
  287. "title": "SIP.js",
  288. "description": "A simple, intuitive, and powerful JavaScript signaling library",
  289. "version": "0.7.8",
  290. "main": "src/index.js",
  291. "browser": {
  292. "./src/environment.js": "./src/environment_browser.js"
  293. },
  294. "homepage": "http://sipjs.com",
  295. "author": "OnSIP <developer@onsip.com> (http://sipjs.com/authors/)",
  296. "contributors": [
  297. {
  298. "url": "https://github.com/onsip/SIP.js/blob/master/THANKS.md"
  299. }
  300. ],
  301. "repository": {
  302. "type": "git",
  303. "url": "https://github.com/onsip/SIP.js.git"
  304. },
  305. "keywords": [
  306. "sip",
  307. "websocket",
  308. "webrtc",
  309. "library",
  310. "javascript"
  311. ],
  312. "devDependencies": {
  313. "beefy": "^2.1.5",
  314. "browserify": "^4.1.8",
  315. "grunt": "~0.4.0",
  316. "grunt-browserify": "^4.0.1",
  317. "grunt-cli": "~0.1.6",
  318. "grunt-contrib-copy": "^0.5.0",
  319. "grunt-contrib-jasmine": "^1.0.3",
  320. "grunt-contrib-jshint": ">0.5.0",
  321. "grunt-contrib-uglify": "~0.2.0",
  322. "grunt-peg": "~1.3.1",
  323. "grunt-trimtrailingspaces": "^0.4.0",
  324. "pegjs": "^0.8.0"
  325. },
  326. "engines": {
  327. "node": ">=0.12"
  328. },
  329. "license": "MIT",
  330. "scripts": {
  331. "repl": "beefy test/repl.js --open",
  332. "build": "grunt build",
  333. "prepublish": "cd src/Grammar && mkdir -p dist && pegjs --extra-options-file peg.json src/Grammar.pegjs dist/Grammar.js",
  334. "test": "grunt travis --verbose"
  335. },
  336. "dependencies": {
  337. "ws": "^1.0.1"
  338. },
  339. "optionalDependencies": {
  340. "promiscuous": "^0.6.0"
  341. }
  342. }
  343. },{}],3:[function(require,module,exports){
  344. "use strict";
  345. module.exports = function (SIP) {
  346. var ClientContext;
  347. ClientContext = function (ua, method, target, options) {
  348. var originalTarget = target;
  349. // Validate arguments
  350. if (target === undefined) {
  351. throw new TypeError('Not enough arguments');
  352. }
  353. this.ua = ua;
  354. this.logger = ua.getLogger('sip.clientcontext');
  355. this.method = method;
  356. target = ua.normalizeTarget(target);
  357. if (!target) {
  358. throw new TypeError('Invalid target: ' + originalTarget);
  359. }
  360. /* Options
  361. * - extraHeaders
  362. * - params
  363. * - contentType
  364. * - body
  365. */
  366. options = Object.create(options || Object.prototype);
  367. options.extraHeaders = (options.extraHeaders || []).slice();
  368. // Build the request
  369. this.request = new SIP.OutgoingRequest(this.method,
  370. target,
  371. this.ua,
  372. options.params,
  373. options.extraHeaders);
  374. if (options.body) {
  375. this.body = {};
  376. this.body.body = options.body;
  377. if (options.contentType) {
  378. this.body.contentType = options.contentType;
  379. }
  380. this.request.body = this.body;
  381. }
  382. /* Set other properties from the request */
  383. this.localIdentity = this.request.from;
  384. this.remoteIdentity = this.request.to;
  385. this.data = {};
  386. };
  387. ClientContext.prototype = Object.create(SIP.EventEmitter.prototype);
  388. ClientContext.prototype.send = function () {
  389. (new SIP.RequestSender(this, this.ua)).send();
  390. return this;
  391. };
  392. ClientContext.prototype.cancel = function (options) {
  393. options = options || {};
  394. options.extraHeaders = (options.extraHeaders || []).slice();
  395. var cancel_reason = SIP.Utils.getCancelReason(options.status_code, options.reason_phrase);
  396. this.request.cancel(cancel_reason, options.extraHeaders);
  397. this.emit('cancel');
  398. };
  399. ClientContext.prototype.receiveResponse = function (response) {
  400. var cause = SIP.Utils.getReasonPhrase(response.status_code);
  401. switch(true) {
  402. case /^1[0-9]{2}$/.test(response.status_code):
  403. this.emit('progress', response, cause);
  404. break;
  405. case /^2[0-9]{2}$/.test(response.status_code):
  406. if(this.ua.applicants[this]) {
  407. delete this.ua.applicants[this];
  408. }
  409. this.emit('accepted', response, cause);
  410. break;
  411. default:
  412. if(this.ua.applicants[this]) {
  413. delete this.ua.applicants[this];
  414. }
  415. this.emit('rejected', response, cause);
  416. this.emit('failed', response, cause);
  417. break;
  418. }
  419. };
  420. ClientContext.prototype.onRequestTimeout = function () {
  421. this.emit('failed', null, SIP.C.causes.REQUEST_TIMEOUT);
  422. };
  423. ClientContext.prototype.onTransportError = function () {
  424. this.emit('failed', null, SIP.C.causes.CONNECTION_ERROR);
  425. };
  426. SIP.ClientContext = ClientContext;
  427. };
  428. },{}],4:[function(require,module,exports){
  429. "use strict";
  430. /**
  431. * @fileoverview SIP Constants
  432. */
  433. /**
  434. * SIP Constants.
  435. * @augments SIP
  436. */
  437. module.exports = function (name, version) {
  438. return {
  439. USER_AGENT: name +'/'+ version,
  440. // SIP scheme
  441. SIP: 'sip',
  442. SIPS: 'sips',
  443. // End and Failure causes
  444. causes: {
  445. // Generic error causes
  446. CONNECTION_ERROR: 'Connection Error',
  447. REQUEST_TIMEOUT: 'Request Timeout',
  448. SIP_FAILURE_CODE: 'SIP Failure Code',
  449. INTERNAL_ERROR: 'Internal Error',
  450. // SIP error causes
  451. BUSY: 'Busy',
  452. REJECTED: 'Rejected',
  453. REDIRECTED: 'Redirected',
  454. UNAVAILABLE: 'Unavailable',
  455. NOT_FOUND: 'Not Found',
  456. ADDRESS_INCOMPLETE: 'Address Incomplete',
  457. INCOMPATIBLE_SDP: 'Incompatible SDP',
  458. AUTHENTICATION_ERROR: 'Authentication Error',
  459. DIALOG_ERROR: 'Dialog Error',
  460. // Session error causes
  461. WEBRTC_NOT_SUPPORTED: 'WebRTC Not Supported',
  462. WEBRTC_ERROR: 'WebRTC Error',
  463. CANCELED: 'Canceled',
  464. NO_ANSWER: 'No Answer',
  465. EXPIRES: 'Expires',
  466. NO_ACK: 'No ACK',
  467. NO_PRACK: 'No PRACK',
  468. USER_DENIED_MEDIA_ACCESS: 'User Denied Media Access',
  469. BAD_MEDIA_DESCRIPTION: 'Bad Media Description',
  470. RTP_TIMEOUT: 'RTP Timeout'
  471. },
  472. supported: {
  473. UNSUPPORTED: 'none',
  474. SUPPORTED: 'supported',
  475. REQUIRED: 'required'
  476. },
  477. SIP_ERROR_CAUSES: {
  478. REDIRECTED: [300,301,302,305,380],
  479. BUSY: [486,600],
  480. REJECTED: [403,603],
  481. NOT_FOUND: [404,604],
  482. UNAVAILABLE: [480,410,408,430],
  483. ADDRESS_INCOMPLETE: [484],
  484. INCOMPATIBLE_SDP: [488,606],
  485. AUTHENTICATION_ERROR:[401,407]
  486. },
  487. // SIP Methods
  488. ACK: 'ACK',
  489. BYE: 'BYE',
  490. CANCEL: 'CANCEL',
  491. INFO: 'INFO',
  492. INVITE: 'INVITE',
  493. MESSAGE: 'MESSAGE',
  494. NOTIFY: 'NOTIFY',
  495. OPTIONS: 'OPTIONS',
  496. REGISTER: 'REGISTER',
  497. UPDATE: 'UPDATE',
  498. SUBSCRIBE: 'SUBSCRIBE',
  499. REFER: 'REFER',
  500. PRACK: 'PRACK',
  501. /* SIP Response Reasons
  502. * DOC: http://www.iana.org/assignments/sip-parameters
  503. * Copied from https://github.com/versatica/OverSIP/blob/master/lib/oversip/sip/constants.rb#L7
  504. */
  505. REASON_PHRASE: {
  506. 100: 'Trying',
  507. 180: 'Ringing',
  508. 181: 'Call Is Being Forwarded',
  509. 182: 'Queued',
  510. 183: 'Session Progress',
  511. 199: 'Early Dialog Terminated', // draft-ietf-sipcore-199
  512. 200: 'OK',
  513. 202: 'Accepted', // RFC 3265
  514. 204: 'No Notification', //RFC 5839
  515. 300: 'Multiple Choices',
  516. 301: 'Moved Permanently',
  517. 302: 'Moved Temporarily',
  518. 305: 'Use Proxy',
  519. 380: 'Alternative Service',
  520. 400: 'Bad Request',
  521. 401: 'Unauthorized',
  522. 402: 'Payment Required',
  523. 403: 'Forbidden',
  524. 404: 'Not Found',
  525. 405: 'Method Not Allowed',
  526. 406: 'Not Acceptable',
  527. 407: 'Proxy Authentication Required',
  528. 408: 'Request Timeout',
  529. 410: 'Gone',
  530. 412: 'Conditional Request Failed', // RFC 3903
  531. 413: 'Request Entity Too Large',
  532. 414: 'Request-URI Too Long',
  533. 415: 'Unsupported Media Type',
  534. 416: 'Unsupported URI Scheme',
  535. 417: 'Unknown Resource-Priority', // RFC 4412
  536. 420: 'Bad Extension',
  537. 421: 'Extension Required',
  538. 422: 'Session Interval Too Small', // RFC 4028
  539. 423: 'Interval Too Brief',
  540. 428: 'Use Identity Header', // RFC 4474
  541. 429: 'Provide Referrer Identity', // RFC 3892
  542. 430: 'Flow Failed', // RFC 5626
  543. 433: 'Anonymity Disallowed', // RFC 5079
  544. 436: 'Bad Identity-Info', // RFC 4474
  545. 437: 'Unsupported Certificate', // RFC 4744
  546. 438: 'Invalid Identity Header', // RFC 4744
  547. 439: 'First Hop Lacks Outbound Support', // RFC 5626
  548. 440: 'Max-Breadth Exceeded', // RFC 5393
  549. 469: 'Bad Info Package', // draft-ietf-sipcore-info-events
  550. 470: 'Consent Needed', // RFC 5360
  551. 478: 'Unresolvable Destination', // Custom code copied from Kamailio.
  552. 480: 'Temporarily Unavailable',
  553. 481: 'Call/Transaction Does Not Exist',
  554. 482: 'Loop Detected',
  555. 483: 'Too Many Hops',
  556. 484: 'Address Incomplete',
  557. 485: 'Ambiguous',
  558. 486: 'Busy Here',
  559. 487: 'Request Terminated',
  560. 488: 'Not Acceptable Here',
  561. 489: 'Bad Event', // RFC 3265
  562. 491: 'Request Pending',
  563. 493: 'Undecipherable',
  564. 494: 'Security Agreement Required', // RFC 3329
  565. 500: 'Internal Server Error',
  566. 501: 'Not Implemented',
  567. 502: 'Bad Gateway',
  568. 503: 'Service Unavailable',
  569. 504: 'Server Time-out',
  570. 505: 'Version Not Supported',
  571. 513: 'Message Too Large',
  572. 580: 'Precondition Failure', // RFC 3312
  573. 600: 'Busy Everywhere',
  574. 603: 'Decline',
  575. 604: 'Does Not Exist Anywhere',
  576. 606: 'Not Acceptable'
  577. },
  578. /* SIP Option Tags
  579. * DOC: http://www.iana.org/assignments/sip-parameters/sip-parameters.xhtml#sip-parameters-4
  580. */
  581. OPTION_TAGS: {
  582. '100rel': true, // RFC 3262
  583. 199: true, // RFC 6228
  584. answermode: true, // RFC 5373
  585. 'early-session': true, // RFC 3959
  586. eventlist: true, // RFC 4662
  587. explicitsub: true, // RFC-ietf-sipcore-refer-explicit-subscription-03
  588. 'from-change': true, // RFC 4916
  589. 'geolocation-http': true, // RFC 6442
  590. 'geolocation-sip': true, // RFC 6442
  591. gin: true, // RFC 6140
  592. gruu: true, // RFC 5627
  593. histinfo: true, // RFC 7044
  594. ice: true, // RFC 5768
  595. join: true, // RFC 3911
  596. 'multiple-refer': true, // RFC 5368
  597. norefersub: true, // RFC 4488
  598. nosub: true, // RFC-ietf-sipcore-refer-explicit-subscription-03
  599. outbound: true, // RFC 5626
  600. path: true, // RFC 3327
  601. policy: true, // RFC 6794
  602. precondition: true, // RFC 3312
  603. pref: true, // RFC 3840
  604. privacy: true, // RFC 3323
  605. 'recipient-list-invite': true, // RFC 5366
  606. 'recipient-list-message': true, // RFC 5365
  607. 'recipient-list-subscribe': true, // RFC 5367
  608. replaces: true, // RFC 3891
  609. 'resource-priority': true, // RFC 4412
  610. 'sdp-anat': true, // RFC 4092
  611. 'sec-agree': true, // RFC 3329
  612. tdialog: true, // RFC 4538
  613. timer: true, // RFC 4028
  614. uui: true // RFC 7433
  615. }
  616. };
  617. };
  618. },{}],5:[function(require,module,exports){
  619. "use strict";
  620. /**
  621. * @fileoverview In-Dialog Request Sender
  622. */
  623. /**
  624. * @augments SIP.Dialog
  625. * @class Class creating an In-dialog request sender.
  626. * @param {SIP.Dialog} dialog
  627. * @param {Object} applicant
  628. * @param {SIP.OutgoingRequest} request
  629. */
  630. /**
  631. * @fileoverview in-Dialog Request Sender
  632. */
  633. module.exports = function (SIP) {
  634. var RequestSender;
  635. RequestSender = function(dialog, applicant, request) {
  636. this.dialog = dialog;
  637. this.applicant = applicant;
  638. this.request = request;
  639. // RFC3261 14.1 Modifying an Existing Session. UAC Behavior.
  640. this.reattempt = false;
  641. this.reattemptTimer = null;
  642. };
  643. RequestSender.prototype = {
  644. send: function() {
  645. var self = this,
  646. request_sender = new SIP.RequestSender(this, this.dialog.owner.ua);
  647. request_sender.send();
  648. // RFC3261 14.2 Modifying an Existing Session -UAC BEHAVIOR-
  649. if (this.request.method === SIP.C.INVITE && request_sender.clientTransaction.state !== SIP.Transactions.C.STATUS_TERMINATED) {
  650. this.dialog.uac_pending_reply = true;
  651. request_sender.clientTransaction.on('stateChanged', function stateChanged(){
  652. if (this.state === SIP.Transactions.C.STATUS_ACCEPTED ||
  653. this.state === SIP.Transactions.C.STATUS_COMPLETED ||
  654. this.state === SIP.Transactions.C.STATUS_TERMINATED) {
  655. this.removeListener('stateChanged', stateChanged);
  656. self.dialog.uac_pending_reply = false;
  657. if (self.dialog.uas_pending_reply === false) {
  658. self.dialog.owner.onReadyToReinvite();
  659. }
  660. }
  661. });
  662. }
  663. },
  664. onRequestTimeout: function() {
  665. this.applicant.onRequestTimeout();
  666. },
  667. onTransportError: function() {
  668. this.applicant.onTransportError();
  669. },
  670. receiveResponse: function(response) {
  671. var self = this;
  672. // RFC3261 12.2.1.2 408 or 481 is received for a request within a dialog.
  673. if (response.status_code === 408 || response.status_code === 481) {
  674. this.applicant.onDialogError(response);
  675. } else if (response.method === SIP.C.INVITE && response.status_code === 491) {
  676. if (this.reattempt) {
  677. this.applicant.receiveResponse(response);
  678. } else {
  679. this.request.cseq.value = this.dialog.local_seqnum += 1;
  680. this.reattemptTimer = SIP.Timers.setTimeout(
  681. function() {
  682. if (self.applicant.owner.status !== SIP.Session.C.STATUS_TERMINATED) {
  683. self.reattempt = true;
  684. self.request_sender.send();
  685. }
  686. },
  687. this.getReattemptTimeout()
  688. );
  689. }
  690. } else {
  691. this.applicant.receiveResponse(response);
  692. }
  693. }
  694. };
  695. return RequestSender;
  696. };
  697. },{}],6:[function(require,module,exports){
  698. "use strict";
  699. /**
  700. * @fileoverview SIP Dialog
  701. */
  702. /**
  703. * @augments SIP
  704. * @class Class creating a SIP dialog.
  705. * @param {SIP.RTCSession} owner
  706. * @param {SIP.IncomingRequest|SIP.IncomingResponse} message
  707. * @param {Enum} type UAC / UAS
  708. * @param {Enum} state SIP.Dialog.C.STATUS_EARLY / SIP.Dialog.C.STATUS_CONFIRMED
  709. */
  710. module.exports = function (SIP) {
  711. var RequestSender = require('./Dialog/RequestSender')(SIP);
  712. var Dialog,
  713. C = {
  714. // Dialog states
  715. STATUS_EARLY: 1,
  716. STATUS_CONFIRMED: 2
  717. };
  718. // RFC 3261 12.1
  719. Dialog = function(owner, message, type, state) {
  720. var contact;
  721. this.uac_pending_reply = false;
  722. this.uas_pending_reply = false;
  723. if(!message.hasHeader('contact')) {
  724. return {
  725. error: 'unable to create a Dialog without Contact header field'
  726. };
  727. }
  728. if(message instanceof SIP.IncomingResponse) {
  729. state = (message.status_code < 200) ? C.STATUS_EARLY : C.STATUS_CONFIRMED;
  730. } else {
  731. // Create confirmed dialog if state is not defined
  732. state = state || C.STATUS_CONFIRMED;
  733. }
  734. contact = message.parseHeader('contact');
  735. // RFC 3261 12.1.1
  736. if(type === 'UAS') {
  737. this.id = {
  738. call_id: message.call_id,
  739. local_tag: message.to_tag,
  740. remote_tag: message.from_tag,
  741. toString: function() {
  742. return this.call_id + this.local_tag + this.remote_tag;
  743. }
  744. };
  745. this.state = state;
  746. this.remote_seqnum = message.cseq;
  747. this.local_uri = message.parseHeader('to').uri;
  748. this.remote_uri = message.parseHeader('from').uri;
  749. this.remote_target = contact.uri;
  750. this.route_set = message.getHeaders('record-route');
  751. this.invite_seqnum = message.cseq;
  752. this.local_seqnum = message.cseq;
  753. }
  754. // RFC 3261 12.1.2
  755. else if(type === 'UAC') {
  756. this.id = {
  757. call_id: message.call_id,
  758. local_tag: message.from_tag,
  759. remote_tag: message.to_tag,
  760. toString: function() {
  761. return this.call_id + this.local_tag + this.remote_tag;
  762. }
  763. };
  764. this.state = state;
  765. this.invite_seqnum = message.cseq;
  766. this.local_seqnum = message.cseq;
  767. this.local_uri = message.parseHeader('from').uri;
  768. this.pracked = [];
  769. this.remote_uri = message.parseHeader('to').uri;
  770. this.remote_target = contact.uri;
  771. this.route_set = message.getHeaders('record-route').reverse();
  772. //RENDERBODY
  773. if (this.state === C.STATUS_EARLY && (!owner.hasOffer)) {
  774. this.mediaHandler = owner.mediaHandlerFactory(owner);
  775. }
  776. }
  777. this.logger = owner.ua.getLogger('sip.dialog', this.id.toString());
  778. this.owner = owner;
  779. owner.ua.dialogs[this.id.toString()] = this;
  780. this.logger.log('new ' + type + ' dialog created with status ' + (this.state === C.STATUS_EARLY ? 'EARLY': 'CONFIRMED'));
  781. owner.emit('dialog', this);
  782. };
  783. Dialog.prototype = {
  784. /**
  785. * @param {SIP.IncomingMessage} message
  786. * @param {Enum} UAC/UAS
  787. */
  788. update: function(message, type) {
  789. this.state = C.STATUS_CONFIRMED;
  790. this.logger.log('dialog '+ this.id.toString() +' changed to CONFIRMED state');
  791. if(type === 'UAC') {
  792. // RFC 3261 13.2.2.4
  793. this.route_set = message.getHeaders('record-route').reverse();
  794. }
  795. },
  796. terminate: function() {
  797. this.logger.log('dialog ' + this.id.toString() + ' deleted');
  798. if (this.mediaHandler && this.state !== C.STATUS_CONFIRMED) {
  799. this.mediaHandler.peerConnection.close();
  800. }
  801. delete this.owner.ua.dialogs[this.id.toString()];
  802. },
  803. /**
  804. * @param {String} method request method
  805. * @param {Object} extraHeaders extra headers
  806. * @returns {SIP.OutgoingRequest}
  807. */
  808. // RFC 3261 12.2.1.1
  809. createRequest: function(method, extraHeaders, body) {
  810. var cseq, request;
  811. extraHeaders = (extraHeaders || []).slice();
  812. if(!this.local_seqnum) { this.local_seqnum = Math.floor(Math.random() * 10000); }
  813. cseq = (method === SIP.C.CANCEL || method === SIP.C.ACK) ? this.invite_seqnum : this.local_seqnum += 1;
  814. request = new SIP.OutgoingRequest(
  815. method,
  816. this.remote_target,
  817. this.owner.ua, {
  818. 'cseq': cseq,
  819. 'call_id': this.id.call_id,
  820. 'from_uri': this.local_uri,
  821. 'from_tag': this.id.local_tag,
  822. 'to_uri': this.remote_uri,
  823. 'to_tag': this.id.remote_tag,
  824. 'route_set': this.route_set
  825. }, extraHeaders, body);
  826. request.dialog = this;
  827. return request;
  828. },
  829. /**
  830. * @param {SIP.IncomingRequest} request
  831. * @returns {Boolean}
  832. */
  833. // RFC 3261 12.2.2
  834. checkInDialogRequest: function(request) {
  835. var self = this;
  836. if(!this.remote_seqnum) {
  837. this.remote_seqnum = request.cseq;
  838. } else if(request.cseq < this.remote_seqnum) {
  839. //Do not try to reply to an ACK request.
  840. if (request.method !== SIP.C.ACK) {
  841. request.reply(500);
  842. }
  843. if (request.cseq === this.invite_seqnum) {
  844. return true;
  845. }
  846. return false;
  847. } else if(request.cseq > this.remote_seqnum) {
  848. this.remote_seqnum = request.cseq;
  849. }
  850. switch(request.method) {
  851. // RFC3261 14.2 Modifying an Existing Session -UAS BEHAVIOR-
  852. case SIP.C.INVITE:
  853. if (this.uac_pending_reply === true) {
  854. request.reply(491);
  855. } else if (this.uas_pending_reply === true) {
  856. var retryAfter = (Math.random() * 10 | 0) + 1;
  857. request.reply(500, null, ['Retry-After:' + retryAfter]);
  858. return false;
  859. } else {
  860. this.uas_pending_reply = true;
  861. request.server_transaction.on('stateChanged', function stateChanged(){
  862. if (this.state === SIP.Transactions.C.STATUS_ACCEPTED ||
  863. this.state === SIP.Transactions.C.STATUS_COMPLETED ||
  864. this.state === SIP.Transactions.C.STATUS_TERMINATED) {
  865. this.removeListener('stateChanged', stateChanged);
  866. self.uas_pending_reply = false;
  867. if (self.uac_pending_reply === false) {
  868. self.owner.onReadyToReinvite();
  869. }
  870. }
  871. });
  872. }
  873. // RFC3261 12.2.2 Replace the dialog`s remote target URI if the request is accepted
  874. if(request.hasHeader('contact')) {
  875. request.server_transaction.on('stateChanged', function(){
  876. if (this.state === SIP.Transactions.C.STATUS_ACCEPTED) {
  877. self.remote_target = request.parseHeader('contact').uri;
  878. }
  879. });
  880. }
  881. break;
  882. case SIP.C.NOTIFY:
  883. // RFC6665 3.2 Replace the dialog`s remote target URI if the request is accepted
  884. if(request.hasHeader('contact')) {
  885. request.server_transaction.on('stateChanged', function(){
  886. if (this.state === SIP.Transactions.C.STATUS_COMPLETED) {
  887. self.remote_target = request.parseHeader('contact').uri;
  888. }
  889. });
  890. }
  891. break;
  892. }
  893. return true;
  894. },
  895. sendRequest: function(applicant, method, options) {
  896. options = options || {};
  897. var extraHeaders = (options.extraHeaders || []).slice();
  898. var body = null;
  899. if (options.body) {
  900. if (options.body.body) {
  901. body = options.body;
  902. } else {
  903. body = {};
  904. body.body = options.body;
  905. if (options.contentType) {
  906. body.contentType = options.contentType;
  907. }
  908. }
  909. }
  910. var request = this.createRequest(method, extraHeaders, body),
  911. request_sender = new RequestSender(this, applicant, request);
  912. request_sender.send();
  913. return request;
  914. },
  915. /**
  916. * @param {SIP.IncomingRequest} request
  917. */
  918. receiveRequest: function(request) {
  919. //Check in-dialog request
  920. if(!this.checkInDialogRequest(request)) {
  921. return;
  922. }
  923. this.owner.receiveRequest(request);
  924. }
  925. };
  926. Dialog.C = C;
  927. SIP.Dialog = Dialog;
  928. };
  929. },{"./Dialog/RequestSender":5}],7:[function(require,module,exports){
  930. "use strict";
  931. /**
  932. * @fileoverview SIP Digest Authentication
  933. */
  934. /**
  935. * SIP Digest Authentication.
  936. * @augments SIP.
  937. * @function Digest Authentication
  938. * @param {SIP.UA} ua
  939. */
  940. module.exports = function (Utils) {
  941. var DigestAuthentication;
  942. DigestAuthentication = function(ua) {
  943. this.logger = ua.getLogger('sipjs.digestauthentication');
  944. this.username = ua.configuration.authorizationUser;
  945. this.password = ua.configuration.password;
  946. this.cnonce = null;
  947. this.nc = 0;
  948. this.ncHex = '00000000';
  949. this.response = null;
  950. };
  951. /**
  952. * Performs Digest authentication given a SIP request and the challenge
  953. * received in a response to that request.
  954. * Returns true if credentials were successfully generated, false otherwise.
  955. *
  956. * @param {SIP.OutgoingRequest} request
  957. * @param {Object} challenge
  958. */
  959. DigestAuthentication.prototype.authenticate = function(request, challenge) {
  960. // Inspect and validate the challenge.
  961. this.algorithm = challenge.algorithm;
  962. this.realm = challenge.realm;
  963. this.nonce = challenge.nonce;
  964. this.opaque = challenge.opaque;
  965. this.stale = challenge.stale;
  966. if (this.algorithm) {
  967. if (this.algorithm !== 'MD5') {
  968. this.logger.warn('challenge with Digest algorithm different than "MD5", authentication aborted');
  969. return false;
  970. }
  971. } else {
  972. this.algorithm = 'MD5';
  973. }
  974. if (! this.realm) {
  975. this.logger.warn('challenge without Digest realm, authentication aborted');
  976. return false;
  977. }
  978. if (! this.nonce) {
  979. this.logger.warn('challenge without Digest nonce, authentication aborted');
  980. return false;
  981. }
  982. // 'qop' can contain a list of values (Array). Let's choose just one.
  983. if (challenge.qop) {
  984. if (challenge.qop.indexOf('auth') > -1) {
  985. this.qop = 'auth';
  986. } else if (challenge.qop.indexOf('auth-int') > -1) {
  987. this.qop = 'auth-int';
  988. } else {
  989. // Otherwise 'qop' is present but does not contain 'auth' or 'auth-int', so abort here.
  990. this.logger.warn('challenge without Digest qop different than "auth" or "auth-int", authentication aborted');
  991. return false;
  992. }
  993. } else {
  994. this.qop = null;
  995. }
  996. // Fill other attributes.
  997. this.method = request.method;
  998. this.uri = request.ruri;
  999. this.cnonce = Utils.createRandomToken(12);
  1000. this.nc += 1;
  1001. this.updateNcHex();
  1002. // nc-value = 8LHEX. Max value = 'FFFFFFFF'.
  1003. if (this.nc === 4294967296) {
  1004. this.nc = 1;
  1005. this.ncHex = '00000001';
  1006. }
  1007. // Calculate the Digest "response" value.
  1008. this.calculateResponse();
  1009. return true;
  1010. };
  1011. /**
  1012. * Generate Digest 'response' value.
  1013. * @private
  1014. */
  1015. DigestAuthentication.prototype.calculateResponse = function() {
  1016. var ha1, ha2;
  1017. // HA1 = MD5(A1) = MD5(username:realm:password)
  1018. ha1 = Utils.calculateMD5(this.username + ":" + this.realm + ":" + this.password);
  1019. if (this.qop === 'auth') {
  1020. // HA2 = MD5(A2) = MD5(method:digestURI)
  1021. ha2 = Utils.calculateMD5(this.method + ":" + this.uri);
  1022. // response = MD5(HA1:nonce:nonceCount:credentialsNonce:qop:HA2)
  1023. this.response = Utils.calculateMD5(ha1 + ":" + this.nonce + ":" + this.ncHex + ":" + this.cnonce + ":auth:" + ha2);
  1024. } else if (this.qop === 'auth-int') {
  1025. // HA2 = MD5(A2) = MD5(method:digestURI:MD5(entityBody))
  1026. ha2 = Utils.calculateMD5(this.method + ":" + this.uri + ":" + Utils.calculateMD5(this.body ? this.body : ""));
  1027. // response = MD5(HA1:nonce:nonceCount:credentialsNonce:qop:HA2)
  1028. this.response = Utils.calculateMD5(ha1 + ":" + this.nonce + ":" + this.ncHex + ":" + this.cnonce + ":auth-int:" + ha2);
  1029. } else if (this.qop === null) {
  1030. // HA2 = MD5(A2) = MD5(method:digestURI)
  1031. ha2 = Utils.calculateMD5(this.method + ":" + this.uri);
  1032. // response = MD5(HA1:nonce:HA2)
  1033. this.response = Utils.calculateMD5(ha1 + ":" + this.nonce + ":" + ha2);
  1034. }
  1035. };
  1036. /**
  1037. * Return the Proxy-Authorization or WWW-Authorization header value.
  1038. */
  1039. DigestAuthentication.prototype.toString = function() {
  1040. var auth_params = [];
  1041. if (! this.response) {
  1042. throw new Error('response field does not exist, cannot generate Authorization header');
  1043. }
  1044. auth_params.push('algorithm=' + this.algorithm);
  1045. auth_params.push('username="' + this.username + '"');
  1046. auth_params.push('realm="' + this.realm + '"');
  1047. auth_params.push('nonce="' + this.nonce + '"');
  1048. auth_params.push('uri="' + this.uri + '"');
  1049. auth_params.push('response="' + this.response + '"');
  1050. if (this.opaque) {
  1051. auth_params.push('opaque="' + this.opaque + '"');
  1052. }
  1053. if (this.qop) {
  1054. auth_params.push('qop=' + this.qop);
  1055. auth_params.push('cnonce="' + this.cnonce + '"');
  1056. auth_params.push('nc=' + this.ncHex);
  1057. }
  1058. return 'Digest ' + auth_params.join(', ');
  1059. };
  1060. /**
  1061. * Generate the 'nc' value as required by Digest in this.ncHex by reading this.nc.
  1062. * @private
  1063. */
  1064. DigestAuthentication.prototype.updateNcHex = function() {
  1065. var hex = Number(this.nc).toString(16);
  1066. this.ncHex = '00000000'.substr(0, 8-hex.length) + hex;
  1067. };
  1068. return DigestAuthentication;
  1069. };
  1070. },{}],8:[function(require,module,exports){
  1071. "use strict";
  1072. var NodeEventEmitter = require('events').EventEmitter;
  1073. module.exports = function (console) {
  1074. // Don't use `new SIP.EventEmitter()` for inheriting.
  1075. // Use Object.create(SIP.EventEmitter.prototoype);
  1076. function EventEmitter () {
  1077. NodeEventEmitter.call(this);
  1078. }
  1079. EventEmitter.prototype = Object.create(NodeEventEmitter.prototype, {
  1080. constructor: {
  1081. value: EventEmitter,
  1082. enumerable: false,
  1083. writable: true,
  1084. configurable: true
  1085. }
  1086. });
  1087. EventEmitter.prototype.off = function off (eventName, listener) {
  1088. var warning = '';
  1089. warning += 'SIP.EventEmitter#off is deprecated and may be removed in future SIP.js versions.\n';
  1090. warning += 'Please use removeListener or removeAllListeners instead.\n';
  1091. warning += 'See here for more details:\n';
  1092. warning += 'http://nodejs.org/api/events.html#events_emitter_removelistener_event_listener';
  1093. console.warn(warning);
  1094. if (arguments.length < 2) {
  1095. return this.removeAllListeners.apply(this, arguments);
  1096. } else {
  1097. return this.removeListener(eventName, listener);
  1098. }
  1099. };
  1100. return EventEmitter;
  1101. };
  1102. },{"events":1}],9:[function(require,module,exports){
  1103. "use strict";
  1104. /**
  1105. * @fileoverview Exceptions
  1106. */
  1107. /**
  1108. * SIP Exceptions.
  1109. * @augments SIP
  1110. */
  1111. module.exports = {
  1112. ConfigurationError: (function(){
  1113. var exception = function(parameter, value) {
  1114. this.code = 1;
  1115. this.name = 'CONFIGURATION_ERROR';
  1116. this.parameter = parameter;
  1117. this.value = value;
  1118. this.message = (!this.value)? 'Missing parameter: '+ this.parameter : 'Invalid value '+ JSON.stringify(this.value) +' for parameter "'+ this.parameter +'"';
  1119. };
  1120. exception.prototype = new Error();
  1121. return exception;
  1122. }()),
  1123. InvalidStateError: (function(){
  1124. var exception = function(status) {
  1125. this.code = 2;
  1126. this.name = 'INVALID_STATE_ERROR';
  1127. this.status = status;
  1128. this.message = 'Invalid status: ' + status;
  1129. };
  1130. exception.prototype = new Error();
  1131. return exception;
  1132. }()),
  1133. NotSupportedError: (function(){
  1134. var exception = function(message) {
  1135. this.code = 3;
  1136. this.name = 'NOT_SUPPORTED_ERROR';
  1137. this.message = message;
  1138. };
  1139. exception.prototype = new Error();
  1140. return exception;
  1141. }()),
  1142. GetDescriptionError: (function(){
  1143. var exception = function(message) {
  1144. this.code = 4;
  1145. this.name = 'GET_DESCRIPTION_ERROR';
  1146. this.message = message;
  1147. };
  1148. exception.prototype = new Error();
  1149. return exception;
  1150. }())
  1151. };
  1152. },{}],10:[function(require,module,exports){
  1153. "use strict";
  1154. var Grammar = require('./Grammar/dist/Grammar');
  1155. module.exports = function (SIP) {
  1156. return {
  1157. parse: function parseCustom (input, startRule) {
  1158. var options = {startRule: startRule, SIP: SIP};
  1159. try {
  1160. Grammar.parse(input, options);
  1161. } catch (e) {
  1162. options.data = -1;
  1163. }
  1164. return options.data;
  1165. }
  1166. };
  1167. };
  1168. },{"./Grammar/dist/Grammar":11}],11:[function(require,module,exports){
  1169. module.exports = (function() {
  1170. /*
  1171. * Generated by PEG.js 0.8.0.
  1172. *
  1173. * http://pegjs.majda.cz/
  1174. */
  1175. function peg$subclass(child, parent) {
  1176. function ctor() { this.constructor = child; }
  1177. ctor.prototype = parent.prototype;
  1178. child.prototype = new ctor();
  1179. }
  1180. function SyntaxError(message, expected, found, offset, line, column) {
  1181. this.message = message;
  1182. this.expected = expected;
  1183. this.found = found;
  1184. this.offset = offset;
  1185. this.line = line;
  1186. this.column = column;
  1187. this.name = "SyntaxError";
  1188. }
  1189. peg$subclass(SyntaxError, Error);
  1190. function parse(input) {
  1191. var options = arguments.length > 1 ? arguments[1] : {},
  1192. peg$FAILED = {},
  1193. peg$startRuleIndices = { Contact: 118, Name_Addr_Header: 155, Record_Route: 175, Request_Response: 81, SIP_URI: 45, Subscription_State: 185, Supported: 190, Require: 181, Via: 193, absoluteURI: 84, Call_ID: 117, Content_Disposition: 129, Content_Length: 134, Content_Type: 135, CSeq: 145, displayName: 121, Event: 148, From: 150, host: 52, Max_Forwards: 153, Min_SE: 212, Proxy_Authenticate: 156, quoted_string: 40, Refer_To: 177, Replaces: 178, Session_Expires: 209, stun_URI: 216, To: 191, turn_URI: 223, uuid: 226, WWW_Authenticate: 208, challenge: 157 },
  1194. peg$startRuleIndex = 118,
  1195. peg$consts = [
  1196. "\r\n",
  1197. { type: "literal", value: "\r\n", description: "\"\\r\\n\"" },
  1198. /^[0-9]/,
  1199. { type: "class", value: "[0-9]", description: "[0-9]" },
  1200. /^[a-zA-Z]/,
  1201. { type: "class", value: "[a-zA-Z]", description: "[a-zA-Z]" },
  1202. /^[0-9a-fA-F]/,
  1203. { type: "class", value: "[0-9a-fA-F]", description: "[0-9a-fA-F]" },
  1204. /^[\0-\xFF]/,
  1205. { type: "class", value: "[\\0-\\xFF]", description: "[\\0-\\xFF]" },
  1206. /^["]/,
  1207. { type: "class", value: "[\"]", description: "[\"]" },
  1208. " ",
  1209. { type: "literal", value: " ", description: "\" \"" },
  1210. "\t",
  1211. { type: "literal", value: "\t", description: "\"\\t\"" },
  1212. /^[a-zA-Z0-9]/,
  1213. { type: "class", value: "[a-zA-Z0-9]", description: "[a-zA-Z0-9]" },
  1214. ";",
  1215. { type: "literal", value: ";", description: "\";\"" },
  1216. "/",
  1217. { type: "literal", value: "/", description: "\"/\"" },
  1218. "?",
  1219. { type: "literal", value: "?", description: "\"?\"" },
  1220. ":",
  1221. { type: "literal", value: ":", description: "\":\"" },
  1222. "@",
  1223. { type: "literal", value: "@", description: "\"@\"" },
  1224. "&",
  1225. { type: "literal", value: "&", description: "\"&\"" },
  1226. "=",
  1227. { type: "literal", value: "=", description: "\"=\"" },
  1228. "+",
  1229. { type: "literal", value: "+", description: "\"+\"" },
  1230. "$",
  1231. { type: "literal", value: "$", description: "\"$\"" },
  1232. ",",
  1233. { type: "literal", value: ",", description: "\",\"" },
  1234. "-",
  1235. { type: "literal", value: "-", description: "\"-\"" },
  1236. "_",
  1237. { type: "literal", value: "_", description: "\"_\"" },
  1238. ".",
  1239. { type: "literal", value: ".", description: "\".\"" },
  1240. "!",
  1241. { type: "literal", value: "!", description: "\"!\"" },
  1242. "~",
  1243. { type: "literal", value: "~", description: "\"~\"" },
  1244. "*",
  1245. { type: "literal", value: "*", description: "\"*\"" },
  1246. "'",
  1247. { type: "literal", value: "'", description: "\"'\"" },
  1248. "(",
  1249. { type: "literal", value: "(", description: "\"(\"" },
  1250. ")",
  1251. { type: "literal", value: ")", description: "\")\"" },
  1252. peg$FAILED,
  1253. "%",
  1254. { type: "literal", value: "%", description: "\"%\"" },
  1255. null,
  1256. [],
  1257. function() {return " "; },
  1258. function() {return ':'; },
  1259. /^[!-~]/,
  1260. { type: "class", value: "[!-~]", description: "[!-~]" },
  1261. /^[\x80-\uFFFF]/,
  1262. { type: "class", value: "[\\x80-\\uFFFF]", description: "[\\x80-\\uFFFF]" },
  1263. /^[\x80-\xBF]/,
  1264. { type: "class", value: "[\\x80-\\xBF]", description: "[\\x80-\\xBF]" },
  1265. /^[a-f]/,
  1266. { type: "class", value: "[a-f]", description: "[a-f]" },
  1267. "`",
  1268. { type: "literal", value: "`", description: "\"`\"" },
  1269. "<",
  1270. { type: "literal", value: "<", description: "\"<\"" },
  1271. ">",
  1272. { type: "literal", value: ">", description: "\">\"" },
  1273. "\\",
  1274. { type: "literal", value: "\\", description: "\"\\\\\"" },
  1275. "[",
  1276. { type: "literal", value: "[", description: "\"[\"" },
  1277. "]",
  1278. { type: "literal", value: "]", description: "\"]\"" },
  1279. "{",
  1280. { type: "literal", value: "{", description: "\"{\"" },
  1281. "}",
  1282. { type: "literal", value: "}", description: "\"}\"" },
  1283. function() {return "*"; },
  1284. function() {return "/"; },
  1285. function() {return "="; },
  1286. function() {return "("; },
  1287. function() {return ")"; },
  1288. function() {return ">"; },
  1289. function() {return "<"; },
  1290. function() {return ","; },
  1291. function() {return ";"; },
  1292. function() {return ":"; },
  1293. function() {return "\""; },
  1294. /^[!-']/,
  1295. { type: "class", value: "[!-']", description: "[!-']" },
  1296. /^[*-[]/,
  1297. { type: "class", value: "[*-[]", description: "[*-[]" },
  1298. /^[\]-~]/,
  1299. { type: "class", value: "[\\]-~]", description: "[\\]-~]" },
  1300. function(contents) {
  1301. return contents; },
  1302. /^[#-[]/,
  1303. { type: "class", value: "[#-[]", description: "[#-[]" },
  1304. /^[\0-\t]/,
  1305. { type: "class", value: "[\\0-\\t]", description: "[\\0-\\t]" },
  1306. /^[\x0B-\f]/,
  1307. { type: "class", value: "[\\x0B-\\f]", description: "[\\x0B-\\f]" },
  1308. /^[\x0E-]/,
  1309. { type: "class", value: "[\\x0E-]", description: "[\\x0E-]" },
  1310. function() {
  1311. options.data.uri = new options.SIP.URI(options.data.scheme, options.data.user, options.data.host, options.data.port);
  1312. delete options.data.scheme;
  1313. delete options.data.user;
  1314. delete options.data.host;
  1315. delete options.data.host_type;
  1316. delete options.data.port;
  1317. },
  1318. function() {
  1319. options.data.uri = new options.SIP.URI(options.data.scheme, options.data.user, options.data.host, options.data.port, options.data.uri_params, options.data.uri_headers);
  1320. delete options.data.scheme;
  1321. delete options.data.user;
  1322. delete options.data.host;
  1323. delete options.data.host_type;
  1324. delete options.data.port;
  1325. delete options.data.uri_params;
  1326. if (options.startRule === 'SIP_URI') { options.data = options.data.uri;}
  1327. },
  1328. "sips",
  1329. { type: "literal", value: "sips", description: "\"sips\"" },
  1330. "sip",
  1331. { type: "literal", value: "sip", description: "\"sip\"" },
  1332. function(uri_scheme) {
  1333. options.data.scheme = uri_scheme; },
  1334. function() {
  1335. options.data.user = decodeURIComponent(text().slice(0, -1));},
  1336. function() {
  1337. options.data.password = text(); },
  1338. function() {
  1339. options.data.host = text();
  1340. return options.data.host; },
  1341. function() {
  1342. options.data.host_type = 'domain';
  1343. return text(); },
  1344. /^[a-zA-Z0-9_\-]/,
  1345. { type: "class", value: "[a-zA-Z0-9_\\-]", description: "[a-zA-Z0-9_\\-]" },
  1346. /^[a-zA-Z0-9\-]/,
  1347. { type: "class", value: "[a-zA-Z0-9\\-]", description: "[a-zA-Z0-9\\-]" },
  1348. function() {
  1349. options.data.host_type = 'IPv6';
  1350. return text(); },
  1351. "::",
  1352. { type: "literal", value: "::", description: "\"::\"" },
  1353. function() {
  1354. options.data.host_type = 'IPv6';
  1355. return text(); },
  1356. function() {
  1357. options.data.host_type = 'IPv4';
  1358. return text(); },
  1359. "25",
  1360. { type: "literal", value: "25", description: "\"25\"" },
  1361. /^[0-5]/,
  1362. { type: "class", value: "[0-5]", description: "[0-5]" },
  1363. "2",
  1364. { type: "literal", value: "2", description: "\"2\"" },
  1365. /^[0-4]/,
  1366. { type: "class", value: "[0-4]", description: "[0-4]" },
  1367. "1",
  1368. { type: "literal", value: "1", description: "\"1\"" },
  1369. /^[1-9]/,
  1370. { type: "class", value: "[1-9]", description: "[1-9]" },
  1371. function(port) {
  1372. port = parseInt(port.join(''));
  1373. options.data.port = port;
  1374. return port; },
  1375. "transport=",
  1376. { type: "literal", value: "transport=", description: "\"transport=\"" },
  1377. "udp",
  1378. { type: "literal", value: "udp", description: "\"udp\"" },
  1379. "tcp",
  1380. { type: "literal", value: "tcp", description: "\"tcp\"" },
  1381. "sctp",
  1382. { type: "literal", value: "sctp", description: "\"sctp\"" },
  1383. "tls",
  1384. { type: "literal", value: "tls", description: "\"tls\"" },
  1385. function(transport) {
  1386. if(!options.data.uri_params) options.data.uri_params={};
  1387. options.data.uri_params['transport'] = transport.toLowerCase(); },
  1388. "user=",
  1389. { type: "literal", value: "user=", description: "\"user=\"" },
  1390. "phone",
  1391. { type: "literal", value: "phone", description: "\"phone\"" },
  1392. "ip",
  1393. { type: "literal", value: "ip", description: "\"ip\"" },
  1394. function(user) {
  1395. if(!options.data.uri_params) options.data.uri_params={};
  1396. options.data.uri_params['user'] = user.toLowerCase(); },
  1397. "method=",
  1398. { type: "literal", value: "method=", description: "\"method=\"" },
  1399. function(method) {
  1400. if(!options.data.uri_params) options.data.uri_params={};
  1401. options.data.uri_params['method'] = method; },
  1402. "ttl=",
  1403. { type: "literal", value: "ttl=", description: "\"ttl=\"" },
  1404. function(ttl) {
  1405. if(!options.data.params) options.data.params={};
  1406. options.data.params['ttl'] = ttl; },
  1407. "maddr=",
  1408. { type: "literal", value: "maddr=", description: "\"maddr=\"" },
  1409. function(maddr) {
  1410. if(!options.data.uri_params) options.data.uri_params={};
  1411. options.data.uri_params['maddr'] = maddr; },
  1412. "lr",
  1413. { type: "literal", value: "lr", description: "\"lr\"" },
  1414. function() {
  1415. if(!options.data.uri_params) options.data.uri_params={};
  1416. options.data.uri_params['lr'] = undefined; },
  1417. function(param, value) {
  1418. if(!options.data.uri_params) options.data.uri_params = {};
  1419. if (value === null){
  1420. value = undefined;
  1421. }
  1422. else {
  1423. value = value[1];
  1424. }
  1425. options.data.uri_params[param.toLowerCase()] = value && value.toLowerCase();},
  1426. function(hname, hvalue) {
  1427. hname = hname.join('').toLowerCase();
  1428. hvalue = hvalue.join('');
  1429. if(!options.data.uri_headers) options.data.uri_headers = {};
  1430. if (!options.data.uri_headers[hname]) {
  1431. options.data.uri_headers[hname] = [hvalue];
  1432. } else {
  1433. options.data.uri_headers[hname].push(hvalue);
  1434. }},
  1435. function() {
  1436. // lots of tests fail if this isn't guarded...
  1437. if (options.startRule === 'Refer_To') {
  1438. options.data.uri = new options.SIP.URI(options.data.scheme, options.data.user, options.data.host, options.data.port, options.data.uri_params, options.data.uri_headers);
  1439. delete options.data.scheme;
  1440. delete options.data.user;
  1441. delete options.data.host;
  1442. delete options.data.host_type;
  1443. delete options.data.port;
  1444. delete options.data.uri_params;
  1445. }
  1446. },
  1447. "//",
  1448. { type: "literal", value: "//", description: "\"//\"" },
  1449. function() {
  1450. options.data.scheme= text(); },
  1451. { type: "literal", value: "SIP", description: "\"SIP\"" },
  1452. function() {
  1453. options.data.sip_version = text(); },
  1454. "INVITE",
  1455. { type: "literal", value: "INVITE", description: "\"INVITE\"" },
  1456. "ACK",
  1457. { type: "literal", value: "ACK", description: "\"ACK\"" },
  1458. "VXACH",
  1459. { type: "literal", value: "VXACH", description: "\"VXACH\"" },
  1460. "OPTIONS",
  1461. { type: "literal", value: "OPTIONS", description: "\"OPTIONS\"" },
  1462. "BYE",
  1463. { type: "literal", value: "BYE", description: "\"BYE\"" },
  1464. "CANCEL",
  1465. { type: "literal", value: "CANCEL", description: "\"CANCEL\"" },
  1466. "REGISTER",
  1467. { type: "literal", value: "REGISTER", description: "\"REGISTER\"" },
  1468. "SUBSCRIBE",
  1469. { type: "literal", value: "SUBSCRIBE", description: "\"SUBSCRIBE\"" },
  1470. "NOTIFY",
  1471. { type: "literal", value: "NOTIFY", description: "\"NOTIFY\"" },
  1472. "REFER",
  1473. { type: "literal", value: "REFER", description: "\"REFER\"" },
  1474. function() {
  1475. options.data.method = text();
  1476. return options.data.method; },
  1477. function(status_code) {
  1478. options.data.status_code = parseInt(status_code.join('')); },
  1479. function() {
  1480. options.data.reason_phrase = text(); },
  1481. function() {
  1482. options.data = text(); },
  1483. function() {
  1484. var idx, length;
  1485. length = options.data.multi_header.length;
  1486. for (idx = 0; idx < length; idx++) {
  1487. if (options.data.multi_header[idx].parsed === null) {
  1488. options.data = null;
  1489. break;
  1490. }
  1491. }
  1492. if (options.data !== null) {
  1493. options.data = options.data.multi_header;
  1494. } else {
  1495. options.data = -1;
  1496. }},
  1497. function() {
  1498. var header;
  1499. if(!options.data.multi_header) options.data.multi_header = [];
  1500. try {
  1501. header = new options.SIP.NameAddrHeader(options.data.uri, options.data.displayName, options.data.params);
  1502. delete options.data.uri;
  1503. delete options.data.displayName;
  1504. delete options.data.params;
  1505. } catch(e) {
  1506. header = null;
  1507. }
  1508. options.data.multi_header.push( { 'position': peg$currPos,
  1509. 'offset': offset(),
  1510. 'parsed': header
  1511. });},
  1512. function(displayName) {
  1513. displayName = text().trim();
  1514. if (displayName[0] === '\"') {
  1515. displayName = displayName.substring(1, displayName.length-1);
  1516. }
  1517. options.data.displayName = displayName; },
  1518. "q",
  1519. { type: "literal", value: "q", description: "\"q\"" },
  1520. function(q) {
  1521. if(!options.data.params) options.data.params = {};
  1522. options.data.params['q'] = q; },
  1523. "expires",
  1524. { type: "literal", value: "expires", description: "\"expires\"" },
  1525. function(expires) {
  1526. if(!options.data.params) options.data.params = {};
  1527. options.data.params['expires'] = expires; },
  1528. function(delta_seconds) {
  1529. return parseInt(delta_seconds.join('')); },
  1530. "0",
  1531. { type: "literal", value: "0", description: "\"0\"" },
  1532. function() {
  1533. return parseFloat(text()); },
  1534. function(param, value) {
  1535. if(!options.data.params) options.data.params = {};
  1536. if (value === null){
  1537. value = undefined;
  1538. }
  1539. else {
  1540. value = value[1];
  1541. }
  1542. options.data.params[param.toLowerCase()] = value;},
  1543. "render",
  1544. { type: "literal", value: "render", description: "\"render\"" },
  1545. "session",
  1546. { type: "literal", value: "session", description: "\"session\"" },
  1547. "icon",
  1548. { type: "literal", value: "icon", description: "\"icon\"" },
  1549. "alert",
  1550. { type: "literal", value: "alert", description: "\"alert\"" },
  1551. function() {
  1552. if (options.startRule === 'Content_Disposition') {
  1553. options.data.type = text().toLowerCase();
  1554. }
  1555. },
  1556. "handling",
  1557. { type: "literal", value: "handling", description: "\"handling\"" },
  1558. "optional",
  1559. { type: "literal", value: "optional", description: "\"optional\"" },
  1560. "required",
  1561. { type: "literal", value: "required", description: "\"required\"" },
  1562. function(length) {
  1563. options.data = parseInt(length.join('')); },
  1564. function() {
  1565. options.data = text(); },
  1566. "text",
  1567. { type: "literal", value: "text", description: "\"text\"" },
  1568. "image",
  1569. { type: "literal", value: "image", description: "\"image\"" },
  1570. "audio",
  1571. { type: "literal", value: "audio", description: "\"audio\"" },
  1572. "video",
  1573. { type: "literal", value: "video", description: "\"video\"" },
  1574. "application",
  1575. { type: "literal", value: "application", description: "\"application\"" },
  1576. "message",
  1577. { type: "literal", value: "message", description: "\"message\"" },
  1578. "multipart",
  1579. { type: "literal", value: "multipart", description: "\"multipart\"" },
  1580. "x-",
  1581. { type: "literal", value: "x-", description: "\"x-\"" },
  1582. function(cseq_value) {
  1583. options.data.value=parseInt(cseq_value.join('')); },
  1584. function(expires) {options.data = expires; },
  1585. function(event_type) {
  1586. options.data.event = event_type.toLowerCase(); },
  1587. function() {
  1588. var tag = options.data.tag;
  1589. options.data = new options.SIP.NameAddrHeader(options.data.uri, options.data.displayName, options.data.params);
  1590. if (tag) {options.data.setParam('tag',tag)}
  1591. },
  1592. "tag",
  1593. { type: "literal", value: "tag", description: "\"tag\"" },
  1594. function(tag) {options.data.tag = tag; },
  1595. function(forwards) {
  1596. options.data = parseInt(forwards.join('')); },
  1597. function(min_expires) {options.data = min_expires; },
  1598. function() {
  1599. options.data = new options.SIP.NameAddrHeader(options.data.uri, options.data.displayName, options.data.params);
  1600. },
  1601. "digest",
  1602. { type: "literal", value: "Digest", description: "\"Digest\"" },
  1603. "realm",
  1604. { type: "literal", value: "realm", description: "\"realm\"" },
  1605. function(realm) { options.data.realm = realm; },
  1606. "domain",
  1607. { type: "literal", value: "domain", description: "\"domain\"" },
  1608. "nonce",
  1609. { type: "literal", value: "nonce", description: "\"nonce\"" },
  1610. function(nonce) { options.data.nonce=nonce; },
  1611. "opaque",
  1612. { type: "literal", value: "opaque", description: "\"opaque\"" },
  1613. function(opaque) { options.data.opaque=opaque; },
  1614. "stale",
  1615. { type: "literal", value: "stale", description: "\"stale\"" },
  1616. "true",
  1617. { type: "literal", value: "true", description: "\"true\"" },
  1618. function() { options.data.stale=true; },
  1619. "false",
  1620. { type: "literal", value: "false", description: "\"false\"" },
  1621. function() { options.data.stale=false; },
  1622. "algorithm",
  1623. { type: "literal", value: "algorithm", description: "\"algorithm\"" },
  1624. "md5",
  1625. { type: "literal", value: "MD5", description: "\"MD5\"" },
  1626. "md5-sess",
  1627. { type: "literal", value: "MD5-sess", description: "\"MD5-sess\"" },
  1628. function(algorithm) {
  1629. options.data.algorithm=algorithm.toUpperCase(); },
  1630. "qop",
  1631. { type: "literal", value: "qop", description: "\"qop\"" },
  1632. "auth-int",
  1633. { type: "literal", value: "auth-int", description: "\"auth-int\"" },
  1634. "auth",
  1635. { type: "literal", value: "auth", description: "\"auth\"" },
  1636. function(qop_value) {
  1637. options.data.qop || (options.data.qop=[]);
  1638. options.data.qop.push(qop_value.toLowerCase()); },
  1639. function(rack_value) {
  1640. options.data.value=parseInt(rack_value.join('')); },
  1641. function() {
  1642. var idx, length;
  1643. length = options.data.multi_header.length;
  1644. for (idx = 0; idx < length; idx++) {
  1645. if (options.data.multi_header[idx].parsed === null) {
  1646. options.data = null;
  1647. break;
  1648. }
  1649. }
  1650. if (options.data !== null) {
  1651. options.data = options.data.multi_header;
  1652. } else {
  1653. options.data = -1;
  1654. }},
  1655. function() {
  1656. var header;
  1657. if(!options.data.multi_header) options.data.multi_header = [];
  1658. try {
  1659. header = new options.SIP.NameAddrHeader(options.data.uri, options.data.displayName, options.data.params);
  1660. delete options.data.uri;
  1661. delete options.data.displayName;
  1662. delete options.data.params;
  1663. } catch(e) {
  1664. header = null;
  1665. }
  1666. options.data.multi_header.push( { 'position': peg$currPos,
  1667. 'offset': offset(),
  1668. 'parsed': header
  1669. });},
  1670. function() {
  1671. options.data = new options.SIP.NameAddrHeader(options.data.uri, options.data.displayName, options.data.params);
  1672. },
  1673. function() {
  1674. if (!(options.data.replaces_from_tag && options.data.replaces_to_tag)) {
  1675. options.data = -1;
  1676. }
  1677. },
  1678. function() {
  1679. options.data = {
  1680. call_id: options.data
  1681. };
  1682. },
  1683. "from-tag",
  1684. { type: "literal", value: "from-tag", description: "\"from-tag\"" },
  1685. function(from_tag) {
  1686. options.data.replaces_from_tag = from_tag;
  1687. },
  1688. "to-tag",
  1689. { type: "literal", value: "to-tag", description: "\"to-tag\"" },
  1690. function(to_tag) {
  1691. options.data.replaces_to_tag = to_tag;
  1692. },
  1693. "early-only",
  1694. { type: "literal", value: "early-only", description: "\"early-only\"" },
  1695. function() {
  1696. options.data.early_only = true;
  1697. },
  1698. function(r) {return r;},
  1699. function(first, rest) { return list(first, rest); },
  1700. function(value) {
  1701. if (options.startRule === 'Require') {
  1702. options.data = value || [];
  1703. }
  1704. },
  1705. function(rseq_value) {
  1706. options.data.value=parseInt(rseq_value.join('')); },
  1707. "active",
  1708. { type: "literal", value: "active", description: "\"active\"" },
  1709. "pending",
  1710. { type: "literal", value: "pending", description: "\"pending\"" },
  1711. "terminated",
  1712. { type: "literal", value: "terminated", description: "\"terminated\"" },
  1713. function() {
  1714. options.data.state = text(); },
  1715. "reason",
  1716. { type: "literal", value: "reason", description: "\"reason\"" },
  1717. function(reason) {
  1718. if (typeof reason !== 'undefined') options.data.reason = reason; },
  1719. function(expires) {
  1720. if (typeof expires !== 'undefined') options.data.expires = expires; },
  1721. "retry_after",
  1722. { type: "literal", value: "retry_after", description: "\"retry_after\"" },
  1723. function(retry_after) {
  1724. if (typeof retry_after !== 'undefined') options.data.retry_after = retry_after; },
  1725. "deactivated",
  1726. { type: "literal", value: "deactivated", description: "\"deactivated\"" },
  1727. "probation",
  1728. { type: "literal", value: "probation", description: "\"probation\"" },
  1729. "rejected",
  1730. { type: "literal", value: "rejected", description: "\"rejected\"" },
  1731. "timeout",
  1732. { type: "literal", value: "timeout", description: "\"timeout\"" },
  1733. "giveup",
  1734. { type: "literal", value: "giveup", description: "\"giveup\"" },
  1735. "noresource",
  1736. { type: "literal", value: "noresource", description: "\"noresource\"" },
  1737. "invariant",
  1738. { type: "literal", value: "invariant", description: "\"invariant\"" },
  1739. function(value) {
  1740. if (options.startRule === 'Supported') {
  1741. options.data = value || [];
  1742. }
  1743. },
  1744. function() {
  1745. var tag = options.data.tag;
  1746. options.data = new options.SIP.NameAddrHeader(options.data.uri, options.data.displayName, options.data.params);
  1747. if (tag) {options.data.setParam('tag',tag)}
  1748. },
  1749. "ttl",
  1750. { type: "literal", value: "ttl", description: "\"ttl\"" },
  1751. function(via_ttl_value) {
  1752. options.data.ttl = via_ttl_value; },
  1753. "maddr",
  1754. { type: "literal", value: "maddr", description: "\"maddr\"" },
  1755. function(via_maddr) {
  1756. options.data.maddr = via_maddr; },
  1757. "received",
  1758. { type: "literal", value: "received", description: "\"received\"" },
  1759. function(via_received) {
  1760. options.data.received = via_received; },
  1761. "branch",
  1762. { type: "literal", value: "branch", description: "\"branch\"" },
  1763. function(via_branch) {
  1764. options.data.branch = via_branch; },
  1765. "rport",
  1766. { type: "literal", value: "rport", description: "\"rport\"" },
  1767. function() {
  1768. if(typeof response_port !== 'undefined')
  1769. options.data.rport = response_port.join(''); },
  1770. function(via_protocol) {
  1771. options.data.protocol = via_protocol; },
  1772. { type: "literal", value: "UDP", description: "\"UDP\"" },
  1773. { type: "literal", value: "TCP", description: "\"TCP\"" },
  1774. { type: "literal", value: "TLS", description: "\"TLS\"" },
  1775. { type: "literal", value: "SCTP", description: "\"SCTP\"" },
  1776. function(via_transport) {
  1777. options.data.transport = via_transport; },
  1778. function() {
  1779. options.data.host = text(); },
  1780. function(via_sent_by_port) {
  1781. options.data.port = parseInt(via_sent_by_port.join('')); },
  1782. function(ttl) {
  1783. return parseInt(ttl.join('')); },
  1784. function(deltaSeconds) {
  1785. if (options.startRule === 'Session_Expires') {
  1786. options.data.deltaSeconds = deltaSeconds;
  1787. }
  1788. },
  1789. "refresher",
  1790. { type: "literal", value: "refresher", description: "\"refresher\"" },
  1791. "uas",
  1792. { type: "literal", value: "uas", description: "\"uas\"" },
  1793. "uac",
  1794. { type: "literal", value: "uac", description: "\"uac\"" },
  1795. function(endpoint) {
  1796. if (options.startRule === 'Session_Expires') {
  1797. options.data.refresher = endpoint;
  1798. }
  1799. },
  1800. function(deltaSeconds) {
  1801. if (options.startRule === 'Min_SE') {
  1802. options.data = deltaSeconds;
  1803. }
  1804. },
  1805. "stuns",
  1806. { type: "literal", value: "stuns", description: "\"stuns\"" },
  1807. "stun",
  1808. { type: "literal", value: "stun", description: "\"stun\"" },
  1809. function(scheme) {
  1810. options.data.scheme = scheme; },
  1811. function(host) {
  1812. options.data.host = host; },
  1813. "?transport=",
  1814. { type: "literal", value: "?transport=", description: "\"?transport=\"" },
  1815. "turns",
  1816. { type: "literal", value: "turns", description: "\"turns\"" },
  1817. "turn",
  1818. { type: "literal", value: "turn", description: "\"turn\"" },
  1819. function() {
  1820. options.data.transport = transport; },
  1821. function() {
  1822. options.data = text(); }
  1823. ],
  1824. peg$bytecode = [
  1825. peg$decode(". \"\"2 3!"),
  1826. peg$decode("0\"\"\"1!3#"),
  1827. peg$decode("0$\"\"1!3%"),
  1828. peg$decode("0&\"\"1!3'"),
  1829. peg$decode("7'*# \"7("),
  1830. peg$decode("0(\"\"1!3)"),
  1831. peg$decode("0*\"\"1!3+"),
  1832. peg$decode(".,\"\"2,3-"),
  1833. peg$decode("..\"\"2.3/"),
  1834. peg$decode("00\"\"1!31"),
  1835. peg$decode(".2\"\"2233*\x89 \".4\"\"2435*} \".6\"\"2637*q \".8\"\"2839*e \".:\"\"2:3;*Y \".<\"\"2<3=*M \".>\"\"2>3?*A \".@\"\"2@3A*5 \".B\"\"2B3C*) \".D\"\"2D3E"),
  1836. peg$decode("7)*# \"7,"),
  1837. peg$decode(".F\"\"2F3G*} \".H\"\"2H3I*q \".J\"\"2J3K*e \".L\"\"2L3M*Y \".N\"\"2N3O*M \".P\"\"2P3Q*A \".R\"\"2R3S*5 \".T\"\"2T3U*) \".V\"\"2V3W"),
  1838. peg$decode("!!.Y\"\"2Y3Z+7$7#+-%7#+#%'#%$## X$\"# X\"# X+! (%"),
  1839. peg$decode("!! \\7$,#&7$\"+-$7 +#%'\"%$\"# X\"# X*# \" [+@$ \\7$+&$,#&7$\"\"\" X+'%4\"6]\" %$\"# X\"# X"),
  1840. peg$decode("7.*# \" ["),
  1841. peg$decode("! \\7'*# \"7(,)&7'*# \"7(\"+A$.8\"\"2839+1%7/+'%4#6^# %$## X$\"# X\"# X"),
  1842. peg$decode("!! \\72+&$,#&72\"\"\" X+o$ \\! \\7.,#&7.\"+-$72+#%'\"%$\"# X\"# X,@&! \\7.,#&7.\"+-$72+#%'\"%$\"# X\"# X\"+#%'\"%$\"# X\"# X+! (%"),
  1843. peg$decode("0_\"\"1!3`*# \"73"),
  1844. peg$decode("0a\"\"1!3b"),
  1845. peg$decode("0c\"\"1!3d"),
  1846. peg$decode("7!*) \"0e\"\"1!3f"),
  1847. peg$decode("! \\7)*\x95 \".F\"\"2F3G*\x89 \".J\"\"2J3K*} \".L\"\"2L3M*q \".Y\"\"2Y3Z*e \".P\"\"2P3Q*Y \".H\"\"2H3I*M \".@\"\"2@3A*A \".g\"\"2g3h*5 \".R\"\"2R3S*) \".N\"\"2N3O+\x9E$,\x9B&7)*\x95 \".F\"\"2F3G*\x89 \".J\"\"2J3K*} \".L\"\"2L3M*q \".Y\"\"2Y3Z*e \".P\"\"2P3Q*Y \".H\"\"2H3I*M \".@\"\"2@3A*A \".g\"\"2g3h*5 \".R\"\"2R3S*) \".N\"\"2N3O\"\"\" X+! (%"),
  1848. peg$decode("! \\7)*\x89 \".F\"\"2F3G*} \".L\"\"2L3M*q \".Y\"\"2Y3Z*e \".P\"\"2P3Q*Y \".H\"\"2H3I*M \".@\"\"2@3A*A \".g\"\"2g3h*5 \".R\"\"2R3S*) \".N\"\"2N3O+\x92$,\x8F&7)*\x89 \".F\"\"2F3G*} \".L\"\"2L3M*q \".Y\"\"2Y3Z*e \".P\"\"2P3Q*Y \".H\"\"2H3I*M \".@\"\"2@3A*A \".g\"\"2g3h*5 \".R\"\"2R3S*) \".N\"\"2N3O\"\"\" X+! (%"),
  1849. peg$decode(".T\"\"2T3U*\xE3 \".V\"\"2V3W*\xD7 \".i\"\"2i3j*\xCB \".k\"\"2k3l*\xBF \".:\"\"2:3;*\xB3 \".D\"\"2D3E*\xA7 \".2\"\"2233*\x9B \".8\"\"2839*\x8F \".m\"\"2m3n*\x83 \"7&*} \".4\"\"2435*q \".o\"\"2o3p*e \".q\"\"2q3r*Y \".6\"\"2637*M \".>\"\"2>3?*A \".s\"\"2s3t*5 \".u\"\"2u3v*) \"7'*# \"7("),
  1850. peg$decode("! \\7)*\u012B \".F\"\"2F3G*\u011F \".J\"\"2J3K*\u0113 \".L\"\"2L3M*\u0107 \".Y\"\"2Y3Z*\xFB \".P\"\"2P3Q*\xEF \".H\"\"2H3I*\xE3 \".@\"\"2@3A*\xD7 \".g\"\"2g3h*\xCB \".R\"\"2R3S*\xBF \".N\"\"2N3O*\xB3 \".T\"\"2T3U*\xA7 \".V\"\"2V3W*\x9B \".i\"\"2i3j*\x8F \".k\"\"2k3l*\x83 \".8\"\"2839*w \".m\"\"2m3n*k \"7&*e \".4\"\"2435*Y \".o\"\"2o3p*M \".q\"\"2q3r*A \".6\"\"2637*5 \".s\"\"2s3t*) \".u\"\"2u3v+\u0134$,\u0131&7)*\u012B \".F\"\"2F3G*\u011F \".J\"\"2J3K*\u0113 \".L\"\"2L3M*\u0107 \".Y\"\"2Y3Z*\xFB \".P\"\"2P3Q*\xEF \".H\"\"2H3I*\xE3 \".@\"\"2@3A*\xD7 \".g\"\"2g3h*\xCB \".R\"\"2R3S*\xBF \".N\"\"2N3O*\xB3 \".T\"\"2T3U*\xA7 \".V\"\"2V3W*\x9B \".i\"\"2i3j*\x8F \".k\"\"2k3l*\x83 \".8\"\"2839*w \".m\"\"2m3n*k \"7&*e \".4\"\"2435*Y \".o\"\"2o3p*M \".q\"\"2q3r*A \".6\"\"2637*5 \".s\"\"2s3t*) \".u\"\"2u3v\"\"\" X+! (%"),
  1851. peg$decode("!7/+A$.P\"\"2P3Q+1%7/+'%4#6w# %$## X$\"# X\"# X"),
  1852. peg$decode("!7/+A$.4\"\"2435+1%7/+'%4#6x# %$## X$\"# X\"# X"),
  1853. peg$decode("!7/+A$.>\"\"2>3?+1%7/+'%4#6y# %$## X$\"# X\"# X"),
  1854. peg$decode("!7/+A$.T\"\"2T3U+1%7/+'%4#6z# %$## X$\"# X\"# X"),
  1855. peg$decode("!7/+A$.V\"\"2V3W+1%7/+'%4#6{# %$## X$\"# X\"# X"),
  1856. peg$decode("!.k\"\"2k3l+1$7/+'%4\"6|\" %$\"# X\"# X"),
  1857. peg$decode("!7/+7$.i\"\"2i3j+'%4\"6}\" %$\"# X\"# X"),
  1858. peg$decode("!7/+A$.D\"\"2D3E+1%7/+'%4#6~# %$## X$\"# X\"# X"),
  1859. peg$decode("!7/+A$.2\"\"2233+1%7/+'%4#6# %$## X$\"# X\"# X"),
  1860. peg$decode("!7/+A$.8\"\"2839+1%7/+'%4#6\x80# %$## X$\"# X\"# X"),
  1861. peg$decode("!7/+1$7&+'%4\"6\x81\" %$\"# X\"# X"),
  1862. peg$decode("!7&+1$7/+'%4\"6\x81\" %$\"# X\"# X"),
  1863. peg$decode("!7=+W$ \\7G*) \"7K*# \"7F,/&7G*) \"7K*# \"7F\"+-%7>+#%'#%$## X$\"# X\"# X"),
  1864. peg$decode("0\x82\"\"1!3\x83*A \"0\x84\"\"1!3\x85*5 \"0\x86\"\"1!3\x87*) \"73*# \"7."),
  1865. peg$decode("!!7/+U$7&+K% \\7J*# \"7K,)&7J*# \"7K\"+-%7&+#%'$%$$# X$## X$\"# X\"# X+! (%"),
  1866. peg$decode("!7/+`$7&+V%! \\7J*# \"7K,)&7J*# \"7K\"+! (%+2%7&+(%4$6\x88$!!%$$# X$## X$\"# X\"# X"),
  1867. peg$decode("7.*G \".L\"\"2L3M*; \"0\x89\"\"1!3\x8A*/ \"0\x86\"\"1!3\x87*# \"73"),
  1868. peg$decode("!.m\"\"2m3n+K$0\x8B\"\"1!3\x8C*5 \"0\x8D\"\"1!3\x8E*) \"0\x8F\"\"1!3\x90+#%'\"%$\"# X\"# X"),
  1869. peg$decode("!7N+Q$.8\"\"2839+A%7O*# \" [+1%7S+'%4$6\x91$ %$$# X$## X$\"# X\"# X"),
  1870. peg$decode("!7N+k$.8\"\"2839+[%7O*# \" [+K%7S+A%7_+7%7l*# \" [+'%4&6\x92& %$&# X$%# X$$# X$## X$\"# X\"# X"),
  1871. peg$decode("!/\x93\"\"1$3\x94*) \"/\x95\"\"1#3\x96+' 4!6\x97!! %"),
  1872. peg$decode("!7P+b$!.8\"\"2839+-$7R+#%'\"%$\"# X\"# X*# \" [+7%.:\"\"2:3;+'%4#6\x98# %$## X$\"# X\"# X"),
  1873. peg$decode(" \\7+*) \"7-*# \"7Q+2$,/&7+*) \"7-*# \"7Q\"\"\" X"),
  1874. peg$decode(".<\"\"2<3=*q \".>\"\"2>3?*e \".@\"\"2@3A*Y \".B\"\"2B3C*M \".D\"\"2D3E*A \".2\"\"2233*5 \".6\"\"2637*) \".4\"\"2435"),
  1875. peg$decode("! \\7+*_ \"7-*Y \".<\"\"2<3=*M \".>\"\"2>3?*A \".@\"\"2@3A*5 \".B\"\"2B3C*) \".D\"\"2D3E,e&7+*_ \"7-*Y \".<\"\"2<3=*M \".>\"\"2>3?*A \".@\"\"2@3A*5 \".B\"\"2B3C*) \".D\"\"2D3E\"+& 4!6\x99! %"),
  1876. peg$decode("!7T+N$!.8\"\"2839+-$7^+#%'\"%$\"# X\"# X*# \" [+#%'\"%$\"# X\"# X"),
  1877. peg$decode("!7U*) \"7\\*# \"7X+& 4!6\x9A! %"),
  1878. peg$decode("! \\!7V+3$.J\"\"2J3K+#%'\"%$\"# X\"# X,>&!7V+3$.J\"\"2J3K+#%'\"%$\"# X\"# X\"+G$7W+=%.J\"\"2J3K*# \" [+'%4#6\x9B# %$## X$\"# X\"# X"),
  1879. peg$decode(" \\0\x9C\"\"1!3\x9D+,$,)&0\x9C\"\"1!3\x9D\"\"\" X"),
  1880. peg$decode("!0$\"\"1!3%+A$ \\0\x9E\"\"1!3\x9F,)&0\x9E\"\"1!3\x9F\"+#%'\"%$\"# X\"# X"),
  1881. peg$decode("!.o\"\"2o3p+A$7Y+7%.q\"\"2q3r+'%4#6\xA0# %$## X$\"# X\"# X"),
  1882. peg$decode("!!7Z+\xBF$.8\"\"2839+\xAF%7Z+\xA5%.8\"\"2839+\x95%7Z+\x8B%.8\"\"2839+{%7Z+q%.8\"\"2839+a%7Z+W%.8\"\"2839+G%7Z+=%.8\"\"2839+-%7[+#%'-%$-# X$,# X$+# X$*# X$)# X$(# X$'# X$&# X$%# X$$# X$## X$\"# X\"# X*\u0838 \"!.\xA1\"\"2\xA13\xA2+\xAF$7Z+\xA5%.8\"\"2839+\x95%7Z+\x8B%.8\"\"2839+{%7Z+q%.8\"\"2839+a%7Z+W%.8\"\"2839+G%7Z+=%.8\"\"2839+-%7[+#%',%$,# X$+# X$*# X$)# X$(# X$'# X$&# X$%# X$$# X$## X$\"# X\"# X*\u0795 \"!.\xA1\"\"2\xA13\xA2+\x95$7Z+\x8B%.8\"\"2839+{%7Z+q%.8\"\"2839+a%7Z+W%.8\"\"2839+G%7Z+=%.8\"\"2839+-%7[+#%'*%$*# X$)# X$(# X$'# X$&# X$%# X$$# X$## X$\"# X\"# X*\u070C \"!.\xA1\"\"2\xA13\xA2+{$7Z+q%.8\"\"2839+a%7Z+W%.8\"\"2839+G%7Z+=%.8\"\"2839+-%7[+#%'(%$(# X$'# X$&# X$%# X$$# X$## X$\"# X\"# X*\u069D \"!.\xA1\"\"2\xA13\xA2+a$7Z+W%.8\"\"2839+G%7Z+=%.8\"\"2839+-%7[+#%'&%$&# X$%# X$$# X$## X$\"# X\"# X*\u0648 \"!.\xA1\"\"2\xA13\xA2+G$7Z+=%.8\"\"2839+-%7[+#%'$%$$# X$## X$\"# X\"# X*\u060D \"!.\xA1\"\"2\xA13\xA2+-$7[+#%'\"%$\"# X\"# X*\u05EC \"!.\xA1\"\"2\xA13\xA2+-$7Z+#%'\"%$\"# X\"# X*\u05CB \"!7Z+\xA5$.\xA1\"\"2\xA13\xA2+\x95%7Z+\x8B%.8\"\"2839+{%7Z+q%.8\"\"2839+a%7Z+W%.8\"\"2839+G%7Z+=%.8\"\"2839+-%7[+#%'+%$+# X$*# X$)# X$(# X$'# X$&# X$%# X$$# X$## X$\"# X\"# X*\u0538 \"!7Z+\xB6$!.8\"\"2839+-$7Z+#%'\"%$\"# X\"# X*# \" [+\x8B%.\xA1\"\"2\xA13\xA2+{%7Z+q%.8\"\"2839+a%7Z+W%.8\"\"2839+G%7Z+=%.8\"\"2839+-%7[+#%'*%$*# X$)# X$(# X$'# X$&# X$%# X$$# X$## X$\"# X\"# X*\u0494 \"!7Z+\xC7$!.8\"\"2839+-$7Z+#%'\"%$\"# X\"# X*# \" [+\x9C%!.8\"\"2839+-$7Z+#%'\"%$\"# X\"# X*# \" [+q%.\xA1\"\"2\xA13\xA2+a%7Z+W%.8\"\"2839+G%7Z+=%.8\"\"2839+-%7[+#%')%$)# X$(# X$'# X$&# X$%# X$$# X$## X$\"# X\"# X*\u03DF \"!7Z+\xD8$!.8\"\"2839+-$7Z+#%'\"%$\"# X\"# X*# \" [+\xAD%!.8\"\"2839+-$7Z+#%'\"%$\"# X\"# X*# \" [+\x82%!.8\"\"2839+-$7Z+#%'\"%$\"# X\"# X*# \" [+W%.\xA1\"\"2\xA13\xA2+G%7Z+=%.8\"\"2839+-%7[+#%'(%$(# X$'# X$&# X$%# X$$# X$## X$\"# X\"# X*\u0319 \"!7Z+\xE9$!.8\"\"2839+-$7Z+#%'\"%$\"# X\"# X*# \" [+\xBE%!.8\"\"2839+-$7Z+#%'\"%$\"# X\"# X*# \" [+\x93%!.8\"\"2839+-$7Z+#%'\"%$\"# X\"# X*# \" [+h%!.8\"\"2839+-$7Z+#%'\"%$\"# X\"# X*# \" [+=%.\xA1\"\"2\xA13\xA2+-%7[+#%''%$'# X$&# X$%# X$$# X$## X$\"# X\"# X*\u0242 \"!7Z+\u0114$!.8\"\"2839+-$7Z+#%'\"%$\"# X\"# X*# \" [+\xE9%!.8\"\"2839+-$7Z+#%'\"%$\"# X\"# X*# \" [+\xBE%!.8\"\"2839+-$7Z+#%'\"%$\"# X\"# X*# \" [+\x93%!.8\"\"2839+-$7Z+#%'\"%$\"# X\"# X*# \" [+h%!.8\"\"2839+-$7Z+#%'\"%$\"# X\"# X*# \" [+=%.\xA1\"\"2\xA13\xA2+-%7Z+#%'(%$(# X$'# X$&# X$%# X$$# X$## X$\"# X\"# X*\u0140 \"!7Z+\u0135$!.8\"\"2839+-$7Z+#%'\"%$\"# X\"# X*# \" [+\u010A%!.8\"\"2839+-$7Z+#%'\"%$\"# X\"# X*# \" [+\xDF%!.8\"\"2839+-$7Z+#%'\"%$\"# X\"# X*# \" [+\xB4%!.8\"\"2839+-$7Z+#%'\"%$\"# X\"# X*# \" [+\x89%!.8\"\"2839+-$7Z+#%'\"%$\"# X\"# X*# \" [+^%!.8\"\"2839+-$7Z+#%'\"%$\"# X\"# X*# \" [+3%.\xA1\"\"2\xA13\xA2+#%'(%$(# X$'# X$&# X$%# X$$# X$## X$\"# X\"# X+& 4!6\xA3! %"),
  1883. peg$decode("!7#+S$7#*# \" [+C%7#*# \" [+3%7#*# \" [+#%'$%$$# X$## X$\"# X\"# X"),
  1884. peg$decode("!7Z+=$.8\"\"2839+-%7Z+#%'#%$## X$\"# X\"# X*# \"7\\"),
  1885. peg$decode("!7]+u$.J\"\"2J3K+e%7]+[%.J\"\"2J3K+K%7]+A%.J\"\"2J3K+1%7]+'%4'6\xA4' %$'# X$&# X$%# X$$# X$## X$\"# X\"# X"),
  1886. peg$decode("!.\xA5\"\"2\xA53\xA6+3$0\xA7\"\"1!3\xA8+#%'\"%$\"# X\"# X*\xA0 \"!.\xA9\"\"2\xA93\xAA+=$0\xAB\"\"1!3\xAC+-%7!+#%'#%$## X$\"# X\"# X*o \"!.\xAD\"\"2\xAD3\xAE+7$7!+-%7!+#%'#%$## X$\"# X\"# X*D \"!0\xAF\"\"1!3\xB0+-$7!+#%'\"%$\"# X\"# X*# \"7!"),
  1887. peg$decode("!!7!*# \" [+c$7!*# \" [+S%7!*# \" [+C%7!*# \" [+3%7!*# \" [+#%'%%$%# X$$# X$## X$\"# X\"# X+' 4!6\xB1!! %"),
  1888. peg$decode(" \\!.2\"\"2233+-$7`+#%'\"%$\"# X\"# X,>&!.2\"\"2233+-$7`+#%'\"%$\"# X\"# X\""),
  1889. peg$decode("7a*A \"7b*; \"7c*5 \"7d*/ \"7e*) \"7f*# \"7g"),
  1890. peg$decode("!/\xB2\"\"1*3\xB3+b$/\xB4\"\"1#3\xB5*G \"/\xB6\"\"1#3\xB7*; \"/\xB8\"\"1$3\xB9*/ \"/\xBA\"\"1#3\xBB*# \"76+(%4\"6\xBC\"! %$\"# X\"# X"),
  1891. peg$decode("!/\xBD\"\"1%3\xBE+J$/\xBF\"\"1%3\xC0*/ \"/\xC1\"\"1\"3\xC2*# \"76+(%4\"6\xC3\"! %$\"# X\"# X"),
  1892. peg$decode("!/\xC4\"\"1'3\xC5+2$7\x8F+(%4\"6\xC6\"! %$\"# X\"# X"),
  1893. peg$decode("!/\xC7\"\"1$3\xC8+2$7\xEF+(%4\"6\xC9\"! %$\"# X\"# X"),
  1894. peg$decode("!/\xCA\"\"1&3\xCB+2$7T+(%4\"6\xCC\"! %$\"# X\"# X"),
  1895. peg$decode("!/\xCD\"\"1\"3\xCE+R$!.>\"\"2>3?+-$76+#%'\"%$\"# X\"# X*# \" [+'%4\"6\xCF\" %$\"# X\"# X"),
  1896. peg$decode("!7h+T$!.>\"\"2>3?+-$7i+#%'\"%$\"# X\"# X*# \" [+)%4\"6\xD0\"\"! %$\"# X\"# X"),
  1897. peg$decode("! \\7j+&$,#&7j\"\"\" X+! (%"),
  1898. peg$decode("! \\7j+&$,#&7j\"\"\" X+! (%"),
  1899. peg$decode("7k*) \"7+*# \"7-"),
  1900. peg$decode(".o\"\"2o3p*e \".q\"\"2q3r*Y \".4\"\"2435*M \".8\"\"2839*A \".<\"\"2<3=*5 \".@\"\"2@3A*) \".B\"\"2B3C"),
  1901. peg$decode("!.6\"\"2637+u$7m+k% \\!.<\"\"2<3=+-$7m+#%'\"%$\"# X\"# X,>&!.<\"\"2<3=+-$7m+#%'\"%$\"# X\"# X\"+#%'#%$## X$\"# X\"# X"),
  1902. peg$decode("!7n+C$.>\"\"2>3?+3%7o+)%4#6\xD1#\"\" %$## X$\"# X\"# X"),
  1903. peg$decode(" \\7p*) \"7+*# \"7-+2$,/&7p*) \"7+*# \"7-\"\"\" X"),
  1904. peg$decode(" \\7p*) \"7+*# \"7-,/&7p*) \"7+*# \"7-\""),
  1905. peg$decode(".o\"\"2o3p*e \".q\"\"2q3r*Y \".4\"\"2435*M \".6\"\"2637*A \".8\"\"2839*5 \".@\"\"2@3A*) \".B\"\"2B3C"),
  1906. peg$decode("7\x90*# \"7r"),
  1907. peg$decode("!7\x8F+K$7'+A%7s+7%7'+-%7\x84+#%'%%$%# X$$# X$## X$\"# X\"# X"),
  1908. peg$decode("7M*# \"7t"),
  1909. peg$decode("!7+G$.8\"\"2839+7%7u*# \"7x+'%4#6\xD2# %$## X$\"# X\"# X"),
  1910. peg$decode("!7v*# \"7w+N$!.6\"\"2637+-$7\x83+#%'\"%$\"# X\"# X*# \" [+#%'\"%$\"# X\"# X"),
  1911. peg$decode("!.\xD3\"\"2\xD33\xD4+=$7\x80+3%7w*# \" [+#%'#%$## X$\"# X\"# X"),
  1912. peg$decode("!.4\"\"2435+-$7{+#%'\"%$\"# X\"# X"),
  1913. peg$decode("!7z+5$ \\7y,#&7y\"+#%'\"%$\"# X\"# X"),
  1914. peg$decode("7**) \"7+*# \"7-"),
  1915. peg$decode("7+*\x8F \"7-*\x89 \".2\"\"2233*} \".6\"\"2637*q \".8\"\"2839*e \".:\"\"2:3;*Y \".<\"\"2<3=*M \".>\"\"2>3?*A \".@\"\"2@3A*5 \".B\"\"2B3C*) \".D\"\"2D3E"),
  1916. peg$decode("!7|+k$ \\!.4\"\"2435+-$7|+#%'\"%$\"# X\"# X,>&!.4\"\"2435+-$7|+#%'\"%$\"# X\"# X\"+#%'\"%$\"# X\"# X"),
  1917. peg$decode("! \\7~,#&7~\"+k$ \\!.2\"\"2233+-$7}+#%'\"%$\"# X\"# X,>&!.2\"\"2233+-$7}+#%'\"%$\"# X\"# X\"+#%'\"%$\"# X\"# X"),
  1918. peg$decode(" \\7~,#&7~\""),
  1919. peg$decode("7+*w \"7-*q \".8\"\"2839*e \".:\"\"2:3;*Y \".<\"\"2<3=*M \".>\"\"2>3?*A \".@\"\"2@3A*5 \".B\"\"2B3C*) \".D\"\"2D3E"),
  1920. peg$decode("!7\"+\x8D$ \\7\"*G \"7!*A \".@\"\"2@3A*5 \".F\"\"2F3G*) \".J\"\"2J3K,M&7\"*G \"7!*A \".@\"\"2@3A*5 \".F\"\"2F3G*) \".J\"\"2J3K\"+'%4\"6\xD5\" %$\"# X\"# X"),
  1921. peg$decode("7\x81*# \"7\x82"),
  1922. peg$decode("!!7O+3$.:\"\"2:3;+#%'\"%$\"# X\"# X*# \" [+-$7S+#%'\"%$\"# X\"# X*# \" ["),
  1923. peg$decode(" \\7+*\x83 \"7-*} \".B\"\"2B3C*q \".D\"\"2D3E*e \".2\"\"2233*Y \".8\"\"2839*M \".:\"\"2:3;*A \".<\"\"2<3=*5 \".>\"\"2>3?*) \".@\"\"2@3A+\x8C$,\x89&7+*\x83 \"7-*} \".B\"\"2B3C*q \".D\"\"2D3E*e \".2\"\"2233*Y \".8\"\"2839*M \".:\"\"2:3;*A \".<\"\"2<3=*5 \".>\"\"2>3?*) \".@\"\"2@3A\"\"\" X"),
  1924. peg$decode(" \\7y,#&7y\""),
  1925. peg$decode("!/\x95\"\"1#3\xD6+y$.4\"\"2435+i% \\7!+&$,#&7!\"\"\" X+P%.J\"\"2J3K+@% \\7!+&$,#&7!\"\"\" X+'%4%6\xD7% %$%# X$$# X$## X$\"# X\"# X"),
  1926. peg$decode(".\xD8\"\"2\xD83\xD9"),
  1927. peg$decode(".\xDA\"\"2\xDA3\xDB"),
  1928. peg$decode(".\xDC\"\"2\xDC3\xDD"),
  1929. peg$decode(".\xDE\"\"2\xDE3\xDF"),
  1930. peg$decode(".\xE0\"\"2\xE03\xE1"),
  1931. peg$decode(".\xE2\"\"2\xE23\xE3"),
  1932. peg$decode(".\xE4\"\"2\xE43\xE5"),
  1933. peg$decode(".\xE6\"\"2\xE63\xE7"),
  1934. peg$decode(".\xE8\"\"2\xE83\xE9"),
  1935. peg$decode(".\xEA\"\"2\xEA3\xEB"),
  1936. peg$decode("!7\x85*S \"7\x86*M \"7\x88*G \"7\x89*A \"7\x8A*; \"7\x8B*5 \"7\x8C*/ \"7\x8D*) \"7\x8E*# \"76+& 4!6\xEC! %"),
  1937. peg$decode("!7\x84+K$7'+A%7\x91+7%7'+-%7\x93+#%'%%$%# X$$# X$## X$\"# X\"# X"),
  1938. peg$decode("!7\x92+' 4!6\xED!! %"),
  1939. peg$decode("!7!+7$7!+-%7!+#%'#%$## X$\"# X\"# X"),
  1940. peg$decode("! \\7**A \"7+*; \"7-*5 \"73*/ \"74*) \"7'*# \"7(,G&7**A \"7+*; \"7-*5 \"73*/ \"74*) \"7'*# \"7(\"+& 4!6\xEE! %"),
  1941. peg$decode("!7\xB5+_$ \\!7A+-$7\xB5+#%'\"%$\"# X\"# X,8&!7A+-$7\xB5+#%'\"%$\"# X\"# X\"+#%'\"%$\"# X\"# X"),
  1942. peg$decode("!79+R$!.:\"\"2:3;+-$79+#%'\"%$\"# X\"# X*# \" [+'%4\"6\xEF\" %$\"# X\"# X"),
  1943. peg$decode("!7:*j \"!7\x97+_$ \\!7A+-$7\x97+#%'\"%$\"# X\"# X,8&!7A+-$7\x97+#%'\"%$\"# X\"# X\"+#%'\"%$\"# X\"# X+& 4!6\xF0! %"),
  1944. peg$decode("!7L*# \"7\x98+c$ \\!7B+-$7\x9A+#%'\"%$\"# X\"# X,8&!7B+-$7\x9A+#%'\"%$\"# X\"# X\"+'%4\"6\xF1\" %$\"# X\"# X"),
  1945. peg$decode("!7\x99*# \" [+A$7@+7%7M+-%7?+#%'$%$$# X$## X$\"# X\"# X"),
  1946. peg$decode("!!76+_$ \\!7.+-$76+#%'\"%$\"# X\"# X,8&!7.+-$76+#%'\"%$\"# X\"# X\"+#%'\"%$\"# X\"# X*# \"7H+' 4!6\xF2!! %"),
  1947. peg$decode("7\x9B*) \"7\x9C*# \"7\x9F"),
  1948. peg$decode("!/\xF3\"\"1!3\xF4+<$7<+2%7\x9E+(%4#6\xF5#! %$## X$\"# X\"# X"),
  1949. peg$decode("!/\xF6\"\"1'3\xF7+<$7<+2%7\x9D+(%4#6\xF8#! %$## X$\"# X\"# X"),
  1950. peg$decode("! \\7!+&$,#&7!\"\"\" X+' 4!6\xF9!! %"),
  1951. peg$decode("!.\xFA\"\"2\xFA3\xFB+x$!.J\"\"2J3K+S$7!*# \" [+C%7!*# \" [+3%7!*# \" [+#%'$%$$# X$## X$\"# X\"# X*# \" [+'%4\"6\xFC\" %$\"# X\"# X"),
  1952. peg$decode("!76+N$!7<+-$7\xA0+#%'\"%$\"# X\"# X*# \" [+)%4\"6\xFD\"\"! %$\"# X\"# X"),
  1953. peg$decode("76*) \"7T*# \"7H"),
  1954. peg$decode("!7\xA2+_$ \\!7B+-$7\xA3+#%'\"%$\"# X\"# X,8&!7B+-$7\xA3+#%'\"%$\"# X\"# X\"+#%'\"%$\"# X\"# X"),
  1955. peg$decode("!/\xFE\"\"1&3\xFF*G \"/\u0100\"\"1'3\u0101*; \"/\u0102\"\"1$3\u0103*/ \"/\u0104\"\"1%3\u0105*# \"76+& 4!6\u0106! %"),
  1956. peg$decode("7\xA4*# \"7\x9F"),
  1957. peg$decode("!/\u0107\"\"1(3\u0108+O$7<+E%/\u0109\"\"1(3\u010A*/ \"/\u010B\"\"1(3\u010C*# \"76+#%'#%$## X$\"# X\"# X"),
  1958. peg$decode("!76+_$ \\!7A+-$76+#%'\"%$\"# X\"# X,8&!7A+-$76+#%'\"%$\"# X\"# X\"+#%'\"%$\"# X\"# X"),
  1959. peg$decode("! \\7!+&$,#&7!\"\"\" X+' 4!6\u010D!! %"),
  1960. peg$decode("!7\xA8+& 4!6\u010E! %"),
  1961. peg$decode("!7\xA9+s$7;+i%7\xAE+_% \\!7B+-$7\xAF+#%'\"%$\"# X\"# X,8&!7B+-$7\xAF+#%'\"%$\"# X\"# X\"+#%'$%$$# X$## X$\"# X\"# X"),
  1962. peg$decode("7\xAA*# \"7\xAB"),
  1963. peg$decode("/\u010F\"\"1$3\u0110*S \"/\u0111\"\"1%3\u0112*G \"/\u0113\"\"1%3\u0114*; \"/\u0115\"\"1%3\u0116*/ \"/\u0117\"\"1+3\u0118*# \"7\xAC"),
  1964. peg$decode("/\u0119\"\"1'3\u011A*/ \"/\u011B\"\"1)3\u011C*# \"7\xAC"),
  1965. peg$decode("76*# \"7\xAD"),
  1966. peg$decode("!/\u011D\"\"1\"3\u011E+-$76+#%'\"%$\"# X\"# X"),
  1967. peg$decode("7\xAC*# \"76"),
  1968. peg$decode("!76+7$7<+-%7\xB0+#%'#%$## X$\"# X\"# X"),
  1969. peg$decode("76*# \"7H"),
  1970. peg$decode("!7\xB2+7$7.+-%7\x8F+#%'#%$## X$\"# X\"# X"),
  1971. peg$decode("! \\7!+&$,#&7!\"\"\" X+' 4!6\u011F!! %"),
  1972. peg$decode("!7\x9D+' 4!6\u0120!! %"),
  1973. peg$decode("!7\xB5+d$ \\!7B+-$7\x9F+#%'\"%$\"# X\"# X,8&!7B+-$7\x9F+#%'\"%$\"# X\"# X\"+(%4\"6\u0121\"!!%$\"# X\"# X"),
  1974. peg$decode("!!77+k$ \\!.J\"\"2J3K+-$77+#%'\"%$\"# X\"# X,>&!.J\"\"2J3K+-$77+#%'\"%$\"# X\"# X\"+#%'\"%$\"# X\"# X+! (%"),
  1975. peg$decode("!7L*# \"7\x98+c$ \\!7B+-$7\xB7+#%'\"%$\"# X\"# X,8&!7B+-$7\xB7+#%'\"%$\"# X\"# X\"+'%4\"6\u0122\" %$\"# X\"# X"),
  1976. peg$decode("7\xB8*# \"7\x9F"),
  1977. peg$decode("!/\u0123\"\"1#3\u0124+<$7<+2%76+(%4#6\u0125#! %$## X$\"# X\"# X"),
  1978. peg$decode("! \\7!+&$,#&7!\"\"\" X+' 4!6\u0126!! %"),
  1979. peg$decode("!7\x9D+' 4!6\u0127!! %"),
  1980. peg$decode("! \\7\x99,#&7\x99\"+\x81$7@+w%7M+m%7?+c% \\!7B+-$7\x9F+#%'\"%$\"# X\"# X,8&!7B+-$7\x9F+#%'\"%$\"# X\"# X\"+'%4%6\u0128% %$%# X$$# X$## X$\"# X\"# X"),
  1981. peg$decode("7\xBD"),
  1982. peg$decode("!/\u0129\"\"1&3\u012A+s$7.+i%7\xC0+_% \\!7A+-$7\xC0+#%'\"%$\"# X\"# X,8&!7A+-$7\xC0+#%'\"%$\"# X\"# X\"+#%'$%$$# X$## X$\"# X\"# X*# \"7\xBE"),
  1983. peg$decode("!76+s$7.+i%7\xBF+_% \\!7A+-$7\xBF+#%'\"%$\"# X\"# X,8&!7A+-$7\xBF+#%'\"%$\"# X\"# X\"+#%'$%$$# X$## X$\"# X\"# X"),
  1984. peg$decode("!76+=$7<+3%76*# \"7H+#%'#%$## X$\"# X\"# X"),
  1985. peg$decode("7\xC1*G \"7\xC3*A \"7\xC5*; \"7\xC7*5 \"7\xC8*/ \"7\xC9*) \"7\xCA*# \"7\xBF"),
  1986. peg$decode("!/\u012B\"\"1%3\u012C+7$7<+-%7\xC2+#%'#%$## X$\"# X\"# X"),
  1987. peg$decode("!7I+' 4!6\u012D!! %"),
  1988. peg$decode("!/\u012E\"\"1&3\u012F+\xA5$7<+\x9B%7D+\x91%7\xC4+\x87% \\! \\7'+&$,#&7'\"\"\" X+-$7\xC4+#%'\"%$\"# X\"# X,G&! \\7'+&$,#&7'\"\"\" X+-$7\xC4+#%'\"%$\"# X\"# X\"+-%7E+#%'&%$&# X$%# X$$# X$## X$\"# X\"# X"),
  1989. peg$decode("7t*# \"7w"),
  1990. peg$decode("!/\u0130\"\"1%3\u0131+7$7<+-%7\xC6+#%'#%$## X$\"# X\"# X"),
  1991. peg$decode("!7I+' 4!6\u0132!! %"),
  1992. peg$decode("!/\u0133\"\"1&3\u0134+<$7<+2%7I+(%4#6\u0135#! %$## X$\"# X\"# X"),
  1993. peg$decode("!/\u0136\"\"1%3\u0137+_$7<+U%!/\u0138\"\"1$3\u0139+& 4!6\u013A! %*4 \"!/\u013B\"\"1%3\u013C+& 4!6\u013D! %+#%'#%$## X$\"# X\"# X"),
  1994. peg$decode("!/\u013E\"\"1)3\u013F+T$7<+J%/\u0140\"\"1#3\u0141*/ \"/\u0142\"\"1(3\u0143*# \"76+(%4#6\u0144#! %$## X$\"# X\"# X"),
  1995. peg$decode("!/\u0145\"\"1#3\u0146+\x9E$7<+\x94%7D+\x8A%!7\xCB+k$ \\!.D\"\"2D3E+-$7\xCB+#%'\"%$\"# X\"# X,>&!.D\"\"2D3E+-$7\xCB+#%'\"%$\"# X\"# X\"+#%'\"%$\"# X\"# X+-%7E+#%'%%$%# X$$# X$## X$\"# X\"# X"),
  1996. peg$decode("!/\u0147\"\"1(3\u0148*/ \"/\u0149\"\"1$3\u014A*# \"76+' 4!6\u014B!! %"),
  1997. peg$decode("!76+_$ \\!7A+-$76+#%'\"%$\"# X\"# X,8&!7A+-$76+#%'\"%$\"# X\"# X\"+#%'\"%$\"# X\"# X"),
  1998. peg$decode("!7\xCE+K$7.+A%7\xCE+7%7.+-%7\x8F+#%'%%$%# X$$# X$## X$\"# X\"# X"),
  1999. peg$decode("! \\7!+&$,#&7!\"\"\" X+' 4!6\u014C!! %"),
  2000. peg$decode("!7\xD0+c$ \\!7A+-$7\xD0+#%'\"%$\"# X\"# X,8&!7A+-$7\xD0+#%'\"%$\"# X\"# X\"+'%4\"6\u014D\" %$\"# X\"# X"),
  2001. peg$decode("!7\x98+c$ \\!7B+-$7\x9F+#%'\"%$\"# X\"# X,8&!7B+-$7\x9F+#%'\"%$\"# X\"# X\"+'%4\"6\u014E\" %$\"# X\"# X"),
  2002. peg$decode("!7L*T \"7\x98*N \"!7@*# \" [+=$7t+3%7?*# \" [+#%'#%$## X$\"# X\"# X+c$ \\!7B+-$7\x9F+#%'\"%$\"# X\"# X,8&!7B+-$7\x9F+#%'\"%$\"# X\"# X\"+'%4\"6\u014F\" %$\"# X\"# X"),
  2003. peg$decode("!7\xD3+c$ \\!7B+-$7\xD4+#%'\"%$\"# X\"# X,8&!7B+-$7\xD4+#%'\"%$\"# X\"# X\"+'%4\"6\u0150\" %$\"# X\"# X"),
  2004. peg$decode("!7\x95+& 4!6\u0151! %"),
  2005. peg$decode("!/\u0152\"\"1(3\u0153+<$7<+2%76+(%4#6\u0154#! %$## X$\"# X\"# X*j \"!/\u0155\"\"1&3\u0156+<$7<+2%76+(%4#6\u0157#! %$## X$\"# X\"# X*: \"!/\u0158\"\"1*3\u0159+& 4!6\u015A! %*# \"7\x9F"),
  2006. peg$decode("!!76+o$ \\!7A+2$76+(%4\"6\u015B\"! %$\"# X\"# X,=&!7A+2$76+(%4\"6\u015B\"! %$\"# X\"# X\"+)%4\"6\u015C\"\"! %$\"# X\"# X*# \" [+' 4!6\u015D!! %"),
  2007. peg$decode("!7\xD7+_$ \\!7A+-$7\xD7+#%'\"%$\"# X\"# X,8&!7A+-$7\xD7+#%'\"%$\"# X\"# X\"+#%'\"%$\"# X\"# X"),
  2008. peg$decode("!7\x98+_$ \\!7B+-$7\x9F+#%'\"%$\"# X\"# X,8&!7B+-$7\x9F+#%'\"%$\"# X\"# X\"+#%'\"%$\"# X\"# X"),
  2009. peg$decode("! \\7!+&$,#&7!\"\"\" X+' 4!6\u015E!! %"),
  2010. peg$decode("!7\xDA+_$ \\!7B+-$7\xDB+#%'\"%$\"# X\"# X,8&!7B+-$7\xDB+#%'\"%$\"# X\"# X\"+#%'\"%$\"# X\"# X"),
  2011. peg$decode("!/\u015F\"\"1&3\u0160*; \"/\u0161\"\"1'3\u0162*/ \"/\u0163\"\"1*3\u0164*# \"76+& 4!6\u0165! %"),
  2012. peg$decode("!/\u0166\"\"1&3\u0167+<$7<+2%7\xDC+(%4#6\u0168#! %$## X$\"# X\"# X*\x83 \"!/\xF6\"\"1'3\xF7+<$7<+2%7\x9D+(%4#6\u0169#! %$## X$\"# X\"# X*S \"!/\u016A\"\"1+3\u016B+<$7<+2%7\x9D+(%4#6\u016C#! %$## X$\"# X\"# X*# \"7\x9F"),
  2013. peg$decode("/\u016D\"\"1+3\u016E*k \"/\u016F\"\"1)3\u0170*_ \"/\u0171\"\"1(3\u0172*S \"/\u0173\"\"1'3\u0174*G \"/\u0175\"\"1&3\u0176*; \"/\u0177\"\"1*3\u0178*/ \"/\u0179\"\"1)3\u017A*# \"76"),
  2014. peg$decode("71*# \" ["),
  2015. peg$decode("!!76+o$ \\!7A+2$76+(%4\"6\u015B\"! %$\"# X\"# X,=&!7A+2$76+(%4\"6\u015B\"! %$\"# X\"# X\"+)%4\"6\u015C\"\"! %$\"# X\"# X*# \" [+' 4!6\u017B!! %"),
  2016. peg$decode("!7L*# \"7\x98+c$ \\!7B+-$7\xE0+#%'\"%$\"# X\"# X,8&!7B+-$7\xE0+#%'\"%$\"# X\"# X\"+'%4\"6\u017C\" %$\"# X\"# X"),
  2017. peg$decode("7\xB8*# \"7\x9F"),
  2018. peg$decode("!7\xE2+_$ \\!7A+-$7\xE2+#%'\"%$\"# X\"# X,8&!7A+-$7\xE2+#%'\"%$\"# X\"# X\"+#%'\"%$\"# X\"# X"),
  2019. peg$decode("!7\xE9+s$7.+i%7\xEC+_% \\!7B+-$7\xE3+#%'\"%$\"# X\"# X,8&!7B+-$7\xE3+#%'\"%$\"# X\"# X\"+#%'$%$$# X$## X$\"# X\"# X"),
  2020. peg$decode("7\xE4*; \"7\xE5*5 \"7\xE6*/ \"7\xE7*) \"7\xE8*# \"7\x9F"),
  2021. peg$decode("!/\u017D\"\"1#3\u017E+<$7<+2%7\xEF+(%4#6\u017F#! %$## X$\"# X\"# X"),
  2022. peg$decode("!/\u0180\"\"1%3\u0181+<$7<+2%7T+(%4#6\u0182#! %$## X$\"# X\"# X"),
  2023. peg$decode("!/\u0183\"\"1(3\u0184+B$7<+8%7\\*# \"7Y+(%4#6\u0185#! %$## X$\"# X\"# X"),
  2024. peg$decode("!/\u0186\"\"1&3\u0187+<$7<+2%76+(%4#6\u0188#! %$## X$\"# X\"# X"),
  2025. peg$decode("!/\u0189\"\"1%3\u018A+T$!7<+5$ \\7!,#&7!\"+#%'\"%$\"# X\"# X*# \" [+'%4\"6\u018B\" %$\"# X\"# X"),
  2026. peg$decode("!7\xEA+K$7;+A%76+7%7;+-%7\xEB+#%'%%$%# X$$# X$## X$\"# X\"# X"),
  2027. peg$decode("!/\x95\"\"1#3\xD6*# \"76+' 4!6\u018C!! %"),
  2028. peg$decode("!/\xB4\"\"1#3\u018D*G \"/\xB6\"\"1#3\u018E*; \"/\xBA\"\"1#3\u018F*/ \"/\xB8\"\"1$3\u0190*# \"76+' 4!6\u0191!! %"),
  2029. peg$decode("!7\xED+H$!7C+-$7\xEE+#%'\"%$\"# X\"# X*# \" [+#%'\"%$\"# X\"# X"),
  2030. peg$decode("!7U*) \"7\\*# \"7X+& 4!6\u0192! %"),
  2031. peg$decode("!!7!*# \" [+c$7!*# \" [+S%7!*# \" [+C%7!*# \" [+3%7!*# \" [+#%'%%$%# X$$# X$## X$\"# X\"# X+' 4!6\u0193!! %"),
  2032. peg$decode("!!7!+C$7!*# \" [+3%7!*# \" [+#%'#%$## X$\"# X\"# X+' 4!6\u0194!! %"),
  2033. peg$decode("7\xBD"),
  2034. peg$decode("!7\x9D+d$ \\!7B+-$7\xF2+#%'\"%$\"# X\"# X,8&!7B+-$7\xF2+#%'\"%$\"# X\"# X\"+(%4\"6\u0195\"!!%$\"# X\"# X"),
  2035. peg$decode("7\xF3*# \"7\x9F"),
  2036. peg$decode("!.\u0196\"\"2\u01963\u0197+N$7<+D%.\u0198\"\"2\u01983\u0199*) \".\u019A\"\"2\u019A3\u019B+(%4#6\u019C#! %$## X$\"# X\"# X"),
  2037. peg$decode("!7\x9D+d$ \\!7B+-$7\x9F+#%'\"%$\"# X\"# X,8&!7B+-$7\x9F+#%'\"%$\"# X\"# X\"+(%4\"6\u019D\"!!%$\"# X\"# X"),
  2038. peg$decode("!76+7$70+-%7\xF6+#%'#%$## X$\"# X\"# X"),
  2039. peg$decode(" \\72*) \"74*# \"7.,/&72*) \"74*# \"7.\""),
  2040. peg$decode(" \\7%,#&7%\""),
  2041. peg$decode("!7\xF9+=$.8\"\"2839+-%7\xFA+#%'#%$## X$\"# X\"# X"),
  2042. peg$decode("!/\u019E\"\"1%3\u019F*) \"/\u01A0\"\"1$3\u01A1+' 4!6\u01A2!! %"),
  2043. peg$decode("!7\xFB+N$!.8\"\"2839+-$7^+#%'\"%$\"# X\"# X*# \" [+#%'\"%$\"# X\"# X"),
  2044. peg$decode("!7\\*) \"7X*# \"7\x82+' 4!6\u01A3!! %"),
  2045. peg$decode("! \\7\xFD*) \"7-*# \"7\xFE,/&7\xFD*) \"7-*# \"7\xFE\"+! (%"),
  2046. peg$decode("7\"*S \"7!*M \".F\"\"2F3G*A \".J\"\"2J3K*5 \".H\"\"2H3I*) \".N\"\"2N3O"),
  2047. peg$decode(".L\"\"2L3M*\x95 \".B\"\"2B3C*\x89 \".<\"\"2<3=*} \".R\"\"2R3S*q \".T\"\"2T3U*e \".V\"\"2V3W*Y \".P\"\"2P3Q*M \".@\"\"2@3A*A \".D\"\"2D3E*5 \".2\"\"2233*) \".>\"\"2>3?"),
  2048. peg$decode("!7\u0100+h$.8\"\"2839+X%7\xFA+N%!.\u01A4\"\"2\u01A43\u01A5+-$7\xEB+#%'\"%$\"# X\"# X*# \" [+#%'$%$$# X$## X$\"# X\"# X"),
  2049. peg$decode("!/\u01A6\"\"1%3\u01A7*) \"/\u01A8\"\"1$3\u01A9+' 4!6\u01A2!! %"),
  2050. peg$decode("!7\xEB+Q$/\xB4\"\"1#3\xB5*7 \"/\xB6\"\"1#3\xB7*+ \" \\7+,#&7+\"+'%4\"6\u01AA\" %$\"# X\"# X"),
  2051. peg$decode("!7\u0104+\x8F$.F\"\"2F3G+%7\u0103+u%.F\"\"2F3G+e%7\u0103+[%.F\"\"2F3G+K%7\u0103+A%.F\"\"2F3G+1%7\u0105+'%4)6\u01AB) %$)# X$(# X$'# X$&# X$%# X$$# X$## X$\"# X\"# X"),
  2052. peg$decode("!7#+A$7#+7%7#+-%7#+#%'$%$$# X$## X$\"# X\"# X"),
  2053. peg$decode("!7\u0103+-$7\u0103+#%'\"%$\"# X\"# X"),
  2054. peg$decode("!7\u0103+7$7\u0103+-%7\u0103+#%'#%$## X$\"# X\"# X")
  2055. ],
  2056. peg$currPos = 0,
  2057. peg$reportedPos = 0,
  2058. peg$cachedPos = 0,
  2059. peg$cachedPosDetails = { line: 1, column: 1, seenCR: false },
  2060. peg$maxFailPos = 0,
  2061. peg$maxFailExpected = [],
  2062. peg$silentFails = 0,
  2063. peg$result;
  2064. if ("startRule" in options) {
  2065. if (!(options.startRule in peg$startRuleIndices)) {
  2066. throw new Error("Can't start parsing from rule \"" + options.startRule + "\".");
  2067. }
  2068. peg$startRuleIndex = peg$startRuleIndices[options.startRule];
  2069. }
  2070. function text() {
  2071. return input.substring(peg$reportedPos, peg$currPos);
  2072. }
  2073. function offset() {
  2074. return peg$reportedPos;
  2075. }
  2076. function line() {
  2077. return peg$computePosDetails(peg$reportedPos).line;
  2078. }
  2079. function column() {
  2080. return peg$computePosDetails(peg$reportedPos).column;
  2081. }
  2082. function expected(description) {
  2083. throw peg$buildException(
  2084. null,
  2085. [{ type: "other", description: description }],
  2086. peg$reportedPos
  2087. );
  2088. }
  2089. function error(message) {
  2090. throw peg$buildException(message, null, peg$reportedPos);
  2091. }
  2092. function peg$computePosDetails(pos) {
  2093. function advance(details, startPos, endPos) {
  2094. var p, ch;
  2095. for (p = startPos; p < endPos; p++) {
  2096. ch = input.charAt(p);
  2097. if (ch === "\n") {
  2098. if (!details.seenCR) { details.line++; }
  2099. details.column = 1;
  2100. details.seenCR = false;
  2101. } else if (ch === "\r" || ch === "\u2028" || ch === "\u2029") {
  2102. details.line++;
  2103. details.column = 1;
  2104. details.seenCR = true;
  2105. } else {
  2106. details.column++;
  2107. details.seenCR = false;
  2108. }
  2109. }
  2110. }
  2111. if (peg$cachedPos !== pos) {
  2112. if (peg$cachedPos > pos) {
  2113. peg$cachedPos = 0;
  2114. peg$cachedPosDetails = { line: 1, column: 1, seenCR: false };
  2115. }
  2116. advance(peg$cachedPosDetails, peg$cachedPos, pos);
  2117. peg$cachedPos = pos;
  2118. }
  2119. return peg$cachedPosDetails;
  2120. }
  2121. function peg$fail(expected) {
  2122. if (peg$currPos < peg$maxFailPos) { return; }
  2123. if (peg$currPos > peg$maxFailPos) {
  2124. peg$maxFailPos = peg$currPos;
  2125. peg$maxFailExpected = [];
  2126. }
  2127. peg$maxFailExpected.push(expected);
  2128. }
  2129. function peg$buildException(message, expected, pos) {
  2130. function cleanupExpected(expected) {
  2131. var i = 1;
  2132. expected.sort(function(a, b) {
  2133. if (a.description < b.description) {
  2134. return -1;
  2135. } else if (a.description > b.description) {
  2136. return 1;
  2137. } else {
  2138. return 0;
  2139. }
  2140. });
  2141. while (i < expected.length) {
  2142. if (expected[i - 1] === expected[i]) {
  2143. expected.splice(i, 1);
  2144. } else {
  2145. i++;
  2146. }
  2147. }
  2148. }
  2149. function buildMessage(expected, found) {
  2150. function stringEscape(s) {
  2151. function hex(ch) { return ch.charCodeAt(0).toString(16).toUpperCase(); }
  2152. return s
  2153. .replace(/\\/g, '\\\\')
  2154. .replace(/"/g, '\\"')
  2155. .replace(/\x08/g, '\\b')
  2156. .replace(/\t/g, '\\t')
  2157. .replace(/\n/g, '\\n')
  2158. .replace(/\f/g, '\\f')
  2159. .replace(/\r/g, '\\r')
  2160. .replace(/[\x00-\x07\x0B\x0E\x0F]/g, function(ch) { return '\\x0' + hex(ch); })
  2161. .replace(/[\x10-\x1F\x80-\xFF]/g, function(ch) { return '\\x' + hex(ch); })
  2162. .replace(/[\u0180-\u0FFF]/g, function(ch) { return '\\u0' + hex(ch); })
  2163. .replace(/[\u1080-\uFFFF]/g, function(ch) { return '\\u' + hex(ch); });
  2164. }
  2165. var expectedDescs = new Array(expected.length),
  2166. expectedDesc, foundDesc, i;
  2167. for (i = 0; i < expected.length; i++) {
  2168. expectedDescs[i] = expected[i].description;
  2169. }
  2170. expectedDesc = expected.length > 1
  2171. ? expectedDescs.slice(0, -1).join(", ")
  2172. + " or "
  2173. + expectedDescs[expected.length - 1]
  2174. : expectedDescs[0];
  2175. foundDesc = found ? "\"" + stringEscape(found) + "\"" : "end of input";
  2176. return "Expected " + expectedDesc + " but " + foundDesc + " found.";
  2177. }
  2178. var posDetails = peg$computePosDetails(pos),
  2179. found = pos < input.length ? input.charAt(pos) : null;
  2180. if (expected !== null) {
  2181. cleanupExpected(expected);
  2182. }
  2183. return new SyntaxError(
  2184. message !== null ? message : buildMessage(expected, found),
  2185. expected,
  2186. found,
  2187. pos,
  2188. posDetails.line,
  2189. posDetails.column
  2190. );
  2191. }
  2192. function peg$decode(s) {
  2193. var bc = new Array(s.length), i;
  2194. for (i = 0; i < s.length; i++) {
  2195. bc[i] = s.charCodeAt(i) - 32;
  2196. }
  2197. return bc;
  2198. }
  2199. function peg$parseRule(index) {
  2200. var bc = peg$bytecode[index],
  2201. ip = 0,
  2202. ips = [],
  2203. end = bc.length,
  2204. ends = [],
  2205. stack = [],
  2206. params, i;
  2207. function protect(object) {
  2208. return Object.prototype.toString.apply(object) === "[object Array]" ? [] : object;
  2209. }
  2210. while (true) {
  2211. while (ip < end) {
  2212. switch (bc[ip]) {
  2213. case 0:
  2214. stack.push(protect(peg$consts[bc[ip + 1]]));
  2215. ip += 2;
  2216. break;
  2217. case 1:
  2218. stack.push(peg$currPos);
  2219. ip++;
  2220. break;
  2221. case 2:
  2222. stack.pop();
  2223. ip++;
  2224. break;
  2225. case 3:
  2226. peg$currPos = stack.pop();
  2227. ip++;
  2228. break;
  2229. case 4:
  2230. stack.length -= bc[ip + 1];
  2231. ip += 2;
  2232. break;
  2233. case 5:
  2234. stack.splice(-2, 1);
  2235. ip++;
  2236. break;
  2237. case 6:
  2238. stack[stack.length - 2].push(stack.pop());
  2239. ip++;
  2240. break;
  2241. case 7:
  2242. stack.push(stack.splice(stack.length - bc[ip + 1], bc[ip + 1]));
  2243. ip += 2;
  2244. break;
  2245. case 8:
  2246. stack.pop();
  2247. stack.push(input.substring(stack[stack.length - 1], peg$currPos));
  2248. ip++;
  2249. break;
  2250. case 9:
  2251. ends.push(end);
  2252. ips.push(ip + 3 + bc[ip + 1] + bc[ip + 2]);
  2253. if (stack[stack.length - 1]) {
  2254. end = ip + 3 + bc[ip + 1];
  2255. ip += 3;
  2256. } else {
  2257. end = ip + 3 + bc[ip + 1] + bc[ip + 2];
  2258. ip += 3 + bc[ip + 1];
  2259. }
  2260. break;
  2261. case 10:
  2262. ends.push(end);
  2263. ips.push(ip + 3 + bc[ip + 1] + bc[ip + 2]);
  2264. if (stack[stack.length - 1] === peg$FAILED) {
  2265. end = ip + 3 + bc[ip + 1];
  2266. ip += 3;
  2267. } else {
  2268. end = ip + 3 + bc[ip + 1] + bc[ip + 2];
  2269. ip += 3 + bc[ip + 1];
  2270. }
  2271. break;
  2272. case 11:
  2273. ends.push(end);
  2274. ips.push(ip + 3 + bc[ip + 1] + bc[ip + 2]);
  2275. if (stack[stack.length - 1] !== peg$FAILED) {
  2276. end = ip + 3 + bc[ip + 1];
  2277. ip += 3;
  2278. } else {
  2279. end = ip + 3 + bc[ip + 1] + bc[ip + 2];
  2280. ip += 3 + bc[ip + 1];
  2281. }
  2282. break;
  2283. case 12:
  2284. if (stack[stack.length - 1] !== peg$FAILED) {
  2285. ends.push(end);
  2286. ips.push(ip);
  2287. end = ip + 2 + bc[ip + 1];
  2288. ip += 2;
  2289. } else {
  2290. ip += 2 + bc[ip + 1];
  2291. }
  2292. break;
  2293. case 13:
  2294. ends.push(end);
  2295. ips.push(ip + 3 + bc[ip + 1] + bc[ip + 2]);
  2296. if (input.length > peg$currPos) {
  2297. end = ip + 3 + bc[ip + 1];
  2298. ip += 3;
  2299. } else {
  2300. end = ip + 3 + bc[ip + 1] + bc[ip + 2];
  2301. ip += 3 + bc[ip + 1];
  2302. }
  2303. break;
  2304. case 14:
  2305. ends.push(end);
  2306. ips.push(ip + 4 + bc[ip + 2] + bc[ip + 3]);
  2307. if (input.substr(peg$currPos, peg$consts[bc[ip + 1]].length) === peg$consts[bc[ip + 1]]) {
  2308. end = ip + 4 + bc[ip + 2];
  2309. ip += 4;
  2310. } else {
  2311. end = ip + 4 + bc[ip + 2] + bc[ip + 3];
  2312. ip += 4 + bc[ip + 2];
  2313. }
  2314. break;
  2315. case 15:
  2316. ends.push(end);
  2317. ips.push(ip + 4 + bc[ip + 2] + bc[ip + 3]);
  2318. if (input.substr(peg$currPos, peg$consts[bc[ip + 1]].length).toLowerCase() === peg$consts[bc[ip + 1]]) {
  2319. end = ip + 4 + bc[ip + 2];
  2320. ip += 4;
  2321. } else {
  2322. end = ip + 4 + bc[ip + 2] + bc[ip + 3];
  2323. ip += 4 + bc[ip + 2];
  2324. }
  2325. break;
  2326. case 16:
  2327. ends.push(end);
  2328. ips.push(ip + 4 + bc[ip + 2] + bc[ip + 3]);
  2329. if (peg$consts[bc[ip + 1]].test(input.charAt(peg$currPos))) {
  2330. end = ip + 4 + bc[ip + 2];
  2331. ip += 4;
  2332. } else {
  2333. end = ip + 4 + bc[ip + 2] + bc[ip + 3];
  2334. ip += 4 + bc[ip + 2];
  2335. }
  2336. break;
  2337. case 17:
  2338. stack.push(input.substr(peg$currPos, bc[ip + 1]));
  2339. peg$currPos += bc[ip + 1];
  2340. ip += 2;
  2341. break;
  2342. case 18:
  2343. stack.push(peg$consts[bc[ip + 1]]);
  2344. peg$currPos += peg$consts[bc[ip + 1]].length;
  2345. ip += 2;
  2346. break;
  2347. case 19:
  2348. stack.push(peg$FAILED);
  2349. if (peg$silentFails === 0) {
  2350. peg$fail(peg$consts[bc[ip + 1]]);
  2351. }
  2352. ip += 2;
  2353. break;
  2354. case 20:
  2355. peg$reportedPos = stack[stack.length - 1 - bc[ip + 1]];
  2356. ip += 2;
  2357. break;
  2358. case 21:
  2359. peg$reportedPos = peg$currPos;
  2360. ip++;
  2361. break;
  2362. case 22:
  2363. params = bc.slice(ip + 4, ip + 4 + bc[ip + 3]);
  2364. for (i = 0; i < bc[ip + 3]; i++) {
  2365. params[i] = stack[stack.length - 1 - params[i]];
  2366. }
  2367. stack.splice(
  2368. stack.length - bc[ip + 2],
  2369. bc[ip + 2],
  2370. peg$consts[bc[ip + 1]].apply(null, params)
  2371. );
  2372. ip += 4 + bc[ip + 3];
  2373. break;
  2374. case 23:
  2375. stack.push(peg$parseRule(bc[ip + 1]));
  2376. ip += 2;
  2377. break;
  2378. case 24:
  2379. peg$silentFails++;
  2380. ip++;
  2381. break;
  2382. case 25:
  2383. peg$silentFails--;
  2384. ip++;
  2385. break;
  2386. default:
  2387. throw new Error("Invalid opcode: " + bc[ip] + ".");
  2388. }
  2389. }
  2390. if (ends.length > 0) {
  2391. end = ends.pop();
  2392. ip = ips.pop();
  2393. } else {
  2394. break;
  2395. }
  2396. }
  2397. return stack[0];
  2398. }
  2399. options.data = {}; // Object to which header attributes will be assigned during parsing
  2400. function list (first, rest) {
  2401. return [first].concat(rest);
  2402. }
  2403. peg$result = peg$parseRule(peg$startRuleIndex);
  2404. if (peg$result !== peg$FAILED && peg$currPos === input.length) {
  2405. return peg$result;
  2406. } else {
  2407. if (peg$result !== peg$FAILED && peg$currPos < input.length) {
  2408. peg$fail({ type: "end", description: "end of input" });
  2409. }
  2410. throw peg$buildException(null, peg$maxFailExpected, peg$maxFailPos);
  2411. }
  2412. }
  2413. return {
  2414. SyntaxError: SyntaxError,
  2415. parse: parse
  2416. };
  2417. })();
  2418. },{}],12:[function(require,module,exports){
  2419. "use strict";
  2420. /**
  2421. * @fileoverview Hacks - This file contains all of the things we
  2422. * wish we didn't have to do, just for interop. It is similar to
  2423. * Utils, which provides actually useful and relevant functions for
  2424. * a SIP library. Methods in this file are grouped by vendor, so
  2425. * as to most easily track when particular hacks may not be necessary anymore.
  2426. */
  2427. module.exports = function (SIP) {
  2428. //keep to quiet jshint, and remain consistent with other files
  2429. SIP = SIP;
  2430. var Hacks = {
  2431. AllBrowsers: {
  2432. maskDtls: function (sdp) {
  2433. if (sdp) {
  2434. sdp = sdp.replace(/ UDP\/TLS\/RTP\/SAVP/gmi, " RTP/SAVP");
  2435. }
  2436. return sdp;
  2437. },
  2438. unmaskDtls: function (sdp) {
  2439. /**
  2440. * Chrome does not handle DTLS correctly (Canaray does, but not production)
  2441. * keeping Chrome as SDES until DTLS is fixed (comment out 'is_opera' condition)
  2442. *
  2443. * UPDATE: May 21, 2014
  2444. * Chrome 35 now properly defaults to DTLS. Only Opera remains using SDES
  2445. *
  2446. * UPDATE: 2014-09-24
  2447. * Opera now supports DTLS by default as well.
  2448. *
  2449. **/
  2450. return sdp.replace(/ RTP\/SAVP/gmi, " UDP/TLS/RTP/SAVP");
  2451. }
  2452. },
  2453. Firefox: {
  2454. /* Condition to detect if hacks are applicable */
  2455. isFirefox: function () {
  2456. return typeof mozRTCPeerConnection !== 'undefined';
  2457. },
  2458. cannotHandleExtraWhitespace: function (sdp) {
  2459. if (this.isFirefox() && sdp) {
  2460. sdp = sdp.replace(/ \r\n/g, "\r\n");
  2461. }
  2462. return sdp;
  2463. },
  2464. hasMissingCLineInSDP: function (sdp) {
  2465. /*
  2466. * This is a Firefox hack to insert valid sdp when getDescription is
  2467. * called with the constraint offerToReceiveVideo = false.
  2468. * We search for either a c-line at the top of the sdp above all
  2469. * m-lines. If that does not exist then we search for a c-line
  2470. * beneath each m-line. If it is missing a c-line, we insert
  2471. * a fake c-line with the ip address 0.0.0.0. This is then valid
  2472. * sdp and no media will be sent for that m-line.
  2473. *
  2474. * Valid SDP is:
  2475. * m=
  2476. * i=
  2477. * c=
  2478. */
  2479. var insertAt, mlines;
  2480. if (sdp.indexOf('c=') > sdp.indexOf('m=')) {
  2481. // Find all m= lines
  2482. mlines = sdp.match(/m=.*\r\n.*/g);
  2483. for (var i=0; i<mlines.length; i++) {
  2484. // If it has an i= line, check if the next line is the c= line
  2485. if (mlines[i].toString().search(/i=.*/) >= 0) {
  2486. insertAt = sdp.indexOf(mlines[i].toString())+mlines[i].toString().length;
  2487. if (sdp.substr(insertAt,2)!=='c=') {
  2488. sdp = sdp.substr(0,insertAt) + '\r\nc=IN IP4 0.0.0.0' + sdp.substr(insertAt);
  2489. }
  2490. // else add the C line if it's missing
  2491. } else if (mlines[i].toString().search(/c=.*/) < 0) {
  2492. insertAt = sdp.indexOf(mlines[i].toString().match(/.*/))+mlines[i].toString().match(/.*/).toString().length;
  2493. sdp = sdp.substr(0,insertAt) + '\r\nc=IN IP4 0.0.0.0' + sdp.substr(insertAt);
  2494. }
  2495. }
  2496. }
  2497. return sdp;
  2498. },
  2499. },
  2500. Chrome: {
  2501. needsExplicitlyInactiveSDP: function (sdp) {
  2502. var sub, index;
  2503. if (Hacks.Firefox.isFirefox()) { // Fix this in Firefox before sending
  2504. index = sdp.indexOf('m=video 0');
  2505. if (index !== -1) {
  2506. sub = sdp.substr(index);
  2507. sub = sub.replace(/\r\nc=IN IP4.*\r\n$/,
  2508. '\r\nc=IN IP4 0.0.0.0\r\na=inactive\r\n');
  2509. return sdp.substr(0, index) + sub;
  2510. }
  2511. }
  2512. return sdp;
  2513. },
  2514. getsConfusedAboutGUM: function (session) {
  2515. if (session.mediaHandler) {
  2516. session.mediaHandler.close();
  2517. }
  2518. }
  2519. }
  2520. };
  2521. return Hacks;
  2522. };
  2523. },{}],13:[function(require,module,exports){
  2524. "use strict";
  2525. var levels = {
  2526. 'error': 0,
  2527. 'warn': 1,
  2528. 'log': 2,
  2529. 'debug': 3
  2530. };
  2531. module.exports = function (console) {
  2532. var LoggerFactory = function () {
  2533. var logger,
  2534. level = 2,
  2535. builtinEnabled = true,
  2536. connector = null;
  2537. this.loggers = {};
  2538. logger = this.getLogger('sip.loggerfactory');
  2539. Object.defineProperties(this, {
  2540. builtinEnabled: {
  2541. get: function(){ return builtinEnabled; },
  2542. set: function(value){
  2543. if (typeof value === 'boolean') {
  2544. builtinEnabled = value;
  2545. } else {
  2546. logger.error('invalid "builtinEnabled" parameter value: '+ JSON.stringify(value));
  2547. }
  2548. }
  2549. },
  2550. level: {
  2551. get: function() {return level; },
  2552. set: function(value) {
  2553. if (value >= 0 && value <=3) {
  2554. level = value;
  2555. } else if (value > 3) {
  2556. level = 3;
  2557. } else if (levels.hasOwnProperty(value)) {
  2558. level = levels[value];
  2559. } else {
  2560. logger.error('invalid "level" parameter value: '+ JSON.stringify(value));
  2561. }
  2562. }
  2563. },
  2564. connector: {
  2565. get: function() {return connector; },
  2566. set: function(value){
  2567. if(value === null || value === "" || value === undefined) {
  2568. connector = null;
  2569. } else if (typeof value === 'function') {
  2570. connector = value;
  2571. } else {
  2572. logger.error('invalid "connector" parameter value: '+ JSON.stringify(value));
  2573. }
  2574. }
  2575. }
  2576. });
  2577. };
  2578. LoggerFactory.prototype.print = function(target, category, label, content) {
  2579. if (typeof content === 'string') {
  2580. var prefix = [new Date(), category];
  2581. if (label) {
  2582. prefix.push(label);
  2583. }
  2584. content = prefix.concat(content).join(' | ');
  2585. }
  2586. target.call(console, content);
  2587. };
  2588. function Logger (logger, category, label) {
  2589. this.logger = logger;
  2590. this.category = category;
  2591. this.label = label;
  2592. }
  2593. Object.keys(levels).forEach(function (targetName) {
  2594. Logger.prototype[targetName] = function (content) {
  2595. this.logger[targetName](this.category, this.label, content);
  2596. };
  2597. LoggerFactory.prototype[targetName] = function (category, label, content) {
  2598. if (this.level >= levels[targetName]) {
  2599. if (this.builtinEnabled) {
  2600. this.print(console[targetName], category, label, content);
  2601. }
  2602. if (this.connector) {
  2603. this.connector(targetName, category, label, content);
  2604. }
  2605. }
  2606. };
  2607. });
  2608. LoggerFactory.prototype.getLogger = function(category, label) {
  2609. var logger;
  2610. if (label && this.level === 3) {
  2611. return new Logger(this, category, label);
  2612. } else if (this.loggers[category]) {
  2613. return this.loggers[category];
  2614. } else {
  2615. logger = new Logger(this, category);
  2616. this.loggers[category] = logger;
  2617. return logger;
  2618. }
  2619. };
  2620. return LoggerFactory;
  2621. };
  2622. },{}],14:[function(require,module,exports){
  2623. "use strict";
  2624. /**
  2625. * @fileoverview MediaHandler
  2626. */
  2627. /* MediaHandler
  2628. * @class PeerConnection helper Class.
  2629. * @param {SIP.Session} session
  2630. * @param {Object} [options]
  2631. */
  2632. module.exports = function (EventEmitter) {
  2633. var MediaHandler = function(session, options) {
  2634. // keep jshint happy
  2635. session = session;
  2636. options = options;
  2637. };
  2638. MediaHandler.prototype = Object.create(EventEmitter.prototype, {
  2639. isReady: {value: function isReady () {}},
  2640. close: {value: function close () {}},
  2641. /**
  2642. * @param {Object} [mediaHint] A custom object describing the media to be used during this session.
  2643. */
  2644. getDescription: {value: function getDescription (mediaHint) {
  2645. // keep jshint happy
  2646. mediaHint = mediaHint;
  2647. }},
  2648. /**
  2649. * Check if a SIP message contains a session description.
  2650. * @param {SIP.SIPMessage} message
  2651. * @returns {boolean}
  2652. */
  2653. hasDescription: {value: function hasDescription (message) {
  2654. // keep jshint happy
  2655. message = message;
  2656. }},
  2657. /**
  2658. * Set the session description contained in a SIP message.
  2659. * @param {SIP.SIPMessage} message
  2660. * @returns {Promise}
  2661. */
  2662. setDescription: {value: function setDescription (message) {
  2663. // keep jshint happy
  2664. message = message;
  2665. }}
  2666. });
  2667. return MediaHandler;
  2668. };
  2669. },{}],15:[function(require,module,exports){
  2670. "use strict";
  2671. /**
  2672. * @fileoverview SIP NameAddrHeader
  2673. */
  2674. /**
  2675. * @augments SIP
  2676. * @class Class creating a Name Address SIP header.
  2677. *
  2678. * @param {SIP.URI} uri
  2679. * @param {String} [displayName]
  2680. * @param {Object} [parameters]
  2681. *
  2682. */
  2683. module.exports = function (SIP) {
  2684. var NameAddrHeader;
  2685. NameAddrHeader = function(uri, displayName, parameters) {
  2686. var param;
  2687. // Checks
  2688. if(!uri || !(uri instanceof SIP.URI)) {
  2689. throw new TypeError('missing or invalid "uri" parameter');
  2690. }
  2691. // Initialize parameters
  2692. this.uri = uri;
  2693. this.parameters = {};
  2694. for (param in parameters) {
  2695. this.setParam(param, parameters[param]);
  2696. }
  2697. Object.defineProperties(this, {
  2698. friendlyName: {
  2699. get: function() { return this.displayName || uri.aor; }
  2700. },
  2701. displayName: {
  2702. get: function() { return displayName; },
  2703. set: function(value) {
  2704. displayName = (value === 0) ? '0' : value;
  2705. }
  2706. }
  2707. });
  2708. };
  2709. NameAddrHeader.prototype = {
  2710. setParam: function (key, value) {
  2711. if(key) {
  2712. this.parameters[key.toLowerCase()] = (typeof value === 'undefined' || value === null) ? null : value.toString();
  2713. }
  2714. },
  2715. getParam: SIP.URI.prototype.getParam,
  2716. hasParam: SIP.URI.prototype.hasParam,
  2717. deleteParam: SIP.URI.prototype.deleteParam,
  2718. clearParams: SIP.URI.prototype.clearParams,
  2719. clone: function() {
  2720. return new NameAddrHeader(
  2721. this.uri.clone(),
  2722. this.displayName,
  2723. JSON.parse(JSON.stringify(this.parameters)));
  2724. },
  2725. toString: function() {
  2726. var body, parameter;
  2727. body = (this.displayName || this.displayName === 0) ? '"' + this.displayName + '" ' : '';
  2728. body += '<' + this.uri.toString() + '>';
  2729. for (parameter in this.parameters) {
  2730. body += ';' + parameter;
  2731. if (this.parameters[parameter] !== null) {
  2732. body += '='+ this.parameters[parameter];
  2733. }
  2734. }
  2735. return body;
  2736. }
  2737. };
  2738. /**
  2739. * Parse the given string and returns a SIP.NameAddrHeader instance or undefined if
  2740. * it is an invalid NameAddrHeader.
  2741. * @public
  2742. * @param {String} name_addr_header
  2743. */
  2744. NameAddrHeader.parse = function(name_addr_header) {
  2745. name_addr_header = SIP.Grammar.parse(name_addr_header,'Name_Addr_Header');
  2746. if (name_addr_header !== -1) {
  2747. return name_addr_header;
  2748. } else {
  2749. return undefined;
  2750. }
  2751. };
  2752. SIP.NameAddrHeader = NameAddrHeader;
  2753. };
  2754. },{}],16:[function(require,module,exports){
  2755. "use strict";
  2756. /**
  2757. * @fileoverview SIP Message Parser
  2758. */
  2759. /**
  2760. * Extract and parse every header of a SIP message.
  2761. * @augments SIP
  2762. * @namespace
  2763. */
  2764. module.exports = function (SIP) {
  2765. var Parser;
  2766. function getHeader(data, headerStart) {
  2767. var
  2768. // 'start' position of the header.
  2769. start = headerStart,
  2770. // 'end' position of the header.
  2771. end = 0,
  2772. // 'partial end' position of the header.
  2773. partialEnd = 0;
  2774. //End of message.
  2775. if (data.substring(start, start + 2).match(/(^\r\n)/)) {
  2776. return -2;
  2777. }
  2778. while(end === 0) {
  2779. // Partial End of Header.
  2780. partialEnd = data.indexOf('\r\n', start);
  2781. // 'indexOf' returns -1 if the value to be found never occurs.
  2782. if (partialEnd === -1) {
  2783. return partialEnd;
  2784. }
  2785. if(!data.substring(partialEnd + 2, partialEnd + 4).match(/(^\r\n)/) && data.charAt(partialEnd + 2).match(/(^\s+)/)) {
  2786. // Not the end of the message. Continue from the next position.
  2787. start = partialEnd + 2;
  2788. } else {
  2789. end = partialEnd;
  2790. }
  2791. }
  2792. return end;
  2793. }
  2794. function parseHeader(message, data, headerStart, headerEnd) {
  2795. var header, idx, length, parsed,
  2796. hcolonIndex = data.indexOf(':', headerStart),
  2797. headerName = data.substring(headerStart, hcolonIndex).trim(),
  2798. headerValue = data.substring(hcolonIndex + 1, headerEnd).trim();
  2799. // If header-field is well-known, parse it.
  2800. switch(headerName.toLowerCase()) {
  2801. case 'via':
  2802. case 'v':
  2803. message.addHeader('via', headerValue);
  2804. if(message.getHeaders('via').length === 1) {
  2805. parsed = message.parseHeader('Via');
  2806. if(parsed) {
  2807. message.via = parsed;
  2808. message.via_branch = parsed.branch;
  2809. }
  2810. } else {
  2811. parsed = 0;
  2812. }
  2813. break;
  2814. case 'from':
  2815. case 'f':
  2816. message.setHeader('from', headerValue);
  2817. parsed = message.parseHeader('from');
  2818. if(parsed) {
  2819. message.from = parsed;
  2820. message.from_tag = parsed.getParam('tag');
  2821. }
  2822. break;
  2823. case 'to':
  2824. case 't':
  2825. message.setHeader('to', headerValue);
  2826. parsed = message.parseHeader('to');
  2827. if(parsed) {
  2828. message.to = parsed;
  2829. message.to_tag = parsed.getParam('tag');
  2830. }
  2831. break;
  2832. case 'record-route':
  2833. parsed = SIP.Grammar.parse(headerValue, 'Record_Route');
  2834. if (parsed === -1) {
  2835. parsed = undefined;
  2836. break;
  2837. }
  2838. length = parsed.length;
  2839. for (idx = 0; idx < length; idx++) {
  2840. header = parsed[idx];
  2841. message.addHeader('record-route', headerValue.substring(header.position, header.offset));
  2842. message.headers['Record-Route'][message.getHeaders('record-route').length - 1].parsed = header.parsed;
  2843. }
  2844. break;
  2845. case 'call-id':
  2846. case 'i':
  2847. message.setHeader('call-id', headerValue);
  2848. parsed = message.parseHeader('call-id');
  2849. if(parsed) {
  2850. message.call_id = headerValue;
  2851. }
  2852. break;
  2853. case 'contact':
  2854. case 'm':
  2855. parsed = SIP.Grammar.parse(headerValue, 'Contact');
  2856. if (parsed === -1) {
  2857. parsed = undefined;
  2858. break;
  2859. }
  2860. length = parsed.length;
  2861. for (idx = 0; idx < length; idx++) {
  2862. header = parsed[idx];
  2863. message.addHeader('contact', headerValue.substring(header.position, header.offset));
  2864. message.headers['Contact'][message.getHeaders('contact').length - 1].parsed = header.parsed;
  2865. }
  2866. break;
  2867. case 'content-length':
  2868. case 'l':
  2869. message.setHeader('content-length', headerValue);
  2870. parsed = message.parseHeader('content-length');
  2871. break;
  2872. case 'content-type':
  2873. case 'c':
  2874. message.setHeader('content-type', headerValue);
  2875. parsed = message.parseHeader('content-type');
  2876. break;
  2877. case 'cseq':
  2878. message.setHeader('cseq', headerValue);
  2879. parsed = message.parseHeader('cseq');
  2880. if(parsed) {
  2881. message.cseq = parsed.value;
  2882. }
  2883. if(message instanceof SIP.IncomingResponse) {
  2884. message.method = parsed.method;
  2885. }
  2886. break;
  2887. case 'max-forwards':
  2888. message.setHeader('max-forwards', headerValue);
  2889. parsed = message.parseHeader('max-forwards');
  2890. break;
  2891. case 'www-authenticate':
  2892. message.setHeader('www-authenticate', headerValue);
  2893. parsed = message.parseHeader('www-authenticate');
  2894. break;
  2895. case 'proxy-authenticate':
  2896. message.setHeader('proxy-authenticate', headerValue);
  2897. parsed = message.parseHeader('proxy-authenticate');
  2898. break;
  2899. case 'refer-to':
  2900. case 'r':
  2901. message.setHeader('refer-to', headerValue);
  2902. parsed = message.parseHeader('refer-to');
  2903. if (parsed) {
  2904. message.refer_to = parsed;
  2905. }
  2906. break;
  2907. default:
  2908. // Do not parse this header.
  2909. message.setHeader(headerName, headerValue);
  2910. parsed = 0;
  2911. }
  2912. if (parsed === undefined) {
  2913. return {
  2914. error: 'error parsing header "'+ headerName +'"'
  2915. };
  2916. } else {
  2917. return true;
  2918. }
  2919. }
  2920. /** Parse SIP Message
  2921. * @function
  2922. * @param {String} message SIP message.
  2923. * @param {Object} logger object.
  2924. * @returns {SIP.IncomingRequest|SIP.IncomingResponse|undefined}
  2925. */
  2926. Parser = {};
  2927. Parser.parseMessage = function(data, ua) {
  2928. var message, firstLine, contentLength, bodyStart, parsed,
  2929. headerStart = 0,
  2930. headerEnd = data.indexOf('\r\n'),
  2931. logger = ua.getLogger('sip.parser');
  2932. if(headerEnd === -1) {
  2933. logger.warn('no CRLF found, not a SIP message, discarded');
  2934. return;
  2935. }
  2936. // Parse first line. Check if it is a Request or a Reply.
  2937. firstLine = data.substring(0, headerEnd);
  2938. parsed = SIP.Grammar.parse(firstLine, 'Request_Response');
  2939. if(parsed === -1) {
  2940. logger.warn('error parsing first line of SIP message: "' + firstLine + '"');
  2941. return;
  2942. } else if(!parsed.status_code) {
  2943. message = new SIP.IncomingRequest(ua);
  2944. message.method = parsed.method;
  2945. message.ruri = parsed.uri;
  2946. } else {
  2947. message = new SIP.IncomingResponse(ua);
  2948. message.status_code = parsed.status_code;
  2949. message.reason_phrase = parsed.reason_phrase;
  2950. }
  2951. message.data = data;
  2952. headerStart = headerEnd + 2;
  2953. /* Loop over every line in data. Detect the end of each header and parse
  2954. * it or simply add to the headers collection.
  2955. */
  2956. while(true) {
  2957. headerEnd = getHeader(data, headerStart);
  2958. // The SIP message has normally finished.
  2959. if(headerEnd === -2) {
  2960. bodyStart = headerStart + 2;
  2961. break;
  2962. }
  2963. // data.indexOf returned -1 due to a malformed message.
  2964. else if(headerEnd === -1) {
  2965. logger.error('malformed message');
  2966. return;
  2967. }
  2968. parsed = parseHeader(message, data, headerStart, headerEnd);
  2969. if(parsed !== true) {
  2970. logger.error(parsed.error);
  2971. return;
  2972. }
  2973. headerStart = headerEnd + 2;
  2974. }
  2975. /* RFC3261 18.3.
  2976. * If there are additional bytes in the transport packet
  2977. * beyond the end of the body, they MUST be discarded.
  2978. */
  2979. if(message.hasHeader('content-length')) {
  2980. contentLength = message.getHeader('content-length');
  2981. message.body = data.substr(bodyStart, contentLength);
  2982. } else {
  2983. message.body = data.substring(bodyStart);
  2984. }
  2985. return message;
  2986. };
  2987. SIP.Parser = Parser;
  2988. };
  2989. },{}],17:[function(require,module,exports){
  2990. "use strict";
  2991. module.exports = function (SIP) {
  2992. var RegisterContext;
  2993. RegisterContext = function (ua) {
  2994. var params = {},
  2995. regId = 1;
  2996. this.registrar = ua.configuration.registrarServer;
  2997. this.expires = ua.configuration.registerExpires;
  2998. // Contact header
  2999. this.contact = ua.contact.toString();
  3000. if(regId) {
  3001. this.contact += ';reg-id='+ regId;
  3002. this.contact += ';+sip.instance="<urn:uuid:'+ ua.configuration.instanceId+'>"';
  3003. }
  3004. // Call-ID and CSeq values RFC3261 10.2
  3005. this.call_id = SIP.Utils.createRandomToken(22);
  3006. this.cseq = 80;
  3007. this.to_uri = ua.configuration.uri;
  3008. params.to_uri = this.to_uri;
  3009. params.to_displayName = ua.configuration.displayName;
  3010. params.call_id = this.call_id;
  3011. params.cseq = this.cseq;
  3012. // Extends ClientContext
  3013. SIP.Utils.augment(this, SIP.ClientContext, [ua, 'REGISTER', this.registrar, {params: params}]);
  3014. this.registrationTimer = null;
  3015. this.registrationExpiredTimer = null;
  3016. // Set status
  3017. this.registered = false;
  3018. this.logger = ua.getLogger('sip.registercontext');
  3019. };
  3020. RegisterContext.prototype = {
  3021. register: function (options) {
  3022. var self = this, extraHeaders;
  3023. // Handle Options
  3024. this.options = options || {};
  3025. extraHeaders = (this.options.extraHeaders || []).slice();
  3026. extraHeaders.push('Contact: ' + this.contact + ';expires=' + this.expires);
  3027. extraHeaders.push('Allow: ' + SIP.UA.C.ALLOWED_METHODS.toString());
  3028. // Save original extraHeaders to be used in .close
  3029. this.closeHeaders = this.options.closeWithHeaders ?
  3030. (this.options.extraHeaders || []).slice() : [];
  3031. this.receiveResponse = function(response) {
  3032. var contact, expires,
  3033. contacts = response.getHeaders('contact').length,
  3034. cause;
  3035. // Discard responses to older REGISTER/un-REGISTER requests.
  3036. if(response.cseq !== this.cseq) {
  3037. return;
  3038. }
  3039. // Clear registration timer
  3040. if (this.registrationTimer !== null) {
  3041. SIP.Timers.clearTimeout(this.registrationTimer);
  3042. this.registrationTimer = null;
  3043. }
  3044. switch(true) {
  3045. case /^1[0-9]{2}$/.test(response.status_code):
  3046. this.emit('progress', response);
  3047. break;
  3048. case /^2[0-9]{2}$/.test(response.status_code):
  3049. this.emit('accepted', response);
  3050. if(response.hasHeader('expires')) {
  3051. expires = response.getHeader('expires');
  3052. }
  3053. if (this.registrationExpiredTimer !== null) {
  3054. SIP.Timers.clearTimeout(this.registrationExpiredTimer);
  3055. this.registrationExpiredTimer = null;
  3056. }
  3057. // Search the Contact pointing to us and update the expires value accordingly.
  3058. if (!contacts) {
  3059. this.logger.warn('no Contact header in response to REGISTER, response ignored');
  3060. break;
  3061. }
  3062. while(contacts--) {
  3063. contact = response.parseHeader('contact', contacts);
  3064. if(contact.uri.user === this.ua.contact.uri.user) {
  3065. expires = contact.getParam('expires');
  3066. break;
  3067. } else {
  3068. contact = null;
  3069. }
  3070. }
  3071. if (!contact) {
  3072. this.logger.warn('no Contact header pointing to us, response ignored');
  3073. break;
  3074. }
  3075. if(!expires) {
  3076. expires = this.expires;
  3077. }
  3078. // Re-Register before the expiration interval has elapsed.
  3079. // For that, decrease the expires value. ie: 3 seconds
  3080. this.registrationTimer = SIP.Timers.setTimeout(function() {
  3081. self.registrationTimer = null;
  3082. self.register(self.options);
  3083. }, (expires * 1000) - 3000);
  3084. this.registrationExpiredTimer = SIP.Timers.setTimeout(function () {
  3085. self.logger.warn('registration expired');
  3086. if (self.registered) {
  3087. self.unregistered(null, SIP.C.causes.EXPIRES);
  3088. }
  3089. }, expires * 1000);
  3090. //Save gruu values
  3091. if (contact.hasParam('temp-gruu')) {
  3092. this.ua.contact.temp_gruu = SIP.URI.parse(contact.getParam('temp-gruu').replace(/"/g,''));
  3093. }
  3094. if (contact.hasParam('pub-gruu')) {
  3095. this.ua.contact.pub_gruu = SIP.URI.parse(contact.getParam('pub-gruu').replace(/"/g,''));
  3096. }
  3097. this.registered = true;
  3098. this.emit('registered', response || null);
  3099. break;
  3100. // Interval too brief RFC3261 10.2.8
  3101. case /^423$/.test(response.status_code):
  3102. if(response.hasHeader('min-expires')) {
  3103. // Increase our registration interval to the suggested minimum
  3104. this.expires = response.getHeader('min-expires');
  3105. // Attempt the registration again immediately
  3106. this.register(this.options);
  3107. } else { //This response MUST contain a Min-Expires header field
  3108. this.logger.warn('423 response received for REGISTER without Min-Expires');
  3109. this.registrationFailure(response, SIP.C.causes.SIP_FAILURE_CODE);
  3110. }
  3111. break;
  3112. default:
  3113. cause = SIP.Utils.sipErrorCause(response.status_code);
  3114. this.registrationFailure(response, cause);
  3115. }
  3116. };
  3117. this.onRequestTimeout = function() {
  3118. this.registrationFailure(null, SIP.C.causes.REQUEST_TIMEOUT);
  3119. };
  3120. this.onTransportError = function() {
  3121. this.registrationFailure(null, SIP.C.causes.CONNECTION_ERROR);
  3122. };
  3123. this.cseq++;
  3124. this.request.cseq = this.cseq;
  3125. this.request.setHeader('cseq', this.cseq + ' REGISTER');
  3126. this.request.extraHeaders = extraHeaders;
  3127. this.send();
  3128. },
  3129. registrationFailure: function (response, cause) {
  3130. this.emit('failed', response || null, cause || null);
  3131. },
  3132. onTransportClosed: function() {
  3133. this.registered_before = this.registered;
  3134. if (this.registrationTimer !== null) {
  3135. SIP.Timers.clearTimeout(this.registrationTimer);
  3136. this.registrationTimer = null;
  3137. }
  3138. if (this.registrationExpiredTimer !== null) {
  3139. SIP.Timers.clearTimeout(this.registrationExpiredTimer);
  3140. this.registrationExpiredTimer = null;
  3141. }
  3142. if(this.registered) {
  3143. this.unregistered(null, SIP.C.causes.CONNECTION_ERROR);
  3144. }
  3145. },
  3146. onTransportConnected: function() {
  3147. this.register(this.options);
  3148. },
  3149. close: function() {
  3150. var options = {
  3151. all: false,
  3152. extraHeaders: this.closeHeaders
  3153. };
  3154. this.registered_before = this.registered;
  3155. this.unregister(options);
  3156. },
  3157. unregister: function(options) {
  3158. var extraHeaders;
  3159. options = options || {};
  3160. if(!this.registered && !options.all) {
  3161. this.logger.warn('already unregistered');
  3162. return;
  3163. }
  3164. extraHeaders = (options.extraHeaders || []).slice();
  3165. this.registered = false;
  3166. // Clear the registration timer.
  3167. if (this.registrationTimer !== null) {
  3168. SIP.Timers.clearTimeout(this.registrationTimer);
  3169. this.registrationTimer = null;
  3170. }
  3171. if(options.all) {
  3172. extraHeaders.push('Contact: *');
  3173. extraHeaders.push('Expires: 0');
  3174. } else {
  3175. extraHeaders.push('Contact: '+ this.contact + ';expires=0');
  3176. }
  3177. this.receiveResponse = function(response) {
  3178. var cause;
  3179. switch(true) {
  3180. case /^1[0-9]{2}$/.test(response.status_code):
  3181. this.emit('progress', response);
  3182. break;
  3183. case /^2[0-9]{2}$/.test(response.status_code):
  3184. this.emit('accepted', response);
  3185. if (this.registrationExpiredTimer !== null) {
  3186. SIP.Timers.clearTimeout(this.registrationExpiredTimer);
  3187. this.registrationExpiredTimer = null;
  3188. }
  3189. this.unregistered(response);
  3190. break;
  3191. default:
  3192. cause = SIP.Utils.sipErrorCause(response.status_code);
  3193. this.unregistered(response,cause);
  3194. }
  3195. };
  3196. this.onRequestTimeout = function() {
  3197. // Not actually unregistered...
  3198. //this.unregistered(null, SIP.C.causes.REQUEST_TIMEOUT);
  3199. };
  3200. this.onTransportError = function() {
  3201. // Not actually unregistered...
  3202. //this.unregistered(null, SIP.C.causes.CONNECTION_ERROR);
  3203. };
  3204. this.cseq++;
  3205. this.request.cseq = this.cseq;
  3206. this.request.setHeader('cseq', this.cseq + ' REGISTER');
  3207. this.request.extraHeaders = extraHeaders;
  3208. this.send();
  3209. },
  3210. unregistered: function(response, cause) {
  3211. this.registered = false;
  3212. this.emit('unregistered', response || null, cause || null);
  3213. }
  3214. };
  3215. SIP.RegisterContext = RegisterContext;
  3216. };
  3217. },{}],18:[function(require,module,exports){
  3218. "use strict";
  3219. /**
  3220. * @fileoverview Request Sender
  3221. */
  3222. /**
  3223. * @augments SIP
  3224. * @class Class creating a request sender.
  3225. * @param {Object} applicant
  3226. * @param {SIP.UA} ua
  3227. */
  3228. module.exports = function (SIP) {
  3229. var RequestSender;
  3230. RequestSender = function(applicant, ua) {
  3231. this.logger = ua.getLogger('sip.requestsender');
  3232. this.ua = ua;
  3233. this.applicant = applicant;
  3234. this.method = applicant.request.method;
  3235. this.request = applicant.request;
  3236. this.credentials = null;
  3237. this.challenged = false;
  3238. this.staled = false;
  3239. // If ua is in closing process or even closed just allow sending Bye and ACK
  3240. if (ua.status === SIP.UA.C.STATUS_USER_CLOSED && (this.method !== SIP.C.BYE || this.method !== SIP.C.ACK)) {
  3241. this.onTransportError();
  3242. }
  3243. };
  3244. /**
  3245. * Create the client transaction and send the message.
  3246. */
  3247. RequestSender.prototype = {
  3248. send: function() {
  3249. switch(this.method) {
  3250. case "INVITE":
  3251. this.clientTransaction = new SIP.Transactions.InviteClientTransaction(this, this.request, this.ua.transport);
  3252. break;
  3253. case "ACK":
  3254. this.clientTransaction = new SIP.Transactions.AckClientTransaction(this, this.request, this.ua.transport);
  3255. break;
  3256. default:
  3257. this.clientTransaction = new SIP.Transactions.NonInviteClientTransaction(this, this.request, this.ua.transport);
  3258. }
  3259. this.clientTransaction.send();
  3260. return this.clientTransaction;
  3261. },
  3262. /**
  3263. * Callback fired when receiving a request timeout error from the client transaction.
  3264. * To be re-defined by the applicant.
  3265. * @event
  3266. */
  3267. onRequestTimeout: function() {
  3268. this.applicant.onRequestTimeout();
  3269. },
  3270. /**
  3271. * Callback fired when receiving a transport error from the client transaction.
  3272. * To be re-defined by the applicant.
  3273. * @event
  3274. */
  3275. onTransportError: function() {
  3276. this.applicant.onTransportError();
  3277. },
  3278. /**
  3279. * Called from client transaction when receiving a correct response to the request.
  3280. * Authenticate request if needed or pass the response back to the applicant.
  3281. * @param {SIP.IncomingResponse} response
  3282. */
  3283. receiveResponse: function(response) {
  3284. var cseq, challenge, authorization_header_name,
  3285. status_code = response.status_code;
  3286. /*
  3287. * Authentication
  3288. * Authenticate once. _challenged_ flag used to avoid infinite authentications.
  3289. */
  3290. if (status_code === 401 || status_code === 407) {
  3291. // Get and parse the appropriate WWW-Authenticate or Proxy-Authenticate header.
  3292. if (response.status_code === 401) {
  3293. challenge = response.parseHeader('www-authenticate');
  3294. authorization_header_name = 'authorization';
  3295. } else {
  3296. challenge = response.parseHeader('proxy-authenticate');
  3297. authorization_header_name = 'proxy-authorization';
  3298. }
  3299. // Verify it seems a valid challenge.
  3300. if (! challenge) {
  3301. this.logger.warn(response.status_code + ' with wrong or missing challenge, cannot authenticate');
  3302. this.applicant.receiveResponse(response);
  3303. return;
  3304. }
  3305. if (!this.challenged || (!this.staled && challenge.stale === true)) {
  3306. if (!this.credentials) {
  3307. this.credentials = this.ua.configuration.authenticationFactory(this.ua);
  3308. }
  3309. // Verify that the challenge is really valid.
  3310. if (!this.credentials.authenticate(this.request, challenge)) {
  3311. this.applicant.receiveResponse(response);
  3312. return;
  3313. }
  3314. this.challenged = true;
  3315. if (challenge.stale) {
  3316. this.staled = true;
  3317. }
  3318. if (response.method === SIP.C.REGISTER) {
  3319. cseq = this.applicant.cseq += 1;
  3320. } else if (this.request.dialog){
  3321. cseq = this.request.dialog.local_seqnum += 1;
  3322. } else {
  3323. cseq = this.request.cseq + 1;
  3324. this.request.cseq = cseq;
  3325. }
  3326. this.request.setHeader('cseq', cseq +' '+ this.method);
  3327. this.request.setHeader(authorization_header_name, this.credentials.toString());
  3328. this.send();
  3329. } else {
  3330. this.applicant.receiveResponse(response);
  3331. }
  3332. } else {
  3333. this.applicant.receiveResponse(response);
  3334. }
  3335. }
  3336. };
  3337. SIP.RequestSender = RequestSender;
  3338. };
  3339. },{}],19:[function(require,module,exports){
  3340. /**
  3341. * @name SIP
  3342. * @namespace
  3343. */
  3344. "use strict";
  3345. module.exports = function (environment) {
  3346. var pkg = require('../package.json'),
  3347. version = pkg.version,
  3348. title = pkg.title;
  3349. var SIP = Object.defineProperties({}, {
  3350. version: {
  3351. get: function(){ return version; }
  3352. },
  3353. name: {
  3354. get: function(){ return title; }
  3355. }
  3356. });
  3357. require('./Utils')(SIP, environment);
  3358. SIP.LoggerFactory = require('./LoggerFactory')(environment.console);
  3359. SIP.EventEmitter = require('./EventEmitter')(environment.console);
  3360. SIP.C = require('./Constants')(SIP.name, SIP.version);
  3361. SIP.Exceptions = require('./Exceptions');
  3362. SIP.Timers = require('./Timers')(environment.timers);
  3363. SIP.Transport = environment.Transport(SIP, environment.WebSocket);
  3364. require('./Parser')(SIP);
  3365. require('./SIPMessage')(SIP);
  3366. require('./URI')(SIP);
  3367. require('./NameAddrHeader')(SIP);
  3368. require('./Transactions')(SIP);
  3369. require('./Dialogs')(SIP);
  3370. require('./RequestSender')(SIP);
  3371. require('./RegisterContext')(SIP);
  3372. SIP.MediaHandler = require('./MediaHandler')(SIP.EventEmitter);
  3373. require('./ClientContext')(SIP);
  3374. require('./ServerContext')(SIP);
  3375. require('./Session')(SIP, environment);
  3376. require('./Subscription')(SIP);
  3377. SIP.WebRTC = require('./WebRTC')(SIP, environment);
  3378. require('./UA')(SIP, environment);
  3379. SIP.Hacks = require('./Hacks')(SIP);
  3380. require('./SanityCheck')(SIP);
  3381. SIP.DigestAuthentication = require('./DigestAuthentication')(SIP.Utils);
  3382. SIP.Grammar = require('./Grammar')(SIP);
  3383. return SIP;
  3384. };
  3385. },{"../package.json":2,"./ClientContext":3,"./Constants":4,"./Dialogs":6,"./DigestAuthentication":7,"./EventEmitter":8,"./Exceptions":9,"./Grammar":10,"./Hacks":12,"./LoggerFactory":13,"./MediaHandler":14,"./NameAddrHeader":15,"./Parser":16,"./RegisterContext":17,"./RequestSender":18,"./SIPMessage":20,"./SanityCheck":21,"./ServerContext":22,"./Session":23,"./Subscription":25,"./Timers":26,"./Transactions":27,"./UA":29,"./URI":30,"./Utils":31,"./WebRTC":32}],20:[function(require,module,exports){
  3386. "use strict";
  3387. /**
  3388. * @fileoverview SIP Message
  3389. */
  3390. module.exports = function (SIP) {
  3391. var
  3392. OutgoingRequest,
  3393. IncomingMessage,
  3394. IncomingRequest,
  3395. IncomingResponse;
  3396. function getSupportedHeader (request) {
  3397. var allowUnregistered = request.ua.configuration.hackAllowUnregisteredOptionTags;
  3398. var optionTags = [];
  3399. var optionTagSet = {};
  3400. if (request.method === SIP.C.REGISTER) {
  3401. optionTags.push('path', 'gruu');
  3402. } else if (request.method === SIP.C.INVITE &&
  3403. (request.ua.contact.pub_gruu || request.ua.contact.temp_gruu)) {
  3404. optionTags.push('gruu');
  3405. }
  3406. if (request.ua.configuration.rel100 === SIP.C.supported.SUPPORTED) {
  3407. optionTags.push('100rel');
  3408. }
  3409. if (request.ua.configuration.replaces === SIP.C.supported.SUPPORTED) {
  3410. optionTags.push('replaces');
  3411. }
  3412. optionTags.push('outbound');
  3413. optionTags = optionTags.concat(request.ua.configuration.extraSupported);
  3414. optionTags = optionTags.filter(function(optionTag) {
  3415. var registered = SIP.C.OPTION_TAGS[optionTag];
  3416. var unique = !optionTagSet[optionTag];
  3417. optionTagSet[optionTag] = true;
  3418. return (registered || allowUnregistered) && unique;
  3419. });
  3420. return 'Supported: ' + optionTags.join(', ') + '\r\n';
  3421. }
  3422. /**
  3423. * @augments SIP
  3424. * @class Class for outgoing SIP request.
  3425. * @param {String} method request method
  3426. * @param {String} ruri request uri
  3427. * @param {SIP.UA} ua
  3428. * @param {Object} params parameters that will have priority over ua.configuration parameters:
  3429. * <br>
  3430. * - cseq, call_id, from_tag, from_uri, from_displayName, to_uri, to_tag, route_set
  3431. * @param {Object} [headers] extra headers
  3432. * @param {String} [body]
  3433. */
  3434. OutgoingRequest = function(method, ruri, ua, params, extraHeaders, body) {
  3435. var
  3436. to,
  3437. from,
  3438. call_id,
  3439. cseq,
  3440. to_uri,
  3441. from_uri;
  3442. params = params || {};
  3443. // Mandatory parameters check
  3444. if(!method || !ruri || !ua) {
  3445. return null;
  3446. }
  3447. this.logger = ua.getLogger('sip.sipmessage');
  3448. this.ua = ua;
  3449. this.headers = {};
  3450. this.method = method;
  3451. this.ruri = ruri;
  3452. this.body = body;
  3453. this.extraHeaders = (extraHeaders || []).slice();
  3454. this.statusCode = params.status_code;
  3455. this.reasonPhrase = params.reason_phrase;
  3456. // Fill the Common SIP Request Headers
  3457. // Route
  3458. if (params.route_set) {
  3459. this.setHeader('route', params.route_set);
  3460. } else if (ua.configuration.usePreloadedRoute){
  3461. this.setHeader('route', ua.transport.server.sip_uri);
  3462. }
  3463. // Via
  3464. // Empty Via header. Will be filled by the client transaction.
  3465. this.setHeader('via', '');
  3466. // Max-Forwards
  3467. this.setHeader('max-forwards', SIP.UA.C.MAX_FORWARDS);
  3468. // To
  3469. to_uri = params.to_uri || ruri;
  3470. to = (params.to_displayName || params.to_displayName === 0) ? '"' + params.to_displayName + '" ' : '';
  3471. to += '<' + (to_uri && to_uri.toRaw ? to_uri.toRaw() : to_uri) + '>';
  3472. to += params.to_tag ? ';tag=' + params.to_tag : '';
  3473. this.to = new SIP.NameAddrHeader.parse(to);
  3474. this.setHeader('to', to);
  3475. // From
  3476. from_uri = params.from_uri || ua.configuration.uri;
  3477. if (params.from_displayName || params.from_displayName === 0) {
  3478. from = '"' + params.from_displayName + '" ';
  3479. } else if (ua.configuration.displayName) {
  3480. from = '"' + ua.configuration.displayName + '" ';
  3481. } else {
  3482. from = '';
  3483. }
  3484. from += '<' + (from_uri && from_uri.toRaw ? from_uri.toRaw() : from_uri) + '>;tag=';
  3485. from += params.from_tag || SIP.Utils.newTag();
  3486. this.from = new SIP.NameAddrHeader.parse(from);
  3487. this.setHeader('from', from);
  3488. // Call-ID
  3489. call_id = params.call_id || (ua.configuration.sipjsId + SIP.Utils.createRandomToken(15));
  3490. this.call_id = call_id;
  3491. this.setHeader('call-id', call_id);
  3492. // CSeq
  3493. cseq = params.cseq || Math.floor(Math.random() * 10000);
  3494. this.cseq = cseq;
  3495. this.setHeader('cseq', cseq + ' ' + method);
  3496. };
  3497. OutgoingRequest.prototype = {
  3498. /**
  3499. * Replace the the given header by the given value.
  3500. * @param {String} name header name
  3501. * @param {String | Array} value header value
  3502. */
  3503. setHeader: function(name, value) {
  3504. this.headers[SIP.Utils.headerize(name)] = (value instanceof Array) ? value : [value];
  3505. },
  3506. /**
  3507. * Get the value of the given header name at the given position.
  3508. * @param {String} name header name
  3509. * @returns {String|undefined} Returns the specified header, undefined if header doesn't exist.
  3510. */
  3511. getHeader: function(name) {
  3512. var regexp, idx,
  3513. length = this.extraHeaders.length,
  3514. header = this.headers[SIP.Utils.headerize(name)];
  3515. if(header) {
  3516. if(header[0]) {
  3517. return header[0];
  3518. }
  3519. } else {
  3520. regexp = new RegExp('^\\s*' + name + '\\s*:','i');
  3521. for (idx = 0; idx < length; idx++) {
  3522. header = this.extraHeaders[idx];
  3523. if (regexp.test(header)) {
  3524. return header.substring(header.indexOf(':')+1).trim();
  3525. }
  3526. }
  3527. }
  3528. return;
  3529. },
  3530. /**
  3531. * Get the header/s of the given name.
  3532. * @param {String} name header name
  3533. * @returns {Array} Array with all the headers of the specified name.
  3534. */
  3535. getHeaders: function(name) {
  3536. var idx, length, regexp,
  3537. header = this.headers[SIP.Utils.headerize(name)],
  3538. result = [];
  3539. if(header) {
  3540. length = header.length;
  3541. for (idx = 0; idx < length; idx++) {
  3542. result.push(header[idx]);
  3543. }
  3544. return result;
  3545. } else {
  3546. length = this.extraHeaders.length;
  3547. regexp = new RegExp('^\\s*' + name + '\\s*:','i');
  3548. for (idx = 0; idx < length; idx++) {
  3549. header = this.extraHeaders[idx];
  3550. if (regexp.test(header)) {
  3551. result.push(header.substring(header.indexOf(':')+1).trim());
  3552. }
  3553. }
  3554. return result;
  3555. }
  3556. },
  3557. /**
  3558. * Verify the existence of the given header.
  3559. * @param {String} name header name
  3560. * @returns {boolean} true if header with given name exists, false otherwise
  3561. */
  3562. hasHeader: function(name) {
  3563. var regexp, idx,
  3564. length = this.extraHeaders.length;
  3565. if (this.headers[SIP.Utils.headerize(name)]) {
  3566. return true;
  3567. } else {
  3568. regexp = new RegExp('^\\s*' + name + '\\s*:','i');
  3569. for (idx = 0; idx < length; idx++) {
  3570. if (regexp.test(this.extraHeaders[idx])) {
  3571. return true;
  3572. }
  3573. }
  3574. }
  3575. return false;
  3576. },
  3577. toString: function() {
  3578. var msg = '', header, length, idx;
  3579. msg += this.method + ' ' + (this.ruri.toRaw ? this.ruri.toRaw() : this.ruri) + ' SIP/2.0\r\n';
  3580. for (header in this.headers) {
  3581. length = this.headers[header].length;
  3582. for (idx = 0; idx < length; idx++) {
  3583. msg += header + ': ' + this.headers[header][idx] + '\r\n';
  3584. }
  3585. }
  3586. length = this.extraHeaders.length;
  3587. for (idx = 0; idx < length; idx++) {
  3588. msg += this.extraHeaders[idx].trim() +'\r\n';
  3589. }
  3590. msg += getSupportedHeader(this);
  3591. msg += 'User-Agent: ' + this.ua.configuration.userAgentString +'\r\n';
  3592. if (this.body) {
  3593. if (typeof this.body === 'string') {
  3594. length = SIP.Utils.str_utf8_length(this.body);
  3595. msg += 'Content-Length: ' + length + '\r\n\r\n';
  3596. msg += this.body;
  3597. } else {
  3598. if (this.body.body && this.body.contentType) {
  3599. length = SIP.Utils.str_utf8_length(this.body.body);
  3600. msg += 'Content-Type: ' + this.body.contentType + '\r\n';
  3601. msg += 'Content-Length: ' + length + '\r\n\r\n';
  3602. msg += this.body.body;
  3603. } else {
  3604. msg += 'Content-Length: ' + 0 + '\r\n\r\n';
  3605. }
  3606. }
  3607. } else {
  3608. msg += 'Content-Length: ' + 0 + '\r\n\r\n';
  3609. }
  3610. return msg;
  3611. }
  3612. };
  3613. /**
  3614. * @augments SIP
  3615. * @class Class for incoming SIP message.
  3616. */
  3617. IncomingMessage = function(){
  3618. this.data = null;
  3619. this.headers = null;
  3620. this.method = null;
  3621. this.via = null;
  3622. this.via_branch = null;
  3623. this.call_id = null;
  3624. this.cseq = null;
  3625. this.from = null;
  3626. this.from_tag = null;
  3627. this.to = null;
  3628. this.to_tag = null;
  3629. this.body = null;
  3630. };
  3631. IncomingMessage.prototype = {
  3632. /**
  3633. * Insert a header of the given name and value into the last position of the
  3634. * header array.
  3635. * @param {String} name header name
  3636. * @param {String} value header value
  3637. */
  3638. addHeader: function(name, value) {
  3639. var header = { raw: value };
  3640. name = SIP.Utils.headerize(name);
  3641. if(this.headers[name]) {
  3642. this.headers[name].push(header);
  3643. } else {
  3644. this.headers[name] = [header];
  3645. }
  3646. },
  3647. /**
  3648. * Get the value of the given header name at the given position.
  3649. * @param {String} name header name
  3650. * @returns {String|undefined} Returns the specified header, null if header doesn't exist.
  3651. */
  3652. getHeader: function(name) {
  3653. var header = this.headers[SIP.Utils.headerize(name)];
  3654. if(header) {
  3655. if(header[0]) {
  3656. return header[0].raw;
  3657. }
  3658. } else {
  3659. return;
  3660. }
  3661. },
  3662. /**
  3663. * Get the header/s of the given name.
  3664. * @param {String} name header name
  3665. * @returns {Array} Array with all the headers of the specified name.
  3666. */
  3667. getHeaders: function(name) {
  3668. var idx, length,
  3669. header = this.headers[SIP.Utils.headerize(name)],
  3670. result = [];
  3671. if(!header) {
  3672. return [];
  3673. }
  3674. length = header.length;
  3675. for (idx = 0; idx < length; idx++) {
  3676. result.push(header[idx].raw);
  3677. }
  3678. return result;
  3679. },
  3680. /**
  3681. * Verify the existence of the given header.
  3682. * @param {String} name header name
  3683. * @returns {boolean} true if header with given name exists, false otherwise
  3684. */
  3685. hasHeader: function(name) {
  3686. return(this.headers[SIP.Utils.headerize(name)]) ? true : false;
  3687. },
  3688. /**
  3689. * Parse the given header on the given index.
  3690. * @param {String} name header name
  3691. * @param {Number} [idx=0] header index
  3692. * @returns {Object|undefined} Parsed header object, undefined if the header is not present or in case of a parsing error.
  3693. */
  3694. parseHeader: function(name, idx) {
  3695. var header, value, parsed;
  3696. name = SIP.Utils.headerize(name);
  3697. idx = idx || 0;
  3698. if(!this.headers[name]) {
  3699. this.logger.log('header "' + name + '" not present');
  3700. return;
  3701. } else if(idx >= this.headers[name].length) {
  3702. this.logger.log('not so many "' + name + '" headers present');
  3703. return;
  3704. }
  3705. header = this.headers[name][idx];
  3706. value = header.raw;
  3707. if(header.parsed) {
  3708. return header.parsed;
  3709. }
  3710. //substitute '-' by '_' for grammar rule matching.
  3711. parsed = SIP.Grammar.parse(value, name.replace(/-/g, '_'));
  3712. if(parsed === -1) {
  3713. this.headers[name].splice(idx, 1); //delete from headers
  3714. this.logger.warn('error parsing "' + name + '" header field with value "' + value + '"');
  3715. return;
  3716. } else {
  3717. header.parsed = parsed;
  3718. return parsed;
  3719. }
  3720. },
  3721. /**
  3722. * Message Header attribute selector. Alias of parseHeader.
  3723. * @param {String} name header name
  3724. * @param {Number} [idx=0] header index
  3725. * @returns {Object|undefined} Parsed header object, undefined if the header is not present or in case of a parsing error.
  3726. *
  3727. * @example
  3728. * message.s('via',3).port
  3729. */
  3730. s: function(name, idx) {
  3731. return this.parseHeader(name, idx);
  3732. },
  3733. /**
  3734. * Replace the value of the given header by the value.
  3735. * @param {String} name header name
  3736. * @param {String} value header value
  3737. */
  3738. setHeader: function(name, value) {
  3739. var header = { raw: value };
  3740. this.headers[SIP.Utils.headerize(name)] = [header];
  3741. },
  3742. toString: function() {
  3743. return this.data;
  3744. }
  3745. };
  3746. /**
  3747. * @augments IncomingMessage
  3748. * @class Class for incoming SIP request.
  3749. */
  3750. IncomingRequest = function(ua) {
  3751. this.logger = ua.getLogger('sip.sipmessage');
  3752. this.ua = ua;
  3753. this.headers = {};
  3754. this.ruri = null;
  3755. this.transport = null;
  3756. this.server_transaction = null;
  3757. };
  3758. IncomingRequest.prototype = new IncomingMessage();
  3759. /**
  3760. * Stateful reply.
  3761. * @param {Number} code status code
  3762. * @param {String} reason reason phrase
  3763. * @param {Object} headers extra headers
  3764. * @param {String} body body
  3765. * @param {Function} [onSuccess] onSuccess callback
  3766. * @param {Function} [onFailure] onFailure callback
  3767. */
  3768. IncomingRequest.prototype.reply = function(code, reason, extraHeaders, body, onSuccess, onFailure) {
  3769. var rr, vias, length, idx, response,
  3770. to = this.getHeader('To'),
  3771. r = 0,
  3772. v = 0;
  3773. response = SIP.Utils.buildStatusLine(code, reason);
  3774. extraHeaders = (extraHeaders || []).slice();
  3775. if(this.method === SIP.C.INVITE && code > 100 && code <= 200) {
  3776. rr = this.getHeaders('record-route');
  3777. length = rr.length;
  3778. for(r; r < length; r++) {
  3779. response += 'Record-Route: ' + rr[r] + '\r\n';
  3780. }
  3781. }
  3782. vias = this.getHeaders('via');
  3783. length = vias.length;
  3784. for(v; v < length; v++) {
  3785. response += 'Via: ' + vias[v] + '\r\n';
  3786. }
  3787. if(!this.to_tag && code > 100) {
  3788. to += ';tag=' + SIP.Utils.newTag();
  3789. } else if(this.to_tag && !this.s('to').hasParam('tag')) {
  3790. to += ';tag=' + this.to_tag;
  3791. }
  3792. response += 'To: ' + to + '\r\n';
  3793. response += 'From: ' + this.getHeader('From') + '\r\n';
  3794. response += 'Call-ID: ' + this.call_id + '\r\n';
  3795. response += 'CSeq: ' + this.cseq + ' ' + this.method + '\r\n';
  3796. length = extraHeaders.length;
  3797. for (idx = 0; idx < length; idx++) {
  3798. response += extraHeaders[idx].trim() +'\r\n';
  3799. }
  3800. response += getSupportedHeader(this);
  3801. response += 'User-Agent: ' + this.ua.configuration.userAgentString +'\r\n';
  3802. if (body) {
  3803. if (typeof body === 'string') {
  3804. length = SIP.Utils.str_utf8_length(body);
  3805. response += 'Content-Type: application/sdp\r\n';
  3806. response += 'Content-Length: ' + length + '\r\n\r\n';
  3807. response += body;
  3808. } else {
  3809. if (body.body && body.contentType) {
  3810. length = SIP.Utils.str_utf8_length(body.body);
  3811. response += 'Content-Type: ' + body.contentType + '\r\n';
  3812. response += 'Content-Length: ' + length + '\r\n\r\n';
  3813. response += body.body;
  3814. } else {
  3815. response += 'Content-Length: ' + 0 + '\r\n\r\n';
  3816. }
  3817. }
  3818. } else {
  3819. response += 'Content-Length: ' + 0 + '\r\n\r\n';
  3820. }
  3821. this.server_transaction.receiveResponse(code, response).then(onSuccess, onFailure);
  3822. return response;
  3823. };
  3824. /**
  3825. * Stateless reply.
  3826. * @param {Number} code status code
  3827. * @param {String} reason reason phrase
  3828. */
  3829. IncomingRequest.prototype.reply_sl = function(code, reason) {
  3830. var to, response,
  3831. v = 0,
  3832. vias = this.getHeaders('via'),
  3833. length = vias.length;
  3834. response = SIP.Utils.buildStatusLine(code, reason);
  3835. for(v; v < length; v++) {
  3836. response += 'Via: ' + vias[v] + '\r\n';
  3837. }
  3838. to = this.getHeader('To');
  3839. if(!this.to_tag && code > 100) {
  3840. to += ';tag=' + SIP.Utils.newTag();
  3841. } else if(this.to_tag && !this.s('to').hasParam('tag')) {
  3842. to += ';tag=' + this.to_tag;
  3843. }
  3844. response += 'To: ' + to + '\r\n';
  3845. response += 'From: ' + this.getHeader('From') + '\r\n';
  3846. response += 'Call-ID: ' + this.call_id + '\r\n';
  3847. response += 'CSeq: ' + this.cseq + ' ' + this.method + '\r\n';
  3848. response += 'User-Agent: ' + this.ua.configuration.userAgentString +'\r\n';
  3849. response += 'Content-Length: ' + 0 + '\r\n\r\n';
  3850. this.transport.send(response);
  3851. };
  3852. /**
  3853. * @augments IncomingMessage
  3854. * @class Class for incoming SIP response.
  3855. */
  3856. IncomingResponse = function(ua) {
  3857. this.logger = ua.getLogger('sip.sipmessage');
  3858. this.headers = {};
  3859. this.status_code = null;
  3860. this.reason_phrase = null;
  3861. };
  3862. IncomingResponse.prototype = new IncomingMessage();
  3863. SIP.OutgoingRequest = OutgoingRequest;
  3864. SIP.IncomingRequest = IncomingRequest;
  3865. SIP.IncomingResponse = IncomingResponse;
  3866. };
  3867. },{}],21:[function(require,module,exports){
  3868. "use strict";
  3869. /**
  3870. * @fileoverview Incoming SIP Message Sanity Check
  3871. */
  3872. /**
  3873. * SIP message sanity check.
  3874. * @augments SIP
  3875. * @function
  3876. * @param {SIP.IncomingMessage} message
  3877. * @param {SIP.UA} ua
  3878. * @param {SIP.Transport} transport
  3879. * @returns {Boolean}
  3880. */
  3881. module.exports = function (SIP) {
  3882. var sanityCheck,
  3883. requests = [],
  3884. responses = [],
  3885. all = [];
  3886. // Reply
  3887. function reply(status_code, message, transport) {
  3888. var to,
  3889. response = SIP.Utils.buildStatusLine(status_code),
  3890. vias = message.getHeaders('via'),
  3891. length = vias.length,
  3892. idx = 0;
  3893. for(idx; idx < length; idx++) {
  3894. response += "Via: " + vias[idx] + "\r\n";
  3895. }
  3896. to = message.getHeader('To');
  3897. if(!message.to_tag) {
  3898. to += ';tag=' + SIP.Utils.newTag();
  3899. }
  3900. response += "To: " + to + "\r\n";
  3901. response += "From: " + message.getHeader('From') + "\r\n";
  3902. response += "Call-ID: " + message.call_id + "\r\n";
  3903. response += "CSeq: " + message.cseq + " " + message.method + "\r\n";
  3904. response += "\r\n";
  3905. transport.send(response);
  3906. }
  3907. /*
  3908. * Sanity Check for incoming Messages
  3909. *
  3910. * Requests:
  3911. * - _rfc3261_8_2_2_1_ Receive a Request with a non supported URI scheme
  3912. * - _rfc3261_16_3_4_ Receive a Request already sent by us
  3913. * Does not look at via sent-by but at sipjsId, which is inserted as
  3914. * a prefix in all initial requests generated by the ua
  3915. * - _rfc3261_18_3_request_ Body Content-Length
  3916. * - _rfc3261_8_2_2_2_ Merged Requests
  3917. *
  3918. * Responses:
  3919. * - _rfc3261_8_1_3_3_ Multiple Via headers
  3920. * - _rfc3261_18_1_2_ sent-by mismatch
  3921. * - _rfc3261_18_3_response_ Body Content-Length
  3922. *
  3923. * All:
  3924. * - Minimum headers in a SIP message
  3925. */
  3926. // Sanity Check functions for requests
  3927. function rfc3261_8_2_2_1(message, ua, transport) {
  3928. if(!message.ruri || message.ruri.scheme !== 'sip') {
  3929. reply(416, message, transport);
  3930. return false;
  3931. }
  3932. }
  3933. function rfc3261_16_3_4(message, ua, transport) {
  3934. if(!message.to_tag) {
  3935. if(message.call_id.substr(0, 5) === ua.configuration.sipjsId) {
  3936. reply(482, message, transport);
  3937. return false;
  3938. }
  3939. }
  3940. }
  3941. function rfc3261_18_3_request(message, ua, transport) {
  3942. var len = SIP.Utils.str_utf8_length(message.body),
  3943. contentLength = message.getHeader('content-length');
  3944. if(len < contentLength) {
  3945. reply(400, message, transport);
  3946. return false;
  3947. }
  3948. }
  3949. function rfc3261_8_2_2_2(message, ua, transport) {
  3950. var tr, idx,
  3951. fromTag = message.from_tag,
  3952. call_id = message.call_id,
  3953. cseq = message.cseq;
  3954. if(!message.to_tag) {
  3955. if(message.method === SIP.C.INVITE) {
  3956. tr = ua.transactions.ist[message.via_branch];
  3957. if(tr) {
  3958. return;
  3959. } else {
  3960. for(idx in ua.transactions.ist) {
  3961. tr = ua.transactions.ist[idx];
  3962. if(tr.request.from_tag === fromTag && tr.request.call_id === call_id && tr.request.cseq === cseq) {
  3963. reply(482, message, transport);
  3964. return false;
  3965. }
  3966. }
  3967. }
  3968. } else {
  3969. tr = ua.transactions.nist[message.via_branch];
  3970. if(tr) {
  3971. return;
  3972. } else {
  3973. for(idx in ua.transactions.nist) {
  3974. tr = ua.transactions.nist[idx];
  3975. if(tr.request.from_tag === fromTag && tr.request.call_id === call_id && tr.request.cseq === cseq) {
  3976. reply(482, message, transport);
  3977. return false;
  3978. }
  3979. }
  3980. }
  3981. }
  3982. }
  3983. }
  3984. // Sanity Check functions for responses
  3985. function rfc3261_8_1_3_3(message, ua) {
  3986. if(message.getHeaders('via').length > 1) {
  3987. ua.getLogger('sip.sanitycheck').warn('More than one Via header field present in the response. Dropping the response');
  3988. return false;
  3989. }
  3990. }
  3991. function rfc3261_18_1_2(message, ua) {
  3992. var viaHost = ua.configuration.viaHost;
  3993. if(message.via.host !== viaHost || message.via.port !== undefined) {
  3994. ua.getLogger('sip.sanitycheck').warn('Via sent-by in the response does not match UA Via host value. Dropping the response');
  3995. return false;
  3996. }
  3997. }
  3998. function rfc3261_18_3_response(message, ua) {
  3999. var
  4000. len = SIP.Utils.str_utf8_length(message.body),
  4001. contentLength = message.getHeader('content-length');
  4002. if(len < contentLength) {
  4003. ua.getLogger('sip.sanitycheck').warn('Message body length is lower than the value in Content-Length header field. Dropping the response');
  4004. return false;
  4005. }
  4006. }
  4007. // Sanity Check functions for requests and responses
  4008. function minimumHeaders(message, ua) {
  4009. var
  4010. mandatoryHeaders = ['from', 'to', 'call_id', 'cseq', 'via'],
  4011. idx = mandatoryHeaders.length;
  4012. while(idx--) {
  4013. if(!message.hasHeader(mandatoryHeaders[idx])) {
  4014. ua.getLogger('sip.sanitycheck').warn('Missing mandatory header field : '+ mandatoryHeaders[idx] +'. Dropping the response');
  4015. return false;
  4016. }
  4017. }
  4018. }
  4019. requests.push(rfc3261_8_2_2_1);
  4020. requests.push(rfc3261_16_3_4);
  4021. requests.push(rfc3261_18_3_request);
  4022. requests.push(rfc3261_8_2_2_2);
  4023. responses.push(rfc3261_8_1_3_3);
  4024. responses.push(rfc3261_18_1_2);
  4025. responses.push(rfc3261_18_3_response);
  4026. all.push(minimumHeaders);
  4027. sanityCheck = function(message, ua, transport) {
  4028. var len, pass;
  4029. len = all.length;
  4030. while(len--) {
  4031. pass = all[len](message, ua, transport);
  4032. if(pass === false) {
  4033. return false;
  4034. }
  4035. }
  4036. if(message instanceof SIP.IncomingRequest) {
  4037. len = requests.length;
  4038. while(len--) {
  4039. pass = requests[len](message, ua, transport);
  4040. if(pass === false) {
  4041. return false;
  4042. }
  4043. }
  4044. }
  4045. else if(message instanceof SIP.IncomingResponse) {
  4046. len = responses.length;
  4047. while(len--) {
  4048. pass = responses[len](message, ua, transport);
  4049. if(pass === false) {
  4050. return false;
  4051. }
  4052. }
  4053. }
  4054. //Everything is OK
  4055. return true;
  4056. };
  4057. SIP.sanityCheck = sanityCheck;
  4058. };
  4059. },{}],22:[function(require,module,exports){
  4060. "use strict";
  4061. module.exports = function (SIP) {
  4062. var ServerContext;
  4063. ServerContext = function (ua, request) {
  4064. this.ua = ua;
  4065. this.logger = ua.getLogger('sip.servercontext');
  4066. this.request = request;
  4067. if (request.method === SIP.C.INVITE) {
  4068. this.transaction = new SIP.Transactions.InviteServerTransaction(request, ua);
  4069. } else {
  4070. this.transaction = new SIP.Transactions.NonInviteServerTransaction(request, ua);
  4071. }
  4072. if (request.body) {
  4073. this.body = request.body;
  4074. }
  4075. if (request.hasHeader('Content-Type')) {
  4076. this.contentType = request.getHeader('Content-Type');
  4077. }
  4078. this.method = request.method;
  4079. this.data = {};
  4080. this.localIdentity = request.to;
  4081. this.remoteIdentity = request.from;
  4082. };
  4083. ServerContext.prototype = Object.create(SIP.EventEmitter.prototype);
  4084. ServerContext.prototype.progress = function (options) {
  4085. options = Object.create(options || Object.prototype);
  4086. options.statusCode || (options.statusCode = 180);
  4087. options.minCode = 100;
  4088. options.maxCode = 199;
  4089. options.events = ['progress'];
  4090. return this.reply(options);
  4091. };
  4092. ServerContext.prototype.accept = function (options) {
  4093. options = Object.create(options || Object.prototype);
  4094. options.statusCode || (options.statusCode = 200);
  4095. options.minCode = 200;
  4096. options.maxCode = 299;
  4097. options.events = ['accepted'];
  4098. return this.reply(options);
  4099. };
  4100. ServerContext.prototype.reject = function (options) {
  4101. options = Object.create(options || Object.prototype);
  4102. options.statusCode || (options.statusCode = 480);
  4103. options.minCode = 300;
  4104. options.maxCode = 699;
  4105. options.events = ['rejected', 'failed'];
  4106. return this.reply(options);
  4107. };
  4108. ServerContext.prototype.reply = function (options) {
  4109. options = options || {}; // This is okay, so long as we treat options as read-only in this method
  4110. var
  4111. statusCode = options.statusCode || 100,
  4112. minCode = options.minCode || 100,
  4113. maxCode = options.maxCode || 699,
  4114. reasonPhrase = SIP.Utils.getReasonPhrase(statusCode, options.reasonPhrase),
  4115. extraHeaders = options.extraHeaders || [],
  4116. body = options.body,
  4117. events = options.events || [],
  4118. response;
  4119. if (statusCode < minCode || statusCode > maxCode) {
  4120. throw new TypeError('Invalid statusCode: ' + statusCode);
  4121. }
  4122. response = this.request.reply(statusCode, reasonPhrase, extraHeaders, body);
  4123. events.forEach(function (event) {
  4124. this.emit(event, response, reasonPhrase);
  4125. }, this);
  4126. return this;
  4127. };
  4128. ServerContext.prototype.onRequestTimeout = function () {
  4129. this.emit('failed', null, SIP.C.causes.REQUEST_TIMEOUT);
  4130. };
  4131. ServerContext.prototype.onTransportError = function () {
  4132. this.emit('failed', null, SIP.C.causes.CONNECTION_ERROR);
  4133. };
  4134. SIP.ServerContext = ServerContext;
  4135. };
  4136. },{}],23:[function(require,module,exports){
  4137. "use strict";
  4138. module.exports = function (SIP, environment) {
  4139. var DTMF = require('./Session/DTMF')(SIP);
  4140. var Session, InviteServerContext, InviteClientContext,
  4141. C = {
  4142. //Session states
  4143. STATUS_NULL: 0,
  4144. STATUS_INVITE_SENT: 1,
  4145. STATUS_1XX_RECEIVED: 2,
  4146. STATUS_INVITE_RECEIVED: 3,
  4147. STATUS_WAITING_FOR_ANSWER: 4,
  4148. STATUS_ANSWERED: 5,
  4149. STATUS_WAITING_FOR_PRACK: 6,
  4150. STATUS_WAITING_FOR_ACK: 7,
  4151. STATUS_CANCELED: 8,
  4152. STATUS_TERMINATED: 9,
  4153. STATUS_ANSWERED_WAITING_FOR_PRACK: 10,
  4154. STATUS_EARLY_MEDIA: 11,
  4155. STATUS_CONFIRMED: 12
  4156. };
  4157. /*
  4158. * @param {function returning SIP.MediaHandler} [mediaHandlerFactory]
  4159. * (See the documentation for the mediaHandlerFactory argument of the UA constructor.)
  4160. */
  4161. Session = function (mediaHandlerFactory) {
  4162. this.status = C.STATUS_NULL;
  4163. this.dialog = null;
  4164. this.earlyDialogs = {};
  4165. this.mediaHandlerFactory = mediaHandlerFactory || SIP.WebRTC.MediaHandler.defaultFactory;
  4166. // this.mediaHandler gets set by ICC/ISC constructors
  4167. this.hasOffer = false;
  4168. this.hasAnswer = false;
  4169. // Session Timers
  4170. this.timers = {
  4171. ackTimer: null,
  4172. expiresTimer: null,
  4173. invite2xxTimer: null,
  4174. userNoAnswerTimer: null,
  4175. rel1xxTimer: null,
  4176. prackTimer: null
  4177. };
  4178. // Session info
  4179. this.startTime = null;
  4180. this.endTime = null;
  4181. this.tones = null;
  4182. // Mute/Hold state
  4183. this.local_hold = false;
  4184. this.remote_hold = false;
  4185. this.pending_actions = {
  4186. actions: [],
  4187. length: function() {
  4188. return this.actions.length;
  4189. },
  4190. isPending: function(name){
  4191. var
  4192. idx = 0,
  4193. length = this.actions.length;
  4194. for (idx; idx<length; idx++) {
  4195. if (this.actions[idx].name === name) {
  4196. return true;
  4197. }
  4198. }
  4199. return false;
  4200. },
  4201. shift: function() {
  4202. return this.actions.shift();
  4203. },
  4204. push: function(name) {
  4205. this.actions.push({
  4206. name: name
  4207. });
  4208. },
  4209. pop: function(name) {
  4210. var
  4211. idx = 0,
  4212. length = this.actions.length;
  4213. for (idx; idx<length; idx++) {
  4214. if (this.actions[idx].name === name) {
  4215. this.actions.splice(idx,1);
  4216. length --;
  4217. idx--;
  4218. }
  4219. }
  4220. }
  4221. };
  4222. this.early_sdp = null;
  4223. this.rel100 = SIP.C.supported.UNSUPPORTED;
  4224. };
  4225. Session.prototype = {
  4226. dtmf: function(tones, options) {
  4227. var tone, dtmfs = [],
  4228. self = this;
  4229. options = options || {};
  4230. if (tones === undefined) {
  4231. throw new TypeError('Not enough arguments');
  4232. }
  4233. // Check Session Status
  4234. if (this.status !== C.STATUS_CONFIRMED && this.status !== C.STATUS_WAITING_FOR_ACK) {
  4235. throw new SIP.Exceptions.InvalidStateError(this.status);
  4236. }
  4237. // Check tones
  4238. if ((typeof tones !== 'string' && typeof tones !== 'number') || !tones.toString().match(/^[0-9A-D#*,]+$/i)) {
  4239. throw new TypeError('Invalid tones: '+ tones);
  4240. }
  4241. tones = tones.toString().split('');
  4242. while (tones.length > 0) { dtmfs.push(new DTMF(this, tones.shift(), options)); }
  4243. if (this.tones) {
  4244. // Tones are already queued, just add to the queue
  4245. this.tones = this.tones.concat(dtmfs);
  4246. return this;
  4247. }
  4248. var sendDTMF = function () {
  4249. var dtmf, timeout;
  4250. if (self.status === C.STATUS_TERMINATED || !self.tones || self.tones.length === 0) {
  4251. // Stop sending DTMF
  4252. self.tones = null;
  4253. return this;
  4254. }
  4255. dtmf = self.tones.shift();
  4256. if (tone === ',') {
  4257. timeout = 2000;
  4258. } else {
  4259. dtmf.on('failed', function(){self.tones = null;});
  4260. dtmf.send(options);
  4261. timeout = dtmf.duration + dtmf.interToneGap;
  4262. }
  4263. // Set timeout for the next tone
  4264. SIP.Timers.setTimeout(sendDTMF, timeout);
  4265. };
  4266. this.tones = dtmfs;
  4267. sendDTMF();
  4268. return this;
  4269. },
  4270. bye: function(options) {
  4271. options = Object.create(options || Object.prototype);
  4272. var statusCode = options.statusCode;
  4273. // Check Session Status
  4274. if (this.status === C.STATUS_TERMINATED) {
  4275. this.logger.error('Error: Attempted to send BYE in a terminated session.');
  4276. return this;
  4277. }
  4278. this.logger.log('terminating Session');
  4279. if (statusCode && (statusCode < 200 || statusCode >= 700)) {
  4280. throw new TypeError('Invalid statusCode: '+ statusCode);
  4281. }
  4282. options.receiveResponse = function () {};
  4283. return this.
  4284. sendRequest(SIP.C.BYE, options).
  4285. terminated();
  4286. },
  4287. refer: function(target, options) {
  4288. options = options || {};
  4289. var extraHeaders = (options.extraHeaders || []).slice(),
  4290. withReplaces =
  4291. target instanceof SIP.InviteServerContext ||
  4292. target instanceof SIP.InviteClientContext,
  4293. originalTarget = target;
  4294. if (target === undefined) {
  4295. throw new TypeError('Not enough arguments');
  4296. }
  4297. // Check Session Status
  4298. if (this.status !== C.STATUS_CONFIRMED) {
  4299. throw new SIP.Exceptions.InvalidStateError(this.status);
  4300. }
  4301. // transform `target` so that it can be a Refer-To header value
  4302. if (withReplaces) {
  4303. //Attended Transfer
  4304. // B.transfer(C)
  4305. target = '"' + target.remoteIdentity.friendlyName + '" ' +
  4306. '<' + target.dialog.remote_target.toString() +
  4307. '?Replaces=' + target.dialog.id.call_id +
  4308. '%3Bto-tag%3D' + target.dialog.id.remote_tag +
  4309. '%3Bfrom-tag%3D' + target.dialog.id.local_tag + '>';
  4310. } else {
  4311. //Blind Transfer
  4312. // normalizeTarget allows instances of SIP.URI to pass through unaltered,
  4313. // so try to make one ahead of time
  4314. try {
  4315. target = SIP.Grammar.parse(target, 'Refer_To').uri || target;
  4316. } catch (e) {
  4317. this.logger.debug(".refer() cannot parse Refer_To from", target);
  4318. this.logger.debug("...falling through to normalizeTarget()");
  4319. }
  4320. // Check target validity
  4321. target = this.ua.normalizeTarget(target);
  4322. if (!target) {
  4323. throw new TypeError('Invalid target: ' + originalTarget);
  4324. }
  4325. }
  4326. extraHeaders.push('Contact: '+ this.contact);
  4327. extraHeaders.push('Allow: '+ SIP.UA.C.ALLOWED_METHODS.toString());
  4328. extraHeaders.push('Refer-To: '+ target);
  4329. // Send the request
  4330. this.sendRequest(SIP.C.REFER, {
  4331. extraHeaders: extraHeaders,
  4332. body: options.body,
  4333. receiveResponse: function (response) {
  4334. if ( ! /^2[0-9]{2}$/.test(response.status_code) ) {
  4335. return;
  4336. }
  4337. // hang up only if we transferred to a SIP address
  4338. if (withReplaces || (target.scheme && target.scheme.match("^sips?$"))) {
  4339. this.terminate();
  4340. }
  4341. }.bind(this)
  4342. });
  4343. return this;
  4344. },
  4345. followRefer: function followRefer (callback) {
  4346. return function referListener (callback, request) {
  4347. // open non-SIP URIs if possible and keep session open
  4348. var referTo = request.parseHeader('refer-to');
  4349. var target = referTo.uri;
  4350. if (!target.scheme.match("^sips?$")) {
  4351. var targetString = target.toString();
  4352. if (typeof environment.open === "function") {
  4353. environment.open(targetString);
  4354. } else {
  4355. this.logger.warn("referred to non-SIP URI but `open` isn't in the environment: " + targetString);
  4356. }
  4357. return;
  4358. }
  4359. var extraHeaders = [];
  4360. /* Copy the Replaces query into a Replaces header */
  4361. /* TODO - make sure we don't copy a poorly formatted header? */
  4362. var replaces = target.getHeader('Replaces');
  4363. if (replaces !== undefined) {
  4364. extraHeaders.push('Replaces: ' + decodeURIComponent(replaces));
  4365. }
  4366. // don't embed headers into Request-URI of INVITE
  4367. target.clearHeaders();
  4368. /*
  4369. Harmless race condition. Both sides of REFER
  4370. may send a BYE, but in the end the dialogs are destroyed.
  4371. */
  4372. var getReferMedia = this.mediaHandler.getReferMedia;
  4373. var mediaHint = getReferMedia ? getReferMedia.call(this.mediaHandler) : this.mediaHint;
  4374. SIP.Hacks.Chrome.getsConfusedAboutGUM(this);
  4375. var referSession = this.ua.invite(target, {
  4376. media: mediaHint,
  4377. params: {
  4378. to_displayName: referTo.friendlyName
  4379. },
  4380. extraHeaders: extraHeaders
  4381. });
  4382. callback.call(this, request, referSession);
  4383. this.terminate();
  4384. }.bind(this, callback);
  4385. },
  4386. sendRequest: function(method,options) {
  4387. options = options || {};
  4388. var self = this;
  4389. var request = new SIP.OutgoingRequest(
  4390. method,
  4391. this.dialog.remote_target,
  4392. this.ua,
  4393. {
  4394. cseq: options.cseq || (this.dialog.local_seqnum += 1),
  4395. call_id: this.dialog.id.call_id,
  4396. from_uri: this.dialog.local_uri,
  4397. from_tag: this.dialog.id.local_tag,
  4398. to_uri: this.dialog.remote_uri,
  4399. to_tag: this.dialog.id.remote_tag,
  4400. route_set: this.dialog.route_set,
  4401. statusCode: options.statusCode,
  4402. reasonPhrase: options.reasonPhrase
  4403. },
  4404. options.extraHeaders || [],
  4405. options.body
  4406. );
  4407. new SIP.RequestSender({
  4408. request: request,
  4409. onRequestTimeout: function() {
  4410. self.onRequestTimeout();
  4411. },
  4412. onTransportError: function() {
  4413. self.onTransportError();
  4414. },
  4415. receiveResponse: options.receiveResponse || function(response) {
  4416. self.receiveNonInviteResponse(response);
  4417. }
  4418. }, this.ua).send();
  4419. // Emit the request event
  4420. this.emit(method.toLowerCase(), request);
  4421. return this;
  4422. },
  4423. close: function() {
  4424. var idx;
  4425. if(this.status === C.STATUS_TERMINATED) {
  4426. return this;
  4427. }
  4428. this.logger.log('closing INVITE session ' + this.id);
  4429. // 1st Step. Terminate media.
  4430. if (this.mediaHandler){
  4431. this.mediaHandler.close();
  4432. }
  4433. // 2nd Step. Terminate signaling.
  4434. // Clear session timers
  4435. for(idx in this.timers) {
  4436. SIP.Timers.clearTimeout(this.timers[idx]);
  4437. }
  4438. // Terminate dialogs
  4439. // Terminate confirmed dialog
  4440. if(this.dialog) {
  4441. this.dialog.terminate();
  4442. delete this.dialog;
  4443. }
  4444. // Terminate early dialogs
  4445. for(idx in this.earlyDialogs) {
  4446. this.earlyDialogs[idx].terminate();
  4447. delete this.earlyDialogs[idx];
  4448. }
  4449. this.status = C.STATUS_TERMINATED;
  4450. delete this.ua.sessions[this.id];
  4451. return this;
  4452. },
  4453. createDialog: function(message, type, early) {
  4454. var dialog, early_dialog,
  4455. local_tag = message[(type === 'UAS') ? 'to_tag' : 'from_tag'],
  4456. remote_tag = message[(type === 'UAS') ? 'from_tag' : 'to_tag'],
  4457. id = message.call_id + local_tag + remote_tag;
  4458. early_dialog = this.earlyDialogs[id];
  4459. // Early Dialog
  4460. if (early) {
  4461. if (early_dialog) {
  4462. return true;
  4463. } else {
  4464. early_dialog = new SIP.Dialog(this, message, type, SIP.Dialog.C.STATUS_EARLY);
  4465. // Dialog has been successfully created.
  4466. if(early_dialog.error) {
  4467. this.logger.error(early_dialog.error);
  4468. this.failed(message, SIP.C.causes.INTERNAL_ERROR);
  4469. return false;
  4470. } else {
  4471. this.earlyDialogs[id] = early_dialog;
  4472. return true;
  4473. }
  4474. }
  4475. }
  4476. // Confirmed Dialog
  4477. else {
  4478. // In case the dialog is in _early_ state, update it
  4479. if (early_dialog) {
  4480. early_dialog.update(message, type);
  4481. this.dialog = early_dialog;
  4482. delete this.earlyDialogs[id];
  4483. for (var dia in this.earlyDialogs) {
  4484. this.earlyDialogs[dia].terminate();
  4485. delete this.earlyDialogs[dia];
  4486. }
  4487. return true;
  4488. }
  4489. // Otherwise, create a _confirmed_ dialog
  4490. dialog = new SIP.Dialog(this, message, type);
  4491. if(dialog.error) {
  4492. this.logger.error(dialog.error);
  4493. this.failed(message, SIP.C.causes.INTERNAL_ERROR);
  4494. return false;
  4495. } else {
  4496. this.to_tag = message.to_tag;
  4497. this.dialog = dialog;
  4498. return true;
  4499. }
  4500. }
  4501. },
  4502. /**
  4503. * Check if Session is ready for a re-INVITE
  4504. *
  4505. * @returns {Boolean}
  4506. */
  4507. isReadyToReinvite: function() {
  4508. return this.mediaHandler.isReady() &&
  4509. !this.dialog.uac_pending_reply &&
  4510. !this.dialog.uas_pending_reply;
  4511. },
  4512. /**
  4513. * Mute
  4514. */
  4515. mute: function(options) {
  4516. var ret = this.mediaHandler.mute(options);
  4517. if (ret) {
  4518. this.onmute(ret);
  4519. }
  4520. },
  4521. /**
  4522. * Unmute
  4523. */
  4524. unmute: function(options) {
  4525. var ret = this.mediaHandler.unmute(options);
  4526. if (ret) {
  4527. this.onunmute(ret);
  4528. }
  4529. },
  4530. /**
  4531. * Hold
  4532. */
  4533. hold: function(options) {
  4534. if (this.status !== C.STATUS_WAITING_FOR_ACK && this.status !== C.STATUS_CONFIRMED) {
  4535. throw new SIP.Exceptions.InvalidStateError(this.status);
  4536. }
  4537. this.mediaHandler.hold();
  4538. // Check if RTCSession is ready to send a reINVITE
  4539. if (!this.isReadyToReinvite()) {
  4540. /* If there is a pending 'unhold' action, cancel it and don't queue this one
  4541. * Else, if there isn't any 'hold' action, add this one to the queue
  4542. * Else, if there is already a 'hold' action, skip
  4543. */
  4544. if (this.pending_actions.isPending('unhold')) {
  4545. this.pending_actions.pop('unhold');
  4546. } else if (!this.pending_actions.isPending('hold')) {
  4547. this.pending_actions.push('hold');
  4548. }
  4549. return;
  4550. } else if (this.local_hold === true) {
  4551. return;
  4552. }
  4553. this.onhold('local');
  4554. this.sendReinvite(options);
  4555. },
  4556. /**
  4557. * Unhold
  4558. */
  4559. unhold: function(options) {
  4560. if (this.status !== C.STATUS_WAITING_FOR_ACK && this.status !== C.STATUS_CONFIRMED) {
  4561. throw new SIP.Exceptions.InvalidStateError(this.status);
  4562. }
  4563. this.mediaHandler.unhold();
  4564. if (!this.isReadyToReinvite()) {
  4565. /* If there is a pending 'hold' action, cancel it and don't queue this one
  4566. * Else, if there isn't any 'unhold' action, add this one to the queue
  4567. * Else, if there is already a 'unhold' action, skip
  4568. */
  4569. if (this.pending_actions.isPending('hold')) {
  4570. this.pending_actions.pop('hold');
  4571. } else if (!this.pending_actions.isPending('unhold')) {
  4572. this.pending_actions.push('unhold');
  4573. }
  4574. return;
  4575. } else if (this.local_hold === false) {
  4576. return;
  4577. }
  4578. this.onunhold('local');
  4579. this.sendReinvite(options);
  4580. },
  4581. /**
  4582. * isOnHold
  4583. */
  4584. isOnHold: function() {
  4585. return {
  4586. local: this.local_hold,
  4587. remote: this.remote_hold
  4588. };
  4589. },
  4590. /**
  4591. * In dialog INVITE Reception
  4592. * @private
  4593. */
  4594. receiveReinvite: function(request) {
  4595. var self = this;
  4596. if (!this.mediaHandler.hasDescription(request)) {
  4597. this.logger.warn('invalid Content-Type');
  4598. request.reply(415);
  4599. return;
  4600. }
  4601. this.mediaHandler.setDescription(request)
  4602. .then(this.mediaHandler.getDescription.bind(this.mediaHandler, this.mediaHint))
  4603. .then(function(description) {
  4604. var extraHeaders = ['Contact: ' + self.contact];
  4605. request.reply(200, null, extraHeaders, description,
  4606. function() {
  4607. self.status = C.STATUS_WAITING_FOR_ACK;
  4608. self.setInvite2xxTimer(request, description);
  4609. self.setACKTimer();
  4610. if (self.remote_hold && !self.mediaHandler.remote_hold) {
  4611. self.onunhold('remote');
  4612. } else if (!self.remote_hold && self.mediaHandler.remote_hold) {
  4613. self.onhold('remote');
  4614. }
  4615. });
  4616. })
  4617. .catch(function onFailure (e) {
  4618. var statusCode;
  4619. if (e instanceof SIP.Exceptions.GetDescriptionError) {
  4620. statusCode = 500;
  4621. } else {
  4622. self.logger.error(e);
  4623. statusCode = 488;
  4624. }
  4625. request.reply(statusCode);
  4626. });
  4627. },
  4628. sendReinvite: function(options) {
  4629. options = options || {};
  4630. var
  4631. self = this,
  4632. extraHeaders = (options.extraHeaders || []).slice(),
  4633. eventHandlers = options.eventHandlers || {},
  4634. succeeded;
  4635. if (eventHandlers.succeeded) {
  4636. succeeded = eventHandlers.succeeded;
  4637. }
  4638. this.reinviteSucceeded = function(){
  4639. SIP.Timers.clearTimeout(self.timers.ackTimer);
  4640. SIP.Timers.clearTimeout(self.timers.invite2xxTimer);
  4641. self.status = C.STATUS_CONFIRMED;
  4642. succeeded && succeeded.apply(this, arguments);
  4643. };
  4644. if (eventHandlers.failed) {
  4645. this.reinviteFailed = eventHandlers.failed;
  4646. } else {
  4647. this.reinviteFailed = function(){};
  4648. }
  4649. extraHeaders.push('Contact: ' + this.contact);
  4650. extraHeaders.push('Allow: '+ SIP.UA.C.ALLOWED_METHODS.toString());
  4651. this.receiveResponse = this.receiveReinviteResponse;
  4652. //REVISIT
  4653. this.mediaHandler.getDescription(self.mediaHint)
  4654. .then(
  4655. function(description){
  4656. self.dialog.sendRequest(self, SIP.C.INVITE, {
  4657. extraHeaders: extraHeaders,
  4658. body: description
  4659. });
  4660. },
  4661. function() {
  4662. if (self.isReadyToReinvite()) {
  4663. self.onReadyToReinvite();
  4664. }
  4665. self.reinviteFailed();
  4666. }
  4667. );
  4668. },
  4669. receiveRequest: function (request) {
  4670. switch (request.method) {
  4671. case SIP.C.BYE:
  4672. request.reply(200);
  4673. if(this.status === C.STATUS_CONFIRMED) {
  4674. this.emit('bye', request);
  4675. this.terminated(request, SIP.C.causes.BYE);
  4676. }
  4677. break;
  4678. case SIP.C.INVITE:
  4679. if(this.status === C.STATUS_CONFIRMED) {
  4680. this.logger.log('re-INVITE received');
  4681. this.receiveReinvite(request);
  4682. }
  4683. break;
  4684. case SIP.C.INFO:
  4685. if(this.status === C.STATUS_CONFIRMED || this.status === C.STATUS_WAITING_FOR_ACK) {
  4686. if (this.onInfo) {
  4687. return this.onInfo(request);
  4688. }
  4689. var body, tone, duration,
  4690. contentType = request.getHeader('content-type'),
  4691. reg_tone = /^(Signal\s*?=\s*?)([0-9A-D#*]{1})(\s)?.*/,
  4692. reg_duration = /^(Duration\s?=\s?)([0-9]{1,4})(\s)?.*/;
  4693. if (contentType) {
  4694. if (contentType.match(/^application\/dtmf-relay/i)) {
  4695. if (request.body) {
  4696. body = request.body.split('\r\n', 2);
  4697. if (body.length === 2) {
  4698. if (reg_tone.test(body[0])) {
  4699. tone = body[0].replace(reg_tone,"$2");
  4700. }
  4701. if (reg_duration.test(body[1])) {
  4702. duration = parseInt(body[1].replace(reg_duration,"$2"), 10);
  4703. }
  4704. }
  4705. }
  4706. new DTMF(this, tone, {duration: duration}).init_incoming(request);
  4707. } else {
  4708. request.reply(415, null, ["Accept: application/dtmf-relay"]);
  4709. }
  4710. }
  4711. }
  4712. break;
  4713. case SIP.C.REFER:
  4714. if(this.status === C.STATUS_CONFIRMED) {
  4715. this.logger.log('REFER received');
  4716. var hasReferListener = this.listeners('refer').length,
  4717. notifyBody;
  4718. if (hasReferListener) {
  4719. request.reply(202, 'Accepted');
  4720. notifyBody = 'SIP/2.0 100 Trying';
  4721. this.sendRequest(SIP.C.NOTIFY, {
  4722. extraHeaders:[
  4723. 'Event: refer',
  4724. 'Subscription-State: terminated',
  4725. 'Content-Type: message/sipfrag'
  4726. ],
  4727. body: notifyBody,
  4728. receiveResponse: function() {}
  4729. });
  4730. this.emit('refer', request);
  4731. } else {
  4732. // RFC 3515.2.4.2: 'the UA MAY decline the request.'
  4733. request.reply(603, 'Declined');
  4734. }
  4735. }
  4736. break;
  4737. case SIP.C.NOTIFY:
  4738. request.reply(200, 'OK');
  4739. this.emit('notify', request);
  4740. break;
  4741. }
  4742. },
  4743. /**
  4744. * Reception of Response for in-dialog INVITE
  4745. * @private
  4746. */
  4747. receiveReinviteResponse: function(response) {
  4748. var self = this;
  4749. if (this.status === C.STATUS_TERMINATED) {
  4750. return;
  4751. }
  4752. switch(true) {
  4753. case /^1[0-9]{2}$/.test(response.status_code):
  4754. break;
  4755. case /^2[0-9]{2}$/.test(response.status_code):
  4756. this.status = C.STATUS_CONFIRMED;
  4757. this.sendRequest(SIP.C.ACK,{cseq:response.cseq});
  4758. if (!this.mediaHandler.hasDescription(response)) {
  4759. this.reinviteFailed();
  4760. break;
  4761. }
  4762. //REVISIT
  4763. this.mediaHandler.setDescription(response)
  4764. .then(
  4765. function onSuccess () {
  4766. self.reinviteSucceeded();
  4767. },
  4768. function onFailure () {
  4769. self.reinviteFailed();
  4770. }
  4771. );
  4772. break;
  4773. default:
  4774. this.reinviteFailed();
  4775. }
  4776. },
  4777. acceptAndTerminate: function(response, status_code, reason_phrase) {
  4778. var extraHeaders = [];
  4779. if (status_code) {
  4780. extraHeaders.push('Reason: ' + SIP.Utils.getReasonHeaderValue(status_code, reason_phrase));
  4781. }
  4782. // An error on dialog creation will fire 'failed' event
  4783. if (this.dialog || this.createDialog(response, 'UAC')) {
  4784. this.sendRequest(SIP.C.ACK,{cseq: response.cseq});
  4785. this.sendRequest(SIP.C.BYE, {
  4786. extraHeaders: extraHeaders
  4787. });
  4788. }
  4789. return this;
  4790. },
  4791. /**
  4792. * RFC3261 13.3.1.4
  4793. * Response retransmissions cannot be accomplished by transaction layer
  4794. * since it is destroyed when receiving the first 2xx answer
  4795. */
  4796. setInvite2xxTimer: function(request, description) {
  4797. var self = this,
  4798. timeout = SIP.Timers.T1;
  4799. this.timers.invite2xxTimer = SIP.Timers.setTimeout(function invite2xxRetransmission() {
  4800. if (self.status !== C.STATUS_WAITING_FOR_ACK) {
  4801. return;
  4802. }
  4803. self.logger.log('no ACK received, attempting to retransmit OK');
  4804. var extraHeaders = ['Contact: ' + self.contact];
  4805. request.reply(200, null, extraHeaders, description);
  4806. timeout = Math.min(timeout * 2, SIP.Timers.T2);
  4807. self.timers.invite2xxTimer = SIP.Timers.setTimeout(invite2xxRetransmission, timeout);
  4808. }, timeout);
  4809. },
  4810. /**
  4811. * RFC3261 14.2
  4812. * If a UAS generates a 2xx response and never receives an ACK,
  4813. * it SHOULD generate a BYE to terminate the dialog.
  4814. */
  4815. setACKTimer: function() {
  4816. var self = this;
  4817. this.timers.ackTimer = SIP.Timers.setTimeout(function() {
  4818. if(self.status === C.STATUS_WAITING_FOR_ACK) {
  4819. self.logger.log('no ACK received for an extended period of time, terminating the call');
  4820. SIP.Timers.clearTimeout(self.timers.invite2xxTimer);
  4821. self.sendRequest(SIP.C.BYE);
  4822. self.terminated(null, SIP.C.causes.NO_ACK);
  4823. }
  4824. }, SIP.Timers.TIMER_H);
  4825. },
  4826. /*
  4827. * @private
  4828. */
  4829. onReadyToReinvite: function() {
  4830. var action = this.pending_actions.shift();
  4831. if (!action || !this[action.name]) {
  4832. return;
  4833. }
  4834. this[action.name]();
  4835. },
  4836. onTransportError: function() {
  4837. if (this.status !== C.STATUS_CONFIRMED && this.status !== C.STATUS_TERMINATED) {
  4838. this.failed(null, SIP.C.causes.CONNECTION_ERROR);
  4839. }
  4840. },
  4841. onRequestTimeout: function() {
  4842. if (this.status === C.STATUS_CONFIRMED) {
  4843. this.terminated(null, SIP.C.causes.REQUEST_TIMEOUT);
  4844. } else if (this.status !== C.STATUS_TERMINATED) {
  4845. this.failed(null, SIP.C.causes.REQUEST_TIMEOUT);
  4846. this.terminated(null, SIP.C.causes.REQUEST_TIMEOUT);
  4847. }
  4848. },
  4849. onDialogError: function(response) {
  4850. if (this.status === C.STATUS_CONFIRMED) {
  4851. this.terminated(response, SIP.C.causes.DIALOG_ERROR);
  4852. } else if (this.status !== C.STATUS_TERMINATED) {
  4853. this.failed(response, SIP.C.causes.DIALOG_ERROR);
  4854. this.terminated(response, SIP.C.causes.DIALOG_ERROR);
  4855. }
  4856. },
  4857. /**
  4858. * @private
  4859. */
  4860. onhold: function(originator) {
  4861. this[originator === 'local' ? 'local_hold' : 'remote_hold'] = true;
  4862. this.emit('hold', { originator: originator });
  4863. },
  4864. /**
  4865. * @private
  4866. */
  4867. onunhold: function(originator) {
  4868. this[originator === 'local' ? 'local_hold' : 'remote_hold'] = false;
  4869. this.emit('unhold', { originator: originator });
  4870. },
  4871. /*
  4872. * @private
  4873. */
  4874. onmute: function(options) {
  4875. this.emit('muted', {
  4876. audio: options.audio,
  4877. video: options.video
  4878. });
  4879. },
  4880. /*
  4881. * @private
  4882. */
  4883. onunmute: function(options) {
  4884. this.emit('unmuted', {
  4885. audio: options.audio,
  4886. video: options.video
  4887. });
  4888. },
  4889. failed: function(response, cause) {
  4890. if (this.status === C.STATUS_TERMINATED) {
  4891. return this;
  4892. }
  4893. this.emit('failed', response || null, cause || null);
  4894. return this;
  4895. },
  4896. rejected: function(response, cause) {
  4897. this.emit('rejected',
  4898. response || null,
  4899. cause || null
  4900. );
  4901. return this;
  4902. },
  4903. canceled: function() {
  4904. this.emit('cancel');
  4905. return this;
  4906. },
  4907. accepted: function(response, cause) {
  4908. cause = SIP.Utils.getReasonPhrase(response && response.status_code, cause);
  4909. this.startTime = new Date();
  4910. if (this.replacee) {
  4911. this.replacee.emit('replaced', this);
  4912. this.replacee.terminate();
  4913. }
  4914. this.emit('accepted', response, cause);
  4915. return this;
  4916. },
  4917. terminated: function(message, cause) {
  4918. if (this.status === C.STATUS_TERMINATED) {
  4919. return this;
  4920. }
  4921. this.endTime = new Date();
  4922. this.close();
  4923. this.emit('terminated',
  4924. message || null,
  4925. cause || null
  4926. );
  4927. return this;
  4928. },
  4929. connecting: function(request) {
  4930. this.emit('connecting', { request: request });
  4931. return this;
  4932. }
  4933. };
  4934. Session.desugar = function desugar(options) {
  4935. if (environment.HTMLMediaElement && options instanceof environment.HTMLMediaElement) {
  4936. options = {
  4937. media: {
  4938. constraints: {
  4939. audio: true,
  4940. video: options.tagName === 'VIDEO'
  4941. },
  4942. render: {
  4943. remote: options
  4944. }
  4945. }
  4946. };
  4947. }
  4948. return options || {};
  4949. };
  4950. Session.C = C;
  4951. SIP.Session = Session;
  4952. InviteServerContext = function(ua, request) {
  4953. var expires,
  4954. self = this,
  4955. contentType = request.getHeader('Content-Type'),
  4956. contentDisp = request.parseHeader('Content-Disposition');
  4957. SIP.Utils.augment(this, SIP.ServerContext, [ua, request]);
  4958. SIP.Utils.augment(this, SIP.Session, [ua.configuration.mediaHandlerFactory]);
  4959. //Initialize Media Session
  4960. this.mediaHandler = this.mediaHandlerFactory(this, {
  4961. RTCConstraints: {"optional": [{'DtlsSrtpKeyAgreement': 'true'}]}
  4962. });
  4963. // Check body and content type
  4964. if ((!contentDisp && !this.mediaHandler.hasDescription(request)) || (contentDisp && contentDisp.type === 'render')) {
  4965. this.renderbody = request.body;
  4966. this.rendertype = contentType;
  4967. } else if (!this.mediaHandler.hasDescription(request) && (contentDisp && contentDisp.type === 'session')) {
  4968. request.reply(415);
  4969. //TODO: instead of 415, pass off to the media handler, who can then decide if we can use it
  4970. return;
  4971. }
  4972. this.status = C.STATUS_INVITE_RECEIVED;
  4973. this.from_tag = request.from_tag;
  4974. this.id = request.call_id + this.from_tag;
  4975. this.request = request;
  4976. this.contact = this.ua.contact.toString();
  4977. this.receiveNonInviteResponse = function () {}; // intentional no-op
  4978. this.logger = ua.getLogger('sip.inviteservercontext', this.id);
  4979. //Save the session into the ua sessions collection.
  4980. this.ua.sessions[this.id] = this;
  4981. //Get the Expires header value if exists
  4982. if(request.hasHeader('expires')) {
  4983. expires = request.getHeader('expires') * 1000;
  4984. }
  4985. //Set 100rel if necessary
  4986. function set100rel(h,c) {
  4987. if (request.hasHeader(h) && request.getHeader(h).toLowerCase().indexOf('100rel') >= 0) {
  4988. self.rel100 = c;
  4989. }
  4990. }
  4991. set100rel('require', SIP.C.supported.REQUIRED);
  4992. set100rel('supported', SIP.C.supported.SUPPORTED);
  4993. /* Set the to_tag before
  4994. * replying a response code that will create a dialog.
  4995. */
  4996. request.to_tag = SIP.Utils.newTag();
  4997. // An error on dialog creation will fire 'failed' event
  4998. if(!this.createDialog(request, 'UAS', true)) {
  4999. request.reply(500, 'Missing Contact header field');
  5000. return;
  5001. }
  5002. if (this.mediaHandler && this.mediaHandler.getRemoteStreams) {
  5003. this.getRemoteStreams = this.mediaHandler.getRemoteStreams.bind(this.mediaHandler);
  5004. this.getLocalStreams = this.mediaHandler.getLocalStreams.bind(this.mediaHandler);
  5005. }
  5006. function fireNewSession() {
  5007. var options = {extraHeaders: ['Contact: ' + self.contact]};
  5008. if (self.rel100 !== SIP.C.supported.REQUIRED) {
  5009. self.progress(options);
  5010. }
  5011. self.status = C.STATUS_WAITING_FOR_ANSWER;
  5012. // Set userNoAnswerTimer
  5013. self.timers.userNoAnswerTimer = SIP.Timers.setTimeout(function() {
  5014. request.reply(408);
  5015. self.failed(request, SIP.C.causes.NO_ANSWER);
  5016. self.terminated(request, SIP.C.causes.NO_ANSWER);
  5017. }, self.ua.configuration.noAnswerTimeout);
  5018. /* Set expiresTimer
  5019. * RFC3261 13.3.1
  5020. */
  5021. if (expires) {
  5022. self.timers.expiresTimer = SIP.Timers.setTimeout(function() {
  5023. if(self.status === C.STATUS_WAITING_FOR_ANSWER) {
  5024. request.reply(487);
  5025. self.failed(request, SIP.C.causes.EXPIRES);
  5026. self.terminated(request, SIP.C.causes.EXPIRES);
  5027. }
  5028. }, expires);
  5029. }
  5030. self.emit('invite',request);
  5031. }
  5032. if (!this.mediaHandler.hasDescription(request) || this.renderbody) {
  5033. SIP.Timers.setTimeout(fireNewSession, 0);
  5034. } else {
  5035. this.hasOffer = true;
  5036. this.mediaHandler.setDescription(request)
  5037. .then(
  5038. fireNewSession,
  5039. function onFailure (e) {
  5040. self.logger.warn('invalid description');
  5041. self.logger.warn(e);
  5042. request.reply(488);
  5043. }
  5044. );
  5045. }
  5046. };
  5047. InviteServerContext.prototype = {
  5048. reject: function(options) {
  5049. // Check Session Status
  5050. if (this.status === C.STATUS_TERMINATED) {
  5051. throw new SIP.Exceptions.InvalidStateError(this.status);
  5052. }
  5053. this.logger.log('rejecting RTCSession');
  5054. SIP.ServerContext.prototype.reject.call(this, options);
  5055. return this.terminated();
  5056. },
  5057. terminate: function(options) {
  5058. options = options || {};
  5059. var
  5060. extraHeaders = (options.extraHeaders || []).slice(),
  5061. body = options.body,
  5062. dialog,
  5063. self = this;
  5064. if (this.status === C.STATUS_WAITING_FOR_ACK &&
  5065. this.request.server_transaction.state !== SIP.Transactions.C.STATUS_TERMINATED) {
  5066. dialog = this.dialog;
  5067. this.receiveRequest = function(request) {
  5068. if (request.method === SIP.C.ACK) {
  5069. this.sendRequest(SIP.C.BYE, {
  5070. extraHeaders: extraHeaders,
  5071. body: body
  5072. });
  5073. dialog.terminate();
  5074. }
  5075. };
  5076. this.request.server_transaction.on('stateChanged', function(){
  5077. if (this.state === SIP.Transactions.C.STATUS_TERMINATED && this.dialog) {
  5078. this.request = new SIP.OutgoingRequest(
  5079. SIP.C.BYE,
  5080. this.dialog.remote_target,
  5081. this.ua,
  5082. {
  5083. 'cseq': this.dialog.local_seqnum+=1,
  5084. 'call_id': this.dialog.id.call_id,
  5085. 'from_uri': this.dialog.local_uri,
  5086. 'from_tag': this.dialog.id.local_tag,
  5087. 'to_uri': this.dialog.remote_uri,
  5088. 'to_tag': this.dialog.id.remote_tag,
  5089. 'route_set': this.dialog.route_set
  5090. },
  5091. extraHeaders,
  5092. body
  5093. );
  5094. new SIP.RequestSender(
  5095. {
  5096. request: this.request,
  5097. onRequestTimeout: function() {
  5098. self.onRequestTimeout();
  5099. },
  5100. onTransportError: function() {
  5101. self.onTransportError();
  5102. },
  5103. receiveResponse: function() {
  5104. return;
  5105. }
  5106. },
  5107. this.ua
  5108. ).send();
  5109. dialog.terminate();
  5110. }
  5111. });
  5112. this.emit('bye', this.request);
  5113. this.terminated();
  5114. // Restore the dialog into 'this' in order to be able to send the in-dialog BYE :-)
  5115. this.dialog = dialog;
  5116. // Restore the dialog into 'ua' so the ACK can reach 'this' session
  5117. this.ua.dialogs[dialog.id.toString()] = dialog;
  5118. } else if (this.status === C.STATUS_CONFIRMED) {
  5119. this.bye(options);
  5120. } else {
  5121. this.reject(options);
  5122. }
  5123. return this;
  5124. },
  5125. /*
  5126. * @param {Object} [options.media] gets passed to SIP.MediaHandler.getDescription as mediaHint
  5127. */
  5128. progress: function (options) {
  5129. options = options || {};
  5130. var
  5131. statusCode = options.statusCode || 180,
  5132. reasonPhrase = options.reasonPhrase,
  5133. extraHeaders = (options.extraHeaders || []).slice(),
  5134. iceServers,
  5135. stunServers = options.stunServers || null,
  5136. turnServers = options.turnServers || null,
  5137. body = options.body,
  5138. response;
  5139. if (statusCode < 100 || statusCode > 199) {
  5140. throw new TypeError('Invalid statusCode: ' + statusCode);
  5141. }
  5142. if (this.isCanceled || this.status === C.STATUS_TERMINATED) {
  5143. return this;
  5144. }
  5145. if (stunServers || turnServers) {
  5146. if (stunServers) {
  5147. iceServers = this.ua.getConfigurationCheck().optional['stunServers'](stunServers);
  5148. if (!iceServers) {
  5149. throw new TypeError('Invalid stunServers: '+ stunServers);
  5150. } else {
  5151. this.stunServers = iceServers;
  5152. }
  5153. }
  5154. if (turnServers) {
  5155. iceServers = this.ua.getConfigurationCheck().optional['turnServers'](turnServers);
  5156. if (!iceServers) {
  5157. throw new TypeError('Invalid turnServers: '+ turnServers);
  5158. } else {
  5159. this.turnServers = iceServers;
  5160. }
  5161. }
  5162. this.mediaHandler.updateIceServers({
  5163. stunServers: this.stunServers,
  5164. turnServers: this.turnServers
  5165. });
  5166. }
  5167. function do100rel() {
  5168. /* jshint validthis: true */
  5169. statusCode = options.statusCode || 183;
  5170. // Set status and add extra headers
  5171. this.status = C.STATUS_WAITING_FOR_PRACK;
  5172. extraHeaders.push('Contact: '+ this.contact);
  5173. extraHeaders.push('Require: 100rel');
  5174. extraHeaders.push('RSeq: ' + Math.floor(Math.random() * 10000));
  5175. // Save media hint for later (referred sessions)
  5176. this.mediaHint = options.media;
  5177. // Get the session description to add to preaccept with
  5178. this.mediaHandler.getDescription(options.media)
  5179. .then(
  5180. function onSuccess (description) {
  5181. if (this.isCanceled || this.status === C.STATUS_TERMINATED) {
  5182. return;
  5183. }
  5184. this.early_sdp = description.body;
  5185. this[this.hasOffer ? 'hasAnswer' : 'hasOffer'] = true;
  5186. // Retransmit until we get a response or we time out (see prackTimer below)
  5187. var timeout = SIP.Timers.T1;
  5188. this.timers.rel1xxTimer = SIP.Timers.setTimeout(function rel1xxRetransmission() {
  5189. this.request.reply(statusCode, null, extraHeaders, description);
  5190. timeout *= 2;
  5191. this.timers.rel1xxTimer = SIP.Timers.setTimeout(rel1xxRetransmission.bind(this), timeout);
  5192. }.bind(this), timeout);
  5193. // Timeout and reject INVITE if no response
  5194. this.timers.prackTimer = SIP.Timers.setTimeout(function () {
  5195. if (this.status !== C.STATUS_WAITING_FOR_PRACK) {
  5196. return;
  5197. }
  5198. this.logger.log('no PRACK received, rejecting the call');
  5199. SIP.Timers.clearTimeout(this.timers.rel1xxTimer);
  5200. this.request.reply(504);
  5201. this.terminated(null, SIP.C.causes.NO_PRACK);
  5202. }.bind(this), SIP.Timers.T1 * 64);
  5203. // Send the initial response
  5204. response = this.request.reply(statusCode, reasonPhrase, extraHeaders, description);
  5205. this.emit('progress', response, reasonPhrase);
  5206. }.bind(this),
  5207. function onFailure () {
  5208. this.request.reply(480);
  5209. this.failed(null, SIP.C.causes.WEBRTC_ERROR);
  5210. this.terminated(null, SIP.C.causes.WEBRTC_ERROR);
  5211. }.bind(this)
  5212. );
  5213. } // end do100rel
  5214. function normalReply() {
  5215. /* jshint validthis:true */
  5216. response = this.request.reply(statusCode, reasonPhrase, extraHeaders, body);
  5217. this.emit('progress', response, reasonPhrase);
  5218. }
  5219. if (options.statusCode !== 100 &&
  5220. (this.rel100 === SIP.C.supported.REQUIRED ||
  5221. (this.rel100 === SIP.C.supported.SUPPORTED && options.rel100) ||
  5222. (this.rel100 === SIP.C.supported.SUPPORTED && (this.ua.configuration.rel100 === SIP.C.supported.REQUIRED)))) {
  5223. do100rel.apply(this);
  5224. } else {
  5225. normalReply.apply(this);
  5226. }
  5227. return this;
  5228. },
  5229. /*
  5230. * @param {Object} [options.media] gets passed to SIP.MediaHandler.getDescription as mediaHint
  5231. */
  5232. accept: function(options) {
  5233. options = Object.create(Session.desugar(options));
  5234. SIP.Utils.optionsOverride(options, 'media', 'mediaConstraints', true, this.logger, this.ua.configuration.media);
  5235. this.mediaHint = options.media;
  5236. this.onInfo = options.onInfo;
  5237. // commented out now-unused hold-related variables for jshint. See below. JMF 2014-1-21
  5238. var
  5239. //idx, length, hasAudio, hasVideo,
  5240. self = this,
  5241. request = this.request,
  5242. extraHeaders = (options.extraHeaders || []).slice(),
  5243. //mediaStream = options.mediaStream || null,
  5244. iceServers,
  5245. stunServers = options.stunServers || null,
  5246. turnServers = options.turnServers || null,
  5247. descriptionCreationSucceeded = function(description) {
  5248. var
  5249. response,
  5250. // run for reply success callback
  5251. replySucceeded = function() {
  5252. self.status = C.STATUS_WAITING_FOR_ACK;
  5253. self.setInvite2xxTimer(request, description);
  5254. self.setACKTimer();
  5255. },
  5256. // run for reply failure callback
  5257. replyFailed = function() {
  5258. self.failed(null, SIP.C.causes.CONNECTION_ERROR);
  5259. self.terminated(null, SIP.C.causes.CONNECTION_ERROR);
  5260. };
  5261. // Chrome might call onaddstream before accept() is called, which means
  5262. // mediaHandler.render() was called without a renderHint, so we need to
  5263. // re-render now that mediaHint.render has been set.
  5264. //
  5265. // Chrome seems to be in the right regarding this, see
  5266. // http://dev.w3.org/2011/webrtc/editor/webrtc.html#widl-RTCPeerConnection-onaddstream
  5267. self.mediaHandler.render();
  5268. extraHeaders.push('Contact: ' + self.contact);
  5269. extraHeaders.push('Allow: ' + SIP.UA.C.ALLOWED_METHODS.toString());
  5270. if(!self.hasOffer) {
  5271. self.hasOffer = true;
  5272. } else {
  5273. self.hasAnswer = true;
  5274. }
  5275. response = request.reply(200, null, extraHeaders,
  5276. description,
  5277. replySucceeded,
  5278. replyFailed
  5279. );
  5280. if (self.status !== C.STATUS_TERMINATED) { // Didn't fail
  5281. self.accepted(response, SIP.Utils.getReasonPhrase(200));
  5282. }
  5283. },
  5284. descriptionCreationFailed = function() {
  5285. if (self.status === C.STATUS_TERMINATED) {
  5286. return;
  5287. }
  5288. // TODO - fail out on error
  5289. self.request.reply(480);
  5290. //self.failed(response, SIP.C.causes.USER_DENIED_MEDIA_ACCESS);
  5291. self.failed(null, SIP.C.causes.WEBRTC_ERROR);
  5292. self.terminated(null, SIP.C.causes.WEBRTC_ERROR);
  5293. };
  5294. // Check Session Status
  5295. if (this.status === C.STATUS_WAITING_FOR_PRACK) {
  5296. this.status = C.STATUS_ANSWERED_WAITING_FOR_PRACK;
  5297. return this;
  5298. } else if (this.status === C.STATUS_WAITING_FOR_ANSWER) {
  5299. this.status = C.STATUS_ANSWERED;
  5300. } else if (this.status !== C.STATUS_EARLY_MEDIA) {
  5301. throw new SIP.Exceptions.InvalidStateError(this.status);
  5302. }
  5303. if ((stunServers || turnServers) &&
  5304. (this.status !== C.STATUS_EARLY_MEDIA && this.status !== C.STATUS_ANSWERED_WAITING_FOR_PRACK)) {
  5305. if (stunServers) {
  5306. iceServers = this.ua.getConfigurationCheck().optional['stunServers'](stunServers);
  5307. if (!iceServers) {
  5308. throw new TypeError('Invalid stunServers: '+ stunServers);
  5309. } else {
  5310. this.stunServers = iceServers;
  5311. }
  5312. }
  5313. if (turnServers) {
  5314. iceServers = this.ua.getConfigurationCheck().optional['turnServers'](turnServers);
  5315. if (!iceServers) {
  5316. throw new TypeError('Invalid turnServers: '+ turnServers);
  5317. } else {
  5318. this.turnServers = iceServers;
  5319. }
  5320. }
  5321. this.mediaHandler.updateIceServers({
  5322. stunServers: this.stunServers,
  5323. turnServers: this.turnServers
  5324. });
  5325. }
  5326. // An error on dialog creation will fire 'failed' event
  5327. if(!this.createDialog(request, 'UAS')) {
  5328. request.reply(500, 'Missing Contact header field');
  5329. return this;
  5330. }
  5331. SIP.Timers.clearTimeout(this.timers.userNoAnswerTimer);
  5332. // this hold-related code breaks FF accepting new calls - JMF 2014-1-21
  5333. /*
  5334. length = this.getRemoteStreams().length;
  5335. for (idx = 0; idx < length; idx++) {
  5336. if (this.mediaHandler.getRemoteStreams()[idx].getVideoTracks().length > 0) {
  5337. hasVideo = true;
  5338. }
  5339. if (this.mediaHandler.getRemoteStreams()[idx].getAudioTracks().length > 0) {
  5340. hasAudio = true;
  5341. }
  5342. }
  5343. if (!hasAudio && this.mediaConstraints.audio === true) {
  5344. this.mediaConstraints.audio = false;
  5345. if (mediaStream) {
  5346. length = mediaStream.getAudioTracks().length;
  5347. for (idx = 0; idx < length; idx++) {
  5348. mediaStream.removeTrack(mediaStream.getAudioTracks()[idx]);
  5349. }
  5350. }
  5351. }
  5352. if (!hasVideo && this.mediaConstraints.video === true) {
  5353. this.mediaConstraints.video = false;
  5354. if (mediaStream) {
  5355. length = mediaStream.getVideoTracks().length;
  5356. for (idx = 0; idx < length; idx++) {
  5357. mediaStream.removeTrack(mediaStream.getVideoTracks()[idx]);
  5358. }
  5359. }
  5360. }
  5361. */
  5362. if (this.status === C.STATUS_EARLY_MEDIA) {
  5363. descriptionCreationSucceeded({});
  5364. } else {
  5365. this.mediaHandler.getDescription(self.mediaHint)
  5366. .then(
  5367. descriptionCreationSucceeded,
  5368. descriptionCreationFailed
  5369. );
  5370. }
  5371. return this;
  5372. },
  5373. receiveRequest: function(request) {
  5374. // ISC RECEIVE REQUEST
  5375. function confirmSession() {
  5376. /* jshint validthis:true */
  5377. var contentType;
  5378. SIP.Timers.clearTimeout(this.timers.ackTimer);
  5379. SIP.Timers.clearTimeout(this.timers.invite2xxTimer);
  5380. this.status = C.STATUS_CONFIRMED;
  5381. this.unmute();
  5382. // TODO - this logic assumes Content-Disposition defaults
  5383. contentType = request.getHeader('Content-Type');
  5384. if (!this.mediaHandler.hasDescription(request)) {
  5385. this.renderbody = request.body;
  5386. this.rendertype = contentType;
  5387. }
  5388. this.emit('confirmed', request);
  5389. }
  5390. switch(request.method) {
  5391. case SIP.C.CANCEL:
  5392. /* RFC3261 15 States that a UAS may have accepted an invitation while a CANCEL
  5393. * was in progress and that the UAC MAY continue with the session established by
  5394. * any 2xx response, or MAY terminate with BYE. SIP does continue with the
  5395. * established session. So the CANCEL is processed only if the session is not yet
  5396. * established.
  5397. */
  5398. /*
  5399. * Terminate the whole session in case the user didn't accept (or yet to send the answer) nor reject the
  5400. *request opening the session.
  5401. */
  5402. if(this.status === C.STATUS_WAITING_FOR_ANSWER ||
  5403. this.status === C.STATUS_WAITING_FOR_PRACK ||
  5404. this.status === C.STATUS_ANSWERED_WAITING_FOR_PRACK ||
  5405. this.status === C.STATUS_EARLY_MEDIA ||
  5406. this.status === C.STATUS_ANSWERED) {
  5407. this.status = C.STATUS_CANCELED;
  5408. this.request.reply(487);
  5409. this.canceled(request);
  5410. this.rejected(request, SIP.C.causes.CANCELED);
  5411. this.failed(request, SIP.C.causes.CANCELED);
  5412. this.terminated(request, SIP.C.causes.CANCELED);
  5413. }
  5414. break;
  5415. case SIP.C.ACK:
  5416. if(this.status === C.STATUS_WAITING_FOR_ACK) {
  5417. if (!this.hasAnswer) {
  5418. if(this.mediaHandler.hasDescription(request)) {
  5419. // ACK contains answer to an INVITE w/o SDP negotiation
  5420. this.hasAnswer = true;
  5421. this.mediaHandler.setDescription(request)
  5422. .then(
  5423. confirmSession.bind(this),
  5424. function onFailure (e) {
  5425. this.logger.warn(e);
  5426. this.terminate({
  5427. statusCode: '488',
  5428. reasonPhrase: 'Bad Media Description'
  5429. });
  5430. this.failed(request, SIP.C.causes.BAD_MEDIA_DESCRIPTION);
  5431. this.terminated(request, SIP.C.causes.BAD_MEDIA_DESCRIPTION);
  5432. }.bind(this)
  5433. );
  5434. } else if (this.early_sdp) {
  5435. confirmSession.apply(this);
  5436. } else {
  5437. //TODO: Pass to mediahandler
  5438. this.failed(request, SIP.C.causes.BAD_MEDIA_DESCRIPTION);
  5439. this.terminated(request, SIP.C.causes.BAD_MEDIA_DESCRIPTION);
  5440. }
  5441. } else {
  5442. confirmSession.apply(this);
  5443. }
  5444. }
  5445. break;
  5446. case SIP.C.PRACK:
  5447. if (this.status === C.STATUS_WAITING_FOR_PRACK || this.status === C.STATUS_ANSWERED_WAITING_FOR_PRACK) {
  5448. //localMedia = session.mediaHandler.localMedia;
  5449. if(!this.hasAnswer) {
  5450. if(this.mediaHandler.hasDescription(request)) {
  5451. this.hasAnswer = true;
  5452. this.mediaHandler.setDescription(request)
  5453. .then(
  5454. function onSuccess () {
  5455. SIP.Timers.clearTimeout(this.timers.rel1xxTimer);
  5456. SIP.Timers.clearTimeout(this.timers.prackTimer);
  5457. request.reply(200);
  5458. if (this.status === C.STATUS_ANSWERED_WAITING_FOR_PRACK) {
  5459. this.status = C.STATUS_EARLY_MEDIA;
  5460. this.accept();
  5461. }
  5462. this.status = C.STATUS_EARLY_MEDIA;
  5463. //REVISIT
  5464. this.mute();
  5465. }.bind(this),
  5466. function onFailure (e) {
  5467. //TODO: Send to media handler
  5468. this.logger.warn(e);
  5469. this.terminate({
  5470. statusCode: '488',
  5471. reasonPhrase: 'Bad Media Description'
  5472. });
  5473. this.failed(request, SIP.C.causes.BAD_MEDIA_DESCRIPTION);
  5474. this.terminated(request, SIP.C.causes.BAD_MEDIA_DESCRIPTION);
  5475. }.bind(this)
  5476. );
  5477. } else {
  5478. this.terminate({
  5479. statusCode: '488',
  5480. reasonPhrase: 'Bad Media Description'
  5481. });
  5482. this.failed(request, SIP.C.causes.BAD_MEDIA_DESCRIPTION);
  5483. this.terminated(request, SIP.C.causes.BAD_MEDIA_DESCRIPTION);
  5484. }
  5485. } else {
  5486. SIP.Timers.clearTimeout(this.timers.rel1xxTimer);
  5487. SIP.Timers.clearTimeout(this.timers.prackTimer);
  5488. request.reply(200);
  5489. if (this.status === C.STATUS_ANSWERED_WAITING_FOR_PRACK) {
  5490. this.status = C.STATUS_EARLY_MEDIA;
  5491. this.accept();
  5492. }
  5493. this.status = C.STATUS_EARLY_MEDIA;
  5494. //REVISIT
  5495. this.mute();
  5496. }
  5497. } else if(this.status === C.STATUS_EARLY_MEDIA) {
  5498. request.reply(200);
  5499. }
  5500. break;
  5501. default:
  5502. Session.prototype.receiveRequest.apply(this, [request]);
  5503. break;
  5504. }
  5505. },
  5506. onTransportError: function() {
  5507. if (this.status !== C.STATUS_CONFIRMED && this.status !== C.STATUS_TERMINATED) {
  5508. this.failed(null, SIP.C.causes.CONNECTION_ERROR);
  5509. }
  5510. },
  5511. onRequestTimeout: function() {
  5512. if (this.status === C.STATUS_CONFIRMED) {
  5513. this.terminated(null, SIP.C.causes.REQUEST_TIMEOUT);
  5514. } else if (this.status !== C.STATUS_TERMINATED) {
  5515. this.failed(null, SIP.C.causes.REQUEST_TIMEOUT);
  5516. this.terminated(null, SIP.C.causes.REQUEST_TIMEOUT);
  5517. }
  5518. }
  5519. };
  5520. SIP.InviteServerContext = InviteServerContext;
  5521. InviteClientContext = function(ua, target, options) {
  5522. options = Object.create(Session.desugar(options));
  5523. options.params = Object.create(options.params || Object.prototype);
  5524. var iceServers,
  5525. extraHeaders = (options.extraHeaders || []).slice(),
  5526. stunServers = options.stunServers || null,
  5527. turnServers = options.turnServers || null,
  5528. mediaHandlerFactory = options.mediaHandlerFactory || ua.configuration.mediaHandlerFactory,
  5529. isMediaSupported = mediaHandlerFactory.isSupported;
  5530. // Check WebRTC support
  5531. if (isMediaSupported && !isMediaSupported()) {
  5532. throw new SIP.Exceptions.NotSupportedError('Media not supported');
  5533. }
  5534. this.RTCConstraints = options.RTCConstraints || {};
  5535. this.inviteWithoutSdp = options.inviteWithoutSdp || false;
  5536. // Set anonymous property
  5537. this.anonymous = options.anonymous || false;
  5538. // Custom data to be sent either in INVITE or in ACK
  5539. this.renderbody = options.renderbody || null;
  5540. this.rendertype = options.rendertype || 'text/plain';
  5541. options.params.from_tag = this.from_tag;
  5542. /* Do not add ;ob in initial forming dialog requests if the registration over
  5543. * the current connection got a GRUU URI.
  5544. */
  5545. this.contact = ua.contact.toString({
  5546. anonymous: this.anonymous,
  5547. outbound: this.anonymous ? !ua.contact.temp_gruu : !ua.contact.pub_gruu
  5548. });
  5549. if (this.anonymous) {
  5550. options.params.from_displayName = 'Anonymous';
  5551. options.params.from_uri = 'sip:anonymous@anonymous.invalid';
  5552. extraHeaders.push('P-Preferred-Identity: '+ ua.configuration.uri.toString());
  5553. extraHeaders.push('Privacy: id');
  5554. }
  5555. extraHeaders.push('Contact: '+ this.contact);
  5556. extraHeaders.push('Allow: '+ SIP.UA.C.ALLOWED_METHODS.toString());
  5557. if (this.inviteWithoutSdp && this.renderbody) {
  5558. extraHeaders.push('Content-Type: ' + this.rendertype);
  5559. extraHeaders.push('Content-Disposition: render;handling=optional');
  5560. }
  5561. if (ua.configuration.rel100 === SIP.C.supported.REQUIRED) {
  5562. extraHeaders.push('Require: 100rel');
  5563. }
  5564. if (ua.configuration.replaces === SIP.C.supported.REQUIRED) {
  5565. extraHeaders.push('Require: replaces');
  5566. }
  5567. options.extraHeaders = extraHeaders;
  5568. SIP.Utils.augment(this, SIP.ClientContext, [ua, SIP.C.INVITE, target, options]);
  5569. SIP.Utils.augment(this, SIP.Session, [mediaHandlerFactory]);
  5570. // Check Session Status
  5571. if (this.status !== C.STATUS_NULL) {
  5572. throw new SIP.Exceptions.InvalidStateError(this.status);
  5573. }
  5574. // Session parameter initialization
  5575. this.from_tag = SIP.Utils.newTag();
  5576. // OutgoingSession specific parameters
  5577. this.isCanceled = false;
  5578. this.received_100 = false;
  5579. this.method = SIP.C.INVITE;
  5580. this.receiveNonInviteResponse = this.receiveResponse;
  5581. this.receiveResponse = this.receiveInviteResponse;
  5582. this.logger = ua.getLogger('sip.inviteclientcontext');
  5583. if (stunServers) {
  5584. iceServers = this.ua.getConfigurationCheck().optional['stunServers'](stunServers);
  5585. if (!iceServers) {
  5586. throw new TypeError('Invalid stunServers: '+ stunServers);
  5587. } else {
  5588. this.stunServers = iceServers;
  5589. }
  5590. }
  5591. if (turnServers) {
  5592. iceServers = this.ua.getConfigurationCheck().optional['turnServers'](turnServers);
  5593. if (!iceServers) {
  5594. throw new TypeError('Invalid turnServers: '+ turnServers);
  5595. } else {
  5596. this.turnServers = iceServers;
  5597. }
  5598. }
  5599. ua.applicants[this] = this;
  5600. this.id = this.request.call_id + this.from_tag;
  5601. //Initialize Media Session
  5602. this.mediaHandler = this.mediaHandlerFactory(this, {
  5603. RTCConstraints: this.RTCConstraints,
  5604. stunServers: this.stunServers,
  5605. turnServers: this.turnServers
  5606. });
  5607. if (this.mediaHandler && this.mediaHandler.getRemoteStreams) {
  5608. this.getRemoteStreams = this.mediaHandler.getRemoteStreams.bind(this.mediaHandler);
  5609. this.getLocalStreams = this.mediaHandler.getLocalStreams.bind(this.mediaHandler);
  5610. }
  5611. SIP.Utils.optionsOverride(options, 'media', 'mediaConstraints', true, this.logger, this.ua.configuration.media);
  5612. this.mediaHint = options.media;
  5613. this.onInfo = options.onInfo;
  5614. };
  5615. InviteClientContext.prototype = {
  5616. invite: function () {
  5617. var self = this;
  5618. //Save the session into the ua sessions collection.
  5619. //Note: placing in constructor breaks call to request.cancel on close... User does not need this anyway
  5620. this.ua.sessions[this.id] = this;
  5621. //Note: due to the way Firefox handles gUM calls, it is recommended to make the gUM call at the app level
  5622. // and hand sip.js a stream as the mediaHint
  5623. if (this.inviteWithoutSdp) {
  5624. //just send an invite with no sdp...
  5625. this.request.body = self.renderbody;
  5626. this.status = C.STATUS_INVITE_SENT;
  5627. this.send();
  5628. } else {
  5629. this.mediaHandler.getDescription(self.mediaHint)
  5630. .then(
  5631. function onSuccess(description) {
  5632. if (self.isCanceled || self.status === C.STATUS_TERMINATED) {
  5633. return;
  5634. }
  5635. self.hasOffer = true;
  5636. self.request.body = description;
  5637. self.status = C.STATUS_INVITE_SENT;
  5638. self.send();
  5639. },
  5640. function onFailure() {
  5641. if (self.status === C.STATUS_TERMINATED) {
  5642. return;
  5643. }
  5644. // TODO...fail out
  5645. //self.failed(null, SIP.C.causes.USER_DENIED_MEDIA_ACCESS);
  5646. //self.failed(null, SIP.C.causes.WEBRTC_ERROR);
  5647. self.failed(null, SIP.C.causes.WEBRTC_ERROR);
  5648. self.terminated(null, SIP.C.causes.WEBRTC_ERROR);
  5649. }
  5650. );
  5651. }
  5652. return this;
  5653. },
  5654. receiveInviteResponse: function(response) {
  5655. var cause, //localMedia,
  5656. session = this,
  5657. id = response.call_id + response.from_tag + response.to_tag,
  5658. extraHeaders = [],
  5659. options = {};
  5660. if (this.status === C.STATUS_TERMINATED || response.method !== SIP.C.INVITE) {
  5661. return;
  5662. }
  5663. if (this.dialog && (response.status_code >= 200 && response.status_code <= 299)) {
  5664. if (id !== this.dialog.id.toString() ) {
  5665. if (!this.createDialog(response, 'UAC', true)) {
  5666. return;
  5667. }
  5668. this.earlyDialogs[id].sendRequest(this, SIP.C.ACK,
  5669. {
  5670. body: SIP.Utils.generateFakeSDP(response.body)
  5671. });
  5672. this.earlyDialogs[id].sendRequest(this, SIP.C.BYE);
  5673. /* NOTE: This fails because the forking proxy does not recognize that an unanswerable
  5674. * leg (due to peerConnection limitations) has been answered first. If your forking
  5675. * proxy does not hang up all unanswered branches on the first branch answered, remove this.
  5676. */
  5677. if(this.status !== C.STATUS_CONFIRMED) {
  5678. this.failed(response, SIP.C.causes.WEBRTC_ERROR);
  5679. this.terminated(response, SIP.C.causes.WEBRTC_ERROR);
  5680. }
  5681. return;
  5682. } else if (this.status === C.STATUS_CONFIRMED) {
  5683. this.sendRequest(SIP.C.ACK,{cseq: response.cseq});
  5684. return;
  5685. } else if (!this.hasAnswer) {
  5686. // invite w/o sdp is waiting for callback
  5687. //an invite with sdp must go on, and hasAnswer is true
  5688. return;
  5689. }
  5690. }
  5691. if (this.dialog && response.status_code < 200) {
  5692. /*
  5693. Early media has been set up with at least one other different branch,
  5694. but a final 2xx response hasn't been received
  5695. */
  5696. if (this.dialog.pracked.indexOf(response.getHeader('rseq')) !== -1 ||
  5697. (this.dialog.pracked[this.dialog.pracked.length-1] >= response.getHeader('rseq') && this.dialog.pracked.length > 0)) {
  5698. return;
  5699. }
  5700. if (!this.earlyDialogs[id] && !this.createDialog(response, 'UAC', true)) {
  5701. return;
  5702. }
  5703. if (this.earlyDialogs[id].pracked.indexOf(response.getHeader('rseq')) !== -1 ||
  5704. (this.earlyDialogs[id].pracked[this.earlyDialogs[id].pracked.length-1] >= response.getHeader('rseq') && this.earlyDialogs[id].pracked.length > 0)) {
  5705. return;
  5706. }
  5707. extraHeaders.push('RAck: ' + response.getHeader('rseq') + ' ' + response.getHeader('cseq'));
  5708. this.earlyDialogs[id].pracked.push(response.getHeader('rseq'));
  5709. this.earlyDialogs[id].sendRequest(this, SIP.C.PRACK, {
  5710. extraHeaders: extraHeaders,
  5711. body: SIP.Utils.generateFakeSDP(response.body)
  5712. });
  5713. return;
  5714. }
  5715. // Proceed to cancellation if the user requested.
  5716. if(this.isCanceled) {
  5717. if(response.status_code >= 100 && response.status_code < 200) {
  5718. this.request.cancel(this.cancelReason, extraHeaders);
  5719. this.canceled(null);
  5720. } else if(response.status_code >= 200 && response.status_code < 299) {
  5721. this.acceptAndTerminate(response);
  5722. this.emit('bye', this.request);
  5723. } else if (response.status_code >= 300) {
  5724. cause = SIP.C.REASON_PHRASE[response.status_code] || SIP.C.causes.CANCELED;
  5725. this.rejected(response, cause);
  5726. this.failed(response, cause);
  5727. this.terminated(response, cause);
  5728. }
  5729. return;
  5730. }
  5731. switch(true) {
  5732. case /^100$/.test(response.status_code):
  5733. this.received_100 = true;
  5734. this.emit('progress', response);
  5735. break;
  5736. case (/^1[0-9]{2}$/.test(response.status_code)):
  5737. // Do nothing with 1xx responses without To tag.
  5738. if(!response.to_tag) {
  5739. this.logger.warn('1xx response received without to tag');
  5740. break;
  5741. }
  5742. // Create Early Dialog if 1XX comes with contact
  5743. if(response.hasHeader('contact')) {
  5744. // An error on dialog creation will fire 'failed' event
  5745. if (!this.createDialog(response, 'UAC', true)) {
  5746. break;
  5747. }
  5748. }
  5749. this.status = C.STATUS_1XX_RECEIVED;
  5750. if(response.hasHeader('require') &&
  5751. response.getHeader('require').indexOf('100rel') !== -1) {
  5752. // Do nothing if this.dialog is already confirmed
  5753. if (this.dialog || !this.earlyDialogs[id]) {
  5754. break;
  5755. }
  5756. if (this.earlyDialogs[id].pracked.indexOf(response.getHeader('rseq')) !== -1 ||
  5757. (this.earlyDialogs[id].pracked[this.earlyDialogs[id].pracked.length-1] >= response.getHeader('rseq') && this.earlyDialogs[id].pracked.length > 0)) {
  5758. return;
  5759. }
  5760. if (!this.mediaHandler.hasDescription(response)) {
  5761. extraHeaders.push('RAck: ' + response.getHeader('rseq') + ' ' + response.getHeader('cseq'));
  5762. this.earlyDialogs[id].pracked.push(response.getHeader('rseq'));
  5763. this.earlyDialogs[id].sendRequest(this, SIP.C.PRACK, {
  5764. extraHeaders: extraHeaders
  5765. });
  5766. this.emit('progress', response);
  5767. } else if (this.hasOffer) {
  5768. if (!this.createDialog(response, 'UAC')) {
  5769. break;
  5770. }
  5771. this.hasAnswer = true;
  5772. this.dialog.pracked.push(response.getHeader('rseq'));
  5773. this.mediaHandler.setDescription(response)
  5774. .then(
  5775. function onSuccess () {
  5776. extraHeaders.push('RAck: ' + response.getHeader('rseq') + ' ' + response.getHeader('cseq'));
  5777. session.sendRequest(SIP.C.PRACK, {
  5778. extraHeaders: extraHeaders,
  5779. receiveResponse: function() {}
  5780. });
  5781. session.status = C.STATUS_EARLY_MEDIA;
  5782. session.mute();
  5783. session.emit('progress', response);
  5784. /*
  5785. if (session.status === C.STATUS_EARLY_MEDIA) {
  5786. localMedia = session.mediaHandler.localMedia;
  5787. if (localMedia.getAudioTracks().length > 0) {
  5788. localMedia.getAudioTracks()[0].enabled = false;
  5789. }
  5790. if (localMedia.getVideoTracks().length > 0) {
  5791. localMedia.getVideoTracks()[0].enabled = false;
  5792. }
  5793. }*/
  5794. },
  5795. function onFailure (e) {
  5796. session.logger.warn(e);
  5797. session.acceptAndTerminate(response, 488, 'Not Acceptable Here');
  5798. session.failed(response, SIP.C.causes.BAD_MEDIA_DESCRIPTION);
  5799. }
  5800. );
  5801. } else {
  5802. var earlyDialog = this.earlyDialogs[id];
  5803. var earlyMedia = earlyDialog.mediaHandler;
  5804. earlyDialog.pracked.push(response.getHeader('rseq'));
  5805. earlyMedia.setDescription(response)
  5806. .then(earlyMedia.getDescription.bind(earlyMedia, session.mediaHint))
  5807. .then(function onSuccess(description) {
  5808. extraHeaders.push('RAck: ' + response.getHeader('rseq') + ' ' + response.getHeader('cseq'));
  5809. earlyDialog.sendRequest(session, SIP.C.PRACK, {
  5810. extraHeaders: extraHeaders,
  5811. body: description
  5812. });
  5813. session.status = C.STATUS_EARLY_MEDIA;
  5814. session.emit('progress', response);
  5815. })
  5816. .catch(function onFailure(e) {
  5817. if (e instanceof SIP.Exceptions.GetDescriptionError) {
  5818. earlyDialog.pracked.push(response.getHeader('rseq'));
  5819. if (session.status === C.STATUS_TERMINATED) {
  5820. return;
  5821. }
  5822. // TODO - fail out on error
  5823. // session.failed(gum error);
  5824. session.failed(null, SIP.C.causes.WEBRTC_ERROR);
  5825. session.terminated(null, SIP.C.causes.WEBRTC_ERROR);
  5826. } else {
  5827. earlyDialog.pracked.splice(earlyDialog.pracked.indexOf(response.getHeader('rseq')), 1);
  5828. // Could not set remote description
  5829. session.logger.warn('invalid description');
  5830. session.logger.warn(e);
  5831. }
  5832. });
  5833. }
  5834. } else {
  5835. this.emit('progress', response);
  5836. }
  5837. break;
  5838. case /^2[0-9]{2}$/.test(response.status_code):
  5839. var cseq = this.request.cseq + ' ' + this.request.method;
  5840. if (cseq !== response.getHeader('cseq')) {
  5841. break;
  5842. }
  5843. if (this.status === C.STATUS_EARLY_MEDIA && this.dialog) {
  5844. this.status = C.STATUS_CONFIRMED;
  5845. this.unmute();
  5846. /*localMedia = this.mediaHandler.localMedia;
  5847. if (localMedia.getAudioTracks().length > 0) {
  5848. localMedia.getAudioTracks()[0].enabled = true;
  5849. }
  5850. if (localMedia.getVideoTracks().length > 0) {
  5851. localMedia.getVideoTracks()[0].enabled = true;
  5852. }*/
  5853. options = {};
  5854. if (this.renderbody) {
  5855. extraHeaders.push('Content-Type: ' + this.rendertype);
  5856. options.extraHeaders = extraHeaders;
  5857. options.body = this.renderbody;
  5858. }
  5859. options.cseq = response.cseq;
  5860. this.sendRequest(SIP.C.ACK, options);
  5861. this.accepted(response);
  5862. break;
  5863. }
  5864. // Do nothing if this.dialog is already confirmed
  5865. if (this.dialog) {
  5866. break;
  5867. }
  5868. // This is an invite without sdp
  5869. if (!this.hasOffer) {
  5870. if (this.earlyDialogs[id] && this.earlyDialogs[id].mediaHandler.localMedia) {
  5871. //REVISIT
  5872. this.hasOffer = true;
  5873. this.hasAnswer = true;
  5874. this.mediaHandler = this.earlyDialogs[id].mediaHandler;
  5875. if (!this.createDialog(response, 'UAC')) {
  5876. break;
  5877. }
  5878. this.status = C.STATUS_CONFIRMED;
  5879. this.sendRequest(SIP.C.ACK, {cseq:response.cseq});
  5880. this.unmute();
  5881. /*
  5882. localMedia = session.mediaHandler.localMedia;
  5883. if (localMedia.getAudioTracks().length > 0) {
  5884. localMedia.getAudioTracks()[0].enabled = true;
  5885. }
  5886. if (localMedia.getVideoTracks().length > 0) {
  5887. localMedia.getVideoTracks()[0].enabled = true;
  5888. }*/
  5889. this.accepted(response);
  5890. } else {
  5891. if(!this.mediaHandler.hasDescription(response)) {
  5892. this.acceptAndTerminate(response, 400, 'Missing session description');
  5893. this.failed(response, SIP.C.causes.BAD_MEDIA_DESCRIPTION);
  5894. break;
  5895. }
  5896. if (!this.createDialog(response, 'UAC')) {
  5897. break;
  5898. }
  5899. this.hasOffer = true;
  5900. this.mediaHandler.setDescription(response)
  5901. .then(this.mediaHandler.getDescription.bind(this.mediaHandler, this.mediaHint))
  5902. .then(function onSuccess(description) {
  5903. //var localMedia;
  5904. if(session.isCanceled || session.status === C.STATUS_TERMINATED) {
  5905. return;
  5906. }
  5907. session.status = C.STATUS_CONFIRMED;
  5908. session.hasAnswer = true;
  5909. session.unmute();
  5910. /*localMedia = session.mediaHandler.localMedia;
  5911. if (localMedia.getAudioTracks().length > 0) {
  5912. localMedia.getAudioTracks()[0].enabled = true;
  5913. }
  5914. if (localMedia.getVideoTracks().length > 0) {
  5915. localMedia.getVideoTracks()[0].enabled = true;
  5916. }*/
  5917. session.sendRequest(SIP.C.ACK,{
  5918. body: description,
  5919. cseq:response.cseq
  5920. });
  5921. session.accepted(response);
  5922. })
  5923. .catch(function onFailure(e) {
  5924. if (e instanceof SIP.Exceptions.GetDescriptionError) {
  5925. // TODO do something here
  5926. session.logger.warn("there was a problem");
  5927. } else {
  5928. session.logger.warn('invalid description');
  5929. session.logger.warn(e);
  5930. session.acceptAndTerminate(response, 488, 'Invalid session description');
  5931. session.failed(response, SIP.C.causes.BAD_MEDIA_DESCRIPTION);
  5932. }
  5933. });
  5934. }
  5935. } else if (this.hasAnswer){
  5936. if (this.renderbody) {
  5937. extraHeaders.push('Content-Type: ' + session.rendertype);
  5938. options.extraHeaders = extraHeaders;
  5939. options.body = this.renderbody;
  5940. }
  5941. this.sendRequest(SIP.C.ACK, options);
  5942. } else {
  5943. if(!this.mediaHandler.hasDescription(response)) {
  5944. this.acceptAndTerminate(response, 400, 'Missing session description');
  5945. this.failed(response, SIP.C.causes.BAD_MEDIA_DESCRIPTION);
  5946. break;
  5947. }
  5948. if (!this.createDialog(response, 'UAC')) {
  5949. break;
  5950. }
  5951. this.hasAnswer = true;
  5952. this.mediaHandler.setDescription(response)
  5953. .then(
  5954. function onSuccess () {
  5955. var options = {};//,localMedia;
  5956. session.status = C.STATUS_CONFIRMED;
  5957. session.unmute();
  5958. /*localMedia = session.mediaHandler.localMedia;
  5959. if (localMedia.getAudioTracks().length > 0) {
  5960. localMedia.getAudioTracks()[0].enabled = true;
  5961. }
  5962. if (localMedia.getVideoTracks().length > 0) {
  5963. localMedia.getVideoTracks()[0].enabled = true;
  5964. }*/
  5965. if (session.renderbody) {
  5966. extraHeaders.push('Content-Type: ' + session.rendertype);
  5967. options.extraHeaders = extraHeaders;
  5968. options.body = session.renderbody;
  5969. }
  5970. options.cseq = response.cseq;
  5971. session.sendRequest(SIP.C.ACK, options);
  5972. session.accepted(response);
  5973. },
  5974. function onFailure (e) {
  5975. session.logger.warn(e);
  5976. session.acceptAndTerminate(response, 488, 'Not Acceptable Here');
  5977. session.failed(response, SIP.C.causes.BAD_MEDIA_DESCRIPTION);
  5978. }
  5979. );
  5980. }
  5981. break;
  5982. default:
  5983. cause = SIP.Utils.sipErrorCause(response.status_code);
  5984. this.rejected(response, cause);
  5985. this.failed(response, cause);
  5986. this.terminated(response, cause);
  5987. }
  5988. },
  5989. cancel: function(options) {
  5990. options = options || {};
  5991. options.extraHeaders = (options.extraHeaders || []).slice();
  5992. // Check Session Status
  5993. if (this.status === C.STATUS_TERMINATED || this.status === C.STATUS_CONFIRMED) {
  5994. throw new SIP.Exceptions.InvalidStateError(this.status);
  5995. }
  5996. this.logger.log('canceling RTCSession');
  5997. var cancel_reason = SIP.Utils.getCancelReason(options.status_code, options.reason_phrase);
  5998. // Check Session Status
  5999. if (this.status === C.STATUS_NULL ||
  6000. (this.status === C.STATUS_INVITE_SENT && !this.received_100)) {
  6001. this.isCanceled = true;
  6002. this.cancelReason = cancel_reason;
  6003. } else if (this.status === C.STATUS_INVITE_SENT ||
  6004. this.status === C.STATUS_1XX_RECEIVED ||
  6005. this.status === C.STATUS_EARLY_MEDIA) {
  6006. this.request.cancel(cancel_reason, options.extraHeaders);
  6007. }
  6008. return this.canceled();
  6009. },
  6010. terminate: function(options) {
  6011. if (this.status === C.STATUS_TERMINATED) {
  6012. return this;
  6013. }
  6014. if (this.status === C.STATUS_WAITING_FOR_ACK || this.status === C.STATUS_CONFIRMED) {
  6015. this.bye(options);
  6016. } else {
  6017. this.cancel(options);
  6018. }
  6019. return this;
  6020. },
  6021. receiveRequest: function(request) {
  6022. // ICC RECEIVE REQUEST
  6023. // Reject CANCELs
  6024. if (request.method === SIP.C.CANCEL) {
  6025. // TODO; make this a switch when it gets added
  6026. }
  6027. if (request.method === SIP.C.ACK && this.status === C.STATUS_WAITING_FOR_ACK) {
  6028. SIP.Timers.clearTimeout(this.timers.ackTimer);
  6029. SIP.Timers.clearTimeout(this.timers.invite2xxTimer);
  6030. this.status = C.STATUS_CONFIRMED;
  6031. this.unmute();
  6032. this.accepted();
  6033. }
  6034. return Session.prototype.receiveRequest.apply(this, [request]);
  6035. },
  6036. onTransportError: function() {
  6037. if (this.status !== C.STATUS_CONFIRMED && this.status !== C.STATUS_TERMINATED) {
  6038. this.failed(null, SIP.C.causes.CONNECTION_ERROR);
  6039. }
  6040. },
  6041. onRequestTimeout: function() {
  6042. if (this.status === C.STATUS_CONFIRMED) {
  6043. this.terminated(null, SIP.C.causes.REQUEST_TIMEOUT);
  6044. } else if (this.status !== C.STATUS_TERMINATED) {
  6045. this.failed(null, SIP.C.causes.REQUEST_TIMEOUT);
  6046. this.terminated(null, SIP.C.causes.REQUEST_TIMEOUT);
  6047. }
  6048. }
  6049. };
  6050. SIP.InviteClientContext = InviteClientContext;
  6051. };
  6052. },{"./Session/DTMF":24}],24:[function(require,module,exports){
  6053. "use strict";
  6054. /**
  6055. * @fileoverview DTMF
  6056. */
  6057. /**
  6058. * @class DTMF
  6059. * @param {SIP.Session} session
  6060. */
  6061. module.exports = function (SIP) {
  6062. var DTMF,
  6063. C = {
  6064. MIN_DURATION: 70,
  6065. MAX_DURATION: 6000,
  6066. DEFAULT_DURATION: 100,
  6067. MIN_INTER_TONE_GAP: 50,
  6068. DEFAULT_INTER_TONE_GAP: 500
  6069. };
  6070. DTMF = function(session, tone, options) {
  6071. var duration, interToneGap;
  6072. if (tone === undefined) {
  6073. throw new TypeError('Not enough arguments');
  6074. }
  6075. this.logger = session.ua.getLogger('sip.invitecontext.dtmf', session.id);
  6076. this.owner = session;
  6077. this.direction = null;
  6078. options = options || {};
  6079. duration = options.duration || null;
  6080. interToneGap = options.interToneGap || null;
  6081. // Check tone type
  6082. if (typeof tone === 'string' ) {
  6083. tone = tone.toUpperCase();
  6084. } else if (typeof tone === 'number') {
  6085. tone = tone.toString();
  6086. } else {
  6087. throw new TypeError('Invalid tone: '+ tone);
  6088. }
  6089. // Check tone value
  6090. if (!tone.match(/^[0-9A-D#*]$/)) {
  6091. throw new TypeError('Invalid tone: '+ tone);
  6092. } else {
  6093. this.tone = tone;
  6094. }
  6095. // Check duration
  6096. if (duration && !SIP.Utils.isDecimal(duration)) {
  6097. throw new TypeError('Invalid tone duration: '+ duration);
  6098. } else if (!duration) {
  6099. duration = DTMF.C.DEFAULT_DURATION;
  6100. } else if (duration < DTMF.C.MIN_DURATION) {
  6101. this.logger.warn('"duration" value is lower than the minimum allowed, setting it to '+ DTMF.C.MIN_DURATION+ ' milliseconds');
  6102. duration = DTMF.C.MIN_DURATION;
  6103. } else if (duration > DTMF.C.MAX_DURATION) {
  6104. this.logger.warn('"duration" value is greater than the maximum allowed, setting it to '+ DTMF.C.MAX_DURATION +' milliseconds');
  6105. duration = DTMF.C.MAX_DURATION;
  6106. } else {
  6107. duration = Math.abs(duration);
  6108. }
  6109. this.duration = duration;
  6110. // Check interToneGap
  6111. if (interToneGap && !SIP.Utils.isDecimal(interToneGap)) {
  6112. throw new TypeError('Invalid interToneGap: '+ interToneGap);
  6113. } else if (!interToneGap) {
  6114. interToneGap = DTMF.C.DEFAULT_INTER_TONE_GAP;
  6115. } else if (interToneGap < DTMF.C.MIN_INTER_TONE_GAP) {
  6116. this.logger.warn('"interToneGap" value is lower than the minimum allowed, setting it to '+ DTMF.C.MIN_INTER_TONE_GAP +' milliseconds');
  6117. interToneGap = DTMF.C.MIN_INTER_TONE_GAP;
  6118. } else {
  6119. interToneGap = Math.abs(interToneGap);
  6120. }
  6121. this.interToneGap = interToneGap;
  6122. };
  6123. DTMF.prototype = Object.create(SIP.EventEmitter.prototype);
  6124. DTMF.prototype.send = function(options) {
  6125. var extraHeaders,
  6126. body = {};
  6127. this.direction = 'outgoing';
  6128. // Check RTCSession Status
  6129. if (this.owner.status !== SIP.Session.C.STATUS_CONFIRMED &&
  6130. this.owner.status !== SIP.Session.C.STATUS_WAITING_FOR_ACK) {
  6131. throw new SIP.Exceptions.InvalidStateError(this.owner.status);
  6132. }
  6133. // Get DTMF options
  6134. options = options || {};
  6135. extraHeaders = options.extraHeaders ? options.extraHeaders.slice() : [];
  6136. body.contentType = 'application/dtmf-relay';
  6137. body.body = "Signal= " + this.tone + "\r\n";
  6138. body.body += "Duration= " + this.duration;
  6139. this.request = this.owner.dialog.sendRequest(this, SIP.C.INFO, {
  6140. extraHeaders: extraHeaders,
  6141. body: body
  6142. });
  6143. this.owner.emit('dtmf', this.request, this);
  6144. };
  6145. /**
  6146. * @private
  6147. */
  6148. DTMF.prototype.receiveResponse = function(response) {
  6149. var cause;
  6150. switch(true) {
  6151. case /^1[0-9]{2}$/.test(response.status_code):
  6152. // Ignore provisional responses.
  6153. break;
  6154. case /^2[0-9]{2}$/.test(response.status_code):
  6155. this.emit('succeeded', {
  6156. originator: 'remote',
  6157. response: response
  6158. });
  6159. break;
  6160. default:
  6161. cause = SIP.Utils.sipErrorCause(response.status_code);
  6162. this.emit('failed', response, cause);
  6163. break;
  6164. }
  6165. };
  6166. /**
  6167. * @private
  6168. */
  6169. DTMF.prototype.onRequestTimeout = function() {
  6170. this.emit('failed', null, SIP.C.causes.REQUEST_TIMEOUT);
  6171. this.owner.onRequestTimeout();
  6172. };
  6173. /**
  6174. * @private
  6175. */
  6176. DTMF.prototype.onTransportError = function() {
  6177. this.emit('failed', null, SIP.C.causes.CONNECTION_ERROR);
  6178. this.owner.onTransportError();
  6179. };
  6180. /**
  6181. * @private
  6182. */
  6183. DTMF.prototype.onDialogError = function(response) {
  6184. this.emit('failed', response, SIP.C.causes.DIALOG_ERROR);
  6185. this.owner.onDialogError(response);
  6186. };
  6187. /**
  6188. * @private
  6189. */
  6190. DTMF.prototype.init_incoming = function(request) {
  6191. this.direction = 'incoming';
  6192. this.request = request;
  6193. request.reply(200);
  6194. if (!this.tone || !this.duration) {
  6195. this.logger.warn('invalid INFO DTMF received, discarded');
  6196. } else {
  6197. this.owner.emit('dtmf', request, this);
  6198. }
  6199. };
  6200. DTMF.C = C;
  6201. return DTMF;
  6202. };
  6203. },{}],25:[function(require,module,exports){
  6204. "use strict";
  6205. /**
  6206. * @fileoverview SIP Subscriber (SIP-Specific Event Notifications RFC6665)
  6207. */
  6208. /**
  6209. * @augments SIP
  6210. * @class Class creating a SIP Subscription.
  6211. */
  6212. module.exports = function (SIP) {
  6213. SIP.Subscription = function (ua, target, event, options) {
  6214. options = Object.create(options || Object.prototype);
  6215. this.extraHeaders = options.extraHeaders = (options.extraHeaders || []).slice();
  6216. this.id = null;
  6217. this.state = 'init';
  6218. if (!event) {
  6219. throw new TypeError('Event necessary to create a subscription.');
  6220. } else {
  6221. //TODO: check for valid events here probably make a list in SIP.C; or leave it up to app to check?
  6222. //The check may need to/should probably occur on the other side,
  6223. this.event = event;
  6224. }
  6225. if(typeof options.expires !== 'number'){
  6226. ua.logger.warn('expires must be a number. Using default of 3600.');
  6227. this.expires = 3600;
  6228. } else {
  6229. this.expires = options.expires;
  6230. }
  6231. options.extraHeaders.push('Event: ' + this.event);
  6232. options.extraHeaders.push('Expires: ' + this.expires);
  6233. if (options.body) {
  6234. this.body = options.body;
  6235. }
  6236. this.contact = ua.contact.toString();
  6237. options.extraHeaders.push('Contact: '+ this.contact);
  6238. options.extraHeaders.push('Allow: '+ SIP.UA.C.ALLOWED_METHODS.toString());
  6239. SIP.Utils.augment(this, SIP.ClientContext, [ua, SIP.C.SUBSCRIBE, target, options]);
  6240. this.logger = ua.getLogger('sip.subscription');
  6241. this.dialog = null;
  6242. this.timers = {N: null, sub_duration: null};
  6243. this.errorCodes = [404,405,410,416,480,481,482,483,484,485,489,501,604];
  6244. };
  6245. SIP.Subscription.prototype = {
  6246. subscribe: function() {
  6247. var sub = this;
  6248. //these states point to an existing subscription, no subscribe is necessary
  6249. if (this.state === 'active') {
  6250. this.refresh();
  6251. return this;
  6252. } else if (this.state === 'notify_wait') {
  6253. return this;
  6254. }
  6255. SIP.Timers.clearTimeout(this.timers.sub_duration);
  6256. SIP.Timers.clearTimeout(this.timers.N);
  6257. this.timers.N = SIP.Timers.setTimeout(sub.timer_fire.bind(sub), SIP.Timers.TIMER_N);
  6258. this.ua.earlySubscriptions[this.request.call_id + this.request.from.parameters.tag + this.event] = this;
  6259. this.send();
  6260. this.state = 'notify_wait';
  6261. return this;
  6262. },
  6263. refresh: function () {
  6264. if (this.state === 'terminated' || this.state === 'pending' || this.state === 'notify_wait') {
  6265. return;
  6266. }
  6267. this.dialog.sendRequest(this, SIP.C.SUBSCRIBE, {
  6268. extraHeaders: this.extraHeaders,
  6269. body: this.body
  6270. });
  6271. },
  6272. receiveResponse: function(response) {
  6273. var expires, sub = this,
  6274. cause = SIP.Utils.getReasonPhrase(response.status_code);
  6275. if ((this.state === 'notify_wait' && response.status_code >= 300) ||
  6276. (this.state !== 'notify_wait' && this.errorCodes.indexOf(response.status_code) !== -1)) {
  6277. this.failed(response, null);
  6278. } else if (/^2[0-9]{2}$/.test(response.status_code)){
  6279. this.emit('accepted', response, cause);
  6280. //As we don't support RFC 5839 or other extensions where the NOTIFY is optional, timer N will not be cleared
  6281. //SIP.Timers.clearTimeout(this.timers.N);
  6282. expires = response.getHeader('Expires');
  6283. if (expires && expires <= this.expires) {
  6284. // Preserve new expires value for subsequent requests
  6285. this.expires = expires;
  6286. this.timers.sub_duration = SIP.Timers.setTimeout(sub.refresh.bind(sub), expires * 900);
  6287. } else {
  6288. if (!expires) {
  6289. this.logger.warn('Expires header missing in a 200-class response to SUBSCRIBE');
  6290. this.failed(response, SIP.C.EXPIRES_HEADER_MISSING);
  6291. } else {
  6292. this.logger.warn('Expires header in a 200-class response to SUBSCRIBE with a higher value than the one in the request');
  6293. this.failed(response, SIP.C.INVALID_EXPIRES_HEADER);
  6294. }
  6295. }
  6296. } else if (response.statusCode > 300) {
  6297. this.emit('failed', response, cause);
  6298. this.emit('rejected', response, cause);
  6299. }
  6300. },
  6301. unsubscribe: function() {
  6302. var extraHeaders = [], sub = this;
  6303. this.state = 'terminated';
  6304. extraHeaders.push('Event: ' + this.event);
  6305. extraHeaders.push('Expires: 0');
  6306. extraHeaders.push('Contact: '+ this.contact);
  6307. extraHeaders.push('Allow: '+ SIP.UA.C.ALLOWED_METHODS.toString());
  6308. //makes sure expires isn't set, and other typical resubscribe behavior
  6309. this.receiveResponse = function(){};
  6310. this.dialog.sendRequest(this, this.method, {
  6311. extraHeaders: extraHeaders,
  6312. body: this.body
  6313. });
  6314. SIP.Timers.clearTimeout(this.timers.sub_duration);
  6315. SIP.Timers.clearTimeout(this.timers.N);
  6316. this.timers.N = SIP.Timers.setTimeout(sub.timer_fire.bind(sub), SIP.Timers.TIMER_N);
  6317. },
  6318. /**
  6319. * @private
  6320. */
  6321. timer_fire: function(){
  6322. if (this.state === 'terminated') {
  6323. this.terminateDialog();
  6324. SIP.Timers.clearTimeout(this.timers.N);
  6325. SIP.Timers.clearTimeout(this.timers.sub_duration);
  6326. delete this.ua.subscriptions[this.id];
  6327. } else if (this.state === 'notify_wait' || this.state === 'pending') {
  6328. this.close();
  6329. } else {
  6330. this.refresh();
  6331. }
  6332. },
  6333. /**
  6334. * @private
  6335. */
  6336. close: function() {
  6337. if (this.state === 'notify_wait') {
  6338. this.state = 'terminated';
  6339. SIP.Timers.clearTimeout(this.timers.N);
  6340. SIP.Timers.clearTimeout(this.timers.sub_duration);
  6341. this.receiveResponse = function(){};
  6342. delete this.ua.earlySubscriptions[this.request.call_id + this.request.from.parameters.tag + this.event];
  6343. } else if (this.state !== 'terminated') {
  6344. this.unsubscribe();
  6345. }
  6346. },
  6347. /**
  6348. * @private
  6349. */
  6350. createConfirmedDialog: function(message, type) {
  6351. var dialog;
  6352. this.terminateDialog();
  6353. dialog = new SIP.Dialog(this, message, type);
  6354. dialog.invite_seqnum = this.request.cseq;
  6355. dialog.local_seqnum = this.request.cseq;
  6356. if(!dialog.error) {
  6357. this.dialog = dialog;
  6358. return true;
  6359. }
  6360. // Dialog not created due to an error
  6361. else {
  6362. return false;
  6363. }
  6364. },
  6365. /**
  6366. * @private
  6367. */
  6368. terminateDialog: function() {
  6369. if(this.dialog) {
  6370. delete this.ua.subscriptions[this.id];
  6371. this.dialog.terminate();
  6372. delete this.dialog;
  6373. }
  6374. },
  6375. /**
  6376. * @private
  6377. */
  6378. receiveRequest: function(request) {
  6379. var sub_state, sub = this;
  6380. function setExpiresTimeout() {
  6381. if (sub_state.expires) {
  6382. SIP.Timers.clearTimeout(sub.timers.sub_duration);
  6383. sub_state.expires = Math.min(sub.expires,
  6384. Math.max(sub_state.expires, 0));
  6385. sub.timers.sub_duration = SIP.Timers.setTimeout(sub.refresh.bind(sub),
  6386. sub_state.expires * 900);
  6387. }
  6388. }
  6389. if (!this.matchEvent(request)) { //checks event and subscription_state headers
  6390. request.reply(489);
  6391. return;
  6392. }
  6393. if (!this.dialog) {
  6394. if (this.createConfirmedDialog(request,'UAS')) {
  6395. this.id = this.dialog.id.toString();
  6396. delete this.ua.earlySubscriptions[this.request.call_id + this.request.from.parameters.tag + this.event];
  6397. this.ua.subscriptions[this.id] = this;
  6398. // UPDATE ROUTE SET TO BE BACKWARDS COMPATIBLE?
  6399. }
  6400. }
  6401. sub_state = request.parseHeader('Subscription-State');
  6402. request.reply(200, SIP.C.REASON_200);
  6403. SIP.Timers.clearTimeout(this.timers.N);
  6404. this.emit('notify', {request: request});
  6405. // if we've set state to terminated, no further processing should take place
  6406. // and we are only interested in cleaning up after the appropriate NOTIFY
  6407. if (this.state === 'terminated') {
  6408. if (sub_state.state === 'terminated') {
  6409. this.terminateDialog();
  6410. SIP.Timers.clearTimeout(this.timers.N);
  6411. SIP.Timers.clearTimeout(this.timers.sub_duration);
  6412. delete this.ua.subscriptions[this.id];
  6413. }
  6414. return;
  6415. }
  6416. switch (sub_state.state) {
  6417. case 'active':
  6418. this.state = 'active';
  6419. setExpiresTimeout();
  6420. break;
  6421. case 'pending':
  6422. if (this.state === 'notify_wait') {
  6423. setExpiresTimeout();
  6424. }
  6425. this.state = 'pending';
  6426. break;
  6427. case 'terminated':
  6428. SIP.Timers.clearTimeout(this.timers.sub_duration);
  6429. if (sub_state.reason) {
  6430. this.logger.log('terminating subscription with reason '+ sub_state.reason);
  6431. switch (sub_state.reason) {
  6432. case 'deactivated':
  6433. case 'timeout':
  6434. this.subscribe();
  6435. return;
  6436. case 'probation':
  6437. case 'giveup':
  6438. if(sub_state.params && sub_state.params['retry-after']) {
  6439. this.timers.sub_duration = SIP.Timers.setTimeout(sub.subscribe.bind(sub), sub_state.params['retry-after']);
  6440. } else {
  6441. this.subscribe();
  6442. }
  6443. return;
  6444. case 'rejected':
  6445. case 'noresource':
  6446. case 'invariant':
  6447. break;
  6448. }
  6449. }
  6450. this.close();
  6451. break;
  6452. }
  6453. },
  6454. failed: function(response, cause) {
  6455. this.close();
  6456. this.emit('failed', response, cause);
  6457. this.emit('rejected', response, cause);
  6458. return this;
  6459. },
  6460. onDialogError: function(response) {
  6461. this.failed(response, SIP.C.causes.DIALOG_ERROR);
  6462. },
  6463. /**
  6464. * @private
  6465. */
  6466. matchEvent: function(request) {
  6467. var event;
  6468. // Check mandatory header Event
  6469. if (!request.hasHeader('Event')) {
  6470. this.logger.warn('missing Event header');
  6471. return false;
  6472. }
  6473. // Check mandatory header Subscription-State
  6474. if (!request.hasHeader('Subscription-State')) {
  6475. this.logger.warn('missing Subscription-State header');
  6476. return false;
  6477. }
  6478. // Check whether the event in NOTIFY matches the event in SUBSCRIBE
  6479. event = request.parseHeader('event').event;
  6480. if (this.event !== event) {
  6481. this.logger.warn('event match failed');
  6482. request.reply(481, 'Event Match Failed');
  6483. return false;
  6484. } else {
  6485. return true;
  6486. }
  6487. }
  6488. };
  6489. };
  6490. },{}],26:[function(require,module,exports){
  6491. "use strict";
  6492. /**
  6493. * @fileoverview SIP TIMERS
  6494. */
  6495. /**
  6496. * @augments SIP
  6497. */
  6498. var
  6499. T1 = 500,
  6500. T2 = 4000,
  6501. T4 = 5000;
  6502. module.exports = function (timers) {
  6503. var Timers = {
  6504. T1: T1,
  6505. T2: T2,
  6506. T4: T4,
  6507. TIMER_B: 64 * T1,
  6508. TIMER_D: 0 * T1,
  6509. TIMER_F: 64 * T1,
  6510. TIMER_H: 64 * T1,
  6511. TIMER_I: 0 * T1,
  6512. TIMER_J: 0 * T1,
  6513. TIMER_K: 0 * T4,
  6514. TIMER_L: 64 * T1,
  6515. TIMER_M: 64 * T1,
  6516. TIMER_N: 64 * T1,
  6517. PROVISIONAL_RESPONSE_INTERVAL: 60000 // See RFC 3261 Section 13.3.1.1
  6518. };
  6519. ['setTimeout', 'clearTimeout', 'setInterval', 'clearInterval']
  6520. .forEach(function (name) {
  6521. // can't just use timers[name].bind(timers) since it bypasses jasmine's
  6522. // clock-mocking
  6523. Timers[name] = function () {
  6524. return timers[name].apply(timers, arguments);
  6525. };
  6526. });
  6527. return Timers;
  6528. };
  6529. },{}],27:[function(require,module,exports){
  6530. "use strict";
  6531. /**
  6532. * @fileoverview SIP Transactions
  6533. */
  6534. /**
  6535. * SIP Transactions module.
  6536. * @augments SIP
  6537. */
  6538. module.exports = function (SIP) {
  6539. var
  6540. C = {
  6541. // Transaction states
  6542. STATUS_TRYING: 1,
  6543. STATUS_PROCEEDING: 2,
  6544. STATUS_CALLING: 3,
  6545. STATUS_ACCEPTED: 4,
  6546. STATUS_COMPLETED: 5,
  6547. STATUS_TERMINATED: 6,
  6548. STATUS_CONFIRMED: 7,
  6549. // Transaction types
  6550. NON_INVITE_CLIENT: 'nict',
  6551. NON_INVITE_SERVER: 'nist',
  6552. INVITE_CLIENT: 'ict',
  6553. INVITE_SERVER: 'ist'
  6554. };
  6555. function buildViaHeader (request_sender, transport, id) {
  6556. var via;
  6557. via = 'SIP/2.0/' + (request_sender.ua.configuration.hackViaTcp ? 'TCP' : transport.server.scheme);
  6558. via += ' ' + request_sender.ua.configuration.viaHost + ';branch=' + id;
  6559. if (request_sender.ua.configuration.forceRport) {
  6560. via += ';rport';
  6561. }
  6562. return via;
  6563. }
  6564. /**
  6565. * @augments SIP.Transactions
  6566. * @class Non Invite Client Transaction
  6567. * @param {SIP.RequestSender} request_sender
  6568. * @param {SIP.OutgoingRequest} request
  6569. * @param {SIP.Transport} transport
  6570. */
  6571. var NonInviteClientTransaction = function(request_sender, request, transport) {
  6572. var via;
  6573. this.type = C.NON_INVITE_CLIENT;
  6574. this.transport = transport;
  6575. this.id = 'z9hG4bK' + Math.floor(Math.random() * 10000000);
  6576. this.request_sender = request_sender;
  6577. this.request = request;
  6578. this.logger = request_sender.ua.getLogger('sip.transaction.nict', this.id);
  6579. via = buildViaHeader(request_sender, transport, this.id);
  6580. this.request.setHeader('via', via);
  6581. this.request_sender.ua.newTransaction(this);
  6582. };
  6583. NonInviteClientTransaction.prototype = Object.create(SIP.EventEmitter.prototype);
  6584. NonInviteClientTransaction.prototype.stateChanged = function(state) {
  6585. this.state = state;
  6586. this.emit('stateChanged');
  6587. };
  6588. NonInviteClientTransaction.prototype.send = function() {
  6589. var tr = this;
  6590. this.stateChanged(C.STATUS_TRYING);
  6591. this.F = SIP.Timers.setTimeout(tr.timer_F.bind(tr), SIP.Timers.TIMER_F);
  6592. if(!this.transport.send(this.request)) {
  6593. this.onTransportError();
  6594. }
  6595. };
  6596. NonInviteClientTransaction.prototype.onTransportError = function() {
  6597. this.logger.log('transport error occurred, deleting non-INVITE client transaction ' + this.id);
  6598. SIP.Timers.clearTimeout(this.F);
  6599. SIP.Timers.clearTimeout(this.K);
  6600. this.stateChanged(C.STATUS_TERMINATED);
  6601. this.request_sender.ua.destroyTransaction(this);
  6602. this.request_sender.onTransportError();
  6603. };
  6604. NonInviteClientTransaction.prototype.timer_F = function() {
  6605. this.logger.log('Timer F expired for non-INVITE client transaction ' + this.id);
  6606. this.stateChanged(C.STATUS_TERMINATED);
  6607. this.request_sender.ua.destroyTransaction(this);
  6608. this.request_sender.onRequestTimeout();
  6609. };
  6610. NonInviteClientTransaction.prototype.timer_K = function() {
  6611. this.stateChanged(C.STATUS_TERMINATED);
  6612. this.request_sender.ua.destroyTransaction(this);
  6613. };
  6614. NonInviteClientTransaction.prototype.receiveResponse = function(response) {
  6615. var
  6616. tr = this,
  6617. status_code = response.status_code;
  6618. if(status_code < 200) {
  6619. switch(this.state) {
  6620. case C.STATUS_TRYING:
  6621. case C.STATUS_PROCEEDING:
  6622. this.stateChanged(C.STATUS_PROCEEDING);
  6623. this.request_sender.receiveResponse(response);
  6624. break;
  6625. }
  6626. } else {
  6627. switch(this.state) {
  6628. case C.STATUS_TRYING:
  6629. case C.STATUS_PROCEEDING:
  6630. this.stateChanged(C.STATUS_COMPLETED);
  6631. SIP.Timers.clearTimeout(this.F);
  6632. if(status_code === 408) {
  6633. this.request_sender.onRequestTimeout();
  6634. } else {
  6635. this.request_sender.receiveResponse(response);
  6636. }
  6637. this.K = SIP.Timers.setTimeout(tr.timer_K.bind(tr), SIP.Timers.TIMER_K);
  6638. break;
  6639. case C.STATUS_COMPLETED:
  6640. break;
  6641. }
  6642. }
  6643. };
  6644. /**
  6645. * @augments SIP.Transactions
  6646. * @class Invite Client Transaction
  6647. * @param {SIP.RequestSender} request_sender
  6648. * @param {SIP.OutgoingRequest} request
  6649. * @param {SIP.Transport} transport
  6650. */
  6651. var InviteClientTransaction = function(request_sender, request, transport) {
  6652. var via,
  6653. tr = this;
  6654. this.type = C.INVITE_CLIENT;
  6655. this.transport = transport;
  6656. this.id = 'z9hG4bK' + Math.floor(Math.random() * 10000000);
  6657. this.request_sender = request_sender;
  6658. this.request = request;
  6659. this.logger = request_sender.ua.getLogger('sip.transaction.ict', this.id);
  6660. via = buildViaHeader(request_sender, transport, this.id);
  6661. this.request.setHeader('via', via);
  6662. this.request_sender.ua.newTransaction(this);
  6663. // Add the cancel property to the request.
  6664. //Will be called from the request instance, not the transaction itself.
  6665. this.request.cancel = function(reason, extraHeaders) {
  6666. extraHeaders = (extraHeaders || []).slice();
  6667. var length = extraHeaders.length;
  6668. var extraHeadersString = null;
  6669. for (var idx = 0; idx < length; idx++) {
  6670. extraHeadersString = (extraHeadersString || '') + extraHeaders[idx].trim() + '\r\n';
  6671. }
  6672. tr.cancel_request(tr, reason, extraHeadersString);
  6673. };
  6674. };
  6675. InviteClientTransaction.prototype = Object.create(SIP.EventEmitter.prototype);
  6676. InviteClientTransaction.prototype.stateChanged = function(state) {
  6677. this.state = state;
  6678. this.emit('stateChanged');
  6679. };
  6680. InviteClientTransaction.prototype.send = function() {
  6681. var tr = this;
  6682. this.stateChanged(C.STATUS_CALLING);
  6683. this.B = SIP.Timers.setTimeout(tr.timer_B.bind(tr), SIP.Timers.TIMER_B);
  6684. if(!this.transport.send(this.request)) {
  6685. this.onTransportError();
  6686. }
  6687. };
  6688. InviteClientTransaction.prototype.onTransportError = function() {
  6689. this.logger.log('transport error occurred, deleting INVITE client transaction ' + this.id);
  6690. SIP.Timers.clearTimeout(this.B);
  6691. SIP.Timers.clearTimeout(this.D);
  6692. SIP.Timers.clearTimeout(this.M);
  6693. this.stateChanged(C.STATUS_TERMINATED);
  6694. this.request_sender.ua.destroyTransaction(this);
  6695. if (this.state !== C.STATUS_ACCEPTED) {
  6696. this.request_sender.onTransportError();
  6697. }
  6698. };
  6699. // RFC 6026 7.2
  6700. InviteClientTransaction.prototype.timer_M = function() {
  6701. this.logger.log('Timer M expired for INVITE client transaction ' + this.id);
  6702. if(this.state === C.STATUS_ACCEPTED) {
  6703. SIP.Timers.clearTimeout(this.B);
  6704. this.stateChanged(C.STATUS_TERMINATED);
  6705. this.request_sender.ua.destroyTransaction(this);
  6706. }
  6707. };
  6708. // RFC 3261 17.1.1
  6709. InviteClientTransaction.prototype.timer_B = function() {
  6710. this.logger.log('Timer B expired for INVITE client transaction ' + this.id);
  6711. if(this.state === C.STATUS_CALLING) {
  6712. this.stateChanged(C.STATUS_TERMINATED);
  6713. this.request_sender.ua.destroyTransaction(this);
  6714. this.request_sender.onRequestTimeout();
  6715. }
  6716. };
  6717. InviteClientTransaction.prototype.timer_D = function() {
  6718. this.logger.log('Timer D expired for INVITE client transaction ' + this.id);
  6719. SIP.Timers.clearTimeout(this.B);
  6720. this.stateChanged(C.STATUS_TERMINATED);
  6721. this.request_sender.ua.destroyTransaction(this);
  6722. };
  6723. InviteClientTransaction.prototype.sendACK = function(response) {
  6724. var tr = this;
  6725. this.ack = 'ACK ' + this.request.ruri + ' SIP/2.0\r\n';
  6726. this.ack += 'Via: ' + this.request.headers['Via'].toString() + '\r\n';
  6727. if(this.request.headers['Route']) {
  6728. this.ack += 'Route: ' + this.request.headers['Route'].toString() + '\r\n';
  6729. }
  6730. this.ack += 'To: ' + response.getHeader('to') + '\r\n';
  6731. this.ack += 'From: ' + this.request.headers['From'].toString() + '\r\n';
  6732. this.ack += 'Call-ID: ' + this.request.headers['Call-ID'].toString() + '\r\n';
  6733. this.ack += 'Content-Length: 0\r\n';
  6734. this.ack += 'CSeq: ' + this.request.headers['CSeq'].toString().split(' ')[0];
  6735. this.ack += ' ACK\r\n\r\n';
  6736. this.D = SIP.Timers.setTimeout(tr.timer_D.bind(tr), SIP.Timers.TIMER_D);
  6737. this.transport.send(this.ack);
  6738. };
  6739. InviteClientTransaction.prototype.cancel_request = function(tr, reason, extraHeaders) {
  6740. var request = tr.request;
  6741. this.cancel = SIP.C.CANCEL + ' ' + request.ruri + ' SIP/2.0\r\n';
  6742. this.cancel += 'Via: ' + request.headers['Via'].toString() + '\r\n';
  6743. if(this.request.headers['Route']) {
  6744. this.cancel += 'Route: ' + request.headers['Route'].toString() + '\r\n';
  6745. }
  6746. this.cancel += 'To: ' + request.headers['To'].toString() + '\r\n';
  6747. this.cancel += 'From: ' + request.headers['From'].toString() + '\r\n';
  6748. this.cancel += 'Call-ID: ' + request.headers['Call-ID'].toString() + '\r\n';
  6749. this.cancel += 'CSeq: ' + request.headers['CSeq'].toString().split(' ')[0] +
  6750. ' CANCEL\r\n';
  6751. if(reason) {
  6752. this.cancel += 'Reason: ' + reason + '\r\n';
  6753. }
  6754. if (extraHeaders) {
  6755. this.cancel += extraHeaders;
  6756. }
  6757. this.cancel += 'Content-Length: 0\r\n\r\n';
  6758. // Send only if a provisional response (>100) has been received.
  6759. if(this.state === C.STATUS_PROCEEDING) {
  6760. this.transport.send(this.cancel);
  6761. }
  6762. };
  6763. InviteClientTransaction.prototype.receiveResponse = function(response) {
  6764. var
  6765. tr = this,
  6766. status_code = response.status_code;
  6767. if(status_code >= 100 && status_code <= 199) {
  6768. switch(this.state) {
  6769. case C.STATUS_CALLING:
  6770. this.stateChanged(C.STATUS_PROCEEDING);
  6771. this.request_sender.receiveResponse(response);
  6772. if(this.cancel) {
  6773. this.transport.send(this.cancel);
  6774. }
  6775. break;
  6776. case C.STATUS_PROCEEDING:
  6777. this.request_sender.receiveResponse(response);
  6778. break;
  6779. }
  6780. } else if(status_code >= 200 && status_code <= 299) {
  6781. switch(this.state) {
  6782. case C.STATUS_CALLING:
  6783. case C.STATUS_PROCEEDING:
  6784. this.stateChanged(C.STATUS_ACCEPTED);
  6785. this.M = SIP.Timers.setTimeout(tr.timer_M.bind(tr), SIP.Timers.TIMER_M);
  6786. this.request_sender.receiveResponse(response);
  6787. break;
  6788. case C.STATUS_ACCEPTED:
  6789. this.request_sender.receiveResponse(response);
  6790. break;
  6791. }
  6792. } else if(status_code >= 300 && status_code <= 699) {
  6793. switch(this.state) {
  6794. case C.STATUS_CALLING:
  6795. case C.STATUS_PROCEEDING:
  6796. this.stateChanged(C.STATUS_COMPLETED);
  6797. this.sendACK(response);
  6798. this.request_sender.receiveResponse(response);
  6799. break;
  6800. case C.STATUS_COMPLETED:
  6801. this.sendACK(response);
  6802. break;
  6803. }
  6804. }
  6805. };
  6806. /**
  6807. * @augments SIP.Transactions
  6808. * @class ACK Client Transaction
  6809. * @param {SIP.RequestSender} request_sender
  6810. * @param {SIP.OutgoingRequest} request
  6811. * @param {SIP.Transport} transport
  6812. */
  6813. var AckClientTransaction = function(request_sender, request, transport) {
  6814. var via;
  6815. this.transport = transport;
  6816. this.id = 'z9hG4bK' + Math.floor(Math.random() * 10000000);
  6817. this.request_sender = request_sender;
  6818. this.request = request;
  6819. this.logger = request_sender.ua.getLogger('sip.transaction.nict', this.id);
  6820. via = buildViaHeader(request_sender, transport, this.id);
  6821. this.request.setHeader('via', via);
  6822. };
  6823. AckClientTransaction.prototype = Object.create(SIP.EventEmitter.prototype);
  6824. AckClientTransaction.prototype.send = function() {
  6825. if(!this.transport.send(this.request)) {
  6826. this.onTransportError();
  6827. }
  6828. };
  6829. AckClientTransaction.prototype.onTransportError = function() {
  6830. this.logger.log('transport error occurred, for an ACK client transaction ' + this.id);
  6831. this.request_sender.onTransportError();
  6832. };
  6833. /**
  6834. * @augments SIP.Transactions
  6835. * @class Non Invite Server Transaction
  6836. * @param {SIP.IncomingRequest} request
  6837. * @param {SIP.UA} ua
  6838. */
  6839. var NonInviteServerTransaction = function(request, ua) {
  6840. this.type = C.NON_INVITE_SERVER;
  6841. this.id = request.via_branch;
  6842. this.request = request;
  6843. this.transport = request.transport;
  6844. this.ua = ua;
  6845. this.last_response = '';
  6846. request.server_transaction = this;
  6847. this.logger = ua.getLogger('sip.transaction.nist', this.id);
  6848. this.state = C.STATUS_TRYING;
  6849. ua.newTransaction(this);
  6850. };
  6851. NonInviteServerTransaction.prototype = Object.create(SIP.EventEmitter.prototype);
  6852. NonInviteServerTransaction.prototype.stateChanged = function(state) {
  6853. this.state = state;
  6854. this.emit('stateChanged');
  6855. };
  6856. NonInviteServerTransaction.prototype.timer_J = function() {
  6857. this.logger.log('Timer J expired for non-INVITE server transaction ' + this.id);
  6858. this.stateChanged(C.STATUS_TERMINATED);
  6859. this.ua.destroyTransaction(this);
  6860. };
  6861. NonInviteServerTransaction.prototype.onTransportError = function() {
  6862. if (!this.transportError) {
  6863. this.transportError = true;
  6864. this.logger.log('transport error occurred, deleting non-INVITE server transaction ' + this.id);
  6865. SIP.Timers.clearTimeout(this.J);
  6866. this.stateChanged(C.STATUS_TERMINATED);
  6867. this.ua.destroyTransaction(this);
  6868. }
  6869. };
  6870. NonInviteServerTransaction.prototype.receiveResponse = function(status_code, response) {
  6871. var tr = this;
  6872. var deferred = SIP.Utils.defer();
  6873. if(status_code === 100) {
  6874. /* RFC 4320 4.1
  6875. * 'A SIP element MUST NOT
  6876. * send any provisional response with a
  6877. * Status-Code other than 100 to a non-INVITE request.'
  6878. */
  6879. switch(this.state) {
  6880. case C.STATUS_TRYING:
  6881. this.stateChanged(C.STATUS_PROCEEDING);
  6882. if(!this.transport.send(response)) {
  6883. this.onTransportError();
  6884. }
  6885. break;
  6886. case C.STATUS_PROCEEDING:
  6887. this.last_response = response;
  6888. if(!this.transport.send(response)) {
  6889. this.onTransportError();
  6890. deferred.reject();
  6891. } else {
  6892. deferred.resolve();
  6893. }
  6894. break;
  6895. }
  6896. } else if(status_code >= 200 && status_code <= 699) {
  6897. switch(this.state) {
  6898. case C.STATUS_TRYING:
  6899. case C.STATUS_PROCEEDING:
  6900. this.stateChanged(C.STATUS_COMPLETED);
  6901. this.last_response = response;
  6902. this.J = SIP.Timers.setTimeout(tr.timer_J.bind(tr), SIP.Timers.TIMER_J);
  6903. if(!this.transport.send(response)) {
  6904. this.onTransportError();
  6905. deferred.reject();
  6906. } else {
  6907. deferred.resolve();
  6908. }
  6909. break;
  6910. case C.STATUS_COMPLETED:
  6911. break;
  6912. }
  6913. }
  6914. return deferred.promise;
  6915. };
  6916. /**
  6917. * @augments SIP.Transactions
  6918. * @class Invite Server Transaction
  6919. * @param {SIP.IncomingRequest} request
  6920. * @param {SIP.UA} ua
  6921. */
  6922. var InviteServerTransaction = function(request, ua) {
  6923. this.type = C.INVITE_SERVER;
  6924. this.id = request.via_branch;
  6925. this.request = request;
  6926. this.transport = request.transport;
  6927. this.ua = ua;
  6928. this.last_response = '';
  6929. request.server_transaction = this;
  6930. this.logger = ua.getLogger('sip.transaction.ist', this.id);
  6931. this.state = C.STATUS_PROCEEDING;
  6932. ua.newTransaction(this);
  6933. this.resendProvisionalTimer = null;
  6934. request.reply(100);
  6935. };
  6936. InviteServerTransaction.prototype = Object.create(SIP.EventEmitter.prototype);
  6937. InviteServerTransaction.prototype.stateChanged = function(state) {
  6938. this.state = state;
  6939. this.emit('stateChanged');
  6940. };
  6941. InviteServerTransaction.prototype.timer_H = function() {
  6942. this.logger.log('Timer H expired for INVITE server transaction ' + this.id);
  6943. if(this.state === C.STATUS_COMPLETED) {
  6944. this.logger.warn('transactions', 'ACK for INVITE server transaction was never received, call will be terminated');
  6945. }
  6946. this.stateChanged(C.STATUS_TERMINATED);
  6947. this.ua.destroyTransaction(this);
  6948. };
  6949. InviteServerTransaction.prototype.timer_I = function() {
  6950. this.stateChanged(C.STATUS_TERMINATED);
  6951. this.ua.destroyTransaction(this);
  6952. };
  6953. // RFC 6026 7.1
  6954. InviteServerTransaction.prototype.timer_L = function() {
  6955. this.logger.log('Timer L expired for INVITE server transaction ' + this.id);
  6956. if(this.state === C.STATUS_ACCEPTED) {
  6957. this.stateChanged(C.STATUS_TERMINATED);
  6958. this.ua.destroyTransaction(this);
  6959. }
  6960. };
  6961. InviteServerTransaction.prototype.onTransportError = function() {
  6962. if (!this.transportError) {
  6963. this.transportError = true;
  6964. this.logger.log('transport error occurred, deleting INVITE server transaction ' + this.id);
  6965. if (this.resendProvisionalTimer !== null) {
  6966. SIP.Timers.clearInterval(this.resendProvisionalTimer);
  6967. this.resendProvisionalTimer = null;
  6968. }
  6969. SIP.Timers.clearTimeout(this.L);
  6970. SIP.Timers.clearTimeout(this.H);
  6971. SIP.Timers.clearTimeout(this.I);
  6972. this.stateChanged(C.STATUS_TERMINATED);
  6973. this.ua.destroyTransaction(this);
  6974. }
  6975. };
  6976. InviteServerTransaction.prototype.resend_provisional = function() {
  6977. if(!this.transport.send(this.last_response)) {
  6978. this.onTransportError();
  6979. }
  6980. };
  6981. // INVITE Server Transaction RFC 3261 17.2.1
  6982. InviteServerTransaction.prototype.receiveResponse = function(status_code, response) {
  6983. var tr = this;
  6984. var deferred = SIP.Utils.defer();
  6985. if(status_code >= 100 && status_code <= 199) {
  6986. switch(this.state) {
  6987. case C.STATUS_PROCEEDING:
  6988. if(!this.transport.send(response)) {
  6989. this.onTransportError();
  6990. }
  6991. this.last_response = response;
  6992. break;
  6993. }
  6994. }
  6995. if(status_code > 100 && status_code <= 199 && this.state === C.STATUS_PROCEEDING) {
  6996. // Trigger the resendProvisionalTimer only for the first non 100 provisional response.
  6997. if(this.resendProvisionalTimer === null) {
  6998. this.resendProvisionalTimer = SIP.Timers.setInterval(tr.resend_provisional.bind(tr),
  6999. SIP.Timers.PROVISIONAL_RESPONSE_INTERVAL);
  7000. }
  7001. } else if(status_code >= 200 && status_code <= 299) {
  7002. switch(this.state) {
  7003. case C.STATUS_PROCEEDING:
  7004. this.stateChanged(C.STATUS_ACCEPTED);
  7005. this.last_response = response;
  7006. this.L = SIP.Timers.setTimeout(tr.timer_L.bind(tr), SIP.Timers.TIMER_L);
  7007. if (this.resendProvisionalTimer !== null) {
  7008. SIP.Timers.clearInterval(this.resendProvisionalTimer);
  7009. this.resendProvisionalTimer = null;
  7010. }
  7011. /* falls through */
  7012. case C.STATUS_ACCEPTED:
  7013. // Note that this point will be reached for proceeding tr.state also.
  7014. if(!this.transport.send(response)) {
  7015. this.onTransportError();
  7016. deferred.reject();
  7017. } else {
  7018. deferred.resolve();
  7019. }
  7020. break;
  7021. }
  7022. } else if(status_code >= 300 && status_code <= 699) {
  7023. switch(this.state) {
  7024. case C.STATUS_PROCEEDING:
  7025. if (this.resendProvisionalTimer !== null) {
  7026. SIP.Timers.clearInterval(this.resendProvisionalTimer);
  7027. this.resendProvisionalTimer = null;
  7028. }
  7029. if(!this.transport.send(response)) {
  7030. this.onTransportError();
  7031. deferred.reject();
  7032. } else {
  7033. this.stateChanged(C.STATUS_COMPLETED);
  7034. this.H = SIP.Timers.setTimeout(tr.timer_H.bind(tr), SIP.Timers.TIMER_H);
  7035. deferred.resolve();
  7036. }
  7037. break;
  7038. }
  7039. }
  7040. return deferred.promise;
  7041. };
  7042. /**
  7043. * @function
  7044. * @param {SIP.UA} ua
  7045. * @param {SIP.IncomingRequest} request
  7046. *
  7047. * @return {boolean}
  7048. * INVITE:
  7049. * _true_ if retransmission
  7050. * _false_ new request
  7051. *
  7052. * ACK:
  7053. * _true_ ACK to non2xx response
  7054. * _false_ ACK must be passed to TU (accepted state)
  7055. * ACK to 2xx response
  7056. *
  7057. * CANCEL:
  7058. * _true_ no matching invite transaction
  7059. * _false_ matching invite transaction and no final response sent
  7060. *
  7061. * OTHER:
  7062. * _true_ retransmission
  7063. * _false_ new request
  7064. */
  7065. var checkTransaction = function(ua, request) {
  7066. var tr;
  7067. switch(request.method) {
  7068. case SIP.C.INVITE:
  7069. tr = ua.transactions.ist[request.via_branch];
  7070. if(tr) {
  7071. switch(tr.state) {
  7072. case C.STATUS_PROCEEDING:
  7073. tr.transport.send(tr.last_response);
  7074. break;
  7075. // RFC 6026 7.1 Invite retransmission
  7076. //received while in C.STATUS_ACCEPTED state. Absorb it.
  7077. case C.STATUS_ACCEPTED:
  7078. break;
  7079. }
  7080. return true;
  7081. }
  7082. break;
  7083. case SIP.C.ACK:
  7084. tr = ua.transactions.ist[request.via_branch];
  7085. // RFC 6026 7.1
  7086. if(tr) {
  7087. if(tr.state === C.STATUS_ACCEPTED) {
  7088. return false;
  7089. } else if(tr.state === C.STATUS_COMPLETED) {
  7090. tr.stateChanged(C.STATUS_CONFIRMED);
  7091. tr.I = SIP.Timers.setTimeout(tr.timer_I.bind(tr), SIP.Timers.TIMER_I);
  7092. return true;
  7093. }
  7094. }
  7095. // ACK to 2XX Response.
  7096. else {
  7097. return false;
  7098. }
  7099. break;
  7100. case SIP.C.CANCEL:
  7101. tr = ua.transactions.ist[request.via_branch];
  7102. if(tr) {
  7103. request.reply_sl(200);
  7104. if(tr.state === C.STATUS_PROCEEDING) {
  7105. return false;
  7106. } else {
  7107. return true;
  7108. }
  7109. } else {
  7110. request.reply_sl(481);
  7111. return true;
  7112. }
  7113. break;
  7114. default:
  7115. // Non-INVITE Server Transaction RFC 3261 17.2.2
  7116. tr = ua.transactions.nist[request.via_branch];
  7117. if(tr) {
  7118. switch(tr.state) {
  7119. case C.STATUS_TRYING:
  7120. break;
  7121. case C.STATUS_PROCEEDING:
  7122. case C.STATUS_COMPLETED:
  7123. tr.transport.send(tr.last_response);
  7124. break;
  7125. }
  7126. return true;
  7127. }
  7128. break;
  7129. }
  7130. };
  7131. SIP.Transactions = {
  7132. C: C,
  7133. checkTransaction: checkTransaction,
  7134. NonInviteClientTransaction: NonInviteClientTransaction,
  7135. InviteClientTransaction: InviteClientTransaction,
  7136. AckClientTransaction: AckClientTransaction,
  7137. NonInviteServerTransaction: NonInviteServerTransaction,
  7138. InviteServerTransaction: InviteServerTransaction
  7139. };
  7140. };
  7141. },{}],28:[function(require,module,exports){
  7142. "use strict";
  7143. /**
  7144. * @fileoverview Transport
  7145. */
  7146. /**
  7147. * @augments SIP
  7148. * @class Transport
  7149. * @param {SIP.UA} ua
  7150. * @param {Object} server ws_server Object
  7151. */
  7152. module.exports = function (SIP, WebSocket) {
  7153. var Transport,
  7154. C = {
  7155. // Transport status codes
  7156. STATUS_READY: 0,
  7157. STATUS_DISCONNECTED: 1,
  7158. STATUS_ERROR: 2
  7159. };
  7160. /**
  7161. * Compute an amount of time in seconds to wait before sending another
  7162. * keep-alive.
  7163. * @returns {Number}
  7164. */
  7165. function computeKeepAliveTimeout(upperBound) {
  7166. var lowerBound = upperBound * 0.8;
  7167. return 1000 * (Math.random() * (upperBound - lowerBound) + lowerBound);
  7168. }
  7169. Transport = function(ua, server) {
  7170. this.logger = ua.getLogger('sip.transport');
  7171. this.ua = ua;
  7172. this.ws = null;
  7173. this.server = server;
  7174. this.reconnection_attempts = 0;
  7175. this.closed = false;
  7176. this.connected = false;
  7177. this.reconnectTimer = null;
  7178. this.lastTransportError = {};
  7179. this.keepAliveInterval = ua.configuration.keepAliveInterval;
  7180. this.keepAliveTimeout = null;
  7181. this.keepAliveTimer = null;
  7182. this.ua.transport = this;
  7183. // Connect
  7184. this.connect();
  7185. };
  7186. Transport.prototype = {
  7187. /**
  7188. * Send a message.
  7189. * @param {SIP.OutgoingRequest|String} msg
  7190. * @returns {Boolean}
  7191. */
  7192. send: function(msg) {
  7193. var message = msg.toString();
  7194. if(this.ws && this.ws.readyState === WebSocket.OPEN) {
  7195. if (this.ua.configuration.traceSip === true) {
  7196. this.logger.log('sending WebSocket message:\n\n' + message + '\n');
  7197. }
  7198. this.ws.send(message);
  7199. return true;
  7200. } else {
  7201. this.logger.warn('unable to send message, WebSocket is not open');
  7202. return false;
  7203. }
  7204. },
  7205. /**
  7206. * Send a keep-alive (a double-CRLF sequence).
  7207. * @private
  7208. * @returns {Boolean}
  7209. */
  7210. sendKeepAlive: function() {
  7211. if(this.keepAliveTimeout) { return; }
  7212. this.keepAliveTimeout = SIP.Timers.setTimeout(function() {
  7213. this.ua.emit('keepAliveTimeout');
  7214. }.bind(this), 10000);
  7215. return this.send('\r\n\r\n');
  7216. },
  7217. /**
  7218. * Start sending keep-alives.
  7219. * @private
  7220. */
  7221. startSendingKeepAlives: function() {
  7222. if (this.keepAliveInterval && !this.keepAliveTimer) {
  7223. this.keepAliveTimer = SIP.Timers.setTimeout(function() {
  7224. this.sendKeepAlive();
  7225. this.keepAliveTimer = null;
  7226. this.startSendingKeepAlives();
  7227. }.bind(this), computeKeepAliveTimeout(this.keepAliveInterval));
  7228. }
  7229. },
  7230. /**
  7231. * Stop sending keep-alives.
  7232. * @private
  7233. */
  7234. stopSendingKeepAlives: function() {
  7235. SIP.Timers.clearTimeout(this.keepAliveTimer);
  7236. SIP.Timers.clearTimeout(this.keepAliveTimeout);
  7237. this.keepAliveTimer = null;
  7238. this.keepAliveTimeout = null;
  7239. },
  7240. /**
  7241. * Disconnect socket.
  7242. */
  7243. disconnect: function() {
  7244. if(this.ws) {
  7245. // Clear reconnectTimer
  7246. SIP.Timers.clearTimeout(this.reconnectTimer);
  7247. this.stopSendingKeepAlives();
  7248. this.closed = true;
  7249. this.logger.log('closing WebSocket ' + this.server.ws_uri);
  7250. this.ws.close();
  7251. this.ws = null;
  7252. }
  7253. if (this.reconnectTimer !== null) {
  7254. SIP.Timers.clearTimeout(this.reconnectTimer);
  7255. this.reconnectTimer = null;
  7256. this.ua.emit('disconnected', {
  7257. transport: this,
  7258. code: this.lastTransportError.code,
  7259. reason: this.lastTransportError.reason
  7260. });
  7261. }
  7262. },
  7263. /**
  7264. * Connect socket.
  7265. */
  7266. connect: function() {
  7267. var transport = this;
  7268. if(this.ws && (this.ws.readyState === WebSocket.OPEN || this.ws.readyState === WebSocket.CONNECTING)) {
  7269. this.logger.log('WebSocket ' + this.server.ws_uri + ' is already connected');
  7270. return false;
  7271. }
  7272. if(this.ws) {
  7273. this.ws.close();
  7274. this.ws = null;
  7275. }
  7276. this.logger.log('connecting to WebSocket ' + this.server.ws_uri);
  7277. this.ua.onTransportConnecting(this,
  7278. (this.reconnection_attempts === 0)?1:this.reconnection_attempts);
  7279. try {
  7280. this.ws = new WebSocket(this.server.ws_uri, 'sip');
  7281. } catch(e) {
  7282. this.logger.warn('error connecting to WebSocket ' + this.server.ws_uri + ': ' + e);
  7283. }
  7284. this.ws.binaryType = 'arraybuffer';
  7285. this.ws.onopen = function() {
  7286. transport.onOpen();
  7287. };
  7288. this.ws.onclose = function(e) {
  7289. transport.onClose(e);
  7290. // Always cleanup. Eases GC, prevents potential memory leaks.
  7291. this.onopen = null;
  7292. this.onclose = null;
  7293. this.onmessage = null;
  7294. this.onerror = null;
  7295. };
  7296. this.ws.onmessage = function(e) {
  7297. transport.onMessage(e);
  7298. };
  7299. this.ws.onerror = function(e) {
  7300. transport.onError(e);
  7301. };
  7302. },
  7303. // Transport Event Handlers
  7304. /**
  7305. * @event
  7306. * @param {event} e
  7307. */
  7308. onOpen: function() {
  7309. this.connected = true;
  7310. this.logger.log('WebSocket ' + this.server.ws_uri + ' connected');
  7311. // Clear reconnectTimer since we are not disconnected
  7312. if (this.reconnectTimer !== null) {
  7313. SIP.Timers.clearTimeout(this.reconnectTimer);
  7314. this.reconnectTimer = null;
  7315. }
  7316. // Reset reconnection_attempts
  7317. this.reconnection_attempts = 0;
  7318. // Disable closed
  7319. this.closed = false;
  7320. // Trigger onTransportConnected callback
  7321. this.ua.onTransportConnected(this);
  7322. // Start sending keep-alives
  7323. this.startSendingKeepAlives();
  7324. },
  7325. /**
  7326. * @event
  7327. * @param {event} e
  7328. */
  7329. onClose: function(e) {
  7330. var connected_before = this.connected;
  7331. this.lastTransportError.code = e.code;
  7332. this.lastTransportError.reason = e.reason;
  7333. this.stopSendingKeepAlives();
  7334. if (this.reconnection_attempts > 0) {
  7335. this.logger.log('Reconnection attempt ' + this.reconnection_attempts + ' failed (code: ' + e.code + (e.reason? '| reason: ' + e.reason : '') +')');
  7336. this.reconnect();
  7337. } else {
  7338. this.connected = false;
  7339. this.logger.log('WebSocket disconnected (code: ' + e.code + (e.reason? '| reason: ' + e.reason : '') +')');
  7340. if(e.wasClean === false) {
  7341. this.logger.warn('WebSocket abrupt disconnection');
  7342. }
  7343. // Transport was connected
  7344. if(connected_before === true) {
  7345. this.ua.onTransportClosed(this);
  7346. // Check whether the user requested to close.
  7347. if(!this.closed) {
  7348. this.reconnect();
  7349. } else {
  7350. this.ua.emit('disconnected', {
  7351. transport: this,
  7352. code: this.lastTransportError.code,
  7353. reason: this.lastTransportError.reason
  7354. });
  7355. }
  7356. } else {
  7357. // This is the first connection attempt
  7358. //Network error
  7359. this.ua.onTransportError(this);
  7360. }
  7361. }
  7362. },
  7363. /**
  7364. * @event
  7365. * @param {event} e
  7366. */
  7367. onMessage: function(e) {
  7368. var message, transaction,
  7369. data = e.data;
  7370. // CRLF Keep Alive response from server. Ignore it.
  7371. if(data === '\r\n') {
  7372. SIP.Timers.clearTimeout(this.keepAliveTimeout);
  7373. this.keepAliveTimeout = null;
  7374. if (this.ua.configuration.traceSip === true) {
  7375. this.logger.log('received WebSocket message with CRLF Keep Alive response');
  7376. }
  7377. return;
  7378. }
  7379. // WebSocket binary message.
  7380. else if (typeof data !== 'string') {
  7381. try {
  7382. data = String.fromCharCode.apply(null, new Uint8Array(data));
  7383. } catch(evt) {
  7384. this.logger.warn('received WebSocket binary message failed to be converted into string, message discarded');
  7385. return;
  7386. }
  7387. if (this.ua.configuration.traceSip === true) {
  7388. this.logger.log('received WebSocket binary message:\n\n' + data + '\n');
  7389. }
  7390. }
  7391. // WebSocket text message.
  7392. else {
  7393. if (this.ua.configuration.traceSip === true) {
  7394. this.logger.log('received WebSocket text message:\n\n' + data + '\n');
  7395. }
  7396. }
  7397. message = SIP.Parser.parseMessage(data, this.ua);
  7398. if (!message) {
  7399. return;
  7400. }
  7401. if(this.ua.status === SIP.UA.C.STATUS_USER_CLOSED && message instanceof SIP.IncomingRequest) {
  7402. return;
  7403. }
  7404. // Do some sanity check
  7405. if(SIP.sanityCheck(message, this.ua, this)) {
  7406. if(message instanceof SIP.IncomingRequest) {
  7407. message.transport = this;
  7408. this.ua.receiveRequest(message);
  7409. } else if(message instanceof SIP.IncomingResponse) {
  7410. /* Unike stated in 18.1.2, if a response does not match
  7411. * any transaction, it is discarded here and no passed to the core
  7412. * in order to be discarded there.
  7413. */
  7414. switch(message.method) {
  7415. case SIP.C.INVITE:
  7416. transaction = this.ua.transactions.ict[message.via_branch];
  7417. if(transaction) {
  7418. transaction.receiveResponse(message);
  7419. }
  7420. break;
  7421. case SIP.C.ACK:
  7422. // Just in case ;-)
  7423. break;
  7424. default:
  7425. transaction = this.ua.transactions.nict[message.via_branch];
  7426. if(transaction) {
  7427. transaction.receiveResponse(message);
  7428. }
  7429. break;
  7430. }
  7431. }
  7432. }
  7433. },
  7434. /**
  7435. * @event
  7436. * @param {event} e
  7437. */
  7438. onError: function(e) {
  7439. this.logger.warn('WebSocket connection error: ' + JSON.stringify(e));
  7440. },
  7441. /**
  7442. * Reconnection attempt logic.
  7443. * @private
  7444. */
  7445. reconnect: function() {
  7446. var transport = this;
  7447. this.reconnection_attempts += 1;
  7448. if(this.reconnection_attempts > this.ua.configuration.wsServerMaxReconnection) {
  7449. this.logger.warn('maximum reconnection attempts for WebSocket ' + this.server.ws_uri);
  7450. this.ua.onTransportError(this);
  7451. } else if (this.reconnection_attempts === 1) {
  7452. this.logger.log('Connection to WebSocket ' + this.server.ws_uri + ' severed, attempting first reconnect');
  7453. transport.connect();
  7454. } else {
  7455. this.logger.log('trying to reconnect to WebSocket ' + this.server.ws_uri + ' (reconnection attempt ' + this.reconnection_attempts + ')');
  7456. this.reconnectTimer = SIP.Timers.setTimeout(function() {
  7457. transport.connect();
  7458. transport.reconnectTimer = null;
  7459. }, this.ua.configuration.wsServerReconnectionTimeout * 1000);
  7460. }
  7461. }
  7462. };
  7463. Transport.C = C;
  7464. return Transport;
  7465. };
  7466. },{}],29:[function(require,module,exports){
  7467. (function (global){
  7468. "use strict";
  7469. /**
  7470. * @augments SIP
  7471. * @class Class creating a SIP User Agent.
  7472. * @param {function returning SIP.MediaHandler} [configuration.mediaHandlerFactory]
  7473. * A function will be invoked by each of the UA's Sessions to build the MediaHandler for that Session.
  7474. * If no (or a falsy) value is provided, each Session will use a default (WebRTC) MediaHandler.
  7475. *
  7476. * @param {Object} [configuration.media] gets passed to SIP.MediaHandler.getDescription as mediaHint
  7477. */
  7478. module.exports = function (SIP, environment) {
  7479. var UA,
  7480. C = {
  7481. // UA status codes
  7482. STATUS_INIT: 0,
  7483. STATUS_STARTING: 1,
  7484. STATUS_READY: 2,
  7485. STATUS_USER_CLOSED: 3,
  7486. STATUS_NOT_READY: 4,
  7487. // UA error codes
  7488. CONFIGURATION_ERROR: 1,
  7489. NETWORK_ERROR: 2,
  7490. ALLOWED_METHODS: [
  7491. 'ACK',
  7492. 'CANCEL',
  7493. 'INVITE',
  7494. 'MESSAGE',
  7495. 'BYE',
  7496. 'OPTIONS',
  7497. 'INFO',
  7498. 'NOTIFY',
  7499. 'REFER'
  7500. ],
  7501. ACCEPTED_BODY_TYPES: [
  7502. 'application/sdp',
  7503. 'application/dtmf-relay'
  7504. ],
  7505. MAX_FORWARDS: 70,
  7506. TAG_LENGTH: 10
  7507. };
  7508. UA = function(configuration) {
  7509. var self = this;
  7510. // Helper function for forwarding events
  7511. function selfEmit(type) {
  7512. //registrationFailed handler is invoked with two arguments. Allow event handlers to be invoked with a variable no. of arguments
  7513. return self.emit.bind(self, type);
  7514. }
  7515. // Set Accepted Body Types
  7516. C.ACCEPTED_BODY_TYPES = C.ACCEPTED_BODY_TYPES.toString();
  7517. this.log = new SIP.LoggerFactory();
  7518. this.logger = this.getLogger('sip.ua');
  7519. this.cache = {
  7520. credentials: {}
  7521. };
  7522. this.configuration = {};
  7523. this.dialogs = {};
  7524. //User actions outside any session/dialog (MESSAGE)
  7525. this.applicants = {};
  7526. this.data = {};
  7527. this.sessions = {};
  7528. this.subscriptions = {};
  7529. this.earlySubscriptions = {};
  7530. this.transport = null;
  7531. this.contact = null;
  7532. this.status = C.STATUS_INIT;
  7533. this.error = null;
  7534. this.transactions = {
  7535. nist: {},
  7536. nict: {},
  7537. ist: {},
  7538. ict: {}
  7539. };
  7540. this.transportRecoverAttempts = 0;
  7541. this.transportRecoveryTimer = null;
  7542. Object.defineProperties(this, {
  7543. transactionsCount: {
  7544. get: function() {
  7545. var type,
  7546. transactions = ['nist','nict','ist','ict'],
  7547. count = 0;
  7548. for (type in transactions) {
  7549. count += Object.keys(this.transactions[transactions[type]]).length;
  7550. }
  7551. return count;
  7552. }
  7553. },
  7554. nictTransactionsCount: {
  7555. get: function() {
  7556. return Object.keys(this.transactions['nict']).length;
  7557. }
  7558. },
  7559. nistTransactionsCount: {
  7560. get: function() {
  7561. return Object.keys(this.transactions['nist']).length;
  7562. }
  7563. },
  7564. ictTransactionsCount: {
  7565. get: function() {
  7566. return Object.keys(this.transactions['ict']).length;
  7567. }
  7568. },
  7569. istTransactionsCount: {
  7570. get: function() {
  7571. return Object.keys(this.transactions['ist']).length;
  7572. }
  7573. }
  7574. });
  7575. /**
  7576. * Load configuration
  7577. *
  7578. * @throws {SIP.Exceptions.ConfigurationError}
  7579. * @throws {TypeError}
  7580. */
  7581. if(configuration === undefined) {
  7582. configuration = {};
  7583. } else if (typeof configuration === 'string' || configuration instanceof String) {
  7584. configuration = {
  7585. uri: configuration
  7586. };
  7587. }
  7588. // Apply log configuration if present
  7589. if (configuration.log) {
  7590. if (configuration.log.hasOwnProperty('builtinEnabled')) {
  7591. this.log.builtinEnabled = configuration.log.builtinEnabled;
  7592. }
  7593. if (configuration.log.hasOwnProperty('level')) {
  7594. this.log.level = configuration.log.level;
  7595. }
  7596. if (configuration.log.hasOwnProperty('connector')) {
  7597. this.log.connector = configuration.log.connector;
  7598. }
  7599. }
  7600. try {
  7601. this.loadConfig(configuration);
  7602. } catch(e) {
  7603. this.status = C.STATUS_NOT_READY;
  7604. this.error = C.CONFIGURATION_ERROR;
  7605. throw e;
  7606. }
  7607. // Initialize registerContext
  7608. this.registerContext = new SIP.RegisterContext(this);
  7609. this.registerContext.on('failed', selfEmit('registrationFailed'));
  7610. this.registerContext.on('registered', selfEmit('registered'));
  7611. this.registerContext.on('unregistered', selfEmit('unregistered'));
  7612. if(this.configuration.autostart) {
  7613. this.start();
  7614. }
  7615. };
  7616. UA.prototype = Object.create(SIP.EventEmitter.prototype);
  7617. //=================
  7618. // High Level API
  7619. //=================
  7620. UA.prototype.register = function(options) {
  7621. this.configuration.register = true;
  7622. this.registerContext.register(options);
  7623. return this;
  7624. };
  7625. /**
  7626. * Unregister.
  7627. *
  7628. * @param {Boolean} [all] unregister all user bindings.
  7629. *
  7630. */
  7631. UA.prototype.unregister = function(options) {
  7632. this.configuration.register = false;
  7633. var context = this.registerContext;
  7634. this.afterConnected(context.unregister.bind(context, options));
  7635. return this;
  7636. };
  7637. UA.prototype.isRegistered = function() {
  7638. return this.registerContext.registered;
  7639. };
  7640. /**
  7641. * Connection state.
  7642. * @param {Boolean}
  7643. */
  7644. UA.prototype.isConnected = function() {
  7645. return this.transport ? this.transport.connected : false;
  7646. };
  7647. UA.prototype.afterConnected = function afterConnected (callback) {
  7648. if (this.isConnected()) {
  7649. callback();
  7650. } else {
  7651. this.once('connected', callback);
  7652. }
  7653. };
  7654. /**
  7655. * Make an outgoing call.
  7656. *
  7657. * @param {String} target
  7658. * @param {Object} views
  7659. * @param {Object} [options.media] gets passed to SIP.MediaHandler.getDescription as mediaHint
  7660. *
  7661. * @throws {TypeError}
  7662. *
  7663. */
  7664. UA.prototype.invite = function(target, options) {
  7665. var context = new SIP.InviteClientContext(this, target, options);
  7666. this.afterConnected(context.invite.bind(context));
  7667. this.emit('inviteSent', context);
  7668. return context;
  7669. };
  7670. UA.prototype.subscribe = function(target, event, options) {
  7671. var sub = new SIP.Subscription(this, target, event, options);
  7672. this.afterConnected(sub.subscribe.bind(sub));
  7673. return sub;
  7674. };
  7675. /**
  7676. * Send a message.
  7677. *
  7678. * @param {String} target
  7679. * @param {String} body
  7680. * @param {Object} [options]
  7681. *
  7682. * @throws {TypeError}
  7683. *
  7684. */
  7685. UA.prototype.message = function(target, body, options) {
  7686. if (body === undefined) {
  7687. throw new TypeError('Not enough arguments');
  7688. }
  7689. // There is no Message module, so it is okay that the UA handles defaults here.
  7690. options = Object.create(options || Object.prototype);
  7691. options.contentType || (options.contentType = 'text/plain');
  7692. options.body = body;
  7693. return this.request(SIP.C.MESSAGE, target, options);
  7694. };
  7695. UA.prototype.request = function (method, target, options) {
  7696. var req = new SIP.ClientContext(this, method, target, options);
  7697. this.afterConnected(req.send.bind(req));
  7698. return req;
  7699. };
  7700. /**
  7701. * Gracefully close.
  7702. *
  7703. */
  7704. UA.prototype.stop = function() {
  7705. var session, subscription, applicant,
  7706. ua = this;
  7707. function transactionsListener() {
  7708. if (ua.nistTransactionsCount === 0 && ua.nictTransactionsCount === 0) {
  7709. ua.removeListener('transactionDestroyed', transactionsListener);
  7710. ua.transport.disconnect();
  7711. }
  7712. }
  7713. this.logger.log('user requested closure...');
  7714. if(this.status === C.STATUS_USER_CLOSED) {
  7715. this.logger.warn('UA already closed');
  7716. return this;
  7717. }
  7718. // Clear transportRecoveryTimer
  7719. SIP.Timers.clearTimeout(this.transportRecoveryTimer);
  7720. // Close registerContext
  7721. this.logger.log('closing registerContext');
  7722. this.registerContext.close();
  7723. // Run _terminate_ on every Session
  7724. for(session in this.sessions) {
  7725. this.logger.log('closing session ' + session);
  7726. this.sessions[session].terminate();
  7727. }
  7728. //Run _close_ on every confirmed Subscription
  7729. for(subscription in this.subscriptions) {
  7730. this.logger.log('unsubscribing from subscription ' + subscription);
  7731. this.subscriptions[subscription].close();
  7732. }
  7733. //Run _close_ on every early Subscription
  7734. for(subscription in this.earlySubscriptions) {
  7735. this.logger.log('unsubscribing from early subscription ' + subscription);
  7736. this.earlySubscriptions[subscription].close();
  7737. }
  7738. // Run _close_ on every applicant
  7739. for(applicant in this.applicants) {
  7740. this.applicants[applicant].close();
  7741. }
  7742. this.status = C.STATUS_USER_CLOSED;
  7743. /*
  7744. * If the remaining transactions are all INVITE transactions, there is no need to
  7745. * wait anymore because every session has already been closed by this method.
  7746. * - locally originated sessions where terminated (CANCEL or BYE)
  7747. * - remotely originated sessions where rejected (4XX) or terminated (BYE)
  7748. * Remaining INVITE transactions belong tho sessions that where answered. This are in
  7749. * 'accepted' state due to timers 'L' and 'M' defined in [RFC 6026]
  7750. */
  7751. if (this.nistTransactionsCount === 0 && this.nictTransactionsCount === 0) {
  7752. this.transport.disconnect();
  7753. } else {
  7754. this.on('transactionDestroyed', transactionsListener);
  7755. }
  7756. if (typeof environment.removeEventListener === 'function') {
  7757. // Google Chrome Packaged Apps don't allow 'unload' listeners:
  7758. // unload is not available in packaged apps
  7759. if (!(global.chrome && global.chrome.app && global.chrome.app.runtime)) {
  7760. environment.removeEventListener('unload', this.environListener);
  7761. }
  7762. }
  7763. return this;
  7764. };
  7765. /**
  7766. * Connect to the WS server if status = STATUS_INIT.
  7767. * Resume UA after being closed.
  7768. *
  7769. */
  7770. UA.prototype.start = function() {
  7771. var server;
  7772. this.logger.log('user requested startup...');
  7773. if (this.status === C.STATUS_INIT) {
  7774. server = this.getNextWsServer();
  7775. this.status = C.STATUS_STARTING;
  7776. new SIP.Transport(this, server);
  7777. } else if(this.status === C.STATUS_USER_CLOSED) {
  7778. this.logger.log('resuming');
  7779. this.status = C.STATUS_READY;
  7780. this.transport.connect();
  7781. } else if (this.status === C.STATUS_STARTING) {
  7782. this.logger.log('UA is in STARTING status, not opening new connection');
  7783. } else if (this.status === C.STATUS_READY) {
  7784. this.logger.log('UA is in READY status, not resuming');
  7785. } else {
  7786. this.logger.error('Connection is down. Auto-Recovery system is trying to connect');
  7787. }
  7788. if (this.configuration.autostop && typeof environment.addEventListener === 'function') {
  7789. // Google Chrome Packaged Apps don't allow 'unload' listeners:
  7790. // unload is not available in packaged apps
  7791. if (!(global.chrome && global.chrome.app && global.chrome.app.runtime)) {
  7792. this.environListener = this.stop.bind(this);
  7793. environment.addEventListener('unload', this.environListener);
  7794. }
  7795. }
  7796. return this;
  7797. };
  7798. /**
  7799. * Normalize a string into a valid SIP request URI
  7800. *
  7801. * @param {String} target
  7802. *
  7803. * @returns {SIP.URI|undefined}
  7804. */
  7805. UA.prototype.normalizeTarget = function(target) {
  7806. return SIP.Utils.normalizeTarget(target, this.configuration.hostportParams);
  7807. };
  7808. //===============================
  7809. // Private (For internal use)
  7810. //===============================
  7811. UA.prototype.saveCredentials = function(credentials) {
  7812. this.cache.credentials[credentials.realm] = this.cache.credentials[credentials.realm] || {};
  7813. this.cache.credentials[credentials.realm][credentials.uri] = credentials;
  7814. return this;
  7815. };
  7816. UA.prototype.getCredentials = function(request) {
  7817. var realm, credentials;
  7818. realm = request.ruri.host;
  7819. if (this.cache.credentials[realm] && this.cache.credentials[realm][request.ruri]) {
  7820. credentials = this.cache.credentials[realm][request.ruri];
  7821. credentials.method = request.method;
  7822. }
  7823. return credentials;
  7824. };
  7825. UA.prototype.getLogger = function(category, label) {
  7826. return this.log.getLogger(category, label);
  7827. };
  7828. //==============================
  7829. // Event Handlers
  7830. //==============================
  7831. /**
  7832. * Transport Close event
  7833. * @private
  7834. * @event
  7835. * @param {SIP.Transport} transport.
  7836. */
  7837. UA.prototype.onTransportClosed = function(transport) {
  7838. // Run _onTransportError_ callback on every client transaction using _transport_
  7839. var type, idx, length,
  7840. client_transactions = ['nict', 'ict', 'nist', 'ist'];
  7841. transport.server.status = SIP.Transport.C.STATUS_DISCONNECTED;
  7842. this.logger.log('connection state set to '+ SIP.Transport.C.STATUS_DISCONNECTED);
  7843. length = client_transactions.length;
  7844. for (type = 0; type < length; type++) {
  7845. for(idx in this.transactions[client_transactions[type]]) {
  7846. this.transactions[client_transactions[type]][idx].onTransportError();
  7847. }
  7848. }
  7849. // Close sessions if GRUU is not being used
  7850. if (!this.contact.pub_gruu) {
  7851. this.closeSessionsOnTransportError();
  7852. }
  7853. };
  7854. /**
  7855. * Unrecoverable transport event.
  7856. * Connection reattempt logic has been done and didn't success.
  7857. * @private
  7858. * @event
  7859. * @param {SIP.Transport} transport.
  7860. */
  7861. UA.prototype.onTransportError = function(transport) {
  7862. var server;
  7863. this.logger.log('transport ' + transport.server.ws_uri + ' failed | connection state set to '+ SIP.Transport.C.STATUS_ERROR);
  7864. // Close sessions.
  7865. //Mark this transport as 'down'
  7866. transport.server.status = SIP.Transport.C.STATUS_ERROR;
  7867. this.emit('disconnected', {
  7868. transport: transport
  7869. });
  7870. // try the next transport if the UA isn't closed
  7871. if(this.status === C.STATUS_USER_CLOSED) {
  7872. return;
  7873. }
  7874. server = this.getNextWsServer();
  7875. if(server) {
  7876. new SIP.Transport(this, server);
  7877. }else {
  7878. this.closeSessionsOnTransportError();
  7879. if (!this.error || this.error !== C.NETWORK_ERROR) {
  7880. this.status = C.STATUS_NOT_READY;
  7881. this.error = C.NETWORK_ERROR;
  7882. }
  7883. // Transport Recovery process
  7884. this.recoverTransport();
  7885. }
  7886. };
  7887. /**
  7888. * Transport connection event.
  7889. * @private
  7890. * @event
  7891. * @param {SIP.Transport} transport.
  7892. */
  7893. UA.prototype.onTransportConnected = function(transport) {
  7894. this.transport = transport;
  7895. // Reset transport recovery counter
  7896. this.transportRecoverAttempts = 0;
  7897. transport.server.status = SIP.Transport.C.STATUS_READY;
  7898. this.logger.log('connection state set to '+ SIP.Transport.C.STATUS_READY);
  7899. if(this.status === C.STATUS_USER_CLOSED) {
  7900. return;
  7901. }
  7902. this.status = C.STATUS_READY;
  7903. this.error = null;
  7904. if(this.configuration.register) {
  7905. this.configuration.authenticationFactory.initialize().then(function () {
  7906. this.registerContext.onTransportConnected();
  7907. }.bind(this));
  7908. }
  7909. this.emit('connected', {
  7910. transport: transport
  7911. });
  7912. };
  7913. /**
  7914. * Transport connecting event
  7915. * @private
  7916. * @param {SIP.Transport} transport.
  7917. * #param {Integer} attempts.
  7918. */
  7919. UA.prototype.onTransportConnecting = function(transport, attempts) {
  7920. this.emit('connecting', {
  7921. transport: transport,
  7922. attempts: attempts
  7923. });
  7924. };
  7925. /**
  7926. * new Transaction
  7927. * @private
  7928. * @param {SIP.Transaction} transaction.
  7929. */
  7930. UA.prototype.newTransaction = function(transaction) {
  7931. this.transactions[transaction.type][transaction.id] = transaction;
  7932. this.emit('newTransaction', {transaction: transaction});
  7933. };
  7934. /**
  7935. * destroy Transaction
  7936. * @private
  7937. * @param {SIP.Transaction} transaction.
  7938. */
  7939. UA.prototype.destroyTransaction = function(transaction) {
  7940. delete this.transactions[transaction.type][transaction.id];
  7941. this.emit('transactionDestroyed', {
  7942. transaction: transaction
  7943. });
  7944. };
  7945. //=========================
  7946. // receiveRequest
  7947. //=========================
  7948. /**
  7949. * Request reception
  7950. * @private
  7951. * @param {SIP.IncomingRequest} request.
  7952. */
  7953. UA.prototype.receiveRequest = function(request) {
  7954. var dialog, session, message, earlySubscription,
  7955. method = request.method,
  7956. transaction,
  7957. replaces,
  7958. replacedDialog,
  7959. self = this;
  7960. function ruriMatches (uri) {
  7961. return uri && uri.user === request.ruri.user;
  7962. }
  7963. // Check that request URI points to us
  7964. if(!(ruriMatches(this.configuration.uri) ||
  7965. ruriMatches(this.contact.uri) ||
  7966. ruriMatches(this.contact.pub_gruu) ||
  7967. ruriMatches(this.contact.temp_gruu))) {
  7968. this.logger.warn('Request-URI does not point to us');
  7969. if (request.method !== SIP.C.ACK) {
  7970. request.reply_sl(404);
  7971. }
  7972. return;
  7973. }
  7974. // Check request URI scheme
  7975. if(request.ruri.scheme === SIP.C.SIPS) {
  7976. request.reply_sl(416);
  7977. return;
  7978. }
  7979. // Check transaction
  7980. if(SIP.Transactions.checkTransaction(this, request)) {
  7981. return;
  7982. }
  7983. /* RFC3261 12.2.2
  7984. * Requests that do not change in any way the state of a dialog may be
  7985. * received within a dialog (for example, an OPTIONS request).
  7986. * They are processed as if they had been received outside the dialog.
  7987. */
  7988. if(method === SIP.C.OPTIONS) {
  7989. new SIP.Transactions.NonInviteServerTransaction(request, this);
  7990. request.reply(200, null, [
  7991. 'Allow: '+ SIP.UA.C.ALLOWED_METHODS.toString(),
  7992. 'Accept: '+ C.ACCEPTED_BODY_TYPES
  7993. ]);
  7994. } else if (method === SIP.C.MESSAGE) {
  7995. message = new SIP.ServerContext(this, request);
  7996. message.body = request.body;
  7997. message.content_type = request.getHeader('Content-Type') || 'text/plain';
  7998. request.reply(200, null);
  7999. this.emit('message', message);
  8000. } else if (method !== SIP.C.INVITE &&
  8001. method !== SIP.C.ACK) {
  8002. // Let those methods pass through to normal processing for now.
  8003. transaction = new SIP.ServerContext(this, request);
  8004. }
  8005. // Initial Request
  8006. if(!request.to_tag) {
  8007. switch(method) {
  8008. case SIP.C.INVITE:
  8009. replaces =
  8010. this.configuration.replaces !== SIP.C.supported.UNSUPPORTED &&
  8011. request.parseHeader('replaces');
  8012. if (replaces) {
  8013. replacedDialog = this.dialogs[replaces.call_id + replaces.replaces_to_tag + replaces.replaces_from_tag];
  8014. if (!replacedDialog) {
  8015. //Replaced header without a matching dialog, reject
  8016. request.reply_sl(481, null);
  8017. return;
  8018. } else if (replacedDialog.owner.status === SIP.Session.C.STATUS_TERMINATED) {
  8019. request.reply_sl(603, null);
  8020. return;
  8021. } else if (replacedDialog.state === SIP.Dialog.C.STATUS_CONFIRMED && replaces.early_only) {
  8022. request.reply_sl(486, null);
  8023. return;
  8024. }
  8025. }
  8026. var isMediaSupported = this.configuration.mediaHandlerFactory.isSupported;
  8027. if(!isMediaSupported || isMediaSupported()) {
  8028. session = new SIP.InviteServerContext(this, request);
  8029. session.replacee = replacedDialog && replacedDialog.owner;
  8030. session.on('invite', function() {
  8031. self.emit('invite', this);
  8032. });
  8033. } else {
  8034. this.logger.warn('INVITE received but WebRTC is not supported');
  8035. request.reply(488);
  8036. }
  8037. break;
  8038. case SIP.C.BYE:
  8039. // Out of dialog BYE received
  8040. request.reply(481);
  8041. break;
  8042. case SIP.C.CANCEL:
  8043. session = this.findSession(request);
  8044. if(session) {
  8045. session.receiveRequest(request);
  8046. } else {
  8047. this.logger.warn('received CANCEL request for a non existent session');
  8048. }
  8049. break;
  8050. case SIP.C.ACK:
  8051. /* Absorb it.
  8052. * ACK request without a corresponding Invite Transaction
  8053. * and without To tag.
  8054. */
  8055. break;
  8056. case SIP.C.NOTIFY:
  8057. if (this.configuration.allowLegacyNotifications && this.listeners('notify').length > 0) {
  8058. request.reply(200, null);
  8059. self.emit('notify', {request: request});
  8060. } else {
  8061. request.reply(481, 'Subscription does not exist');
  8062. }
  8063. break;
  8064. default:
  8065. request.reply(405);
  8066. break;
  8067. }
  8068. }
  8069. // In-dialog request
  8070. else {
  8071. dialog = this.findDialog(request);
  8072. if(dialog) {
  8073. if (method === SIP.C.INVITE) {
  8074. new SIP.Transactions.InviteServerTransaction(request, this);
  8075. }
  8076. dialog.receiveRequest(request);
  8077. } else if (method === SIP.C.NOTIFY) {
  8078. session = this.findSession(request);
  8079. earlySubscription = this.findEarlySubscription(request);
  8080. if(session) {
  8081. session.receiveRequest(request);
  8082. } else if(earlySubscription) {
  8083. earlySubscription.receiveRequest(request);
  8084. } else {
  8085. this.logger.warn('received NOTIFY request for a non existent session or subscription');
  8086. request.reply(481, 'Subscription does not exist');
  8087. }
  8088. }
  8089. /* RFC3261 12.2.2
  8090. * Request with to tag, but no matching dialog found.
  8091. * Exception: ACK for an Invite request for which a dialog has not
  8092. * been created.
  8093. */
  8094. else {
  8095. if(method !== SIP.C.ACK) {
  8096. request.reply(481);
  8097. }
  8098. }
  8099. }
  8100. };
  8101. //=================
  8102. // Utils
  8103. //=================
  8104. /**
  8105. * Get the session to which the request belongs to, if any.
  8106. * @private
  8107. * @param {SIP.IncomingRequest} request.
  8108. * @returns {SIP.OutgoingSession|SIP.IncomingSession|null}
  8109. */
  8110. UA.prototype.findSession = function(request) {
  8111. return this.sessions[request.call_id + request.from_tag] ||
  8112. this.sessions[request.call_id + request.to_tag] ||
  8113. null;
  8114. };
  8115. /**
  8116. * Get the dialog to which the request belongs to, if any.
  8117. * @private
  8118. * @param {SIP.IncomingRequest}
  8119. * @returns {SIP.Dialog|null}
  8120. */
  8121. UA.prototype.findDialog = function(request) {
  8122. return this.dialogs[request.call_id + request.from_tag + request.to_tag] ||
  8123. this.dialogs[request.call_id + request.to_tag + request.from_tag] ||
  8124. null;
  8125. };
  8126. /**
  8127. * Get the subscription which has not been confirmed to which the request belongs to, if any
  8128. * @private
  8129. * @param {SIP.IncomingRequest}
  8130. * @returns {SIP.Subscription|null}
  8131. */
  8132. UA.prototype.findEarlySubscription = function(request) {
  8133. return this.earlySubscriptions[request.call_id + request.to_tag + request.getHeader('event')] || null;
  8134. };
  8135. /**
  8136. * Retrieve the next server to which connect.
  8137. * @private
  8138. * @returns {Object} ws_server
  8139. */
  8140. UA.prototype.getNextWsServer = function() {
  8141. // Order servers by weight
  8142. var idx, length, ws_server,
  8143. candidates = [];
  8144. length = this.configuration.wsServers.length;
  8145. for (idx = 0; idx < length; idx++) {
  8146. ws_server = this.configuration.wsServers[idx];
  8147. if (ws_server.status === SIP.Transport.C.STATUS_ERROR) {
  8148. continue;
  8149. } else if (candidates.length === 0) {
  8150. candidates.push(ws_server);
  8151. } else if (ws_server.weight > candidates[0].weight) {
  8152. candidates = [ws_server];
  8153. } else if (ws_server.weight === candidates[0].weight) {
  8154. candidates.push(ws_server);
  8155. }
  8156. }
  8157. idx = Math.floor(Math.random() * candidates.length);
  8158. return candidates[idx];
  8159. };
  8160. /**
  8161. * Close all sessions on transport error.
  8162. * @private
  8163. */
  8164. UA.prototype.closeSessionsOnTransportError = function() {
  8165. var idx;
  8166. // Run _transportError_ for every Session
  8167. for(idx in this.sessions) {
  8168. this.sessions[idx].onTransportError();
  8169. }
  8170. // Call registerContext _onTransportClosed_
  8171. this.registerContext.onTransportClosed();
  8172. };
  8173. UA.prototype.recoverTransport = function(ua) {
  8174. var idx, length, k, nextRetry, count, server;
  8175. ua = ua || this;
  8176. count = ua.transportRecoverAttempts;
  8177. length = ua.configuration.wsServers.length;
  8178. for (idx = 0; idx < length; idx++) {
  8179. ua.configuration.wsServers[idx].status = 0;
  8180. }
  8181. server = ua.getNextWsServer();
  8182. k = Math.floor((Math.random() * Math.pow(2,count)) +1);
  8183. nextRetry = k * ua.configuration.connectionRecoveryMinInterval;
  8184. if (nextRetry > ua.configuration.connectionRecoveryMaxInterval) {
  8185. this.logger.log('time for next connection attempt exceeds connectionRecoveryMaxInterval, resetting counter');
  8186. nextRetry = ua.configuration.connectionRecoveryMinInterval;
  8187. count = 0;
  8188. }
  8189. this.logger.log('next connection attempt in '+ nextRetry +' seconds');
  8190. this.transportRecoveryTimer = SIP.Timers.setTimeout(
  8191. function(){
  8192. ua.transportRecoverAttempts = count + 1;
  8193. new SIP.Transport(ua, server);
  8194. }, nextRetry * 1000);
  8195. };
  8196. function checkAuthenticationFactory (authenticationFactory) {
  8197. if (!(authenticationFactory instanceof Function)) {
  8198. return;
  8199. }
  8200. if (!authenticationFactory.initialize) {
  8201. authenticationFactory.initialize = function initialize () {
  8202. return SIP.Utils.Promise.resolve();
  8203. };
  8204. }
  8205. return authenticationFactory;
  8206. }
  8207. /**
  8208. * Configuration load.
  8209. * @private
  8210. * returns {Boolean}
  8211. */
  8212. UA.prototype.loadConfig = function(configuration) {
  8213. // Settings and default values
  8214. var parameter, value, checked_value, hostportParams, registrarServer,
  8215. settings = {
  8216. /* Host address
  8217. * Value to be set in Via sent_by and host part of Contact FQDN
  8218. */
  8219. viaHost: SIP.Utils.createRandomToken(12) + '.invalid',
  8220. uri: new SIP.URI('sip', 'anonymous.' + SIP.Utils.createRandomToken(6), 'anonymous.invalid', null, null),
  8221. wsServers: [{
  8222. scheme: 'WSS',
  8223. sip_uri: '<sip:edge.sip.onsip.com;transport=ws;lr>',
  8224. status: 0,
  8225. weight: 0,
  8226. ws_uri: 'wss://edge.sip.onsip.com'
  8227. }],
  8228. //Custom Configuration Settings
  8229. custom: {},
  8230. //Display name
  8231. displayName: '',
  8232. // Password
  8233. password: null,
  8234. // Registration parameters
  8235. registerExpires: 600,
  8236. register: true,
  8237. registrarServer: null,
  8238. // Transport related parameters
  8239. wsServerMaxReconnection: 3,
  8240. wsServerReconnectionTimeout: 4,
  8241. connectionRecoveryMinInterval: 2,
  8242. connectionRecoveryMaxInterval: 30,
  8243. keepAliveInterval: 0,
  8244. extraSupported: [],
  8245. usePreloadedRoute: false,
  8246. //string to be inserted into User-Agent request header
  8247. userAgentString: SIP.C.USER_AGENT,
  8248. // Session parameters
  8249. iceCheckingTimeout: 5000,
  8250. noAnswerTimeout: 60,
  8251. stunServers: ['stun:stun.l.google.com:19302'],
  8252. turnServers: [],
  8253. // Logging parameters
  8254. traceSip: false,
  8255. // Hacks
  8256. hackViaTcp: false,
  8257. hackIpInContact: false,
  8258. hackWssInTransport: false,
  8259. hackAllowUnregisteredOptionTags: false,
  8260. hackCleanJitsiSdpImageattr: false,
  8261. hackStripTcp: false,
  8262. contactTransport: 'ws',
  8263. forceRport: false,
  8264. //autostarting
  8265. autostart: true,
  8266. autostop: true,
  8267. //Reliable Provisional Responses
  8268. rel100: SIP.C.supported.UNSUPPORTED,
  8269. // Replaces header (RFC 3891)
  8270. // http://tools.ietf.org/html/rfc3891
  8271. replaces: SIP.C.supported.UNSUPPORTED,
  8272. mediaHandlerFactory: SIP.WebRTC.MediaHandler.defaultFactory,
  8273. authenticationFactory: checkAuthenticationFactory(function authenticationFactory (ua) {
  8274. return new SIP.DigestAuthentication(ua);
  8275. }),
  8276. allowLegacyNotifications: false
  8277. };
  8278. // Pre-Configuration
  8279. function aliasUnderscored (parameter, logger) {
  8280. var underscored = parameter.replace(/([a-z][A-Z])/g, function (m) {
  8281. return m[0] + '_' + m[1].toLowerCase();
  8282. });
  8283. if (parameter === underscored) {
  8284. return;
  8285. }
  8286. var hasParameter = configuration.hasOwnProperty(parameter);
  8287. if (configuration.hasOwnProperty(underscored)) {
  8288. logger.warn(underscored + ' is deprecated, please use ' + parameter);
  8289. if (hasParameter) {
  8290. logger.warn(parameter + ' overriding ' + underscored);
  8291. }
  8292. }
  8293. configuration[parameter] = hasParameter ? configuration[parameter] : configuration[underscored];
  8294. }
  8295. var configCheck = this.getConfigurationCheck();
  8296. // Check Mandatory parameters
  8297. for(parameter in configCheck.mandatory) {
  8298. aliasUnderscored(parameter, this.logger);
  8299. if(!configuration.hasOwnProperty(parameter)) {
  8300. throw new SIP.Exceptions.ConfigurationError(parameter);
  8301. } else {
  8302. value = configuration[parameter];
  8303. checked_value = configCheck.mandatory[parameter](value);
  8304. if (checked_value !== undefined) {
  8305. settings[parameter] = checked_value;
  8306. } else {
  8307. throw new SIP.Exceptions.ConfigurationError(parameter, value);
  8308. }
  8309. }
  8310. }
  8311. SIP.Utils.optionsOverride(configuration, 'rel100', 'reliable', true, this.logger, SIP.C.supported.UNSUPPORTED);
  8312. var emptyArraysAllowed = ['stunServers', 'turnServers'];
  8313. // Check Optional parameters
  8314. for(parameter in configCheck.optional) {
  8315. aliasUnderscored(parameter, this.logger);
  8316. if(configuration.hasOwnProperty(parameter)) {
  8317. value = configuration[parameter];
  8318. // If the parameter value is an empty array, but shouldn't be, apply its default value.
  8319. if (value instanceof Array && value.length === 0 && emptyArraysAllowed.indexOf(parameter) < 0) { continue; }
  8320. // If the parameter value is null, empty string, or undefined then apply its default value.
  8321. if(value === null || value === "" || value === undefined) { continue; }
  8322. // If it's a number with NaN value then also apply its default value.
  8323. // NOTE: JS does not allow "value === NaN", the following does the work:
  8324. else if(typeof(value) === 'number' && isNaN(value)) { continue; }
  8325. checked_value = configCheck.optional[parameter](value);
  8326. if (checked_value !== undefined) {
  8327. settings[parameter] = checked_value;
  8328. } else {
  8329. throw new SIP.Exceptions.ConfigurationError(parameter, value);
  8330. }
  8331. }
  8332. }
  8333. // Sanity Checks
  8334. // Connection recovery intervals
  8335. if(settings.connectionRecoveryMaxInterval < settings.connectionRecoveryMinInterval) {
  8336. throw new SIP.Exceptions.ConfigurationError('connectionRecoveryMaxInterval', settings.connectionRecoveryMaxInterval);
  8337. }
  8338. // Post Configuration Process
  8339. // Allow passing 0 number as displayName.
  8340. if (settings.displayName === 0) {
  8341. settings.displayName = '0';
  8342. }
  8343. // Instance-id for GRUU
  8344. if (!settings.instanceId) {
  8345. settings.instanceId = SIP.Utils.newUUID();
  8346. }
  8347. // sipjsId instance parameter. Static random tag of length 5
  8348. settings.sipjsId = SIP.Utils.createRandomToken(5);
  8349. // String containing settings.uri without scheme and user.
  8350. hostportParams = settings.uri.clone();
  8351. hostportParams.user = null;
  8352. settings.hostportParams = hostportParams.toRaw().replace(/^sip:/i, '');
  8353. /* Check whether authorizationUser is explicitly defined.
  8354. * Take 'settings.uri.user' value if not.
  8355. */
  8356. if (!settings.authorizationUser) {
  8357. settings.authorizationUser = settings.uri.user;
  8358. }
  8359. /* If no 'registrarServer' is set use the 'uri' value without user portion. */
  8360. if (!settings.registrarServer) {
  8361. registrarServer = settings.uri.clone();
  8362. registrarServer.user = null;
  8363. settings.registrarServer = registrarServer;
  8364. }
  8365. // User noAnswerTimeout
  8366. settings.noAnswerTimeout = settings.noAnswerTimeout * 1000;
  8367. // Via Host
  8368. if (settings.hackIpInContact) {
  8369. if (typeof settings.hackIpInContact === 'boolean') {
  8370. settings.viaHost = SIP.Utils.getRandomTestNetIP();
  8371. }
  8372. else if (typeof settings.hackIpInContact === 'string') {
  8373. settings.viaHost = settings.hackIpInContact;
  8374. }
  8375. }
  8376. // Contact transport parameter
  8377. if (settings.hackWssInTransport) {
  8378. settings.contactTransport = 'wss';
  8379. }
  8380. this.contact = {
  8381. pub_gruu: null,
  8382. temp_gruu: null,
  8383. uri: new SIP.URI('sip', SIP.Utils.createRandomToken(8), settings.viaHost, null, {transport: settings.contactTransport}),
  8384. toString: function(options){
  8385. options = options || {};
  8386. var
  8387. anonymous = options.anonymous || null,
  8388. outbound = options.outbound || null,
  8389. contact = '<';
  8390. if (anonymous) {
  8391. contact += (this.temp_gruu || ('sip:anonymous@anonymous.invalid;transport='+settings.contactTransport)).toString();
  8392. } else {
  8393. contact += (this.pub_gruu || this.uri).toString();
  8394. }
  8395. if (outbound) {
  8396. contact += ';ob';
  8397. }
  8398. contact += '>';
  8399. return contact;
  8400. }
  8401. };
  8402. // media overrides mediaConstraints
  8403. SIP.Utils.optionsOverride(settings, 'media', 'mediaConstraints', true, this.logger);
  8404. var skeleton = {};
  8405. // Fill the value of the configuration_skeleton
  8406. for(parameter in settings) {
  8407. skeleton[parameter] = {
  8408. value: settings[parameter],
  8409. writable: (parameter === 'register' || parameter === 'custom'),
  8410. configurable: false
  8411. };
  8412. }
  8413. Object.defineProperties(this.configuration, skeleton);
  8414. this.logger.log('configuration parameters after validation:');
  8415. for(parameter in settings) {
  8416. switch(parameter) {
  8417. case 'uri':
  8418. case 'registrarServer':
  8419. case 'mediaHandlerFactory':
  8420. this.logger.log('· ' + parameter + ': ' + settings[parameter]);
  8421. break;
  8422. case 'password':
  8423. this.logger.log('· ' + parameter + ': ' + 'NOT SHOWN');
  8424. break;
  8425. default:
  8426. this.logger.log('· ' + parameter + ': ' + JSON.stringify(settings[parameter]));
  8427. }
  8428. }
  8429. return;
  8430. };
  8431. /**
  8432. * Configuration checker.
  8433. * @private
  8434. * @return {Boolean}
  8435. */
  8436. UA.prototype.getConfigurationCheck = function () {
  8437. return {
  8438. mandatory: {
  8439. },
  8440. optional: {
  8441. uri: function(uri) {
  8442. var parsed;
  8443. if (!(/^sip:/i).test(uri)) {
  8444. uri = SIP.C.SIP + ':' + uri;
  8445. }
  8446. parsed = SIP.URI.parse(uri);
  8447. if(!parsed) {
  8448. return;
  8449. } else if(!parsed.user) {
  8450. return;
  8451. } else {
  8452. return parsed;
  8453. }
  8454. },
  8455. //Note: this function used to call 'this.logger.error' but calling 'this' with anything here is invalid
  8456. wsServers: function(wsServers) {
  8457. var idx, length, url;
  8458. /* Allow defining wsServers parameter as:
  8459. * String: "host"
  8460. * Array of Strings: ["host1", "host2"]
  8461. * Array of Objects: [{ws_uri:"host1", weight:1}, {ws_uri:"host2", weight:0}]
  8462. * Array of Objects and Strings: [{ws_uri:"host1"}, "host2"]
  8463. */
  8464. if (typeof wsServers === 'string') {
  8465. wsServers = [{ws_uri: wsServers}];
  8466. } else if (wsServers instanceof Array) {
  8467. length = wsServers.length;
  8468. for (idx = 0; idx < length; idx++) {
  8469. if (typeof wsServers[idx] === 'string'){
  8470. wsServers[idx] = {ws_uri: wsServers[idx]};
  8471. }
  8472. }
  8473. } else {
  8474. return;
  8475. }
  8476. if (wsServers.length === 0) {
  8477. return false;
  8478. }
  8479. length = wsServers.length;
  8480. for (idx = 0; idx < length; idx++) {
  8481. if (!wsServers[idx].ws_uri) {
  8482. return;
  8483. }
  8484. if (wsServers[idx].weight && !Number(wsServers[idx].weight)) {
  8485. return;
  8486. }
  8487. url = SIP.Grammar.parse(wsServers[idx].ws_uri, 'absoluteURI');
  8488. if(url === -1) {
  8489. return;
  8490. } else if(['wss', 'ws', 'udp'].indexOf(url.scheme) < 0) {
  8491. return;
  8492. } else {
  8493. wsServers[idx].sip_uri = '<sip:' + url.host + (url.port ? ':' + url.port : '') + ';transport=' + url.scheme.replace(/^wss$/i, 'ws') + ';lr>';
  8494. if (!wsServers[idx].weight) {
  8495. wsServers[idx].weight = 0;
  8496. }
  8497. wsServers[idx].status = 0;
  8498. wsServers[idx].scheme = url.scheme.toUpperCase();
  8499. }
  8500. }
  8501. return wsServers;
  8502. },
  8503. authorizationUser: function(authorizationUser) {
  8504. if(SIP.Grammar.parse('"'+ authorizationUser +'"', 'quoted_string') === -1) {
  8505. return;
  8506. } else {
  8507. return authorizationUser;
  8508. }
  8509. },
  8510. connectionRecoveryMaxInterval: function(connectionRecoveryMaxInterval) {
  8511. var value;
  8512. if(SIP.Utils.isDecimal(connectionRecoveryMaxInterval)) {
  8513. value = Number(connectionRecoveryMaxInterval);
  8514. if(value > 0) {
  8515. return value;
  8516. }
  8517. }
  8518. },
  8519. connectionRecoveryMinInterval: function(connectionRecoveryMinInterval) {
  8520. var value;
  8521. if(SIP.Utils.isDecimal(connectionRecoveryMinInterval)) {
  8522. value = Number(connectionRecoveryMinInterval);
  8523. if(value > 0) {
  8524. return value;
  8525. }
  8526. }
  8527. },
  8528. displayName: function(displayName) {
  8529. if(SIP.Grammar.parse('"' + displayName + '"', 'displayName') === -1) {
  8530. return;
  8531. } else {
  8532. return displayName;
  8533. }
  8534. },
  8535. hackViaTcp: function(hackViaTcp) {
  8536. if (typeof hackViaTcp === 'boolean') {
  8537. return hackViaTcp;
  8538. }
  8539. },
  8540. hackIpInContact: function(hackIpInContact) {
  8541. if (typeof hackIpInContact === 'boolean') {
  8542. return hackIpInContact;
  8543. }
  8544. else if (typeof hackIpInContact === 'string' && SIP.Grammar.parse(hackIpInContact, 'host') !== -1) {
  8545. return hackIpInContact;
  8546. }
  8547. },
  8548. iceCheckingTimeout: function(iceCheckingTimeout) {
  8549. if(SIP.Utils.isDecimal(iceCheckingTimeout)) {
  8550. return Math.max(500, iceCheckingTimeout);
  8551. }
  8552. },
  8553. hackWssInTransport: function(hackWssInTransport) {
  8554. if (typeof hackWssInTransport === 'boolean') {
  8555. return hackWssInTransport;
  8556. }
  8557. },
  8558. hackAllowUnregisteredOptionTags: function(hackAllowUnregisteredOptionTags) {
  8559. if (typeof hackAllowUnregisteredOptionTags === 'boolean') {
  8560. return hackAllowUnregisteredOptionTags;
  8561. }
  8562. },
  8563. hackCleanJitsiSdpImageattr: function(hackCleanJitsiSdpImageattr) {
  8564. if (typeof hackCleanJitsiSdpImageattr === 'boolean') {
  8565. return hackCleanJitsiSdpImageattr;
  8566. }
  8567. },
  8568. hackStripTcp: function(hackStripTcp) {
  8569. if (typeof hackStripTcp === 'boolean') {
  8570. return hackStripTcp;
  8571. }
  8572. },
  8573. contactTransport: function(contactTransport) {
  8574. if (typeof contactTransport === 'string') {
  8575. return contactTransport;
  8576. }
  8577. },
  8578. forceRport: function(forceRport) {
  8579. if (typeof forceRport === 'boolean') {
  8580. return forceRport;
  8581. }
  8582. },
  8583. instanceId: function(instanceId) {
  8584. if(typeof instanceId !== 'string') {
  8585. return;
  8586. }
  8587. if ((/^uuid:/i.test(instanceId))) {
  8588. instanceId = instanceId.substr(5);
  8589. }
  8590. if(SIP.Grammar.parse(instanceId, 'uuid') === -1) {
  8591. return;
  8592. } else {
  8593. return instanceId;
  8594. }
  8595. },
  8596. keepAliveInterval: function(keepAliveInterval) {
  8597. var value;
  8598. if (SIP.Utils.isDecimal(keepAliveInterval)) {
  8599. value = Number(keepAliveInterval);
  8600. if (value > 0) {
  8601. return value;
  8602. }
  8603. }
  8604. },
  8605. extraSupported: function(optionTags) {
  8606. var idx, length;
  8607. if (!(optionTags instanceof Array)) {
  8608. return;
  8609. }
  8610. length = optionTags.length;
  8611. for (idx = 0; idx < length; idx++) {
  8612. if (typeof optionTags[idx] !== 'string') {
  8613. return;
  8614. }
  8615. }
  8616. return optionTags;
  8617. },
  8618. noAnswerTimeout: function(noAnswerTimeout) {
  8619. var value;
  8620. if (SIP.Utils.isDecimal(noAnswerTimeout)) {
  8621. value = Number(noAnswerTimeout);
  8622. if (value > 0) {
  8623. return value;
  8624. }
  8625. }
  8626. },
  8627. password: function(password) {
  8628. return String(password);
  8629. },
  8630. rel100: function(rel100) {
  8631. if(rel100 === SIP.C.supported.REQUIRED) {
  8632. return SIP.C.supported.REQUIRED;
  8633. } else if (rel100 === SIP.C.supported.SUPPORTED) {
  8634. return SIP.C.supported.SUPPORTED;
  8635. } else {
  8636. return SIP.C.supported.UNSUPPORTED;
  8637. }
  8638. },
  8639. replaces: function(replaces) {
  8640. if(replaces === SIP.C.supported.REQUIRED) {
  8641. return SIP.C.supported.REQUIRED;
  8642. } else if (replaces === SIP.C.supported.SUPPORTED) {
  8643. return SIP.C.supported.SUPPORTED;
  8644. } else {
  8645. return SIP.C.supported.UNSUPPORTED;
  8646. }
  8647. },
  8648. register: function(register) {
  8649. if (typeof register === 'boolean') {
  8650. return register;
  8651. }
  8652. },
  8653. registerExpires: function(registerExpires) {
  8654. var value;
  8655. if (SIP.Utils.isDecimal(registerExpires)) {
  8656. value = Number(registerExpires);
  8657. if (value > 0) {
  8658. return value;
  8659. }
  8660. }
  8661. },
  8662. registrarServer: function(registrarServer) {
  8663. var parsed;
  8664. if(typeof registrarServer !== 'string') {
  8665. return;
  8666. }
  8667. if (!/^sip:/i.test(registrarServer)) {
  8668. registrarServer = SIP.C.SIP + ':' + registrarServer;
  8669. }
  8670. parsed = SIP.URI.parse(registrarServer);
  8671. if(!parsed) {
  8672. return;
  8673. } else if(parsed.user) {
  8674. return;
  8675. } else {
  8676. return parsed;
  8677. }
  8678. },
  8679. stunServers: function(stunServers) {
  8680. var idx, length, stun_server;
  8681. if (typeof stunServers === 'string') {
  8682. stunServers = [stunServers];
  8683. } else if (!(stunServers instanceof Array)) {
  8684. return;
  8685. }
  8686. length = stunServers.length;
  8687. for (idx = 0; idx < length; idx++) {
  8688. stun_server = stunServers[idx];
  8689. if (!(/^stuns?:/.test(stun_server))) {
  8690. stun_server = 'stun:' + stun_server;
  8691. }
  8692. if(SIP.Grammar.parse(stun_server, 'stun_URI') === -1) {
  8693. return;
  8694. } else {
  8695. stunServers[idx] = stun_server;
  8696. }
  8697. }
  8698. return stunServers;
  8699. },
  8700. traceSip: function(traceSip) {
  8701. if (typeof traceSip === 'boolean') {
  8702. return traceSip;
  8703. }
  8704. },
  8705. turnServers: function(turnServers) {
  8706. var idx, jdx, length, turn_server, num_turn_server_urls, url;
  8707. if (turnServers instanceof Array) {
  8708. // Do nothing
  8709. } else {
  8710. turnServers = [turnServers];
  8711. }
  8712. length = turnServers.length;
  8713. for (idx = 0; idx < length; idx++) {
  8714. turn_server = turnServers[idx];
  8715. //Backwards compatibility: Allow defining the turn_server url with the 'server' property.
  8716. if (turn_server.server) {
  8717. turn_server.urls = [turn_server.server];
  8718. }
  8719. if (!turn_server.urls) {
  8720. return;
  8721. }
  8722. if (turn_server.urls instanceof Array) {
  8723. num_turn_server_urls = turn_server.urls.length;
  8724. } else {
  8725. turn_server.urls = [turn_server.urls];
  8726. num_turn_server_urls = 1;
  8727. }
  8728. for (jdx = 0; jdx < num_turn_server_urls; jdx++) {
  8729. url = turn_server.urls[jdx];
  8730. if (!(/^turns?:/.test(url))) {
  8731. url = 'turn:' + url;
  8732. }
  8733. if(SIP.Grammar.parse(url, 'turn_URI') === -1) {
  8734. return;
  8735. }
  8736. }
  8737. }
  8738. return turnServers;
  8739. },
  8740. rtcpMuxPolicy: function(rtcpMuxPolicy) {
  8741. if (typeof rtcpMuxPolicy === 'string') {
  8742. return rtcpMuxPolicy;
  8743. }
  8744. },
  8745. userAgentString: function(userAgentString) {
  8746. if (typeof userAgentString === 'string') {
  8747. return userAgentString;
  8748. }
  8749. },
  8750. usePreloadedRoute: function(usePreloadedRoute) {
  8751. if (typeof usePreloadedRoute === 'boolean') {
  8752. return usePreloadedRoute;
  8753. }
  8754. },
  8755. wsServerMaxReconnection: function(wsServerMaxReconnection) {
  8756. var value;
  8757. if (SIP.Utils.isDecimal(wsServerMaxReconnection)) {
  8758. value = Number(wsServerMaxReconnection);
  8759. if (value > 0) {
  8760. return value;
  8761. }
  8762. }
  8763. },
  8764. wsServerReconnectionTimeout: function(wsServerReconnectionTimeout) {
  8765. var value;
  8766. if (SIP.Utils.isDecimal(wsServerReconnectionTimeout)) {
  8767. value = Number(wsServerReconnectionTimeout);
  8768. if (value > 0) {
  8769. return value;
  8770. }
  8771. }
  8772. },
  8773. autostart: function(autostart) {
  8774. if (typeof autostart === 'boolean') {
  8775. return autostart;
  8776. }
  8777. },
  8778. autostop: function(autostop) {
  8779. if (typeof autostop === 'boolean') {
  8780. return autostop;
  8781. }
  8782. },
  8783. mediaHandlerFactory: function(mediaHandlerFactory) {
  8784. if (mediaHandlerFactory instanceof Function) {
  8785. var promisifiedFactory = function promisifiedFactory () {
  8786. var mediaHandler = mediaHandlerFactory.apply(this, arguments);
  8787. function patchMethod (methodName) {
  8788. var method = mediaHandler[methodName];
  8789. if (method.length > 1) {
  8790. var callbacksFirst = methodName === 'getDescription';
  8791. mediaHandler[methodName] = SIP.Utils.promisify(mediaHandler, methodName, callbacksFirst);
  8792. }
  8793. }
  8794. patchMethod('getDescription');
  8795. patchMethod('setDescription');
  8796. return mediaHandler;
  8797. };
  8798. promisifiedFactory.isSupported = mediaHandlerFactory.isSupported;
  8799. return promisifiedFactory;
  8800. }
  8801. },
  8802. authenticationFactory: checkAuthenticationFactory,
  8803. allowLegacyNotifications: function(allowLegacyNotifications) {
  8804. if (typeof allowLegacyNotifications === 'boolean') {
  8805. return allowLegacyNotifications;
  8806. }
  8807. },
  8808. custom: function(custom) {
  8809. if (typeof custom === 'object') {
  8810. return custom;
  8811. }
  8812. }
  8813. }
  8814. };
  8815. };
  8816. UA.C = C;
  8817. SIP.UA = UA;
  8818. };
  8819. }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
  8820. },{}],30:[function(require,module,exports){
  8821. "use strict";
  8822. /**
  8823. * @fileoverview SIP URI
  8824. */
  8825. /**
  8826. * @augments SIP
  8827. * @class Class creating a SIP URI.
  8828. *
  8829. * @param {String} [scheme]
  8830. * @param {String} [user]
  8831. * @param {String} host
  8832. * @param {String} [port]
  8833. * @param {Object} [parameters]
  8834. * @param {Object} [headers]
  8835. *
  8836. */
  8837. module.exports = function (SIP) {
  8838. var URI;
  8839. URI = function(scheme, user, host, port, parameters, headers) {
  8840. var param, header, raw, normal;
  8841. // Checks
  8842. if(!host) {
  8843. throw new TypeError('missing or invalid "host" parameter');
  8844. }
  8845. // Initialize parameters
  8846. scheme = scheme || SIP.C.SIP;
  8847. this.parameters = {};
  8848. this.headers = {};
  8849. for (param in parameters) {
  8850. this.setParam(param, parameters[param]);
  8851. }
  8852. for (header in headers) {
  8853. this.setHeader(header, headers[header]);
  8854. }
  8855. // Raw URI
  8856. raw = {
  8857. scheme: scheme,
  8858. user: user,
  8859. host: host,
  8860. port: port
  8861. };
  8862. // Normalized URI
  8863. normal = {
  8864. scheme: scheme.toLowerCase(),
  8865. user: user,
  8866. host: host.toLowerCase(),
  8867. port: port
  8868. };
  8869. Object.defineProperties(this, {
  8870. _normal: {
  8871. get: function() { return normal; }
  8872. },
  8873. _raw: {
  8874. get: function() { return raw; }
  8875. },
  8876. scheme: {
  8877. get: function() { return normal.scheme; },
  8878. set: function(value) {
  8879. raw.scheme = value;
  8880. normal.scheme = value.toLowerCase();
  8881. }
  8882. },
  8883. user: {
  8884. get: function() { return normal.user; },
  8885. set: function(value) {
  8886. normal.user = raw.user = value;
  8887. }
  8888. },
  8889. host: {
  8890. get: function() { return normal.host; },
  8891. set: function(value) {
  8892. raw.host = value;
  8893. normal.host = value.toLowerCase();
  8894. }
  8895. },
  8896. aor: {
  8897. get: function() { return normal.user + '@' + normal.host; }
  8898. },
  8899. port: {
  8900. get: function() { return normal.port; },
  8901. set: function(value) {
  8902. normal.port = raw.port = value === 0 ? value : (parseInt(value,10) || null);
  8903. }
  8904. }
  8905. });
  8906. };
  8907. URI.prototype = {
  8908. setParam: function(key, value) {
  8909. if(key) {
  8910. this.parameters[key.toLowerCase()] = (typeof value === 'undefined' || value === null) ? null : value.toString().toLowerCase();
  8911. }
  8912. },
  8913. getParam: function(key) {
  8914. if(key) {
  8915. return this.parameters[key.toLowerCase()];
  8916. }
  8917. },
  8918. hasParam: function(key) {
  8919. if(key) {
  8920. return (this.parameters.hasOwnProperty(key.toLowerCase()) && true) || false;
  8921. }
  8922. },
  8923. deleteParam: function(parameter) {
  8924. var value;
  8925. parameter = parameter.toLowerCase();
  8926. if (this.parameters.hasOwnProperty(parameter)) {
  8927. value = this.parameters[parameter];
  8928. delete this.parameters[parameter];
  8929. return value;
  8930. }
  8931. },
  8932. clearParams: function() {
  8933. this.parameters = {};
  8934. },
  8935. setHeader: function(name, value) {
  8936. this.headers[SIP.Utils.headerize(name)] = (value instanceof Array) ? value : [value];
  8937. },
  8938. getHeader: function(name) {
  8939. if(name) {
  8940. return this.headers[SIP.Utils.headerize(name)];
  8941. }
  8942. },
  8943. hasHeader: function(name) {
  8944. if(name) {
  8945. return (this.headers.hasOwnProperty(SIP.Utils.headerize(name)) && true) || false;
  8946. }
  8947. },
  8948. deleteHeader: function(header) {
  8949. var value;
  8950. header = SIP.Utils.headerize(header);
  8951. if(this.headers.hasOwnProperty(header)) {
  8952. value = this.headers[header];
  8953. delete this.headers[header];
  8954. return value;
  8955. }
  8956. },
  8957. clearHeaders: function() {
  8958. this.headers = {};
  8959. },
  8960. clone: function() {
  8961. return new URI(
  8962. this._raw.scheme,
  8963. this._raw.user,
  8964. this._raw.host,
  8965. this._raw.port,
  8966. JSON.parse(JSON.stringify(this.parameters)),
  8967. JSON.parse(JSON.stringify(this.headers)));
  8968. },
  8969. toRaw: function() {
  8970. return this._toString(this._raw);
  8971. },
  8972. toString: function() {
  8973. return this._toString(this._normal);
  8974. },
  8975. _toString: function(uri) {
  8976. var header, parameter, idx, uriString, headers = [];
  8977. uriString = uri.scheme + ':';
  8978. // add slashes if it's not a sip(s) URI
  8979. if (!uri.scheme.toLowerCase().match("^sips?$")) {
  8980. uriString += "//";
  8981. }
  8982. if (uri.user) {
  8983. uriString += SIP.Utils.escapeUser(uri.user) + '@';
  8984. }
  8985. uriString += uri.host;
  8986. if (uri.port || uri.port === 0) {
  8987. uriString += ':' + uri.port;
  8988. }
  8989. for (parameter in this.parameters) {
  8990. uriString += ';' + parameter;
  8991. if (this.parameters[parameter] !== null) {
  8992. uriString += '='+ this.parameters[parameter];
  8993. }
  8994. }
  8995. for(header in this.headers) {
  8996. for(idx in this.headers[header]) {
  8997. headers.push(header + '=' + this.headers[header][idx]);
  8998. }
  8999. }
  9000. if (headers.length > 0) {
  9001. uriString += '?' + headers.join('&');
  9002. }
  9003. return uriString;
  9004. }
  9005. };
  9006. /**
  9007. * Parse the given string and returns a SIP.URI instance or undefined if
  9008. * it is an invalid URI.
  9009. * @public
  9010. * @param {String} uri
  9011. */
  9012. URI.parse = function(uri) {
  9013. uri = SIP.Grammar.parse(uri,'SIP_URI');
  9014. if (uri !== -1) {
  9015. return uri;
  9016. } else {
  9017. return undefined;
  9018. }
  9019. };
  9020. SIP.URI = URI;
  9021. };
  9022. },{}],31:[function(require,module,exports){
  9023. "use strict";
  9024. /**
  9025. * @fileoverview Utils
  9026. */
  9027. module.exports = function (SIP, environment) {
  9028. var Utils;
  9029. Utils= {
  9030. Promise: environment.Promise,
  9031. defer: function defer () {
  9032. var deferred = {};
  9033. deferred.promise = new Utils.Promise(function (resolve, reject) {
  9034. deferred.resolve = resolve;
  9035. deferred.reject = reject;
  9036. });
  9037. return deferred;
  9038. },
  9039. promisify: function promisify (object, methodName, callbacksFirst) {
  9040. var oldMethod = object[methodName];
  9041. return function promisifiedMethod (arg, onSuccess, onFailure) {
  9042. return new Utils.Promise(function (resolve, reject) {
  9043. var oldArgs = [arg, resolve, reject];
  9044. if (callbacksFirst) {
  9045. oldArgs = [resolve, reject, arg];
  9046. }
  9047. oldMethod.apply(object, oldArgs);
  9048. }).then(onSuccess, onFailure);
  9049. };
  9050. },
  9051. augment: function (object, constructor, args, override) {
  9052. var idx, proto;
  9053. // Add public properties from constructor's prototype onto object
  9054. proto = constructor.prototype;
  9055. for (idx in proto) {
  9056. if (override || object[idx] === undefined) {
  9057. object[idx] = proto[idx];
  9058. }
  9059. }
  9060. // Construct the object as though it were just created by constructor
  9061. constructor.apply(object, args);
  9062. },
  9063. optionsOverride: function (options, winner, loser, isDeprecated, logger, defaultValue) {
  9064. if (isDeprecated && options[loser]) {
  9065. logger.warn(loser + ' is deprecated, please use ' + winner + ' instead');
  9066. }
  9067. if (options[winner] && options[loser]) {
  9068. logger.warn(winner + ' overriding ' + loser);
  9069. }
  9070. options[winner] = options[winner] || options[loser] || defaultValue;
  9071. },
  9072. str_utf8_length: function(string) {
  9073. return encodeURIComponent(string).replace(/%[A-F\d]{2}/g, 'U').length;
  9074. },
  9075. generateFakeSDP: function(body) {
  9076. if (!body) {
  9077. return;
  9078. }
  9079. var start = body.indexOf('o=');
  9080. var end = body.indexOf('\r\n', start);
  9081. return 'v=0\r\n' + body.slice(start, end) + '\r\ns=-\r\nt=0 0\r\nc=IN IP4 0.0.0.0';
  9082. },
  9083. isFunction: function(fn) {
  9084. if (fn !== undefined) {
  9085. return Object.prototype.toString.call(fn) === '[object Function]';
  9086. } else {
  9087. return false;
  9088. }
  9089. },
  9090. isDecimal: function (num) {
  9091. return !isNaN(num) && (parseFloat(num) === parseInt(num,10));
  9092. },
  9093. createRandomToken: function(size, base) {
  9094. var i, r,
  9095. token = '';
  9096. base = base || 32;
  9097. for( i=0; i < size; i++ ) {
  9098. r = Math.random() * base|0;
  9099. token += r.toString(base);
  9100. }
  9101. return token;
  9102. },
  9103. newTag: function() {
  9104. return SIP.Utils.createRandomToken(SIP.UA.C.TAG_LENGTH);
  9105. },
  9106. // http://stackoverflow.com/users/109538/broofa
  9107. newUUID: function() {
  9108. var UUID = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
  9109. var r = Math.random()*16|0, v = c === 'x' ? r : (r&0x3|0x8);
  9110. return v.toString(16);
  9111. });
  9112. return UUID;
  9113. },
  9114. hostType: function(host) {
  9115. if (!host) {
  9116. return;
  9117. } else {
  9118. host = SIP.Grammar.parse(host,'host');
  9119. if (host !== -1) {
  9120. return host.host_type;
  9121. }
  9122. }
  9123. },
  9124. /**
  9125. * Normalize SIP URI.
  9126. * NOTE: It does not allow a SIP URI without username.
  9127. * Accepts 'sip', 'sips' and 'tel' URIs and convert them into 'sip'.
  9128. * Detects the domain part (if given) and properly hex-escapes the user portion.
  9129. * If the user portion has only 'tel' number symbols the user portion is clean of 'tel' visual separators.
  9130. * @private
  9131. * @param {String} target
  9132. * @param {String} [domain]
  9133. */
  9134. normalizeTarget: function(target, domain) {
  9135. var uri, target_array, target_user, target_domain;
  9136. // If no target is given then raise an error.
  9137. if (!target) {
  9138. return;
  9139. // If a SIP.URI instance is given then return it.
  9140. } else if (target instanceof SIP.URI) {
  9141. return target;
  9142. // If a string is given split it by '@':
  9143. // - Last fragment is the desired domain.
  9144. // - Otherwise append the given domain argument.
  9145. } else if (typeof target === 'string') {
  9146. target_array = target.split('@');
  9147. switch(target_array.length) {
  9148. case 1:
  9149. if (!domain) {
  9150. return;
  9151. }
  9152. target_user = target;
  9153. target_domain = domain;
  9154. break;
  9155. case 2:
  9156. target_user = target_array[0];
  9157. target_domain = target_array[1];
  9158. break;
  9159. default:
  9160. target_user = target_array.slice(0, target_array.length-1).join('@');
  9161. target_domain = target_array[target_array.length-1];
  9162. }
  9163. // Remove the URI scheme (if present).
  9164. target_user = target_user.replace(/^(sips?|tel):/i, '');
  9165. // Remove 'tel' visual separators if the user portion just contains 'tel' number symbols.
  9166. if (/^[\-\.\(\)]*\+?[0-9\-\.\(\)]+$/.test(target_user)) {
  9167. target_user = target_user.replace(/[\-\.\(\)]/g, '');
  9168. }
  9169. // Build the complete SIP URI.
  9170. target = SIP.C.SIP + ':' + SIP.Utils.escapeUser(target_user) + '@' + target_domain;
  9171. // Finally parse the resulting URI.
  9172. if (uri = SIP.URI.parse(target)) {
  9173. return uri;
  9174. } else {
  9175. return;
  9176. }
  9177. } else {
  9178. return;
  9179. }
  9180. },
  9181. /**
  9182. * Hex-escape a SIP URI user.
  9183. * @private
  9184. * @param {String} user
  9185. */
  9186. escapeUser: function(user) {
  9187. // Don't hex-escape ':' (%3A), '+' (%2B), '?' (%3F"), '/' (%2F).
  9188. return encodeURIComponent(decodeURIComponent(user)).replace(/%3A/ig, ':').replace(/%2B/ig, '+').replace(/%3F/ig, '?').replace(/%2F/ig, '/');
  9189. },
  9190. headerize: function(string) {
  9191. var exceptions = {
  9192. 'Call-Id': 'Call-ID',
  9193. 'Cseq': 'CSeq',
  9194. 'Min-Se': 'Min-SE',
  9195. 'Rack': 'RAck',
  9196. 'Rseq': 'RSeq',
  9197. 'Www-Authenticate': 'WWW-Authenticate'
  9198. },
  9199. name = string.toLowerCase().replace(/_/g,'-').split('-'),
  9200. hname = '',
  9201. parts = name.length, part;
  9202. for (part = 0; part < parts; part++) {
  9203. if (part !== 0) {
  9204. hname +='-';
  9205. }
  9206. hname += name[part].charAt(0).toUpperCase()+name[part].substring(1);
  9207. }
  9208. if (exceptions[hname]) {
  9209. hname = exceptions[hname];
  9210. }
  9211. return hname;
  9212. },
  9213. sipErrorCause: function(status_code) {
  9214. var cause;
  9215. for (cause in SIP.C.SIP_ERROR_CAUSES) {
  9216. if (SIP.C.SIP_ERROR_CAUSES[cause].indexOf(status_code) !== -1) {
  9217. return SIP.C.causes[cause];
  9218. }
  9219. }
  9220. return SIP.C.causes.SIP_FAILURE_CODE;
  9221. },
  9222. getReasonPhrase: function getReasonPhrase (code, specific) {
  9223. return specific || SIP.C.REASON_PHRASE[code] || '';
  9224. },
  9225. getReasonHeaderValue: function getReasonHeaderValue (code, reason) {
  9226. reason = SIP.Utils.getReasonPhrase(code, reason);
  9227. return 'SIP ;cause=' + code + ' ;text="' + reason + '"';
  9228. },
  9229. getCancelReason: function getCancelReason (code, reason) {
  9230. if (code && code < 200 || code > 699) {
  9231. throw new TypeError('Invalid status_code: ' + code);
  9232. } else if (code) {
  9233. return SIP.Utils.getReasonHeaderValue(code, reason);
  9234. }
  9235. },
  9236. buildStatusLine: function buildStatusLine (code, reason) {
  9237. code = code || null;
  9238. reason = reason || null;
  9239. // Validate code and reason values
  9240. if (!code || (code < 100 || code > 699)) {
  9241. throw new TypeError('Invalid status_code: '+ code);
  9242. } else if (reason && typeof reason !== 'string' && !(reason instanceof String)) {
  9243. throw new TypeError('Invalid reason_phrase: '+ reason);
  9244. }
  9245. reason = Utils.getReasonPhrase(code, reason);
  9246. return 'SIP/2.0 ' + code + ' ' + reason + '\r\n';
  9247. },
  9248. /**
  9249. * Generate a random Test-Net IP (http://tools.ietf.org/html/rfc5735)
  9250. * @private
  9251. */
  9252. getRandomTestNetIP: function() {
  9253. function getOctet(from,to) {
  9254. return Math.floor(Math.random()*(to-from+1)+from);
  9255. }
  9256. return '192.0.2.' + getOctet(1, 254);
  9257. },
  9258. // MD5 (Message-Digest Algorithm) http://www.webtoolkit.info
  9259. calculateMD5: function(string) {
  9260. function RotateLeft(lValue, iShiftBits) {
  9261. return (lValue<<iShiftBits) | (lValue>>>(32-iShiftBits));
  9262. }
  9263. function AddUnsigned(lX,lY) {
  9264. var lX4,lY4,lX8,lY8,lResult;
  9265. lX8 = (lX & 0x80000000);
  9266. lY8 = (lY & 0x80000000);
  9267. lX4 = (lX & 0x40000000);
  9268. lY4 = (lY & 0x40000000);
  9269. lResult = (lX & 0x3FFFFFFF)+(lY & 0x3FFFFFFF);
  9270. if (lX4 & lY4) {
  9271. return (lResult ^ 0x80000000 ^ lX8 ^ lY8);
  9272. }
  9273. if (lX4 | lY4) {
  9274. if (lResult & 0x40000000) {
  9275. return (lResult ^ 0xC0000000 ^ lX8 ^ lY8);
  9276. } else {
  9277. return (lResult ^ 0x40000000 ^ lX8 ^ lY8);
  9278. }
  9279. } else {
  9280. return (lResult ^ lX8 ^ lY8);
  9281. }
  9282. }
  9283. function F(x,y,z) {
  9284. return (x & y) | ((~x) & z);
  9285. }
  9286. function G(x,y,z) {
  9287. return (x & z) | (y & (~z));
  9288. }
  9289. function H(x,y,z) {
  9290. return (x ^ y ^ z);
  9291. }
  9292. function I(x,y,z) {
  9293. return (y ^ (x | (~z)));
  9294. }
  9295. function FF(a,b,c,d,x,s,ac) {
  9296. a = AddUnsigned(a, AddUnsigned(AddUnsigned(F(b, c, d), x), ac));
  9297. return AddUnsigned(RotateLeft(a, s), b);
  9298. }
  9299. function GG(a,b,c,d,x,s,ac) {
  9300. a = AddUnsigned(a, AddUnsigned(AddUnsigned(G(b, c, d), x), ac));
  9301. return AddUnsigned(RotateLeft(a, s), b);
  9302. }
  9303. function HH(a,b,c,d,x,s,ac) {
  9304. a = AddUnsigned(a, AddUnsigned(AddUnsigned(H(b, c, d), x), ac));
  9305. return AddUnsigned(RotateLeft(a, s), b);
  9306. }
  9307. function II(a,b,c,d,x,s,ac) {
  9308. a = AddUnsigned(a, AddUnsigned(AddUnsigned(I(b, c, d), x), ac));
  9309. return AddUnsigned(RotateLeft(a, s), b);
  9310. }
  9311. function ConvertToWordArray(string) {
  9312. var lWordCount;
  9313. var lMessageLength = string.length;
  9314. var lNumberOfWords_temp1=lMessageLength + 8;
  9315. var lNumberOfWords_temp2=(lNumberOfWords_temp1-(lNumberOfWords_temp1 % 64))/64;
  9316. var lNumberOfWords = (lNumberOfWords_temp2+1)*16;
  9317. var lWordArray=Array(lNumberOfWords-1);
  9318. var lBytePosition = 0;
  9319. var lByteCount = 0;
  9320. while ( lByteCount < lMessageLength ) {
  9321. lWordCount = (lByteCount-(lByteCount % 4))/4;
  9322. lBytePosition = (lByteCount % 4)*8;
  9323. lWordArray[lWordCount] = (lWordArray[lWordCount] | (string.charCodeAt(lByteCount)<<lBytePosition));
  9324. lByteCount++;
  9325. }
  9326. lWordCount = (lByteCount-(lByteCount % 4))/4;
  9327. lBytePosition = (lByteCount % 4)*8;
  9328. lWordArray[lWordCount] = lWordArray[lWordCount] | (0x80<<lBytePosition);
  9329. lWordArray[lNumberOfWords-2] = lMessageLength<<3;
  9330. lWordArray[lNumberOfWords-1] = lMessageLength>>>29;
  9331. return lWordArray;
  9332. }
  9333. function WordToHex(lValue) {
  9334. var WordToHexValue="",WordToHexValue_temp="",lByte,lCount;
  9335. for (lCount = 0;lCount<=3;lCount++) {
  9336. lByte = (lValue>>>(lCount*8)) & 255;
  9337. WordToHexValue_temp = "0" + lByte.toString(16);
  9338. WordToHexValue = WordToHexValue + WordToHexValue_temp.substr(WordToHexValue_temp.length-2,2);
  9339. }
  9340. return WordToHexValue;
  9341. }
  9342. function Utf8Encode(string) {
  9343. string = string.replace(/\r\n/g,"\n");
  9344. var utftext = "";
  9345. for (var n = 0; n < string.length; n++) {
  9346. var c = string.charCodeAt(n);
  9347. if (c < 128) {
  9348. utftext += String.fromCharCode(c);
  9349. }
  9350. else if((c > 127) && (c < 2048)) {
  9351. utftext += String.fromCharCode((c >> 6) | 192);
  9352. utftext += String.fromCharCode((c & 63) | 128);
  9353. }
  9354. else {
  9355. utftext += String.fromCharCode((c >> 12) | 224);
  9356. utftext += String.fromCharCode(((c >> 6) & 63) | 128);
  9357. utftext += String.fromCharCode((c & 63) | 128);
  9358. }
  9359. }
  9360. return utftext;
  9361. }
  9362. var x=[];
  9363. var k,AA,BB,CC,DD,a,b,c,d;
  9364. var S11=7, S12=12, S13=17, S14=22;
  9365. var S21=5, S22=9 , S23=14, S24=20;
  9366. var S31=4, S32=11, S33=16, S34=23;
  9367. var S41=6, S42=10, S43=15, S44=21;
  9368. string = Utf8Encode(string);
  9369. x = ConvertToWordArray(string);
  9370. a = 0x67452301; b = 0xEFCDAB89; c = 0x98BADCFE; d = 0x10325476;
  9371. for (k=0;k<x.length;k+=16) {
  9372. AA=a; BB=b; CC=c; DD=d;
  9373. a=FF(a,b,c,d,x[k+0], S11,0xD76AA478);
  9374. d=FF(d,a,b,c,x[k+1], S12,0xE8C7B756);
  9375. c=FF(c,d,a,b,x[k+2], S13,0x242070DB);
  9376. b=FF(b,c,d,a,x[k+3], S14,0xC1BDCEEE);
  9377. a=FF(a,b,c,d,x[k+4], S11,0xF57C0FAF);
  9378. d=FF(d,a,b,c,x[k+5], S12,0x4787C62A);
  9379. c=FF(c,d,a,b,x[k+6], S13,0xA8304613);
  9380. b=FF(b,c,d,a,x[k+7], S14,0xFD469501);
  9381. a=FF(a,b,c,d,x[k+8], S11,0x698098D8);
  9382. d=FF(d,a,b,c,x[k+9], S12,0x8B44F7AF);
  9383. c=FF(c,d,a,b,x[k+10],S13,0xFFFF5BB1);
  9384. b=FF(b,c,d,a,x[k+11],S14,0x895CD7BE);
  9385. a=FF(a,b,c,d,x[k+12],S11,0x6B901122);
  9386. d=FF(d,a,b,c,x[k+13],S12,0xFD987193);
  9387. c=FF(c,d,a,b,x[k+14],S13,0xA679438E);
  9388. b=FF(b,c,d,a,x[k+15],S14,0x49B40821);
  9389. a=GG(a,b,c,d,x[k+1], S21,0xF61E2562);
  9390. d=GG(d,a,b,c,x[k+6], S22,0xC040B340);
  9391. c=GG(c,d,a,b,x[k+11],S23,0x265E5A51);
  9392. b=GG(b,c,d,a,x[k+0], S24,0xE9B6C7AA);
  9393. a=GG(a,b,c,d,x[k+5], S21,0xD62F105D);
  9394. d=GG(d,a,b,c,x[k+10],S22,0x2441453);
  9395. c=GG(c,d,a,b,x[k+15],S23,0xD8A1E681);
  9396. b=GG(b,c,d,a,x[k+4], S24,0xE7D3FBC8);
  9397. a=GG(a,b,c,d,x[k+9], S21,0x21E1CDE6);
  9398. d=GG(d,a,b,c,x[k+14],S22,0xC33707D6);
  9399. c=GG(c,d,a,b,x[k+3], S23,0xF4D50D87);
  9400. b=GG(b,c,d,a,x[k+8], S24,0x455A14ED);
  9401. a=GG(a,b,c,d,x[k+13],S21,0xA9E3E905);
  9402. d=GG(d,a,b,c,x[k+2], S22,0xFCEFA3F8);
  9403. c=GG(c,d,a,b,x[k+7], S23,0x676F02D9);
  9404. b=GG(b,c,d,a,x[k+12],S24,0x8D2A4C8A);
  9405. a=HH(a,b,c,d,x[k+5], S31,0xFFFA3942);
  9406. d=HH(d,a,b,c,x[k+8], S32,0x8771F681);
  9407. c=HH(c,d,a,b,x[k+11],S33,0x6D9D6122);
  9408. b=HH(b,c,d,a,x[k+14],S34,0xFDE5380C);
  9409. a=HH(a,b,c,d,x[k+1], S31,0xA4BEEA44);
  9410. d=HH(d,a,b,c,x[k+4], S32,0x4BDECFA9);
  9411. c=HH(c,d,a,b,x[k+7], S33,0xF6BB4B60);
  9412. b=HH(b,c,d,a,x[k+10],S34,0xBEBFBC70);
  9413. a=HH(a,b,c,d,x[k+13],S31,0x289B7EC6);
  9414. d=HH(d,a,b,c,x[k+0], S32,0xEAA127FA);
  9415. c=HH(c,d,a,b,x[k+3], S33,0xD4EF3085);
  9416. b=HH(b,c,d,a,x[k+6], S34,0x4881D05);
  9417. a=HH(a,b,c,d,x[k+9], S31,0xD9D4D039);
  9418. d=HH(d,a,b,c,x[k+12],S32,0xE6DB99E5);
  9419. c=HH(c,d,a,b,x[k+15],S33,0x1FA27CF8);
  9420. b=HH(b,c,d,a,x[k+2], S34,0xC4AC5665);
  9421. a=II(a,b,c,d,x[k+0], S41,0xF4292244);
  9422. d=II(d,a,b,c,x[k+7], S42,0x432AFF97);
  9423. c=II(c,d,a,b,x[k+14],S43,0xAB9423A7);
  9424. b=II(b,c,d,a,x[k+5], S44,0xFC93A039);
  9425. a=II(a,b,c,d,x[k+12],S41,0x655B59C3);
  9426. d=II(d,a,b,c,x[k+3], S42,0x8F0CCC92);
  9427. c=II(c,d,a,b,x[k+10],S43,0xFFEFF47D);
  9428. b=II(b,c,d,a,x[k+1], S44,0x85845DD1);
  9429. a=II(a,b,c,d,x[k+8], S41,0x6FA87E4F);
  9430. d=II(d,a,b,c,x[k+15],S42,0xFE2CE6E0);
  9431. c=II(c,d,a,b,x[k+6], S43,0xA3014314);
  9432. b=II(b,c,d,a,x[k+13],S44,0x4E0811A1);
  9433. a=II(a,b,c,d,x[k+4], S41,0xF7537E82);
  9434. d=II(d,a,b,c,x[k+11],S42,0xBD3AF235);
  9435. c=II(c,d,a,b,x[k+2], S43,0x2AD7D2BB);
  9436. b=II(b,c,d,a,x[k+9], S44,0xEB86D391);
  9437. a=AddUnsigned(a,AA);
  9438. b=AddUnsigned(b,BB);
  9439. c=AddUnsigned(c,CC);
  9440. d=AddUnsigned(d,DD);
  9441. }
  9442. var temp = WordToHex(a)+WordToHex(b)+WordToHex(c)+WordToHex(d);
  9443. return temp.toLowerCase();
  9444. }
  9445. };
  9446. SIP.Utils = Utils;
  9447. };
  9448. },{}],32:[function(require,module,exports){
  9449. "use strict";
  9450. /**
  9451. * @fileoverview WebRTC
  9452. */
  9453. module.exports = function (SIP, environment) {
  9454. var WebRTC;
  9455. WebRTC = {};
  9456. WebRTC.MediaHandler = require('./WebRTC/MediaHandler')(SIP);
  9457. WebRTC.MediaStreamManager = require('./WebRTC/MediaStreamManager')(SIP, environment);
  9458. var _isSupported;
  9459. WebRTC.isSupported = function () {
  9460. if (_isSupported !== undefined) {
  9461. return _isSupported;
  9462. }
  9463. WebRTC.MediaStream = environment.MediaStream;
  9464. WebRTC.getUserMedia = environment.getUserMedia;
  9465. WebRTC.RTCPeerConnection = environment.RTCPeerConnection;
  9466. WebRTC.RTCSessionDescription = environment.RTCSessionDescription;
  9467. if (WebRTC.RTCPeerConnection && WebRTC.RTCSessionDescription) {
  9468. if (WebRTC.getUserMedia) {
  9469. WebRTC.getUserMedia = SIP.Utils.promisify(environment, 'getUserMedia');
  9470. }
  9471. _isSupported = true;
  9472. }
  9473. else {
  9474. _isSupported = false;
  9475. }
  9476. return _isSupported;
  9477. };
  9478. return WebRTC;
  9479. };
  9480. },{"./WebRTC/MediaHandler":33,"./WebRTC/MediaStreamManager":34}],33:[function(require,module,exports){
  9481. "use strict";
  9482. /**
  9483. * @fileoverview MediaHandler
  9484. */
  9485. /* MediaHandler
  9486. * @class PeerConnection helper Class.
  9487. * @param {SIP.Session} session
  9488. * @param {Object} [options]
  9489. * @param {SIP.WebRTC.MediaStreamManager} [options.mediaStreamManager]
  9490. * The MediaStreamManager to acquire/release streams from/to.
  9491. * If not provided, a default MediaStreamManager will be used.
  9492. */
  9493. module.exports = function (SIP) {
  9494. var MediaHandler = function(session, options) {
  9495. options = options || {};
  9496. this.logger = session.ua.getLogger('sip.invitecontext.mediahandler', session.id);
  9497. this.session = session;
  9498. this.localMedia = null;
  9499. this.ready = true;
  9500. this.mediaStreamManager = options.mediaStreamManager || new SIP.WebRTC.MediaStreamManager(this.logger);
  9501. this.audioMuted = false;
  9502. this.videoMuted = false;
  9503. this.local_hold = false;
  9504. this.remote_hold = false;
  9505. // old init() from here on
  9506. var servers = this.prepareIceServers(options.stunServers, options.turnServers);
  9507. this.RTCConstraints = options.RTCConstraints || {};
  9508. this.initPeerConnection(servers);
  9509. function selfEmit(mh, event) {
  9510. if (mh.mediaStreamManager.on) {
  9511. mh.mediaStreamManager.on(event, function () {
  9512. mh.emit.apply(mh, [event].concat(Array.prototype.slice.call(arguments)));
  9513. });
  9514. }
  9515. }
  9516. selfEmit(this, 'userMediaRequest');
  9517. selfEmit(this, 'userMedia');
  9518. selfEmit(this, 'userMediaFailed');
  9519. };
  9520. MediaHandler.defaultFactory = function defaultFactory (session, options) {
  9521. return new MediaHandler(session, options);
  9522. };
  9523. MediaHandler.defaultFactory.isSupported = function () {
  9524. return SIP.WebRTC.isSupported();
  9525. };
  9526. MediaHandler.prototype = Object.create(SIP.MediaHandler.prototype, {
  9527. // Functions the session can use
  9528. isReady: {writable: true, value: function isReady () {
  9529. return this.ready;
  9530. }},
  9531. close: {writable: true, value: function close () {
  9532. this.logger.log('closing PeerConnection');
  9533. this._remoteStreams = [];
  9534. // have to check signalingState since this.close() gets called multiple times
  9535. // TODO figure out why that happens
  9536. if(this.peerConnection && this.peerConnection.signalingState !== 'closed') {
  9537. this.peerConnection.close();
  9538. if(this.localMedia) {
  9539. this.mediaStreamManager.release(this.localMedia);
  9540. }
  9541. }
  9542. }},
  9543. /**
  9544. * @param {SIP.WebRTC.MediaStream | (getUserMedia constraints)} [mediaHint]
  9545. * the MediaStream (or the constraints describing it) to be used for the session
  9546. */
  9547. getDescription: {writable: true, value: function getDescription (mediaHint) {
  9548. var self = this;
  9549. var acquire = self.mediaStreamManager.acquire;
  9550. if (acquire.length > 1) {
  9551. acquire = SIP.Utils.promisify(this.mediaStreamManager, 'acquire', true);
  9552. }
  9553. mediaHint = mediaHint || {};
  9554. if (mediaHint.dataChannel === true) {
  9555. mediaHint.dataChannel = {};
  9556. }
  9557. this.mediaHint = mediaHint;
  9558. /*
  9559. * 1. acquire streams (skip if MediaStreams passed in)
  9560. * 2. addStreams
  9561. * 3. createOffer/createAnswer
  9562. */
  9563. var streamPromise;
  9564. if (self.localMedia) {
  9565. self.logger.log('already have local media');
  9566. streamPromise = SIP.Utils.Promise.resolve(self.localMedia);
  9567. }
  9568. else {
  9569. self.logger.log('acquiring local media');
  9570. streamPromise = acquire.call(self.mediaStreamManager, mediaHint)
  9571. .then(function acquireSucceeded(streams) {
  9572. self.logger.log('acquired local media streams');
  9573. self.localMedia = streams;
  9574. self.session.connecting();
  9575. return streams;
  9576. }, function acquireFailed(err) {
  9577. self.logger.error('unable to acquire streams');
  9578. self.logger.error(err);
  9579. self.session.connecting();
  9580. throw err;
  9581. })
  9582. .then(this.addStreams.bind(this))
  9583. ;
  9584. }
  9585. return streamPromise
  9586. .then(function streamAdditionSucceeded() {
  9587. if (self.hasOffer('remote')) {
  9588. self.peerConnection.ondatachannel = function (evt) {
  9589. self.dataChannel = evt.channel;
  9590. self.emit('dataChannel', self.dataChannel);
  9591. };
  9592. } else if (mediaHint.dataChannel &&
  9593. self.peerConnection.createDataChannel) {
  9594. self.dataChannel = self.peerConnection.createDataChannel(
  9595. 'sipjs',
  9596. mediaHint.dataChannel
  9597. );
  9598. self.emit('dataChannel', self.dataChannel);
  9599. }
  9600. self.render();
  9601. return self.createOfferOrAnswer(self.RTCConstraints);
  9602. })
  9603. .then(function(sdp) {
  9604. sdp = SIP.Hacks.Firefox.hasMissingCLineInSDP(sdp);
  9605. if (self.local_hold) {
  9606. // Don't receive media
  9607. // TODO - This will break for media streams with different directions.
  9608. if (!(/a=(sendrecv|sendonly|recvonly|inactive)/).test(sdp)) {
  9609. sdp = sdp.replace(/(m=[^\r]*\r\n)/g, '$1a=sendonly\r\n');
  9610. } else {
  9611. sdp = sdp.replace(/a=sendrecv\r\n/g, 'a=sendonly\r\n');
  9612. sdp = sdp.replace(/a=recvonly\r\n/g, 'a=inactive\r\n');
  9613. }
  9614. }
  9615. return {
  9616. body: sdp,
  9617. contentType: 'application/sdp'
  9618. };
  9619. })
  9620. ;
  9621. }},
  9622. /**
  9623. * Check if a SIP message contains a session description.
  9624. * @param {SIP.SIPMessage} message
  9625. * @returns {boolean}
  9626. */
  9627. hasDescription: {writeable: true, value: function hasDescription (message) {
  9628. return message.getHeader('Content-Type') === 'application/sdp' && !!message.body;
  9629. }},
  9630. /**
  9631. * Set the session description contained in a SIP message.
  9632. * @param {SIP.SIPMessage} message
  9633. * @returns {Promise}
  9634. */
  9635. setDescription: {writable: true, value: function setDescription (message) {
  9636. var self = this;
  9637. var sdp = message.body;
  9638. this.remote_hold = /a=(sendonly|inactive)/.test(sdp);
  9639. sdp = SIP.Hacks.Firefox.cannotHandleExtraWhitespace(sdp);
  9640. sdp = SIP.Hacks.AllBrowsers.maskDtls(sdp);
  9641. var rawDescription = {
  9642. type: this.hasOffer('local') ? 'answer' : 'offer',
  9643. sdp: sdp
  9644. };
  9645. this.emit('setDescription', rawDescription);
  9646. var description = new SIP.WebRTC.RTCSessionDescription(rawDescription);
  9647. return SIP.Utils.promisify(this.peerConnection, 'setRemoteDescription')(description)
  9648. .catch(function setRemoteDescriptionError(e) {
  9649. self.emit('peerConnection-setRemoteDescriptionFailed', e);
  9650. throw e;
  9651. });
  9652. }},
  9653. /**
  9654. * If the Session associated with this MediaHandler were to be referred,
  9655. * what mediaHint should be provided to the UA's invite method?
  9656. */
  9657. getReferMedia: {writable: true, value: function getReferMedia () {
  9658. function hasTracks (trackGetter, stream) {
  9659. return stream[trackGetter]().length > 0;
  9660. }
  9661. function bothHaveTracks (trackGetter) {
  9662. /* jshint validthis:true */
  9663. return this.getLocalStreams().some(hasTracks.bind(null, trackGetter)) &&
  9664. this.getRemoteStreams().some(hasTracks.bind(null, trackGetter));
  9665. }
  9666. return {
  9667. constraints: {
  9668. audio: bothHaveTracks.call(this, 'getAudioTracks'),
  9669. video: bothHaveTracks.call(this, 'getVideoTracks')
  9670. }
  9671. };
  9672. }},
  9673. updateIceServers: {writeable:true, value: function (options) {
  9674. var servers = this.prepareIceServers(options.stunServers, options.turnServers);
  9675. this.RTCConstraints = options.RTCConstraints || this.RTCConstraints;
  9676. this.initPeerConnection(servers);
  9677. /* once updateIce is implemented correctly, this is better than above
  9678. //no op if browser does not support this
  9679. if (!this.peerConnection.updateIce) {
  9680. return;
  9681. }
  9682. this.peerConnection.updateIce({'iceServers': servers}, this.RTCConstraints);
  9683. */
  9684. }},
  9685. // Functions the session can use, but only because it's convenient for the application
  9686. isMuted: {writable: true, value: function isMuted () {
  9687. return {
  9688. audio: this.audioMuted,
  9689. video: this.videoMuted
  9690. };
  9691. }},
  9692. mute: {writable: true, value: function mute (options) {
  9693. if (this.getLocalStreams().length === 0) {
  9694. return;
  9695. }
  9696. options = options || {
  9697. audio: this.getLocalStreams()[0].getAudioTracks().length > 0,
  9698. video: this.getLocalStreams()[0].getVideoTracks().length > 0
  9699. };
  9700. var audioMuted = false,
  9701. videoMuted = false;
  9702. if (options.audio && !this.audioMuted) {
  9703. audioMuted = true;
  9704. this.audioMuted = true;
  9705. this.toggleMuteAudio(true);
  9706. }
  9707. if (options.video && !this.videoMuted) {
  9708. videoMuted = true;
  9709. this.videoMuted = true;
  9710. this.toggleMuteVideo(true);
  9711. }
  9712. //REVISIT
  9713. if (audioMuted || videoMuted) {
  9714. return {
  9715. audio: audioMuted,
  9716. video: videoMuted
  9717. };
  9718. /*this.session.onmute({
  9719. audio: audioMuted,
  9720. video: videoMuted
  9721. });*/
  9722. }
  9723. }},
  9724. unmute: {writable: true, value: function unmute (options) {
  9725. if (this.getLocalStreams().length === 0) {
  9726. return;
  9727. }
  9728. options = options || {
  9729. audio: this.getLocalStreams()[0].getAudioTracks().length > 0,
  9730. video: this.getLocalStreams()[0].getVideoTracks().length > 0
  9731. };
  9732. var audioUnMuted = false,
  9733. videoUnMuted = false;
  9734. if (options.audio && this.audioMuted) {
  9735. audioUnMuted = true;
  9736. this.audioMuted = false;
  9737. this.toggleMuteAudio(false);
  9738. }
  9739. if (options.video && this.videoMuted) {
  9740. videoUnMuted = true;
  9741. this.videoMuted = false;
  9742. this.toggleMuteVideo(false);
  9743. }
  9744. //REVISIT
  9745. if (audioUnMuted || videoUnMuted) {
  9746. return {
  9747. audio: audioUnMuted,
  9748. video: videoUnMuted
  9749. };
  9750. /*this.session.onunmute({
  9751. audio: audioUnMuted,
  9752. video: videoUnMuted
  9753. });*/
  9754. }
  9755. }},
  9756. hold: {writable: true, value: function hold () {
  9757. this.local_hold = true;
  9758. this.toggleMuteAudio(true);
  9759. this.toggleMuteVideo(true);
  9760. }},
  9761. unhold: {writable: true, value: function unhold () {
  9762. this.local_hold = false;
  9763. if (!this.audioMuted) {
  9764. this.toggleMuteAudio(false);
  9765. }
  9766. if (!this.videoMuted) {
  9767. this.toggleMuteVideo(false);
  9768. }
  9769. }},
  9770. // Functions the application can use, but not the session
  9771. getLocalStreams: {writable: true, value: function getLocalStreams () {
  9772. var pc = this.peerConnection;
  9773. if (pc && pc.signalingState === 'closed') {
  9774. this.logger.warn('peerConnection is closed, getLocalStreams returning []');
  9775. return [];
  9776. }
  9777. return (pc.getLocalStreams && pc.getLocalStreams()) ||
  9778. pc.localStreams || [];
  9779. }},
  9780. getRemoteStreams: {writable: true, value: function getRemoteStreams () {
  9781. var pc = this.peerConnection;
  9782. if (pc && pc.signalingState === 'closed') {
  9783. this.logger.warn('peerConnection is closed, getRemoteStreams returning this._remoteStreams');
  9784. return this._remoteStreams;
  9785. }
  9786. return(pc.getRemoteStreams && pc.getRemoteStreams()) ||
  9787. pc.remoteStreams || [];
  9788. }},
  9789. render: {writable: true, value: function render (renderHint) {
  9790. renderHint = renderHint || (this.mediaHint && this.mediaHint.render);
  9791. if (!renderHint) {
  9792. return false;
  9793. }
  9794. var streamGetters = {
  9795. local: 'getLocalStreams',
  9796. remote: 'getRemoteStreams'
  9797. };
  9798. Object.keys(streamGetters).forEach(function (loc) {
  9799. var streamGetter = streamGetters[loc];
  9800. var streams = this[streamGetter]();
  9801. SIP.WebRTC.MediaStreamManager.render(streams, renderHint[loc]);
  9802. }.bind(this));
  9803. }},
  9804. // Internal functions
  9805. hasOffer: {writable: true, value: function hasOffer (where) {
  9806. var offerState = 'have-' + where + '-offer';
  9807. return this.peerConnection.signalingState === offerState;
  9808. // TODO consider signalingStates with 'pranswer'?
  9809. }},
  9810. prepareIceServers: {writable: true, value: function prepareIceServers (stunServers, turnServers) {
  9811. var servers = [],
  9812. config = this.session.ua.configuration;
  9813. stunServers = stunServers || config.stunServers;
  9814. turnServers = turnServers || config.turnServers;
  9815. [].concat(stunServers).forEach(function (server) {
  9816. servers.push({'urls': server});
  9817. });
  9818. [].concat(turnServers).forEach(function (server) {
  9819. var turnServer = {'urls': server.urls};
  9820. if (server.username) {
  9821. turnServer.username = server.username;
  9822. }
  9823. if (server.password) {
  9824. turnServer.credential = server.password;
  9825. }
  9826. servers.push(turnServer);
  9827. });
  9828. return servers;
  9829. }},
  9830. initPeerConnection: {writable: true, value: function initPeerConnection(servers) {
  9831. var self = this,
  9832. config = this.session.ua.configuration;
  9833. this.onIceCompleted = SIP.Utils.defer();
  9834. this.onIceCompleted.promise.then(function(pc) {
  9835. self.emit('iceGatheringComplete', pc);
  9836. if (self.iceCheckingTimer) {
  9837. SIP.Timers.clearTimeout(self.iceCheckingTimer);
  9838. self.iceCheckingTimer = null;
  9839. }
  9840. });
  9841. if (this.peerConnection) {
  9842. this.peerConnection.close();
  9843. }
  9844. var connConfig = {
  9845. iceServers: servers
  9846. };
  9847. if (config.rtcpMuxPolicy) {
  9848. connConfig.rtcpMuxPolicy = config.rtcpMuxPolicy;
  9849. }
  9850. this.peerConnection = new SIP.WebRTC.RTCPeerConnection(connConfig);
  9851. // Firefox (35.0.1) sometimes throws on calls to peerConnection.getRemoteStreams
  9852. // even if peerConnection.onaddstream was just called. In order to make
  9853. // MediaHandler.prototype.getRemoteStreams work, keep track of them manually
  9854. this._remoteStreams = [];
  9855. this.peerConnection.onaddstream = function(e) {
  9856. self.logger.log('stream added: '+ e.stream.id);
  9857. self._remoteStreams.push(e.stream);
  9858. self.render();
  9859. self.emit('addStream', e);
  9860. };
  9861. this.peerConnection.onremovestream = function(e) {
  9862. self.logger.log('stream removed: '+ e.stream.id);
  9863. };
  9864. this.startIceCheckingTimer = function () {
  9865. if (!self.iceCheckingTimer) {
  9866. self.iceCheckingTimer = SIP.Timers.setTimeout(function() {
  9867. self.logger.log('RTCIceChecking Timeout Triggered after '+config.iceCheckingTimeout+' milliseconds');
  9868. self.onIceCompleted.resolve(this);
  9869. }.bind(this.peerConnection), config.iceCheckingTimeout);
  9870. }
  9871. };
  9872. this.peerConnection.onicecandidate = function(e) {
  9873. self.emit('iceCandidate', e);
  9874. if (e.candidate) {
  9875. self.logger.log('ICE candidate received: '+ (e.candidate.candidate === null ? null : e.candidate.candidate.trim()));
  9876. self.startIceCheckingTimer();
  9877. } else {
  9878. self.onIceCompleted.resolve(this);
  9879. }
  9880. };
  9881. this.peerConnection.onicegatheringstatechange = function () {
  9882. self.logger.log('RTCIceGatheringState changed: ' + this.iceGatheringState);
  9883. if (this.iceGatheringState === 'gathering') {
  9884. self.emit('iceGathering', this);
  9885. }
  9886. if (this.iceGatheringState === 'complete') {
  9887. self.onIceCompleted.resolve(this);
  9888. }
  9889. };
  9890. this.peerConnection.oniceconnectionstatechange = function() { //need e for commented out case
  9891. var stateEvent;
  9892. if (this.iceConnectionState === 'checking') {
  9893. self.startIceCheckingTimer();
  9894. }
  9895. switch (this.iceConnectionState) {
  9896. case 'new':
  9897. stateEvent = 'iceConnection';
  9898. break;
  9899. case 'checking':
  9900. stateEvent = 'iceConnectionChecking';
  9901. break;
  9902. case 'connected':
  9903. stateEvent = 'iceConnectionConnected';
  9904. break;
  9905. case 'completed':
  9906. stateEvent = 'iceConnectionCompleted';
  9907. break;
  9908. case 'failed':
  9909. stateEvent = 'iceConnectionFailed';
  9910. break;
  9911. case 'disconnected':
  9912. stateEvent = 'iceConnectionDisconnected';
  9913. break;
  9914. case 'closed':
  9915. stateEvent = 'iceConnectionClosed';
  9916. break;
  9917. default:
  9918. self.logger.warn('Unknown iceConnection state:', this.iceConnectionState);
  9919. return;
  9920. }
  9921. self.emit(stateEvent, this);
  9922. //Bria state changes are always connected -> disconnected -> connected on accept, so session gets terminated
  9923. //normal calls switch from failed to connected in some cases, so checking for failed and terminated
  9924. /*if (this.iceConnectionState === 'failed') {
  9925. self.session.terminate({
  9926. cause: SIP.C.causes.RTP_TIMEOUT,
  9927. status_code: 200,
  9928. reason_phrase: SIP.C.causes.RTP_TIMEOUT
  9929. });
  9930. } else if (e.currentTarget.iceGatheringState === 'complete' && this.iceConnectionState !== 'closed') {
  9931. self.onIceCompleted(this);
  9932. }*/
  9933. };
  9934. this.peerConnection.onstatechange = function() {
  9935. self.logger.log('PeerConnection state changed to "'+ this.readyState +'"');
  9936. };
  9937. }},
  9938. createOfferOrAnswer: {writable: true, value: function createOfferOrAnswer (constraints) {
  9939. var self = this;
  9940. var methodName;
  9941. var pc = self.peerConnection;
  9942. self.ready = false;
  9943. methodName = self.hasOffer('remote') ? 'createAnswer' : 'createOffer';
  9944. return SIP.Utils.promisify(pc, methodName, true)(constraints)
  9945. .catch(function methodError(e) {
  9946. self.emit('peerConnection-' + methodName + 'Failed', e);
  9947. throw e;
  9948. })
  9949. .then(SIP.Utils.promisify(pc, 'setLocalDescription'))
  9950. .catch(function localDescError(e) {
  9951. self.emit('peerConnection-selLocalDescriptionFailed', e);
  9952. throw e;
  9953. })
  9954. .then(function onSetLocalDescriptionSuccess() {
  9955. var deferred = SIP.Utils.defer();
  9956. if (pc.iceGatheringState === 'complete' && (pc.iceConnectionState === 'connected' || pc.iceConnectionState === 'completed')) {
  9957. deferred.resolve();
  9958. } else {
  9959. self.onIceCompleted.promise.then(deferred.resolve);
  9960. }
  9961. return deferred.promise;
  9962. })
  9963. .then(function readySuccess () {
  9964. var sdp = pc.localDescription.sdp;
  9965. sdp = SIP.Hacks.Chrome.needsExplicitlyInactiveSDP(sdp);
  9966. sdp = SIP.Hacks.AllBrowsers.unmaskDtls(sdp);
  9967. var sdpWrapper = {
  9968. type: methodName === 'createOffer' ? 'offer' : 'answer',
  9969. sdp: sdp
  9970. };
  9971. self.emit('getDescription', sdpWrapper);
  9972. if (self.session.ua.configuration.hackStripTcp) {
  9973. sdpWrapper.sdp = sdpWrapper.sdp.replace(/^a=candidate:\d+ \d+ tcp .*?\r\n/img, "");
  9974. }
  9975. self.ready = true;
  9976. return sdpWrapper.sdp;
  9977. })
  9978. .catch(function createOfferAnswerError (e) {
  9979. self.logger.error(e);
  9980. self.ready = true;
  9981. throw new SIP.Exceptions.GetDescriptionError(e);
  9982. })
  9983. ;
  9984. }},
  9985. addStreams: {writable: true, value: function addStreams (streams) {
  9986. try {
  9987. streams = [].concat(streams);
  9988. streams.forEach(function (stream) {
  9989. this.peerConnection.addStream(stream);
  9990. }, this);
  9991. } catch(e) {
  9992. this.logger.error('error adding stream');
  9993. this.logger.error(e);
  9994. return SIP.Utils.Promise.reject(e);
  9995. }
  9996. return SIP.Utils.Promise.resolve();
  9997. }},
  9998. toggleMuteHelper: {writable: true, value: function toggleMuteHelper (trackGetter, mute) {
  9999. this.getLocalStreams().forEach(function (stream) {
  10000. stream[trackGetter]().forEach(function (track) {
  10001. track.enabled = !mute;
  10002. });
  10003. });
  10004. }},
  10005. toggleMuteAudio: {writable: true, value: function toggleMuteAudio (mute) {
  10006. this.toggleMuteHelper('getAudioTracks', mute);
  10007. }},
  10008. toggleMuteVideo: {writable: true, value: function toggleMuteVideo (mute) {
  10009. this.toggleMuteHelper('getVideoTracks', mute);
  10010. }}
  10011. });
  10012. // Return since it will be assigned to a variable.
  10013. return MediaHandler;
  10014. };
  10015. },{}],34:[function(require,module,exports){
  10016. "use strict";
  10017. /**
  10018. * @fileoverview MediaStreamManager
  10019. */
  10020. /* MediaStreamManager
  10021. * @class Manages the acquisition and release of MediaStreams.
  10022. * @param {mediaHint} [defaultMediaHint] The mediaHint to use if none is provided to acquire()
  10023. */
  10024. module.exports = function (SIP, environment) {
  10025. // Default MediaStreamManager provides single-use streams created with getUserMedia
  10026. var MediaStreamManager = function MediaStreamManager (logger, defaultMediaHint) {
  10027. if (!SIP.WebRTC.isSupported()) {
  10028. throw new SIP.Exceptions.NotSupportedError('Media not supported');
  10029. }
  10030. this.mediaHint = defaultMediaHint || {
  10031. constraints: {audio: true, video: true}
  10032. };
  10033. // map of streams to acquisition manner:
  10034. // true -> passed in as mediaHint.stream
  10035. // false -> getUserMedia
  10036. this.acquisitions = {};
  10037. };
  10038. MediaStreamManager.streamId = function (stream) {
  10039. return stream.getAudioTracks().concat(stream.getVideoTracks())
  10040. .map(function trackId (track) {
  10041. return track.id;
  10042. })
  10043. .join('');
  10044. };
  10045. /**
  10046. * @param {(Array of) MediaStream} streams - The streams to render
  10047. *
  10048. * @param {(Array of) HTMLMediaElement} elements
  10049. * - The <audio>/<video> element(s) that should render the streams
  10050. *
  10051. * Each stream in streams renders to the corresponding element in elements,
  10052. * wrapping around elements if needed.
  10053. */
  10054. MediaStreamManager.render = function render (streams, elements) {
  10055. if (!elements) {
  10056. return false;
  10057. }
  10058. if (Array.isArray(elements) && !elements.length) {
  10059. throw new TypeError('elements must not be empty');
  10060. }
  10061. function attachMediaStream(element, stream) {
  10062. element.srcObject = stream;
  10063. }
  10064. function ensureMediaPlaying (mediaElement) {
  10065. var interval = 100;
  10066. mediaElement.ensurePlayingIntervalId = SIP.Timers.setInterval(function () {
  10067. if (mediaElement.paused && mediaElement.srcObject) {
  10068. mediaElement.play();
  10069. }
  10070. else {
  10071. SIP.Timers.clearInterval(mediaElement.ensurePlayingIntervalId);
  10072. }
  10073. }, interval);
  10074. }
  10075. function attachAndPlay (elements, stream, index) {
  10076. var element = elements[index % elements.length];
  10077. if (typeof element === 'function') {
  10078. element = element();
  10079. }
  10080. (environment.attachMediaStream || attachMediaStream)(element, stream);
  10081. ensureMediaPlaying(element);
  10082. }
  10083. // [].concat "casts" `elements` into an array
  10084. // so forEach works even if `elements` was a single element
  10085. elements = [].concat(elements);
  10086. [].concat(streams).forEach(attachAndPlay.bind(null, elements));
  10087. };
  10088. MediaStreamManager.prototype = Object.create(SIP.EventEmitter.prototype, {
  10089. 'acquire': {writable: true, value: function acquire (mediaHint) {
  10090. mediaHint = Object.keys(mediaHint || {}).length ? mediaHint : this.mediaHint;
  10091. var saveSuccess = function (isHintStream, streams) {
  10092. streams = [].concat(streams);
  10093. streams.forEach(function (stream) {
  10094. var streamId = MediaStreamManager.streamId(stream);
  10095. this.acquisitions[streamId] = !!isHintStream;
  10096. }, this);
  10097. return SIP.Utils.Promise.resolve(streams);
  10098. }.bind(this);
  10099. if (mediaHint.stream) {
  10100. return saveSuccess(true, mediaHint.stream);
  10101. } else {
  10102. // Fallback to audio/video enabled if no mediaHint can be found.
  10103. var constraints = mediaHint.constraints ||
  10104. (this.mediaHint && this.mediaHint.constraints) ||
  10105. {audio: true, video: true};
  10106. var deferred = SIP.Utils.defer();
  10107. /*
  10108. * Make the call asynchronous, so that ICCs have a chance
  10109. * to define callbacks to `userMediaRequest`
  10110. */
  10111. SIP.Timers.setTimeout(function () {
  10112. this.emit('userMediaRequest', constraints);
  10113. var emitThenCall = function (eventName, callback) {
  10114. var callbackArgs = Array.prototype.slice.call(arguments, 2);
  10115. // Emit with all of the arguments from the real callback.
  10116. var newArgs = [eventName].concat(callbackArgs);
  10117. this.emit.apply(this, newArgs);
  10118. return callback.apply(null, callbackArgs);
  10119. }.bind(this);
  10120. if (constraints.audio || constraints.video) {
  10121. deferred.resolve(
  10122. SIP.WebRTC.getUserMedia(constraints)
  10123. .then(
  10124. emitThenCall.bind(this, 'userMedia', saveSuccess.bind(null, false)),
  10125. emitThenCall.bind(this, 'userMediaFailed', function(e){throw e;})
  10126. )
  10127. );
  10128. } else {
  10129. // Local streams were explicitly excluded.
  10130. deferred.resolve([]);
  10131. }
  10132. }.bind(this), 0);
  10133. return deferred.promise;
  10134. }
  10135. }},
  10136. 'release': {writable: true, value: function release (streams) {
  10137. streams = [].concat(streams);
  10138. streams.forEach(function (stream) {
  10139. var streamId = MediaStreamManager.streamId(stream);
  10140. if (this.acquisitions[streamId] === false) {
  10141. stream.getTracks().forEach(function (track) {
  10142. track.stop();
  10143. });
  10144. }
  10145. delete this.acquisitions[streamId];
  10146. }, this);
  10147. }},
  10148. });
  10149. // Return since it will be assigned to a variable.
  10150. return MediaStreamManager;
  10151. };
  10152. },{}],35:[function(require,module,exports){
  10153. (function (global){
  10154. "use strict";
  10155. var toplevel = global.window || global;
  10156. function getPrefixedProperty (object, name) {
  10157. if (object == null) {
  10158. return;
  10159. }
  10160. var capitalizedName = name.charAt(0).toUpperCase() + name.slice(1);
  10161. var prefixedNames = [name, 'webkit' + capitalizedName, 'moz' + capitalizedName];
  10162. for (var i in prefixedNames) {
  10163. var property = object[prefixedNames[i]];
  10164. if (property) {
  10165. return property.bind(object);
  10166. }
  10167. }
  10168. }
  10169. module.exports = {
  10170. WebSocket: toplevel.WebSocket,
  10171. Transport: require('./Transport'),
  10172. open: toplevel.open,
  10173. Promise: toplevel.Promise,
  10174. timers: toplevel,
  10175. // Console is not defined in ECMAScript, so just in case...
  10176. console: toplevel.console || {
  10177. debug: function () {},
  10178. log: function () {},
  10179. warn: function () {},
  10180. error: function () {}
  10181. },
  10182. MediaStream: getPrefixedProperty(toplevel, 'MediaStream'),
  10183. getUserMedia: getPrefixedProperty(toplevel.navigator, 'getUserMedia'),
  10184. RTCPeerConnection: getPrefixedProperty(toplevel, 'RTCPeerConnection'),
  10185. RTCSessionDescription: getPrefixedProperty(toplevel, 'RTCSessionDescription'),
  10186. addEventListener: getPrefixedProperty(toplevel, 'addEventListener'),
  10187. removeEventListener: getPrefixedProperty(toplevel, 'removeEventListener'),
  10188. HTMLMediaElement: toplevel.HTMLMediaElement,
  10189. attachMediaStream: toplevel.attachMediaStream,
  10190. createObjectURL: toplevel.URL && toplevel.URL.createObjectURL,
  10191. revokeObjectURL: toplevel.URL && toplevel.URL.revokeObjectURL
  10192. };
  10193. }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
  10194. },{"./Transport":28}],36:[function(require,module,exports){
  10195. "use strict";
  10196. module.exports = require('./SIP')(require('./environment'));
  10197. },{"./SIP":19,"./environment":35}]},{},[36])(36)
  10198. });