UNPKG

jssip

Version:

the Javascript SIP library

552 lines (481 loc) 13.8 kB
const JsSIP_C = require('./Constants'); const URI = require('./URI'); const Grammar = require('./Grammar'); exports.str_utf8_length = (string) => unescape(encodeURIComponent(string)).length; // Used by 'hasMethods'. const isFunction = exports.isFunction = (fn) => { if (fn !== undefined) { return (Object.prototype.toString.call(fn) === '[object Function]')? true : false; } else { return false; } }; exports.isString = (str) => { if (str !== undefined) { return (Object.prototype.toString.call(str) === '[object String]')? true : false; } else { return false; } }; exports.isDecimal = (num) => !isNaN(num) && (parseFloat(num) === parseInt(num, 10)); exports.isEmpty = (value) => { return (value === null || value === '' || value === undefined || (Array.isArray(value) && value.length === 0) || (typeof(value) === 'number' && isNaN(value))); }; exports.hasMethods = function(obj, ...methodNames) { for (const methodName of methodNames) { if (isFunction(obj[methodName])) { return false; } } return true; }; // Used by 'newTag'. const createRandomToken = exports.createRandomToken = (size, base = 32) => { let i, r, token = ''; for (i=0; i < size; i++) { r = (Math.random() * base) | 0; token += r.toString(base); } return token; }; exports.newTag = () => createRandomToken(10); // https://stackoverflow.com/users/109538/broofa. exports.newUUID = () => { const UUID = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => { const r = Math.random()*16|0, v = c === 'x' ? r : ((r&0x3)|0x8); return v.toString(16); }); return UUID; }; exports.hostType = (host) => { if (!host) { return; } else { host = Grammar.parse(host, 'host'); if (host !== -1) { return host.host_type; } } }; /** * Hex-escape a SIP URI user. * Don't hex-escape ':' (%3A), '+' (%2B), '?' (%3F"), '/' (%2F). * * Used by 'normalizeTarget'. */ const escapeUser = exports.escapeUser = (user) => encodeURIComponent(decodeURIComponent(user)) .replace(/%3A/ig, ':') .replace(/%2B/ig, '+') .replace(/%3F/ig, '?') .replace(/%2F/ig, '/'); /** * 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. */ exports.normalizeTarget = (target, domain) => { // If no target is given then raise an error. if (!target) { return; // If a URI instance is given then return it. } else if (target instanceof 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') { const target_array = target.split('@'); let target_user; let target_domain; 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}:${escapeUser(target_user)}@${target_domain}`; // Finally parse the resulting URI. let uri; if ((uri = URI.parse(target))) { return uri; } else { return; } } else { return; } }; exports.headerize = (string) => { const exceptions = { 'Call-Id' : 'Call-ID', 'Cseq' : 'CSeq', 'Www-Authenticate' : 'WWW-Authenticate' }; const name = string.toLowerCase() .replace(/_/g, '-') .split('-'); let hname = ''; const parts = name.length; let 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; }; exports.sipErrorCause = (status_code) => { for (const 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 (https://tools.ietf.org/html/rfc5735) */ exports.getRandomTestNetIP = () => { function getOctet(from, to) { return Math.floor((Math.random() * (to-from+1)) + from); } return `192.0.2.${getOctet(1, 254)}`; }; // MD5 (Message-Digest Algorithm) https://www.webtoolkit.info. exports.calculateMD5 = (string) => { function rotateLeft(lValue, iShiftBits) { return (lValue<<iShiftBits) | (lValue>>>(32-iShiftBits)); } function addUnsigned(lX, lY) { const lX8 = (lX & 0x80000000); const lY8 = (lY & 0x80000000); const lX4 = (lX & 0x40000000); const lY4 = (lY & 0x40000000); const 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 doF(x, y, z) { return (x & y) | ((~x) & z); } function doG(x, y, z) { return (x & z) | (y & (~z)); } function doH(x, y, z) { return (x ^ y ^ z); } function doI(x, y, z) { return (y ^ (x | (~z))); } function doFF(a, b, c, d, x, s, ac) { a = addUnsigned(a, addUnsigned(addUnsigned(doF(b, c, d), x), ac)); return addUnsigned(rotateLeft(a, s), b); } function doGG(a, b, c, d, x, s, ac) { a = addUnsigned(a, addUnsigned(addUnsigned(doG(b, c, d), x), ac)); return addUnsigned(rotateLeft(a, s), b); } function doHH(a, b, c, d, x, s, ac) { a = addUnsigned(a, addUnsigned(addUnsigned(doH(b, c, d), x), ac)); return addUnsigned(rotateLeft(a, s), b); } function doII(a, b, c, d, x, s, ac) { a = addUnsigned(a, addUnsigned(addUnsigned(doI(b, c, d), x), ac)); return addUnsigned(rotateLeft(a, s), b); } function convertToWordArray(str) { let lWordCount; const lMessageLength = str.length; const lNumberOfWords_temp1=lMessageLength + 8; const lNumberOfWords_temp2=(lNumberOfWords_temp1-(lNumberOfWords_temp1 % 64))/64; const lNumberOfWords = (lNumberOfWords_temp2+1)*16; const lWordArray = new Array(lNumberOfWords-1); let lBytePosition = 0; let lByteCount = 0; while (lByteCount < lMessageLength) { lWordCount = (lByteCount-(lByteCount % 4))/4; lBytePosition = (lByteCount % 4)*8; lWordArray[lWordCount] = (lWordArray[lWordCount] | (str.charCodeAt(lByteCount)<<lBytePosition)); lByteCount++; } lWordCount = (lByteCount-(lByteCount % 4))/4; lBytePosition = (lByteCount % 4)*8; lWordArray[lWordCount] = lWordArray[lWordCount] | (0x80<<lBytePosition); lWordArray[lNumberOfWords-2] = lMessageLength<<3; lWordArray[lNumberOfWords-1] = lMessageLength>>>29; return lWordArray; } function wordToHex(lValue) { let 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(str) { let utftext = ''; for (let n = 0; n < str.length; n++) { const c = str.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; } let x=[]; let k, AA, BB, CC, DD, a, b, c, d; const S11=7, S12=12, S13=17, S14=22; const S21=5, S22=9, S23=14, S24=20; const S31=4, S32=11, S33=16, S34=23; const 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<x.length; k+=16) { AA=a; BB=b; CC=c; DD=d; a=doFF(a, b, c, d, x[k+0], S11, 0xD76AA478); d=doFF(d, a, b, c, x[k+1], S12, 0xE8C7B756); c=doFF(c, d, a, b, x[k+2], S13, 0x242070DB); b=doFF(b, c, d, a, x[k+3], S14, 0xC1BDCEEE); a=doFF(a, b, c, d, x[k+4], S11, 0xF57C0FAF); d=doFF(d, a, b, c, x[k+5], S12, 0x4787C62A); c=doFF(c, d, a, b, x[k+6], S13, 0xA8304613); b=doFF(b, c, d, a, x[k+7], S14, 0xFD469501); a=doFF(a, b, c, d, x[k+8], S11, 0x698098D8); d=doFF(d, a, b, c, x[k+9], S12, 0x8B44F7AF); c=doFF(c, d, a, b, x[k+10], S13, 0xFFFF5BB1); b=doFF(b, c, d, a, x[k+11], S14, 0x895CD7BE); a=doFF(a, b, c, d, x[k+12], S11, 0x6B901122); d=doFF(d, a, b, c, x[k+13], S12, 0xFD987193); c=doFF(c, d, a, b, x[k+14], S13, 0xA679438E); b=doFF(b, c, d, a, x[k+15], S14, 0x49B40821); a=doGG(a, b, c, d, x[k+1], S21, 0xF61E2562); d=doGG(d, a, b, c, x[k+6], S22, 0xC040B340); c=doGG(c, d, a, b, x[k+11], S23, 0x265E5A51); b=doGG(b, c, d, a, x[k+0], S24, 0xE9B6C7AA); a=doGG(a, b, c, d, x[k+5], S21, 0xD62F105D); d=doGG(d, a, b, c, x[k+10], S22, 0x2441453); c=doGG(c, d, a, b, x[k+15], S23, 0xD8A1E681); b=doGG(b, c, d, a, x[k+4], S24, 0xE7D3FBC8); a=doGG(a, b, c, d, x[k+9], S21, 0x21E1CDE6); d=doGG(d, a, b, c, x[k+14], S22, 0xC33707D6); c=doGG(c, d, a, b, x[k+3], S23, 0xF4D50D87); b=doGG(b, c, d, a, x[k+8], S24, 0x455A14ED); a=doGG(a, b, c, d, x[k+13], S21, 0xA9E3E905); d=doGG(d, a, b, c, x[k+2], S22, 0xFCEFA3F8); c=doGG(c, d, a, b, x[k+7], S23, 0x676F02D9); b=doGG(b, c, d, a, x[k+12], S24, 0x8D2A4C8A); a=doHH(a, b, c, d, x[k+5], S31, 0xFFFA3942); d=doHH(d, a, b, c, x[k+8], S32, 0x8771F681); c=doHH(c, d, a, b, x[k+11], S33, 0x6D9D6122); b=doHH(b, c, d, a, x[k+14], S34, 0xFDE5380C); a=doHH(a, b, c, d, x[k+1], S31, 0xA4BEEA44); d=doHH(d, a, b, c, x[k+4], S32, 0x4BDECFA9); c=doHH(c, d, a, b, x[k+7], S33, 0xF6BB4B60); b=doHH(b, c, d, a, x[k+10], S34, 0xBEBFBC70); a=doHH(a, b, c, d, x[k+13], S31, 0x289B7EC6); d=doHH(d, a, b, c, x[k+0], S32, 0xEAA127FA); c=doHH(c, d, a, b, x[k+3], S33, 0xD4EF3085); b=doHH(b, c, d, a, x[k+6], S34, 0x4881D05); a=doHH(a, b, c, d, x[k+9], S31, 0xD9D4D039); d=doHH(d, a, b, c, x[k+12], S32, 0xE6DB99E5); c=doHH(c, d, a, b, x[k+15], S33, 0x1FA27CF8); b=doHH(b, c, d, a, x[k+2], S34, 0xC4AC5665); a=doII(a, b, c, d, x[k+0], S41, 0xF4292244); d=doII(d, a, b, c, x[k+7], S42, 0x432AFF97); c=doII(c, d, a, b, x[k+14], S43, 0xAB9423A7); b=doII(b, c, d, a, x[k+5], S44, 0xFC93A039); a=doII(a, b, c, d, x[k+12], S41, 0x655B59C3); d=doII(d, a, b, c, x[k+3], S42, 0x8F0CCC92); c=doII(c, d, a, b, x[k+10], S43, 0xFFEFF47D); b=doII(b, c, d, a, x[k+1], S44, 0x85845DD1); a=doII(a, b, c, d, x[k+8], S41, 0x6FA87E4F); d=doII(d, a, b, c, x[k+15], S42, 0xFE2CE6E0); c=doII(c, d, a, b, x[k+6], S43, 0xA3014314); b=doII(b, c, d, a, x[k+13], S44, 0x4E0811A1); a=doII(a, b, c, d, x[k+4], S41, 0xF7537E82); d=doII(d, a, b, c, x[k+11], S42, 0xBD3AF235); c=doII(c, d, a, b, x[k+2], S43, 0x2AD7D2BB); b=doII(b, c, d, a, x[k+9], S44, 0xEB86D391); a=addUnsigned(a, AA); b=addUnsigned(b, BB); c=addUnsigned(c, CC); d=addUnsigned(d, DD); } const temp = wordToHex(a)+wordToHex(b)+wordToHex(c)+wordToHex(d); return temp.toLowerCase(); }; exports.closeMediaStream = (stream) => { if (!stream) { return; } // Latest spec states that MediaStream has no stop() method and instead must // call stop() on every MediaStreamTrack. try { let tracks; if (stream.getTracks) { tracks = stream.getTracks(); for (const track of tracks) { track.stop(); } } else { tracks = stream.getAudioTracks(); for (const track of tracks) { track.stop(); } tracks = stream.getVideoTracks(); for (const track of tracks) { track.stop(); } } } catch (error) { // Deprecated by the spec, but still in use. // NOTE: In Temasys IE plugin stream.stop is a callable 'object'. if (typeof stream.stop === 'function' || typeof stream.stop === 'object') { stream.stop(); } } }; exports.cloneArray = (array) => { return (array && array.slice()) || []; }; exports.cloneObject = (obj, fallback = {}) => { return (obj && Object.assign({}, obj)) || fallback; };