/* * JsSIP version 0.4.0-devel * Copyright (c) 2012-2014 José Luis Millán - Versatica * Homepage: http://jssip.net * License: http://jssip.net/license */ /*global console: false*/ /** * @name JsSIP * @namespace */ (function(window) { var JsSIP = (function() { "use strict"; var JsSIP = {}; Object.defineProperties(JsSIP, { version: { get: function(){ return '0.4.0-devel'; } }, name: { get: function(){ return 'JsSIP'; } } }); return JsSIP; }()); (function(JsSIP) { var Logger = (function() { var Logger = function(logger, category, label) { this.logger = logger; this.category = category; this.label = label; }; Logger.prototype.debug = function(content) { this.logger.debug(this.category, this.label, content); }; Logger.prototype.log = function(content) { this.logger.log(this.category, this.label, content); }; Logger.prototype.warn = function(content) { this.logger.warn(this.category, this.label, content); }; Logger.prototype.error = function(content) { this.logger.error(this.category, this.label, content); }; return Logger; }()); var LoggerFactory = function() { var logger, levels = { 'error': 0, 'warn': 1, 'log': 2, 'debug': 3 }, level = 2, builtinEnabled = true, connector = null; this.loggers = {}; logger = this.getLogger('jssip.loggerfactory'); Object.defineProperties(this, { builtinEnabled: { get: function(){ return builtinEnabled; }, set: function(value){ if (typeof value === 'boolean') { builtinEnabled = value; } else { logger.error('invalid "builtinEnabled" parameter value: '+ window.JSON.stringify(value)); } } }, level: { get: function() {return level; }, set: function(value) { if (value >= 0 && value <=3) { level = value; } else if (value > 3) { level = 3; } else if (levels.hasOwnProperty(value)) { level = levels[value]; } else { logger.error('invalid "level" parameter value: '+ window.JSON.stringify(value)); } } }, connector: { get: function() {return connector; }, set: function(value){ if(value === null || value === "" || value === undefined) { connector = null; } else if (typeof value === 'function') { connector = value; } else { logger.error('invalid "connector" parameter value: '+ window.JSON.stringify(value)); } } } }); }; LoggerFactory.prototype.print = function(target, category, label, content) { var prefix = []; prefix.push(new Date()); prefix.push(category); if (label) { prefix.push(label); } prefix.push(''); if (typeof content === 'string') { target.call(console, prefix.join(' | ') + content); } else { target.call(console, content); } }; LoggerFactory.prototype.debug = function(category, label, content) { if (this.level === 3) { if (this.builtinEnabled) { this.print(console.debug, category, label, content); } if (this.connector) { this.connector('debug', category, label, content); } } }; LoggerFactory.prototype.log = function(category, label, content) { if (this.level >= 2) { if (this.builtinEnabled) { this.print(console.log, category, label, content); } if (this.connector) { this.connector('log', category, label, content); } } }; LoggerFactory.prototype.warn = function(category, label, content) { if (this.level >= 1) { if (this.builtinEnabled) { this.print(console.warn, category, label, content); } if (this.connector) { this.connector('warn', category, label, content); } } }; LoggerFactory.prototype.error = function(category, label, content) { if (this.builtinEnabled) { this.print(console.error,category, label, content); } if (this.connector) { this.connector('error', category, label, content); } }; LoggerFactory.prototype.getLogger = function(category, label) { var logger; if (label && this.level === 3) { return new Logger(this, category, label); } else if (this.loggers[category]) { return this.loggers[category]; } else { logger = new Logger(this, category); this.loggers[category] = logger; return logger; } }; JsSIP.LoggerFactory = LoggerFactory; }(JsSIP)); /** * @fileoverview EventEmitter */ /** * @augments JsSIP * @class Class creating an event emitter. */ (function(JsSIP) { var EventEmitter, Event, logger = new JsSIP.LoggerFactory().getLogger('jssip.eventemitter'), C = { MAX_LISTENERS: 10 }; EventEmitter = function(){}; EventEmitter.prototype = { /** * Initialize events dictionaries. * @param {Array} events */ initEvents: function(events) { var idx; if (!this.logger) { this.logger = logger; } this.maxListeners = C.MAX_LISTENERS; this.events = {}; this.oneTimeListeners = {}; for (idx in events) { this.events[events[idx]] = []; this.oneTimeListeners[events[idx]] = []; } }, /** * Check whether an event exists or not. * @param {String} event * @returns {Boolean} */ checkEvent: function(event) { return !!this.events[event]; }, /** * Add a listener to the end of the listeners array for the specified event. * @param {String} event * @param {Function} listener */ addListener: function(event, listener) { if (listener === undefined) { return; } else if (typeof listener !== 'function') { this.logger.error('listener must be a function'); return; } else if (!this.checkEvent(event)) { this.logger.error('unable to add a listener to a nonexistent event'+ event); return; } if (this.events[event].length >= this.maxListeners) { this.logger.warn('max listeners exceeded for event '+ event); } this.events[event].push(listener); }, on: function(event, listener) { this.addListener(event, listener); }, /** * Add a one time listener for the specified event. * The listener is invoked only the next time the event is fired, then it is removed. * @param {String} event * @param {Function} listener */ once: function(event, listener) { this.on(event, listener); this.oneTimeListeners[event].push(listener); }, /** * Remove a listener from the listener array for the specified event. * Note that the order of the array elements will change after removing the listener * @param {String} event * @param {Function} listener */ removeListener: function(event, listener) { var events, length, idx = 0; if (listener === undefined) { return; } else if (typeof listener !== 'function') { this.logger.error('listener must be a function'); } else if (!this.checkEvent(event)) { this.logger.error('unable to remove a listener from a nonexistent event'+ event); return; } events = this.events[event]; length = events.length; while (idx < length) { if (events[idx] === listener) { events.splice(idx,1); } else { idx ++; } } }, /** * Remove all listeners from the listener array for the specified event. * @param {String} event */ removeAllListener: function(event) { if (!this.checkEvent(event)) { this.logger.error('unable to remove listeners from a nonexistent event'+ event); return; } this.events[event] = []; this.oneTimeListeners[event] = []; }, /** * By default EventEmitter will print a warning * if more than C.MAX_LISTENERS listeners are added for a particular event. * This function allows that limit to be modified. * @param {Number} listeners */ setMaxListeners: function(listeners) { if (typeof listeners !== 'number' || listeners < 0) { this.logger.error('listeners must be a positive number'); return; } this.maxListeners = listeners; }, /** * Get the listeners for a specific event. * @param {String} event * @returns {Array} Array of listeners for the specified event. */ listeners: function(event) { if (!this.checkEvent(event)) { this.logger.error('no event '+ event); return; } return this.events[event]; }, /** * Execute each of the listeners in order with the supplied arguments. * @param {String} events * @param {Array} args */ emit: function(event, sender, data) { var listeners, e, idx; if (!this.checkEvent(event)) { this.logger.error('unable to emit a nonexistent event'+ event); return; } this.logger.log('emitting event '+ event); listeners = this.events[event]; e = new JsSIP.Event(event, sender, data); listeners.forEach(function (callback) { try { callback.call(null, e); } catch(err) { this.logger.error(err.stack); } }); // Remove one time listeners for (idx in this.oneTimeListeners[event]) { this.removeListener(event, this.oneTimeListeners[event][idx]); } this.oneTimeListeners[event] = []; } }; Event = function(type, sender, data) { this.type = type; this.sender= sender; this.data = data; }; EventEmitter.C = C; JsSIP.EventEmitter = EventEmitter; JsSIP.Event = Event; }(JsSIP)); /** * @fileoverview JsSIP Constants */ /** * JsSIP Constants. * @augments JsSIP */ JsSIP.C= { USER_AGENT: JsSIP.name +' '+ JsSIP.version, // SIP scheme SIP: 'sip', SIPS: 'sips', // End and Failure causes causes: { // Generic error causes CONNECTION_ERROR: 'Connection Error', REQUEST_TIMEOUT: 'Request Timeout', SIP_FAILURE_CODE: 'SIP Failure Code', INTERNAL_ERROR: 'Internal Error', // SIP error causes BUSY: 'Busy', REJECTED: 'Rejected', REDIRECTED: 'Redirected', UNAVAILABLE: 'Unavailable', NOT_FOUND: 'Not Found', ADDRESS_INCOMPLETE: 'Address Incomplete', INCOMPATIBLE_SDP: 'Incompatible SDP', AUTHENTICATION_ERROR: 'Authentication Error', DIALOG_ERROR: 'Dialog Error', // Session error causes WEBRTC_NOT_SUPPORTED: 'WebRTC Not Supported', WEBRTC_ERROR: 'WebRTC Error', CANCELED: 'Canceled', NO_ANSWER: 'No Answer', EXPIRES: 'Expires', NO_ACK: 'No ACK', USER_DENIED_MEDIA_ACCESS: 'User Denied Media Access', BAD_MEDIA_DESCRIPTION: 'Bad Media Description', RTP_TIMEOUT: 'RTP Timeout' }, SIP_ERROR_CAUSES: { REDIRECTED: [300,301,302,305,380], BUSY: [486,600], REJECTED: [403,603], NOT_FOUND: [404,604], UNAVAILABLE: [480,410,408,430], ADDRESS_INCOMPLETE: [484], INCOMPATIBLE_SDP: [488,606], AUTHENTICATION_ERROR:[401,407] }, // SIP Methods ACK: 'ACK', BYE: 'BYE', CANCEL: 'CANCEL', INFO: 'INFO', INVITE: 'INVITE', MESSAGE: 'MESSAGE', NOTIFY: 'NOTIFY', OPTIONS: 'OPTIONS', REGISTER: 'REGISTER', UPDATE: 'UPDATE', SUBSCRIBE: 'SUBSCRIBE', /* SIP Response Reasons * DOC: http://www.iana.org/assignments/sip-parameters * Copied from https://github.com/versatica/OverSIP/blob/master/lib/oversip/sip/constants.rb#L7 */ REASON_PHRASE: { 100: 'Trying', 180: 'Ringing', 181: 'Call Is Being Forwarded', 182: 'Queued', 183: 'Session Progress', 199: 'Early Dialog Terminated', // draft-ietf-sipcore-199 200: 'OK', 202: 'Accepted', // RFC 3265 204: 'No Notification', //RFC 5839 300: 'Multiple Choices', 301: 'Moved Permanently', 302: 'Moved Temporarily', 305: 'Use Proxy', 380: 'Alternative Service', 400: 'Bad Request', 401: 'Unauthorized', 402: 'Payment Required', 403: 'Forbidden', 404: 'Not Found', 405: 'Method Not Allowed', 406: 'Not Acceptable', 407: 'Proxy Authentication Required', 408: 'Request Timeout', 410: 'Gone', 412: 'Conditional Request Failed', // RFC 3903 413: 'Request Entity Too Large', 414: 'Request-URI Too Long', 415: 'Unsupported Media Type', 416: 'Unsupported URI Scheme', 417: 'Unknown Resource-Priority', // RFC 4412 420: 'Bad Extension', 421: 'Extension Required', 422: 'Session Interval Too Small', // RFC 4028 423: 'Interval Too Brief', 428: 'Use Identity Header', // RFC 4474 429: 'Provide Referrer Identity', // RFC 3892 430: 'Flow Failed', // RFC 5626 433: 'Anonymity Disallowed', // RFC 5079 436: 'Bad Identity-Info', // RFC 4474 437: 'Unsupported Certificate', // RFC 4744 438: 'Invalid Identity Header', // RFC 4744 439: 'First Hop Lacks Outbound Support', // RFC 5626 440: 'Max-Breadth Exceeded', // RFC 5393 469: 'Bad Info Package', // draft-ietf-sipcore-info-events 470: 'Consent Needed', // RFC 5360 478: 'Unresolvable Destination', // Custom code copied from Kamailio. 480: 'Temporarily Unavailable', 481: 'Call/Transaction Does Not Exist', 482: 'Loop Detected', 483: 'Too Many Hops', 484: 'Address Incomplete', 485: 'Ambiguous', 486: 'Busy Here', 487: 'Request Terminated', 488: 'Not Acceptable Here', 489: 'Bad Event', // RFC 3265 491: 'Request Pending', 493: 'Undecipherable', 494: 'Security Agreement Required', // RFC 3329 500: 'Server Internal Error', 501: 'Not Implemented', 502: 'Bad Gateway', 503: 'Service Unavailable', 504: 'Server Time-out', 505: 'Version Not Supported', 513: 'Message Too Large', 580: 'Precondition Failure', // RFC 3312 600: 'Busy Everywhere', 603: 'Decline', 604: 'Does Not Exist Anywhere', 606: 'Not Acceptable' } }; /** * @fileoverview Exceptions */ /** * JsSIP Exceptions. * @augments JsSIP */ (function(JsSIP) { var Exceptions; Exceptions= { ConfigurationError: (function(){ var exception = function(parameter, value) { this.code = 1; this.name = 'CONFIGURATION_ERROR'; this.parameter = parameter; this.value = value; this.message = (!this.value)? 'Missing parameter: '+ this.parameter : 'Invalid value '+ window.JSON.stringify(this.value) +' for parameter "'+ this.parameter +'"'; }; exception.prototype = new Error(); return exception; }()), InvalidStateError: (function(){ var exception = function(status) { this.code = 2; this.name = 'INVALID_STATE_ERROR'; this.status = status; this.message = 'Invalid status: '+ status; }; exception.prototype = new Error(); return exception; }()), NotSupportedError: (function(){ var exception = function(message) { this.code = 3; this.name = 'NOT_SUPPORTED_ERROR'; this.message = message; }; exception.prototype = new Error(); return exception; }()), NotReadyError: (function(){ var exception = function(message) { this.code = 4; this.name = 'NOT_READY_ERROR'; this.message = message; }; exception.prototype = new Error(); return exception; }()) }; JsSIP.Exceptions = Exceptions; }(JsSIP)); /** * @fileoverview SIP TIMERS */ /** * @augments JsSIP */ (function(JsSIP) { var Timers, T1 = 500, T2 = 4000, T4 = 5000; Timers = { T1: T1, T2: T2, T4: T4, TIMER_B: 64 * T1, TIMER_D: 0 * T1, TIMER_F: 64 * T1, TIMER_H: 64 * T1, TIMER_I: 0 * T1, TIMER_J: 0 * T1, TIMER_K: 0 * T4, TIMER_L: 64 * T1, TIMER_M: 64 * T1, PROVISIONAL_RESPONSE_INTERVAL: 60000 // See RFC 3261 Section 13.3.1.1 }; JsSIP.Timers = Timers; }(JsSIP)); /** * @fileoverview Transport */ /** * @augments JsSIP * @class Transport * @param {JsSIP.UA} ua * @param {Object} server ws_server Object */ (function(JsSIP) { var Transport, C = { // Transport status codes STATUS_READY: 0, STATUS_DISCONNECTED: 1, STATUS_ERROR: 2 }; Transport = function(ua, server) { this.logger = ua.getLogger('jssip.transport'); this.ua = ua; this.ws = null; this.server = server; this.reconnection_attempts = 0; this.closed = false; this.connected = false; this.reconnectTimer = null; this.lastTransportError = {}; this.ua.transport = this; // Connect this.connect(); }; Transport.prototype = { /** * Send a message. * @param {JsSIP.OutgoingRequest|String} msg * @returns {Boolean} */ send: function(msg) { var message = msg.toString(); if(this.ws && this.ws.readyState === WebSocket.OPEN) { if (this.ua.configuration.trace_sip === true) { this.logger.log('sending WebSocket message:\n\n' + message + '\n'); } this.ws.send(message); return true; } else { this.logger.warn('unable to send message, WebSocket is not open'); return false; } }, /** * Disconnect socket. */ disconnect: function() { if(this.ws) { // Clear reconnectTimer window.clearTimeout(this.reconnectTimer); this.closed = true; this.logger.log('closing WebSocket ' + this.server.ws_uri); this.ws.close(); } if (this.reconnectTimer !== null) { window.clearTimeout(this.reconnectTimer); this.reconnectTimer = null; this.ua.emit('disconnected', this.ua, { transport: this, code: this.lastTransportError.code, reason: this.lastTransportError.reason }); } }, /** * Connect socket. */ connect: function() { var transport = this; if(this.ws && (this.ws.readyState === WebSocket.OPEN || this.ws.readyState === WebSocket.CONNECTING)) { this.logger.log('WebSocket ' + this.server.ws_uri + ' is already connected'); return false; } if(this.ws) { this.ws.close(); } this.logger.log('connecting to WebSocket ' + this.server.ws_uri); this.ua.onTransportConnecting(this, (this.reconnection_attempts === 0)?1:this.reconnection_attempts); try { this.ws = new WebSocket(this.server.ws_uri, 'sip'); this.ws.binaryType = 'arraybuffer'; this.ws.onopen = function() { transport.onOpen(); }; this.ws.onclose = function(e) { transport.onClose(e); }; this.ws.onmessage = function(e) { transport.onMessage(e); }; this.ws.onerror = function(e) { transport.onError(e); }; } catch(e) { this.logger.warn('error connecting to WebSocket ' + this.server.ws_uri + ': ' + e); this.lastTransportError.code = null; this.lastTransportError.reason = e.message; this.ua.onTransportError(this); } }, // Transport Event Handlers /** * @event * @param {event} e */ onOpen: function() { this.connected = true; this.logger.log('WebSocket ' + this.server.ws_uri + ' connected'); // Clear reconnectTimer since we are not disconnected if (this.reconnectTimer !== null) { window.clearTimeout(this.reconnectTimer); this.reconnectTimer = null; } // Reset reconnection_attempts this.reconnection_attempts = 0; // Disable closed this.closed = false; // Trigger onTransportConnected callback this.ua.onTransportConnected(this); }, /** * @event * @param {event} e */ onClose: function(e) { var connected_before = this.connected; this.connected = false; this.lastTransportError.code = e.code; this.lastTransportError.reason = e.reason; this.logger.log('WebSocket disconnected (code: ' + e.code + (e.reason? '| reason: ' + e.reason : '') +')'); if(e.wasClean === false) { this.logger.warn('WebSocket abrupt disconnection'); } // Transport was connected if(connected_before === true) { this.ua.onTransportClosed(this); // Check whether the user requested to close. if(!this.closed) { this.reConnect(); } else { this.ua.emit('disconnected', this.ua, { transport: this, code: this.lastTransportError.code, reason: this.lastTransportError.reason }); } } else { // This is the first connection attempt //Network error this.ua.onTransportError(this); } }, /** * @event * @param {event} e */ onMessage: function(e) { var message, transaction, data = e.data; // CRLF Keep Alive response from server. Ignore it. if(data === '\r\n') { if (this.ua.configuration.trace_sip === true) { this.logger.log('received WebSocket message with CRLF Keep Alive response'); } return; } // WebSocket binary message. else if (typeof data !== 'string') { try { data = String.fromCharCode.apply(null, new Uint8Array(data)); } catch(evt) { this.logger.warn('received WebSocket binary message failed to be converted into string, message discarded'); return; } if (this.ua.configuration.trace_sip === true) { this.logger.log('received WebSocket binary message:\n\n' + data + '\n'); } } // WebSocket text message. else { if (this.ua.configuration.trace_sip === true) { this.logger.log('received WebSocket text message:\n\n' + data + '\n'); } } message = JsSIP.Parser.parseMessage(data, this.ua); if (!message) { return; } if(this.ua.status === JsSIP.UA.C.STATUS_USER_CLOSED && message instanceof JsSIP.IncomingRequest) { return; } // Do some sanity check if(JsSIP.sanityCheck(message, this.ua, this)) { if(message instanceof JsSIP.IncomingRequest) { message.transport = this; this.ua.receiveRequest(message); } else if(message instanceof JsSIP.IncomingResponse) { /* Unike stated in 18.1.2, if a response does not match * any transaction, it is discarded here and no passed to the core * in order to be discarded there. */ switch(message.method) { case JsSIP.C.INVITE: transaction = this.ua.transactions.ict[message.via_branch]; if(transaction) { transaction.receiveResponse(message); } break; case JsSIP.C.ACK: // Just in case ;-) break; default: transaction = this.ua.transactions.nict[message.via_branch]; if(transaction) { transaction.receiveResponse(message); } break; } } } }, /** * @event * @param {event} e */ onError: function(e) { this.logger.warn('WebSocket connection error: ' + e); }, /** * Reconnection attempt logic. * @private */ reConnect: function() { var transport = this; this.reconnection_attempts += 1; if(this.reconnection_attempts > this.ua.configuration.ws_server_max_reconnection) { this.logger.warn('maximum reconnection attempts for WebSocket ' + this.server.ws_uri); this.ua.onTransportError(this); } else { this.logger.log('trying to reconnect to WebSocket ' + this.server.ws_uri + ' (reconnection attempt ' + this.reconnection_attempts + ')'); this.reconnectTimer = window.setTimeout(function() { transport.connect(); transport.reconnectTimer = null; }, this.ua.configuration.ws_server_reconnection_timeout * 1000); } } }; Transport.C = C; JsSIP.Transport = Transport; }(JsSIP)); /** * @fileoverview SIP Message Parser */ /** * Extract and parse every header of a SIP message. * @augments JsSIP * @namespace */ (function(JsSIP) { var Parser; function getHeader(data, headerStart) { var // 'start' position of the header. start = headerStart, // 'end' position of the header. end = 0, // 'partial end' position of the header. partialEnd = 0; //End of message. if (data.substring(start, start + 2).match(/(^\r\n)/)) { return -2; } while(end === 0) { // Partial End of Header. partialEnd = data.indexOf('\r\n', start); // 'indexOf' returns -1 if the value to be found never occurs. if (partialEnd === -1) { return partialEnd; } if(!data.substring(partialEnd + 2, partialEnd + 4).match(/(^\r\n)/) && data.charAt(partialEnd + 2).match(/(^\s+)/)) { // Not the end of the message. Continue from the next position. start = partialEnd + 2; } else { end = partialEnd; } } return end; } function parseHeader(message, data, headerStart, headerEnd) { var header, idx, length, parsed, hcolonIndex = data.indexOf(':', headerStart), headerName = data.substring(headerStart, hcolonIndex).trim(), headerValue = data.substring(hcolonIndex + 1, headerEnd).trim(); // If header-field is well-known, parse it. switch(headerName.toLowerCase()) { case 'via': case 'v': message.addHeader('via', headerValue); if(message.getHeaders('via').length === 1) { parsed = message.parseHeader('Via'); if(parsed) { message.via = parsed; message.via_branch = parsed.branch; } } else { parsed = 0; } break; case 'from': case 'f': message.setHeader('from', headerValue); parsed = message.parseHeader('from'); if(parsed) { message.from = parsed; message.from_tag = parsed.getParam('tag'); } break; case 'to': case 't': message.setHeader('to', headerValue); parsed = message.parseHeader('to'); if(parsed) { message.to = parsed; message.to_tag = parsed.getParam('tag'); } break; case 'record-route': parsed = JsSIP.Grammar.parse(headerValue, 'Record_Route'); if (parsed === -1) { parsed = undefined; } length = parsed.length; for (idx = 0; idx < length; idx++) { header = parsed[idx]; message.addHeader('record-route', headerValue.substring(header.possition, header.offset)); message.headers['Record-Route'][message.getHeaders('record-route').length - 1].parsed = header.parsed; } break; case 'call-id': case 'i': message.setHeader('call-id', headerValue); parsed = message.parseHeader('call-id'); if(parsed) { message.call_id = headerValue; } break; case 'contact': case 'm': parsed = JsSIP.Grammar.parse(headerValue, 'Contact'); if (parsed === -1) { parsed = undefined; } length = parsed.length; for (idx = 0; idx < length; idx++) { header = parsed[idx]; message.addHeader('contact', headerValue.substring(header.possition, header.offset)); message.headers['Contact'][message.getHeaders('contact').length - 1].parsed = header.parsed; } break; case 'content-length': case 'l': message.setHeader('content-length', headerValue); parsed = message.parseHeader('content-length'); break; case 'content-type': case 'c': message.setHeader('content-type', headerValue); parsed = message.parseHeader('content-type'); break; case 'cseq': message.setHeader('cseq', headerValue); parsed = message.parseHeader('cseq'); if(parsed) { message.cseq = parsed.value; } if(message instanceof JsSIP.IncomingResponse) { message.method = parsed.method; } break; case 'max-forwards': message.setHeader('max-forwards', headerValue); parsed = message.parseHeader('max-forwards'); break; case 'www-authenticate': message.setHeader('www-authenticate', headerValue); parsed = message.parseHeader('www-authenticate'); break; case 'proxy-authenticate': message.setHeader('proxy-authenticate', headerValue); parsed = message.parseHeader('proxy-authenticate'); break; default: // Do not parse this header. message.setHeader(headerName, headerValue); parsed = 0; } if (parsed === undefined) { return { error: 'error parsing header "'+ headerName +'"' }; } else { return true; } } /** Parse SIP Message * @function * @param {String} message SIP message. * @param {Object} logger object. * @returns {JsSIP.IncomingRequest|JsSIP.IncomingResponse|undefined} */ Parser = {}; Parser.parseMessage = function(data, ua) { var message, firstLine, contentLength, bodyStart, parsed, headerStart = 0, headerEnd = data.indexOf('\r\n'), logger = ua.getLogger('jssip.parser'); if(headerEnd === -1) { logger.warn('no CRLF found, not a SIP message, discarded'); return; } // Parse first line. Check if it is a Request or a Reply. firstLine = data.substring(0, headerEnd); parsed = JsSIP.Grammar.parse(firstLine, 'Request_Response'); if(parsed === -1) { logger.warn('error parsing first line of SIP message: "' + firstLine + '"'); return; } else if(!parsed.status_code) { message = new JsSIP.IncomingRequest(ua); message.method = parsed.method; message.ruri = parsed.uri; } else { message = new JsSIP.IncomingResponse(ua); message.status_code = parsed.status_code; message.reason_phrase = parsed.reason_phrase; } message.data = data; headerStart = headerEnd + 2; /* Loop over every line in data. Detect the end of each header and parse * it or simply add to the headers collection. */ while(true) { headerEnd = getHeader(data, headerStart); // The SIP message has normally finished. if(headerEnd === -2) { bodyStart = headerStart + 2; break; } // data.indexOf returned -1 due to a malformed message. else if(headerEnd === -1) { parsed.error('malformed message'); return; } parsed = parseHeader(message, data, headerStart, headerEnd); if(parsed !== true) { logger.error(parsed.error); return; } headerStart = headerEnd + 2; } /* RFC3261 18.3. * If there are additional bytes in the transport packet * beyond the end of the body, they MUST be discarded. */ if(message.hasHeader('content-length')) { contentLength = message.getHeader('content-length'); message.body = data.substr(bodyStart, contentLength); } else { message.body = data.substring(bodyStart); } return message; }; JsSIP.Parser = Parser; }(JsSIP)); /** * @fileoverview SIP Message */ (function(JsSIP) { var OutgoingRequest, IncomingMessage, IncomingRequest, IncomingResponse; /** * @augments JsSIP * @class Class for outgoing SIP request. * @param {String} method request method * @param {String} ruri request uri * @param {JsSIP.UA} ua * @param {Object} params parameters that will have priority over ua.configuration parameters: *
* - cseq, call_id, from_tag, from_uri, from_display_name, to_uri, to_tag, route_set * @param {Object} [headers] extra headers * @param {String} [body] */ OutgoingRequest = function(method, ruri, ua, params, extraHeaders, body) { var to, from, call_id, cseq; params = params || {}; // Mandatory parameters check if(!method || !ruri || !ua) { return null; } this.logger = ua.getLogger('jssip.sipmessage'); this.ua = ua; this.headers = {}; this.method = method; this.ruri = ruri; this.body = body; this.extraHeaders = extraHeaders && extraHeaders.slice() || []; // Fill the Common SIP Request Headers // Route if (params.route_set) { this.setHeader('route', params.route_set); } else if (ua.configuration.use_preloaded_route){ this.setHeader('route', ua.transport.server.sip_uri); } // Via // Empty Via header. Will be filled by the client transaction. this.setHeader('via', ''); // Max-Forwards this.setHeader('max-forwards', JsSIP.UA.C.MAX_FORWARDS); // To to = (params.to_display_name || params.to_display_name === 0) ? '"' + params.to_display_name + '" ' : ''; to += '<' + (params.to_uri || ruri) + '>'; to += params.to_tag ? ';tag=' + params.to_tag : ''; this.to = new JsSIP.NameAddrHeader.parse(to); this.setHeader('to', to); // From if (params.from_display_name || params.from_display_name === 0) { from = '"' + params.from_display_name + '" '; } else if (ua.configuration.display_name) { from = '"' + ua.configuration.display_name + '" '; } else { from = ''; } from += '<' + (params.from_uri || ua.configuration.uri) + '>;tag='; from += params.from_tag || JsSIP.Utils.newTag(); this.from = new JsSIP.NameAddrHeader.parse(from); this.setHeader('from', from); // Call-ID call_id = params.call_id || (ua.configuration.jssip_id + JsSIP.Utils.createRandomToken(15)); this.call_id = call_id; this.setHeader('call-id', call_id); // CSeq cseq = params.cseq || Math.floor(Math.random() * 10000); this.cseq = cseq; this.setHeader('cseq', cseq + ' ' + method); }; OutgoingRequest.prototype = { /** * Replace the the given header by the given value. * @param {String} name header name * @param {String | Array} value header value */ setHeader: function(name, value) { this.headers[JsSIP.Utils.headerize(name)] = (value instanceof Array) ? value : [value]; }, /** * Get the value of the given header name at the given position. * @param {String} name header name * @returns {String|undefined} Returns the specified header, null if header doesn't exist. */ getHeader: function(name) { var regexp, idx, length = this.extraHeaders.length, header = this.headers[JsSIP.Utils.headerize(name)]; if(header) { if(header[0]) { return header[0]; } } else { regexp = new RegExp('^\\s*'+ name +'\\s*:','i'); for (idx=0; idx= this.headers[name].length) { this.logger.log('not so many "' + name + '" headers present'); return; } header = this.headers[name][idx]; value = header.raw; if(header.parsed) { return header.parsed; } //substitute '-' by '_' for grammar rule matching. parsed = JsSIP.Grammar.parse(value, name.replace(/-/g, '_')); if(parsed === -1) { this.headers[name].splice(idx, 1); //delete from headers this.logger.warn('error parsing "' + name + '" header field with value "' + value + '"'); return; } else { header.parsed = parsed; return parsed; } }, /** * Message Header attribute selector. Alias of parseHeader. * @param {String} name header name * @param {Number} [idx=0] header index * @returns {Object|undefined} Parsed header object, undefined if the header is not present or in case of a parsing error. * * @example * message.s('via',3).port */ s: function(name, idx) { return this.parseHeader(name, idx); }, /** * Replace the value of the given header by the value. * @param {String} name header name * @param {String} value header value */ setHeader: function(name, value) { var header = { raw: value }; this.headers[JsSIP.Utils.headerize(name)] = [header]; }, toString: function() { return this.data; } }; /** * @augments IncomingMessage * @class Class for incoming SIP request. */ IncomingRequest = function(ua) { this.logger = ua.getLogger('jssip.sipmessage'); this.ua = ua; this.headers = {}; this.ruri = null; this.transport = null; this.server_transaction = null; }; IncomingRequest.prototype = new IncomingMessage(); /** * Stateful reply. * @param {Number} code status code * @param {String} reason reason phrase * @param {Object} headers extra headers * @param {String} body body * @param {Function} [onSuccess] onSuccess callback * @param {Function} [onFailure] onFailure callback */ IncomingRequest.prototype.reply = function(code, reason, extraHeaders, body, onSuccess, onFailure) { var rr, vias, length, idx, response, supported = [], to = this.getHeader('To'), r = 0, v = 0; code = code || null; reason = reason || null; // Validate code and reason values if (!code || (code < 100 || code > 699)) { throw new TypeError('Invalid status_code: '+ code); } else if (reason && typeof reason !== 'string' && !(reason instanceof String)) { throw new TypeError('Invalid reason_phrase: '+ reason); } reason = reason || JsSIP.C.REASON_PHRASE[code] || ''; extraHeaders = extraHeaders && extraHeaders.slice() || []; response = 'SIP/2.0 ' + code + ' ' + reason + '\r\n'; if(this.method === JsSIP.C.INVITE && code > 100 && code <= 200) { rr = this.getHeaders('record-route'); length = rr.length; for(r; r < length; r++) { response += 'Record-Route: ' + rr[r] + '\r\n'; } } vias = this.getHeaders('via'); length = vias.length; for(v; v < length; v++) { response += 'Via: ' + vias[v] + '\r\n'; } if(!this.to_tag && code > 100) { to += ';tag=' + JsSIP.Utils.newTag(); } else if(this.to_tag && !this.s('to').hasParam('tag')) { to += ';tag=' + this.to_tag; } response += 'To: ' + to + '\r\n'; response += 'From: ' + this.getHeader('From') + '\r\n'; response += 'Call-ID: ' + this.call_id + '\r\n'; response += 'CSeq: ' + this.cseq + ' ' + this.method + '\r\n'; length = extraHeaders.length; for (idx = 0; idx < length; idx++) { response += extraHeaders[idx].trim() +'\r\n'; } // Supported switch (this.method) { case JsSIP.C.INVITE: if (this.ua.contact.pub_gruu || this.ua.contact.temp_gruu) { supported.push('gruu'); } break; } supported.push('outbound'); // Allow and Accept if (this.method === JsSIP.C.OPTIONS) { response += 'Allow: '+ JsSIP.Utils.getAllowedMethods(this.ua) +'\r\n'; response += 'Accept: '+ JsSIP.UA.C.ACCEPTED_BODY_TYPES +'\r\n'; } else if (code === 405) { response += 'Allow: '+ JsSIP.Utils.getAllowedMethods(this.ua) +'\r\n'; } response += 'Supported: ' + supported +'\r\n'; if(body) { length = JsSIP.Utils.str_utf8_length(body); response += 'Content-Type: application/sdp\r\n'; response += 'Content-Length: ' + length + '\r\n\r\n'; response += body; } else { response += 'Content-Length: ' + 0 + '\r\n\r\n'; } this.server_transaction.receiveResponse(code, response, onSuccess, onFailure); }; /** * Stateless reply. * @param {Number} code status code * @param {String} reason reason phrase */ IncomingRequest.prototype.reply_sl = function(code, reason) { var to, response, v = 0, vias = this.getHeaders('via'), length = vias.length; code = code || null; reason = reason || null; // Validate code and reason values if (!code || (code < 100 || code > 699)) { throw new TypeError('Invalid status_code: '+ code); } else if (reason && typeof reason !== 'string' && !(reason instanceof String)) { throw new TypeError('Invalid reason_phrase: '+ reason); } reason = reason || JsSIP.C.REASON_PHRASE[code] || ''; response = 'SIP/2.0 ' + code + ' ' + reason + '\r\n'; for(v; v < length; v++) { response += 'Via: ' + vias[v] + '\r\n'; } to = this.getHeader('To'); if(!this.to_tag && code > 100) { to += ';tag=' + JsSIP.Utils.newTag(); } else if(this.to_tag && !this.s('to').hasParam('tag')) { to += ';tag=' + this.to_tag; } response += 'To: ' + to + '\r\n'; response += 'From: ' + this.getHeader('From') + '\r\n'; response += 'Call-ID: ' + this.call_id + '\r\n'; response += 'CSeq: ' + this.cseq + ' ' + this.method + '\r\n'; response += 'Content-Length: ' + 0 + '\r\n\r\n'; this.transport.send(response); }; /** * @augments IncomingMessage * @class Class for incoming SIP response. */ IncomingResponse = function(ua) { this.logger = ua.getLogger('jssip.sipmessage'); this.headers = {}; this.status_code = null; this.reason_phrase = null; }; IncomingResponse.prototype = new IncomingMessage(); JsSIP.OutgoingRequest = OutgoingRequest; JsSIP.IncomingRequest = IncomingRequest; JsSIP.IncomingResponse = IncomingResponse; }(JsSIP)); /** * @fileoverview SIP URI */ /** * @augments JsSIP * @class Class creating a SIP URI. * * @param {String} [scheme] * @param {String} [user] * @param {String} host * @param {String} [port] * @param {Object} [parameters] * @param {Object} [headers] * */ (function(JsSIP) { var URI; URI = function(scheme, user, host, port, parameters, headers) { var param, header; // Checks if(!host) { throw new TypeError('missing or invalid "host" parameter'); } // Initialize parameters scheme = scheme || JsSIP.C.SIP; this.parameters = {}; this.headers = {}; for (param in parameters) { this.setParam(param, parameters[param]); } for (header in headers) { this.setHeader(header, headers[header]); } Object.defineProperties(this, { scheme: { get: function(){ return scheme; }, set: function(value){ scheme = value.toLowerCase(); } }, user: { get: function(){ return user; }, set: function(value){ user = value; } }, host: { get: function(){ return host; }, set: function(value){ host = value.toLowerCase(); } }, port: { get: function(){ return port; }, set: function(value){ port = value === 0 ? value : (parseInt(value,10) || null); } } }); }; URI.prototype = { setParam: function(key, value) { if(key) { this.parameters[key.toLowerCase()] = (typeof value === 'undefined' || value === null) ? null : value.toString().toLowerCase(); } }, getParam: function(key) { if(key) { return this.parameters[key.toLowerCase()]; } }, hasParam: function(key) { if(key) { return (this.parameters.hasOwnProperty(key.toLowerCase()) && true) || false; } }, deleteParam: function(parameter) { var value; parameter = parameter.toLowerCase(); if (this.parameters.hasOwnProperty(parameter)) { value = this.parameters[parameter]; delete this.parameters[parameter]; return value; } }, clearParams: function() { this.parameters = {}; }, setHeader: function(name, value) { this.headers[JsSIP.Utils.headerize(name)] = (value instanceof Array) ? value : [value]; }, getHeader: function(name) { if(name) { return this.headers[JsSIP.Utils.headerize(name)]; } }, hasHeader: function(name) { if(name) { return (this.headers.hasOwnProperty(JsSIP.Utils.headerize(name)) && true) || false; } }, deleteHeader: function(header) { var value; header = JsSIP.Utils.headerize(header); if(this.headers.hasOwnProperty(header)) { value = this.headers[header]; delete this.headers[header]; return value; } }, clearHeaders: function() { this.headers = {}; }, clone: function() { return new URI( this.scheme, this.user, this.host, this.port, window.JSON.parse(window.JSON.stringify(this.parameters)), window.JSON.parse(window.JSON.stringify(this.headers))); }, toString: function(){ var header, parameter, idx, uri, headers = []; uri = this.scheme + ':'; if (this.user) { uri += JsSIP.Utils.escapeUser(this.user) + '@'; } uri += this.host; if (this.port || this.port === 0) { uri += ':' + this.port; } for (parameter in this.parameters) { uri += ';' + parameter; if (this.parameters[parameter] !== null) { uri += '='+ this.parameters[parameter]; } } for(header in this.headers) { for(idx in this.headers[header]) { headers.push(header + '=' + this.headers[header][idx]); } } if (headers.length > 0) { uri += '?' + headers.join('&'); } return uri; }, toAor: function(show_port){ var aor; aor = this.scheme + ':'; if (this.user) { aor += JsSIP.Utils.escapeUser(this.user) + '@'; } aor += this.host; if (show_port && (this.port || this.port === 0)) { aor += ':' + this.port; } return aor; } }; /** * Parse the given string and returns a JsSIP.URI instance or undefined if * it is an invalid URI. * @public * @param {String} uri */ URI.parse = function(uri) { uri = JsSIP.Grammar.parse(uri,'SIP_URI'); if (uri !== -1) { return uri; } else { return undefined; } }; JsSIP.URI = URI; }(JsSIP)); /** * @fileoverview JsSIP NameAddrHeader */ /** * @augments JsSIP * @class Class creating a Name Address SIP header. * * @param {JsSIP.URI} uri * @param {String} [display_name] * @param {Object} [parameters] * */ (function(JsSIP) { var NameAddrHeader; NameAddrHeader = function(uri, display_name, parameters) { var param; // Checks if(!uri || !(uri instanceof JsSIP.URI)) { throw new TypeError('missing or invalid "uri" parameter'); } // Initialize parameters this.uri = uri; this.parameters = {}; for (param in parameters) { this.setParam(param, parameters[param]); } Object.defineProperties(this, { display_name: { get: function() { return display_name; }, set: function(value) { display_name = (value === 0) ? '0' : value; } } }); }; NameAddrHeader.prototype = { setParam: function(key, value) { if (key) { this.parameters[key.toLowerCase()] = (typeof value === 'undefined' || value === null) ? null : value.toString(); } }, getParam: function(key) { if(key) { return this.parameters[key.toLowerCase()]; } }, hasParam: function(key) { if(key) { return (this.parameters.hasOwnProperty(key.toLowerCase()) && true) || false; } }, deleteParam: function(parameter) { var value; parameter = parameter.toLowerCase(); if (this.parameters.hasOwnProperty(parameter)) { value = this.parameters[parameter]; delete this.parameters[parameter]; return value; } }, clearParams: function() { this.parameters = {}; }, clone: function() { return new NameAddrHeader( this.uri.clone(), this.display_name, window.JSON.parse(window.JSON.stringify(this.parameters))); }, toString: function() { var body, parameter; body = (this.display_name || this.display_name === 0) ? '"' + this.display_name + '" ' : ''; body += '<' + this.uri.toString() + '>'; for (parameter in this.parameters) { body += ';' + parameter; if (this.parameters[parameter] !== null) { body += '='+ this.parameters[parameter]; } } return body; } }; /** * Parse the given string and returns a JsSIP.NameAddrHeader instance or undefined if * it is an invalid NameAddrHeader. * @public * @param {String} name_addr_header */ NameAddrHeader.parse = function(name_addr_header) { name_addr_header = JsSIP.Grammar.parse(name_addr_header,'Name_Addr_Header'); if (name_addr_header !== -1) { return name_addr_header; } else { return undefined; } }; JsSIP.NameAddrHeader = NameAddrHeader; }(JsSIP)); /** * @fileoverview SIP Transactions */ /** * SIP Transactions module. * @augments JsSIP */ (function(JsSIP) { var C = { // Transaction states STATUS_TRYING: 1, STATUS_PROCEEDING: 2, STATUS_CALLING: 3, STATUS_ACCEPTED: 4, STATUS_COMPLETED: 5, STATUS_TERMINATED: 6, STATUS_CONFIRMED: 7, // Transaction types NON_INVITE_CLIENT: 'nict', NON_INVITE_SERVER: 'nist', INVITE_CLIENT: 'ict', INVITE_SERVER: 'ist' }; /** * @augments JsSIP.Transactions * @class Non Invite Client Transaction * @param {JsSIP.RequestSender} request_sender * @param {JsSIP.OutgoingRequest} request * @param {JsSIP.Transport} transport */ var NonInviteClientTransaction = function(request_sender, request, transport) { var via, events = ['stateChanged']; this.type = C.NON_INVITE_CLIENT; this.transport = transport; this.id = 'z9hG4bK' + Math.floor(Math.random() * 10000000); this.request_sender = request_sender; this.request = request; this.logger = request_sender.ua.getLogger('jssip.transaction.nict', this.id); via = 'SIP/2.0/' + (request_sender.ua.configuration.hack_via_tcp ? 'TCP' : transport.server.scheme); via += ' ' + request_sender.ua.configuration.via_host + ';branch=' + this.id; this.request.setHeader('via', via); this.request_sender.ua.newTransaction(this); this.initEvents(events); }; NonInviteClientTransaction.prototype = new JsSIP.EventEmitter(); NonInviteClientTransaction.prototype.stateChanged = function(state) { this.state = state; this.emit('stateChanged', this); }; NonInviteClientTransaction.prototype.send = function() { var tr = this; this.stateChanged(C.STATUS_TRYING); this.F = window.setTimeout(function() {tr.timer_F();}, JsSIP.Timers.TIMER_F); if(!this.transport.send(this.request)) { this.onTransportError(); } }; NonInviteClientTransaction.prototype.onTransportError = function() { this.logger.log('transport error occurred, deleting non-INVITE client transaction ' + this.id); window.clearTimeout(this.F); window.clearTimeout(this.K); this.stateChanged(C.STATUS_TERMINATED); this.request_sender.ua.destroyTransaction(this); this.request_sender.onTransportError(); }; NonInviteClientTransaction.prototype.timer_F = function() { this.logger.log('Timer F expired for non-INVITE client transaction ' + this.id); this.stateChanged(C.STATUS_TERMINATED); this.request_sender.ua.destroyTransaction(this); this.request_sender.onRequestTimeout(); }; NonInviteClientTransaction.prototype.timer_K = function() { this.stateChanged(C.STATUS_TERMINATED); this.request_sender.ua.destroyTransaction(this); }; NonInviteClientTransaction.prototype.receiveResponse = function(response) { var tr = this, status_code = response.status_code; if(status_code < 200) { switch(this.state) { case C.STATUS_TRYING: case C.STATUS_PROCEEDING: this.stateChanged(C.STATUS_PROCEEDING); this.request_sender.receiveResponse(response); break; } } else { switch(this.state) { case C.STATUS_TRYING: case C.STATUS_PROCEEDING: this.stateChanged(C.STATUS_COMPLETED); window.clearTimeout(this.F); if(status_code === 408) { this.request_sender.onRequestTimeout(); } else { this.request_sender.receiveResponse(response); } this.K = window.setTimeout(function() {tr.timer_K();}, JsSIP.Timers.TIMER_K); break; case C.STATUS_COMPLETED: break; } } }; /** * @augments JsSIP.Transactions * @class Invite Client Transaction * @param {JsSIP.RequestSender} request_sender * @param {JsSIP.OutgoingRequest} request * @param {JsSIP.Transport} transport */ var InviteClientTransaction = function(request_sender, request, transport) { var via, tr = this, events = ['stateChanged']; this.type = C.INVITE_CLIENT; this.transport = transport; this.id = 'z9hG4bK' + Math.floor(Math.random() * 10000000); this.request_sender = request_sender; this.request = request; this.logger = request_sender.ua.getLogger('jssip.transaction.ict', this.id); via = 'SIP/2.0/' + (request_sender.ua.configuration.hack_via_tcp ? 'TCP' : transport.server.scheme); via += ' ' + request_sender.ua.configuration.via_host + ';branch=' + this.id; this.request.setHeader('via', via); this.request_sender.ua.newTransaction(this); // Add the cancel property to the request. //Will be called from the request instance, not the transaction itself. this.request.cancel = function(reason) { tr.cancel_request(tr, reason); }; this.initEvents(events); }; InviteClientTransaction.prototype = new JsSIP.EventEmitter(); InviteClientTransaction.prototype.stateChanged = function(state) { this.state = state; this.emit('stateChanged', this); }; InviteClientTransaction.prototype.send = function() { var tr = this; this.stateChanged(C.STATUS_CALLING); this.B = window.setTimeout(function() { tr.timer_B(); }, JsSIP.Timers.TIMER_B); if(!this.transport.send(this.request)) { this.onTransportError(); } }; InviteClientTransaction.prototype.onTransportError = function() { this.logger.log('transport error occurred, deleting INVITE client transaction ' + this.id); window.clearTimeout(this.B); window.clearTimeout(this.D); window.clearTimeout(this.M); this.stateChanged(C.STATUS_TERMINATED); this.request_sender.ua.destroyTransaction(this); if (this.state !== C.STATUS_ACCEPTED) { this.request_sender.onTransportError(); } }; // RFC 6026 7.2 InviteClientTransaction.prototype.timer_M = function() { this.logger.log('Timer M expired for INVITE client transaction ' + this.id); if(this.state === C.STATUS_ACCEPTED) { window.clearTimeout(this.B); this.stateChanged(C.STATUS_TERMINATED); this.request_sender.ua.destroyTransaction(this); } }; // RFC 3261 17.1.1 InviteClientTransaction.prototype.timer_B = function() { this.logger.log('Timer B expired for INVITE client transaction ' + this.id); if(this.state === C.STATUS_CALLING) { this.stateChanged(C.STATUS_TERMINATED); this.request_sender.ua.destroyTransaction(this); this.request_sender.onRequestTimeout(); } }; InviteClientTransaction.prototype.timer_D = function() { this.logger.log('Timer D expired for INVITE client transaction ' + this.id); window.clearTimeout(this.B); this.stateChanged(C.STATUS_TERMINATED); this.request_sender.ua.destroyTransaction(this); }; InviteClientTransaction.prototype.sendACK = function(response) { var tr = this; this.ack = 'ACK ' + this.request.ruri + ' SIP/2.0\r\n'; this.ack += 'Via: ' + this.request.headers['Via'].toString() + '\r\n'; if(this.request.headers['Route']) { this.ack += 'Route: ' + this.request.headers['Route'].toString() + '\r\n'; } this.ack += 'To: ' + response.getHeader('to') + '\r\n'; this.ack += 'From: ' + this.request.headers['From'].toString() + '\r\n'; this.ack += 'Call-ID: ' + this.request.headers['Call-ID'].toString() + '\r\n'; this.ack += 'CSeq: ' + this.request.headers['CSeq'].toString().split(' ')[0]; this.ack += ' ACK\r\n'; this.ack += 'Content-Length: 0\r\n\r\n'; this.D = window.setTimeout(function() {tr.timer_D();}, JsSIP.Timers.TIMER_D); this.transport.send(this.ack); }; InviteClientTransaction.prototype.cancel_request = function(tr, reason) { var request = tr.request; this.cancel = JsSIP.C.CANCEL + ' ' + request.ruri + ' SIP/2.0\r\n'; this.cancel += 'Via: ' + request.headers['Via'].toString() + '\r\n'; if(this.request.headers['Route']) { this.cancel += 'Route: ' + request.headers['Route'].toString() + '\r\n'; } this.cancel += 'To: ' + request.headers['To'].toString() + '\r\n'; this.cancel += 'From: ' + request.headers['From'].toString() + '\r\n'; this.cancel += 'Call-ID: ' + request.headers['Call-ID'].toString() + '\r\n'; this.cancel += 'CSeq: ' + request.headers['CSeq'].toString().split(' ')[0] + ' CANCEL\r\n'; if(reason) { this.cancel += 'Reason: ' + reason + '\r\n'; } this.cancel += 'Content-Length: 0\r\n\r\n'; // Send only if a provisional response (>100) has been received. if(this.state === C.STATUS_PROCEEDING) { this.transport.send(this.cancel); } }; InviteClientTransaction.prototype.receiveResponse = function(response) { var tr = this, status_code = response.status_code; if(status_code >= 100 && status_code <= 199) { switch(this.state) { case C.STATUS_CALLING: this.stateChanged(C.STATUS_PROCEEDING); this.request_sender.receiveResponse(response); if(this.cancel) { this.transport.send(this.cancel); } break; case C.STATUS_PROCEEDING: this.request_sender.receiveResponse(response); break; } } else if(status_code >= 200 && status_code <= 299) { switch(this.state) { case C.STATUS_CALLING: case C.STATUS_PROCEEDING: this.stateChanged(C.STATUS_ACCEPTED); this.M = window.setTimeout(function() { tr.timer_M(); }, JsSIP.Timers.TIMER_M); this.request_sender.receiveResponse(response); break; case C.STATUS_ACCEPTED: this.request_sender.receiveResponse(response); break; } } else if(status_code >= 300 && status_code <= 699) { switch(this.state) { case C.STATUS_CALLING: case C.STATUS_PROCEEDING: this.stateChanged(C.STATUS_COMPLETED); this.sendACK(response); this.request_sender.receiveResponse(response); break; case C.STATUS_COMPLETED: this.sendACK(response); break; } } }; /** * @augments JsSIP.Transactions * @class ACK Client Transaction * @param {JsSIP.RequestSender} request_sender * @param {JsSIP.OutgoingRequest} request * @param {JsSIP.Transport} transport */ var AckClientTransaction = function(request_sender, request, transport) { var via; this.transport = transport; this.id = 'z9hG4bK' + Math.floor(Math.random() * 10000000); this.request_sender = request_sender; this.request = request; this.logger = request_sender.ua.getLogger('jssip.transaction.nict', this.id); via = 'SIP/2.0/' + (request_sender.ua.configuration.hack_via_tcp ? 'TCP' : transport.server.scheme); via += ' ' + request_sender.ua.configuration.via_host + ';branch=' + this.id; this.request.setHeader('via', via); }; AckClientTransaction.prototype = new JsSIP.EventEmitter(); AckClientTransaction.prototype.send = function() { if(!this.transport.send(this.request)) { this.onTransportError(); } }; AckClientTransaction.prototype.onTransportError = function() { this.logger.log('transport error occurred, for an ACK client transaction ' + this.id); this.request_sender.onTransportError(); }; /** * @augments JsSIP.Transactions * @class Non Invite Server Transaction * @param {JsSIP.IncomingRequest} request * @param {JsSIP.UA} ua */ var NonInviteServerTransaction = function(request, ua) { var events = ['stateChanged']; this.type = C.NON_INVITE_SERVER; this.id = request.via_branch; this.request = request; this.transport = request.transport; this.ua = ua; this.last_response = ''; request.server_transaction = this; this.logger = ua.getLogger('jssip.transaction.nist', this.id); this.state = C.STATUS_TRYING; ua.newTransaction(this); this.initEvents(events); }; NonInviteServerTransaction.prototype = new JsSIP.EventEmitter(); NonInviteServerTransaction.prototype.stateChanged = function(state) { this.state = state; this.emit('stateChanged', this); }; NonInviteServerTransaction.prototype.timer_J = function() { this.logger.log('Timer J expired for non-INVITE server transaction ' + this.id); this.stateChanged(C.STATUS_TERMINATED); this.ua.destroyTransaction(this); }; NonInviteServerTransaction.prototype.onTransportError = function() { if (!this.transportError) { this.transportError = true; this.logger.log('transport error occurred, deleting non-INVITE server transaction ' + this.id); window.clearTimeout(this.J); this.stateChanged(C.STATUS_TERMINATED); this.ua.destroyTransaction(this); } }; NonInviteServerTransaction.prototype.receiveResponse = function(status_code, response, onSuccess, onFailure) { var tr = this; if(status_code === 100) { /* RFC 4320 4.1 * 'A SIP element MUST NOT * send any provisional response with a * Status-Code other than 100 to a non-INVITE request.' */ switch(this.state) { case C.STATUS_TRYING: this.stateChanged(C.STATUS_PROCEEDING); if(!this.transport.send(response)) { this.onTransportError(); } break; case C.STATUS_PROCEEDING: this.last_response = response; if(!this.transport.send(response)) { this.onTransportError(); if (onFailure) { onFailure(); } } else if (onSuccess) { onSuccess(); } break; } } else if(status_code >= 200 && status_code <= 699) { switch(this.state) { case C.STATUS_TRYING: case C.STATUS_PROCEEDING: this.stateChanged(C.STATUS_COMPLETED); this.last_response = response; this.J = window.setTimeout(function() { tr.timer_J(); }, JsSIP.Timers.TIMER_J); if(!this.transport.send(response)) { this.onTransportError(); if (onFailure) { onFailure(); } } else if (onSuccess) { onSuccess(); } break; case C.STATUS_COMPLETED: break; } } }; /** * @augments JsSIP.Transactions * @class Invite Server Transaction * @param {JsSIP.IncomingRequest} request * @param {JsSIP.UA} ua */ var InviteServerTransaction = function(request, ua) { var events = ['stateChanged']; this.type = C.INVITE_SERVER; this.id = request.via_branch; this.request = request; this.transport = request.transport; this.ua = ua; this.last_response = ''; request.server_transaction = this; this.logger = ua.getLogger('jssip.transaction.ist', this.id); this.state = C.STATUS_PROCEEDING; ua.newTransaction(this); this.resendProvisionalTimer = null; request.reply(100); this.initEvents(events); }; InviteServerTransaction.prototype = new JsSIP.EventEmitter(); InviteServerTransaction.prototype.stateChanged = function(state) { this.state = state; this.emit('stateChanged', this); }; InviteServerTransaction.prototype.timer_H = function() { this.logger.log('Timer H expired for INVITE server transaction ' + this.id); if(this.state === C.STATUS_COMPLETED) { this.logger.warn('transactions', 'ACK for INVITE server transaction was never received, call will be terminated'); } this.stateChanged(C.STATUS_TERMINATED); this.ua.destroyTransaction(this); }; InviteServerTransaction.prototype.timer_I = function() { this.stateChanged(C.STATUS_TERMINATED); this.ua.destroyTransaction(this); }; // RFC 6026 7.1 InviteServerTransaction.prototype.timer_L = function() { this.logger.log('Timer L expired for INVITE server transaction ' + this.id); if(this.state === C.STATUS_ACCEPTED) { this.stateChanged(C.STATUS_TERMINATED); this.ua.destroyTransaction(this); } }; InviteServerTransaction.prototype.onTransportError = function() { if (!this.transportError) { this.transportError = true; this.logger.log('transport error occurred, deleting INVITE server transaction ' + this.id); if (this.resendProvisionalTimer !== null) { window.clearInterval(this.resendProvisionalTimer); this.resendProvisionalTimer = null; } window.clearTimeout(this.L); window.clearTimeout(this.H); window.clearTimeout(this.I); this.stateChanged(C.STATUS_TERMINATED); this.ua.destroyTransaction(this); } }; InviteServerTransaction.prototype.resend_provisional = function() { if(!this.transport.send(this.last_response)) { this.onTransportError(); } }; // INVITE Server Transaction RFC 3261 17.2.1 InviteServerTransaction.prototype.receiveResponse = function(status_code, response, onSuccess, onFailure) { var tr = this; if(status_code >= 100 && status_code <= 199) { switch(this.state) { case C.STATUS_PROCEEDING: if(!this.transport.send(response)) { this.onTransportError(); } this.last_response = response; break; } } if(status_code > 100 && status_code <= 199 && this.state === C.STATUS_PROCEEDING) { // Trigger the resendProvisionalTimer only for the first non 100 provisional response. if(this.resendProvisionalTimer === null) { this.resendProvisionalTimer = window.setInterval(function() { tr.resend_provisional();}, JsSIP.Timers.PROVISIONAL_RESPONSE_INTERVAL); } } else if(status_code >= 200 && status_code <= 299) { switch(this.state) { case C.STATUS_PROCEEDING: this.stateChanged(C.STATUS_ACCEPTED); this.last_response = response; this.L = window.setTimeout(function() { tr.timer_L(); }, JsSIP.Timers.TIMER_L); if (this.resendProvisionalTimer !== null) { window.clearInterval(this.resendProvisionalTimer); this.resendProvisionalTimer = null; } /* falls through */ case C.STATUS_ACCEPTED: // Note that this point will be reached for proceeding tr.state also. if(!this.transport.send(response)) { this.onTransportError(); if (onFailure) { onFailure(); } } else if (onSuccess) { onSuccess(); } break; } } else if(status_code >= 300 && status_code <= 699) { switch(this.state) { case C.STATUS_PROCEEDING: if (this.resendProvisionalTimer !== null) { window.clearInterval(this.resendProvisionalTimer); this.resendProvisionalTimer = null; } if(!this.transport.send(response)) { this.onTransportError(); if (onFailure) { onFailure(); } } else { this.stateChanged(C.STATUS_COMPLETED); this.H = window.setTimeout(function() { tr.timer_H(); }, JsSIP.Timers.TIMER_H); if (onSuccess) { onSuccess(); } } break; } } }; /** * @function * @param {JsSIP.UA} ua * @param {JsSIP.IncomingRequest} request * * @return {boolean} * INVITE: * _true_ if retransmission * _false_ new request * * ACK: * _true_ ACK to non2xx response * _false_ ACK must be passed to TU (accepted state) * ACK to 2xx response * * CANCEL: * _true_ no matching invite transaction * _false_ matching invite transaction and no final response sent * * OTHER: * _true_ retransmission * _false_ new request */ var checkTransaction = function(ua, request) { var tr; switch(request.method) { case JsSIP.C.INVITE: tr = ua.transactions.ist[request.via_branch]; if(tr) { switch(tr.state) { case C.STATUS_PROCEEDING: tr.transport.send(tr.last_response); break; // RFC 6026 7.1 Invite retransmission //received while in C.STATUS_ACCEPTED state. Absorb it. case C.STATUS_ACCEPTED: break; } return true; } break; case JsSIP.C.ACK: tr = ua.transactions.ist[request.via_branch]; // RFC 6026 7.1 if(tr) { if(tr.state === C.STATUS_ACCEPTED) { return false; } else if(tr.state === C.STATUS_COMPLETED) { tr.state = C.STATUS_CONFIRMED; tr.I = window.setTimeout(function() {tr.timer_I();}, JsSIP.Timers.TIMER_I); return true; } } // ACK to 2XX Response. else { return false; } break; case JsSIP.C.CANCEL: tr = ua.transactions.ist[request.via_branch]; if(tr) { request.reply_sl(200); if(tr.state === C.STATUS_PROCEEDING) { return false; } else { return true; } } else { request.reply_sl(481); return true; } break; default: // Non-INVITE Server Transaction RFC 3261 17.2.2 tr = ua.transactions.nist[request.via_branch]; if(tr) { switch(tr.state) { case C.STATUS_TRYING: break; case C.STATUS_PROCEEDING: case C.STATUS_COMPLETED: tr.transport.send(tr.last_response); break; } return true; } break; } }; JsSIP.Transactions = { C: C, checkTransaction: checkTransaction, NonInviteClientTransaction: NonInviteClientTransaction, InviteClientTransaction: InviteClientTransaction, AckClientTransaction: AckClientTransaction, NonInviteServerTransaction: NonInviteServerTransaction, InviteServerTransaction: InviteServerTransaction }; }(JsSIP)); /** * @fileoverview SIP Dialog */ /** * @augments JsSIP * @class Class creating a SIP dialog. * @param {JsSIP.RTCSession} owner * @param {JsSIP.IncomingRequest|JsSIP.IncomingResponse} message * @param {Enum} type UAC / UAS * @param {Enum} state JsSIP.Dialog.C.STATUS_EARLY / JsSIP.Dialog.C.STATUS_CONFIRMED */ (function(JsSIP) { // Load dependencies var RequestSender = /** * @fileoverview In-Dialog Request Sender */ /** * @augments JsSIP.Dialog * @class Class creating an In-dialog request sender. * @param {JsSIP.Dialog} dialog * @param {Object} applicant * @param {JsSIP.OutgoingRequest} request */ /** * @fileoverview in-Dialog Request Sender */ (function(JsSIP) { var RequestSender; RequestSender = function(dialog, applicant, request) { this.dialog = dialog; this.applicant = applicant; this.request = request; // RFC3261 14.1 Modifying an Existing Session. UAC Behavior. this.reattempt = false; this.reattemptTimer = null; }; RequestSender.prototype = { send: function() { var self = this, request_sender = new JsSIP.RequestSender(this, this.dialog.owner.ua); request_sender.send(); // RFC3261 14.2 Modifying an Existing Session -UAC BEHAVIOR- if (this.request.method === JsSIP.C.INVITE && request_sender.clientTransaction.state !== JsSIP.Transactions.C.STATUS_TERMINATED) { this.dialog.uac_pending_reply = true; request_sender.clientTransaction.on('stateChanged', function stateChanged(e){ if (e.sender.state === JsSIP.Transactions.C.STATUS_ACCEPTED || e.sender.state === JsSIP.Transactions.C.STATUS_COMPLETED || e.sender.state === JsSIP.Transactions.C.STATUS_TERMINATED) { request_sender.clientTransaction.removeListener('stateChanged', stateChanged); self.dialog.uac_pending_reply = false; if (self.dialog.uas_pending_reply === false) { self.dialog.owner.onReadyToReinvite(); } } }); } }, onRequestTimeout: function() { this.applicant.onRequestTimeout(); }, onTransportError: function() { this.applicant.onTransportError(); }, receiveResponse: function(response) { var self = this; // RFC3261 12.2.1.2 408 or 481 is received for a request within a dialog. if (response.status_code === 408 || response.status_code === 481) { this.applicant.onDialogError(response); } else if (response.method === JsSIP.C.INVITE && response.status_code === 491) { if (this.reattempt) { this.applicant.receiveResponse(response); } else { this.request.cseq.value = this.dialog.local_seqnum += 1; this.reattemptTimer = window.setTimeout( function() { if (self.applicant.owner.status !== JsSIP.RTCSession.C.STATUS_TERMINATED) { self.reattempt = true; self.request_sender.send(); } }, this.getReattemptTimeout() ); } } else { this.applicant.receiveResponse(response); } } }; return RequestSender; }(JsSIP)); var Dialog, C = { // Dialog states STATUS_EARLY: 1, STATUS_CONFIRMED: 2 }; // RFC 3261 12.1 Dialog = function(owner, message, type, state) { var contact; this.uac_pending_reply = false; this.uas_pending_reply = false; if(!message.hasHeader('contact')) { return { error: 'unable to create a Dialog without Contact header field' }; } if(message instanceof JsSIP.IncomingResponse) { state = (message.status_code < 200) ? C.STATUS_EARLY : C.STATUS_CONFIRMED; } else { // Create confirmed dialog if state is not defined state = state || C.STATUS_CONFIRMED; } contact = message.parseHeader('contact'); // RFC 3261 12.1.1 if(type === 'UAS') { this.id = { call_id: message.call_id, local_tag: message.to_tag, remote_tag: message.from_tag, toString: function() { return this.call_id + this.local_tag + this.remote_tag; } }; this.state = state; this.remote_seqnum = message.cseq; this.local_uri = message.parseHeader('to').uri; this.remote_uri = message.parseHeader('from').uri; this.remote_target = contact.uri; this.route_set = message.getHeaders('record-route'); } // RFC 3261 12.1.2 else if(type === 'UAC') { this.id = { call_id: message.call_id, local_tag: message.from_tag, remote_tag: message.to_tag, toString: function() { return this.call_id + this.local_tag + this.remote_tag; } }; this.state = state; this.local_seqnum = message.cseq; this.local_uri = message.parseHeader('from').uri; this.remote_uri = message.parseHeader('to').uri; this.remote_target = contact.uri; this.route_set = message.getHeaders('record-route').reverse(); } this.logger = owner.ua.getLogger('jssip.dialog', this.id.toString()); this.owner = owner; owner.ua.dialogs[this.id.toString()] = this; this.logger.log('new ' + type + ' dialog created with status ' + (this.state === C.STATUS_EARLY ? 'EARLY': 'CONFIRMED')); }; Dialog.prototype = { /** * @param {JsSIP.IncomingMessage} message * @param {Enum} UAC/UAS */ update: function(message, type) { this.state = C.STATUS_CONFIRMED; this.logger.log('dialog '+ this.id.toString() +' changed to CONFIRMED state'); if(type === 'UAC') { // RFC 3261 13.2.2.4 this.route_set = message.getHeaders('record-route').reverse(); } }, terminate: function() { this.logger.log('dialog ' + this.id.toString() + ' deleted'); delete this.owner.ua.dialogs[this.id.toString()]; }, /** * @param {String} method request method * @param {Object} extraHeaders extra headers * @returns {JsSIP.OutgoingRequest} */ // RFC 3261 12.2.1.1 createRequest: function(method, extraHeaders, body) { var cseq, request; extraHeaders = extraHeaders && extraHeaders.slice() || []; if(!this.local_seqnum) { this.local_seqnum = Math.floor(Math.random() * 10000); } cseq = (method === JsSIP.C.CANCEL || method === JsSIP.C.ACK) ? this.local_seqnum : this.local_seqnum += 1; request = new JsSIP.OutgoingRequest( method, this.remote_target, this.owner.ua, { 'cseq': cseq, 'call_id': this.id.call_id, 'from_uri': this.local_uri, 'from_tag': this.id.local_tag, 'to_uri': this.remote_uri, 'to_tag': this.id.remote_tag, 'route_set': this.route_set }, extraHeaders, body); request.dialog = this; return request; }, /** * @param {JsSIP.IncomingRequest} request * @returns {Boolean} */ // RFC 3261 12.2.2 checkInDialogRequest: function(request) { var self = this; if(!this.remote_seqnum) { this.remote_seqnum = request.cseq; } else if(request.cseq < this.remote_seqnum) { //Do not try to reply to an ACK request. if (request.method !== JsSIP.C.ACK) { request.reply(500); } return false; } else if(request.cseq > this.remote_seqnum) { this.remote_seqnum = request.cseq; } switch(request.method) { // RFC3261 14.2 Modifying an Existing Session -UAS BEHAVIOR- case JsSIP.C.INVITE: if (this.uac_pending_reply === true) { request.reply(491); } else if (this.uas_pending_reply === true) { var retryAfter = (Math.random() * 10 | 0) + 1; request.reply(500, null, ['Retry-After:'+ retryAfter]); return false; } else { this.uas_pending_reply = true; request.server_transaction.on('stateChanged', function stateChanged(e){ if (e.sender.state === JsSIP.Transactions.C.STATUS_ACCEPTED || e.sender.state === JsSIP.Transactions.C.STATUS_COMPLETED || e.sender.state === JsSIP.Transactions.C.STATUS_TERMINATED) { request.server_transaction.removeListener('stateChanged', stateChanged); self.uas_pending_reply = false; if (self.uac_pending_reply === false) { self.owner.onReadyToReinvite(); } } }); } // RFC3261 12.2.2 Replace the dialog`s remote target URI if the request is accepted if(request.hasHeader('contact')) { request.server_transaction.on('stateChanged', function(e){ if (e.sender.state === JsSIP.Transactions.C.STATUS_ACCEPTED) { self.remote_target = request.parseHeader('contact').uri; } }); } break; case JsSIP.C.NOTIFY: // RFC6665 3.2 Replace the dialog`s remote target URI if the request is accepted if(request.hasHeader('contact')) { request.server_transaction.on('stateChanged', function(e){ if (e.sender.state === JsSIP.Transactions.C.STATUS_COMPLETED) { self.remote_target = request.parseHeader('contact').uri; } }); } break; } return true; }, sendRequest: function(applicant, method, options) { options = options || {}; var extraHeaders = options.extraHeaders && options.extraHeaders.slice() || [], body = options.body || null, request = this.createRequest(method, extraHeaders, body), request_sender = new RequestSender(this, applicant, request); request_sender.send(); }, /** * @param {JsSIP.IncomingRequest} request */ receiveRequest: function(request) { //Check in-dialog request if(!this.checkInDialogRequest(request)) { return; } this.owner.receiveRequest(request); } }; Dialog.C = C; JsSIP.Dialog = Dialog; }(JsSIP)); /** * @fileoverview Request Sender */ /** * @augments JsSIP * @class Class creating a request sender. * @param {Object} applicant * @param {JsSIP.UA} ua */ (function(JsSIP) { var RequestSender; RequestSender = function(applicant, ua) { this.logger = ua.getLogger('jssip.requestsender'); this.ua = ua; this.applicant = applicant; this.method = applicant.request.method; this.request = applicant.request; this.credentials = null; this.challenged = false; this.staled = false; // If ua is in closing process or even closed just allow sending Bye and ACK if (ua.status === JsSIP.UA.C.STATUS_USER_CLOSED && (this.method !== JsSIP.C.BYE || this.method !== JsSIP.C.ACK)) { this.onTransportError(); } }; /** * Create the client transaction and send the message. */ RequestSender.prototype = { send: function() { switch(this.method) { case "INVITE": this.clientTransaction = new JsSIP.Transactions.InviteClientTransaction(this, this.request, this.ua.transport); break; case "ACK": this.clientTransaction = new JsSIP.Transactions.AckClientTransaction(this, this.request, this.ua.transport); break; default: this.clientTransaction = new JsSIP.Transactions.NonInviteClientTransaction(this, this.request, this.ua.transport); } this.clientTransaction.send(); }, /** * Callback fired when receiving a request timeout error from the client transaction. * To be re-defined by the applicant. * @event */ onRequestTimeout: function() { this.applicant.onRequestTimeout(); }, /** * Callback fired when receiving a transport error from the client transaction. * To be re-defined by the applicant. * @event */ onTransportError: function() { this.applicant.onTransportError(); }, /** * Called from client transaction when receiving a correct response to the request. * Authenticate request if needed or pass the response back to the applicant. * @param {JsSIP.IncomingResponse} response */ receiveResponse: function(response) { var cseq, challenge, authorization_header_name, status_code = response.status_code; /* * Authentication * Authenticate once. _challenged_ flag used to avoid infinite authentications. */ if ((status_code === 401 || status_code === 407) && this.ua.configuration.password !== null) { // Get and parse the appropriate WWW-Authenticate or Proxy-Authenticate header. if (response.status_code === 401) { challenge = response.parseHeader('www-authenticate'); authorization_header_name = 'authorization'; } else { challenge = response.parseHeader('proxy-authenticate'); authorization_header_name = 'proxy-authorization'; } // Verify it seems a valid challenge. if (! challenge) { this.logger.warn(response.status_code + ' with wrong or missing challenge, cannot authenticate'); this.applicant.receiveResponse(response); return; } if (!this.challenged || (!this.staled && challenge.stale === true)) { if (!this.credentials) { this.credentials = new JsSIP.DigestAuthentication(this.ua); } // Verify that the challenge is really valid. if (!this.credentials.authenticate(this.request, challenge)) { this.applicant.receiveResponse(response); return; } this.challenged = true; if (challenge.stale) { this.staled = true; } if (response.method === JsSIP.C.REGISTER) { cseq = this.applicant.cseq += 1; } else if (this.request.dialog){ cseq = this.request.dialog.local_seqnum += 1; } else { cseq = this.request.cseq + 1; this.request.cseq = cseq; } this.request.setHeader('cseq', cseq +' '+ this.method); this.request.setHeader(authorization_header_name, this.credentials.toString()); this.send(); } else { this.applicant.receiveResponse(response); } } else { this.applicant.receiveResponse(response); } } }; JsSIP.RequestSender = RequestSender; }(JsSIP)); /** * @fileoverview Registrator Agent */ /** * @augments JsSIP * @class Class creating a registrator agent. * @param {JsSIP.UA} ua * @param {JsSIP.Transport} transport */ (function(JsSIP) { var Registrator; Registrator = function(ua, transport) { var reg_id=1; //Force reg_id to 1. this.logger = ua.getLogger('jssip.registrator'); this.ua = ua; this.transport = transport; this.registrar = ua.configuration.registrar_server; this.expires = ua.configuration.register_expires; // Call-ID and CSeq values RFC3261 10.2 this.call_id = JsSIP.Utils.createRandomToken(22); this.cseq = 80; // this.to_uri this.to_uri = ua.configuration.uri; this.registrationTimer = null; // Set status this.registered = this.registered_before = false; // Save into ua instance this.ua.registrator = this; // Contact header this.contact = this.ua.contact.toString(); // sip.ice media feature tag (RFC 5768) this.contact += ';+sip.ice'; this.extraHeaders = []; if(reg_id) { this.contact += ';reg-id='+ reg_id; this.contact += ';+sip.instance=""'; } }; Registrator.prototype = { /** * @param {Object} [options] */ register: function(options) { var request_sender, cause, extraHeaders, self = this; options = options || {}; if (options.extraHeaders && Object.keys(options.extraHeaders).length !== 0) { this.extraHeaders = options.extraHeaders && options.extraHeaders.slice(); } extraHeaders = this.extraHeaders.slice(); extraHeaders.push('Contact: '+ this.contact + ';expires=' + this.expires); this.request = new JsSIP.OutgoingRequest(JsSIP.C.REGISTER, this.registrar, this.ua, { 'to_uri': this.to_uri, 'call_id': this.call_id, 'cseq': (this.cseq += 1) }, extraHeaders); request_sender = new JsSIP.RequestSender(this, this.ua); /** * @private */ this.receiveResponse = function(response) { var contact, expires, contacts = response.getHeaders('contact').length; // Discard responses to older REGISTER/un-REGISTER requests. if(response.cseq !== this.cseq) { return; } // Clear registration timer if (this.registrationTimer !== null) { window.clearTimeout(this.registrationTimer); this.registrationTimer = null; } switch(true) { case /^1[0-9]{2}$/.test(response.status_code): // Ignore provisional responses. break; case /^2[0-9]{2}$/.test(response.status_code): if(response.hasHeader('expires')) { expires = response.getHeader('expires'); } // Search the Contact pointing to us and update the expires value accordingly. if (!contacts) { this.logger.warn('no Contact header in response to REGISTER, response ignored'); break; } while(contacts--) { contact = response.parseHeader('contact', contacts); if(contact.uri.user === this.ua.contact.uri.user) { expires = contact.getParam('expires'); break; } else { contact = null; } } if (!contact) { this.logger.warn('no Contact header pointing to us, response ignored'); break; } if(!expires) { expires = this.expires; } // Re-Register before the expiration interval has elapsed. // For that, decrease the expires value. ie: 3 seconds this.registrationTimer = window.setTimeout(function() { self.registrationTimer = null; self.register(); }, (expires * 1000) - 3000); //Save gruu values if (contact.hasParam('temp-gruu')) { this.ua.contact.temp_gruu = contact.getParam('temp-gruu').replace(/"/g,''); } if (contact.hasParam('pub-gruu')) { this.ua.contact.pub_gruu = contact.getParam('pub-gruu').replace(/"/g,''); } this.registered = true; this.ua.emit('registered', this.ua, { response: response }); break; // Interval too brief RFC3261 10.2.8 case /^423$/.test(response.status_code): if(response.hasHeader('min-expires')) { // Increase our registration interval to the suggested minimum this.expires = response.getHeader('min-expires'); // Attempt the registration again immediately this.register(); } else { //This response MUST contain a Min-Expires header field this.logger.warn('423 response received for REGISTER without Min-Expires'); this.registrationFailure(response, JsSIP.C.causes.SIP_FAILURE_CODE); } break; default: cause = JsSIP.Utils.sipErrorCause(response.status_code); this.registrationFailure(response, cause); } }; /** * @private */ this.onRequestTimeout = function() { this.registrationFailure(null, JsSIP.C.causes.REQUEST_TIMEOUT); }; /** * @private */ this.onTransportError = function() { this.registrationFailure(null, JsSIP.C.causes.CONNECTION_ERROR); }; request_sender.send(); }, /** * @param {Object} [options] */ unregister: function(options) { var extraHeaders; if(!this.registered) { this.logger.warn('already unregistered'); return; } options = options || {}; if (options.extraHeaders && Object.keys(options.extraHeaders).length !== 0) { this.extraHeaders = options.extraHeaders && options.extraHeaders.slice(); } extraHeaders = this.extraHeaders.slice(); this.registered = false; // Clear the registration timer. if (this.registrationTimer !== null) { window.clearTimeout(this.registrationTimer); this.registrationTimer = null; } if(options.all) { extraHeaders.push('Contact: *'); extraHeaders.push('Expires: 0'); this.request = new JsSIP.OutgoingRequest(JsSIP.C.REGISTER, this.registrar, this.ua, { 'to_uri': this.to_uri, 'call_id': this.call_id, 'cseq': (this.cseq += 1) }, extraHeaders); } else { extraHeaders.push('Contact: '+ this.contact + ';expires=0'); this.request = new JsSIP.OutgoingRequest(JsSIP.C.REGISTER, this.registrar, this.ua, { 'to_uri': this.to_uri, 'call_id': this.call_id, 'cseq': (this.cseq += 1) }, extraHeaders); } var request_sender = new JsSIP.RequestSender(this, this.ua); /** * @private */ this.receiveResponse = function(response) { var cause; switch(true) { case /^1[0-9]{2}$/.test(response.status_code): // Ignore provisional responses. break; case /^2[0-9]{2}$/.test(response.status_code): this.unregistered(response); break; default: cause = JsSIP.Utils.sipErrorCause(response.status_code); this.unregistered(response, cause); } }; /** * @private */ this.onRequestTimeout = function() { this.unregistered(null, JsSIP.C.causes.REQUEST_TIMEOUT); }; /** * @private */ this.onTransportError = function() { this.unregistered(null, JsSIP.C.causes.CONNECTION_ERROR); }; request_sender.send(); }, /** * @private */ registrationFailure: function(response, cause) { this.ua.emit('registrationFailed', this.ua, { response: response || null, cause: cause }); if (this.registered) { this.registered = false; this.ua.emit('unregistered', this.ua, { response: response || null, cause: cause }); } }, /** * @private */ unregistered: function(response, cause) { this.registered = false; this.ua.emit('unregistered', this.ua, { response: response || null, cause: cause || null }); }, /** * @private */ onTransportClosed: function() { this.registered_before = this.registered; if (this.registrationTimer !== null) { window.clearTimeout(this.registrationTimer); this.registrationTimer = null; } if(this.registered) { this.registered = false; this.ua.emit('unregistered', this.ua); } }, /** * @private */ onTransportConnected: function() { this.register(); }, /** * @private */ close: function() { this.registered_before = this.registered; this.unregister(); } }; JsSIP.Registrator = Registrator; }(JsSIP)); /** * @fileoverview Session */ /** * @augments JsSIP * @class Invite Session */ (function(JsSIP) { // Load dependencies var Request = /** * @fileoverview Request */ /** * @class Request * @param {JsSIP.RTCSession} session */ (function(JsSIP) { var Request = function(session) { var events = [ 'progress', 'succeeded', 'failed' ]; this.owner = session; this.logger = session.ua.getLogger('jssip.rtcsession.request', session.id); this.initEvents(events); }; Request.prototype = new JsSIP.EventEmitter(); Request.prototype.send = function(method, options) { options = options || {}; var event, extraHeaders = options.extraHeaders && options.extraHeaders.slice() || [], eventHandlers = options.eventHandlers || {}, body = options.body || null; if (method === undefined) { throw new TypeError('Not enough arguments'); } // Check RTCSession Status if (this.owner.status !== JsSIP.RTCSession.C.STATUS_1XX_RECEIVED && this.owner.status !== JsSIP.RTCSession.C.STATUS_WAITING_FOR_ANSWER && this.owner.status !== JsSIP.RTCSession.C.STATUS_WAITING_FOR_ACK && this.owner.status !== JsSIP.RTCSession.C.STATUS_CONFIRMED && this.owner.status !== JsSIP.RTCSession.C.STATUS_TERMINATED) { throw new JsSIP.Exceptions.InvalidStateError(this.owner.status); } /* * Allow sending BYE in TERMINATED status since the RTCSession * could had been terminated before the ACK had arrived. * RFC3261 Section 15, Paragraph 2 */ else if (this.owner.status === C.STATUS_TERMINATED && method !== JsSIP.C.BYE) { throw new JsSIP.Exceptions.InvalidStateError(this.owner.status); } // Set event handlers for (event in eventHandlers) { this.on(event, eventHandlers[event]); } this.owner.dialog.sendRequest(this, method, { extraHeaders: extraHeaders, body: body }); }; /** * @private */ Request.prototype.receiveResponse = function(response) { var cause; switch(true) { case /^1[0-9]{2}$/.test(response.status_code): this.emit('progress', this, { originator: 'remote', response: response }); break; case /^2[0-9]{2}$/.test(response.status_code): this.emit('succeeded', this, { originator: 'remote', response: response }); break; default: cause = JsSIP.Utils.sipErrorCause(response.status_code); this.emit('failed', this, { originator: 'remote', response: response, cause: cause }); break; } }; /** * @private */ Request.prototype.onRequestTimeout = function() { this.emit('failed', this, { originator: 'system', cause: JsSIP.C.causes.REQUEST_TIMEOUT }); this.owner.onRequestTimeout(); }; /** * @private */ Request.prototype.onTransportError = function() { this.emit('failed', this, { originator: 'system', cause: JsSIP.C.causes.CONNECTION_ERROR }); this.owner.onTransportError(); }; /** * @private */ Request.prototype.onDialogError = function(response) { this.emit('failed', this, { originator: 'remote', response: response, cause: JsSIP.C.causes.DIALOG_ERROR }); this.owner.onDialogError(response); }; return Request; }(JsSIP)); var RTCMediaHandler = /** * @fileoverview RTCMediaHandler */ /* RTCMediaHandler * @class PeerConnection helper Class. * @param {JsSIP.RTCSession} session * @param {Object} [contraints] */ (function(JsSIP){ var RTCMediaHandler = function(session, constraints) { constraints = constraints || {}; this.logger = session.ua.getLogger('jssip.rtcsession.rtcmediahandler', session.id); this.session = session; this.localMedia = null; this.peerConnection = null; this.ready = true; this.init(constraints); }; RTCMediaHandler.prototype = { isReady: function() { return this.ready; }, createOffer: function(onSuccess, onFailure, constraints) { var self = this; function onSetLocalDescriptionSuccess() { if (self.peerConnection.iceGatheringState === 'complete' && self.peerConnection.iceConnectionState === 'connected') { self.ready = true; onSuccess(self.peerConnection.localDescription.sdp); } else { self.onIceCompleted = function() { self.onIceCompleted = undefined; self.ready = true; onSuccess(self.peerConnection.localDescription.sdp); }; } } this.ready = false; this.peerConnection.createOffer( function(sessionDescription){ self.setLocalDescription( sessionDescription, onSetLocalDescriptionSuccess, function(e) { self.ready = true; onFailure(e); } ); }, function(e) { self.ready = true; self.logger.error('unable to create offer'); self.logger.error(e); onFailure(e); }, constraints ); }, createAnswer: function(onSuccess, onFailure, constraints) { var self = this; function onSetLocalDescriptionSuccess() { if (self.peerConnection.iceGatheringState === 'complete' && self.peerConnection.iceConnectionState === 'connected') { self.ready = true; onSuccess(self.peerConnection.localDescription.sdp); } else { self.onIceCompleted = function() { self.onIceCompleted = undefined; self.ready = true; onSuccess(self.peerConnection.localDescription.sdp); }; } } this.ready = false; this.peerConnection.createAnswer( function(sessionDescription){ self.setLocalDescription( sessionDescription, onSetLocalDescriptionSuccess, function(e) { self.ready = true; onFailure(e); } ); }, function(e) { self.ready = true; self.logger.error('unable to create answer'); self.logger.error(e); onFailure(e); }, constraints ); }, setLocalDescription: function(sessionDescription, onSuccess, onFailure) { var self = this; this.peerConnection.setLocalDescription( sessionDescription, onSuccess, function(e) { self.logger.error('unable to set local description'); self.logger.error(e); onFailure(e); } ); }, addStream: function(stream, onSuccess, onFailure, constraints) { try { this.peerConnection.addStream(stream, constraints); } catch(e) { this.logger.error('error adding stream'); this.logger.error(e); onFailure(); return; } onSuccess(); }, /** * peerConnection creation. * @param {Function} onSuccess Fired when there are no more ICE candidates */ init: function(options) { options = options || {}; var idx, length, server, self = this, servers = [], constraints = options.constraints || {}, stun_servers = options.stun_servers || null, turn_servers = options.turn_servers || null, config = this.session.ua.configuration; if (!stun_servers) { stun_servers = config.stun_servers; } if (!turn_servers) { turn_servers = config.turn_servers; } /* Change 'url' to 'urls' whenever this issue is solved: * https://code.google.com/p/webrtc/issues/detail?id=2096 */ if (stun_servers.length > 0) { servers.push({'url': stun_servers}); } length = turn_servers.length; for (idx = 0; idx < length; idx++) { server = turn_servers[idx]; servers.push({ 'url': server.urls, 'username': server.username, 'credential': server.credential }); } this.peerConnection = new JsSIP.WebRTC.RTCPeerConnection({'iceServers': servers}, constraints); this.peerConnection.onaddstream = function(e) { self.logger.log('stream added: '+ e.stream.id); }; this.peerConnection.onremovestream = function(e) { self.logger.log('stream removed: '+ e.stream.id); }; this.peerConnection.onicecandidate = function(e) { if (e.candidate) { self.logger.log('ICE candidate received: '+ e.candidate.candidate); } else if (self.onIceCompleted !== undefined) { self.onIceCompleted(); } }; this.peerConnection.oniceconnectionstatechange = function() { self.logger.log('ICE connection state changed to "'+ this.iceConnectionState +'"'); if (this.iceConnectionState === 'disconnected') { self.session.terminate({ cause: JsSIP.C.causes.RTP_TIMEOUT, status_code: 200, reason_phrase: JsSIP.C.causes.RTP_TIMEOUT }); } }; this.peerConnection.onstatechange = function() { self.logger.log('PeerConnection state changed to "'+ this.readyState +'"'); }; }, close: function() { this.logger.log('closing PeerConnection'); if(this.peerConnection) { this.peerConnection.close(); if(this.localMedia) { this.localMedia.stop(); } } }, /** * @param {Object} mediaConstraints * @param {Function} onSuccess * @param {Function} onFailure */ getUserMedia: function(onSuccess, onFailure, constraints) { var self = this; this.logger.log('requesting access to local media'); JsSIP.WebRTC.getUserMedia(constraints, function(stream) { self.logger.log('got local media stream'); self.localMedia = stream; onSuccess(stream); }, function(e) { self.logger.error('unable to get user media'); self.logger.error(e); onFailure(); } ); }, /** * Message reception. * @param {String} type * @param {String} sdp * @param {Function} onSuccess * @param {Function} onFailure */ onMessage: function(type, body, onSuccess, onFailure) { this.peerConnection.setRemoteDescription( new JsSIP.WebRTC.RTCSessionDescription({type: type, sdp:body}), onSuccess, onFailure ); } }; // Return since it will be assigned to a variable. return RTCMediaHandler; }(JsSIP)); var DTMF = /** * @fileoverview DTMF */ /** * @class DTMF * @param {JsSIP.RTCSession} session */ (function(JsSIP) { var DTMF, C = { MIN_DURATION: 70, MAX_DURATION: 6000, DEFAULT_DURATION: 100, MIN_INTER_TONE_GAP: 50, DEFAULT_INTER_TONE_GAP: 500 }; DTMF = function(session) { var events = [ 'succeeded', 'failed' ]; this.logger = session.ua.getLogger('jssip.rtcsession.dtmf', session.id); this.owner = session; this.direction = null; this.tone = null; this.duration = null; this.initEvents(events); }; DTMF.prototype = new JsSIP.EventEmitter(); DTMF.prototype.send = function(tone, options) { var event, eventHandlers, extraHeaders, body; if (tone === undefined) { throw new TypeError('Not enough arguments'); } this.direction = 'outgoing'; // Check RTCSession Status if (this.owner.status !== JsSIP.RTCSession.C.STATUS_CONFIRMED && this.owner.status !== JsSIP.RTCSession.C.STATUS_WAITING_FOR_ACK) { throw new JsSIP.Exceptions.InvalidStateError(this.owner.status); } // Get DTMF options options = options || {}; extraHeaders = options.extraHeaders ? options.extraHeaders.slice() : []; eventHandlers = options.eventHandlers || {}; // Check tone type if (typeof tone === 'string' ) { tone = tone.toUpperCase(); } else if (typeof tone === 'number') { tone = tone.toString(); } else { throw new TypeError('Invalid tone: '+ tone); } // Check tone value if (!tone.match(/^[0-9A-D#*]$/)) { throw new TypeError('Invalid tone: '+ tone); } else { this.tone = tone; } // Duration is checked/corrected in RTCSession this.duration = options.duration; // Set event handlers for (event in eventHandlers) { this.on(event, eventHandlers[event]); } extraHeaders.push('Content-Type: application/dtmf-relay'); body = "Signal= " + this.tone + "\r\n"; body += "Duration= " + this.duration; this.owner.emit('newDTMF', this.owner, { originator: 'local', dtmf: this, request: this.request }); this.owner.dialog.sendRequest(this, JsSIP.C.INFO, { extraHeaders: extraHeaders, body: body }); }; /** * @private */ DTMF.prototype.receiveResponse = function(response) { var cause; switch(true) { case /^1[0-9]{2}$/.test(response.status_code): // Ignore provisional responses. break; case /^2[0-9]{2}$/.test(response.status_code): this.emit('succeeded', this, { originator: 'remote', response: response }); break; default: cause = JsSIP.Utils.sipErrorCause(response.status_code); this.emit('failed', this, { originator: 'remote', response: response, cause: cause }); break; } }; /** * @private */ DTMF.prototype.onRequestTimeout = function() { this.emit('failed', this, { originator: 'system', cause: JsSIP.C.causes.REQUEST_TIMEOUT }); this.owner.onRequestTimeout(); }; /** * @private */ DTMF.prototype.onTransportError = function() { this.emit('failed', this, { originator: 'system', cause: JsSIP.C.causes.CONNECTION_ERROR }); this.owner.onTransportError(); }; /** * @private */ DTMF.prototype.onDialogError = function(response) { this.emit('failed', this, { originator: 'remote', response: response, cause: JsSIP.C.causes.DIALOG_ERROR }); this.owner.onDialogError(response); }; /** * @private */ DTMF.prototype.init_incoming = function(request) { var body, reg_tone = /^(Signal\s*?=\s*?)([0-9A-D#*]{1})(\s)?.*/, reg_duration = /^(Duration\s?=\s?)([0-9]{1,4})(\s)?.*/; this.direction = 'incoming'; this.request = request; request.reply(200); if (request.body) { body = request.body.split('\r\n'); if (body.length === 2) { if (reg_tone.test(body[0])) { this.tone = body[0].replace(reg_tone,"$2"); } if (reg_duration.test(body[1])) { this.duration = parseInt(body[1].replace(reg_duration,"$2"), 10); } } } if (!this.tone || !this.duration) { this.logger.warn('invalid INFO DTMF received, discarded'); } else { this.owner.emit('newDTMF', this.owner, { originator: 'remote', dtmf: this, request: request }); } }; DTMF.C = C; return DTMF; }(JsSIP)); var RTCSession, C = { // RTCSession states STATUS_NULL: 0, STATUS_INVITE_SENT: 1, STATUS_1XX_RECEIVED: 2, STATUS_INVITE_RECEIVED: 3, STATUS_WAITING_FOR_ANSWER: 4, STATUS_ANSWERED: 5, STATUS_WAITING_FOR_ACK: 6, STATUS_CANCELED: 7, STATUS_TERMINATED: 8, STATUS_CONFIRMED: 9 }; RTCSession = function(ua) { var events = [ 'connecting', 'progress', 'failed', 'started', 'ended', 'newDTMF', 'hold', 'unhold', 'muted', 'unmuted' ]; this.ua = ua; this.status = C.STATUS_NULL; this.dialog = null; this.earlyDialogs = {}; this.rtcMediaHandler = null; // Session Timers this.timers = { ackTimer: null, expiresTimer: null, invite2xxTimer: null, userNoAnswerTimer: null }; // Session info this.direction = null; this.local_identity = null; this.remote_identity = null; this.start_time = null; this.end_time = null; this.tones = null; // Mute/Hold state this.audioMuted = false; this.videoMuted = false; this.local_hold = false; this.remote_hold = false; this.pending_actions = { actions: [], length: function() { return this.actions.length; }, isPending: function(name){ var idx = 0, length = this.actions.length; for (idx; idx= 700)) { throw new TypeError('Invalid status_code: '+ status_code); } else if (status_code) { reason_phrase = reason_phrase || JsSIP.C.REASON_PHRASE[status_code] || ''; cancel_reason = 'SIP ;cause=' + status_code + ' ;text="' + reason_phrase + '"'; } // Check Session Status if (this.status === C.STATUS_NULL) { this.isCanceled = true; this.cancelReason = cancel_reason; } else if (this.status === C.STATUS_INVITE_SENT) { if(this.received_100) { this.request.cancel(cancel_reason); } else { this.isCanceled = true; this.cancelReason = cancel_reason; } } else if(this.status === C.STATUS_1XX_RECEIVED) { this.request.cancel(cancel_reason); } this.failed('local', null, JsSIP.C.causes.CANCELED); break; // - UAS - case C.STATUS_WAITING_FOR_ANSWER: case C.STATUS_ANSWERED: this.logger.log('rejecting RTCSession'); status_code = status_code || 480; if (status_code < 300 || status_code >= 700) { throw new TypeError('Invalid status_code: '+ status_code); } this.request.reply(status_code, reason_phrase, extraHeaders, body); this.failed('local', null, JsSIP.C.causes.REJECTED); break; case C.STATUS_WAITING_FOR_ACK: case C.STATUS_CONFIRMED: this.logger.log('terminating RTCSession'); reason_phrase = options.reason_phrase || JsSIP.C.REASON_PHRASE[status_code] || ''; if (status_code && (status_code < 200 || status_code >= 700)) { throw new TypeError('Invalid status_code: '+ status_code); } else if (status_code) { extraHeaders.push('Reason: SIP ;cause=' + status_code + '; text="' + reason_phrase + '"'); } /* RFC 3261 section 15 (Terminating a session): * * "...the callee's UA MUST NOT send a BYE on a confirmed dialog * until it has received an ACK for its 2xx response or until the server * transaction times out." */ if (this.status === C.STATUS_WAITING_FOR_ACK && this.direction === 'incoming' && this.request.server_transaction.state !== JsSIP.Transactions.C.STATUS_TERMINATED) { // Save the dialog for later restoration dialog = this.dialog; // Send the BYE as soon as the ACK is received... this.receiveRequest = function(request) { if(request.method === JsSIP.C.ACK) { this.sendRequest(JsSIP.C.BYE, { extraHeaders: extraHeaders, body: body }); dialog.terminate(); } }; // .., or when the INVITE transaction times out this.request.server_transaction.on('stateChanged', function(e){ if (e.sender.state === JsSIP.Transactions.C.STATUS_TERMINATED) { self.sendRequest(JsSIP.C.BYE, { extraHeaders: extraHeaders, body: body }); dialog.terminate(); } }); this.ended('local', null, cause); // Restore the dialog into 'this' in order to be able to send the in-dialog BYE :-) this.dialog = dialog; // Restore the dialog into 'ua' so the ACK can reach 'this' session this.ua.dialogs[dialog.id.toString()] = dialog; } else { this.sendRequest(JsSIP.C.BYE, { extraHeaders: extraHeaders, body: body }); this.ended('local', null, cause); } } this.close(); }; /** * Answer the call. * @param {Object} [options] */ RTCSession.prototype.answer = function(options) { options = options || {}; var idx, length, hasAudio, hasVideo, self = this, request = this.request, extraHeaders = options.extraHeaders && options.extraHeaders.slice() || [], mediaConstraints = options.mediaConstraints || {'audio':true, 'video':true}, RTCAnswerConstraints = options.RTCAnswerConstraints || {}, mediaStream = options.mediaStream || null, // User media succeeded userMediaSucceeded = function(stream) { self.rtcMediaHandler.addStream( stream, streamAdditionSucceeded, streamAdditionFailed ); }, // User media failed userMediaFailed = function() { request.reply(480); self.failed('local', null, JsSIP.C.causes.USER_DENIED_MEDIA_ACCESS); }, // rtcMediaHandler.addStream successfully added streamAdditionSucceeded = function() { self.connecting(request); if (self.status === C.STATUS_TERMINATED) { return; } self.rtcMediaHandler.createAnswer( answerCreationSucceeded, answerCreationFailed, RTCAnswerConstraints ); }, // rtcMediaHandler.addStream failed streamAdditionFailed = function() { if (self.status === C.STATUS_TERMINATED) { return; } self.failed('system', null, JsSIP.C.causes.WEBRTC_ERROR); }, // rtcMediaHandler.createAnswer succeeded answerCreationSucceeded = function(body) { var // run for reply success callback replySucceeded = function() { self.status = C.STATUS_WAITING_FOR_ACK; self.setInvite2xxTimer(request, body); self.setACKTimer(); self.started('local'); }, // run for reply failure callback replyFailed = function() { self.failed('system', null, JsSIP.C.causes.CONNECTION_ERROR); }; request.reply(200, null, extraHeaders, body, replySucceeded, replyFailed ); }, // rtcMediaHandler.createAnsewr failed answerCreationFailed = function() { if (self.status === C.STATUS_TERMINATED) { return; } self.failed('system', null, JsSIP.C.causes.WEBRTC_ERROR); }; // Check Session Direction and Status if (this.direction !== 'incoming') { throw new JsSIP.Exceptions.NotSupportedError('"answer" not supported for outgoing RTCSession'); } else if (this.status !== C.STATUS_WAITING_FOR_ANSWER) { throw new JsSIP.Exceptions.InvalidStateError(this.status); } this.status = C.STATUS_ANSWERED; // An error on dialog creation will fire 'failed' event if(!this.createDialog(request, 'UAS')) { request.reply(500, 'Missing Contact header field'); return; } window.clearTimeout(this.timers.userNoAnswerTimer); extraHeaders.unshift('Contact: ' + self.contact); length = this.getRemoteStreams().length; for (idx=0; idx 0) { hasAudio=true; } if (this.getRemoteStreams()[idx].getVideoTracks().length > 0) { hasVideo=true; } } if (!hasAudio && mediaConstraints.audio === true) { mediaConstraints.audio = false; if (mediaStream) { length = mediaStream.getAudioTracks().length; for (idx=0; idx DTMF.C.MAX_DURATION) { this.logger.warn('"duration" value is greater than the maximum allowed, setting it to '+ DTMF.C.MAX_DURATION +' milliseconds'); duration = DTMF.C.MAX_DURATION; } else { duration = Math.abs(duration); } options.duration = duration; // Check interToneGap if (interToneGap && !JsSIP.Utils.isDecimal(interToneGap)) { throw new TypeError('Invalid interToneGap: '+ interToneGap); } else if (!interToneGap) { interToneGap = DTMF.C.DEFAULT_INTER_TONE_GAP; } else if (interToneGap < DTMF.C.MIN_INTER_TONE_GAP) { this.logger.warn('"interToneGap" value is lower than the minimum allowed, setting it to '+ DTMF.C.MIN_INTER_TONE_GAP +' milliseconds'); interToneGap = DTMF.C.MIN_INTER_TONE_GAP; } else { interToneGap = Math.abs(interToneGap); } if (this.tones) { // Tones are already queued, just add to the queue this.tones += tones; return; } // New set of tones to start sending this.tones = tones; var sendDTMF = function () { var tone, timeout, tones = self.tones; if (self.status === C.STATUS_TERMINATED || !tones || position >= tones.length) { // Stop sending DTMF self.tones = null; return; } tone = tones[position]; position += 1; if (tone === ',') { timeout = 2000; } else { var dtmf = new DTMF(self); dtmf.on('failed', function(){self.tones = null;}); dtmf.send(tone, options); timeout = duration + interToneGap; } // Set timeout for the next tone window.setTimeout(sendDTMF, timeout); }; // Send the first tone sendDTMF(); }; /** * Send a generic in-dialog Request * * @param {String} method * @param {Object} [options] */ RTCSession.prototype.sendRequest = function(method, options) { var request = new Request(this); request.send(method, options); }; /** * Check if RTCSession is ready for a re-INVITE * * @returns {Boolean} */ RTCSession.prototype.isReadyToReinvite = function() { // rtcMediaHandler is not ready if (!this.rtcMediaHandler.isReady()) { return; } // Another INVITE transaction is in progress if (this.dialog.uac_pending_reply === true || this.dialog.uas_pending_reply === true) { return false; } else { return true; } }; /** * Mute */ RTCSession.prototype.mute = function(options) { options = options || {audio:true, video:false}; var audioMuted = false, videoMuted = false; if (this.audioMuted === false && options.audio) { audioMuted = true; this.audioMuted = true; this.toogleMuteAudio(true); } if (this.videoMuted === false && options.video) { videoMuted = true; this.videoMuted = true; this.toogleMuteVideo(true); } if (audioMuted === true || videoMuted === true) { this.onmute({ audio: audioMuted, video: videoMuted }); } }; /** * Unmute */ RTCSession.prototype.unmute = function(options) { options = options || {audio:true, video:true}; var audioUnMuted = false, videoUnMuted = false; if (this.audioMuted === true && options.audio) { audioUnMuted = true; this.audioMuted = false; if (this.local_hold === false) { this.toogleMuteAudio(false); } } if (this.videoMuted === true && options.video) { videoUnMuted = true; this.videoMuted = false; if (this.local_hold === false) { this.toogleMuteVideo(false); } } if (audioUnMuted === true || videoUnMuted === true) { this.onunmute({ audio: audioUnMuted, video: videoUnMuted }); } }; /** * isMuted */ RTCSession.prototype.isMuted = function() { return { audio: this.audioMuted, video: this.videoMuted }; }; /** * Hold */ RTCSession.prototype.hold = function() { if (this.status !== C.STATUS_WAITING_FOR_ACK && this.status !== C.STATUS_CONFIRMED) { throw new JsSIP.Exceptions.InvalidStateError(this.status); } this.toogleMuteAudio(true); this.toogleMuteVideo(true); if (!this.isReadyToReinvite()) { /* If there is a pending 'unhold' action, cancel it and don't queue this one * Else, if there isn't any 'hold' action, add this one to the queue * Else, if there is already a 'hold' action, skip */ if (this.pending_actions.isPending('unhold')) { this.pending_actions.pop('unhold'); return; } else if (!this.pending_actions.isPending('hold')) { this.pending_actions.push('hold'); return; } else { return; } } else { if (this.local_hold === true) { return; } } this.onhold('local'); this.sendReinvite({ mangle: function(body){ var idx, length; body = JsSIP.Parser.parseSDP(body); length = body.media.length; for (idx=0; idx JsSIP.Timers.T2) { timeout = JsSIP.Timers.T2; } } self.timers.invite2xxTimer = window.setTimeout( invite2xxRetransmission, timeout ); }, timeout); }; /** * RFC3261 14.2 * If a UAS generates a 2xx response and never receives an ACK, * it SHOULD generate a BYE to terminate the dialog. */ RTCSession.prototype.setACKTimer = function() { var self = this; this.timers.ackTimer = window.setTimeout(function() { if(self.status === C.STATUS_WAITING_FOR_ACK) { this.logger.log('no ACK received, terminating the call'); window.clearTimeout(self.timers.invite2xxTimer); self.sendRequest(JsSIP.C.BYE); self.ended('remote', null, JsSIP.C.causes.NO_ACK); } }, JsSIP.Timers.TIMER_H); }; /** * RTCPeerconnection handlers */ RTCSession.prototype.getLocalStreams = function() { return this.rtcMediaHandler && this.rtcMediaHandler.peerConnection && this.rtcMediaHandler.peerConnection.getLocalStreams() || []; }; RTCSession.prototype.getRemoteStreams = function() { return this.rtcMediaHandler && this.rtcMediaHandler.peerConnection && this.rtcMediaHandler.peerConnection.getRemoteStreams() || []; }; /** * Session Management */ /** * @private */ RTCSession.prototype.init_incoming = function(request) { var expires, self = this, contentType = request.getHeader('Content-Type'); // Check body and content type if(!request.body || (contentType !== 'application/sdp')) { request.reply(415); return; } // Session parameter initialization this.status = C.STATUS_INVITE_RECEIVED; this.from_tag = request.from_tag; this.id = request.call_id + this.from_tag; this.request = request; this.contact = this.ua.contact.toString(); this.logger = this.ua.getLogger('jssip.rtcsession', this.id); //Save the session into the ua sessions collection. this.ua.sessions[this.id] = this; //Get the Expires header value if exists if(request.hasHeader('expires')) { expires = request.getHeader('expires') * 1000; } /* Set the to_tag before * replying a response code that will create a dialog. */ request.to_tag = JsSIP.Utils.newTag(); // An error on dialog creation will fire 'failed' event if(!this.createDialog(request, 'UAS', true)) { request.reply(500, 'Missing Contact header field'); return; } //Initialize Media Session this.rtcMediaHandler = new RTCMediaHandler(this, { RTCConstraints: {"optional": [{'DtlsSrtpKeyAgreement': 'true'}]} }); this.rtcMediaHandler.onMessage( 'offer', request.body, /* * onSuccess * SDP Offer is valid. Fire UA newRTCSession */ function() { request.reply(180, null, ['Contact: ' + self.contact]); self.status = C.STATUS_WAITING_FOR_ANSWER; // Set userNoAnswerTimer self.timers.userNoAnswerTimer = window.setTimeout(function() { request.reply(408); self.failed('local',null, JsSIP.C.causes.NO_ANSWER); }, self.ua.configuration.no_answer_timeout ); /* Set expiresTimer * RFC3261 13.3.1 */ if (expires) { self.timers.expiresTimer = window.setTimeout(function() { if(self.status === C.STATUS_WAITING_FOR_ANSWER) { request.reply(487); self.failed('system', null, JsSIP.C.causes.EXPIRES); } }, expires ); } self.newRTCSession('remote', request); }, /* * onFailure * Bad media description */ function(e) { self.logger.warn('invalid SDP'); self.logger.warn(e); request.reply(488); } ); }; /** * @private */ RTCSession.prototype.connect = function(target, options) { options = options || {}; var event, requestParams, iceServers, originalTarget = target, eventHandlers = options.eventHandlers || {}, extraHeaders = options.extraHeaders && options.extraHeaders.slice() || [], mediaConstraints = options.mediaConstraints || {audio: true, video: true}, mediaStream = options.mediaStream || null, RTCConstraints = options.RTCConstraints || {}, RTCOfferConstraints = options.RTCOfferConstraints || {}, stun_servers = options.stun_servers || null, turn_servers = options.turn_servers || null; if (stun_servers) { iceServers = JsSIP.UA.configuration_check.optional['stun_servers'](stun_servers); if (!iceServers) { throw new TypeError('Invalid stun_servers: '+ stun_servers); } else { stun_servers = iceServers; } } if (turn_servers) { iceServers = JsSIP.UA.configuration_check.optional['turn_servers'](turn_servers); if (!iceServers){ throw new TypeError('Invalid turn_servers: '+ turn_servers); } else { turn_servers = iceServers; } } if (target === undefined) { throw new TypeError('Not enough arguments'); } // Check WebRTC support if (!JsSIP.WebRTC.isSupported) { throw new JsSIP.Exceptions.NotSupportedError('WebRTC not supported'); } // Check target validity target = this.ua.normalizeTarget(target); if (!target) { throw new TypeError('Invalid target: '+ originalTarget); } // Check Session Status if (this.status !== C.STATUS_NULL) { throw new JsSIP.Exceptions.InvalidStateError(this.status); } // Set event handlers for (event in eventHandlers) { this.on(event, eventHandlers[event]); } // Session parameter initialization this.from_tag = JsSIP.Utils.newTag(); // Set anonymous property this.anonymous = options.anonymous || false; // OutgoingSession specific parameters this.isCanceled = false; this.received_100 = false; requestParams = {from_tag: this.from_tag}; this.contact = this.ua.contact.toString({ anonymous: this.anonymous, outbound: true }); if (this.anonymous) { requestParams.from_display_name = 'Anonymous'; requestParams.from_uri = 'sip:anonymous@anonymous.invalid'; extraHeaders.push('P-Preferred-Identity: '+ this.ua.configuration.uri.toString()); extraHeaders.push('Privacy: id'); } extraHeaders.push('Contact: '+ this.contact); extraHeaders.push('Content-Type: application/sdp'); this.request = new JsSIP.OutgoingRequest(JsSIP.C.INVITE, target, this.ua, requestParams, extraHeaders); this.id = this.request.call_id + this.from_tag; this.logger = this.ua.getLogger('jssip.rtcsession', this.id); this.rtcMediaHandler = new RTCMediaHandler(this, { RTCConstraints: RTCConstraints, stun_servers: stun_servers, turn_servers: turn_servers }); //Save the session into the ua sessions collection. this.ua.sessions[this.id] = this; this.newRTCSession('local', this.request); this.sendInitialRequest(mediaConstraints, RTCOfferConstraints, mediaStream); }; /** * @private */ RTCSession.prototype.close = function() { var idx; if(this.status === C.STATUS_TERMINATED) { return; } this.logger.log('closing INVITE session ' + this.id); // 1st Step. Terminate media. if (this.rtcMediaHandler){ this.rtcMediaHandler.close(); } // 2nd Step. Terminate signaling. // Clear session timers for(idx in this.timers) { window.clearTimeout(this.timers[idx]); } // Terminate dialogs // Terminate confirmed dialog if(this.dialog) { this.dialog.terminate(); delete this.dialog; } // Terminate early dialogs for(idx in this.earlyDialogs) { this.earlyDialogs[idx].terminate(); delete this.earlyDialogs[idx]; } this.status = C.STATUS_TERMINATED; delete this.ua.sessions[this.id]; }; /** * Dialog Management * @private */ RTCSession.prototype.createDialog = function(message, type, early) { var dialog, early_dialog, local_tag = (type === 'UAS') ? message.to_tag : message.from_tag, remote_tag = (type === 'UAS') ? message.from_tag : message.to_tag, id = message.call_id + local_tag + remote_tag; early_dialog = this.earlyDialogs[id]; // Early Dialog if (early) { if (early_dialog) { return true; } else { early_dialog = new JsSIP.Dialog(this, message, type, JsSIP.Dialog.C.STATUS_EARLY); // Dialog has been successfully created. if(early_dialog.error) { this.logger.error(dialog.error); this.failed('remote', message, JsSIP.C.causes.INTERNAL_ERROR); return false; } else { this.earlyDialogs[id] = early_dialog; return true; } } } // Confirmed Dialog else { // In case the dialog is in _early_ state, update it if (early_dialog) { early_dialog.update(message, type); this.dialog = early_dialog; delete this.earlyDialogs[id]; return true; } // Otherwise, create a _confirmed_ dialog dialog = new JsSIP.Dialog(this, message, type); if(dialog.error) { this.logger.error(dialog.error); this.failed('remote', message, JsSIP.C.causes.INTERNAL_ERROR); return false; } else { this.to_tag = message.to_tag; this.dialog = dialog; return true; } } }; /** * In dialog INVITE Reception * @private */ RTCSession.prototype.receiveReinvite = function(request) { var sdp, idx, direction, self = this, contentType = request.getHeader('Content-Type'), hold = true; if (request.body) { if (contentType !== 'application/sdp') { this.logger.warn('invalid Content-Type'); request.reply(415); return; } sdp = JsSIP.Parser.parseSDP(request.body); for (idx=0; idx < sdp.media.length; idx++) { direction = sdp.direction || sdp.media[idx].direction || 'sendrecv'; if (direction !== 'sendonly' && direction !== 'inactive') { hold = false; } } this.rtcMediaHandler.onMessage( 'offer', request.body, /* * onSuccess * SDP Offer is valid */ function() { self.rtcMediaHandler.createAnswer( function(body) { request.reply(200, null, ['Contact: ' + self.contact], body, function() { self.status = C.STATUS_WAITING_FOR_ACK; self.setInvite2xxTimer(request, body); self.setACKTimer(); if (self.remote_hold === true && hold === false) { self.onunhold('remote'); } else if (self.remote_hold === false && hold === true) { self.onhold('remote'); } } ); }, function() { request.reply(500); } ); }, /* * onFailure * Bad media description */ function(e) { self.logger.error(e); request.reply(488); } ); } }; /** * In dialog Request Reception * @private */ RTCSession.prototype.receiveRequest = function(request) { var contentType; if(request.method === JsSIP.C.CANCEL) { /* RFC3261 15 States that a UAS may have accepted an invitation while a CANCEL * was in progress and that the UAC MAY continue with the session established by * any 2xx response, or MAY terminate with BYE. JsSIP does continue with the * established session. So the CANCEL is processed only if the session is not yet * established. */ /* * Terminate the whole session in case the user didn't accept (or yet send the answer) * nor reject the request opening the session. */ if(this.status === C.STATUS_WAITING_FOR_ANSWER || this.status === C.STATUS_ANSWERED) { this.status = C.STATUS_CANCELED; this.request.reply(487); this.failed('remote', request, JsSIP.C.causes.CANCELED); } } else { // Requests arriving here are in-dialog requests. switch(request.method) { case JsSIP.C.ACK: if(this.status === C.STATUS_WAITING_FOR_ACK) { window.clearTimeout(this.timers.ackTimer); window.clearTimeout(this.timers.invite2xxTimer); this.status = C.STATUS_CONFIRMED; } break; case JsSIP.C.BYE: if(this.status === C.STATUS_CONFIRMED) { request.reply(200); this.ended('remote', request, JsSIP.C.causes.BYE); } break; case JsSIP.C.INVITE: if(this.status === C.STATUS_CONFIRMED) { this.logger.log('re-INVITE received'); this.receiveReinvite(request); } break; case JsSIP.C.INFO: if(this.status === C.STATUS_CONFIRMED || this.status === C.STATUS_WAITING_FOR_ACK) { contentType = request.getHeader('content-type'); if (contentType && (contentType.match(/^application\/dtmf-relay/i))) { new DTMF(this).init_incoming(request); } } } } }; /** * Initial Request Sender * @private */ RTCSession.prototype.sendInitialRequest = function(mediaConstraints, RTCOfferConstraints, mediaStream) { var self = this, request_sender = new JsSIP.RequestSender(self, this.ua), // User media succeeded userMediaSucceeded = function(stream) { self.rtcMediaHandler.addStream( stream, streamAdditionSucceeded, streamAdditionFailed ); }, // User media failed userMediaFailed = function() { if (self.status === C.STATUS_TERMINATED) { return; } self.failed('local', null, JsSIP.C.causes.USER_DENIED_MEDIA_ACCESS); }, // rtcMediaHandler.addStream successfully added streamAdditionSucceeded = function() { self.connecting(self.request); if (self.status === C.STATUS_TERMINATED) { return; } self.rtcMediaHandler.createOffer( offerCreationSucceeded, offerCreationFailed, RTCOfferConstraints ); }, // rtcMediaHandler.addStream failed streamAdditionFailed = function() { if (self.status === C.STATUS_TERMINATED) { return; } self.failed('system', null, JsSIP.C.causes.WEBRTC_ERROR); }, // rtcMediaHandler.createOffer succeeded offerCreationSucceeded = function(offer) { if (self.isCanceled || self.status === C.STATUS_TERMINATED) { return; } self.request.body = offer; self.status = C.STATUS_INVITE_SENT; request_sender.send(); }, // rtcMediaHandler.createOffer failed offerCreationFailed = function() { if (self.status === C.STATUS_TERMINATED) { return; } self.failed('system', null, JsSIP.C.causes.WEBRTC_ERROR); }; this.receiveResponse = this.receiveInviteResponse; if (mediaStream) { userMediaSucceeded(mediaStream); } else { this.rtcMediaHandler.getUserMedia( userMediaSucceeded, userMediaFailed, mediaConstraints ); } }; /** * Send Re-INVITE * @private */ RTCSession.prototype.sendReinvite = function(options) { options = options || {}; var self = this, extraHeaders = options.extraHeaders || [], eventHandlers = options.eventHandlers || {}, mangle = options.mangle || null; if (eventHandlers.succeeded) { this.reinviteSucceeded = eventHandlers.succeeded; } else { this.reinviteSucceeded = function(){}; } if (eventHandlers.failed) { this.reinviteFailed = eventHandlers.failed; } else { this.reinviteFailed = function(){}; } extraHeaders.push('Contact: ' + this.contact); extraHeaders.push('Allow: '+ JsSIP.Utils.getAllowedMethods(this.ua)); extraHeaders.push('Content-Type: application/sdp'); this.receiveResponse = this.receiveReinviteResponse; this.rtcMediaHandler.createOffer( function(body){ if (mangle) { body = mangle(body); } self.dialog.sendRequest(self, JsSIP.C.INVITE, { extraHeaders: extraHeaders, body: body }); }, function() { if (self.isReadyToReinvite()) { self.onReadyToReinvite(); } self.reinviteFailed(); } ); }; /** * Reception of Response for Initial INVITE * @private */ RTCSession.prototype.receiveInviteResponse = function(response) { var cause, dialog, session = this; // Handle 2XX retransmissions and responses from forked requests if (this.dialog && (response.status_code >=200 && response.status_code <=299)) { /* * If it is a retransmission from the endpoint that established * the dialog, send an ACK */ if (this.dialog.id.call_id === response.call_id && this.dialog.id.local_tag === response.from_tag && this.dialog.id.remote_tag === response.to_tag) { this.sendRequest(JsSIP.C.ACK); return; } // If not, send an ACK and terminate else { dialog = new JsSIP.Dialog(this, response, 'UAC'); if (dialog.error !== undefined) { this.logger.error(dialog.error); return; } dialog.sendRequest({ owner: {status: C.STATUS_TERMINATED}, onRequestTimeout: function(){}, onTransportError: function(){}, onDialogError: function(){}, receiveResponse: function(){} }, JsSIP.C.ACK); dialog.sendRequest({ owner: {status: C.STATUS_TERMINATED}, onRequestTimeout: function(){}, onTransportError: function(){}, onDialogError: function(){}, receiveResponse: function(){} }, JsSIP.C.BYE); return; } } else if(this.status !== C.STATUS_INVITE_SENT && this.status !== C.STATUS_1XX_RECEIVED) { return; } // Proceed to cancellation if the user requested. if(this.isCanceled) { if(response.status_code >= 100 && response.status_code < 200) { this.request.cancel(this.cancelReason); } else if(response.status_code >= 200 && response.status_code < 299) { this.acceptAndTerminate(response); } return; } switch(true) { case /^100$/.test(response.status_code): this.received_100 = true; break; case /^1[0-9]{2}$/.test(response.status_code): if(this.status !== C.STATUS_INVITE_SENT && this.status !== C.STATUS_1XX_RECEIVED) { break; } // Do nothing with 1xx responses without To tag. if(!response.to_tag) { this.logger.warn('1xx response received without to tag'); break; } // Create Early Dialog if 1XX comes with contact if(response.hasHeader('contact')) { // An error on dialog creation will fire 'failed' event if(!this.createDialog(response, 'UAC', true)) { break; } } if (!response.body) { session.status = C.STATUS_1XX_RECEIVED; session.progress('remote', response); break; } this.rtcMediaHandler.onMessage( 'pranswer', response.body, /* * OnSuccess. * SDP Answer fits with Offer. */ function() { session.status = C.STATUS_1XX_RECEIVED; session.progress('remote', response); }, /* * OnFailure. * SDP Answer does not fit with Offer. */ function(e) { session.logger.warn(e); this.earlyDialogs[response.call_id + response.from_tag + response.to_tag].terminate(); } ); break; case /^2[0-9]{2}$/.test(response.status_code): if(!response.body) { this.acceptAndTerminate(response, 400, 'Missing session description'); this.failed('remote', response, JsSIP.C.causes.BAD_MEDIA_DESCRIPTION); break; } // An error on dialog creation will fire 'failed' event if (!this.createDialog(response, 'UAC')) { break; } this.rtcMediaHandler.onMessage( 'answer', response.body, /* * onSuccess * SDP Answer fits with Offer. Media will start */ function() { session.status = C.STATUS_CONFIRMED; session.sendRequest(JsSIP.C.ACK); session.started('remote', response); }, /* * onFailure * SDP Answer does not fit the Offer. Accept the call and Terminate. */ function(e) { session.logger.warn(e); session.acceptAndTerminate(response, 488, 'Not Acceptable Here'); session.failed('remote', response, JsSIP.C.causes.BAD_MEDIA_DESCRIPTION); } ); break; default: cause = JsSIP.Utils.sipErrorCause(response.status_code); this.failed('remote', response, cause); } }; /** * Reception of Response for in-dialog INVITE * @private */ RTCSession.prototype.receiveReinviteResponse = function(response) { var self = this, contentType = response.getHeader('Content-Type'); if (this.status === C.STATUS_TERMINATED) { return; } switch(true) { case /^1[0-9]{2}$/.test(response.status_code): break; case /^2[0-9]{2}$/.test(response.status_code): this.status = C.STATUS_CONFIRMED; this.sendRequest(JsSIP.C.ACK); if(!response.body) { this.reinviteFailed(); break; } else if (contentType !== 'application/sdp') { this.reinviteFailed(); break; } this.rtcMediaHandler.onMessage( 'answer', response.body, /* * onSuccess * SDP Answer fits with Offer. */ function() { self.reinviteSucceeded(); }, /* * onFailure * SDP Answer does not fit the Offer. */ function() { self.reinviteFailed(); } ); break; default: this.reinviteFailed(); } }; /** * @private */ RTCSession.prototype.acceptAndTerminate = function(response, status_code, reason_phrase) { var extraHeaders = []; if (status_code) { reason_phrase = reason_phrase || JsSIP.C.REASON_PHRASE[status_code] || ''; extraHeaders.push('Reason: SIP ;cause=' + status_code + '; text="' + reason_phrase + '"'); } // An error on dialog creation will fire 'failed' event if (this.dialog || this.createDialog(response, 'UAC')) { this.sendRequest(JsSIP.C.ACK); this.sendRequest(JsSIP.C.BYE, { extraHeaders: extraHeaders }); } }; /* * @private */ RTCSession.prototype.toogleMuteAudio = function(mute) { var streamIdx, trackIdx, tracks, localStreams = this.getLocalStreams(); for (streamIdx in localStreams) { tracks = localStreams[streamIdx].getAudioTracks(); for (trackIdx in tracks) { tracks[trackIdx].enabled = !mute; } } }; /* * @private */ RTCSession.prototype.toogleMuteVideo = function(mute) { var streamIdx, trackIdx, tracks, localStreams = this.getLocalStreams(); for (streamIdx in localStreams) { tracks = localStreams[streamIdx].getVideoTracks(); for (trackIdx in tracks) { tracks[trackIdx].enabled = !mute; } } }; /** * Session Callbacks */ /** * @private */ RTCSession.prototype.onTransportError = function() { if(this.status !== C.STATUS_TERMINATED) { if (this.status === C.STATUS_CONFIRMED) { this.ended('system', null, JsSIP.C.causes.CONNECTION_ERROR); } else { this.failed('system', null, JsSIP.C.causes.CONNECTION_ERROR); } } }; /** * @private */ RTCSession.prototype.onRequestTimeout = function() { if(this.status !== C.STATUS_TERMINATED) { if (this.status === C.STATUS_CONFIRMED) { this.ended('system', null, JsSIP.C.causes.REQUEST_TIMEOUT); } else { this.failed('system', null, JsSIP.C.causes.REQUEST_TIMEOUT); } } }; /** * @private */ RTCSession.prototype.onDialogError = function(response) { if(this.status !== C.STATUS_TERMINATED) { if (this.status === C.STATUS_CONFIRMED) { this.ended('remote', response, JsSIP.C.causes.DIALOG_ERROR); } else { this.failed('remote', response, JsSIP.C.causes.DIALOG_ERROR); } } }; /** * Internal Callbacks */ /** * @private */ RTCSession.prototype.newRTCSession = function(originator, request) { var session = this, event_name = 'newRTCSession'; if (originator === 'remote') { session.direction = 'incoming'; session.local_identity = request.to; session.remote_identity = request.from; } else if (originator === 'local'){ session.direction = 'outgoing'; session.local_identity = request.from; session.remote_identity = request.to; } session.ua.emit(event_name, session.ua, { originator: originator, session: session, request: request }); }; /** * @private */ RTCSession.prototype.connecting = function(request) { var session = this, event_name = 'connecting'; session.emit(event_name, session, { request: request }); }; /** * @private */ RTCSession.prototype.progress = function(originator, response) { var session = this, event_name = 'progress'; session.emit(event_name, session, { originator: originator, response: response || null }); }; /** * @private */ RTCSession.prototype.started = function(originator, message) { var session = this, event_name = 'started'; session.start_time = new Date(); session.emit(event_name, session, { originator: originator, response: message || null }); }; /** * @private */ RTCSession.prototype.ended = function(originator, message, cause) { var session = this, event_name = 'ended'; session.end_time = new Date(); session.close(); session.emit(event_name, session, { originator: originator, message: message || null, cause: cause }); }; /** * @private */ RTCSession.prototype.failed = function(originator, message, cause) { var session = this, event_name = 'failed'; session.close(); session.emit(event_name, session, { originator: originator, message: message || null, cause: cause }); }; /** * @private */ RTCSession.prototype.onhold = function(originator) { if (originator === 'local') { this.local_hold = true; } else { this.remote_hold = true; } this.emit('hold', this, { originator: originator }); }; /** * @private */ RTCSession.prototype.onunhold = function(originator) { if (originator === 'local') { this.local_hold = false; } else { this.remote_hold = false; } this.emit('unhold', this, { originator: originator }); }; /* * @private */ RTCSession.prototype.onmute = function(options) { this.emit('muted', this, { audio: options.audio, video: options.video }); }; /* * @private */ RTCSession.prototype.onunmute = function(options) { this.emit('unmuted', this, { audio: options.audio, video: options.video }); }; /* * @private */ RTCSession.prototype.onReadyToReinvite = function() { var action = (this.pending_actions.length() > 0)? this.pending_actions.shift() : null; if (!action) { return; } if (action.name === 'hold') { this.hold(); } else if (action.name === 'unhold') { this.unhold(); } }; RTCSession.C = C; JsSIP.RTCSession = RTCSession; }(JsSIP)); /** * @fileoverview Message */ /** * @augments JsSIP * @class Class creating SIP MESSAGE request. * @param {JsSIP.UA} ua */ (function(JsSIP) { var Message; Message = function(ua) { this.ua = ua; this.logger = ua.getLogger('jssip.message'); // Custom message empty object for high level use this.data = {}; }; Message.prototype = new JsSIP.EventEmitter(); Message.prototype.send = function(target, body, options) { var request_sender, event, contentType, eventHandlers, extraHeaders, events = [ 'succeeded', 'failed' ], originalTarget = target; if (target === undefined || body === undefined) { throw new TypeError('Not enough arguments'); } // Check target validity target = this.ua.normalizeTarget(target); if (!target) { throw new TypeError('Invalid target: '+ originalTarget); } this.initEvents(events); // Get call options options = options || {}; extraHeaders = options.extraHeaders && options.extraHeaders.slice() || []; eventHandlers = options.eventHandlers || {}; contentType = options.contentType || 'text/plain'; this.content_type = contentType; // Set event handlers for (event in eventHandlers) { this.on(event, eventHandlers[event]); } this.closed = false; this.ua.applicants[this] = this; extraHeaders.push('Content-Type: '+ contentType); this.request = new JsSIP.OutgoingRequest(JsSIP.C.MESSAGE, target, this.ua, null, extraHeaders); if(body) { this.request.body = body; this.content = body; } else { this.content = null; } request_sender = new JsSIP.RequestSender(this, this.ua); this.newMessage('local', this.request); request_sender.send(); }; /** * @private */ Message.prototype.receiveResponse = function(response) { var cause; if(this.closed) { return; } switch(true) { case /^1[0-9]{2}$/.test(response.status_code): // Ignore provisional responses. break; case /^2[0-9]{2}$/.test(response.status_code): delete this.ua.applicants[this]; this.emit('succeeded', this, { originator: 'remote', response: response }); break; default: delete this.ua.applicants[this]; cause = JsSIP.Utils.sipErrorCause(response.status_code); this.emit('failed', this, { originator: 'remote', response: response, cause: cause }); break; } }; /** * @private */ Message.prototype.onRequestTimeout = function() { if(this.closed) { return; } this.emit('failed', this, { originator: 'system', cause: JsSIP.C.causes.REQUEST_TIMEOUT }); }; /** * @private */ Message.prototype.onTransportError = function() { if(this.closed) { return; } this.emit('failed', this, { originator: 'system', cause: JsSIP.C.causes.CONNECTION_ERROR }); }; /** * @private */ Message.prototype.close = function() { this.closed = true; delete this.ua.applicants[this]; }; /** * @private */ Message.prototype.init_incoming = function(request) { var transaction; this.request = request; this.content_type = request.getHeader('Content-Type'); if (request.body) { this.content = request.body; } else { this.content = null; } this.newMessage('remote', request); transaction = this.ua.transactions.nist[request.via_branch]; if (transaction && (transaction.state === JsSIP.Transactions.C.STATUS_TRYING || transaction.state === JsSIP.Transactions.C.STATUS_PROCEEDING)) { request.reply(200); } }; /** * Accept the incoming Message * Only valid for incoming Messages */ Message.prototype.accept = function(options) { options = options || {}; var extraHeaders = options.extraHeaders && options.extraHeaders.slice() || [], body = options.body; if (this.direction !== 'incoming') { throw new JsSIP.Exceptions.NotSupportedError('"accept" not supported for outgoing Message'); } this.request.reply(200, null, extraHeaders, body); }; /** * Reject the incoming Message * Only valid for incoming Messages * * @param {Number} status_code * @param {String} [reason_phrase] */ Message.prototype.reject = function(options) { options = options || {}; var status_code = options.status_code || 480, reason_phrase = options.reason_phrase, extraHeaders = options.extraHeaders && options.extraHeaders.slice() || [], body = options.body; if (this.direction !== 'incoming') { throw new JsSIP.Exceptions.NotSupportedError('"reject" not supported for outgoing Message'); } if (status_code < 300 || status_code >= 700) { throw new TypeError('Invalid status_code: '+ status_code); } this.request.reply(status_code, reason_phrase, extraHeaders, body); }; /** * Internal Callbacks */ /** * @private */ Message.prototype.newMessage = function(originator, request) { var message = this, event_name = 'newMessage'; if (originator === 'remote') { message.direction = 'incoming'; message.local_identity = request.to; message.remote_identity = request.from; } else if (originator === 'local'){ message.direction = 'outgoing'; message.local_identity = request.from; message.remote_identity = request.to; } message.ua.emit(event_name, message.ua, { originator: originator, message: message, request: request }); }; JsSIP.Message = Message; }(JsSIP)); /** * @fileoverview SIP User Agent */ /** * @augments JsSIP * @class Class creating a SIP User Agent. */ (function(JsSIP) { var UA, C = { // UA status codes STATUS_INIT : 0, STATUS_READY: 1, STATUS_USER_CLOSED: 2, STATUS_NOT_READY: 3, // UA error codes CONFIGURATION_ERROR: 1, NETWORK_ERROR: 2, /* UA events and corresponding SIP Methods. * Dynamically added to 'Allow' header field if the * corresponding event handler is set. */ EVENT_METHODS: { 'newRTCSession': 'INVITE', 'newMessage': 'MESSAGE' }, ALLOWED_METHODS: [ 'ACK', 'CANCEL', 'BYE', 'OPTIONS' ], ACCEPTED_BODY_TYPES: [ 'application/sdp', 'application/dtmf-relay' ], MAX_FORWARDS: 69, TAG_LENGTH: 10 }; UA = function(configuration) { var events = [ 'connecting', 'connected', 'disconnected', 'newTransaction', 'transactionDestroyed', 'registered', 'unregistered', 'registrationFailed', 'newRTCSession', 'newMessage' ]; // Set Accepted Body Types C.ACCEPTED_BODY_TYPES = C.ACCEPTED_BODY_TYPES.toString(); this.log = new JsSIP.LoggerFactory(); this.logger = this.getLogger('jssip.ua'); this.cache = { credentials: {} }; this.configuration = {}; this.dialogs = {}; //User actions outside any session/dialog (MESSAGE) this.applicants = {}; this.sessions = {}; this.transport = null; this.contact = null; this.status = C.STATUS_INIT; this.error = null; this.transactions = { nist: {}, nict: {}, ist: {}, ict: {} }; this.transportRecoverAttempts = 0; this.transportRecoveryTimer = null; Object.defineProperties(this, { transactionsCount: { get: function() { var type, transactions = ['nist','nict','ist','ict'], count = 0; for (type in transactions) { count += Object.keys(this.transactions[transactions[type]]).length; } return count; } }, nictTransactionsCount: { get: function() { return Object.keys(this.transactions['nict']).length; } }, nistTransactionsCount: { get: function() { return Object.keys(this.transactions['nist']).length; } }, ictTransactionsCount: { get: function() { return Object.keys(this.transactions['ict']).length; } }, istTransactionsCount: { get: function() { return Object.keys(this.transactions['ist']).length; } } }); /** * Load configuration * * @throws {JsSIP.Exceptions.ConfigurationError} * @throws {TypeError} */ if(configuration === undefined) { throw new TypeError('Not enough arguments'); } // Apply log configuration if present if (configuration.log) { if (configuration.log.hasOwnProperty('builtinEnabled')) { this.log.builtinEnabled = configuration.log.builtinEnabled; } if (configuration.log.hasOwnProperty('level')) { this.log.level = configuration.log.level; } if (configuration.log.hasOwnProperty('connector')) { this.log.connector = configuration.log.connector; } } try { this.loadConfig(configuration); this.initEvents(events); } catch(e) { this.status = C.STATUS_NOT_READY; this.error = C.CONFIGURATION_ERROR; throw e; } // Initialize registrator this.registrator = new JsSIP.Registrator(this); }; UA.prototype = new JsSIP.EventEmitter(); //================= // High Level API //================= /** * Register. * * */ UA.prototype.register = function(options) { this.configuration.register = true; this.registrator.register(options); }; /** * Unregister. * * @param {Boolean} [all] unregister all user bindings. * */ UA.prototype.unregister = function(options) { this.configuration.register = false; this.registrator.unregister(options); }; /** * Registration state. * @param {Boolean} */ UA.prototype.isRegistered = function() { if(this.registrator.registered) { return true; } else { return false; } }; /** * Connection state. * @param {Boolean} */ UA.prototype.isConnected = function() { if(this.transport) { return this.transport.connected; } else { return false; } }; /** * Make an outgoing call. * * @param {String} target * @param {Object} views * @param {Object} [options] * * @throws {TypeError} * */ UA.prototype.call = function(target, options) { var session; session = new JsSIP.RTCSession(this); session.connect(target, options); }; /** * Send a message. * * @param {String} target * @param {String} body * @param {Object} [options] * * @throws {TypeError} * */ UA.prototype.sendMessage = function(target, body, options) { var message; message = new JsSIP.Message(this); message.send(target, body, options); }; /** * Gracefully close. * */ UA.prototype.stop = function() { var session, applicant, ua = this; function transactionsListener() { if (ua.nistTransactionsCount === 0 && ua.nictTransactionsCount === 0) { ua.removeListener('transactionDestroyed', transactionsListener); ua.transport.disconnect(); } } this.logger.log('user requested closure...'); if(this.status === C.STATUS_USER_CLOSED) { this.logger.warn('UA already closed'); return; } // Clear transportRecoveryTimer window.clearTimeout(this.transportRecoveryTimer); // Close registrator this.logger.log('closing registrator'); this.registrator.close(); // Run _terminate_ on every Session for(session in this.sessions) { this.logger.log('closing session ' + session); this.sessions[session].terminate(); } // Run _close_ on every applicant for(applicant in this.applicants) { this.applicants[applicant].close(); } this.status = C.STATUS_USER_CLOSED; /* * If the remaining transactions are all INVITE transactions, there is no need to * wait anymore because every session has already been closed by this method. * - locally originated sessions where terminated (CANCEL or BYE) * - remotely originated sessions where rejected (4XX) or terminated (BYE) * Remaining INVITE transactions belong tho sessions that where answered. This are in * 'accepted' state due to timers 'L' and 'M' defined in [RFC 6026] */ if (this.nistTransactionsCount === 0 && this.nictTransactionsCount === 0) { this.transport.disconnect(); } else { this.on('transactionDestroyed', transactionsListener); } }; /** * Connect to the WS server if status = STATUS_INIT. * Resume UA after being closed. * */ UA.prototype.start = function() { var server; this.logger.log('user requested startup...'); if (this.status === C.STATUS_INIT) { server = this.getNextWsServer(); new JsSIP.Transport(this, server); } else if(this.status === C.STATUS_USER_CLOSED) { this.logger.log('resuming'); this.status = C.STATUS_READY; this.transport.connect(); } else if (this.status === C.STATUS_READY) { this.logger.log('UA is in READY status, not resuming'); } else { this.logger.error('Connection is down. Auto-Recovery system is trying to connect'); } }; /** * Normalice a string into a valid SIP request URI * * @param {String} target * * @returns {JsSIP.URI|undefined} */ UA.prototype.normalizeTarget = function(target) { return JsSIP.Utils.normalizeTarget(target, this.configuration.hostport_params); }; //=============================== // Private (For internal use) //=============================== UA.prototype.saveCredentials = function(credentials) { this.cache.credentials[credentials.realm] = this.cache.credentials[credentials.realm] || {}; this.cache.credentials[credentials.realm][credentials.uri] = credentials; }; UA.prototype.getCredentials = function(request) { var realm, credentials; realm = request.ruri.host; if (this.cache.credentials[realm] && this.cache.credentials[realm][request.ruri]) { credentials = this.cache.credentials[realm][request.ruri]; credentials.method = request.method; } return credentials; }; UA.prototype.getLogger = function(category, label) { return this.log.getLogger(category, label); }; //========================== // Event Handlers //========================== /** * Transport Close event. * @private * @event * @param {JsSIP.Transport} transport. */ UA.prototype.onTransportClosed = function(transport) { // Run _onTransportError_ callback on every client transaction using _transport_ var type, idx, length, client_transactions = ['nict', 'ict', 'nist', 'ist']; transport.server.status = JsSIP.Transport.C.STATUS_DISCONNECTED; this.logger.log('connection state set to '+ JsSIP.Transport.C.STATUS_DISCONNECTED); length = client_transactions.length; for (type = 0; type < length; type++) { for(idx in this.transactions[client_transactions[type]]) { this.transactions[client_transactions[type]][idx].onTransportError(); } } // Close sessions if GRUU is not being used if (!this.contact.pub_gruu) { this.closeSessionsOnTransportError(); } }; /** * Unrecoverable transport event. * Connection reattempt logic has been done and didn't success. * @private * @event * @param {JsSIP.Transport} transport. */ UA.prototype.onTransportError = function(transport) { var server; this.logger.log('transport ' + transport.server.ws_uri + ' failed | connection state set to '+ JsSIP.Transport.C.STATUS_ERROR); // Close sessions. //Mark this transport as 'down' and try the next one transport.server.status = JsSIP.Transport.C.STATUS_ERROR; this.emit('disconnected', this, { transport: transport, code: transport.lastTransportError.code, reason: transport.lastTransportError.reason }); server = this.getNextWsServer(); if(server) { new JsSIP.Transport(this, server); }else { this.closeSessionsOnTransportError(); if (!this.error || this.error !== C.NETWORK_ERROR) { this.status = C.STATUS_NOT_READY; this.error = C.NETWORK_ERROR; } // Transport Recovery process this.recoverTransport(); } }; /** * Transport connection event. * @private * @event * @param {JsSIP.Transport} transport. */ UA.prototype.onTransportConnected = function(transport) { this.transport = transport; // Reset transport recovery counter this.transportRecoverAttempts = 0; transport.server.status = JsSIP.Transport.C.STATUS_READY; this.logger.log('connection state set to '+ JsSIP.Transport.C.STATUS_READY); if(this.status === C.STATUS_USER_CLOSED) { return; } this.status = C.STATUS_READY; this.error = null; if(this.configuration.register) { this.registrator.onTransportConnected(); } this.emit('connected', this, { transport: transport }); }; /** * Transport connecting event * @private * @param {JsSIP.Transport} transport. * @param {Integer} attempts. */ UA.prototype.onTransportConnecting = function(transport, attempts) { this.emit('connecting', this, { transport: transport, attempts: attempts }); }; /** * new Transaction * @private * @param {JsSIP.Transaction} transaction. */ UA.prototype.newTransaction = function(transaction) { this.transactions[transaction.type][transaction.id] = transaction; this.emit('newTransaction', this, { transaction: transaction }); }; /** * new Transaction * @private * @param {JsSIP.Transaction} transaction. */ UA.prototype.destroyTransaction = function(transaction) { delete this.transactions[transaction.type][transaction.id]; this.emit('transactionDestroyed', this, { transaction: transaction }); }; //========================= // receiveRequest //========================= /** * Request reception * @private * @param {JsSIP.IncomingRequest} request. */ UA.prototype.receiveRequest = function(request) { var dialog, session, message, method = request.method; // Check that request URI points to us if(request.ruri.user !== this.configuration.uri.user && request.ruri.user !== this.contact.uri.user) { this.logger.warn('Request-URI does not point to us'); if (request.method !== JsSIP.C.ACK) { request.reply_sl(404); } return; } // Check request URI scheme if(request.ruri.scheme === JsSIP.C.SIPS) { request.reply_sl(416); return; } // Check transaction if(JsSIP.Transactions.checkTransaction(this, request)) { return; } // Create the server transaction if(method === JsSIP.C.INVITE) { new JsSIP.Transactions.InviteServerTransaction(request, this); } else if(method !== JsSIP.C.ACK) { new JsSIP.Transactions.NonInviteServerTransaction(request, this); } /* RFC3261 12.2.2 * Requests that do not change in any way the state of a dialog may be * received within a dialog (for example, an OPTIONS request). * They are processed as if they had been received outside the dialog. */ if(method === JsSIP.C.OPTIONS) { request.reply(200); } else if (method === JsSIP.C.MESSAGE) { if (!this.checkEvent('newMessage') || this.listeners('newMessage').length === 0) { request.reply(405); return; } message = new JsSIP.Message(this); message.init_incoming(request); } else if (method === JsSIP.C.INVITE) { if (!this.checkEvent('newRTCSession') || this.listeners('newRTCSession').length === 0) { request.reply(405); return; } } // Initial Request if(!request.to_tag) { switch(method) { case JsSIP.C.INVITE: if(JsSIP.WebRTC.isSupported) { session = new JsSIP.RTCSession(this); session.init_incoming(request); } else { this.logger.warn('INVITE received but WebRTC is not supported'); request.reply(488); } break; case JsSIP.C.BYE: // Out of dialog BYE received request.reply(481); break; case JsSIP.C.CANCEL: session = this.findSession(request); if(session) { session.receiveRequest(request); } else { this.logger.warn('received CANCEL request for a non existent session'); } break; case JsSIP.C.ACK: /* Absorb it. * ACK request without a corresponding Invite Transaction * and without To tag. */ break; default: request.reply(405); break; } } // In-dialog request else { dialog = this.findDialog(request); if(dialog) { dialog.receiveRequest(request); } else if (method === JsSIP.C.NOTIFY) { session = this.findSession(request); if(session) { session.receiveRequest(request); } else { this.logger.warn('received NOTIFY request for a non existent session'); request.reply(481, 'Subscription does not exist'); } } /* RFC3261 12.2.2 * Request with to tag, but no matching dialog found. * Exception: ACK for an Invite request for which a dialog has not * been created. */ else { if(method !== JsSIP.C.ACK) { request.reply(481); } } } }; //================= // Utils //================= /** * Get the session to which the request belongs to, if any. * @private * @param {JsSIP.IncomingRequest} request. * @returns {JsSIP.OutgoingSession|JsSIP.IncomingSession|null} */ UA.prototype.findSession = function(request) { var sessionIDa = request.call_id + request.from_tag, sessionA = this.sessions[sessionIDa], sessionIDb = request.call_id + request.to_tag, sessionB = this.sessions[sessionIDb]; if(sessionA) { return sessionA; } else if(sessionB) { return sessionB; } else { return null; } }; /** * Get the dialog to which the request belongs to, if any. * @private * @param {JsSIP.IncomingRequest} * @returns {JsSIP.Dialog|null} */ UA.prototype.findDialog = function(request) { var id = request.call_id + request.from_tag + request.to_tag, dialog = this.dialogs[id]; if(dialog) { return dialog; } else { id = request.call_id + request.to_tag + request.from_tag; dialog = this.dialogs[id]; if(dialog) { return dialog; } else { return null; } } }; /** * Retrieve the next server to which connect. * @private * @returns {Object} ws_server */ UA.prototype.getNextWsServer = function() { // Order servers by weight var idx, length, ws_server, candidates = []; length = this.configuration.ws_servers.length; for (idx = 0; idx < length; idx++) { ws_server = this.configuration.ws_servers[idx]; if (ws_server.status === JsSIP.Transport.C.STATUS_ERROR) { continue; } else if (candidates.length === 0) { candidates.push(ws_server); } else if (ws_server.weight > candidates[0].weight) { candidates = [ws_server]; } else if (ws_server.weight === candidates[0].weight) { candidates.push(ws_server); } } idx = Math.floor((Math.random()* candidates.length)); return candidates[idx]; }; /** * Close all sessions on transport error. * @private */ UA.prototype.closeSessionsOnTransportError = function() { var idx; // Run _transportError_ for every Session for(idx in this.sessions) { this.sessions[idx].onTransportError(); } // Call registrator _onTransportClosed_ this.registrator.onTransportClosed(); }; UA.prototype.recoverTransport = function(ua) { var idx, length, k, nextRetry, count, server; ua = ua || this; count = ua.transportRecoverAttempts; length = ua.configuration.ws_servers.length; for (idx = 0; idx < length; idx++) { ua.configuration.ws_servers[idx].status = 0; } server = ua.getNextWsServer(); k = Math.floor((Math.random() * Math.pow(2,count)) +1); nextRetry = k * ua.configuration.connection_recovery_min_interval; if (nextRetry > ua.configuration.connection_recovery_max_interval) { this.logger.log('time for next connection attempt exceeds connection_recovery_max_interval, resetting counter'); nextRetry = ua.configuration.connection_recovery_min_interval; count = 0; } this.logger.log('next connection attempt in '+ nextRetry +' seconds'); this.transportRecoveryTimer = window.setTimeout( function(){ ua.transportRecoverAttempts = count + 1; new JsSIP.Transport(ua, server); }, nextRetry * 1000); }; /** * Configuration load. * @private * returns {Boolean} */ UA.prototype.loadConfig = function(configuration) { // Settings and default values var parameter, value, checked_value, hostport_params, registrar_server, settings = { /* Host address * Value to be set in Via sent_by and host part of Contact FQDN */ via_host: JsSIP.Utils.createRandomToken(12) + '.invalid', // Password password: null, // Registration parameters register_expires: 600, register: true, registrar_server: null, // Transport related parameters ws_server_max_reconnection: 3, ws_server_reconnection_timeout: 4, connection_recovery_min_interval: 2, connection_recovery_max_interval: 30, use_preloaded_route: false, // Session parameters no_answer_timeout: 60, stun_servers: ['stun:stun.l.google.com:19302'], turn_servers: [], // Logging parameters trace_sip: false, // Hacks hack_via_tcp: false, hack_ip_in_contact: false }; // Pre-Configuration // Check Mandatory parameters for(parameter in UA.configuration_check.mandatory) { if(!configuration.hasOwnProperty(parameter)) { throw new JsSIP.Exceptions.ConfigurationError(parameter); } else { value = configuration[parameter]; checked_value = UA.configuration_check.mandatory[parameter](value); if (checked_value !== undefined) { settings[parameter] = checked_value; } else { throw new JsSIP.Exceptions.ConfigurationError(parameter, value); } } } // Check Optional parameters for(parameter in UA.configuration_check.optional) { if(configuration.hasOwnProperty(parameter)) { value = configuration[parameter]; /* If the parameter value is null, empty string, undefined, empty array * or it's a number with NaN value, then apply its default value. */ if (JsSIP.Utils.isEmpty(value)) { continue; } checked_value = UA.configuration_check.optional[parameter](value); if (checked_value !== undefined) { settings[parameter] = checked_value; } else { throw new JsSIP.Exceptions.ConfigurationError(parameter, value); } } } // Sanity Checks // Connection recovery intervals if(settings.connection_recovery_max_interval < settings.connection_recovery_min_interval) { throw new JsSIP.Exceptions.ConfigurationError('connection_recovery_max_interval', settings.connection_recovery_max_interval); } // Post Configuration Process // Allow passing 0 number as display_name. if (settings.display_name === 0) { settings.display_name = '0'; } // Instance-id for GRUU if (!settings.instance_id) { settings.instance_id = JsSIP.Utils.newUUID(); } // jssip_id instance parameter. Static random tag of length 5 settings.jssip_id = JsSIP.Utils.createRandomToken(5); // String containing settings.uri without scheme and user. hostport_params = settings.uri.clone(); hostport_params.user = null; settings.hostport_params = hostport_params.toString().replace(/^sip:/i, ''); /* Check whether authorization_user is explicitly defined. * Take 'settings.uri.user' value if not. */ if (!settings.authorization_user) { settings.authorization_user = settings.uri.user; } /* If no 'registrar_server' is set use the 'uri' value without user portion. */ if (!settings.registrar_server) { registrar_server = settings.uri.clone(); registrar_server.user = null; settings.registrar_server = registrar_server; } // User no_answer_timeout settings.no_answer_timeout = settings.no_answer_timeout * 1000; // Via Host if (settings.hack_ip_in_contact) { settings.via_host = JsSIP.Utils.getRandomTestNetIP(); } // Set empty Stun Server Set if explicitly passed an empty Array value = configuration.stun_servers; if (value instanceof Array && value.length === 0) { settings.stun_servers = []; } this.contact = { pub_gruu: null, temp_gruu: null, uri: new JsSIP.URI('sip', JsSIP.Utils.createRandomToken(8), settings.via_host, null, {transport: 'ws'}), toString: function(options){ options = options || {}; var anonymous = options.anonymous || null, outbound = options.outbound || null, contact = '<'; if (anonymous) { contact += this.temp_gruu || 'sip:anonymous@anonymous.invalid;transport=ws'; } else { contact += this.pub_gruu || this.uri.toString(); } if (outbound && (anonymous ? !this.temp_gruu : !this.pub_gruu)) { contact += ';ob'; } contact += '>'; return contact; } }; // Fill the value of the configuration_skeleton for(parameter in settings) { UA.configuration_skeleton[parameter].value = settings[parameter]; } Object.defineProperties(this.configuration, UA.configuration_skeleton); // Clean UA.configuration_skeleton for(parameter in settings) { UA.configuration_skeleton[parameter].value = ''; } this.logger.log('configuration parameters after validation:'); for(parameter in settings) { switch(parameter) { case 'uri': case 'registrar_server': this.logger.log('· ' + parameter + ': ' + settings[parameter]); break; case 'password': this.logger.log('· ' + parameter + ': ' + 'NOT SHOWN'); break; default: this.logger.log('· ' + parameter + ': ' + window.JSON.stringify(settings[parameter])); } } return; }; /** * Configuration Object skeleton. * @private */ UA.configuration_skeleton = (function() { var idx, parameter, skeleton = {}, parameters = [ // Internal parameters "jssip_id", "ws_server_max_reconnection", "ws_server_reconnection_timeout", "hostport_params", // Mandatory user configurable parameters "uri", "ws_servers", // Optional user configurable parameters "authorization_user", "connection_recovery_max_interval", "connection_recovery_min_interval", "display_name", "hack_via_tcp", // false. "hack_ip_in_contact", //false "instance_id", "no_answer_timeout", // 30 seconds. "password", "register_expires", // 600 seconds. "registrar_server", "stun_servers", "trace_sip", "turn_servers", "use_preloaded_route", // Post-configuration generated parameters "via_core_value", "via_host" ]; for(idx in parameters) { parameter = parameters[idx]; skeleton[parameter] = { value: '', writable: false, configurable: false }; } skeleton['register'] = { value: '', writable: true, configurable: false }; return skeleton; }()); /** * Configuration checker. * @private * @return {Boolean} */ UA.configuration_check = { mandatory: { uri: function(uri) { var parsed; if (!/^sip:/i.test(uri)) { uri = JsSIP.C.SIP + ':' + uri; } parsed = JsSIP.URI.parse(uri); if(!parsed) { return; } else if(!parsed.user) { return; } else { return parsed; } }, ws_servers: function(ws_servers) { var idx, length, url; /* Allow defining ws_servers parameter as: * String: "host" * Array of Strings: ["host1", "host2"] * Array of Objects: [{ws_uri:"host1", weight:1}, {ws_uri:"host2", weight:0}] * Array of Objects and Strings: [{ws_uri:"host1"}, "host2"] */ if (typeof ws_servers === 'string') { ws_servers = [{ws_uri: ws_servers}]; } else if (ws_servers instanceof Array) { length = ws_servers.length; for (idx = 0; idx < length; idx++) { if (typeof ws_servers[idx] === 'string'){ ws_servers[idx] = {ws_uri: ws_servers[idx]}; } } } else { return; } if (ws_servers.length === 0) { return false; } length = ws_servers.length; for (idx = 0; idx < length; idx++) { if (!ws_servers[idx].ws_uri) { this.logger.error('missing "ws_uri" attribute in ws_servers parameter'); return; } if (ws_servers[idx].weight && !Number(ws_servers[idx].weight)) { this.logger.error('"weight" attribute in ws_servers parameter must be a Number'); return; } url = JsSIP.Grammar.parse(ws_servers[idx].ws_uri, 'absoluteURI'); if(url === -1) { this.logger.error('invalid "ws_uri" attribute in ws_servers parameter: ' + ws_servers[idx].ws_uri); return; } else if(url.scheme !== 'wss' && url.scheme !== 'ws') { this.logger.error('invalid URI scheme in ws_servers parameter: ' + url.scheme); return; } else { ws_servers[idx].sip_uri = ''; if (!ws_servers[idx].weight) { ws_servers[idx].weight = 0; } ws_servers[idx].status = 0; ws_servers[idx].scheme = url.scheme.toUpperCase(); } } return ws_servers; } }, optional: { authorization_user: function(authorization_user) { if(JsSIP.Grammar.parse('"'+ authorization_user +'"', 'quoted_string') === -1) { return; } else { return authorization_user; } }, connection_recovery_max_interval: function(connection_recovery_max_interval) { var value; if(JsSIP.Utils.isDecimal(connection_recovery_max_interval)) { value = window.Number(connection_recovery_max_interval); if(value > 0) { return value; } } }, connection_recovery_min_interval: function(connection_recovery_min_interval) { var value; if(JsSIP.Utils.isDecimal(connection_recovery_min_interval)) { value = window.Number(connection_recovery_min_interval); if(value > 0) { return value; } } }, display_name: function(display_name) { if(JsSIP.Grammar.parse('"' + display_name + '"', 'display_name') === -1) { return; } else { return display_name; } }, hack_via_tcp: function(hack_via_tcp) { if (typeof hack_via_tcp === 'boolean') { return hack_via_tcp; } }, hack_ip_in_contact: function(hack_ip_in_contact) { if (typeof hack_ip_in_contact === 'boolean') { return hack_ip_in_contact; } }, instance_id: function(instance_id) { if ((/^uuid:/i.test(instance_id))) { instance_id = instance_id.substr(5); } if(JsSIP.Grammar.parse(instance_id, 'uuid') === -1) { return; } else { return instance_id; } }, no_answer_timeout: function(no_answer_timeout) { var value; if (JsSIP.Utils.isDecimal(no_answer_timeout)) { value = window.Number(no_answer_timeout); if (value > 0) { return value; } } }, password: function(password) { return String(password); }, register: function(register) { if (typeof register === 'boolean') { return register; } }, register_expires: function(register_expires) { var value; if (JsSIP.Utils.isDecimal(register_expires)) { value = window.Number(register_expires); if (value > 0) { return value; } } }, registrar_server: function(registrar_server) { var parsed; if (!/^sip:/i.test(registrar_server)) { registrar_server = JsSIP.C.SIP + ':' + registrar_server; } parsed = JsSIP.URI.parse(registrar_server); if(!parsed) { return; } else if(parsed.user) { return; } else { return parsed; } }, stun_servers: function(stun_servers) { var idx, length, stun_server; if (typeof stun_servers === 'string') { stun_servers = [stun_servers]; } else if (!(stun_servers instanceof Array)) { return; } length = stun_servers.length; for (idx = 0; idx < length; idx++) { stun_server = stun_servers[idx]; if (!(/^stuns?:/.test(stun_server))) { stun_server = 'stun:' + stun_server; } if(JsSIP.Grammar.parse(stun_server, 'stun_URI') === -1) { return; } else { stun_servers[idx] = stun_server; } } return stun_servers; }, trace_sip: function(trace_sip) { if (typeof trace_sip === 'boolean') { return trace_sip; } }, turn_servers: function(turn_servers) { var idx, idx2, length, length2, turn_server, url; if (turn_servers instanceof Array) { // Do nothing } else { turn_servers = [turn_servers]; } length = turn_servers.length; for (idx = 0; idx < length; idx++) { turn_server = turn_servers[idx]; // Backward compatibility: //Allow defining the turn_server 'urls' with the 'server' property. if (turn_server.server) { turn_server.urls = [turn_server.server]; } // Backward compatibility: //Allow defining the turn_server 'credential' with the 'password' property. if (turn_server.password) { turn_server.credential = [turn_server.password]; } if (!turn_server.urls || !turn_server.username || !turn_server.credential) { return; } if (!(turn_server.urls instanceof Array)) { turn_server.urls = [turn_server.urls]; } length2 = turn_server.urls.length; for (idx2 = 0; idx2 < length2; idx2++) { url = turn_server.urls[idx2]; if (!(/^turns?:/.test(url))) { url = 'turn:' + url; } if(JsSIP.Grammar.parse(url, 'turn_URI') === -1) { return; } } } return turn_servers; }, use_preloaded_route: function(use_preloaded_route) { if (typeof use_preloaded_route === 'boolean') { return use_preloaded_route; } } } }; UA.C = C; JsSIP.UA = UA; }(JsSIP)); /** * @fileoverview Utils */ (function(JsSIP) { var Utils; Utils= { str_utf8_length: function(string) { return window.unescape(encodeURIComponent(string)).length; }, isFunction: function(fn) { if (fn !== undefined) { return (Object.prototype.toString.call(fn) === '[object Function]')? true : false; } else { return false; } }, isDecimal: function (num) { return !isNaN(num) && (parseFloat(num) === parseInt(num,10)); }, isEmpty: function(value) { if (value === null || value === "" || value === undefined || (value instanceof Array && value.length === 0) || (typeof(value) === 'number' && window.isNaN(value))) { return true; } }, createRandomToken: function(size, base) { var i, r, token = ''; base = base || 32; for( i=0; i < size; i++ ) { r = Math.random() * base|0; token += r.toString(base); } return token; }, newTag: function() { return JsSIP.Utils.createRandomToken(JsSIP.UA.C.TAG_LENGTH); }, // http://stackoverflow.com/users/109538/broofa newUUID: function() { var UUID = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) { var r = Math.random()*16|0, v = c === 'x' ? r : (r&0x3|0x8); return v.toString(16); }); return UUID; }, hostType: function(host) { if (!host) { return; } else { host = JsSIP.Grammar.parse(host,'host'); if (host !== -1) { return host.host_type; } } }, /** * Normalize SIP URI. * NOTE: It does not allow a SIP URI without username. * Accepts 'sip', 'sips' and 'tel' URIs and convert them into 'sip'. * Detects the domain part (if given) and properly hex-escapes the user portion. * If the user portion has only 'tel' number symbols the user portion is clean of 'tel' visual separators. * @private * @param {String} target * @param {String} [domain] */ normalizeTarget: function(target, domain) { var uri, target_array, target_user, target_domain; // If no target is given then raise an error. if (!target) { return; // If a JsSIP.URI instance is given then return it. } else if (target instanceof JsSIP.URI) { return target; // If a string is given split it by '@': // - Last fragment is the desired domain. // - Otherwise append the given domain argument. } else if (typeof target === 'string') { target_array = target.split('@'); switch(target_array.length) { case 1: if (!domain) { return; } target_user = target; target_domain = domain; break; case 2: target_user = target_array[0]; target_domain = target_array[1]; break; default: target_user = target_array.slice(0, target_array.length-1).join('@'); target_domain = target_array[target_array.length-1]; } // Remove the URI scheme (if present). target_user = target_user.replace(/^(sips?|tel):/i, ''); // Remove 'tel' visual separators if the user portion just contains 'tel' number symbols. if (/^[\-\.\(\)]*\+?[0-9\-\.\(\)]+$/.test(target_user)) { target_user = target_user.replace(/[\-\.\(\)]/g, ''); } // Build the complete SIP URI. target = JsSIP.C.SIP + ':' + JsSIP.Utils.escapeUser(target_user) + '@' + target_domain; // Finally parse the resulting URI. if (uri = JsSIP.URI.parse(target)) { return uri; } else { return; } } else { return; } }, /** * Hex-escape a SIP URI user. * @private * @param {String} user */ escapeUser: function(user) { // Don't hex-escape ':' (%3A), '+' (%2B), '?' (%3F"), '/' (%2F). return window.encodeURIComponent(window.decodeURIComponent(user)).replace(/%3A/ig, ':').replace(/%2B/ig, '+').replace(/%3F/ig, '?').replace(/%2F/ig, '/'); }, headerize: function(string) { var exceptions = { 'Call-Id': 'Call-ID', 'Cseq': 'CSeq', 'Www-Authenticate': 'WWW-Authenticate' }, name = string.toLowerCase().replace(/_/g,'-').split('-'), hname = '', parts = name.length, part; for (part = 0; part < parts; part++) { if (part !== 0) { hname +='-'; } hname += name[part].charAt(0).toUpperCase()+name[part].substring(1); } if (exceptions[hname]) { hname = exceptions[hname]; } return hname; }, sipErrorCause: function(status_code) { var cause; for (cause in JsSIP.C.SIP_ERROR_CAUSES) { if (JsSIP.C.SIP_ERROR_CAUSES[cause].indexOf(status_code) !== -1) { return JsSIP.C.causes[cause]; } } return JsSIP.C.causes.SIP_FAILURE_CODE; }, /** * Generate a random Test-Net IP (http://tools.ietf.org/html/rfc5735) * @private */ getRandomTestNetIP: function() { function getOctet(from,to) { return window.Math.floor(window.Math.random()*(to-from+1)+from); } return '192.0.2.' + getOctet(1, 254); }, getAllowedMethods: function(ua) { var event, allowed = JsSIP.UA.C.ALLOWED_METHODS.toString(); for (event in JsSIP.UA.C.EVENT_METHODS) { if (ua.checkEvent(event) && ua.listeners(event).length > 0) { allowed += ','+ JsSIP.UA.C.EVENT_METHODS[event]; } } return allowed; }, // MD5 (Message-Digest Algorithm) http://www.webtoolkit.info calculateMD5: function(string) { function RotateLeft(lValue, iShiftBits) { return (lValue<>>(32-iShiftBits)); } function AddUnsigned(lX,lY) { var lX4,lY4,lX8,lY8,lResult; lX8 = (lX & 0x80000000); lY8 = (lY & 0x80000000); lX4 = (lX & 0x40000000); lY4 = (lY & 0x40000000); lResult = (lX & 0x3FFFFFFF)+(lY & 0x3FFFFFFF); if (lX4 & lY4) { return (lResult ^ 0x80000000 ^ lX8 ^ lY8); } if (lX4 | lY4) { if (lResult & 0x40000000) { return (lResult ^ 0xC0000000 ^ lX8 ^ lY8); } else { return (lResult ^ 0x40000000 ^ lX8 ^ lY8); } } else { return (lResult ^ lX8 ^ lY8); } } function F(x,y,z) { return (x & y) | ((~x) & z); } function G(x,y,z) { return (x & z) | (y & (~z)); } function H(x,y,z) { return (x ^ y ^ z); } function I(x,y,z) { return (y ^ (x | (~z))); } function FF(a,b,c,d,x,s,ac) { a = AddUnsigned(a, AddUnsigned(AddUnsigned(F(b, c, d), x), ac)); return AddUnsigned(RotateLeft(a, s), b); } function GG(a,b,c,d,x,s,ac) { a = AddUnsigned(a, AddUnsigned(AddUnsigned(G(b, c, d), x), ac)); return AddUnsigned(RotateLeft(a, s), b); } function HH(a,b,c,d,x,s,ac) { a = AddUnsigned(a, AddUnsigned(AddUnsigned(H(b, c, d), x), ac)); return AddUnsigned(RotateLeft(a, s), b); } function II(a,b,c,d,x,s,ac) { a = AddUnsigned(a, AddUnsigned(AddUnsigned(I(b, c, d), x), ac)); return AddUnsigned(RotateLeft(a, s), b); } function ConvertToWordArray(string) { var lWordCount; var lMessageLength = string.length; var lNumberOfWords_temp1=lMessageLength + 8; var lNumberOfWords_temp2=(lNumberOfWords_temp1-(lNumberOfWords_temp1 % 64))/64; var lNumberOfWords = (lNumberOfWords_temp2+1)*16; var lWordArray=Array(lNumberOfWords-1); var lBytePosition = 0; var lByteCount = 0; while ( lByteCount < lMessageLength ) { lWordCount = (lByteCount-(lByteCount % 4))/4; lBytePosition = (lByteCount % 4)*8; lWordArray[lWordCount] = (lWordArray[lWordCount] | (string.charCodeAt(lByteCount)<>>29; return lWordArray; } function WordToHex(lValue) { var WordToHexValue="",WordToHexValue_temp="",lByte,lCount; for (lCount = 0;lCount<=3;lCount++) { lByte = (lValue>>>(lCount*8)) & 255; WordToHexValue_temp = "0" + lByte.toString(16); WordToHexValue = WordToHexValue + WordToHexValue_temp.substr(WordToHexValue_temp.length-2,2); } return WordToHexValue; } function Utf8Encode(string) { string = string.replace(/\r\n/g,"\n"); var utftext = ""; for (var n = 0; n < string.length; n++) { var c = string.charCodeAt(n); if (c < 128) { utftext += String.fromCharCode(c); } else if((c > 127) && (c < 2048)) { utftext += String.fromCharCode((c >> 6) | 192); utftext += String.fromCharCode((c & 63) | 128); } else { utftext += String.fromCharCode((c >> 12) | 224); utftext += String.fromCharCode(((c >> 6) & 63) | 128); utftext += String.fromCharCode((c & 63) | 128); } } return utftext; } var x=[]; var k,AA,BB,CC,DD,a,b,c,d; var S11=7, S12=12, S13=17, S14=22; var S21=5, S22=9 , S23=14, S24=20; var S31=4, S32=11, S33=16, S34=23; var S41=6, S42=10, S43=15, S44=21; string = Utf8Encode(string); x = ConvertToWordArray(string); a = 0x67452301; b = 0xEFCDAB89; c = 0x98BADCFE; d = 0x10325476; for (k=0;k 1) { logger.warn('More than one Via header field present in the response. Dropping the response'); return false; } } function rfc3261_18_1_2() { var via_host = ua.configuration.via_host; if(message.via.host !== via_host) { logger.warn('Via host in the response does not match UA Via host value. Dropping the response'); return false; } } function rfc3261_18_3_response() { var len = JsSIP.Utils.str_utf8_length(message.body), contentLength = message.getHeader('content-length'); if(len < contentLength) { logger.warn('Message body length is lower than the value in Content-Length header field. Dropping the response'); return false; } } // Sanity Check functions for requests and responses function minimumHeaders() { var mandatoryHeaders = ['from', 'to', 'call_id', 'cseq', 'via'], idx = mandatoryHeaders.length; while(idx--) { if(!message.hasHeader(mandatoryHeaders[idx])) { logger.warn('Missing mandatory header field : '+ mandatoryHeaders[idx] +'. Dropping the response'); return false; } } } // Reply function reply(status_code) { var to, response = "SIP/2.0 " + status_code + " " + JsSIP.C.REASON_PHRASE[status_code] + "\r\n", vias = message.getHeaders('via'), length = vias.length, idx = 0; for(idx; idx < length; idx++) { response += "Via: " + vias[idx] + "\r\n"; } to = message.getHeader('To'); if(!message.to_tag) { to += ';tag=' + JsSIP.Utils.newTag(); } response += "To: " + to + "\r\n"; response += "From: " + message.getHeader('From') + "\r\n"; response += "Call-ID: " + message.call_id + "\r\n"; response += "CSeq: " + message.cseq + " " + message.method + "\r\n"; response += "\r\n"; transport.send(response); } requests.push(rfc3261_8_2_2_1); requests.push(rfc3261_16_3_4); requests.push(rfc3261_18_3_request); requests.push(rfc3261_8_2_2_2); responses.push(rfc3261_8_1_3_3); responses.push(rfc3261_18_1_2); responses.push(rfc3261_18_3_response); all.push(minimumHeaders); sanityCheck = function(m, u, t) { var len, pass; message = m; ua = u; transport = t; logger = ua.getLogger('jssip.sanitycheck'); len = all.length; while(len--) { pass = all[len](message); if(pass === false) { return false; } } if(message instanceof JsSIP.IncomingRequest) { len = requests.length; while(len--) { pass = requests[len](message); if(pass === false) { return false; } } } else if(message instanceof JsSIP.IncomingResponse) { len = responses.length; while(len--) { pass = responses[len](message); if(pass === false) { return false; } } } //Everything is OK return true; }; JsSIP.sanityCheck = sanityCheck; }(JsSIP)); /** * @fileoverview SIP Digest Authentication */ /** * SIP Digest Authentication. * @augments JsSIP. * @function Digest Authentication * @param {JsSIP.UA} ua */ (function(JsSIP) { var DigestAuthentication; DigestAuthentication = function(ua) { this.logger = ua.getLogger('jssip.digestauthentication'); this.username = ua.configuration.authorization_user; this.password = ua.configuration.password; this.cnonce = null; this.nc = 0; this.ncHex = '00000000'; this.response = null; }; /** * Performs Digest authentication given a SIP request and the challenge * received in a response to that request. * Returns true if credentials were successfully generated, false otherwise. * * @param {JsSIP.OutgoingRequest} request * @param {Object} challenge */ DigestAuthentication.prototype.authenticate = function(request, challenge) { // Inspect and validate the challenge. this.algorithm = challenge.algorithm; this.realm = challenge.realm; this.nonce = challenge.nonce; this.opaque = challenge.opaque; this.stale = challenge.stale; if (this.algorithm) { if (this.algorithm !== 'MD5') { this.logger.warn('challenge with Digest algorithm different than "MD5", authentication aborted'); return false; } } else { this.algorithm = 'MD5'; } if (! this.realm) { this.logger.warn('challenge without Digest realm, authentication aborted'); return false; } if (! this.nonce) { this.logger.warn('challenge without Digest nonce, authentication aborted'); return false; } // 'qop' can contain a list of values (Array). Let's choose just one. if (challenge.qop) { if (challenge.qop.indexOf('auth') > -1) { this.qop = 'auth'; } else if (challenge.qop.indexOf('auth-int') > -1) { this.qop = 'auth-int'; } else { // Otherwise 'qop' is present but does not contain 'auth' or 'auth-int', so abort here. this.logger.warn('challenge without Digest qop different than "auth" or "auth-int", authentication aborted'); return false; } } else { this.qop = null; } // Fill other attributes. this.method = request.method; this.uri = request.ruri; this.cnonce = JsSIP.Utils.createRandomToken(12); this.nc += 1; this.updateNcHex(); // nc-value = 8LHEX. Max value = 'FFFFFFFF'. if (this.nc === 4294967296) { this.nc = 1; this.ncHex = '00000001'; } // Calculate the Digest "response" value. this.calculateResponse(); return true; }; /** * Generate Digest 'response' value. * @private */ DigestAuthentication.prototype.calculateResponse = function() { var ha1, ha2; // HA1 = MD5(A1) = MD5(username:realm:password) ha1 = JsSIP.Utils.calculateMD5(this.username + ":" + this.realm + ":" + this.password); if (this.qop === 'auth') { // HA2 = MD5(A2) = MD5(method:digestURI) ha2 = JsSIP.Utils.calculateMD5(this.method + ":" + this.uri); // response = MD5(HA1:nonce:nonceCount:credentialsNonce:qop:HA2) this.response = JsSIP.Utils.calculateMD5(ha1 + ":" + this.nonce + ":" + this.ncHex + ":" + this.cnonce + ":auth:" + ha2); } else if (this.qop === 'auth-int') { // HA2 = MD5(A2) = MD5(method:digestURI:MD5(entityBody)) ha2 = JsSIP.Utils.calculateMD5(this.method + ":" + this.uri + ":" + JsSIP.Utils.calculateMD5(this.body ? this.body : "")); // response = MD5(HA1:nonce:nonceCount:credentialsNonce:qop:HA2) this.response = JsSIP.Utils.calculateMD5(ha1 + ":" + this.nonce + ":" + this.ncHex + ":" + this.cnonce + ":auth-int:" + ha2); } else if (this.qop === null) { // HA2 = MD5(A2) = MD5(method:digestURI) ha2 = JsSIP.Utils.calculateMD5(this.method + ":" + this.uri); // response = MD5(HA1:nonce:HA2) this.response = JsSIP.Utils.calculateMD5(ha1 + ":" + this.nonce + ":" + ha2); } }; /** * Return the Proxy-Authorization or WWW-Authorization header value. */ DigestAuthentication.prototype.toString = function() { var auth_params = []; if (! this.response) { throw new Error('response field does not exist, cannot generate Authorization header'); } auth_params.push('algorithm=' + this.algorithm); auth_params.push('username="' + this.username + '"'); auth_params.push('realm="' + this.realm + '"'); auth_params.push('nonce="' + this.nonce + '"'); auth_params.push('uri="' + this.uri + '"'); auth_params.push('response="' + this.response + '"'); if (this.opaque) { auth_params.push('opaque="' + this.opaque + '"'); } if (this.qop) { auth_params.push('qop=' + this.qop); auth_params.push('cnonce="' + this.cnonce + '"'); auth_params.push('nc=' + this.ncHex); } return 'Digest ' + auth_params.join(', '); }; /** * Generate the 'nc' value as required by Digest in this.ncHex by reading this.nc. * @private */ DigestAuthentication.prototype.updateNcHex = function() { var hex = Number(this.nc).toString(16); this.ncHex = '00000000'.substr(0, 8-hex.length) + hex; }; JsSIP.DigestAuthentication = DigestAuthentication; }(JsSIP)); /** * @fileoverview WebRTC */ (function(JsSIP) { var WebRTC; WebRTC = {}; // getUserMedia if (window.navigator.webkitGetUserMedia) { WebRTC.getUserMedia = window.navigator.webkitGetUserMedia.bind(navigator); } else if (window.navigator.mozGetUserMedia) { WebRTC.getUserMedia = window.navigator.mozGetUserMedia.bind(navigator); } else if (window.navigator.getUserMedia) { WebRTC.getUserMedia = window.navigator.getUserMedia.bind(navigator); } // RTCPeerConnection if (window.webkitRTCPeerConnection) { WebRTC.RTCPeerConnection = window.webkitRTCPeerConnection; } else if (window.mozRTCPeerConnection) { WebRTC.RTCPeerConnection = window.mozRTCPeerConnection; } else if (window.RTCPeerConnection) { WebRTC.RTCPeerConnection = window.RTCPeerConnection; } // RTCSessionDescription if (window.webkitRTCSessionDescription) { WebRTC.RTCSessionDescription = window.webkitRTCSessionDescription; } else if (window.mozRTCSessionDescription) { WebRTC.RTCSessionDescription = window.mozRTCSessionDescription; } else if (window.RTCSessionDescription) { WebRTC.RTCSessionDescription = window.RTCSessionDescription; } // New syntax for getting streams in Chrome M26. if (WebRTC.RTCPeerConnection && WebRTC.RTCPeerConnection.prototype) { if (!WebRTC.RTCPeerConnection.prototype.getLocalStreams) { WebRTC.RTCPeerConnection.prototype.getLocalStreams = function() { return this.localStreams; }; WebRTC.RTCPeerConnection.prototype.getRemoteStreams = function() { return this.remoteStreams; }; } } // isSupported attribute. if (WebRTC.getUserMedia && WebRTC.RTCPeerConnection && WebRTC.RTCSessionDescription) { WebRTC.isSupported = true; } else { WebRTC.isSupported = false; } JsSIP.WebRTC = WebRTC; }(JsSIP)); if (typeof module === "object" && module && typeof module.exports === "object") { // Expose JsSIP as module.exports in loaders that implement the Node // module pattern (including browserify). Do not create the global, since // the user will be storing it themselves locally, and globals are frowned // upon in the Node module world. module.exports = JsSIP; } else { // Otherwise expose JsSIP to the global object as usual. window.JsSIP = JsSIP; // Register as a named AMD module, since JsSIP can be concatenated with other // files that may use define, but not via a proper concatenation script that // understands anonymous AMD modules. A named AMD is safest and most robust // way to register. Lowercase jssip is used because AMD module names are // derived from file names, and JsSIP is normally delivered in a lowercase // file name. if (typeof define === "function" && define.amd) { define("jssip", [], function () { return JsSIP; }); } } })(window); JsSIP.Grammar = (function(){ /* * Generated by PEG.js 0.7.0. * * http://pegjs.majda.cz/ */ function quote(s) { /* * ECMA-262, 5th ed., 7.8.4: All characters may appear literally in a * string literal except for the closing quote character, backslash, * carriage return, line separator, paragraph separator, and line feed. * Any character may appear in the form of an escape sequence. * * For portability, we also escape escape all control and non-ASCII * characters. Note that "\0" and "\v" escape sequences are not used * because JSHint does not like the first and IE the second. */ return '"' + s .replace(/\\/g, '\\\\') // backslash .replace(/"/g, '\\"') // closing quote character .replace(/\x08/g, '\\b') // backspace .replace(/\t/g, '\\t') // horizontal tab .replace(/\n/g, '\\n') // line feed .replace(/\f/g, '\\f') // form feed .replace(/\r/g, '\\r') // carriage return .replace(/[\x00-\x07\x0B\x0E-\x1F\x80-\uFFFF]/g, escape) + '"'; } var result = { /* * Parses the input with a generated parser. If the parsing is successfull, * returns a value explicitly or implicitly specified by the grammar from * which the parser was generated (see |PEG.buildParser|). If the parsing is * unsuccessful, throws |PEG.parser.SyntaxError| describing the error. */ parse: function(input, startRule) { var parseFunctions = { "CRLF": parse_CRLF, "DIGIT": parse_DIGIT, "ALPHA": parse_ALPHA, "HEXDIG": parse_HEXDIG, "WSP": parse_WSP, "OCTET": parse_OCTET, "DQUOTE": parse_DQUOTE, "SP": parse_SP, "HTAB": parse_HTAB, "alphanum": parse_alphanum, "reserved": parse_reserved, "unreserved": parse_unreserved, "mark": parse_mark, "escaped": parse_escaped, "LWS": parse_LWS, "SWS": parse_SWS, "HCOLON": parse_HCOLON, "TEXT_UTF8_TRIM": parse_TEXT_UTF8_TRIM, "TEXT_UTF8char": parse_TEXT_UTF8char, "UTF8_NONASCII": parse_UTF8_NONASCII, "UTF8_CONT": parse_UTF8_CONT, "LHEX": parse_LHEX, "token": parse_token, "token_nodot": parse_token_nodot, "separators": parse_separators, "word": parse_word, "STAR": parse_STAR, "SLASH": parse_SLASH, "EQUAL": parse_EQUAL, "LPAREN": parse_LPAREN, "RPAREN": parse_RPAREN, "RAQUOT": parse_RAQUOT, "LAQUOT": parse_LAQUOT, "COMMA": parse_COMMA, "SEMI": parse_SEMI, "COLON": parse_COLON, "LDQUOT": parse_LDQUOT, "RDQUOT": parse_RDQUOT, "comment": parse_comment, "ctext": parse_ctext, "quoted_string": parse_quoted_string, "quoted_string_clean": parse_quoted_string_clean, "qdtext": parse_qdtext, "quoted_pair": parse_quoted_pair, "SIP_URI_noparams": parse_SIP_URI_noparams, "SIP_URI": parse_SIP_URI, "uri_scheme": parse_uri_scheme, "userinfo": parse_userinfo, "user": parse_user, "user_unreserved": parse_user_unreserved, "password": parse_password, "hostport": parse_hostport, "host": parse_host, "hostname": parse_hostname, "domainlabel": parse_domainlabel, "toplabel": parse_toplabel, "IPv6reference": parse_IPv6reference, "IPv6address": parse_IPv6address, "h16": parse_h16, "ls32": parse_ls32, "IPv4address": parse_IPv4address, "dec_octet": parse_dec_octet, "port": parse_port, "uri_parameters": parse_uri_parameters, "uri_parameter": parse_uri_parameter, "transport_param": parse_transport_param, "user_param": parse_user_param, "method_param": parse_method_param, "ttl_param": parse_ttl_param, "maddr_param": parse_maddr_param, "lr_param": parse_lr_param, "other_param": parse_other_param, "pname": parse_pname, "pvalue": parse_pvalue, "paramchar": parse_paramchar, "param_unreserved": parse_param_unreserved, "headers": parse_headers, "header": parse_header, "hname": parse_hname, "hvalue": parse_hvalue, "hnv_unreserved": parse_hnv_unreserved, "Request_Response": parse_Request_Response, "Request_Line": parse_Request_Line, "Request_URI": parse_Request_URI, "absoluteURI": parse_absoluteURI, "hier_part": parse_hier_part, "net_path": parse_net_path, "abs_path": parse_abs_path, "opaque_part": parse_opaque_part, "uric": parse_uric, "uric_no_slash": parse_uric_no_slash, "path_segments": parse_path_segments, "segment": parse_segment, "param": parse_param, "pchar": parse_pchar, "scheme": parse_scheme, "authority": parse_authority, "srvr": parse_srvr, "reg_name": parse_reg_name, "query": parse_query, "SIP_Version": parse_SIP_Version, "INVITEm": parse_INVITEm, "ACKm": parse_ACKm, "OPTIONSm": parse_OPTIONSm, "BYEm": parse_BYEm, "CANCELm": parse_CANCELm, "REGISTERm": parse_REGISTERm, "SUBSCRIBEm": parse_SUBSCRIBEm, "NOTIFYm": parse_NOTIFYm, "Method": parse_Method, "Status_Line": parse_Status_Line, "Status_Code": parse_Status_Code, "extension_code": parse_extension_code, "Reason_Phrase": parse_Reason_Phrase, "Allow_Events": parse_Allow_Events, "Call_ID": parse_Call_ID, "Contact": parse_Contact, "contact_param": parse_contact_param, "name_addr": parse_name_addr, "display_name": parse_display_name, "contact_params": parse_contact_params, "c_p_q": parse_c_p_q, "c_p_expires": parse_c_p_expires, "delta_seconds": parse_delta_seconds, "qvalue": parse_qvalue, "generic_param": parse_generic_param, "gen_value": parse_gen_value, "Content_Disposition": parse_Content_Disposition, "disp_type": parse_disp_type, "disp_param": parse_disp_param, "handling_param": parse_handling_param, "Content_Encoding": parse_Content_Encoding, "Content_Length": parse_Content_Length, "Content_Type": parse_Content_Type, "media_type": parse_media_type, "m_type": parse_m_type, "discrete_type": parse_discrete_type, "composite_type": parse_composite_type, "extension_token": parse_extension_token, "x_token": parse_x_token, "m_subtype": parse_m_subtype, "m_parameter": parse_m_parameter, "m_value": parse_m_value, "CSeq": parse_CSeq, "CSeq_value": parse_CSeq_value, "Expires": parse_Expires, "Event": parse_Event, "event_type": parse_event_type, "From": parse_From, "from_param": parse_from_param, "tag_param": parse_tag_param, "Max_Forwards": parse_Max_Forwards, "Min_Expires": parse_Min_Expires, "Name_Addr_Header": parse_Name_Addr_Header, "Proxy_Authenticate": parse_Proxy_Authenticate, "challenge": parse_challenge, "other_challenge": parse_other_challenge, "auth_param": parse_auth_param, "digest_cln": parse_digest_cln, "realm": parse_realm, "realm_value": parse_realm_value, "domain": parse_domain, "URI": parse_URI, "nonce": parse_nonce, "nonce_value": parse_nonce_value, "opaque": parse_opaque, "stale": parse_stale, "algorithm": parse_algorithm, "qop_options": parse_qop_options, "qop_value": parse_qop_value, "Proxy_Require": parse_Proxy_Require, "Record_Route": parse_Record_Route, "rec_route": parse_rec_route, "Require": parse_Require, "Route": parse_Route, "route_param": parse_route_param, "Subscription_State": parse_Subscription_State, "substate_value": parse_substate_value, "subexp_params": parse_subexp_params, "event_reason_value": parse_event_reason_value, "Subject": parse_Subject, "Supported": parse_Supported, "To": parse_To, "to_param": parse_to_param, "Via": parse_Via, "via_parm": parse_via_parm, "via_params": parse_via_params, "via_ttl": parse_via_ttl, "via_maddr": parse_via_maddr, "via_received": parse_via_received, "via_branch": parse_via_branch, "response_port": parse_response_port, "sent_protocol": parse_sent_protocol, "protocol_name": parse_protocol_name, "transport": parse_transport, "sent_by": parse_sent_by, "via_host": parse_via_host, "via_port": parse_via_port, "ttl": parse_ttl, "WWW_Authenticate": parse_WWW_Authenticate, "extension_header": parse_extension_header, "header_value": parse_header_value, "message_body": parse_message_body, "stun_URI": parse_stun_URI, "stun_scheme": parse_stun_scheme, "stun_host_port": parse_stun_host_port, "stun_host": parse_stun_host, "reg_name": parse_reg_name, "stun_unreserved": parse_stun_unreserved, "sub_delims": parse_sub_delims, "turn_URI": parse_turn_URI, "turn_scheme": parse_turn_scheme, "turn_transport": parse_turn_transport, "uuid_URI": parse_uuid_URI, "uuid": parse_uuid, "hex4": parse_hex4, "hex8": parse_hex8, "hex12": parse_hex12 }; if (startRule !== undefined) { if (parseFunctions[startRule] === undefined) { throw new Error("Invalid rule name: " + quote(startRule) + "."); } } else { startRule = "CRLF"; } var pos = 0; var reportFailures = 0; var rightmostFailuresPos = 0; var rightmostFailuresExpected = []; function padLeft(input, padding, length) { var result = input; var padLength = length - input.length; for (var i = 0; i < padLength; i++) { result = padding + result; } return result; } function escape(ch) { var charCode = ch.charCodeAt(0); var escapeChar; var length; if (charCode <= 0xFF) { escapeChar = 'x'; length = 2; } else { escapeChar = 'u'; length = 4; } return '\\' + escapeChar + padLeft(charCode.toString(16).toUpperCase(), '0', length); } function matchFailed(failure) { if (pos < rightmostFailuresPos) { return; } if (pos > rightmostFailuresPos) { rightmostFailuresPos = pos; rightmostFailuresExpected = []; } rightmostFailuresExpected.push(failure); } function parse_CRLF() { var result0; if (input.substr(pos, 2) === "\r\n") { result0 = "\r\n"; pos += 2; } else { result0 = null; if (reportFailures === 0) { matchFailed("\"\\r\\n\""); } } return result0; } function parse_DIGIT() { var result0; if (/^[0-9]/.test(input.charAt(pos))) { result0 = input.charAt(pos); pos++; } else { result0 = null; if (reportFailures === 0) { matchFailed("[0-9]"); } } return result0; } function parse_ALPHA() { var result0; if (/^[a-zA-Z]/.test(input.charAt(pos))) { result0 = input.charAt(pos); pos++; } else { result0 = null; if (reportFailures === 0) { matchFailed("[a-zA-Z]"); } } return result0; } function parse_HEXDIG() { var result0; if (/^[0-9a-fA-F]/.test(input.charAt(pos))) { result0 = input.charAt(pos); pos++; } else { result0 = null; if (reportFailures === 0) { matchFailed("[0-9a-fA-F]"); } } return result0; } function parse_WSP() { var result0; result0 = parse_SP(); if (result0 === null) { result0 = parse_HTAB(); } return result0; } function parse_OCTET() { var result0; if (/^[\0-\xFF]/.test(input.charAt(pos))) { result0 = input.charAt(pos); pos++; } else { result0 = null; if (reportFailures === 0) { matchFailed("[\\0-\\xFF]"); } } return result0; } function parse_DQUOTE() { var result0; if (/^["]/.test(input.charAt(pos))) { result0 = input.charAt(pos); pos++; } else { result0 = null; if (reportFailures === 0) { matchFailed("[\"]"); } } return result0; } function parse_SP() { var result0; if (input.charCodeAt(pos) === 32) { result0 = " "; pos++; } else { result0 = null; if (reportFailures === 0) { matchFailed("\" \""); } } return result0; } function parse_HTAB() { var result0; if (input.charCodeAt(pos) === 9) { result0 = "\t"; pos++; } else { result0 = null; if (reportFailures === 0) { matchFailed("\"\\t\""); } } return result0; } function parse_alphanum() { var result0; if (/^[a-zA-Z0-9]/.test(input.charAt(pos))) { result0 = input.charAt(pos); pos++; } else { result0 = null; if (reportFailures === 0) { matchFailed("[a-zA-Z0-9]"); } } return result0; } function parse_reserved() { var result0; if (input.charCodeAt(pos) === 59) { result0 = ";"; pos++; } else { result0 = null; if (reportFailures === 0) { matchFailed("\";\""); } } if (result0 === null) { if (input.charCodeAt(pos) === 47) { result0 = "/"; pos++; } else { result0 = null; if (reportFailures === 0) { matchFailed("\"/\""); } } if (result0 === null) { if (input.charCodeAt(pos) === 63) { result0 = "?"; pos++; } else { result0 = null; if (reportFailures === 0) { matchFailed("\"?\""); } } if (result0 === null) { if (input.charCodeAt(pos) === 58) { result0 = ":"; pos++; } else { result0 = null; if (reportFailures === 0) { matchFailed("\":\""); } } if (result0 === null) { if (input.charCodeAt(pos) === 64) { result0 = "@"; pos++; } else { result0 = null; if (reportFailures === 0) { matchFailed("\"@\""); } } if (result0 === null) { if (input.charCodeAt(pos) === 38) { result0 = "&"; pos++; } else { result0 = null; if (reportFailures === 0) { matchFailed("\"&\""); } } if (result0 === null) { if (input.charCodeAt(pos) === 61) { result0 = "="; pos++; } else { result0 = null; if (reportFailures === 0) { matchFailed("\"=\""); } } if (result0 === null) { if (input.charCodeAt(pos) === 43) { result0 = "+"; pos++; } else { result0 = null; if (reportFailures === 0) { matchFailed("\"+\""); } } if (result0 === null) { if (input.charCodeAt(pos) === 36) { result0 = "$"; pos++; } else { result0 = null; if (reportFailures === 0) { matchFailed("\"$\""); } } if (result0 === null) { if (input.charCodeAt(pos) === 44) { result0 = ","; pos++; } else { result0 = null; if (reportFailures === 0) { matchFailed("\",\""); } } } } } } } } } } } return result0; } function parse_unreserved() { var result0; result0 = parse_alphanum(); if (result0 === null) { result0 = parse_mark(); } return result0; } function parse_mark() { var result0; if (input.charCodeAt(pos) === 45) { result0 = "-"; pos++; } else { result0 = null; if (reportFailures === 0) { matchFailed("\"-\""); } } if (result0 === null) { if (input.charCodeAt(pos) === 95) { result0 = "_"; pos++; } else { result0 = null; if (reportFailures === 0) { matchFailed("\"_\""); } } if (result0 === null) { if (input.charCodeAt(pos) === 46) { result0 = "."; pos++; } else { result0 = null; if (reportFailures === 0) { matchFailed("\".\""); } } if (result0 === null) { if (input.charCodeAt(pos) === 33) { result0 = "!"; pos++; } else { result0 = null; if (reportFailures === 0) { matchFailed("\"!\""); } } if (result0 === null) { if (input.charCodeAt(pos) === 126) { result0 = "~"; pos++; } else { result0 = null; if (reportFailures === 0) { matchFailed("\"~\""); } } if (result0 === null) { if (input.charCodeAt(pos) === 42) { result0 = "*"; pos++; } else { result0 = null; if (reportFailures === 0) { matchFailed("\"*\""); } } if (result0 === null) { if (input.charCodeAt(pos) === 39) { result0 = "'"; pos++; } else { result0 = null; if (reportFailures === 0) { matchFailed("\"'\""); } } if (result0 === null) { if (input.charCodeAt(pos) === 40) { result0 = "("; pos++; } else { result0 = null; if (reportFailures === 0) { matchFailed("\"(\""); } } if (result0 === null) { if (input.charCodeAt(pos) === 41) { result0 = ")"; pos++; } else { result0 = null; if (reportFailures === 0) { matchFailed("\")\""); } } } } } } } } } } return result0; } function parse_escaped() { var result0, result1, result2; var pos0, pos1; pos0 = pos; pos1 = pos; if (input.charCodeAt(pos) === 37) { result0 = "%"; pos++; } else { result0 = null; if (reportFailures === 0) { matchFailed("\"%\""); } } if (result0 !== null) { result1 = parse_HEXDIG(); if (result1 !== null) { result2 = parse_HEXDIG(); if (result2 !== null) { result0 = [result0, result1, result2]; } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } if (result0 !== null) { result0 = (function(offset, escaped) {return escaped.join(''); })(pos0, result0); } if (result0 === null) { pos = pos0; } return result0; } function parse_LWS() { var result0, result1, result2; var pos0, pos1, pos2; pos0 = pos; pos1 = pos; pos2 = pos; result0 = []; result1 = parse_WSP(); while (result1 !== null) { result0.push(result1); result1 = parse_WSP(); } if (result0 !== null) { result1 = parse_CRLF(); if (result1 !== null) { result0 = [result0, result1]; } else { result0 = null; pos = pos2; } } else { result0 = null; pos = pos2; } result0 = result0 !== null ? result0 : ""; if (result0 !== null) { result2 = parse_WSP(); if (result2 !== null) { result1 = []; while (result2 !== null) { result1.push(result2); result2 = parse_WSP(); } } else { result1 = null; } if (result1 !== null) { result0 = [result0, result1]; } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } if (result0 !== null) { result0 = (function(offset) {return " "; })(pos0); } if (result0 === null) { pos = pos0; } return result0; } function parse_SWS() { var result0; result0 = parse_LWS(); result0 = result0 !== null ? result0 : ""; return result0; } function parse_HCOLON() { var result0, result1, result2; var pos0, pos1; pos0 = pos; pos1 = pos; result0 = []; result1 = parse_SP(); if (result1 === null) { result1 = parse_HTAB(); } while (result1 !== null) { result0.push(result1); result1 = parse_SP(); if (result1 === null) { result1 = parse_HTAB(); } } if (result0 !== null) { if (input.charCodeAt(pos) === 58) { result1 = ":"; pos++; } else { result1 = null; if (reportFailures === 0) { matchFailed("\":\""); } } if (result1 !== null) { result2 = parse_SWS(); if (result2 !== null) { result0 = [result0, result1, result2]; } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } if (result0 !== null) { result0 = (function(offset) {return ':'; })(pos0); } if (result0 === null) { pos = pos0; } return result0; } function parse_TEXT_UTF8_TRIM() { var result0, result1, result2, result3; var pos0, pos1, pos2; pos0 = pos; pos1 = pos; result1 = parse_TEXT_UTF8char(); if (result1 !== null) { result0 = []; while (result1 !== null) { result0.push(result1); result1 = parse_TEXT_UTF8char(); } } else { result0 = null; } if (result0 !== null) { result1 = []; pos2 = pos; result2 = []; result3 = parse_LWS(); while (result3 !== null) { result2.push(result3); result3 = parse_LWS(); } if (result2 !== null) { result3 = parse_TEXT_UTF8char(); if (result3 !== null) { result2 = [result2, result3]; } else { result2 = null; pos = pos2; } } else { result2 = null; pos = pos2; } while (result2 !== null) { result1.push(result2); pos2 = pos; result2 = []; result3 = parse_LWS(); while (result3 !== null) { result2.push(result3); result3 = parse_LWS(); } if (result2 !== null) { result3 = parse_TEXT_UTF8char(); if (result3 !== null) { result2 = [result2, result3]; } else { result2 = null; pos = pos2; } } else { result2 = null; pos = pos2; } } if (result1 !== null) { result0 = [result0, result1]; } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } if (result0 !== null) { result0 = (function(offset) { return input.substring(pos, offset); })(pos0); } if (result0 === null) { pos = pos0; } return result0; } function parse_TEXT_UTF8char() { var result0; if (/^[!-~]/.test(input.charAt(pos))) { result0 = input.charAt(pos); pos++; } else { result0 = null; if (reportFailures === 0) { matchFailed("[!-~]"); } } if (result0 === null) { result0 = parse_UTF8_NONASCII(); } return result0; } function parse_UTF8_NONASCII() { var result0; if (/^[\x80-\uFFFF]/.test(input.charAt(pos))) { result0 = input.charAt(pos); pos++; } else { result0 = null; if (reportFailures === 0) { matchFailed("[\\x80-\\uFFFF]"); } } return result0; } function parse_UTF8_CONT() { var result0; if (/^[\x80-\xBF]/.test(input.charAt(pos))) { result0 = input.charAt(pos); pos++; } else { result0 = null; if (reportFailures === 0) { matchFailed("[\\x80-\\xBF]"); } } return result0; } function parse_LHEX() { var result0; result0 = parse_DIGIT(); if (result0 === null) { if (/^[a-f]/.test(input.charAt(pos))) { result0 = input.charAt(pos); pos++; } else { result0 = null; if (reportFailures === 0) { matchFailed("[a-f]"); } } } return result0; } function parse_token() { var result0, result1; var pos0; pos0 = pos; result1 = parse_alphanum(); if (result1 === null) { if (input.charCodeAt(pos) === 45) { result1 = "-"; pos++; } else { result1 = null; if (reportFailures === 0) { matchFailed("\"-\""); } } if (result1 === null) { if (input.charCodeAt(pos) === 46) { result1 = "."; pos++; } else { result1 = null; if (reportFailures === 0) { matchFailed("\".\""); } } if (result1 === null) { if (input.charCodeAt(pos) === 33) { result1 = "!"; pos++; } else { result1 = null; if (reportFailures === 0) { matchFailed("\"!\""); } } if (result1 === null) { if (input.charCodeAt(pos) === 37) { result1 = "%"; pos++; } else { result1 = null; if (reportFailures === 0) { matchFailed("\"%\""); } } if (result1 === null) { if (input.charCodeAt(pos) === 42) { result1 = "*"; pos++; } else { result1 = null; if (reportFailures === 0) { matchFailed("\"*\""); } } if (result1 === null) { if (input.charCodeAt(pos) === 95) { result1 = "_"; pos++; } else { result1 = null; if (reportFailures === 0) { matchFailed("\"_\""); } } if (result1 === null) { if (input.charCodeAt(pos) === 43) { result1 = "+"; pos++; } else { result1 = null; if (reportFailures === 0) { matchFailed("\"+\""); } } if (result1 === null) { if (input.charCodeAt(pos) === 96) { result1 = "`"; pos++; } else { result1 = null; if (reportFailures === 0) { matchFailed("\"`\""); } } if (result1 === null) { if (input.charCodeAt(pos) === 39) { result1 = "'"; pos++; } else { result1 = null; if (reportFailures === 0) { matchFailed("\"'\""); } } if (result1 === null) { if (input.charCodeAt(pos) === 126) { result1 = "~"; pos++; } else { result1 = null; if (reportFailures === 0) { matchFailed("\"~\""); } } } } } } } } } } } } if (result1 !== null) { result0 = []; while (result1 !== null) { result0.push(result1); result1 = parse_alphanum(); if (result1 === null) { if (input.charCodeAt(pos) === 45) { result1 = "-"; pos++; } else { result1 = null; if (reportFailures === 0) { matchFailed("\"-\""); } } if (result1 === null) { if (input.charCodeAt(pos) === 46) { result1 = "."; pos++; } else { result1 = null; if (reportFailures === 0) { matchFailed("\".\""); } } if (result1 === null) { if (input.charCodeAt(pos) === 33) { result1 = "!"; pos++; } else { result1 = null; if (reportFailures === 0) { matchFailed("\"!\""); } } if (result1 === null) { if (input.charCodeAt(pos) === 37) { result1 = "%"; pos++; } else { result1 = null; if (reportFailures === 0) { matchFailed("\"%\""); } } if (result1 === null) { if (input.charCodeAt(pos) === 42) { result1 = "*"; pos++; } else { result1 = null; if (reportFailures === 0) { matchFailed("\"*\""); } } if (result1 === null) { if (input.charCodeAt(pos) === 95) { result1 = "_"; pos++; } else { result1 = null; if (reportFailures === 0) { matchFailed("\"_\""); } } if (result1 === null) { if (input.charCodeAt(pos) === 43) { result1 = "+"; pos++; } else { result1 = null; if (reportFailures === 0) { matchFailed("\"+\""); } } if (result1 === null) { if (input.charCodeAt(pos) === 96) { result1 = "`"; pos++; } else { result1 = null; if (reportFailures === 0) { matchFailed("\"`\""); } } if (result1 === null) { if (input.charCodeAt(pos) === 39) { result1 = "'"; pos++; } else { result1 = null; if (reportFailures === 0) { matchFailed("\"'\""); } } if (result1 === null) { if (input.charCodeAt(pos) === 126) { result1 = "~"; pos++; } else { result1 = null; if (reportFailures === 0) { matchFailed("\"~\""); } } } } } } } } } } } } } } else { result0 = null; } if (result0 !== null) { result0 = (function(offset) { return input.substring(pos, offset); })(pos0); } if (result0 === null) { pos = pos0; } return result0; } function parse_token_nodot() { var result0, result1; var pos0; pos0 = pos; result1 = parse_alphanum(); if (result1 === null) { if (input.charCodeAt(pos) === 45) { result1 = "-"; pos++; } else { result1 = null; if (reportFailures === 0) { matchFailed("\"-\""); } } if (result1 === null) { if (input.charCodeAt(pos) === 33) { result1 = "!"; pos++; } else { result1 = null; if (reportFailures === 0) { matchFailed("\"!\""); } } if (result1 === null) { if (input.charCodeAt(pos) === 37) { result1 = "%"; pos++; } else { result1 = null; if (reportFailures === 0) { matchFailed("\"%\""); } } if (result1 === null) { if (input.charCodeAt(pos) === 42) { result1 = "*"; pos++; } else { result1 = null; if (reportFailures === 0) { matchFailed("\"*\""); } } if (result1 === null) { if (input.charCodeAt(pos) === 95) { result1 = "_"; pos++; } else { result1 = null; if (reportFailures === 0) { matchFailed("\"_\""); } } if (result1 === null) { if (input.charCodeAt(pos) === 43) { result1 = "+"; pos++; } else { result1 = null; if (reportFailures === 0) { matchFailed("\"+\""); } } if (result1 === null) { if (input.charCodeAt(pos) === 96) { result1 = "`"; pos++; } else { result1 = null; if (reportFailures === 0) { matchFailed("\"`\""); } } if (result1 === null) { if (input.charCodeAt(pos) === 39) { result1 = "'"; pos++; } else { result1 = null; if (reportFailures === 0) { matchFailed("\"'\""); } } if (result1 === null) { if (input.charCodeAt(pos) === 126) { result1 = "~"; pos++; } else { result1 = null; if (reportFailures === 0) { matchFailed("\"~\""); } } } } } } } } } } } if (result1 !== null) { result0 = []; while (result1 !== null) { result0.push(result1); result1 = parse_alphanum(); if (result1 === null) { if (input.charCodeAt(pos) === 45) { result1 = "-"; pos++; } else { result1 = null; if (reportFailures === 0) { matchFailed("\"-\""); } } if (result1 === null) { if (input.charCodeAt(pos) === 33) { result1 = "!"; pos++; } else { result1 = null; if (reportFailures === 0) { matchFailed("\"!\""); } } if (result1 === null) { if (input.charCodeAt(pos) === 37) { result1 = "%"; pos++; } else { result1 = null; if (reportFailures === 0) { matchFailed("\"%\""); } } if (result1 === null) { if (input.charCodeAt(pos) === 42) { result1 = "*"; pos++; } else { result1 = null; if (reportFailures === 0) { matchFailed("\"*\""); } } if (result1 === null) { if (input.charCodeAt(pos) === 95) { result1 = "_"; pos++; } else { result1 = null; if (reportFailures === 0) { matchFailed("\"_\""); } } if (result1 === null) { if (input.charCodeAt(pos) === 43) { result1 = "+"; pos++; } else { result1 = null; if (reportFailures === 0) { matchFailed("\"+\""); } } if (result1 === null) { if (input.charCodeAt(pos) === 96) { result1 = "`"; pos++; } else { result1 = null; if (reportFailures === 0) { matchFailed("\"`\""); } } if (result1 === null) { if (input.charCodeAt(pos) === 39) { result1 = "'"; pos++; } else { result1 = null; if (reportFailures === 0) { matchFailed("\"'\""); } } if (result1 === null) { if (input.charCodeAt(pos) === 126) { result1 = "~"; pos++; } else { result1 = null; if (reportFailures === 0) { matchFailed("\"~\""); } } } } } } } } } } } } } else { result0 = null; } if (result0 !== null) { result0 = (function(offset) { return input.substring(pos, offset); })(pos0); } if (result0 === null) { pos = pos0; } return result0; } function parse_separators() { var result0; if (input.charCodeAt(pos) === 40) { result0 = "("; pos++; } else { result0 = null; if (reportFailures === 0) { matchFailed("\"(\""); } } if (result0 === null) { if (input.charCodeAt(pos) === 41) { result0 = ")"; pos++; } else { result0 = null; if (reportFailures === 0) { matchFailed("\")\""); } } if (result0 === null) { if (input.charCodeAt(pos) === 60) { result0 = "<"; pos++; } else { result0 = null; if (reportFailures === 0) { matchFailed("\"<\""); } } if (result0 === null) { if (input.charCodeAt(pos) === 62) { result0 = ">"; pos++; } else { result0 = null; if (reportFailures === 0) { matchFailed("\">\""); } } if (result0 === null) { if (input.charCodeAt(pos) === 64) { result0 = "@"; pos++; } else { result0 = null; if (reportFailures === 0) { matchFailed("\"@\""); } } if (result0 === null) { if (input.charCodeAt(pos) === 44) { result0 = ","; pos++; } else { result0 = null; if (reportFailures === 0) { matchFailed("\",\""); } } if (result0 === null) { if (input.charCodeAt(pos) === 59) { result0 = ";"; pos++; } else { result0 = null; if (reportFailures === 0) { matchFailed("\";\""); } } if (result0 === null) { if (input.charCodeAt(pos) === 58) { result0 = ":"; pos++; } else { result0 = null; if (reportFailures === 0) { matchFailed("\":\""); } } if (result0 === null) { if (input.charCodeAt(pos) === 92) { result0 = "\\"; pos++; } else { result0 = null; if (reportFailures === 0) { matchFailed("\"\\\\\""); } } if (result0 === null) { result0 = parse_DQUOTE(); if (result0 === null) { if (input.charCodeAt(pos) === 47) { result0 = "/"; pos++; } else { result0 = null; if (reportFailures === 0) { matchFailed("\"/\""); } } if (result0 === null) { if (input.charCodeAt(pos) === 91) { result0 = "["; pos++; } else { result0 = null; if (reportFailures === 0) { matchFailed("\"[\""); } } if (result0 === null) { if (input.charCodeAt(pos) === 93) { result0 = "]"; pos++; } else { result0 = null; if (reportFailures === 0) { matchFailed("\"]\""); } } if (result0 === null) { if (input.charCodeAt(pos) === 63) { result0 = "?"; pos++; } else { result0 = null; if (reportFailures === 0) { matchFailed("\"?\""); } } if (result0 === null) { if (input.charCodeAt(pos) === 61) { result0 = "="; pos++; } else { result0 = null; if (reportFailures === 0) { matchFailed("\"=\""); } } if (result0 === null) { if (input.charCodeAt(pos) === 123) { result0 = "{"; pos++; } else { result0 = null; if (reportFailures === 0) { matchFailed("\"{\""); } } if (result0 === null) { if (input.charCodeAt(pos) === 125) { result0 = "}"; pos++; } else { result0 = null; if (reportFailures === 0) { matchFailed("\"}\""); } } if (result0 === null) { result0 = parse_SP(); if (result0 === null) { result0 = parse_HTAB(); } } } } } } } } } } } } } } } } } } return result0; } function parse_word() { var result0, result1; var pos0; pos0 = pos; result1 = parse_alphanum(); if (result1 === null) { if (input.charCodeAt(pos) === 45) { result1 = "-"; pos++; } else { result1 = null; if (reportFailures === 0) { matchFailed("\"-\""); } } if (result1 === null) { if (input.charCodeAt(pos) === 46) { result1 = "."; pos++; } else { result1 = null; if (reportFailures === 0) { matchFailed("\".\""); } } if (result1 === null) { if (input.charCodeAt(pos) === 33) { result1 = "!"; pos++; } else { result1 = null; if (reportFailures === 0) { matchFailed("\"!\""); } } if (result1 === null) { if (input.charCodeAt(pos) === 37) { result1 = "%"; pos++; } else { result1 = null; if (reportFailures === 0) { matchFailed("\"%\""); } } if (result1 === null) { if (input.charCodeAt(pos) === 42) { result1 = "*"; pos++; } else { result1 = null; if (reportFailures === 0) { matchFailed("\"*\""); } } if (result1 === null) { if (input.charCodeAt(pos) === 95) { result1 = "_"; pos++; } else { result1 = null; if (reportFailures === 0) { matchFailed("\"_\""); } } if (result1 === null) { if (input.charCodeAt(pos) === 43) { result1 = "+"; pos++; } else { result1 = null; if (reportFailures === 0) { matchFailed("\"+\""); } } if (result1 === null) { if (input.charCodeAt(pos) === 96) { result1 = "`"; pos++; } else { result1 = null; if (reportFailures === 0) { matchFailed("\"`\""); } } if (result1 === null) { if (input.charCodeAt(pos) === 39) { result1 = "'"; pos++; } else { result1 = null; if (reportFailures === 0) { matchFailed("\"'\""); } } if (result1 === null) { if (input.charCodeAt(pos) === 126) { result1 = "~"; pos++; } else { result1 = null; if (reportFailures === 0) { matchFailed("\"~\""); } } if (result1 === null) { if (input.charCodeAt(pos) === 40) { result1 = "("; pos++; } else { result1 = null; if (reportFailures === 0) { matchFailed("\"(\""); } } if (result1 === null) { if (input.charCodeAt(pos) === 41) { result1 = ")"; pos++; } else { result1 = null; if (reportFailures === 0) { matchFailed("\")\""); } } if (result1 === null) { if (input.charCodeAt(pos) === 60) { result1 = "<"; pos++; } else { result1 = null; if (reportFailures === 0) { matchFailed("\"<\""); } } if (result1 === null) { if (input.charCodeAt(pos) === 62) { result1 = ">"; pos++; } else { result1 = null; if (reportFailures === 0) { matchFailed("\">\""); } } if (result1 === null) { if (input.charCodeAt(pos) === 58) { result1 = ":"; pos++; } else { result1 = null; if (reportFailures === 0) { matchFailed("\":\""); } } if (result1 === null) { if (input.charCodeAt(pos) === 92) { result1 = "\\"; pos++; } else { result1 = null; if (reportFailures === 0) { matchFailed("\"\\\\\""); } } if (result1 === null) { result1 = parse_DQUOTE(); if (result1 === null) { if (input.charCodeAt(pos) === 47) { result1 = "/"; pos++; } else { result1 = null; if (reportFailures === 0) { matchFailed("\"/\""); } } if (result1 === null) { if (input.charCodeAt(pos) === 91) { result1 = "["; pos++; } else { result1 = null; if (reportFailures === 0) { matchFailed("\"[\""); } } if (result1 === null) { if (input.charCodeAt(pos) === 93) { result1 = "]"; pos++; } else { result1 = null; if (reportFailures === 0) { matchFailed("\"]\""); } } if (result1 === null) { if (input.charCodeAt(pos) === 63) { result1 = "?"; pos++; } else { result1 = null; if (reportFailures === 0) { matchFailed("\"?\""); } } if (result1 === null) { if (input.charCodeAt(pos) === 123) { result1 = "{"; pos++; } else { result1 = null; if (reportFailures === 0) { matchFailed("\"{\""); } } if (result1 === null) { if (input.charCodeAt(pos) === 125) { result1 = "}"; pos++; } else { result1 = null; if (reportFailures === 0) { matchFailed("\"}\""); } } } } } } } } } } } } } } } } } } } } } } } } } if (result1 !== null) { result0 = []; while (result1 !== null) { result0.push(result1); result1 = parse_alphanum(); if (result1 === null) { if (input.charCodeAt(pos) === 45) { result1 = "-"; pos++; } else { result1 = null; if (reportFailures === 0) { matchFailed("\"-\""); } } if (result1 === null) { if (input.charCodeAt(pos) === 46) { result1 = "."; pos++; } else { result1 = null; if (reportFailures === 0) { matchFailed("\".\""); } } if (result1 === null) { if (input.charCodeAt(pos) === 33) { result1 = "!"; pos++; } else { result1 = null; if (reportFailures === 0) { matchFailed("\"!\""); } } if (result1 === null) { if (input.charCodeAt(pos) === 37) { result1 = "%"; pos++; } else { result1 = null; if (reportFailures === 0) { matchFailed("\"%\""); } } if (result1 === null) { if (input.charCodeAt(pos) === 42) { result1 = "*"; pos++; } else { result1 = null; if (reportFailures === 0) { matchFailed("\"*\""); } } if (result1 === null) { if (input.charCodeAt(pos) === 95) { result1 = "_"; pos++; } else { result1 = null; if (reportFailures === 0) { matchFailed("\"_\""); } } if (result1 === null) { if (input.charCodeAt(pos) === 43) { result1 = "+"; pos++; } else { result1 = null; if (reportFailures === 0) { matchFailed("\"+\""); } } if (result1 === null) { if (input.charCodeAt(pos) === 96) { result1 = "`"; pos++; } else { result1 = null; if (reportFailures === 0) { matchFailed("\"`\""); } } if (result1 === null) { if (input.charCodeAt(pos) === 39) { result1 = "'"; pos++; } else { result1 = null; if (reportFailures === 0) { matchFailed("\"'\""); } } if (result1 === null) { if (input.charCodeAt(pos) === 126) { result1 = "~"; pos++; } else { result1 = null; if (reportFailures === 0) { matchFailed("\"~\""); } } if (result1 === null) { if (input.charCodeAt(pos) === 40) { result1 = "("; pos++; } else { result1 = null; if (reportFailures === 0) { matchFailed("\"(\""); } } if (result1 === null) { if (input.charCodeAt(pos) === 41) { result1 = ")"; pos++; } else { result1 = null; if (reportFailures === 0) { matchFailed("\")\""); } } if (result1 === null) { if (input.charCodeAt(pos) === 60) { result1 = "<"; pos++; } else { result1 = null; if (reportFailures === 0) { matchFailed("\"<\""); } } if (result1 === null) { if (input.charCodeAt(pos) === 62) { result1 = ">"; pos++; } else { result1 = null; if (reportFailures === 0) { matchFailed("\">\""); } } if (result1 === null) { if (input.charCodeAt(pos) === 58) { result1 = ":"; pos++; } else { result1 = null; if (reportFailures === 0) { matchFailed("\":\""); } } if (result1 === null) { if (input.charCodeAt(pos) === 92) { result1 = "\\"; pos++; } else { result1 = null; if (reportFailures === 0) { matchFailed("\"\\\\\""); } } if (result1 === null) { result1 = parse_DQUOTE(); if (result1 === null) { if (input.charCodeAt(pos) === 47) { result1 = "/"; pos++; } else { result1 = null; if (reportFailures === 0) { matchFailed("\"/\""); } } if (result1 === null) { if (input.charCodeAt(pos) === 91) { result1 = "["; pos++; } else { result1 = null; if (reportFailures === 0) { matchFailed("\"[\""); } } if (result1 === null) { if (input.charCodeAt(pos) === 93) { result1 = "]"; pos++; } else { result1 = null; if (reportFailures === 0) { matchFailed("\"]\""); } } if (result1 === null) { if (input.charCodeAt(pos) === 63) { result1 = "?"; pos++; } else { result1 = null; if (reportFailures === 0) { matchFailed("\"?\""); } } if (result1 === null) { if (input.charCodeAt(pos) === 123) { result1 = "{"; pos++; } else { result1 = null; if (reportFailures === 0) { matchFailed("\"{\""); } } if (result1 === null) { if (input.charCodeAt(pos) === 125) { result1 = "}"; pos++; } else { result1 = null; if (reportFailures === 0) { matchFailed("\"}\""); } } } } } } } } } } } } } } } } } } } } } } } } } } } else { result0 = null; } if (result0 !== null) { result0 = (function(offset) { return input.substring(pos, offset); })(pos0); } if (result0 === null) { pos = pos0; } return result0; } function parse_STAR() { var result0, result1, result2; var pos0, pos1; pos0 = pos; pos1 = pos; result0 = parse_SWS(); if (result0 !== null) { if (input.charCodeAt(pos) === 42) { result1 = "*"; pos++; } else { result1 = null; if (reportFailures === 0) { matchFailed("\"*\""); } } if (result1 !== null) { result2 = parse_SWS(); if (result2 !== null) { result0 = [result0, result1, result2]; } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } if (result0 !== null) { result0 = (function(offset) {return "*"; })(pos0); } if (result0 === null) { pos = pos0; } return result0; } function parse_SLASH() { var result0, result1, result2; var pos0, pos1; pos0 = pos; pos1 = pos; result0 = parse_SWS(); if (result0 !== null) { if (input.charCodeAt(pos) === 47) { result1 = "/"; pos++; } else { result1 = null; if (reportFailures === 0) { matchFailed("\"/\""); } } if (result1 !== null) { result2 = parse_SWS(); if (result2 !== null) { result0 = [result0, result1, result2]; } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } if (result0 !== null) { result0 = (function(offset) {return "/"; })(pos0); } if (result0 === null) { pos = pos0; } return result0; } function parse_EQUAL() { var result0, result1, result2; var pos0, pos1; pos0 = pos; pos1 = pos; result0 = parse_SWS(); if (result0 !== null) { if (input.charCodeAt(pos) === 61) { result1 = "="; pos++; } else { result1 = null; if (reportFailures === 0) { matchFailed("\"=\""); } } if (result1 !== null) { result2 = parse_SWS(); if (result2 !== null) { result0 = [result0, result1, result2]; } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } if (result0 !== null) { result0 = (function(offset) {return "="; })(pos0); } if (result0 === null) { pos = pos0; } return result0; } function parse_LPAREN() { var result0, result1, result2; var pos0, pos1; pos0 = pos; pos1 = pos; result0 = parse_SWS(); if (result0 !== null) { if (input.charCodeAt(pos) === 40) { result1 = "("; pos++; } else { result1 = null; if (reportFailures === 0) { matchFailed("\"(\""); } } if (result1 !== null) { result2 = parse_SWS(); if (result2 !== null) { result0 = [result0, result1, result2]; } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } if (result0 !== null) { result0 = (function(offset) {return "("; })(pos0); } if (result0 === null) { pos = pos0; } return result0; } function parse_RPAREN() { var result0, result1, result2; var pos0, pos1; pos0 = pos; pos1 = pos; result0 = parse_SWS(); if (result0 !== null) { if (input.charCodeAt(pos) === 41) { result1 = ")"; pos++; } else { result1 = null; if (reportFailures === 0) { matchFailed("\")\""); } } if (result1 !== null) { result2 = parse_SWS(); if (result2 !== null) { result0 = [result0, result1, result2]; } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } if (result0 !== null) { result0 = (function(offset) {return ")"; })(pos0); } if (result0 === null) { pos = pos0; } return result0; } function parse_RAQUOT() { var result0, result1; var pos0, pos1; pos0 = pos; pos1 = pos; if (input.charCodeAt(pos) === 62) { result0 = ">"; pos++; } else { result0 = null; if (reportFailures === 0) { matchFailed("\">\""); } } if (result0 !== null) { result1 = parse_SWS(); if (result1 !== null) { result0 = [result0, result1]; } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } if (result0 !== null) { result0 = (function(offset) {return ">"; })(pos0); } if (result0 === null) { pos = pos0; } return result0; } function parse_LAQUOT() { var result0, result1; var pos0, pos1; pos0 = pos; pos1 = pos; result0 = parse_SWS(); if (result0 !== null) { if (input.charCodeAt(pos) === 60) { result1 = "<"; pos++; } else { result1 = null; if (reportFailures === 0) { matchFailed("\"<\""); } } if (result1 !== null) { result0 = [result0, result1]; } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } if (result0 !== null) { result0 = (function(offset) {return "<"; })(pos0); } if (result0 === null) { pos = pos0; } return result0; } function parse_COMMA() { var result0, result1, result2; var pos0, pos1; pos0 = pos; pos1 = pos; result0 = parse_SWS(); if (result0 !== null) { if (input.charCodeAt(pos) === 44) { result1 = ","; pos++; } else { result1 = null; if (reportFailures === 0) { matchFailed("\",\""); } } if (result1 !== null) { result2 = parse_SWS(); if (result2 !== null) { result0 = [result0, result1, result2]; } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } if (result0 !== null) { result0 = (function(offset) {return ","; })(pos0); } if (result0 === null) { pos = pos0; } return result0; } function parse_SEMI() { var result0, result1, result2; var pos0, pos1; pos0 = pos; pos1 = pos; result0 = parse_SWS(); if (result0 !== null) { if (input.charCodeAt(pos) === 59) { result1 = ";"; pos++; } else { result1 = null; if (reportFailures === 0) { matchFailed("\";\""); } } if (result1 !== null) { result2 = parse_SWS(); if (result2 !== null) { result0 = [result0, result1, result2]; } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } if (result0 !== null) { result0 = (function(offset) {return ";"; })(pos0); } if (result0 === null) { pos = pos0; } return result0; } function parse_COLON() { var result0, result1, result2; var pos0, pos1; pos0 = pos; pos1 = pos; result0 = parse_SWS(); if (result0 !== null) { if (input.charCodeAt(pos) === 58) { result1 = ":"; pos++; } else { result1 = null; if (reportFailures === 0) { matchFailed("\":\""); } } if (result1 !== null) { result2 = parse_SWS(); if (result2 !== null) { result0 = [result0, result1, result2]; } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } if (result0 !== null) { result0 = (function(offset) {return ":"; })(pos0); } if (result0 === null) { pos = pos0; } return result0; } function parse_LDQUOT() { var result0, result1; var pos0, pos1; pos0 = pos; pos1 = pos; result0 = parse_SWS(); if (result0 !== null) { result1 = parse_DQUOTE(); if (result1 !== null) { result0 = [result0, result1]; } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } if (result0 !== null) { result0 = (function(offset) {return "\""; })(pos0); } if (result0 === null) { pos = pos0; } return result0; } function parse_RDQUOT() { var result0, result1; var pos0, pos1; pos0 = pos; pos1 = pos; result0 = parse_DQUOTE(); if (result0 !== null) { result1 = parse_SWS(); if (result1 !== null) { result0 = [result0, result1]; } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } if (result0 !== null) { result0 = (function(offset) {return "\""; })(pos0); } if (result0 === null) { pos = pos0; } return result0; } function parse_comment() { var result0, result1, result2; var pos0; pos0 = pos; result0 = parse_LPAREN(); if (result0 !== null) { result1 = []; result2 = parse_ctext(); if (result2 === null) { result2 = parse_quoted_pair(); if (result2 === null) { result2 = parse_comment(); } } while (result2 !== null) { result1.push(result2); result2 = parse_ctext(); if (result2 === null) { result2 = parse_quoted_pair(); if (result2 === null) { result2 = parse_comment(); } } } if (result1 !== null) { result2 = parse_RPAREN(); if (result2 !== null) { result0 = [result0, result1, result2]; } else { result0 = null; pos = pos0; } } else { result0 = null; pos = pos0; } } else { result0 = null; pos = pos0; } return result0; } function parse_ctext() { var result0; if (/^[!-']/.test(input.charAt(pos))) { result0 = input.charAt(pos); pos++; } else { result0 = null; if (reportFailures === 0) { matchFailed("[!-']"); } } if (result0 === null) { if (/^[*-[]/.test(input.charAt(pos))) { result0 = input.charAt(pos); pos++; } else { result0 = null; if (reportFailures === 0) { matchFailed("[*-[]"); } } if (result0 === null) { if (/^[\]-~]/.test(input.charAt(pos))) { result0 = input.charAt(pos); pos++; } else { result0 = null; if (reportFailures === 0) { matchFailed("[\\]-~]"); } } if (result0 === null) { result0 = parse_UTF8_NONASCII(); if (result0 === null) { result0 = parse_LWS(); } } } } return result0; } function parse_quoted_string() { var result0, result1, result2, result3; var pos0, pos1; pos0 = pos; pos1 = pos; result0 = parse_SWS(); if (result0 !== null) { result1 = parse_DQUOTE(); if (result1 !== null) { result2 = []; result3 = parse_qdtext(); if (result3 === null) { result3 = parse_quoted_pair(); } while (result3 !== null) { result2.push(result3); result3 = parse_qdtext(); if (result3 === null) { result3 = parse_quoted_pair(); } } if (result2 !== null) { result3 = parse_DQUOTE(); if (result3 !== null) { result0 = [result0, result1, result2, result3]; } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } if (result0 !== null) { result0 = (function(offset) { return input.substring(pos, offset); })(pos0); } if (result0 === null) { pos = pos0; } return result0; } function parse_quoted_string_clean() { var result0, result1, result2, result3; var pos0, pos1; pos0 = pos; pos1 = pos; result0 = parse_SWS(); if (result0 !== null) { result1 = parse_DQUOTE(); if (result1 !== null) { result2 = []; result3 = parse_qdtext(); if (result3 === null) { result3 = parse_quoted_pair(); } while (result3 !== null) { result2.push(result3); result3 = parse_qdtext(); if (result3 === null) { result3 = parse_quoted_pair(); } } if (result2 !== null) { result3 = parse_DQUOTE(); if (result3 !== null) { result0 = [result0, result1, result2, result3]; } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } if (result0 !== null) { result0 = (function(offset) { return input.substring(pos-1, offset+1); })(pos0); } if (result0 === null) { pos = pos0; } return result0; } function parse_qdtext() { var result0; result0 = parse_LWS(); if (result0 === null) { if (input.charCodeAt(pos) === 33) { result0 = "!"; pos++; } else { result0 = null; if (reportFailures === 0) { matchFailed("\"!\""); } } if (result0 === null) { if (/^[#-[]/.test(input.charAt(pos))) { result0 = input.charAt(pos); pos++; } else { result0 = null; if (reportFailures === 0) { matchFailed("[#-[]"); } } if (result0 === null) { if (/^[\]-~]/.test(input.charAt(pos))) { result0 = input.charAt(pos); pos++; } else { result0 = null; if (reportFailures === 0) { matchFailed("[\\]-~]"); } } if (result0 === null) { result0 = parse_UTF8_NONASCII(); } } } } return result0; } function parse_quoted_pair() { var result0, result1; var pos0; pos0 = pos; if (input.charCodeAt(pos) === 92) { result0 = "\\"; pos++; } else { result0 = null; if (reportFailures === 0) { matchFailed("\"\\\\\""); } } if (result0 !== null) { if (/^[\0-\t]/.test(input.charAt(pos))) { result1 = input.charAt(pos); pos++; } else { result1 = null; if (reportFailures === 0) { matchFailed("[\\0-\\t]"); } } if (result1 === null) { if (/^[\x0B-\f]/.test(input.charAt(pos))) { result1 = input.charAt(pos); pos++; } else { result1 = null; if (reportFailures === 0) { matchFailed("[\\x0B-\\f]"); } } if (result1 === null) { if (/^[\x0E-]/.test(input.charAt(pos))) { result1 = input.charAt(pos); pos++; } else { result1 = null; if (reportFailures === 0) { matchFailed("[\\x0E-]"); } } } } if (result1 !== null) { result0 = [result0, result1]; } else { result0 = null; pos = pos0; } } else { result0 = null; pos = pos0; } return result0; } function parse_SIP_URI_noparams() { var result0, result1, result2, result3; var pos0, pos1; pos0 = pos; pos1 = pos; result0 = parse_uri_scheme(); if (result0 !== null) { if (input.charCodeAt(pos) === 58) { result1 = ":"; pos++; } else { result1 = null; if (reportFailures === 0) { matchFailed("\":\""); } } if (result1 !== null) { result2 = parse_userinfo(); result2 = result2 !== null ? result2 : ""; if (result2 !== null) { result3 = parse_hostport(); if (result3 !== null) { result0 = [result0, result1, result2, result3]; } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } if (result0 !== null) { result0 = (function(offset) { try { data.uri = new JsSIP.URI(data.scheme, data.user, data.host, data.port); delete data.scheme; delete data.user; delete data.host; delete data.host_type; delete data.port; } catch(e) { data = -1; }})(pos0); } if (result0 === null) { pos = pos0; } return result0; } function parse_SIP_URI() { var result0, result1, result2, result3, result4, result5; var pos0, pos1; pos0 = pos; pos1 = pos; result0 = parse_uri_scheme(); if (result0 !== null) { if (input.charCodeAt(pos) === 58) { result1 = ":"; pos++; } else { result1 = null; if (reportFailures === 0) { matchFailed("\":\""); } } if (result1 !== null) { result2 = parse_userinfo(); result2 = result2 !== null ? result2 : ""; if (result2 !== null) { result3 = parse_hostport(); if (result3 !== null) { result4 = parse_uri_parameters(); if (result4 !== null) { result5 = parse_headers(); result5 = result5 !== null ? result5 : ""; if (result5 !== null) { result0 = [result0, result1, result2, result3, result4, result5]; } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } if (result0 !== null) { result0 = (function(offset) { var header; try { data.uri = new JsSIP.URI(data.scheme, data.user, data.host, data.port, data.uri_params, data.uri_headers); delete data.scheme; delete data.user; delete data.host; delete data.host_type; delete data.port; delete data.uri_params; if (startRule === 'SIP_URI') { data = data.uri;} } catch(e) { data = -1; }})(pos0); } if (result0 === null) { pos = pos0; } return result0; } function parse_uri_scheme() { var result0; var pos0; if (input.substr(pos, 3).toLowerCase() === "sip") { result0 = input.substr(pos, 3); pos += 3; } else { result0 = null; if (reportFailures === 0) { matchFailed("\"sip\""); } } if (result0 === null) { pos0 = pos; if (input.substr(pos, 4).toLowerCase() === "sips") { result0 = input.substr(pos, 4); pos += 4; } else { result0 = null; if (reportFailures === 0) { matchFailed("\"sips\""); } } if (result0 !== null) { result0 = (function(offset) { data.scheme = uri_scheme.toLowerCase(); })(pos0); } if (result0 === null) { pos = pos0; } } return result0; } function parse_userinfo() { var result0, result1, result2; var pos0, pos1, pos2; pos0 = pos; pos1 = pos; result0 = parse_user(); if (result0 !== null) { pos2 = pos; if (input.charCodeAt(pos) === 58) { result1 = ":"; pos++; } else { result1 = null; if (reportFailures === 0) { matchFailed("\":\""); } } if (result1 !== null) { result2 = parse_password(); if (result2 !== null) { result1 = [result1, result2]; } else { result1 = null; pos = pos2; } } else { result1 = null; pos = pos2; } result1 = result1 !== null ? result1 : ""; if (result1 !== null) { if (input.charCodeAt(pos) === 64) { result2 = "@"; pos++; } else { result2 = null; if (reportFailures === 0) { matchFailed("\"@\""); } } if (result2 !== null) { result0 = [result0, result1, result2]; } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } if (result0 !== null) { result0 = (function(offset) { data.user = window.decodeURIComponent(input.substring(pos-1, offset));})(pos0); } if (result0 === null) { pos = pos0; } return result0; } function parse_user() { var result0, result1; result1 = parse_unreserved(); if (result1 === null) { result1 = parse_escaped(); if (result1 === null) { result1 = parse_user_unreserved(); } } if (result1 !== null) { result0 = []; while (result1 !== null) { result0.push(result1); result1 = parse_unreserved(); if (result1 === null) { result1 = parse_escaped(); if (result1 === null) { result1 = parse_user_unreserved(); } } } } else { result0 = null; } return result0; } function parse_user_unreserved() { var result0; if (input.charCodeAt(pos) === 38) { result0 = "&"; pos++; } else { result0 = null; if (reportFailures === 0) { matchFailed("\"&\""); } } if (result0 === null) { if (input.charCodeAt(pos) === 61) { result0 = "="; pos++; } else { result0 = null; if (reportFailures === 0) { matchFailed("\"=\""); } } if (result0 === null) { if (input.charCodeAt(pos) === 43) { result0 = "+"; pos++; } else { result0 = null; if (reportFailures === 0) { matchFailed("\"+\""); } } if (result0 === null) { if (input.charCodeAt(pos) === 36) { result0 = "$"; pos++; } else { result0 = null; if (reportFailures === 0) { matchFailed("\"$\""); } } if (result0 === null) { if (input.charCodeAt(pos) === 44) { result0 = ","; pos++; } else { result0 = null; if (reportFailures === 0) { matchFailed("\",\""); } } if (result0 === null) { if (input.charCodeAt(pos) === 59) { result0 = ";"; pos++; } else { result0 = null; if (reportFailures === 0) { matchFailed("\";\""); } } if (result0 === null) { if (input.charCodeAt(pos) === 63) { result0 = "?"; pos++; } else { result0 = null; if (reportFailures === 0) { matchFailed("\"?\""); } } if (result0 === null) { if (input.charCodeAt(pos) === 47) { result0 = "/"; pos++; } else { result0 = null; if (reportFailures === 0) { matchFailed("\"/\""); } } } } } } } } } return result0; } function parse_password() { var result0, result1; var pos0; pos0 = pos; result0 = []; result1 = parse_unreserved(); if (result1 === null) { result1 = parse_escaped(); if (result1 === null) { if (input.charCodeAt(pos) === 38) { result1 = "&"; pos++; } else { result1 = null; if (reportFailures === 0) { matchFailed("\"&\""); } } if (result1 === null) { if (input.charCodeAt(pos) === 61) { result1 = "="; pos++; } else { result1 = null; if (reportFailures === 0) { matchFailed("\"=\""); } } if (result1 === null) { if (input.charCodeAt(pos) === 43) { result1 = "+"; pos++; } else { result1 = null; if (reportFailures === 0) { matchFailed("\"+\""); } } if (result1 === null) { if (input.charCodeAt(pos) === 36) { result1 = "$"; pos++; } else { result1 = null; if (reportFailures === 0) { matchFailed("\"$\""); } } if (result1 === null) { if (input.charCodeAt(pos) === 44) { result1 = ","; pos++; } else { result1 = null; if (reportFailures === 0) { matchFailed("\",\""); } } } } } } } } while (result1 !== null) { result0.push(result1); result1 = parse_unreserved(); if (result1 === null) { result1 = parse_escaped(); if (result1 === null) { if (input.charCodeAt(pos) === 38) { result1 = "&"; pos++; } else { result1 = null; if (reportFailures === 0) { matchFailed("\"&\""); } } if (result1 === null) { if (input.charCodeAt(pos) === 61) { result1 = "="; pos++; } else { result1 = null; if (reportFailures === 0) { matchFailed("\"=\""); } } if (result1 === null) { if (input.charCodeAt(pos) === 43) { result1 = "+"; pos++; } else { result1 = null; if (reportFailures === 0) { matchFailed("\"+\""); } } if (result1 === null) { if (input.charCodeAt(pos) === 36) { result1 = "$"; pos++; } else { result1 = null; if (reportFailures === 0) { matchFailed("\"$\""); } } if (result1 === null) { if (input.charCodeAt(pos) === 44) { result1 = ","; pos++; } else { result1 = null; if (reportFailures === 0) { matchFailed("\",\""); } } } } } } } } } if (result0 !== null) { result0 = (function(offset) { data.password = input.substring(pos, offset); })(pos0); } if (result0 === null) { pos = pos0; } return result0; } function parse_hostport() { var result0, result1, result2; var pos0, pos1; pos0 = pos; result0 = parse_host(); if (result0 !== null) { pos1 = pos; if (input.charCodeAt(pos) === 58) { result1 = ":"; pos++; } else { result1 = null; if (reportFailures === 0) { matchFailed("\":\""); } } if (result1 !== null) { result2 = parse_port(); if (result2 !== null) { result1 = [result1, result2]; } else { result1 = null; pos = pos1; } } else { result1 = null; pos = pos1; } result1 = result1 !== null ? result1 : ""; if (result1 !== null) { result0 = [result0, result1]; } else { result0 = null; pos = pos0; } } else { result0 = null; pos = pos0; } return result0; } function parse_host() { var result0; var pos0; pos0 = pos; result0 = parse_IPv4address(); if (result0 === null) { result0 = parse_IPv6reference(); if (result0 === null) { result0 = parse_hostname(); } } if (result0 !== null) { result0 = (function(offset) { data.host = input.substring(pos, offset).toLowerCase(); return data.host; })(pos0); } if (result0 === null) { pos = pos0; } return result0; } function parse_hostname() { var result0, result1, result2; var pos0, pos1, pos2; pos0 = pos; pos1 = pos; result0 = []; pos2 = pos; result1 = parse_domainlabel(); if (result1 !== null) { if (input.charCodeAt(pos) === 46) { result2 = "."; pos++; } else { result2 = null; if (reportFailures === 0) { matchFailed("\".\""); } } if (result2 !== null) { result1 = [result1, result2]; } else { result1 = null; pos = pos2; } } else { result1 = null; pos = pos2; } while (result1 !== null) { result0.push(result1); pos2 = pos; result1 = parse_domainlabel(); if (result1 !== null) { if (input.charCodeAt(pos) === 46) { result2 = "."; pos++; } else { result2 = null; if (reportFailures === 0) { matchFailed("\".\""); } } if (result2 !== null) { result1 = [result1, result2]; } else { result1 = null; pos = pos2; } } else { result1 = null; pos = pos2; } } if (result0 !== null) { result1 = parse_toplabel(); if (result1 !== null) { if (input.charCodeAt(pos) === 46) { result2 = "."; pos++; } else { result2 = null; if (reportFailures === 0) { matchFailed("\".\""); } } result2 = result2 !== null ? result2 : ""; if (result2 !== null) { result0 = [result0, result1, result2]; } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } if (result0 !== null) { result0 = (function(offset) { data.host_type = 'domain'; return input.substring(pos, offset); })(pos0); } if (result0 === null) { pos = pos0; } return result0; } function parse_domainlabel() { var result0, result1; if (/^[a-zA-Z0-9_\-]/.test(input.charAt(pos))) { result1 = input.charAt(pos); pos++; } else { result1 = null; if (reportFailures === 0) { matchFailed("[a-zA-Z0-9_\\-]"); } } if (result1 !== null) { result0 = []; while (result1 !== null) { result0.push(result1); if (/^[a-zA-Z0-9_\-]/.test(input.charAt(pos))) { result1 = input.charAt(pos); pos++; } else { result1 = null; if (reportFailures === 0) { matchFailed("[a-zA-Z0-9_\\-]"); } } } } else { result0 = null; } return result0; } function parse_toplabel() { var result0, result1; if (/^[a-zA-Z0-9_\-]/.test(input.charAt(pos))) { result1 = input.charAt(pos); pos++; } else { result1 = null; if (reportFailures === 0) { matchFailed("[a-zA-Z0-9_\\-]"); } } if (result1 !== null) { result0 = []; while (result1 !== null) { result0.push(result1); if (/^[a-zA-Z0-9_\-]/.test(input.charAt(pos))) { result1 = input.charAt(pos); pos++; } else { result1 = null; if (reportFailures === 0) { matchFailed("[a-zA-Z0-9_\\-]"); } } } } else { result0 = null; } return result0; } function parse_IPv6reference() { var result0, result1, result2; var pos0, pos1; pos0 = pos; pos1 = pos; if (input.charCodeAt(pos) === 91) { result0 = "["; pos++; } else { result0 = null; if (reportFailures === 0) { matchFailed("\"[\""); } } if (result0 !== null) { result1 = parse_IPv6address(); if (result1 !== null) { if (input.charCodeAt(pos) === 93) { result2 = "]"; pos++; } else { result2 = null; if (reportFailures === 0) { matchFailed("\"]\""); } } if (result2 !== null) { result0 = [result0, result1, result2]; } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } if (result0 !== null) { result0 = (function(offset) { data.host_type = 'IPv6'; return input.substring(pos, offset); })(pos0); } if (result0 === null) { pos = pos0; } return result0; } function parse_IPv6address() { var result0, result1, result2, result3, result4, result5, result6, result7, result8, result9, result10, result11, result12; var pos0, pos1, pos2; pos0 = pos; pos1 = pos; result0 = parse_h16(); if (result0 !== null) { if (input.charCodeAt(pos) === 58) { result1 = ":"; pos++; } else { result1 = null; if (reportFailures === 0) { matchFailed("\":\""); } } if (result1 !== null) { result2 = parse_h16(); if (result2 !== null) { if (input.charCodeAt(pos) === 58) { result3 = ":"; pos++; } else { result3 = null; if (reportFailures === 0) { matchFailed("\":\""); } } if (result3 !== null) { result4 = parse_h16(); if (result4 !== null) { if (input.charCodeAt(pos) === 58) { result5 = ":"; pos++; } else { result5 = null; if (reportFailures === 0) { matchFailed("\":\""); } } if (result5 !== null) { result6 = parse_h16(); if (result6 !== null) { if (input.charCodeAt(pos) === 58) { result7 = ":"; pos++; } else { result7 = null; if (reportFailures === 0) { matchFailed("\":\""); } } if (result7 !== null) { result8 = parse_h16(); if (result8 !== null) { if (input.charCodeAt(pos) === 58) { result9 = ":"; pos++; } else { result9 = null; if (reportFailures === 0) { matchFailed("\":\""); } } if (result9 !== null) { result10 = parse_h16(); if (result10 !== null) { if (input.charCodeAt(pos) === 58) { result11 = ":"; pos++; } else { result11 = null; if (reportFailures === 0) { matchFailed("\":\""); } } if (result11 !== null) { result12 = parse_ls32(); if (result12 !== null) { result0 = [result0, result1, result2, result3, result4, result5, result6, result7, result8, result9, result10, result11, result12]; } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } if (result0 === null) { pos1 = pos; if (input.substr(pos, 2) === "::") { result0 = "::"; pos += 2; } else { result0 = null; if (reportFailures === 0) { matchFailed("\"::\""); } } if (result0 !== null) { result1 = parse_h16(); if (result1 !== null) { if (input.charCodeAt(pos) === 58) { result2 = ":"; pos++; } else { result2 = null; if (reportFailures === 0) { matchFailed("\":\""); } } if (result2 !== null) { result3 = parse_h16(); if (result3 !== null) { if (input.charCodeAt(pos) === 58) { result4 = ":"; pos++; } else { result4 = null; if (reportFailures === 0) { matchFailed("\":\""); } } if (result4 !== null) { result5 = parse_h16(); if (result5 !== null) { if (input.charCodeAt(pos) === 58) { result6 = ":"; pos++; } else { result6 = null; if (reportFailures === 0) { matchFailed("\":\""); } } if (result6 !== null) { result7 = parse_h16(); if (result7 !== null) { if (input.charCodeAt(pos) === 58) { result8 = ":"; pos++; } else { result8 = null; if (reportFailures === 0) { matchFailed("\":\""); } } if (result8 !== null) { result9 = parse_h16(); if (result9 !== null) { if (input.charCodeAt(pos) === 58) { result10 = ":"; pos++; } else { result10 = null; if (reportFailures === 0) { matchFailed("\":\""); } } if (result10 !== null) { result11 = parse_ls32(); if (result11 !== null) { result0 = [result0, result1, result2, result3, result4, result5, result6, result7, result8, result9, result10, result11]; } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } if (result0 === null) { pos1 = pos; if (input.substr(pos, 2) === "::") { result0 = "::"; pos += 2; } else { result0 = null; if (reportFailures === 0) { matchFailed("\"::\""); } } if (result0 !== null) { result1 = parse_h16(); if (result1 !== null) { if (input.charCodeAt(pos) === 58) { result2 = ":"; pos++; } else { result2 = null; if (reportFailures === 0) { matchFailed("\":\""); } } if (result2 !== null) { result3 = parse_h16(); if (result3 !== null) { if (input.charCodeAt(pos) === 58) { result4 = ":"; pos++; } else { result4 = null; if (reportFailures === 0) { matchFailed("\":\""); } } if (result4 !== null) { result5 = parse_h16(); if (result5 !== null) { if (input.charCodeAt(pos) === 58) { result6 = ":"; pos++; } else { result6 = null; if (reportFailures === 0) { matchFailed("\":\""); } } if (result6 !== null) { result7 = parse_h16(); if (result7 !== null) { if (input.charCodeAt(pos) === 58) { result8 = ":"; pos++; } else { result8 = null; if (reportFailures === 0) { matchFailed("\":\""); } } if (result8 !== null) { result9 = parse_ls32(); if (result9 !== null) { result0 = [result0, result1, result2, result3, result4, result5, result6, result7, result8, result9]; } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } if (result0 === null) { pos1 = pos; if (input.substr(pos, 2) === "::") { result0 = "::"; pos += 2; } else { result0 = null; if (reportFailures === 0) { matchFailed("\"::\""); } } if (result0 !== null) { result1 = parse_h16(); if (result1 !== null) { if (input.charCodeAt(pos) === 58) { result2 = ":"; pos++; } else { result2 = null; if (reportFailures === 0) { matchFailed("\":\""); } } if (result2 !== null) { result3 = parse_h16(); if (result3 !== null) { if (input.charCodeAt(pos) === 58) { result4 = ":"; pos++; } else { result4 = null; if (reportFailures === 0) { matchFailed("\":\""); } } if (result4 !== null) { result5 = parse_h16(); if (result5 !== null) { if (input.charCodeAt(pos) === 58) { result6 = ":"; pos++; } else { result6 = null; if (reportFailures === 0) { matchFailed("\":\""); } } if (result6 !== null) { result7 = parse_ls32(); if (result7 !== null) { result0 = [result0, result1, result2, result3, result4, result5, result6, result7]; } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } if (result0 === null) { pos1 = pos; if (input.substr(pos, 2) === "::") { result0 = "::"; pos += 2; } else { result0 = null; if (reportFailures === 0) { matchFailed("\"::\""); } } if (result0 !== null) { result1 = parse_h16(); if (result1 !== null) { if (input.charCodeAt(pos) === 58) { result2 = ":"; pos++; } else { result2 = null; if (reportFailures === 0) { matchFailed("\":\""); } } if (result2 !== null) { result3 = parse_h16(); if (result3 !== null) { if (input.charCodeAt(pos) === 58) { result4 = ":"; pos++; } else { result4 = null; if (reportFailures === 0) { matchFailed("\":\""); } } if (result4 !== null) { result5 = parse_ls32(); if (result5 !== null) { result0 = [result0, result1, result2, result3, result4, result5]; } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } if (result0 === null) { pos1 = pos; if (input.substr(pos, 2) === "::") { result0 = "::"; pos += 2; } else { result0 = null; if (reportFailures === 0) { matchFailed("\"::\""); } } if (result0 !== null) { result1 = parse_h16(); if (result1 !== null) { if (input.charCodeAt(pos) === 58) { result2 = ":"; pos++; } else { result2 = null; if (reportFailures === 0) { matchFailed("\":\""); } } if (result2 !== null) { result3 = parse_ls32(); if (result3 !== null) { result0 = [result0, result1, result2, result3]; } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } if (result0 === null) { pos1 = pos; if (input.substr(pos, 2) === "::") { result0 = "::"; pos += 2; } else { result0 = null; if (reportFailures === 0) { matchFailed("\"::\""); } } if (result0 !== null) { result1 = parse_ls32(); if (result1 !== null) { result0 = [result0, result1]; } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } if (result0 === null) { pos1 = pos; if (input.substr(pos, 2) === "::") { result0 = "::"; pos += 2; } else { result0 = null; if (reportFailures === 0) { matchFailed("\"::\""); } } if (result0 !== null) { result1 = parse_h16(); if (result1 !== null) { result0 = [result0, result1]; } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } if (result0 === null) { pos1 = pos; result0 = parse_h16(); if (result0 !== null) { if (input.substr(pos, 2) === "::") { result1 = "::"; pos += 2; } else { result1 = null; if (reportFailures === 0) { matchFailed("\"::\""); } } if (result1 !== null) { result2 = parse_h16(); if (result2 !== null) { if (input.charCodeAt(pos) === 58) { result3 = ":"; pos++; } else { result3 = null; if (reportFailures === 0) { matchFailed("\":\""); } } if (result3 !== null) { result4 = parse_h16(); if (result4 !== null) { if (input.charCodeAt(pos) === 58) { result5 = ":"; pos++; } else { result5 = null; if (reportFailures === 0) { matchFailed("\":\""); } } if (result5 !== null) { result6 = parse_h16(); if (result6 !== null) { if (input.charCodeAt(pos) === 58) { result7 = ":"; pos++; } else { result7 = null; if (reportFailures === 0) { matchFailed("\":\""); } } if (result7 !== null) { result8 = parse_h16(); if (result8 !== null) { if (input.charCodeAt(pos) === 58) { result9 = ":"; pos++; } else { result9 = null; if (reportFailures === 0) { matchFailed("\":\""); } } if (result9 !== null) { result10 = parse_ls32(); if (result10 !== null) { result0 = [result0, result1, result2, result3, result4, result5, result6, result7, result8, result9, result10]; } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } if (result0 === null) { pos1 = pos; result0 = parse_h16(); if (result0 !== null) { pos2 = pos; if (input.charCodeAt(pos) === 58) { result1 = ":"; pos++; } else { result1 = null; if (reportFailures === 0) { matchFailed("\":\""); } } if (result1 !== null) { result2 = parse_h16(); if (result2 !== null) { result1 = [result1, result2]; } else { result1 = null; pos = pos2; } } else { result1 = null; pos = pos2; } result1 = result1 !== null ? result1 : ""; if (result1 !== null) { if (input.substr(pos, 2) === "::") { result2 = "::"; pos += 2; } else { result2 = null; if (reportFailures === 0) { matchFailed("\"::\""); } } if (result2 !== null) { result3 = parse_h16(); if (result3 !== null) { if (input.charCodeAt(pos) === 58) { result4 = ":"; pos++; } else { result4 = null; if (reportFailures === 0) { matchFailed("\":\""); } } if (result4 !== null) { result5 = parse_h16(); if (result5 !== null) { if (input.charCodeAt(pos) === 58) { result6 = ":"; pos++; } else { result6 = null; if (reportFailures === 0) { matchFailed("\":\""); } } if (result6 !== null) { result7 = parse_h16(); if (result7 !== null) { if (input.charCodeAt(pos) === 58) { result8 = ":"; pos++; } else { result8 = null; if (reportFailures === 0) { matchFailed("\":\""); } } if (result8 !== null) { result9 = parse_ls32(); if (result9 !== null) { result0 = [result0, result1, result2, result3, result4, result5, result6, result7, result8, result9]; } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } if (result0 === null) { pos1 = pos; result0 = parse_h16(); if (result0 !== null) { pos2 = pos; if (input.charCodeAt(pos) === 58) { result1 = ":"; pos++; } else { result1 = null; if (reportFailures === 0) { matchFailed("\":\""); } } if (result1 !== null) { result2 = parse_h16(); if (result2 !== null) { result1 = [result1, result2]; } else { result1 = null; pos = pos2; } } else { result1 = null; pos = pos2; } result1 = result1 !== null ? result1 : ""; if (result1 !== null) { pos2 = pos; if (input.charCodeAt(pos) === 58) { result2 = ":"; pos++; } else { result2 = null; if (reportFailures === 0) { matchFailed("\":\""); } } if (result2 !== null) { result3 = parse_h16(); if (result3 !== null) { result2 = [result2, result3]; } else { result2 = null; pos = pos2; } } else { result2 = null; pos = pos2; } result2 = result2 !== null ? result2 : ""; if (result2 !== null) { if (input.substr(pos, 2) === "::") { result3 = "::"; pos += 2; } else { result3 = null; if (reportFailures === 0) { matchFailed("\"::\""); } } if (result3 !== null) { result4 = parse_h16(); if (result4 !== null) { if (input.charCodeAt(pos) === 58) { result5 = ":"; pos++; } else { result5 = null; if (reportFailures === 0) { matchFailed("\":\""); } } if (result5 !== null) { result6 = parse_h16(); if (result6 !== null) { if (input.charCodeAt(pos) === 58) { result7 = ":"; pos++; } else { result7 = null; if (reportFailures === 0) { matchFailed("\":\""); } } if (result7 !== null) { result8 = parse_ls32(); if (result8 !== null) { result0 = [result0, result1, result2, result3, result4, result5, result6, result7, result8]; } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } if (result0 === null) { pos1 = pos; result0 = parse_h16(); if (result0 !== null) { pos2 = pos; if (input.charCodeAt(pos) === 58) { result1 = ":"; pos++; } else { result1 = null; if (reportFailures === 0) { matchFailed("\":\""); } } if (result1 !== null) { result2 = parse_h16(); if (result2 !== null) { result1 = [result1, result2]; } else { result1 = null; pos = pos2; } } else { result1 = null; pos = pos2; } result1 = result1 !== null ? result1 : ""; if (result1 !== null) { pos2 = pos; if (input.charCodeAt(pos) === 58) { result2 = ":"; pos++; } else { result2 = null; if (reportFailures === 0) { matchFailed("\":\""); } } if (result2 !== null) { result3 = parse_h16(); if (result3 !== null) { result2 = [result2, result3]; } else { result2 = null; pos = pos2; } } else { result2 = null; pos = pos2; } result2 = result2 !== null ? result2 : ""; if (result2 !== null) { pos2 = pos; if (input.charCodeAt(pos) === 58) { result3 = ":"; pos++; } else { result3 = null; if (reportFailures === 0) { matchFailed("\":\""); } } if (result3 !== null) { result4 = parse_h16(); if (result4 !== null) { result3 = [result3, result4]; } else { result3 = null; pos = pos2; } } else { result3 = null; pos = pos2; } result3 = result3 !== null ? result3 : ""; if (result3 !== null) { if (input.substr(pos, 2) === "::") { result4 = "::"; pos += 2; } else { result4 = null; if (reportFailures === 0) { matchFailed("\"::\""); } } if (result4 !== null) { result5 = parse_h16(); if (result5 !== null) { if (input.charCodeAt(pos) === 58) { result6 = ":"; pos++; } else { result6 = null; if (reportFailures === 0) { matchFailed("\":\""); } } if (result6 !== null) { result7 = parse_ls32(); if (result7 !== null) { result0 = [result0, result1, result2, result3, result4, result5, result6, result7]; } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } if (result0 === null) { pos1 = pos; result0 = parse_h16(); if (result0 !== null) { pos2 = pos; if (input.charCodeAt(pos) === 58) { result1 = ":"; pos++; } else { result1 = null; if (reportFailures === 0) { matchFailed("\":\""); } } if (result1 !== null) { result2 = parse_h16(); if (result2 !== null) { result1 = [result1, result2]; } else { result1 = null; pos = pos2; } } else { result1 = null; pos = pos2; } result1 = result1 !== null ? result1 : ""; if (result1 !== null) { pos2 = pos; if (input.charCodeAt(pos) === 58) { result2 = ":"; pos++; } else { result2 = null; if (reportFailures === 0) { matchFailed("\":\""); } } if (result2 !== null) { result3 = parse_h16(); if (result3 !== null) { result2 = [result2, result3]; } else { result2 = null; pos = pos2; } } else { result2 = null; pos = pos2; } result2 = result2 !== null ? result2 : ""; if (result2 !== null) { pos2 = pos; if (input.charCodeAt(pos) === 58) { result3 = ":"; pos++; } else { result3 = null; if (reportFailures === 0) { matchFailed("\":\""); } } if (result3 !== null) { result4 = parse_h16(); if (result4 !== null) { result3 = [result3, result4]; } else { result3 = null; pos = pos2; } } else { result3 = null; pos = pos2; } result3 = result3 !== null ? result3 : ""; if (result3 !== null) { pos2 = pos; if (input.charCodeAt(pos) === 58) { result4 = ":"; pos++; } else { result4 = null; if (reportFailures === 0) { matchFailed("\":\""); } } if (result4 !== null) { result5 = parse_h16(); if (result5 !== null) { result4 = [result4, result5]; } else { result4 = null; pos = pos2; } } else { result4 = null; pos = pos2; } result4 = result4 !== null ? result4 : ""; if (result4 !== null) { if (input.substr(pos, 2) === "::") { result5 = "::"; pos += 2; } else { result5 = null; if (reportFailures === 0) { matchFailed("\"::\""); } } if (result5 !== null) { result6 = parse_ls32(); if (result6 !== null) { result0 = [result0, result1, result2, result3, result4, result5, result6]; } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } if (result0 === null) { pos1 = pos; result0 = parse_h16(); if (result0 !== null) { pos2 = pos; if (input.charCodeAt(pos) === 58) { result1 = ":"; pos++; } else { result1 = null; if (reportFailures === 0) { matchFailed("\":\""); } } if (result1 !== null) { result2 = parse_h16(); if (result2 !== null) { result1 = [result1, result2]; } else { result1 = null; pos = pos2; } } else { result1 = null; pos = pos2; } result1 = result1 !== null ? result1 : ""; if (result1 !== null) { pos2 = pos; if (input.charCodeAt(pos) === 58) { result2 = ":"; pos++; } else { result2 = null; if (reportFailures === 0) { matchFailed("\":\""); } } if (result2 !== null) { result3 = parse_h16(); if (result3 !== null) { result2 = [result2, result3]; } else { result2 = null; pos = pos2; } } else { result2 = null; pos = pos2; } result2 = result2 !== null ? result2 : ""; if (result2 !== null) { pos2 = pos; if (input.charCodeAt(pos) === 58) { result3 = ":"; pos++; } else { result3 = null; if (reportFailures === 0) { matchFailed("\":\""); } } if (result3 !== null) { result4 = parse_h16(); if (result4 !== null) { result3 = [result3, result4]; } else { result3 = null; pos = pos2; } } else { result3 = null; pos = pos2; } result3 = result3 !== null ? result3 : ""; if (result3 !== null) { pos2 = pos; if (input.charCodeAt(pos) === 58) { result4 = ":"; pos++; } else { result4 = null; if (reportFailures === 0) { matchFailed("\":\""); } } if (result4 !== null) { result5 = parse_h16(); if (result5 !== null) { result4 = [result4, result5]; } else { result4 = null; pos = pos2; } } else { result4 = null; pos = pos2; } result4 = result4 !== null ? result4 : ""; if (result4 !== null) { pos2 = pos; if (input.charCodeAt(pos) === 58) { result5 = ":"; pos++; } else { result5 = null; if (reportFailures === 0) { matchFailed("\":\""); } } if (result5 !== null) { result6 = parse_h16(); if (result6 !== null) { result5 = [result5, result6]; } else { result5 = null; pos = pos2; } } else { result5 = null; pos = pos2; } result5 = result5 !== null ? result5 : ""; if (result5 !== null) { if (input.substr(pos, 2) === "::") { result6 = "::"; pos += 2; } else { result6 = null; if (reportFailures === 0) { matchFailed("\"::\""); } } if (result6 !== null) { result7 = parse_h16(); if (result7 !== null) { result0 = [result0, result1, result2, result3, result4, result5, result6, result7]; } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } if (result0 === null) { pos1 = pos; result0 = parse_h16(); if (result0 !== null) { pos2 = pos; if (input.charCodeAt(pos) === 58) { result1 = ":"; pos++; } else { result1 = null; if (reportFailures === 0) { matchFailed("\":\""); } } if (result1 !== null) { result2 = parse_h16(); if (result2 !== null) { result1 = [result1, result2]; } else { result1 = null; pos = pos2; } } else { result1 = null; pos = pos2; } result1 = result1 !== null ? result1 : ""; if (result1 !== null) { pos2 = pos; if (input.charCodeAt(pos) === 58) { result2 = ":"; pos++; } else { result2 = null; if (reportFailures === 0) { matchFailed("\":\""); } } if (result2 !== null) { result3 = parse_h16(); if (result3 !== null) { result2 = [result2, result3]; } else { result2 = null; pos = pos2; } } else { result2 = null; pos = pos2; } result2 = result2 !== null ? result2 : ""; if (result2 !== null) { pos2 = pos; if (input.charCodeAt(pos) === 58) { result3 = ":"; pos++; } else { result3 = null; if (reportFailures === 0) { matchFailed("\":\""); } } if (result3 !== null) { result4 = parse_h16(); if (result4 !== null) { result3 = [result3, result4]; } else { result3 = null; pos = pos2; } } else { result3 = null; pos = pos2; } result3 = result3 !== null ? result3 : ""; if (result3 !== null) { pos2 = pos; if (input.charCodeAt(pos) === 58) { result4 = ":"; pos++; } else { result4 = null; if (reportFailures === 0) { matchFailed("\":\""); } } if (result4 !== null) { result5 = parse_h16(); if (result5 !== null) { result4 = [result4, result5]; } else { result4 = null; pos = pos2; } } else { result4 = null; pos = pos2; } result4 = result4 !== null ? result4 : ""; if (result4 !== null) { pos2 = pos; if (input.charCodeAt(pos) === 58) { result5 = ":"; pos++; } else { result5 = null; if (reportFailures === 0) { matchFailed("\":\""); } } if (result5 !== null) { result6 = parse_h16(); if (result6 !== null) { result5 = [result5, result6]; } else { result5 = null; pos = pos2; } } else { result5 = null; pos = pos2; } result5 = result5 !== null ? result5 : ""; if (result5 !== null) { pos2 = pos; if (input.charCodeAt(pos) === 58) { result6 = ":"; pos++; } else { result6 = null; if (reportFailures === 0) { matchFailed("\":\""); } } if (result6 !== null) { result7 = parse_h16(); if (result7 !== null) { result6 = [result6, result7]; } else { result6 = null; pos = pos2; } } else { result6 = null; pos = pos2; } result6 = result6 !== null ? result6 : ""; if (result6 !== null) { if (input.substr(pos, 2) === "::") { result7 = "::"; pos += 2; } else { result7 = null; if (reportFailures === 0) { matchFailed("\"::\""); } } if (result7 !== null) { result0 = [result0, result1, result2, result3, result4, result5, result6, result7]; } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } } } } } } } } } } } } } } } if (result0 !== null) { result0 = (function(offset) { data.host_type = 'IPv6'; return input.substring(pos, offset); })(pos0); } if (result0 === null) { pos = pos0; } return result0; } function parse_h16() { var result0, result1, result2, result3; var pos0; pos0 = pos; result0 = parse_HEXDIG(); if (result0 !== null) { result1 = parse_HEXDIG(); result1 = result1 !== null ? result1 : ""; if (result1 !== null) { result2 = parse_HEXDIG(); result2 = result2 !== null ? result2 : ""; if (result2 !== null) { result3 = parse_HEXDIG(); result3 = result3 !== null ? result3 : ""; if (result3 !== null) { result0 = [result0, result1, result2, result3]; } else { result0 = null; pos = pos0; } } else { result0 = null; pos = pos0; } } else { result0 = null; pos = pos0; } } else { result0 = null; pos = pos0; } return result0; } function parse_ls32() { var result0, result1, result2; var pos0; pos0 = pos; result0 = parse_h16(); if (result0 !== null) { if (input.charCodeAt(pos) === 58) { result1 = ":"; pos++; } else { result1 = null; if (reportFailures === 0) { matchFailed("\":\""); } } if (result1 !== null) { result2 = parse_h16(); if (result2 !== null) { result0 = [result0, result1, result2]; } else { result0 = null; pos = pos0; } } else { result0 = null; pos = pos0; } } else { result0 = null; pos = pos0; } if (result0 === null) { result0 = parse_IPv4address(); } return result0; } function parse_IPv4address() { var result0, result1, result2, result3, result4, result5, result6; var pos0, pos1; pos0 = pos; pos1 = pos; result0 = parse_dec_octet(); if (result0 !== null) { if (input.charCodeAt(pos) === 46) { result1 = "."; pos++; } else { result1 = null; if (reportFailures === 0) { matchFailed("\".\""); } } if (result1 !== null) { result2 = parse_dec_octet(); if (result2 !== null) { if (input.charCodeAt(pos) === 46) { result3 = "."; pos++; } else { result3 = null; if (reportFailures === 0) { matchFailed("\".\""); } } if (result3 !== null) { result4 = parse_dec_octet(); if (result4 !== null) { if (input.charCodeAt(pos) === 46) { result5 = "."; pos++; } else { result5 = null; if (reportFailures === 0) { matchFailed("\".\""); } } if (result5 !== null) { result6 = parse_dec_octet(); if (result6 !== null) { result0 = [result0, result1, result2, result3, result4, result5, result6]; } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } if (result0 !== null) { result0 = (function(offset) { data.host_type = 'IPv4'; return input.substring(pos, offset); })(pos0); } if (result0 === null) { pos = pos0; } return result0; } function parse_dec_octet() { var result0, result1, result2; var pos0; pos0 = pos; if (input.substr(pos, 2) === "25") { result0 = "25"; pos += 2; } else { result0 = null; if (reportFailures === 0) { matchFailed("\"25\""); } } if (result0 !== null) { if (/^[0-5]/.test(input.charAt(pos))) { result1 = input.charAt(pos); pos++; } else { result1 = null; if (reportFailures === 0) { matchFailed("[0-5]"); } } if (result1 !== null) { result0 = [result0, result1]; } else { result0 = null; pos = pos0; } } else { result0 = null; pos = pos0; } if (result0 === null) { pos0 = pos; if (input.charCodeAt(pos) === 50) { result0 = "2"; pos++; } else { result0 = null; if (reportFailures === 0) { matchFailed("\"2\""); } } if (result0 !== null) { if (/^[0-4]/.test(input.charAt(pos))) { result1 = input.charAt(pos); pos++; } else { result1 = null; if (reportFailures === 0) { matchFailed("[0-4]"); } } if (result1 !== null) { result2 = parse_DIGIT(); if (result2 !== null) { result0 = [result0, result1, result2]; } else { result0 = null; pos = pos0; } } else { result0 = null; pos = pos0; } } else { result0 = null; pos = pos0; } if (result0 === null) { pos0 = pos; if (input.charCodeAt(pos) === 49) { result0 = "1"; pos++; } else { result0 = null; if (reportFailures === 0) { matchFailed("\"1\""); } } if (result0 !== null) { result1 = parse_DIGIT(); if (result1 !== null) { result2 = parse_DIGIT(); if (result2 !== null) { result0 = [result0, result1, result2]; } else { result0 = null; pos = pos0; } } else { result0 = null; pos = pos0; } } else { result0 = null; pos = pos0; } if (result0 === null) { pos0 = pos; if (/^[1-9]/.test(input.charAt(pos))) { result0 = input.charAt(pos); pos++; } else { result0 = null; if (reportFailures === 0) { matchFailed("[1-9]"); } } if (result0 !== null) { result1 = parse_DIGIT(); if (result1 !== null) { result0 = [result0, result1]; } else { result0 = null; pos = pos0; } } else { result0 = null; pos = pos0; } if (result0 === null) { result0 = parse_DIGIT(); } } } } return result0; } function parse_port() { var result0, result1, result2, result3, result4; var pos0, pos1; pos0 = pos; pos1 = pos; result0 = parse_DIGIT(); result0 = result0 !== null ? result0 : ""; if (result0 !== null) { result1 = parse_DIGIT(); result1 = result1 !== null ? result1 : ""; if (result1 !== null) { result2 = parse_DIGIT(); result2 = result2 !== null ? result2 : ""; if (result2 !== null) { result3 = parse_DIGIT(); result3 = result3 !== null ? result3 : ""; if (result3 !== null) { result4 = parse_DIGIT(); result4 = result4 !== null ? result4 : ""; if (result4 !== null) { result0 = [result0, result1, result2, result3, result4]; } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } if (result0 !== null) { result0 = (function(offset, port) { port = parseInt(port.join('')); data.port = port; return port; })(pos0, result0); } if (result0 === null) { pos = pos0; } return result0; } function parse_uri_parameters() { var result0, result1, result2; var pos0; result0 = []; pos0 = pos; if (input.charCodeAt(pos) === 59) { result1 = ";"; pos++; } else { result1 = null; if (reportFailures === 0) { matchFailed("\";\""); } } if (result1 !== null) { result2 = parse_uri_parameter(); if (result2 !== null) { result1 = [result1, result2]; } else { result1 = null; pos = pos0; } } else { result1 = null; pos = pos0; } while (result1 !== null) { result0.push(result1); pos0 = pos; if (input.charCodeAt(pos) === 59) { result1 = ";"; pos++; } else { result1 = null; if (reportFailures === 0) { matchFailed("\";\""); } } if (result1 !== null) { result2 = parse_uri_parameter(); if (result2 !== null) { result1 = [result1, result2]; } else { result1 = null; pos = pos0; } } else { result1 = null; pos = pos0; } } return result0; } function parse_uri_parameter() { var result0; result0 = parse_transport_param(); if (result0 === null) { result0 = parse_user_param(); if (result0 === null) { result0 = parse_method_param(); if (result0 === null) { result0 = parse_ttl_param(); if (result0 === null) { result0 = parse_maddr_param(); if (result0 === null) { result0 = parse_lr_param(); if (result0 === null) { result0 = parse_other_param(); } } } } } } return result0; } function parse_transport_param() { var result0, result1; var pos0, pos1; pos0 = pos; pos1 = pos; if (input.substr(pos, 10).toLowerCase() === "transport=") { result0 = input.substr(pos, 10); pos += 10; } else { result0 = null; if (reportFailures === 0) { matchFailed("\"transport=\""); } } if (result0 !== null) { if (input.substr(pos, 3).toLowerCase() === "udp") { result1 = input.substr(pos, 3); pos += 3; } else { result1 = null; if (reportFailures === 0) { matchFailed("\"udp\""); } } if (result1 === null) { if (input.substr(pos, 3).toLowerCase() === "tcp") { result1 = input.substr(pos, 3); pos += 3; } else { result1 = null; if (reportFailures === 0) { matchFailed("\"tcp\""); } } if (result1 === null) { if (input.substr(pos, 4).toLowerCase() === "sctp") { result1 = input.substr(pos, 4); pos += 4; } else { result1 = null; if (reportFailures === 0) { matchFailed("\"sctp\""); } } if (result1 === null) { if (input.substr(pos, 3).toLowerCase() === "tls") { result1 = input.substr(pos, 3); pos += 3; } else { result1 = null; if (reportFailures === 0) { matchFailed("\"tls\""); } } if (result1 === null) { result1 = parse_token(); } } } } if (result1 !== null) { result0 = [result0, result1]; } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } if (result0 !== null) { result0 = (function(offset, transport) { if(!data.uri_params) data.uri_params={}; data.uri_params['transport'] = transport.toLowerCase(); })(pos0, result0[1]); } if (result0 === null) { pos = pos0; } return result0; } function parse_user_param() { var result0, result1; var pos0, pos1; pos0 = pos; pos1 = pos; if (input.substr(pos, 5).toLowerCase() === "user=") { result0 = input.substr(pos, 5); pos += 5; } else { result0 = null; if (reportFailures === 0) { matchFailed("\"user=\""); } } if (result0 !== null) { if (input.substr(pos, 5).toLowerCase() === "phone") { result1 = input.substr(pos, 5); pos += 5; } else { result1 = null; if (reportFailures === 0) { matchFailed("\"phone\""); } } if (result1 === null) { if (input.substr(pos, 2).toLowerCase() === "ip") { result1 = input.substr(pos, 2); pos += 2; } else { result1 = null; if (reportFailures === 0) { matchFailed("\"ip\""); } } if (result1 === null) { result1 = parse_token(); } } if (result1 !== null) { result0 = [result0, result1]; } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } if (result0 !== null) { result0 = (function(offset, user) { if(!data.uri_params) data.uri_params={}; data.uri_params['user'] = user.toLowerCase(); })(pos0, result0[1]); } if (result0 === null) { pos = pos0; } return result0; } function parse_method_param() { var result0, result1; var pos0, pos1; pos0 = pos; pos1 = pos; if (input.substr(pos, 7).toLowerCase() === "method=") { result0 = input.substr(pos, 7); pos += 7; } else { result0 = null; if (reportFailures === 0) { matchFailed("\"method=\""); } } if (result0 !== null) { result1 = parse_Method(); if (result1 !== null) { result0 = [result0, result1]; } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } if (result0 !== null) { result0 = (function(offset, method) { if(!data.uri_params) data.uri_params={}; data.uri_params['method'] = method; })(pos0, result0[1]); } if (result0 === null) { pos = pos0; } return result0; } function parse_ttl_param() { var result0, result1; var pos0, pos1; pos0 = pos; pos1 = pos; if (input.substr(pos, 4).toLowerCase() === "ttl=") { result0 = input.substr(pos, 4); pos += 4; } else { result0 = null; if (reportFailures === 0) { matchFailed("\"ttl=\""); } } if (result0 !== null) { result1 = parse_ttl(); if (result1 !== null) { result0 = [result0, result1]; } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } if (result0 !== null) { result0 = (function(offset, ttl) { if(!data.params) data.params={}; data.params['ttl'] = ttl; })(pos0, result0[1]); } if (result0 === null) { pos = pos0; } return result0; } function parse_maddr_param() { var result0, result1; var pos0, pos1; pos0 = pos; pos1 = pos; if (input.substr(pos, 6).toLowerCase() === "maddr=") { result0 = input.substr(pos, 6); pos += 6; } else { result0 = null; if (reportFailures === 0) { matchFailed("\"maddr=\""); } } if (result0 !== null) { result1 = parse_host(); if (result1 !== null) { result0 = [result0, result1]; } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } if (result0 !== null) { result0 = (function(offset, maddr) { if(!data.uri_params) data.uri_params={}; data.uri_params['maddr'] = maddr; })(pos0, result0[1]); } if (result0 === null) { pos = pos0; } return result0; } function parse_lr_param() { var result0, result1, result2; var pos0, pos1, pos2; pos0 = pos; pos1 = pos; if (input.substr(pos, 2).toLowerCase() === "lr") { result0 = input.substr(pos, 2); pos += 2; } else { result0 = null; if (reportFailures === 0) { matchFailed("\"lr\""); } } if (result0 !== null) { pos2 = pos; if (input.charCodeAt(pos) === 61) { result1 = "="; pos++; } else { result1 = null; if (reportFailures === 0) { matchFailed("\"=\""); } } if (result1 !== null) { result2 = parse_token(); if (result2 !== null) { result1 = [result1, result2]; } else { result1 = null; pos = pos2; } } else { result1 = null; pos = pos2; } result1 = result1 !== null ? result1 : ""; if (result1 !== null) { result0 = [result0, result1]; } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } if (result0 !== null) { result0 = (function(offset) { if(!data.uri_params) data.uri_params={}; data.uri_params['lr'] = undefined; })(pos0); } if (result0 === null) { pos = pos0; } return result0; } function parse_other_param() { var result0, result1, result2; var pos0, pos1, pos2; pos0 = pos; pos1 = pos; result0 = parse_pname(); if (result0 !== null) { pos2 = pos; if (input.charCodeAt(pos) === 61) { result1 = "="; pos++; } else { result1 = null; if (reportFailures === 0) { matchFailed("\"=\""); } } if (result1 !== null) { result2 = parse_pvalue(); if (result2 !== null) { result1 = [result1, result2]; } else { result1 = null; pos = pos2; } } else { result1 = null; pos = pos2; } result1 = result1 !== null ? result1 : ""; if (result1 !== null) { result0 = [result0, result1]; } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } if (result0 !== null) { result0 = (function(offset, param, value) { if(!data.uri_params) data.uri_params = {}; if (typeof value === 'undefined'){ value = undefined; } else { value = value[1]; } data.uri_params[param.toLowerCase()] = value && value.toLowerCase();})(pos0, result0[0], result0[1]); } if (result0 === null) { pos = pos0; } return result0; } function parse_pname() { var result0, result1; var pos0; pos0 = pos; result1 = parse_paramchar(); if (result1 !== null) { result0 = []; while (result1 !== null) { result0.push(result1); result1 = parse_paramchar(); } } else { result0 = null; } if (result0 !== null) { result0 = (function(offset, pname) {return pname.join(''); })(pos0, result0); } if (result0 === null) { pos = pos0; } return result0; } function parse_pvalue() { var result0, result1; var pos0; pos0 = pos; result1 = parse_paramchar(); if (result1 !== null) { result0 = []; while (result1 !== null) { result0.push(result1); result1 = parse_paramchar(); } } else { result0 = null; } if (result0 !== null) { result0 = (function(offset, pvalue) {return pvalue.join(''); })(pos0, result0); } if (result0 === null) { pos = pos0; } return result0; } function parse_paramchar() { var result0; result0 = parse_param_unreserved(); if (result0 === null) { result0 = parse_unreserved(); if (result0 === null) { result0 = parse_escaped(); } } return result0; } function parse_param_unreserved() { var result0; if (input.charCodeAt(pos) === 91) { result0 = "["; pos++; } else { result0 = null; if (reportFailures === 0) { matchFailed("\"[\""); } } if (result0 === null) { if (input.charCodeAt(pos) === 93) { result0 = "]"; pos++; } else { result0 = null; if (reportFailures === 0) { matchFailed("\"]\""); } } if (result0 === null) { if (input.charCodeAt(pos) === 47) { result0 = "/"; pos++; } else { result0 = null; if (reportFailures === 0) { matchFailed("\"/\""); } } if (result0 === null) { if (input.charCodeAt(pos) === 58) { result0 = ":"; pos++; } else { result0 = null; if (reportFailures === 0) { matchFailed("\":\""); } } if (result0 === null) { if (input.charCodeAt(pos) === 38) { result0 = "&"; pos++; } else { result0 = null; if (reportFailures === 0) { matchFailed("\"&\""); } } if (result0 === null) { if (input.charCodeAt(pos) === 43) { result0 = "+"; pos++; } else { result0 = null; if (reportFailures === 0) { matchFailed("\"+\""); } } if (result0 === null) { if (input.charCodeAt(pos) === 36) { result0 = "$"; pos++; } else { result0 = null; if (reportFailures === 0) { matchFailed("\"$\""); } } } } } } } } return result0; } function parse_headers() { var result0, result1, result2, result3, result4; var pos0, pos1; pos0 = pos; if (input.charCodeAt(pos) === 63) { result0 = "?"; pos++; } else { result0 = null; if (reportFailures === 0) { matchFailed("\"?\""); } } if (result0 !== null) { result1 = parse_header(); if (result1 !== null) { result2 = []; pos1 = pos; if (input.charCodeAt(pos) === 38) { result3 = "&"; pos++; } else { result3 = null; if (reportFailures === 0) { matchFailed("\"&\""); } } if (result3 !== null) { result4 = parse_header(); if (result4 !== null) { result3 = [result3, result4]; } else { result3 = null; pos = pos1; } } else { result3 = null; pos = pos1; } while (result3 !== null) { result2.push(result3); pos1 = pos; if (input.charCodeAt(pos) === 38) { result3 = "&"; pos++; } else { result3 = null; if (reportFailures === 0) { matchFailed("\"&\""); } } if (result3 !== null) { result4 = parse_header(); if (result4 !== null) { result3 = [result3, result4]; } else { result3 = null; pos = pos1; } } else { result3 = null; pos = pos1; } } if (result2 !== null) { result0 = [result0, result1, result2]; } else { result0 = null; pos = pos0; } } else { result0 = null; pos = pos0; } } else { result0 = null; pos = pos0; } return result0; } function parse_header() { var result0, result1, result2; var pos0, pos1; pos0 = pos; pos1 = pos; result0 = parse_hname(); if (result0 !== null) { if (input.charCodeAt(pos) === 61) { result1 = "="; pos++; } else { result1 = null; if (reportFailures === 0) { matchFailed("\"=\""); } } if (result1 !== null) { result2 = parse_hvalue(); if (result2 !== null) { result0 = [result0, result1, result2]; } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } if (result0 !== null) { result0 = (function(offset, hname, hvalue) { hname = hname.join('').toLowerCase(); hvalue = hvalue.join(''); if(!data.uri_headers) data.uri_headers = {}; if (!data.uri_headers[hname]) { data.uri_headers[hname] = [hvalue]; } else { data.uri_headers[hname].push(hvalue); }})(pos0, result0[0], result0[2]); } if (result0 === null) { pos = pos0; } return result0; } function parse_hname() { var result0, result1; result1 = parse_hnv_unreserved(); if (result1 === null) { result1 = parse_unreserved(); if (result1 === null) { result1 = parse_escaped(); } } if (result1 !== null) { result0 = []; while (result1 !== null) { result0.push(result1); result1 = parse_hnv_unreserved(); if (result1 === null) { result1 = parse_unreserved(); if (result1 === null) { result1 = parse_escaped(); } } } } else { result0 = null; } return result0; } function parse_hvalue() { var result0, result1; result0 = []; result1 = parse_hnv_unreserved(); if (result1 === null) { result1 = parse_unreserved(); if (result1 === null) { result1 = parse_escaped(); } } while (result1 !== null) { result0.push(result1); result1 = parse_hnv_unreserved(); if (result1 === null) { result1 = parse_unreserved(); if (result1 === null) { result1 = parse_escaped(); } } } return result0; } function parse_hnv_unreserved() { var result0; if (input.charCodeAt(pos) === 91) { result0 = "["; pos++; } else { result0 = null; if (reportFailures === 0) { matchFailed("\"[\""); } } if (result0 === null) { if (input.charCodeAt(pos) === 93) { result0 = "]"; pos++; } else { result0 = null; if (reportFailures === 0) { matchFailed("\"]\""); } } if (result0 === null) { if (input.charCodeAt(pos) === 47) { result0 = "/"; pos++; } else { result0 = null; if (reportFailures === 0) { matchFailed("\"/\""); } } if (result0 === null) { if (input.charCodeAt(pos) === 63) { result0 = "?"; pos++; } else { result0 = null; if (reportFailures === 0) { matchFailed("\"?\""); } } if (result0 === null) { if (input.charCodeAt(pos) === 58) { result0 = ":"; pos++; } else { result0 = null; if (reportFailures === 0) { matchFailed("\":\""); } } if (result0 === null) { if (input.charCodeAt(pos) === 43) { result0 = "+"; pos++; } else { result0 = null; if (reportFailures === 0) { matchFailed("\"+\""); } } if (result0 === null) { if (input.charCodeAt(pos) === 36) { result0 = "$"; pos++; } else { result0 = null; if (reportFailures === 0) { matchFailed("\"$\""); } } } } } } } } return result0; } function parse_Request_Response() { var result0; result0 = parse_Status_Line(); if (result0 === null) { result0 = parse_Request_Line(); } return result0; } function parse_Request_Line() { var result0, result1, result2, result3, result4; var pos0; pos0 = pos; result0 = parse_Method(); if (result0 !== null) { result1 = parse_SP(); if (result1 !== null) { result2 = parse_Request_URI(); if (result2 !== null) { result3 = parse_SP(); if (result3 !== null) { result4 = parse_SIP_Version(); if (result4 !== null) { result0 = [result0, result1, result2, result3, result4]; } else { result0 = null; pos = pos0; } } else { result0 = null; pos = pos0; } } else { result0 = null; pos = pos0; } } else { result0 = null; pos = pos0; } } else { result0 = null; pos = pos0; } return result0; } function parse_Request_URI() { var result0; result0 = parse_SIP_URI(); if (result0 === null) { result0 = parse_absoluteURI(); } return result0; } function parse_absoluteURI() { var result0, result1, result2; var pos0; pos0 = pos; result0 = parse_scheme(); if (result0 !== null) { if (input.charCodeAt(pos) === 58) { result1 = ":"; pos++; } else { result1 = null; if (reportFailures === 0) { matchFailed("\":\""); } } if (result1 !== null) { result2 = parse_hier_part(); if (result2 === null) { result2 = parse_opaque_part(); } if (result2 !== null) { result0 = [result0, result1, result2]; } else { result0 = null; pos = pos0; } } else { result0 = null; pos = pos0; } } else { result0 = null; pos = pos0; } return result0; } function parse_hier_part() { var result0, result1, result2; var pos0, pos1; pos0 = pos; result0 = parse_net_path(); if (result0 === null) { result0 = parse_abs_path(); } if (result0 !== null) { pos1 = pos; if (input.charCodeAt(pos) === 63) { result1 = "?"; pos++; } else { result1 = null; if (reportFailures === 0) { matchFailed("\"?\""); } } if (result1 !== null) { result2 = parse_query(); if (result2 !== null) { result1 = [result1, result2]; } else { result1 = null; pos = pos1; } } else { result1 = null; pos = pos1; } result1 = result1 !== null ? result1 : ""; if (result1 !== null) { result0 = [result0, result1]; } else { result0 = null; pos = pos0; } } else { result0 = null; pos = pos0; } return result0; } function parse_net_path() { var result0, result1, result2; var pos0; pos0 = pos; if (input.substr(pos, 2) === "//") { result0 = "//"; pos += 2; } else { result0 = null; if (reportFailures === 0) { matchFailed("\"//\""); } } if (result0 !== null) { result1 = parse_authority(); if (result1 !== null) { result2 = parse_abs_path(); result2 = result2 !== null ? result2 : ""; if (result2 !== null) { result0 = [result0, result1, result2]; } else { result0 = null; pos = pos0; } } else { result0 = null; pos = pos0; } } else { result0 = null; pos = pos0; } return result0; } function parse_abs_path() { var result0, result1; var pos0; pos0 = pos; if (input.charCodeAt(pos) === 47) { result0 = "/"; pos++; } else { result0 = null; if (reportFailures === 0) { matchFailed("\"/\""); } } if (result0 !== null) { result1 = parse_path_segments(); if (result1 !== null) { result0 = [result0, result1]; } else { result0 = null; pos = pos0; } } else { result0 = null; pos = pos0; } return result0; } function parse_opaque_part() { var result0, result1, result2; var pos0; pos0 = pos; result0 = parse_uric_no_slash(); if (result0 !== null) { result1 = []; result2 = parse_uric(); while (result2 !== null) { result1.push(result2); result2 = parse_uric(); } if (result1 !== null) { result0 = [result0, result1]; } else { result0 = null; pos = pos0; } } else { result0 = null; pos = pos0; } return result0; } function parse_uric() { var result0; result0 = parse_reserved(); if (result0 === null) { result0 = parse_unreserved(); if (result0 === null) { result0 = parse_escaped(); } } return result0; } function parse_uric_no_slash() { var result0; result0 = parse_unreserved(); if (result0 === null) { result0 = parse_escaped(); if (result0 === null) { if (input.charCodeAt(pos) === 59) { result0 = ";"; pos++; } else { result0 = null; if (reportFailures === 0) { matchFailed("\";\""); } } if (result0 === null) { if (input.charCodeAt(pos) === 63) { result0 = "?"; pos++; } else { result0 = null; if (reportFailures === 0) { matchFailed("\"?\""); } } if (result0 === null) { if (input.charCodeAt(pos) === 58) { result0 = ":"; pos++; } else { result0 = null; if (reportFailures === 0) { matchFailed("\":\""); } } if (result0 === null) { if (input.charCodeAt(pos) === 64) { result0 = "@"; pos++; } else { result0 = null; if (reportFailures === 0) { matchFailed("\"@\""); } } if (result0 === null) { if (input.charCodeAt(pos) === 38) { result0 = "&"; pos++; } else { result0 = null; if (reportFailures === 0) { matchFailed("\"&\""); } } if (result0 === null) { if (input.charCodeAt(pos) === 61) { result0 = "="; pos++; } else { result0 = null; if (reportFailures === 0) { matchFailed("\"=\""); } } if (result0 === null) { if (input.charCodeAt(pos) === 43) { result0 = "+"; pos++; } else { result0 = null; if (reportFailures === 0) { matchFailed("\"+\""); } } if (result0 === null) { if (input.charCodeAt(pos) === 36) { result0 = "$"; pos++; } else { result0 = null; if (reportFailures === 0) { matchFailed("\"$\""); } } if (result0 === null) { if (input.charCodeAt(pos) === 44) { result0 = ","; pos++; } else { result0 = null; if (reportFailures === 0) { matchFailed("\",\""); } } } } } } } } } } } } return result0; } function parse_path_segments() { var result0, result1, result2, result3; var pos0, pos1; pos0 = pos; result0 = parse_segment(); if (result0 !== null) { result1 = []; pos1 = pos; if (input.charCodeAt(pos) === 47) { result2 = "/"; pos++; } else { result2 = null; if (reportFailures === 0) { matchFailed("\"/\""); } } if (result2 !== null) { result3 = parse_segment(); if (result3 !== null) { result2 = [result2, result3]; } else { result2 = null; pos = pos1; } } else { result2 = null; pos = pos1; } while (result2 !== null) { result1.push(result2); pos1 = pos; if (input.charCodeAt(pos) === 47) { result2 = "/"; pos++; } else { result2 = null; if (reportFailures === 0) { matchFailed("\"/\""); } } if (result2 !== null) { result3 = parse_segment(); if (result3 !== null) { result2 = [result2, result3]; } else { result2 = null; pos = pos1; } } else { result2 = null; pos = pos1; } } if (result1 !== null) { result0 = [result0, result1]; } else { result0 = null; pos = pos0; } } else { result0 = null; pos = pos0; } return result0; } function parse_segment() { var result0, result1, result2, result3; var pos0, pos1; pos0 = pos; result0 = []; result1 = parse_pchar(); while (result1 !== null) { result0.push(result1); result1 = parse_pchar(); } if (result0 !== null) { result1 = []; pos1 = pos; if (input.charCodeAt(pos) === 59) { result2 = ";"; pos++; } else { result2 = null; if (reportFailures === 0) { matchFailed("\";\""); } } if (result2 !== null) { result3 = parse_param(); if (result3 !== null) { result2 = [result2, result3]; } else { result2 = null; pos = pos1; } } else { result2 = null; pos = pos1; } while (result2 !== null) { result1.push(result2); pos1 = pos; if (input.charCodeAt(pos) === 59) { result2 = ";"; pos++; } else { result2 = null; if (reportFailures === 0) { matchFailed("\";\""); } } if (result2 !== null) { result3 = parse_param(); if (result3 !== null) { result2 = [result2, result3]; } else { result2 = null; pos = pos1; } } else { result2 = null; pos = pos1; } } if (result1 !== null) { result0 = [result0, result1]; } else { result0 = null; pos = pos0; } } else { result0 = null; pos = pos0; } return result0; } function parse_param() { var result0, result1; result0 = []; result1 = parse_pchar(); while (result1 !== null) { result0.push(result1); result1 = parse_pchar(); } return result0; } function parse_pchar() { var result0; result0 = parse_unreserved(); if (result0 === null) { result0 = parse_escaped(); if (result0 === null) { if (input.charCodeAt(pos) === 58) { result0 = ":"; pos++; } else { result0 = null; if (reportFailures === 0) { matchFailed("\":\""); } } if (result0 === null) { if (input.charCodeAt(pos) === 64) { result0 = "@"; pos++; } else { result0 = null; if (reportFailures === 0) { matchFailed("\"@\""); } } if (result0 === null) { if (input.charCodeAt(pos) === 38) { result0 = "&"; pos++; } else { result0 = null; if (reportFailures === 0) { matchFailed("\"&\""); } } if (result0 === null) { if (input.charCodeAt(pos) === 61) { result0 = "="; pos++; } else { result0 = null; if (reportFailures === 0) { matchFailed("\"=\""); } } if (result0 === null) { if (input.charCodeAt(pos) === 43) { result0 = "+"; pos++; } else { result0 = null; if (reportFailures === 0) { matchFailed("\"+\""); } } if (result0 === null) { if (input.charCodeAt(pos) === 36) { result0 = "$"; pos++; } else { result0 = null; if (reportFailures === 0) { matchFailed("\"$\""); } } if (result0 === null) { if (input.charCodeAt(pos) === 44) { result0 = ","; pos++; } else { result0 = null; if (reportFailures === 0) { matchFailed("\",\""); } } } } } } } } } } return result0; } function parse_scheme() { var result0, result1, result2; var pos0, pos1; pos0 = pos; pos1 = pos; result0 = parse_ALPHA(); if (result0 !== null) { result1 = []; result2 = parse_ALPHA(); if (result2 === null) { result2 = parse_DIGIT(); if (result2 === null) { if (input.charCodeAt(pos) === 43) { result2 = "+"; pos++; } else { result2 = null; if (reportFailures === 0) { matchFailed("\"+\""); } } if (result2 === null) { if (input.charCodeAt(pos) === 45) { result2 = "-"; pos++; } else { result2 = null; if (reportFailures === 0) { matchFailed("\"-\""); } } if (result2 === null) { if (input.charCodeAt(pos) === 46) { result2 = "."; pos++; } else { result2 = null; if (reportFailures === 0) { matchFailed("\".\""); } } } } } } while (result2 !== null) { result1.push(result2); result2 = parse_ALPHA(); if (result2 === null) { result2 = parse_DIGIT(); if (result2 === null) { if (input.charCodeAt(pos) === 43) { result2 = "+"; pos++; } else { result2 = null; if (reportFailures === 0) { matchFailed("\"+\""); } } if (result2 === null) { if (input.charCodeAt(pos) === 45) { result2 = "-"; pos++; } else { result2 = null; if (reportFailures === 0) { matchFailed("\"-\""); } } if (result2 === null) { if (input.charCodeAt(pos) === 46) { result2 = "."; pos++; } else { result2 = null; if (reportFailures === 0) { matchFailed("\".\""); } } } } } } } if (result1 !== null) { result0 = [result0, result1]; } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } if (result0 !== null) { result0 = (function(offset) { data.scheme= input.substring(pos, offset); })(pos0); } if (result0 === null) { pos = pos0; } return result0; } function parse_authority() { var result0; result0 = parse_srvr(); if (result0 === null) { result0 = parse_reg_name(); } return result0; } function parse_srvr() { var result0, result1; var pos0, pos1; pos0 = pos; pos1 = pos; result0 = parse_userinfo(); if (result0 !== null) { if (input.charCodeAt(pos) === 64) { result1 = "@"; pos++; } else { result1 = null; if (reportFailures === 0) { matchFailed("\"@\""); } } if (result1 !== null) { result0 = [result0, result1]; } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } result0 = result0 !== null ? result0 : ""; if (result0 !== null) { result1 = parse_hostport(); if (result1 !== null) { result0 = [result0, result1]; } else { result0 = null; pos = pos0; } } else { result0 = null; pos = pos0; } result0 = result0 !== null ? result0 : ""; return result0; } function parse_reg_name() { var result0, result1; result1 = parse_unreserved(); if (result1 === null) { result1 = parse_escaped(); if (result1 === null) { if (input.charCodeAt(pos) === 36) { result1 = "$"; pos++; } else { result1 = null; if (reportFailures === 0) { matchFailed("\"$\""); } } if (result1 === null) { if (input.charCodeAt(pos) === 44) { result1 = ","; pos++; } else { result1 = null; if (reportFailures === 0) { matchFailed("\",\""); } } if (result1 === null) { if (input.charCodeAt(pos) === 59) { result1 = ";"; pos++; } else { result1 = null; if (reportFailures === 0) { matchFailed("\";\""); } } if (result1 === null) { if (input.charCodeAt(pos) === 58) { result1 = ":"; pos++; } else { result1 = null; if (reportFailures === 0) { matchFailed("\":\""); } } if (result1 === null) { if (input.charCodeAt(pos) === 64) { result1 = "@"; pos++; } else { result1 = null; if (reportFailures === 0) { matchFailed("\"@\""); } } if (result1 === null) { if (input.charCodeAt(pos) === 38) { result1 = "&"; pos++; } else { result1 = null; if (reportFailures === 0) { matchFailed("\"&\""); } } if (result1 === null) { if (input.charCodeAt(pos) === 61) { result1 = "="; pos++; } else { result1 = null; if (reportFailures === 0) { matchFailed("\"=\""); } } if (result1 === null) { if (input.charCodeAt(pos) === 43) { result1 = "+"; pos++; } else { result1 = null; if (reportFailures === 0) { matchFailed("\"+\""); } } } } } } } } } } } if (result1 !== null) { result0 = []; while (result1 !== null) { result0.push(result1); result1 = parse_unreserved(); if (result1 === null) { result1 = parse_escaped(); if (result1 === null) { if (input.charCodeAt(pos) === 36) { result1 = "$"; pos++; } else { result1 = null; if (reportFailures === 0) { matchFailed("\"$\""); } } if (result1 === null) { if (input.charCodeAt(pos) === 44) { result1 = ","; pos++; } else { result1 = null; if (reportFailures === 0) { matchFailed("\",\""); } } if (result1 === null) { if (input.charCodeAt(pos) === 59) { result1 = ";"; pos++; } else { result1 = null; if (reportFailures === 0) { matchFailed("\";\""); } } if (result1 === null) { if (input.charCodeAt(pos) === 58) { result1 = ":"; pos++; } else { result1 = null; if (reportFailures === 0) { matchFailed("\":\""); } } if (result1 === null) { if (input.charCodeAt(pos) === 64) { result1 = "@"; pos++; } else { result1 = null; if (reportFailures === 0) { matchFailed("\"@\""); } } if (result1 === null) { if (input.charCodeAt(pos) === 38) { result1 = "&"; pos++; } else { result1 = null; if (reportFailures === 0) { matchFailed("\"&\""); } } if (result1 === null) { if (input.charCodeAt(pos) === 61) { result1 = "="; pos++; } else { result1 = null; if (reportFailures === 0) { matchFailed("\"=\""); } } if (result1 === null) { if (input.charCodeAt(pos) === 43) { result1 = "+"; pos++; } else { result1 = null; if (reportFailures === 0) { matchFailed("\"+\""); } } } } } } } } } } } } } else { result0 = null; } return result0; } function parse_query() { var result0, result1; result0 = []; result1 = parse_uric(); while (result1 !== null) { result0.push(result1); result1 = parse_uric(); } return result0; } function parse_SIP_Version() { var result0, result1, result2, result3, result4, result5; var pos0, pos1; pos0 = pos; pos1 = pos; if (input.substr(pos, 3).toLowerCase() === "sip") { result0 = input.substr(pos, 3); pos += 3; } else { result0 = null; if (reportFailures === 0) { matchFailed("\"SIP\""); } } if (result0 !== null) { if (input.charCodeAt(pos) === 47) { result1 = "/"; pos++; } else { result1 = null; if (reportFailures === 0) { matchFailed("\"/\""); } } if (result1 !== null) { result3 = parse_DIGIT(); if (result3 !== null) { result2 = []; while (result3 !== null) { result2.push(result3); result3 = parse_DIGIT(); } } else { result2 = null; } if (result2 !== null) { if (input.charCodeAt(pos) === 46) { result3 = "."; pos++; } else { result3 = null; if (reportFailures === 0) { matchFailed("\".\""); } } if (result3 !== null) { result5 = parse_DIGIT(); if (result5 !== null) { result4 = []; while (result5 !== null) { result4.push(result5); result5 = parse_DIGIT(); } } else { result4 = null; } if (result4 !== null) { result0 = [result0, result1, result2, result3, result4]; } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } if (result0 !== null) { result0 = (function(offset) { data.sip_version = input.substring(pos, offset); })(pos0); } if (result0 === null) { pos = pos0; } return result0; } function parse_INVITEm() { var result0; if (input.substr(pos, 6) === "INVITE") { result0 = "INVITE"; pos += 6; } else { result0 = null; if (reportFailures === 0) { matchFailed("\"INVITE\""); } } return result0; } function parse_ACKm() { var result0; if (input.substr(pos, 3) === "ACK") { result0 = "ACK"; pos += 3; } else { result0 = null; if (reportFailures === 0) { matchFailed("\"ACK\""); } } return result0; } function parse_OPTIONSm() { var result0; if (input.substr(pos, 7) === "OPTIONS") { result0 = "OPTIONS"; pos += 7; } else { result0 = null; if (reportFailures === 0) { matchFailed("\"OPTIONS\""); } } return result0; } function parse_BYEm() { var result0; if (input.substr(pos, 3) === "BYE") { result0 = "BYE"; pos += 3; } else { result0 = null; if (reportFailures === 0) { matchFailed("\"BYE\""); } } return result0; } function parse_CANCELm() { var result0; if (input.substr(pos, 6) === "CANCEL") { result0 = "CANCEL"; pos += 6; } else { result0 = null; if (reportFailures === 0) { matchFailed("\"CANCEL\""); } } return result0; } function parse_REGISTERm() { var result0; if (input.substr(pos, 8) === "REGISTER") { result0 = "REGISTER"; pos += 8; } else { result0 = null; if (reportFailures === 0) { matchFailed("\"REGISTER\""); } } return result0; } function parse_SUBSCRIBEm() { var result0; if (input.substr(pos, 9) === "SUBSCRIBE") { result0 = "SUBSCRIBE"; pos += 9; } else { result0 = null; if (reportFailures === 0) { matchFailed("\"SUBSCRIBE\""); } } return result0; } function parse_NOTIFYm() { var result0; if (input.substr(pos, 6) === "NOTIFY") { result0 = "NOTIFY"; pos += 6; } else { result0 = null; if (reportFailures === 0) { matchFailed("\"NOTIFY\""); } } return result0; } function parse_Method() { var result0; var pos0; pos0 = pos; result0 = parse_INVITEm(); if (result0 === null) { result0 = parse_ACKm(); if (result0 === null) { result0 = parse_OPTIONSm(); if (result0 === null) { result0 = parse_BYEm(); if (result0 === null) { result0 = parse_CANCELm(); if (result0 === null) { result0 = parse_REGISTERm(); if (result0 === null) { result0 = parse_SUBSCRIBEm(); if (result0 === null) { result0 = parse_NOTIFYm(); if (result0 === null) { result0 = parse_token(); } } } } } } } } if (result0 !== null) { result0 = (function(offset) { data.method = input.substring(pos, offset); return data.method; })(pos0); } if (result0 === null) { pos = pos0; } return result0; } function parse_Status_Line() { var result0, result1, result2, result3, result4; var pos0; pos0 = pos; result0 = parse_SIP_Version(); if (result0 !== null) { result1 = parse_SP(); if (result1 !== null) { result2 = parse_Status_Code(); if (result2 !== null) { result3 = parse_SP(); if (result3 !== null) { result4 = parse_Reason_Phrase(); if (result4 !== null) { result0 = [result0, result1, result2, result3, result4]; } else { result0 = null; pos = pos0; } } else { result0 = null; pos = pos0; } } else { result0 = null; pos = pos0; } } else { result0 = null; pos = pos0; } } else { result0 = null; pos = pos0; } return result0; } function parse_Status_Code() { var result0; var pos0; pos0 = pos; result0 = parse_extension_code(); if (result0 !== null) { result0 = (function(offset, status_code) { data.status_code = parseInt(status_code.join('')); })(pos0, result0); } if (result0 === null) { pos = pos0; } return result0; } function parse_extension_code() { var result0, result1, result2; var pos0; pos0 = pos; result0 = parse_DIGIT(); if (result0 !== null) { result1 = parse_DIGIT(); if (result1 !== null) { result2 = parse_DIGIT(); if (result2 !== null) { result0 = [result0, result1, result2]; } else { result0 = null; pos = pos0; } } else { result0 = null; pos = pos0; } } else { result0 = null; pos = pos0; } return result0; } function parse_Reason_Phrase() { var result0, result1; var pos0; pos0 = pos; result0 = []; result1 = parse_reserved(); if (result1 === null) { result1 = parse_unreserved(); if (result1 === null) { result1 = parse_escaped(); if (result1 === null) { result1 = parse_UTF8_NONASCII(); if (result1 === null) { result1 = parse_UTF8_CONT(); if (result1 === null) { result1 = parse_SP(); if (result1 === null) { result1 = parse_HTAB(); } } } } } } while (result1 !== null) { result0.push(result1); result1 = parse_reserved(); if (result1 === null) { result1 = parse_unreserved(); if (result1 === null) { result1 = parse_escaped(); if (result1 === null) { result1 = parse_UTF8_NONASCII(); if (result1 === null) { result1 = parse_UTF8_CONT(); if (result1 === null) { result1 = parse_SP(); if (result1 === null) { result1 = parse_HTAB(); } } } } } } } if (result0 !== null) { result0 = (function(offset) { data.reason_phrase = input.substring(pos, offset); })(pos0); } if (result0 === null) { pos = pos0; } return result0; } function parse_Allow_Events() { var result0, result1, result2, result3; var pos0, pos1; pos0 = pos; result0 = parse_event_type(); if (result0 !== null) { result1 = []; pos1 = pos; result2 = parse_COMMA(); if (result2 !== null) { result3 = parse_event_type(); if (result3 !== null) { result2 = [result2, result3]; } else { result2 = null; pos = pos1; } } else { result2 = null; pos = pos1; } while (result2 !== null) { result1.push(result2); pos1 = pos; result2 = parse_COMMA(); if (result2 !== null) { result3 = parse_event_type(); if (result3 !== null) { result2 = [result2, result3]; } else { result2 = null; pos = pos1; } } else { result2 = null; pos = pos1; } } if (result1 !== null) { result0 = [result0, result1]; } else { result0 = null; pos = pos0; } } else { result0 = null; pos = pos0; } return result0; } function parse_Call_ID() { var result0, result1, result2; var pos0, pos1, pos2; pos0 = pos; pos1 = pos; result0 = parse_word(); if (result0 !== null) { pos2 = pos; if (input.charCodeAt(pos) === 64) { result1 = "@"; pos++; } else { result1 = null; if (reportFailures === 0) { matchFailed("\"@\""); } } if (result1 !== null) { result2 = parse_word(); if (result2 !== null) { result1 = [result1, result2]; } else { result1 = null; pos = pos2; } } else { result1 = null; pos = pos2; } result1 = result1 !== null ? result1 : ""; if (result1 !== null) { result0 = [result0, result1]; } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } if (result0 !== null) { result0 = (function(offset) { data = input.substring(pos, offset); })(pos0); } if (result0 === null) { pos = pos0; } return result0; } function parse_Contact() { var result0, result1, result2, result3; var pos0, pos1, pos2; pos0 = pos; result0 = parse_STAR(); if (result0 === null) { pos1 = pos; result0 = parse_contact_param(); if (result0 !== null) { result1 = []; pos2 = pos; result2 = parse_COMMA(); if (result2 !== null) { result3 = parse_contact_param(); if (result3 !== null) { result2 = [result2, result3]; } else { result2 = null; pos = pos2; } } else { result2 = null; pos = pos2; } while (result2 !== null) { result1.push(result2); pos2 = pos; result2 = parse_COMMA(); if (result2 !== null) { result3 = parse_contact_param(); if (result3 !== null) { result2 = [result2, result3]; } else { result2 = null; pos = pos2; } } else { result2 = null; pos = pos2; } } if (result1 !== null) { result0 = [result0, result1]; } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } } if (result0 !== null) { result0 = (function(offset) { var idx, length; length = data.multi_header.length; for (idx = 0; idx < length; idx++) { if (data.multi_header[idx].parsed === null) { data = null; break; } } if (data !== null) { data = data.multi_header; } else { data = -1; }})(pos0); } if (result0 === null) { pos = pos0; } return result0; } function parse_contact_param() { var result0, result1, result2, result3; var pos0, pos1, pos2; pos0 = pos; pos1 = pos; result0 = parse_SIP_URI_noparams(); if (result0 === null) { result0 = parse_name_addr(); } if (result0 !== null) { result1 = []; pos2 = pos; result2 = parse_SEMI(); if (result2 !== null) { result3 = parse_contact_params(); if (result3 !== null) { result2 = [result2, result3]; } else { result2 = null; pos = pos2; } } else { result2 = null; pos = pos2; } while (result2 !== null) { result1.push(result2); pos2 = pos; result2 = parse_SEMI(); if (result2 !== null) { result3 = parse_contact_params(); if (result3 !== null) { result2 = [result2, result3]; } else { result2 = null; pos = pos2; } } else { result2 = null; pos = pos2; } } if (result1 !== null) { result0 = [result0, result1]; } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } if (result0 !== null) { result0 = (function(offset) { var header; if(!data.multi_header) data.multi_header = []; try { header = new JsSIP.NameAddrHeader(data.uri, data.display_name, data.params); delete data.uri; delete data.display_name; delete data.params; } catch(e) { header = null; } data.multi_header.push( { 'possition': pos, 'offset': offset, 'parsed': header });})(pos0); } if (result0 === null) { pos = pos0; } return result0; } function parse_name_addr() { var result0, result1, result2, result3; var pos0; pos0 = pos; result0 = parse_display_name(); result0 = result0 !== null ? result0 : ""; if (result0 !== null) { result1 = parse_LAQUOT(); if (result1 !== null) { result2 = parse_SIP_URI(); if (result2 !== null) { result3 = parse_RAQUOT(); if (result3 !== null) { result0 = [result0, result1, result2, result3]; } else { result0 = null; pos = pos0; } } else { result0 = null; pos = pos0; } } else { result0 = null; pos = pos0; } } else { result0 = null; pos = pos0; } return result0; } function parse_display_name() { var result0, result1, result2, result3; var pos0, pos1, pos2; pos0 = pos; pos1 = pos; result0 = parse_token(); if (result0 !== null) { result1 = []; pos2 = pos; result2 = parse_LWS(); if (result2 !== null) { result3 = parse_token(); if (result3 !== null) { result2 = [result2, result3]; } else { result2 = null; pos = pos2; } } else { result2 = null; pos = pos2; } while (result2 !== null) { result1.push(result2); pos2 = pos; result2 = parse_LWS(); if (result2 !== null) { result3 = parse_token(); if (result3 !== null) { result2 = [result2, result3]; } else { result2 = null; pos = pos2; } } else { result2 = null; pos = pos2; } } if (result1 !== null) { result0 = [result0, result1]; } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } if (result0 === null) { result0 = parse_quoted_string(); } if (result0 !== null) { result0 = (function(offset, display_name) { display_name = input.substring(pos, offset).trim(); if (display_name[0] === '\"') { display_name = display_name.substring(1, display_name.length-1); } data.display_name = display_name; })(pos0, result0); } if (result0 === null) { pos = pos0; } return result0; } function parse_contact_params() { var result0; result0 = parse_c_p_q(); if (result0 === null) { result0 = parse_c_p_expires(); if (result0 === null) { result0 = parse_generic_param(); } } return result0; } function parse_c_p_q() { var result0, result1, result2; var pos0, pos1; pos0 = pos; pos1 = pos; if (input.substr(pos, 1).toLowerCase() === "q") { result0 = input.substr(pos, 1); pos++; } else { result0 = null; if (reportFailures === 0) { matchFailed("\"q\""); } } if (result0 !== null) { result1 = parse_EQUAL(); if (result1 !== null) { result2 = parse_qvalue(); if (result2 !== null) { result0 = [result0, result1, result2]; } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } if (result0 !== null) { result0 = (function(offset, q) { if(!data.params) data.params = {}; data.params['q'] = q; })(pos0, result0[2]); } if (result0 === null) { pos = pos0; } return result0; } function parse_c_p_expires() { var result0, result1, result2; var pos0, pos1; pos0 = pos; pos1 = pos; if (input.substr(pos, 7).toLowerCase() === "expires") { result0 = input.substr(pos, 7); pos += 7; } else { result0 = null; if (reportFailures === 0) { matchFailed("\"expires\""); } } if (result0 !== null) { result1 = parse_EQUAL(); if (result1 !== null) { result2 = parse_delta_seconds(); if (result2 !== null) { result0 = [result0, result1, result2]; } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } if (result0 !== null) { result0 = (function(offset, expires) { if(!data.params) data.params = {}; data.params['expires'] = expires; })(pos0, result0[2]); } if (result0 === null) { pos = pos0; } return result0; } function parse_delta_seconds() { var result0, result1; var pos0; pos0 = pos; result1 = parse_DIGIT(); if (result1 !== null) { result0 = []; while (result1 !== null) { result0.push(result1); result1 = parse_DIGIT(); } } else { result0 = null; } if (result0 !== null) { result0 = (function(offset, delta_seconds) { return parseInt(delta_seconds.join('')); })(pos0, result0); } if (result0 === null) { pos = pos0; } return result0; } function parse_qvalue() { var result0, result1, result2, result3, result4; var pos0, pos1, pos2; pos0 = pos; pos1 = pos; if (input.charCodeAt(pos) === 48) { result0 = "0"; pos++; } else { result0 = null; if (reportFailures === 0) { matchFailed("\"0\""); } } if (result0 !== null) { pos2 = pos; if (input.charCodeAt(pos) === 46) { result1 = "."; pos++; } else { result1 = null; if (reportFailures === 0) { matchFailed("\".\""); } } if (result1 !== null) { result2 = parse_DIGIT(); result2 = result2 !== null ? result2 : ""; if (result2 !== null) { result3 = parse_DIGIT(); result3 = result3 !== null ? result3 : ""; if (result3 !== null) { result4 = parse_DIGIT(); result4 = result4 !== null ? result4 : ""; if (result4 !== null) { result1 = [result1, result2, result3, result4]; } else { result1 = null; pos = pos2; } } else { result1 = null; pos = pos2; } } else { result1 = null; pos = pos2; } } else { result1 = null; pos = pos2; } result1 = result1 !== null ? result1 : ""; if (result1 !== null) { result0 = [result0, result1]; } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } if (result0 !== null) { result0 = (function(offset) { return parseFloat(input.substring(pos, offset)); })(pos0); } if (result0 === null) { pos = pos0; } return result0; } function parse_generic_param() { var result0, result1, result2; var pos0, pos1, pos2; pos0 = pos; pos1 = pos; result0 = parse_token(); if (result0 !== null) { pos2 = pos; result1 = parse_EQUAL(); if (result1 !== null) { result2 = parse_gen_value(); if (result2 !== null) { result1 = [result1, result2]; } else { result1 = null; pos = pos2; } } else { result1 = null; pos = pos2; } result1 = result1 !== null ? result1 : ""; if (result1 !== null) { result0 = [result0, result1]; } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } if (result0 !== null) { result0 = (function(offset, param, value) { if(!data.params) data.params = {}; if (typeof value === 'undefined'){ value = undefined; } else { value = value[1]; } data.params[param.toLowerCase()] = value;})(pos0, result0[0], result0[1]); } if (result0 === null) { pos = pos0; } return result0; } function parse_gen_value() { var result0; result0 = parse_token(); if (result0 === null) { result0 = parse_host(); if (result0 === null) { result0 = parse_quoted_string(); } } return result0; } function parse_Content_Disposition() { var result0, result1, result2, result3; var pos0, pos1; pos0 = pos; result0 = parse_disp_type(); if (result0 !== null) { result1 = []; pos1 = pos; result2 = parse_SEMI(); if (result2 !== null) { result3 = parse_disp_param(); if (result3 !== null) { result2 = [result2, result3]; } else { result2 = null; pos = pos1; } } else { result2 = null; pos = pos1; } while (result2 !== null) { result1.push(result2); pos1 = pos; result2 = parse_SEMI(); if (result2 !== null) { result3 = parse_disp_param(); if (result3 !== null) { result2 = [result2, result3]; } else { result2 = null; pos = pos1; } } else { result2 = null; pos = pos1; } } if (result1 !== null) { result0 = [result0, result1]; } else { result0 = null; pos = pos0; } } else { result0 = null; pos = pos0; } return result0; } function parse_disp_type() { var result0; if (input.substr(pos, 6).toLowerCase() === "render") { result0 = input.substr(pos, 6); pos += 6; } else { result0 = null; if (reportFailures === 0) { matchFailed("\"render\""); } } if (result0 === null) { if (input.substr(pos, 7).toLowerCase() === "session") { result0 = input.substr(pos, 7); pos += 7; } else { result0 = null; if (reportFailures === 0) { matchFailed("\"session\""); } } if (result0 === null) { if (input.substr(pos, 4).toLowerCase() === "icon") { result0 = input.substr(pos, 4); pos += 4; } else { result0 = null; if (reportFailures === 0) { matchFailed("\"icon\""); } } if (result0 === null) { if (input.substr(pos, 5).toLowerCase() === "alert") { result0 = input.substr(pos, 5); pos += 5; } else { result0 = null; if (reportFailures === 0) { matchFailed("\"alert\""); } } if (result0 === null) { result0 = parse_token(); } } } } return result0; } function parse_disp_param() { var result0; result0 = parse_handling_param(); if (result0 === null) { result0 = parse_generic_param(); } return result0; } function parse_handling_param() { var result0, result1, result2; var pos0; pos0 = pos; if (input.substr(pos, 8).toLowerCase() === "handling") { result0 = input.substr(pos, 8); pos += 8; } else { result0 = null; if (reportFailures === 0) { matchFailed("\"handling\""); } } if (result0 !== null) { result1 = parse_EQUAL(); if (result1 !== null) { if (input.substr(pos, 8).toLowerCase() === "optional") { result2 = input.substr(pos, 8); pos += 8; } else { result2 = null; if (reportFailures === 0) { matchFailed("\"optional\""); } } if (result2 === null) { if (input.substr(pos, 8).toLowerCase() === "required") { result2 = input.substr(pos, 8); pos += 8; } else { result2 = null; if (reportFailures === 0) { matchFailed("\"required\""); } } if (result2 === null) { result2 = parse_token(); } } if (result2 !== null) { result0 = [result0, result1, result2]; } else { result0 = null; pos = pos0; } } else { result0 = null; pos = pos0; } } else { result0 = null; pos = pos0; } return result0; } function parse_Content_Encoding() { var result0, result1, result2, result3; var pos0, pos1; pos0 = pos; result0 = parse_token(); if (result0 !== null) { result1 = []; pos1 = pos; result2 = parse_COMMA(); if (result2 !== null) { result3 = parse_token(); if (result3 !== null) { result2 = [result2, result3]; } else { result2 = null; pos = pos1; } } else { result2 = null; pos = pos1; } while (result2 !== null) { result1.push(result2); pos1 = pos; result2 = parse_COMMA(); if (result2 !== null) { result3 = parse_token(); if (result3 !== null) { result2 = [result2, result3]; } else { result2 = null; pos = pos1; } } else { result2 = null; pos = pos1; } } if (result1 !== null) { result0 = [result0, result1]; } else { result0 = null; pos = pos0; } } else { result0 = null; pos = pos0; } return result0; } function parse_Content_Length() { var result0, result1; var pos0; pos0 = pos; result1 = parse_DIGIT(); if (result1 !== null) { result0 = []; while (result1 !== null) { result0.push(result1); result1 = parse_DIGIT(); } } else { result0 = null; } if (result0 !== null) { result0 = (function(offset, length) { data = parseInt(length.join('')); })(pos0, result0); } if (result0 === null) { pos = pos0; } return result0; } function parse_Content_Type() { var result0; var pos0; pos0 = pos; result0 = parse_media_type(); if (result0 !== null) { result0 = (function(offset) { data = input.substring(pos, offset); })(pos0); } if (result0 === null) { pos = pos0; } return result0; } function parse_media_type() { var result0, result1, result2, result3, result4, result5; var pos0, pos1; pos0 = pos; result0 = parse_m_type(); if (result0 !== null) { result1 = parse_SLASH(); if (result1 !== null) { result2 = parse_m_subtype(); if (result2 !== null) { result3 = []; pos1 = pos; result4 = parse_SEMI(); if (result4 !== null) { result5 = parse_m_parameter(); if (result5 !== null) { result4 = [result4, result5]; } else { result4 = null; pos = pos1; } } else { result4 = null; pos = pos1; } while (result4 !== null) { result3.push(result4); pos1 = pos; result4 = parse_SEMI(); if (result4 !== null) { result5 = parse_m_parameter(); if (result5 !== null) { result4 = [result4, result5]; } else { result4 = null; pos = pos1; } } else { result4 = null; pos = pos1; } } if (result3 !== null) { result0 = [result0, result1, result2, result3]; } else { result0 = null; pos = pos0; } } else { result0 = null; pos = pos0; } } else { result0 = null; pos = pos0; } } else { result0 = null; pos = pos0; } return result0; } function parse_m_type() { var result0; result0 = parse_discrete_type(); if (result0 === null) { result0 = parse_composite_type(); } return result0; } function parse_discrete_type() { var result0; if (input.substr(pos, 4).toLowerCase() === "text") { result0 = input.substr(pos, 4); pos += 4; } else { result0 = null; if (reportFailures === 0) { matchFailed("\"text\""); } } if (result0 === null) { if (input.substr(pos, 5).toLowerCase() === "image") { result0 = input.substr(pos, 5); pos += 5; } else { result0 = null; if (reportFailures === 0) { matchFailed("\"image\""); } } if (result0 === null) { if (input.substr(pos, 5).toLowerCase() === "audio") { result0 = input.substr(pos, 5); pos += 5; } else { result0 = null; if (reportFailures === 0) { matchFailed("\"audio\""); } } if (result0 === null) { if (input.substr(pos, 5).toLowerCase() === "video") { result0 = input.substr(pos, 5); pos += 5; } else { result0 = null; if (reportFailures === 0) { matchFailed("\"video\""); } } if (result0 === null) { if (input.substr(pos, 11).toLowerCase() === "application") { result0 = input.substr(pos, 11); pos += 11; } else { result0 = null; if (reportFailures === 0) { matchFailed("\"application\""); } } if (result0 === null) { result0 = parse_extension_token(); } } } } } return result0; } function parse_composite_type() { var result0; if (input.substr(pos, 7).toLowerCase() === "message") { result0 = input.substr(pos, 7); pos += 7; } else { result0 = null; if (reportFailures === 0) { matchFailed("\"message\""); } } if (result0 === null) { if (input.substr(pos, 9).toLowerCase() === "multipart") { result0 = input.substr(pos, 9); pos += 9; } else { result0 = null; if (reportFailures === 0) { matchFailed("\"multipart\""); } } if (result0 === null) { result0 = parse_extension_token(); } } return result0; } function parse_extension_token() { var result0; result0 = parse_token(); if (result0 === null) { result0 = parse_x_token(); } return result0; } function parse_x_token() { var result0, result1; var pos0; pos0 = pos; if (input.substr(pos, 2).toLowerCase() === "x-") { result0 = input.substr(pos, 2); pos += 2; } else { result0 = null; if (reportFailures === 0) { matchFailed("\"x-\""); } } if (result0 !== null) { result1 = parse_token(); if (result1 !== null) { result0 = [result0, result1]; } else { result0 = null; pos = pos0; } } else { result0 = null; pos = pos0; } return result0; } function parse_m_subtype() { var result0; result0 = parse_extension_token(); if (result0 === null) { result0 = parse_token(); } return result0; } function parse_m_parameter() { var result0, result1, result2; var pos0; pos0 = pos; result0 = parse_token(); if (result0 !== null) { result1 = parse_EQUAL(); if (result1 !== null) { result2 = parse_m_value(); if (result2 !== null) { result0 = [result0, result1, result2]; } else { result0 = null; pos = pos0; } } else { result0 = null; pos = pos0; } } else { result0 = null; pos = pos0; } return result0; } function parse_m_value() { var result0; result0 = parse_token(); if (result0 === null) { result0 = parse_quoted_string(); } return result0; } function parse_CSeq() { var result0, result1, result2; var pos0; pos0 = pos; result0 = parse_CSeq_value(); if (result0 !== null) { result1 = parse_LWS(); if (result1 !== null) { result2 = parse_Method(); if (result2 !== null) { result0 = [result0, result1, result2]; } else { result0 = null; pos = pos0; } } else { result0 = null; pos = pos0; } } else { result0 = null; pos = pos0; } return result0; } function parse_CSeq_value() { var result0, result1; var pos0; pos0 = pos; result1 = parse_DIGIT(); if (result1 !== null) { result0 = []; while (result1 !== null) { result0.push(result1); result1 = parse_DIGIT(); } } else { result0 = null; } if (result0 !== null) { result0 = (function(offset, cseq_value) { data.value=parseInt(cseq_value.join('')); })(pos0, result0); } if (result0 === null) { pos = pos0; } return result0; } function parse_Expires() { var result0; var pos0; pos0 = pos; result0 = parse_delta_seconds(); if (result0 !== null) { result0 = (function(offset, expires) {data = expires; })(pos0, result0); } if (result0 === null) { pos = pos0; } return result0; } function parse_Event() { var result0, result1, result2, result3; var pos0, pos1, pos2; pos0 = pos; pos1 = pos; result0 = parse_event_type(); if (result0 !== null) { result1 = []; pos2 = pos; result2 = parse_SEMI(); if (result2 !== null) { result3 = parse_generic_param(); if (result3 !== null) { result2 = [result2, result3]; } else { result2 = null; pos = pos2; } } else { result2 = null; pos = pos2; } while (result2 !== null) { result1.push(result2); pos2 = pos; result2 = parse_SEMI(); if (result2 !== null) { result3 = parse_generic_param(); if (result3 !== null) { result2 = [result2, result3]; } else { result2 = null; pos = pos2; } } else { result2 = null; pos = pos2; } } if (result1 !== null) { result0 = [result0, result1]; } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } if (result0 !== null) { result0 = (function(offset, event_type) { data.event = event_type.join('').toLowerCase(); })(pos0, result0[0]); } if (result0 === null) { pos = pos0; } return result0; } function parse_event_type() { var result0, result1, result2, result3; var pos0, pos1; pos0 = pos; result0 = parse_token_nodot(); if (result0 !== null) { result1 = []; pos1 = pos; if (input.charCodeAt(pos) === 46) { result2 = "."; pos++; } else { result2 = null; if (reportFailures === 0) { matchFailed("\".\""); } } if (result2 !== null) { result3 = parse_token_nodot(); if (result3 !== null) { result2 = [result2, result3]; } else { result2 = null; pos = pos1; } } else { result2 = null; pos = pos1; } while (result2 !== null) { result1.push(result2); pos1 = pos; if (input.charCodeAt(pos) === 46) { result2 = "."; pos++; } else { result2 = null; if (reportFailures === 0) { matchFailed("\".\""); } } if (result2 !== null) { result3 = parse_token_nodot(); if (result3 !== null) { result2 = [result2, result3]; } else { result2 = null; pos = pos1; } } else { result2 = null; pos = pos1; } } if (result1 !== null) { result0 = [result0, result1]; } else { result0 = null; pos = pos0; } } else { result0 = null; pos = pos0; } return result0; } function parse_From() { var result0, result1, result2, result3; var pos0, pos1, pos2; pos0 = pos; pos1 = pos; result0 = parse_SIP_URI_noparams(); if (result0 === null) { result0 = parse_name_addr(); } if (result0 !== null) { result1 = []; pos2 = pos; result2 = parse_SEMI(); if (result2 !== null) { result3 = parse_from_param(); if (result3 !== null) { result2 = [result2, result3]; } else { result2 = null; pos = pos2; } } else { result2 = null; pos = pos2; } while (result2 !== null) { result1.push(result2); pos2 = pos; result2 = parse_SEMI(); if (result2 !== null) { result3 = parse_from_param(); if (result3 !== null) { result2 = [result2, result3]; } else { result2 = null; pos = pos2; } } else { result2 = null; pos = pos2; } } if (result1 !== null) { result0 = [result0, result1]; } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } if (result0 !== null) { result0 = (function(offset) { var tag = data.tag; try { data = new JsSIP.NameAddrHeader(data.uri, data.display_name, data.params); if (tag) {data.setParam('tag',tag)} } catch(e) { data = -1; }})(pos0); } if (result0 === null) { pos = pos0; } return result0; } function parse_from_param() { var result0; result0 = parse_tag_param(); if (result0 === null) { result0 = parse_generic_param(); } return result0; } function parse_tag_param() { var result0, result1, result2; var pos0, pos1; pos0 = pos; pos1 = pos; if (input.substr(pos, 3).toLowerCase() === "tag") { result0 = input.substr(pos, 3); pos += 3; } else { result0 = null; if (reportFailures === 0) { matchFailed("\"tag\""); } } if (result0 !== null) { result1 = parse_EQUAL(); if (result1 !== null) { result2 = parse_token(); if (result2 !== null) { result0 = [result0, result1, result2]; } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } if (result0 !== null) { result0 = (function(offset, tag) {data.tag = tag; })(pos0, result0[2]); } if (result0 === null) { pos = pos0; } return result0; } function parse_Max_Forwards() { var result0, result1; var pos0; pos0 = pos; result1 = parse_DIGIT(); if (result1 !== null) { result0 = []; while (result1 !== null) { result0.push(result1); result1 = parse_DIGIT(); } } else { result0 = null; } if (result0 !== null) { result0 = (function(offset, forwards) { data = parseInt(forwards.join('')); })(pos0, result0); } if (result0 === null) { pos = pos0; } return result0; } function parse_Min_Expires() { var result0; var pos0; pos0 = pos; result0 = parse_delta_seconds(); if (result0 !== null) { result0 = (function(offset, min_expires) {data = min_expires; })(pos0, result0); } if (result0 === null) { pos = pos0; } return result0; } function parse_Name_Addr_Header() { var result0, result1, result2, result3, result4, result5, result6; var pos0, pos1, pos2; pos0 = pos; pos1 = pos; result0 = []; result1 = parse_display_name(); while (result1 !== null) { result0.push(result1); result1 = parse_display_name(); } if (result0 !== null) { result1 = parse_LAQUOT(); if (result1 !== null) { result2 = parse_SIP_URI(); if (result2 !== null) { result3 = parse_RAQUOT(); if (result3 !== null) { result4 = []; pos2 = pos; result5 = parse_SEMI(); if (result5 !== null) { result6 = parse_generic_param(); if (result6 !== null) { result5 = [result5, result6]; } else { result5 = null; pos = pos2; } } else { result5 = null; pos = pos2; } while (result5 !== null) { result4.push(result5); pos2 = pos; result5 = parse_SEMI(); if (result5 !== null) { result6 = parse_generic_param(); if (result6 !== null) { result5 = [result5, result6]; } else { result5 = null; pos = pos2; } } else { result5 = null; pos = pos2; } } if (result4 !== null) { result0 = [result0, result1, result2, result3, result4]; } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } if (result0 !== null) { result0 = (function(offset) { try { data = new JsSIP.NameAddrHeader(data.uri, data.display_name, data.params); } catch(e) { data = -1; }})(pos0); } if (result0 === null) { pos = pos0; } return result0; } function parse_Proxy_Authenticate() { var result0; result0 = parse_challenge(); return result0; } function parse_challenge() { var result0, result1, result2, result3, result4, result5; var pos0, pos1; pos0 = pos; if (input.substr(pos, 6).toLowerCase() === "digest") { result0 = input.substr(pos, 6); pos += 6; } else { result0 = null; if (reportFailures === 0) { matchFailed("\"Digest\""); } } if (result0 !== null) { result1 = parse_LWS(); if (result1 !== null) { result2 = parse_digest_cln(); if (result2 !== null) { result3 = []; pos1 = pos; result4 = parse_COMMA(); if (result4 !== null) { result5 = parse_digest_cln(); if (result5 !== null) { result4 = [result4, result5]; } else { result4 = null; pos = pos1; } } else { result4 = null; pos = pos1; } while (result4 !== null) { result3.push(result4); pos1 = pos; result4 = parse_COMMA(); if (result4 !== null) { result5 = parse_digest_cln(); if (result5 !== null) { result4 = [result4, result5]; } else { result4 = null; pos = pos1; } } else { result4 = null; pos = pos1; } } if (result3 !== null) { result0 = [result0, result1, result2, result3]; } else { result0 = null; pos = pos0; } } else { result0 = null; pos = pos0; } } else { result0 = null; pos = pos0; } } else { result0 = null; pos = pos0; } if (result0 === null) { result0 = parse_other_challenge(); } return result0; } function parse_other_challenge() { var result0, result1, result2, result3, result4, result5; var pos0, pos1; pos0 = pos; result0 = parse_token(); if (result0 !== null) { result1 = parse_LWS(); if (result1 !== null) { result2 = parse_auth_param(); if (result2 !== null) { result3 = []; pos1 = pos; result4 = parse_COMMA(); if (result4 !== null) { result5 = parse_auth_param(); if (result5 !== null) { result4 = [result4, result5]; } else { result4 = null; pos = pos1; } } else { result4 = null; pos = pos1; } while (result4 !== null) { result3.push(result4); pos1 = pos; result4 = parse_COMMA(); if (result4 !== null) { result5 = parse_auth_param(); if (result5 !== null) { result4 = [result4, result5]; } else { result4 = null; pos = pos1; } } else { result4 = null; pos = pos1; } } if (result3 !== null) { result0 = [result0, result1, result2, result3]; } else { result0 = null; pos = pos0; } } else { result0 = null; pos = pos0; } } else { result0 = null; pos = pos0; } } else { result0 = null; pos = pos0; } return result0; } function parse_auth_param() { var result0, result1, result2; var pos0; pos0 = pos; result0 = parse_token(); if (result0 !== null) { result1 = parse_EQUAL(); if (result1 !== null) { result2 = parse_token(); if (result2 === null) { result2 = parse_quoted_string(); } if (result2 !== null) { result0 = [result0, result1, result2]; } else { result0 = null; pos = pos0; } } else { result0 = null; pos = pos0; } } else { result0 = null; pos = pos0; } return result0; } function parse_digest_cln() { var result0; result0 = parse_realm(); if (result0 === null) { result0 = parse_domain(); if (result0 === null) { result0 = parse_nonce(); if (result0 === null) { result0 = parse_opaque(); if (result0 === null) { result0 = parse_stale(); if (result0 === null) { result0 = parse_algorithm(); if (result0 === null) { result0 = parse_qop_options(); if (result0 === null) { result0 = parse_auth_param(); } } } } } } } return result0; } function parse_realm() { var result0, result1, result2; var pos0; pos0 = pos; if (input.substr(pos, 5).toLowerCase() === "realm") { result0 = input.substr(pos, 5); pos += 5; } else { result0 = null; if (reportFailures === 0) { matchFailed("\"realm\""); } } if (result0 !== null) { result1 = parse_EQUAL(); if (result1 !== null) { result2 = parse_realm_value(); if (result2 !== null) { result0 = [result0, result1, result2]; } else { result0 = null; pos = pos0; } } else { result0 = null; pos = pos0; } } else { result0 = null; pos = pos0; } return result0; } function parse_realm_value() { var result0; var pos0; pos0 = pos; result0 = parse_quoted_string_clean(); if (result0 !== null) { result0 = (function(offset, realm) { data.realm = realm; })(pos0, result0); } if (result0 === null) { pos = pos0; } return result0; } function parse_domain() { var result0, result1, result2, result3, result4, result5, result6; var pos0, pos1; pos0 = pos; if (input.substr(pos, 6).toLowerCase() === "domain") { result0 = input.substr(pos, 6); pos += 6; } else { result0 = null; if (reportFailures === 0) { matchFailed("\"domain\""); } } if (result0 !== null) { result1 = parse_EQUAL(); if (result1 !== null) { result2 = parse_LDQUOT(); if (result2 !== null) { result3 = parse_URI(); if (result3 !== null) { result4 = []; pos1 = pos; result6 = parse_SP(); if (result6 !== null) { result5 = []; while (result6 !== null) { result5.push(result6); result6 = parse_SP(); } } else { result5 = null; } if (result5 !== null) { result6 = parse_URI(); if (result6 !== null) { result5 = [result5, result6]; } else { result5 = null; pos = pos1; } } else { result5 = null; pos = pos1; } while (result5 !== null) { result4.push(result5); pos1 = pos; result6 = parse_SP(); if (result6 !== null) { result5 = []; while (result6 !== null) { result5.push(result6); result6 = parse_SP(); } } else { result5 = null; } if (result5 !== null) { result6 = parse_URI(); if (result6 !== null) { result5 = [result5, result6]; } else { result5 = null; pos = pos1; } } else { result5 = null; pos = pos1; } } if (result4 !== null) { result5 = parse_RDQUOT(); if (result5 !== null) { result0 = [result0, result1, result2, result3, result4, result5]; } else { result0 = null; pos = pos0; } } else { result0 = null; pos = pos0; } } else { result0 = null; pos = pos0; } } else { result0 = null; pos = pos0; } } else { result0 = null; pos = pos0; } } else { result0 = null; pos = pos0; } return result0; } function parse_URI() { var result0; result0 = parse_absoluteURI(); if (result0 === null) { result0 = parse_abs_path(); } return result0; } function parse_nonce() { var result0, result1, result2; var pos0; pos0 = pos; if (input.substr(pos, 5).toLowerCase() === "nonce") { result0 = input.substr(pos, 5); pos += 5; } else { result0 = null; if (reportFailures === 0) { matchFailed("\"nonce\""); } } if (result0 !== null) { result1 = parse_EQUAL(); if (result1 !== null) { result2 = parse_nonce_value(); if (result2 !== null) { result0 = [result0, result1, result2]; } else { result0 = null; pos = pos0; } } else { result0 = null; pos = pos0; } } else { result0 = null; pos = pos0; } return result0; } function parse_nonce_value() { var result0; var pos0; pos0 = pos; result0 = parse_quoted_string_clean(); if (result0 !== null) { result0 = (function(offset, nonce) { data.nonce=nonce; })(pos0, result0); } if (result0 === null) { pos = pos0; } return result0; } function parse_opaque() { var result0, result1, result2; var pos0, pos1; pos0 = pos; pos1 = pos; if (input.substr(pos, 6).toLowerCase() === "opaque") { result0 = input.substr(pos, 6); pos += 6; } else { result0 = null; if (reportFailures === 0) { matchFailed("\"opaque\""); } } if (result0 !== null) { result1 = parse_EQUAL(); if (result1 !== null) { result2 = parse_quoted_string_clean(); if (result2 !== null) { result0 = [result0, result1, result2]; } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } if (result0 !== null) { result0 = (function(offset, opaque) { data.opaque=opaque; })(pos0, result0[2]); } if (result0 === null) { pos = pos0; } return result0; } function parse_stale() { var result0, result1, result2; var pos0, pos1; pos0 = pos; if (input.substr(pos, 5).toLowerCase() === "stale") { result0 = input.substr(pos, 5); pos += 5; } else { result0 = null; if (reportFailures === 0) { matchFailed("\"stale\""); } } if (result0 !== null) { result1 = parse_EQUAL(); if (result1 !== null) { pos1 = pos; if (input.substr(pos, 4).toLowerCase() === "true") { result2 = input.substr(pos, 4); pos += 4; } else { result2 = null; if (reportFailures === 0) { matchFailed("\"true\""); } } if (result2 !== null) { result2 = (function(offset) { data.stale=true; })(pos1); } if (result2 === null) { pos = pos1; } if (result2 === null) { pos1 = pos; if (input.substr(pos, 5).toLowerCase() === "false") { result2 = input.substr(pos, 5); pos += 5; } else { result2 = null; if (reportFailures === 0) { matchFailed("\"false\""); } } if (result2 !== null) { result2 = (function(offset) { data.stale=false; })(pos1); } if (result2 === null) { pos = pos1; } } if (result2 !== null) { result0 = [result0, result1, result2]; } else { result0 = null; pos = pos0; } } else { result0 = null; pos = pos0; } } else { result0 = null; pos = pos0; } return result0; } function parse_algorithm() { var result0, result1, result2; var pos0, pos1; pos0 = pos; pos1 = pos; if (input.substr(pos, 9).toLowerCase() === "algorithm") { result0 = input.substr(pos, 9); pos += 9; } else { result0 = null; if (reportFailures === 0) { matchFailed("\"algorithm\""); } } if (result0 !== null) { result1 = parse_EQUAL(); if (result1 !== null) { if (input.substr(pos, 3).toLowerCase() === "md5") { result2 = input.substr(pos, 3); pos += 3; } else { result2 = null; if (reportFailures === 0) { matchFailed("\"MD5\""); } } if (result2 === null) { if (input.substr(pos, 8).toLowerCase() === "md5-sess") { result2 = input.substr(pos, 8); pos += 8; } else { result2 = null; if (reportFailures === 0) { matchFailed("\"MD5-sess\""); } } if (result2 === null) { result2 = parse_token(); } } if (result2 !== null) { result0 = [result0, result1, result2]; } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } if (result0 !== null) { result0 = (function(offset, algorithm) { data.algorithm=algorithm.toUpperCase(); })(pos0, result0[2]); } if (result0 === null) { pos = pos0; } return result0; } function parse_qop_options() { var result0, result1, result2, result3, result4, result5, result6; var pos0, pos1, pos2; pos0 = pos; if (input.substr(pos, 3).toLowerCase() === "qop") { result0 = input.substr(pos, 3); pos += 3; } else { result0 = null; if (reportFailures === 0) { matchFailed("\"qop\""); } } if (result0 !== null) { result1 = parse_EQUAL(); if (result1 !== null) { result2 = parse_LDQUOT(); if (result2 !== null) { pos1 = pos; result3 = parse_qop_value(); if (result3 !== null) { result4 = []; pos2 = pos; if (input.charCodeAt(pos) === 44) { result5 = ","; pos++; } else { result5 = null; if (reportFailures === 0) { matchFailed("\",\""); } } if (result5 !== null) { result6 = parse_qop_value(); if (result6 !== null) { result5 = [result5, result6]; } else { result5 = null; pos = pos2; } } else { result5 = null; pos = pos2; } while (result5 !== null) { result4.push(result5); pos2 = pos; if (input.charCodeAt(pos) === 44) { result5 = ","; pos++; } else { result5 = null; if (reportFailures === 0) { matchFailed("\",\""); } } if (result5 !== null) { result6 = parse_qop_value(); if (result6 !== null) { result5 = [result5, result6]; } else { result5 = null; pos = pos2; } } else { result5 = null; pos = pos2; } } if (result4 !== null) { result3 = [result3, result4]; } else { result3 = null; pos = pos1; } } else { result3 = null; pos = pos1; } if (result3 !== null) { result4 = parse_RDQUOT(); if (result4 !== null) { result0 = [result0, result1, result2, result3, result4]; } else { result0 = null; pos = pos0; } } else { result0 = null; pos = pos0; } } else { result0 = null; pos = pos0; } } else { result0 = null; pos = pos0; } } else { result0 = null; pos = pos0; } return result0; } function parse_qop_value() { var result0; var pos0; pos0 = pos; if (input.substr(pos, 8).toLowerCase() === "auth-int") { result0 = input.substr(pos, 8); pos += 8; } else { result0 = null; if (reportFailures === 0) { matchFailed("\"auth-int\""); } } if (result0 === null) { if (input.substr(pos, 4).toLowerCase() === "auth") { result0 = input.substr(pos, 4); pos += 4; } else { result0 = null; if (reportFailures === 0) { matchFailed("\"auth\""); } } if (result0 === null) { result0 = parse_token(); } } if (result0 !== null) { result0 = (function(offset, qop_value) { data.qop || (data.qop=[]); data.qop.push(qop_value.toLowerCase()); })(pos0, result0); } if (result0 === null) { pos = pos0; } return result0; } function parse_Proxy_Require() { var result0, result1, result2, result3; var pos0, pos1; pos0 = pos; result0 = parse_token(); if (result0 !== null) { result1 = []; pos1 = pos; result2 = parse_COMMA(); if (result2 !== null) { result3 = parse_token(); if (result3 !== null) { result2 = [result2, result3]; } else { result2 = null; pos = pos1; } } else { result2 = null; pos = pos1; } while (result2 !== null) { result1.push(result2); pos1 = pos; result2 = parse_COMMA(); if (result2 !== null) { result3 = parse_token(); if (result3 !== null) { result2 = [result2, result3]; } else { result2 = null; pos = pos1; } } else { result2 = null; pos = pos1; } } if (result1 !== null) { result0 = [result0, result1]; } else { result0 = null; pos = pos0; } } else { result0 = null; pos = pos0; } return result0; } function parse_Record_Route() { var result0, result1, result2, result3; var pos0, pos1, pos2; pos0 = pos; pos1 = pos; result0 = parse_rec_route(); if (result0 !== null) { result1 = []; pos2 = pos; result2 = parse_COMMA(); if (result2 !== null) { result3 = parse_rec_route(); if (result3 !== null) { result2 = [result2, result3]; } else { result2 = null; pos = pos2; } } else { result2 = null; pos = pos2; } while (result2 !== null) { result1.push(result2); pos2 = pos; result2 = parse_COMMA(); if (result2 !== null) { result3 = parse_rec_route(); if (result3 !== null) { result2 = [result2, result3]; } else { result2 = null; pos = pos2; } } else { result2 = null; pos = pos2; } } if (result1 !== null) { result0 = [result0, result1]; } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } if (result0 !== null) { result0 = (function(offset) { var idx, length; length = data.multi_header.length; for (idx = 0; idx < length; idx++) { if (data.multi_header[idx].parsed === null) { data = null; break; } } if (data !== null) { data = data.multi_header; } else { data = -1; }})(pos0); } if (result0 === null) { pos = pos0; } return result0; } function parse_rec_route() { var result0, result1, result2, result3; var pos0, pos1, pos2; pos0 = pos; pos1 = pos; result0 = parse_name_addr(); if (result0 !== null) { result1 = []; pos2 = pos; result2 = parse_SEMI(); if (result2 !== null) { result3 = parse_generic_param(); if (result3 !== null) { result2 = [result2, result3]; } else { result2 = null; pos = pos2; } } else { result2 = null; pos = pos2; } while (result2 !== null) { result1.push(result2); pos2 = pos; result2 = parse_SEMI(); if (result2 !== null) { result3 = parse_generic_param(); if (result3 !== null) { result2 = [result2, result3]; } else { result2 = null; pos = pos2; } } else { result2 = null; pos = pos2; } } if (result1 !== null) { result0 = [result0, result1]; } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } if (result0 !== null) { result0 = (function(offset) { var header; if(!data.multi_header) data.multi_header = []; try { header = new JsSIP.NameAddrHeader(data.uri, data.display_name, data.params); delete data.uri; delete data.display_name; delete data.params; } catch(e) { header = null; } data.multi_header.push( { 'possition': pos, 'offset': offset, 'parsed': header });})(pos0); } if (result0 === null) { pos = pos0; } return result0; } function parse_Require() { var result0, result1, result2, result3; var pos0, pos1; pos0 = pos; result0 = parse_token(); if (result0 !== null) { result1 = []; pos1 = pos; result2 = parse_COMMA(); if (result2 !== null) { result3 = parse_token(); if (result3 !== null) { result2 = [result2, result3]; } else { result2 = null; pos = pos1; } } else { result2 = null; pos = pos1; } while (result2 !== null) { result1.push(result2); pos1 = pos; result2 = parse_COMMA(); if (result2 !== null) { result3 = parse_token(); if (result3 !== null) { result2 = [result2, result3]; } else { result2 = null; pos = pos1; } } else { result2 = null; pos = pos1; } } if (result1 !== null) { result0 = [result0, result1]; } else { result0 = null; pos = pos0; } } else { result0 = null; pos = pos0; } return result0; } function parse_Route() { var result0, result1, result2, result3; var pos0, pos1; pos0 = pos; result0 = parse_route_param(); if (result0 !== null) { result1 = []; pos1 = pos; result2 = parse_COMMA(); if (result2 !== null) { result3 = parse_route_param(); if (result3 !== null) { result2 = [result2, result3]; } else { result2 = null; pos = pos1; } } else { result2 = null; pos = pos1; } while (result2 !== null) { result1.push(result2); pos1 = pos; result2 = parse_COMMA(); if (result2 !== null) { result3 = parse_route_param(); if (result3 !== null) { result2 = [result2, result3]; } else { result2 = null; pos = pos1; } } else { result2 = null; pos = pos1; } } if (result1 !== null) { result0 = [result0, result1]; } else { result0 = null; pos = pos0; } } else { result0 = null; pos = pos0; } return result0; } function parse_route_param() { var result0, result1, result2, result3; var pos0, pos1; pos0 = pos; result0 = parse_name_addr(); if (result0 !== null) { result1 = []; pos1 = pos; result2 = parse_SEMI(); if (result2 !== null) { result3 = parse_generic_param(); if (result3 !== null) { result2 = [result2, result3]; } else { result2 = null; pos = pos1; } } else { result2 = null; pos = pos1; } while (result2 !== null) { result1.push(result2); pos1 = pos; result2 = parse_SEMI(); if (result2 !== null) { result3 = parse_generic_param(); if (result3 !== null) { result2 = [result2, result3]; } else { result2 = null; pos = pos1; } } else { result2 = null; pos = pos1; } } if (result1 !== null) { result0 = [result0, result1]; } else { result0 = null; pos = pos0; } } else { result0 = null; pos = pos0; } return result0; } function parse_Subscription_State() { var result0, result1, result2, result3; var pos0, pos1; pos0 = pos; result0 = parse_substate_value(); if (result0 !== null) { result1 = []; pos1 = pos; result2 = parse_SEMI(); if (result2 !== null) { result3 = parse_subexp_params(); if (result3 !== null) { result2 = [result2, result3]; } else { result2 = null; pos = pos1; } } else { result2 = null; pos = pos1; } while (result2 !== null) { result1.push(result2); pos1 = pos; result2 = parse_SEMI(); if (result2 !== null) { result3 = parse_subexp_params(); if (result3 !== null) { result2 = [result2, result3]; } else { result2 = null; pos = pos1; } } else { result2 = null; pos = pos1; } } if (result1 !== null) { result0 = [result0, result1]; } else { result0 = null; pos = pos0; } } else { result0 = null; pos = pos0; } return result0; } function parse_substate_value() { var result0; var pos0; pos0 = pos; if (input.substr(pos, 6).toLowerCase() === "active") { result0 = input.substr(pos, 6); pos += 6; } else { result0 = null; if (reportFailures === 0) { matchFailed("\"active\""); } } if (result0 === null) { if (input.substr(pos, 7).toLowerCase() === "pending") { result0 = input.substr(pos, 7); pos += 7; } else { result0 = null; if (reportFailures === 0) { matchFailed("\"pending\""); } } if (result0 === null) { if (input.substr(pos, 10).toLowerCase() === "terminated") { result0 = input.substr(pos, 10); pos += 10; } else { result0 = null; if (reportFailures === 0) { matchFailed("\"terminated\""); } } if (result0 === null) { result0 = parse_token(); } } } if (result0 !== null) { result0 = (function(offset) { data.state = input.substring(pos, offset); })(pos0); } if (result0 === null) { pos = pos0; } return result0; } function parse_subexp_params() { var result0, result1, result2; var pos0, pos1; pos0 = pos; pos1 = pos; if (input.substr(pos, 6).toLowerCase() === "reason") { result0 = input.substr(pos, 6); pos += 6; } else { result0 = null; if (reportFailures === 0) { matchFailed("\"reason\""); } } if (result0 !== null) { result1 = parse_EQUAL(); if (result1 !== null) { result2 = parse_event_reason_value(); if (result2 !== null) { result0 = [result0, result1, result2]; } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } if (result0 !== null) { result0 = (function(offset, reason) { if (typeof reason !== 'undefined') data.reason = reason; })(pos0, result0[2]); } if (result0 === null) { pos = pos0; } if (result0 === null) { pos0 = pos; pos1 = pos; if (input.substr(pos, 7).toLowerCase() === "expires") { result0 = input.substr(pos, 7); pos += 7; } else { result0 = null; if (reportFailures === 0) { matchFailed("\"expires\""); } } if (result0 !== null) { result1 = parse_EQUAL(); if (result1 !== null) { result2 = parse_delta_seconds(); if (result2 !== null) { result0 = [result0, result1, result2]; } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } if (result0 !== null) { result0 = (function(offset, expires) { if (typeof expires !== 'undefined') data.expires = expires; })(pos0, result0[2]); } if (result0 === null) { pos = pos0; } if (result0 === null) { pos0 = pos; pos1 = pos; if (input.substr(pos, 11).toLowerCase() === "retry_after") { result0 = input.substr(pos, 11); pos += 11; } else { result0 = null; if (reportFailures === 0) { matchFailed("\"retry_after\""); } } if (result0 !== null) { result1 = parse_EQUAL(); if (result1 !== null) { result2 = parse_delta_seconds(); if (result2 !== null) { result0 = [result0, result1, result2]; } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } if (result0 !== null) { result0 = (function(offset, retry_after) { if (typeof retry_after !== 'undefined') data.retry_after = retry_after; })(pos0, result0[2]); } if (result0 === null) { pos = pos0; } if (result0 === null) { result0 = parse_generic_param(); } } } return result0; } function parse_event_reason_value() { var result0; if (input.substr(pos, 11).toLowerCase() === "deactivated") { result0 = input.substr(pos, 11); pos += 11; } else { result0 = null; if (reportFailures === 0) { matchFailed("\"deactivated\""); } } if (result0 === null) { if (input.substr(pos, 9).toLowerCase() === "probation") { result0 = input.substr(pos, 9); pos += 9; } else { result0 = null; if (reportFailures === 0) { matchFailed("\"probation\""); } } if (result0 === null) { if (input.substr(pos, 8).toLowerCase() === "rejected") { result0 = input.substr(pos, 8); pos += 8; } else { result0 = null; if (reportFailures === 0) { matchFailed("\"rejected\""); } } if (result0 === null) { if (input.substr(pos, 7).toLowerCase() === "timeout") { result0 = input.substr(pos, 7); pos += 7; } else { result0 = null; if (reportFailures === 0) { matchFailed("\"timeout\""); } } if (result0 === null) { if (input.substr(pos, 6).toLowerCase() === "giveup") { result0 = input.substr(pos, 6); pos += 6; } else { result0 = null; if (reportFailures === 0) { matchFailed("\"giveup\""); } } if (result0 === null) { if (input.substr(pos, 10).toLowerCase() === "noresource") { result0 = input.substr(pos, 10); pos += 10; } else { result0 = null; if (reportFailures === 0) { matchFailed("\"noresource\""); } } if (result0 === null) { if (input.substr(pos, 9).toLowerCase() === "invariant") { result0 = input.substr(pos, 9); pos += 9; } else { result0 = null; if (reportFailures === 0) { matchFailed("\"invariant\""); } } if (result0 === null) { result0 = parse_token(); } } } } } } } return result0; } function parse_Subject() { var result0; result0 = parse_TEXT_UTF8_TRIM(); result0 = result0 !== null ? result0 : ""; return result0; } function parse_Supported() { var result0, result1, result2, result3; var pos0, pos1; pos0 = pos; result0 = parse_token(); if (result0 !== null) { result1 = []; pos1 = pos; result2 = parse_COMMA(); if (result2 !== null) { result3 = parse_token(); if (result3 !== null) { result2 = [result2, result3]; } else { result2 = null; pos = pos1; } } else { result2 = null; pos = pos1; } while (result2 !== null) { result1.push(result2); pos1 = pos; result2 = parse_COMMA(); if (result2 !== null) { result3 = parse_token(); if (result3 !== null) { result2 = [result2, result3]; } else { result2 = null; pos = pos1; } } else { result2 = null; pos = pos1; } } if (result1 !== null) { result0 = [result0, result1]; } else { result0 = null; pos = pos0; } } else { result0 = null; pos = pos0; } result0 = result0 !== null ? result0 : ""; return result0; } function parse_To() { var result0, result1, result2, result3; var pos0, pos1, pos2; pos0 = pos; pos1 = pos; result0 = parse_SIP_URI_noparams(); if (result0 === null) { result0 = parse_name_addr(); } if (result0 !== null) { result1 = []; pos2 = pos; result2 = parse_SEMI(); if (result2 !== null) { result3 = parse_to_param(); if (result3 !== null) { result2 = [result2, result3]; } else { result2 = null; pos = pos2; } } else { result2 = null; pos = pos2; } while (result2 !== null) { result1.push(result2); pos2 = pos; result2 = parse_SEMI(); if (result2 !== null) { result3 = parse_to_param(); if (result3 !== null) { result2 = [result2, result3]; } else { result2 = null; pos = pos2; } } else { result2 = null; pos = pos2; } } if (result1 !== null) { result0 = [result0, result1]; } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } if (result0 !== null) { result0 = (function(offset) { var tag = data.tag; try { data = new JsSIP.NameAddrHeader(data.uri, data.display_name, data.params); if (tag) {data.setParam('tag',tag)} } catch(e) { data = -1; }})(pos0); } if (result0 === null) { pos = pos0; } return result0; } function parse_to_param() { var result0; result0 = parse_tag_param(); if (result0 === null) { result0 = parse_generic_param(); } return result0; } function parse_Via() { var result0, result1, result2, result3; var pos0, pos1; pos0 = pos; result0 = parse_via_parm(); if (result0 !== null) { result1 = []; pos1 = pos; result2 = parse_COMMA(); if (result2 !== null) { result3 = parse_via_parm(); if (result3 !== null) { result2 = [result2, result3]; } else { result2 = null; pos = pos1; } } else { result2 = null; pos = pos1; } while (result2 !== null) { result1.push(result2); pos1 = pos; result2 = parse_COMMA(); if (result2 !== null) { result3 = parse_via_parm(); if (result3 !== null) { result2 = [result2, result3]; } else { result2 = null; pos = pos1; } } else { result2 = null; pos = pos1; } } if (result1 !== null) { result0 = [result0, result1]; } else { result0 = null; pos = pos0; } } else { result0 = null; pos = pos0; } return result0; } function parse_via_parm() { var result0, result1, result2, result3, result4, result5; var pos0, pos1; pos0 = pos; result0 = parse_sent_protocol(); if (result0 !== null) { result1 = parse_LWS(); if (result1 !== null) { result2 = parse_sent_by(); if (result2 !== null) { result3 = []; pos1 = pos; result4 = parse_SEMI(); if (result4 !== null) { result5 = parse_via_params(); if (result5 !== null) { result4 = [result4, result5]; } else { result4 = null; pos = pos1; } } else { result4 = null; pos = pos1; } while (result4 !== null) { result3.push(result4); pos1 = pos; result4 = parse_SEMI(); if (result4 !== null) { result5 = parse_via_params(); if (result5 !== null) { result4 = [result4, result5]; } else { result4 = null; pos = pos1; } } else { result4 = null; pos = pos1; } } if (result3 !== null) { result0 = [result0, result1, result2, result3]; } else { result0 = null; pos = pos0; } } else { result0 = null; pos = pos0; } } else { result0 = null; pos = pos0; } } else { result0 = null; pos = pos0; } return result0; } function parse_via_params() { var result0; result0 = parse_via_ttl(); if (result0 === null) { result0 = parse_via_maddr(); if (result0 === null) { result0 = parse_via_received(); if (result0 === null) { result0 = parse_via_branch(); if (result0 === null) { result0 = parse_response_port(); if (result0 === null) { result0 = parse_generic_param(); } } } } } return result0; } function parse_via_ttl() { var result0, result1, result2; var pos0, pos1; pos0 = pos; pos1 = pos; if (input.substr(pos, 3).toLowerCase() === "ttl") { result0 = input.substr(pos, 3); pos += 3; } else { result0 = null; if (reportFailures === 0) { matchFailed("\"ttl\""); } } if (result0 !== null) { result1 = parse_EQUAL(); if (result1 !== null) { result2 = parse_ttl(); if (result2 !== null) { result0 = [result0, result1, result2]; } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } if (result0 !== null) { result0 = (function(offset, via_ttl_value) { data.ttl = via_ttl_value; })(pos0, result0[2]); } if (result0 === null) { pos = pos0; } return result0; } function parse_via_maddr() { var result0, result1, result2; var pos0, pos1; pos0 = pos; pos1 = pos; if (input.substr(pos, 5).toLowerCase() === "maddr") { result0 = input.substr(pos, 5); pos += 5; } else { result0 = null; if (reportFailures === 0) { matchFailed("\"maddr\""); } } if (result0 !== null) { result1 = parse_EQUAL(); if (result1 !== null) { result2 = parse_host(); if (result2 !== null) { result0 = [result0, result1, result2]; } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } if (result0 !== null) { result0 = (function(offset, via_maddr) { data.maddr = via_maddr; })(pos0, result0[2]); } if (result0 === null) { pos = pos0; } return result0; } function parse_via_received() { var result0, result1, result2; var pos0, pos1; pos0 = pos; pos1 = pos; if (input.substr(pos, 8).toLowerCase() === "received") { result0 = input.substr(pos, 8); pos += 8; } else { result0 = null; if (reportFailures === 0) { matchFailed("\"received\""); } } if (result0 !== null) { result1 = parse_EQUAL(); if (result1 !== null) { result2 = parse_IPv4address(); if (result2 === null) { result2 = parse_IPv6address(); } if (result2 !== null) { result0 = [result0, result1, result2]; } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } if (result0 !== null) { result0 = (function(offset, via_received) { data.received = via_received; })(pos0, result0[2]); } if (result0 === null) { pos = pos0; } return result0; } function parse_via_branch() { var result0, result1, result2; var pos0, pos1; pos0 = pos; pos1 = pos; if (input.substr(pos, 6).toLowerCase() === "branch") { result0 = input.substr(pos, 6); pos += 6; } else { result0 = null; if (reportFailures === 0) { matchFailed("\"branch\""); } } if (result0 !== null) { result1 = parse_EQUAL(); if (result1 !== null) { result2 = parse_token(); if (result2 !== null) { result0 = [result0, result1, result2]; } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } if (result0 !== null) { result0 = (function(offset, via_branch) { data.branch = via_branch; })(pos0, result0[2]); } if (result0 === null) { pos = pos0; } return result0; } function parse_response_port() { var result0, result1, result2, result3; var pos0, pos1, pos2; pos0 = pos; pos1 = pos; if (input.substr(pos, 5).toLowerCase() === "rport") { result0 = input.substr(pos, 5); pos += 5; } else { result0 = null; if (reportFailures === 0) { matchFailed("\"rport\""); } } if (result0 !== null) { pos2 = pos; result1 = parse_EQUAL(); if (result1 !== null) { result2 = []; result3 = parse_DIGIT(); while (result3 !== null) { result2.push(result3); result3 = parse_DIGIT(); } if (result2 !== null) { result1 = [result1, result2]; } else { result1 = null; pos = pos2; } } else { result1 = null; pos = pos2; } result1 = result1 !== null ? result1 : ""; if (result1 !== null) { result0 = [result0, result1]; } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } if (result0 !== null) { result0 = (function(offset) { if(typeof response_port !== 'undefined') data.rport = response_port.join(''); })(pos0); } if (result0 === null) { pos = pos0; } return result0; } function parse_sent_protocol() { var result0, result1, result2, result3, result4; var pos0; pos0 = pos; result0 = parse_protocol_name(); if (result0 !== null) { result1 = parse_SLASH(); if (result1 !== null) { result2 = parse_token(); if (result2 !== null) { result3 = parse_SLASH(); if (result3 !== null) { result4 = parse_transport(); if (result4 !== null) { result0 = [result0, result1, result2, result3, result4]; } else { result0 = null; pos = pos0; } } else { result0 = null; pos = pos0; } } else { result0 = null; pos = pos0; } } else { result0 = null; pos = pos0; } } else { result0 = null; pos = pos0; } return result0; } function parse_protocol_name() { var result0; var pos0; pos0 = pos; if (input.substr(pos, 3).toLowerCase() === "sip") { result0 = input.substr(pos, 3); pos += 3; } else { result0 = null; if (reportFailures === 0) { matchFailed("\"SIP\""); } } if (result0 === null) { result0 = parse_token(); } if (result0 !== null) { result0 = (function(offset, via_protocol) { data.protocol = via_protocol; })(pos0, result0); } if (result0 === null) { pos = pos0; } return result0; } function parse_transport() { var result0; var pos0; pos0 = pos; if (input.substr(pos, 3).toLowerCase() === "udp") { result0 = input.substr(pos, 3); pos += 3; } else { result0 = null; if (reportFailures === 0) { matchFailed("\"UDP\""); } } if (result0 === null) { if (input.substr(pos, 3).toLowerCase() === "tcp") { result0 = input.substr(pos, 3); pos += 3; } else { result0 = null; if (reportFailures === 0) { matchFailed("\"TCP\""); } } if (result0 === null) { if (input.substr(pos, 3).toLowerCase() === "tls") { result0 = input.substr(pos, 3); pos += 3; } else { result0 = null; if (reportFailures === 0) { matchFailed("\"TLS\""); } } if (result0 === null) { if (input.substr(pos, 4).toLowerCase() === "sctp") { result0 = input.substr(pos, 4); pos += 4; } else { result0 = null; if (reportFailures === 0) { matchFailed("\"SCTP\""); } } if (result0 === null) { result0 = parse_token(); } } } } if (result0 !== null) { result0 = (function(offset, via_transport) { data.transport = via_transport; })(pos0, result0); } if (result0 === null) { pos = pos0; } return result0; } function parse_sent_by() { var result0, result1, result2; var pos0, pos1; pos0 = pos; result0 = parse_via_host(); if (result0 !== null) { pos1 = pos; result1 = parse_COLON(); if (result1 !== null) { result2 = parse_via_port(); if (result2 !== null) { result1 = [result1, result2]; } else { result1 = null; pos = pos1; } } else { result1 = null; pos = pos1; } result1 = result1 !== null ? result1 : ""; if (result1 !== null) { result0 = [result0, result1]; } else { result0 = null; pos = pos0; } } else { result0 = null; pos = pos0; } return result0; } function parse_via_host() { var result0; var pos0; pos0 = pos; result0 = parse_IPv4address(); if (result0 === null) { result0 = parse_IPv6reference(); if (result0 === null) { result0 = parse_hostname(); } } if (result0 !== null) { result0 = (function(offset) { data.host = input.substring(pos, offset); })(pos0); } if (result0 === null) { pos = pos0; } return result0; } function parse_via_port() { var result0, result1, result2, result3, result4; var pos0, pos1; pos0 = pos; pos1 = pos; result0 = parse_DIGIT(); result0 = result0 !== null ? result0 : ""; if (result0 !== null) { result1 = parse_DIGIT(); result1 = result1 !== null ? result1 : ""; if (result1 !== null) { result2 = parse_DIGIT(); result2 = result2 !== null ? result2 : ""; if (result2 !== null) { result3 = parse_DIGIT(); result3 = result3 !== null ? result3 : ""; if (result3 !== null) { result4 = parse_DIGIT(); result4 = result4 !== null ? result4 : ""; if (result4 !== null) { result0 = [result0, result1, result2, result3, result4]; } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } if (result0 !== null) { result0 = (function(offset, via_sent_by_port) { data.port = parseInt(via_sent_by_port.join('')); })(pos0, result0); } if (result0 === null) { pos = pos0; } return result0; } function parse_ttl() { var result0, result1, result2; var pos0, pos1; pos0 = pos; pos1 = pos; result0 = parse_DIGIT(); if (result0 !== null) { result1 = parse_DIGIT(); result1 = result1 !== null ? result1 : ""; if (result1 !== null) { result2 = parse_DIGIT(); result2 = result2 !== null ? result2 : ""; if (result2 !== null) { result0 = [result0, result1, result2]; } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } if (result0 !== null) { result0 = (function(offset, ttl) { return parseInt(ttl.join('')); })(pos0, result0); } if (result0 === null) { pos = pos0; } return result0; } function parse_WWW_Authenticate() { var result0; result0 = parse_challenge(); return result0; } function parse_extension_header() { var result0, result1, result2; var pos0; pos0 = pos; result0 = parse_token(); if (result0 !== null) { result1 = parse_HCOLON(); if (result1 !== null) { result2 = parse_header_value(); if (result2 !== null) { result0 = [result0, result1, result2]; } else { result0 = null; pos = pos0; } } else { result0 = null; pos = pos0; } } else { result0 = null; pos = pos0; } return result0; } function parse_header_value() { var result0, result1; result0 = []; result1 = parse_TEXT_UTF8char(); if (result1 === null) { result1 = parse_UTF8_CONT(); if (result1 === null) { result1 = parse_LWS(); } } while (result1 !== null) { result0.push(result1); result1 = parse_TEXT_UTF8char(); if (result1 === null) { result1 = parse_UTF8_CONT(); if (result1 === null) { result1 = parse_LWS(); } } } return result0; } function parse_message_body() { var result0, result1; result0 = []; result1 = parse_OCTET(); while (result1 !== null) { result0.push(result1); result1 = parse_OCTET(); } return result0; } function parse_stun_URI() { var result0, result1, result2; var pos0; pos0 = pos; result0 = parse_stun_scheme(); if (result0 !== null) { if (input.charCodeAt(pos) === 58) { result1 = ":"; pos++; } else { result1 = null; if (reportFailures === 0) { matchFailed("\":\""); } } if (result1 !== null) { result2 = parse_stun_host_port(); if (result2 !== null) { result0 = [result0, result1, result2]; } else { result0 = null; pos = pos0; } } else { result0 = null; pos = pos0; } } else { result0 = null; pos = pos0; } return result0; } function parse_stun_scheme() { var result0; var pos0; pos0 = pos; if (input.substr(pos, 5).toLowerCase() === "stuns") { result0 = input.substr(pos, 5); pos += 5; } else { result0 = null; if (reportFailures === 0) { matchFailed("\"stuns\""); } } if (result0 === null) { if (input.substr(pos, 4).toLowerCase() === "stun") { result0 = input.substr(pos, 4); pos += 4; } else { result0 = null; if (reportFailures === 0) { matchFailed("\"stun\""); } } } if (result0 !== null) { result0 = (function(offset, scheme) { data.scheme = scheme; })(pos0, result0); } if (result0 === null) { pos = pos0; } return result0; } function parse_stun_host_port() { var result0, result1, result2; var pos0, pos1; pos0 = pos; result0 = parse_stun_host(); if (result0 !== null) { pos1 = pos; if (input.charCodeAt(pos) === 58) { result1 = ":"; pos++; } else { result1 = null; if (reportFailures === 0) { matchFailed("\":\""); } } if (result1 !== null) { result2 = parse_port(); if (result2 !== null) { result1 = [result1, result2]; } else { result1 = null; pos = pos1; } } else { result1 = null; pos = pos1; } result1 = result1 !== null ? result1 : ""; if (result1 !== null) { result0 = [result0, result1]; } else { result0 = null; pos = pos0; } } else { result0 = null; pos = pos0; } return result0; } function parse_stun_host() { var result0; var pos0; pos0 = pos; result0 = parse_IPv4address(); if (result0 === null) { result0 = parse_IPv6reference(); if (result0 === null) { result0 = parse_reg_name(); } } if (result0 !== null) { result0 = (function(offset, host) { data.host = host; })(pos0, result0); } if (result0 === null) { pos = pos0; } return result0; } function parse_reg_name() { var result0, result1; var pos0; pos0 = pos; result0 = []; result1 = parse_stun_unreserved(); if (result1 === null) { result1 = parse_escaped(); if (result1 === null) { result1 = parse_sub_delims(); } } while (result1 !== null) { result0.push(result1); result1 = parse_stun_unreserved(); if (result1 === null) { result1 = parse_escaped(); if (result1 === null) { result1 = parse_sub_delims(); } } } if (result0 !== null) { result0 = (function(offset) { return input.substring(pos, offset); })(pos0); } if (result0 === null) { pos = pos0; } return result0; } function parse_stun_unreserved() { var result0; result0 = parse_ALPHA(); if (result0 === null) { result0 = parse_DIGIT(); if (result0 === null) { if (input.charCodeAt(pos) === 45) { result0 = "-"; pos++; } else { result0 = null; if (reportFailures === 0) { matchFailed("\"-\""); } } if (result0 === null) { if (input.charCodeAt(pos) === 46) { result0 = "."; pos++; } else { result0 = null; if (reportFailures === 0) { matchFailed("\".\""); } } if (result0 === null) { if (input.charCodeAt(pos) === 95) { result0 = "_"; pos++; } else { result0 = null; if (reportFailures === 0) { matchFailed("\"_\""); } } if (result0 === null) { if (input.charCodeAt(pos) === 126) { result0 = "~"; pos++; } else { result0 = null; if (reportFailures === 0) { matchFailed("\"~\""); } } } } } } } return result0; } function parse_sub_delims() { var result0; if (input.charCodeAt(pos) === 33) { result0 = "!"; pos++; } else { result0 = null; if (reportFailures === 0) { matchFailed("\"!\""); } } if (result0 === null) { if (input.charCodeAt(pos) === 36) { result0 = "$"; pos++; } else { result0 = null; if (reportFailures === 0) { matchFailed("\"$\""); } } if (result0 === null) { if (input.charCodeAt(pos) === 38) { result0 = "&"; pos++; } else { result0 = null; if (reportFailures === 0) { matchFailed("\"&\""); } } if (result0 === null) { if (input.charCodeAt(pos) === 39) { result0 = "'"; pos++; } else { result0 = null; if (reportFailures === 0) { matchFailed("\"'\""); } } if (result0 === null) { if (input.charCodeAt(pos) === 40) { result0 = "("; pos++; } else { result0 = null; if (reportFailures === 0) { matchFailed("\"(\""); } } if (result0 === null) { if (input.charCodeAt(pos) === 41) { result0 = ")"; pos++; } else { result0 = null; if (reportFailures === 0) { matchFailed("\")\""); } } if (result0 === null) { if (input.charCodeAt(pos) === 42) { result0 = "*"; pos++; } else { result0 = null; if (reportFailures === 0) { matchFailed("\"*\""); } } if (result0 === null) { if (input.charCodeAt(pos) === 43) { result0 = "+"; pos++; } else { result0 = null; if (reportFailures === 0) { matchFailed("\"+\""); } } if (result0 === null) { if (input.charCodeAt(pos) === 44) { result0 = ","; pos++; } else { result0 = null; if (reportFailures === 0) { matchFailed("\",\""); } } if (result0 === null) { if (input.charCodeAt(pos) === 59) { result0 = ";"; pos++; } else { result0 = null; if (reportFailures === 0) { matchFailed("\";\""); } } if (result0 === null) { if (input.charCodeAt(pos) === 61) { result0 = "="; pos++; } else { result0 = null; if (reportFailures === 0) { matchFailed("\"=\""); } } } } } } } } } } } } return result0; } function parse_turn_URI() { var result0, result1, result2, result3, result4; var pos0, pos1; pos0 = pos; result0 = parse_turn_scheme(); if (result0 !== null) { if (input.charCodeAt(pos) === 58) { result1 = ":"; pos++; } else { result1 = null; if (reportFailures === 0) { matchFailed("\":\""); } } if (result1 !== null) { result2 = parse_stun_host_port(); if (result2 !== null) { pos1 = pos; if (input.substr(pos, 11) === "?transport=") { result3 = "?transport="; pos += 11; } else { result3 = null; if (reportFailures === 0) { matchFailed("\"?transport=\""); } } if (result3 !== null) { result4 = parse_transport(); if (result4 !== null) { result3 = [result3, result4]; } else { result3 = null; pos = pos1; } } else { result3 = null; pos = pos1; } result3 = result3 !== null ? result3 : ""; if (result3 !== null) { result0 = [result0, result1, result2, result3]; } else { result0 = null; pos = pos0; } } else { result0 = null; pos = pos0; } } else { result0 = null; pos = pos0; } } else { result0 = null; pos = pos0; } return result0; } function parse_turn_scheme() { var result0; var pos0; pos0 = pos; if (input.substr(pos, 5).toLowerCase() === "turns") { result0 = input.substr(pos, 5); pos += 5; } else { result0 = null; if (reportFailures === 0) { matchFailed("\"turns\""); } } if (result0 === null) { if (input.substr(pos, 4).toLowerCase() === "turn") { result0 = input.substr(pos, 4); pos += 4; } else { result0 = null; if (reportFailures === 0) { matchFailed("\"turn\""); } } } if (result0 !== null) { result0 = (function(offset, scheme) { data.scheme = scheme; })(pos0, result0); } if (result0 === null) { pos = pos0; } return result0; } function parse_turn_transport() { var result0, result1, result2; var pos0, pos1; pos0 = pos; pos1 = pos; result0 = parse_transport(); if (result0 !== null) { if (input.substr(pos, 3).toLowerCase() === "udp") { result1 = input.substr(pos, 3); pos += 3; } else { result1 = null; if (reportFailures === 0) { matchFailed("\"udp\""); } } if (result1 === null) { if (input.substr(pos, 3).toLowerCase() === "tcp") { result1 = input.substr(pos, 3); pos += 3; } else { result1 = null; if (reportFailures === 0) { matchFailed("\"tcp\""); } } if (result1 === null) { result1 = []; result2 = parse_unreserved(); while (result2 !== null) { result1.push(result2); result2 = parse_unreserved(); } } } if (result1 !== null) { result0 = [result0, result1]; } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } if (result0 !== null) { result0 = (function(offset) { data.transport = transport; })(pos0); } if (result0 === null) { pos = pos0; } return result0; } function parse_uuid_URI() { var result0, result1; var pos0; pos0 = pos; if (input.substr(pos, 5) === "uuid:") { result0 = "uuid:"; pos += 5; } else { result0 = null; if (reportFailures === 0) { matchFailed("\"uuid:\""); } } if (result0 !== null) { result1 = parse_uuid(); if (result1 !== null) { result0 = [result0, result1]; } else { result0 = null; pos = pos0; } } else { result0 = null; pos = pos0; } return result0; } function parse_uuid() { var result0, result1, result2, result3, result4, result5, result6, result7, result8; var pos0, pos1; pos0 = pos; pos1 = pos; result0 = parse_hex8(); if (result0 !== null) { if (input.charCodeAt(pos) === 45) { result1 = "-"; pos++; } else { result1 = null; if (reportFailures === 0) { matchFailed("\"-\""); } } if (result1 !== null) { result2 = parse_hex4(); if (result2 !== null) { if (input.charCodeAt(pos) === 45) { result3 = "-"; pos++; } else { result3 = null; if (reportFailures === 0) { matchFailed("\"-\""); } } if (result3 !== null) { result4 = parse_hex4(); if (result4 !== null) { if (input.charCodeAt(pos) === 45) { result5 = "-"; pos++; } else { result5 = null; if (reportFailures === 0) { matchFailed("\"-\""); } } if (result5 !== null) { result6 = parse_hex4(); if (result6 !== null) { if (input.charCodeAt(pos) === 45) { result7 = "-"; pos++; } else { result7 = null; if (reportFailures === 0) { matchFailed("\"-\""); } } if (result7 !== null) { result8 = parse_hex12(); if (result8 !== null) { result0 = [result0, result1, result2, result3, result4, result5, result6, result7, result8]; } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } } else { result0 = null; pos = pos1; } if (result0 !== null) { result0 = (function(offset, uuid) { data = input.substring(pos+5, offset); })(pos0, result0[0]); } if (result0 === null) { pos = pos0; } return result0; } function parse_hex4() { var result0, result1, result2, result3; var pos0; pos0 = pos; result0 = parse_HEXDIG(); if (result0 !== null) { result1 = parse_HEXDIG(); if (result1 !== null) { result2 = parse_HEXDIG(); if (result2 !== null) { result3 = parse_HEXDIG(); if (result3 !== null) { result0 = [result0, result1, result2, result3]; } else { result0 = null; pos = pos0; } } else { result0 = null; pos = pos0; } } else { result0 = null; pos = pos0; } } else { result0 = null; pos = pos0; } return result0; } function parse_hex8() { var result0, result1; var pos0; pos0 = pos; result0 = parse_hex4(); if (result0 !== null) { result1 = parse_hex4(); if (result1 !== null) { result0 = [result0, result1]; } else { result0 = null; pos = pos0; } } else { result0 = null; pos = pos0; } return result0; } function parse_hex12() { var result0, result1, result2; var pos0; pos0 = pos; result0 = parse_hex4(); if (result0 !== null) { result1 = parse_hex4(); if (result1 !== null) { result2 = parse_hex4(); if (result2 !== null) { result0 = [result0, result1, result2]; } else { result0 = null; pos = pos0; } } else { result0 = null; pos = pos0; } } else { result0 = null; pos = pos0; } return result0; } function cleanupExpected(expected) { expected.sort(); var lastExpected = null; var cleanExpected = []; for (var i = 0; i < expected.length; i++) { if (expected[i] !== lastExpected) { cleanExpected.push(expected[i]); lastExpected = expected[i]; } } return cleanExpected; } function computeErrorPosition() { /* * The first idea was to use |String.split| to break the input up to the * error position along newlines and derive the line and column from * there. However IE's |split| implementation is so broken that it was * enough to prevent it. */ var line = 1; var column = 1; var seenCR = false; for (var i = 0; i < Math.max(pos, rightmostFailuresPos); i++) { var ch = input.charAt(i); if (ch === "\n") { if (!seenCR) { line++; } column = 1; seenCR = false; } else if (ch === "\r" || ch === "\u2028" || ch === "\u2029") { line++; column = 1; seenCR = true; } else { column++; seenCR = false; } } return { line: line, column: column }; } var data = {}; var result = parseFunctions[startRule](); /* * The parser is now in one of the following three states: * * 1. The parser successfully parsed the whole input. * * - |result !== null| * - |pos === input.length| * - |rightmostFailuresExpected| may or may not contain something * * 2. The parser successfully parsed only a part of the input. * * - |result !== null| * - |pos < input.length| * - |rightmostFailuresExpected| may or may not contain something * * 3. The parser did not successfully parse any part of the input. * * - |result === null| * - |pos === 0| * - |rightmostFailuresExpected| contains at least one failure * * All code following this comment (including called functions) must * handle these states. */ if (result === null || pos !== input.length) { var offset = Math.max(pos, rightmostFailuresPos); var found = offset < input.length ? input.charAt(offset) : null; var errorPosition = computeErrorPosition(); new this.SyntaxError( cleanupExpected(rightmostFailuresExpected), found, offset, errorPosition.line, errorPosition.column ); return -1; } return data; }, /* Returns the parser source code. */ toSource: function() { return this._source; } }; /* Thrown when a parser encounters a syntax error. */ result.SyntaxError = function(expected, found, offset, line, column) { function buildMessage(expected, found) { var expectedHumanized, foundHumanized; switch (expected.length) { case 0: expectedHumanized = "end of input"; break; case 1: expectedHumanized = expected[0]; break; default: expectedHumanized = expected.slice(0, expected.length - 1).join(", ") + " or " + expected[expected.length - 1]; } foundHumanized = found ? quote(found) : "end of input"; return "Expected " + expectedHumanized + " but " + foundHumanized + " found."; } this.name = "SyntaxError"; this.expected = expected; this.found = found; this.message = buildMessage(expected, found); this.offset = offset; this.line = line; this.column = column; }; result.SyntaxError.prototype = Error.prototype; return result; })(); (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);throw new Error("Cannot find module '"+o+"'")}var f=n[o]={exports:{}};t[o][0].call(f.exports,function(e){var n=t[o][1][e];return s(n?n:e)},f,f.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o= len) { return x; // missing argument } var arg = args[i]; i += 1; switch (x) { case '%%': return '%'; case '%s': return String(arg); case '%d': return Number(arg); case '%v': return ''; } }); // NB: we discard excess arguments - they are typically undefined from makeLine }; var makeLine = function (type, obj, location) { var str = obj.format instanceof Function ? (obj.format(obj.push ? location : location[obj.name])) : obj.format; var args = [type + '=' + str]; if (obj.names) { for (var i = 0; i < obj.names.length; i += 1) { var n = obj.names[i]; if (obj.name) { args.push(location[obj.name][n]); } else { // for mLine and push attributes args.push(location[obj.names[i]]); } } } else { args.push(location[obj.name]); } return format.apply(null, args); }; // RFC specified order // TODO: extend this with all the rest var defaultOuterOrder = [ 'v', 'o', 's', 'i', 'u', 'e', 'p', 'c', 'b', 't', 'r', 'z', 'a' ]; var defaultInnerOrder = ['i', 'c', 'b', 'a']; module.exports = function (session, opts) { opts = opts || {}; // ensure certain properties exist if (session.version == null) { session.version = 0; // "v=0" must be there (only defined version atm) } if (session.name == null) { session.name = " "; // "s= " must be there if no meaningful name set } session.media.forEach(function (mLine) { if (mLine.payloads == null) { mLine.payloads = ""; } }); var outerOrder = opts.outerOrder || defaultOuterOrder; var innerOrder = opts.innerOrder || defaultInnerOrder; var sdp = []; // loop through outerOrder for matching properties on session outerOrder.forEach(function (type) { grammar[type].forEach(function (obj) { if (obj.name in session) { sdp.push(makeLine(type, obj, session)); } else if (obj.push in session) { session[obj.push].forEach(function (el) { sdp.push(makeLine(type, obj, el)); }); } }); }); // then for each media line, follow the innerOrder session.media.forEach(function (mLine) { sdp.push(makeLine('m', grammar.m[0], mLine)); innerOrder.forEach(function (type) { grammar[type].forEach(function (obj) { if (obj.name in mLine) { sdp.push(makeLine(type, obj, mLine)); } else if (obj.push in mLine) { mLine[obj.push].forEach(function (el) { sdp.push(makeLine(type, obj, el)); }); } }); }); }); return sdp.join('\r\n') + '\r\n'; }; },{"./grammar":2}],5:[function(require,module,exports){ /** * @fileoverview SDP Parser * * https://github.com/clux/sdp-transform * */ (function(JsSIP) { var parser = require('sdp-transform'); JsSIP.Parser.parseSDP = parser.parse; JsSIP.Parser.writeSDP = parser.write; JsSIP.Parser.parseFmtpConfig = parser.parseFmtpConfig; JsSIP.Parser.parsePayloads = parser.parsePayloads; JsSIP.Parser.parseRemoteCandidates = parser.parseRemoteCandidates; }(JsSIP)); },{"sdp-transform":1}]},{},[5])