New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

ringcentral-softphone

Package Overview
Dependencies
Maintainers
0
Versions
64
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

ringcentral-softphone - npm Package Compare versions

Comparing version 1.1.2 to 1.1.3

133

dist/cjs/call-session/inbound.js
"use strict";
var __extends = (this && this.__extends) || (function () {
var extendStatics = function (d, b) {
extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
return extendStatics(d, b);
};
return function (d, b) {
if (typeof b !== "function" && b !== null)
throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __generator = (this && this.__generator) || function (thisArg, body) {
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
function verb(n) { return function (v) { return step([n, v]); }; }
function step(op) {
if (f) throw new TypeError("Generator is already executing.");
while (g && (g = 0, op[0] && (_ = 0)), _) try {
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
if (y = 0, t) op = [op[0] & 2, t.value];
switch (op[0]) {
case 0: case 1: t = op; break;
case 4: _.label++; return { value: op[1], done: false };
case 5: _.label++; y = op[1]; op = [0]; continue;
case 7: op = _.ops.pop(); _.trys.pop(); continue;
default:
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
if (t[2]) _.ops.pop();
_.trys.pop(); continue;
}
op = body.call(thisArg, _);
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
}
};
var __importDefault = (this && this.__importDefault) || function (mod) {

@@ -57,45 +6,43 @@ return (mod && mod.__esModule) ? mod : { "default": mod };

Object.defineProperty(exports, "__esModule", { value: true });
var index_1 = __importDefault(require("./index"));
var index_2 = require("../sip-message/index");
var utils_1 = require("../utils");
var InboundCallSession = /** @class */ (function (_super) {
__extends(InboundCallSession, _super);
function InboundCallSession(softphone, inviteMessage) {
var _this = _super.call(this, softphone, inviteMessage) || this;
_this.localPeer = inviteMessage.headers.To;
_this.remotePeer = inviteMessage.headers.From;
_this.remoteKey = inviteMessage.body.match(/AES_CM_128_HMAC_SHA1_80 inline:([\w+/]+)/)[1];
return _this;
const index_1 = __importDefault(require("./index"));
const index_2 = require("../sip-message/index");
const utils_1 = require("../utils");
class InboundCallSession extends index_1.default {
constructor(softphone, inviteMessage) {
super(softphone, inviteMessage);
this.localPeer = inviteMessage.headers.To;
this.remotePeer = inviteMessage.headers.From;
this.remoteKey = inviteMessage.body.match(/AES_CM_128_HMAC_SHA1_80 inline:([\w+/]+)/)[1];
}
InboundCallSession.prototype.answer = function () {
return __awaiter(this, void 0, void 0, function () {
var answerSDP, newMessage;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
answerSDP = "\nv=0\no=- ".concat(Date.now(), " 0 IN IP4 ").concat(this.softphone.client.localAddress, "\ns=rc-softphone-ts\nc=IN IP4 ").concat(this.softphone.client.localAddress, "\nt=0 0\nm=audio ").concat((0, utils_1.randomInt)(), " RTP/SAVP ").concat(this.softphone.codec.id, " 101\na=rtpmap:").concat(this.softphone.codec.id, " ").concat(this.softphone.codec.name, "\na=rtpmap:101 telephone-event/8000\na=fmtp:101 0-15\na=sendrecv\na=crypto:1 AES_CM_128_HMAC_SHA1_80 inline:").concat(utils_1.localKey, "\n").trim();
newMessage = new index_2.OutboundMessage("SIP/2.0 200 OK", {
Via: this.sipMessage.headers.Via,
"Call-ID": this.sipMessage.headers["Call-ID"],
From: this.sipMessage.headers.From,
To: this.sipMessage.headers.To,
CSeq: this.sipMessage.headers.CSeq,
Contact: "<sip:".concat(this.softphone.sipInfo.username, "@").concat(this.softphone.client.localAddress, ":").concat(this.softphone.client.localPort, ";transport=TLS;ob>"),
Allow: "PRACK, INVITE, ACK, BYE, CANCEL, UPDATE, INFO, SUBSCRIBE, NOTIFY, REFER, MESSAGE, OPTIONS",
Supported: "replaces, 100rel, timer, norefersub",
"Session-Expires": "14400;refresher=uac",
Require: "timer",
"Content-Type": "application/sdp",
}, answerSDP);
return [4 /*yield*/, this.softphone.send(newMessage)];
case 1:
_a.sent();
this.startLocalServices();
return [2 /*return*/];
}
});
});
};
return InboundCallSession;
}(index_1.default));
async answer() {
const answerSDP = `
v=0
o=- ${Date.now()} 0 IN IP4 ${this.softphone.client.localAddress}
s=rc-softphone-ts
c=IN IP4 ${this.softphone.client.localAddress}
t=0 0
m=audio ${(0, utils_1.randomInt)()} RTP/SAVP ${this.softphone.codec.id} 101
a=rtpmap:${this.softphone.codec.id} ${this.softphone.codec.name}
a=rtpmap:101 telephone-event/8000
a=fmtp:101 0-15
a=sendrecv
a=crypto:1 AES_CM_128_HMAC_SHA1_80 inline:${utils_1.localKey}
`.trim();
const newMessage = new index_2.OutboundMessage("SIP/2.0 200 OK", {
Via: this.sipMessage.headers.Via,
"Call-ID": this.sipMessage.headers["Call-ID"],
From: this.sipMessage.headers.From,
To: this.sipMessage.headers.To,
CSeq: this.sipMessage.headers.CSeq,
Contact: `<sip:${this.softphone.sipInfo.username}@${this.softphone.client.localAddress}:${this.softphone.client.localPort};transport=TLS;ob>`,
Allow: "PRACK, INVITE, ACK, BYE, CANCEL, UPDATE, INFO, SUBSCRIBE, NOTIFY, REFER, MESSAGE, OPTIONS",
Supported: "replaces, 100rel, timer, norefersub",
"Session-Expires": "14400;refresher=uac",
Require: "timer",
"Content-Type": "application/sdp",
}, answerSDP);
await this.softphone.send(newMessage);
this.startLocalServices();
}
}
exports.default = InboundCallSession;
"use strict";
var __extends = (this && this.__extends) || (function () {
var extendStatics = function (d, b) {
extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
return extendStatics(d, b);
};
return function (d, b) {
if (typeof b !== "function" && b !== null)
throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __generator = (this && this.__generator) || function (thisArg, body) {
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
function verb(n) { return function (v) { return step([n, v]); }; }
function step(op) {
if (f) throw new TypeError("Generator is already executing.");
while (g && (g = 0, op[0] && (_ = 0)), _) try {
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
if (y = 0, t) op = [op[0] & 2, t.value];
switch (op[0]) {
case 0: case 1: t = op; break;
case 4: _.label++; return { value: op[1], done: false };
case 5: _.label++; y = op[1]; op = [0]; continue;
case 7: op = _.ops.pop(); _.trys.pop(); continue;
default:
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
if (t[2]) _.ops.pop();
_.trys.pop(); continue;
}
op = body.call(thisArg, _);
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
}
};
var __importDefault = (this && this.__importDefault) || function (mod) {

@@ -57,78 +6,67 @@ return (mod && mod.__esModule) ? mod : { "default": mod };

Object.defineProperty(exports, "__esModule", { value: true });
var node_dgram_1 = __importDefault(require("node:dgram"));
var node_events_1 = __importDefault(require("node:events"));
var node_buffer_1 = require("node:buffer");
var werift_rtp_1 = require("werift-rtp");
var dtmf_1 = __importDefault(require("../dtmf"));
var index_1 = require("../sip-message/index");
var utils_1 = require("../utils");
var streamer_1 = __importDefault(require("./streamer"));
var CallSession = /** @class */ (function (_super) {
__extends(CallSession, _super);
function CallSession(softphone, sipMessage) {
var _this = _super.call(this) || this;
_this.disposed = false;
// for audio streaming
_this.ssrc = (0, utils_1.randomInt)();
_this.sequenceNumber = (0, utils_1.randomInt)();
_this.timestamp = (0, utils_1.randomInt)();
_this.softphone = softphone;
_this.encoder = softphone.codec.createEncoder();
_this.decoder = softphone.codec.createDecoder();
_this.sipMessage = sipMessage;
_this.remoteIP = _this.sipMessage.body.match(/c=IN IP4 ([\d.]+)/)[1];
_this.remotePort = parseInt(_this.sipMessage.body.match(/m=audio (\d+) /)[1], 10);
return _this;
const node_dgram_1 = __importDefault(require("node:dgram"));
const node_events_1 = __importDefault(require("node:events"));
const node_buffer_1 = require("node:buffer");
const werift_rtp_1 = require("werift-rtp");
const dtmf_1 = __importDefault(require("../dtmf"));
const index_1 = require("../sip-message/index");
const utils_1 = require("../utils");
const streamer_1 = __importDefault(require("./streamer"));
class CallSession extends node_events_1.default {
softphone;
sipMessage;
socket;
localPeer;
remotePeer;
remoteIP;
remotePort;
disposed = false;
srtpSession;
encoder;
decoder;
// for audio streaming
ssrc = (0, utils_1.randomInt)();
sequenceNumber = (0, utils_1.randomInt)();
timestamp = (0, utils_1.randomInt)();
constructor(softphone, sipMessage) {
super();
this.softphone = softphone;
this.encoder = softphone.codec.createEncoder();
this.decoder = softphone.codec.createDecoder();
this.sipMessage = sipMessage;
this.remoteIP = this.sipMessage.body.match(/c=IN IP4 ([\d.]+)/)[1];
this.remotePort = parseInt(this.sipMessage.body.match(/m=audio (\d+) /)[1], 10);
}
Object.defineProperty(CallSession.prototype, "remoteKey", {
set: function (key) {
var localKeyBuffer = node_buffer_1.Buffer.from(utils_1.localKey, "base64");
var remoteKeyBuffer = node_buffer_1.Buffer.from(key, "base64");
this.srtpSession = new werift_rtp_1.SrtpSession({
profile: 0x0001,
keys: {
localMasterKey: localKeyBuffer.subarray(0, 16),
localMasterSalt: localKeyBuffer.subarray(16, 30),
remoteMasterKey: remoteKeyBuffer.subarray(0, 16),
remoteMasterSalt: remoteKeyBuffer.subarray(16, 30),
},
});
},
enumerable: false,
configurable: true
});
Object.defineProperty(CallSession.prototype, "callId", {
get: function () {
return this.sipMessage.headers["Call-ID"];
},
enumerable: false,
configurable: true
});
CallSession.prototype.send = function (data) {
set remoteKey(key) {
const localKeyBuffer = node_buffer_1.Buffer.from(utils_1.localKey, "base64");
const remoteKeyBuffer = node_buffer_1.Buffer.from(key, "base64");
this.srtpSession = new werift_rtp_1.SrtpSession({
profile: 0x0001,
keys: {
localMasterKey: localKeyBuffer.subarray(0, 16),
localMasterSalt: localKeyBuffer.subarray(16, 30),
remoteMasterKey: remoteKeyBuffer.subarray(0, 16),
remoteMasterSalt: remoteKeyBuffer.subarray(16, 30),
},
});
}
get callId() {
return this.sipMessage.headers["Call-ID"];
}
send(data) {
this.socket.send(data, this.remotePort, this.remoteIP);
};
CallSession.prototype.hangup = function () {
return __awaiter(this, void 0, void 0, function () {
var requestMessage;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
requestMessage = new index_1.RequestMessage("BYE sip:".concat(this.softphone.sipInfo.domain, " SIP/2.0"), {
"Call-ID": this.callId,
From: this.localPeer,
To: this.remotePeer,
Via: "SIP/2.0/TLS ".concat(this.softphone.fakeDomain, ";branch=").concat((0, utils_1.branch)()),
});
return [4 /*yield*/, this.softphone.send(requestMessage)];
case 1:
_a.sent();
return [2 /*return*/];
}
});
}
async hangup() {
const requestMessage = new index_1.RequestMessage(`BYE sip:${this.softphone.sipInfo.domain} SIP/2.0`, {
"Call-ID": this.callId,
From: this.localPeer,
To: this.remotePeer,
Via: `SIP/2.0/TLS ${this.softphone.fakeDomain};branch=${(0, utils_1.branch)()}`,
});
};
CallSession.prototype.sendDTMF = function (char) {
var timestamp = Math.floor(Date.now() / 1000);
var sequenceNumber = timestamp % 65536;
var rtpHeader = new werift_rtp_1.RtpHeader({
await this.softphone.send(requestMessage);
}
sendDTMF(char) {
const timestamp = Math.floor(Date.now() / 1000);
let sequenceNumber = timestamp % 65536;
const rtpHeader = new werift_rtp_1.RtpHeader({
version: 2,

@@ -141,4 +79,4 @@ padding: false,

payloadType: 101,
sequenceNumber: sequenceNumber,
timestamp: timestamp,
sequenceNumber,
timestamp,
ssrc: (0, utils_1.randomInt)(),

@@ -151,18 +89,17 @@ csrcLength: 0,

});
for (var _i = 0, _a = dtmf_1.default.charToPayloads(char); _i < _a.length; _i++) {
var payload = _a[_i];
for (const payload of dtmf_1.default.charToPayloads(char)) {
rtpHeader.sequenceNumber = sequenceNumber++;
var rtpPacket = new werift_rtp_1.RtpPacket(rtpHeader, payload);
const rtpPacket = new werift_rtp_1.RtpPacket(rtpHeader, payload);
this.send(this.srtpSession.encrypt(rtpPacket.payload, rtpPacket.header));
}
};
}
// buffer is the content of a audio file, it is supposed to be uncompressed PCM data
// The audio should be playable by command: play -t raw -b 16 -r 16000 -e signed-integer test.wav
CallSession.prototype.streamAudio = function (input) {
var streamer = new streamer_1.default(this, input);
streamAudio(input) {
const streamer = new streamer_1.default(this, input);
streamer.start();
return streamer;
};
}
// send a single rtp packet
CallSession.prototype.sendPacket = function (rtpPacket) {
sendPacket(rtpPacket) {
if (this.disposed) {

@@ -172,17 +109,16 @@ return;

this.send(this.srtpSession.encrypt(rtpPacket.payload, rtpPacket.header));
};
CallSession.prototype.startLocalServices = function () {
var _this = this;
}
startLocalServices() {
this.socket = node_dgram_1.default.createSocket("udp4");
this.socket.on("message", function (message) {
var rtpPacket = werift_rtp_1.RtpPacket.deSerialize(_this.srtpSession.decrypt(message));
_this.emit("rtpPacket", rtpPacket);
this.socket.on("message", (message) => {
const rtpPacket = werift_rtp_1.RtpPacket.deSerialize(this.srtpSession.decrypt(message));
this.emit("rtpPacket", rtpPacket);
if (rtpPacket.header.payloadType === 101) {
_this.emit("dtmfPacket", rtpPacket);
var char = dtmf_1.default.payloadToChar(rtpPacket.payload);
this.emit("dtmfPacket", rtpPacket);
const char = dtmf_1.default.payloadToChar(rtpPacket.payload);
if (char) {
_this.emit("dtmf", char);
this.emit("dtmf", char);
}
}
else if (rtpPacket.header.payloadType === _this.softphone.codec.id) {
else if (rtpPacket.header.payloadType === this.softphone.codec.id) {
if (rtpPacket.payload.length === 4 &&

@@ -200,6 +136,6 @@ rtpPacket.payload[0] >= 0x00 &&

try {
rtpPacket.payload = _this.decoder.decode(rtpPacket.payload);
_this.emit("audioPacket", rtpPacket);
rtpPacket.payload = this.decoder.decode(rtpPacket.payload);
this.emit("audioPacket", rtpPacket);
}
catch (_a) {
catch {
console.error("opus decode failed", rtpPacket);

@@ -214,29 +150,27 @@ }

this.send("hello");
var byeHandler = function (inboundMessage) {
if (inboundMessage.headers["Call-ID"] !== _this.callId) {
const byeHandler = (inboundMessage) => {
if (inboundMessage.headers["Call-ID"] !== this.callId) {
return;
}
if (inboundMessage.headers.CSeq.endsWith(" BYE")) {
_this.softphone.off("message", byeHandler);
_this.dispose();
this.softphone.off("message", byeHandler);
this.dispose();
}
};
this.softphone.on("message", byeHandler);
};
CallSession.prototype.dispose = function () {
var _a, _b;
}
dispose() {
this.disposed = true;
this.emit("disposed");
this.removeAllListeners();
(_a = this.socket) === null || _a === void 0 ? void 0 : _a.removeAllListeners();
(_b = this.socket) === null || _b === void 0 ? void 0 : _b.close();
};
CallSession.prototype.transfer = function (transferTo) {
var _this = this;
var requestMessage = new index_1.RequestMessage("REFER sip:".concat(this.softphone.sipInfo.username, "@").concat(this.softphone.sipInfo.outboundProxy, ";transport=tls SIP/2.0"), {
Via: "SIP/2.0/TLS ".concat(this.softphone.client.localAddress, ":").concat(this.softphone.client.localPort, ";rport;branch=").concat((0, utils_1.branch)(), ";alias"),
this.socket?.removeAllListeners();
this.socket?.close();
}
transfer(transferTo) {
const requestMessage = new index_1.RequestMessage(`REFER sip:${this.softphone.sipInfo.username}@${this.softphone.sipInfo.outboundProxy};transport=tls SIP/2.0`, {
Via: `SIP/2.0/TLS ${this.softphone.client.localAddress}:${this.softphone.client.localPort};rport;branch=${(0, utils_1.branch)()};alias`,
"Max-Forwards": 70,
From: this.localPeer,
To: this.remotePeer,
Contact: "<sip:".concat(this.softphone.sipInfo.username, "@").concat(this.softphone.client.localAddress, ":").concat(this.softphone.client.localPort, ";transport=TLS;ob>"),
Contact: `<sip:${this.softphone.sipInfo.username}@${this.softphone.client.localAddress}:${this.softphone.client.localPort};transport=TLS;ob>`,
"Call-ID": this.callId,

@@ -248,21 +182,20 @@ Event: "refer",

"Allow-Events": "presence, message-summary, refer",
"Refer-To": "sip:".concat(transferTo, "@").concat(this.softphone.sipInfo.domain),
"Referred-By": "<sip:".concat(this.softphone.sipInfo.username, "@").concat(this.softphone.sipInfo.domain, ">"),
"Refer-To": `sip:${transferTo}@${this.softphone.sipInfo.domain}`,
"Referred-By": `<sip:${this.softphone.sipInfo.username}@${this.softphone.sipInfo.domain}>`,
});
this.softphone.send(requestMessage);
// reply to those NOTIFY messages
var notifyHandler = function (inboundMessage) {
const notifyHandler = (inboundMessage) => {
if (!inboundMessage.subject.startsWith("NOTIFY ")) {
return;
}
var responseMessage = new index_1.ResponseMessage(inboundMessage, 200);
_this.softphone.send(responseMessage);
const responseMessage = new index_1.ResponseMessage(inboundMessage, 200);
this.softphone.send(responseMessage);
if (inboundMessage.body.trim() === "SIP/2.0 200 OK") {
_this.softphone.off("message", notifyHandler);
this.softphone.off("message", notifyHandler);
}
};
this.softphone.on("message", notifyHandler);
};
return CallSession;
}(node_events_1.default));
}
}
exports.default = CallSession;
"use strict";
var __extends = (this && this.__extends) || (function () {
var extendStatics = function (d, b) {
extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
return extendStatics(d, b);
};
return function (d, b) {
if (typeof b !== "function" && b !== null)
throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __generator = (this && this.__generator) || function (thisArg, body) {
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
function verb(n) { return function (v) { return step([n, v]); }; }
function step(op) {
if (f) throw new TypeError("Generator is already executing.");
while (g && (g = 0, op[0] && (_ = 0)), _) try {
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
if (y = 0, t) op = [op[0] & 2, t.value];
switch (op[0]) {
case 0: case 1: t = op; break;
case 4: _.label++; return { value: op[1], done: false };
case 5: _.label++; y = op[1]; op = [0]; continue;
case 7: op = _.ops.pop(); _.trys.pop(); continue;
default:
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
if (t[2]) _.ops.pop();
_.trys.pop(); continue;
}
op = body.call(thisArg, _);
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
}
};
var __importDefault = (this && this.__importDefault) || function (mod) {

@@ -57,67 +6,52 @@ return (mod && mod.__esModule) ? mod : { "default": mod };

Object.defineProperty(exports, "__esModule", { value: true });
var index_1 = __importDefault(require("./index"));
var index_2 = require("../sip-message/index");
var utils_1 = require("../utils");
var OutboundCallSession = /** @class */ (function (_super) {
__extends(OutboundCallSession, _super);
function OutboundCallSession(softphone, answerMessage) {
var _this = _super.call(this, softphone, answerMessage) || this;
_this.localPeer = answerMessage.headers.From;
_this.remotePeer = answerMessage.headers.To;
_this.remoteKey = answerMessage.body.match(/AES_CM_128_HMAC_SHA1_80 inline:([\w+/]+)/)[1];
_this.init();
return _this;
const index_1 = __importDefault(require("./index"));
const index_2 = require("../sip-message/index");
const utils_1 = require("../utils");
class OutboundCallSession extends index_1.default {
constructor(softphone, answerMessage) {
super(softphone, answerMessage);
this.localPeer = answerMessage.headers.From;
this.remotePeer = answerMessage.headers.To;
this.remoteKey = answerMessage.body.match(/AES_CM_128_HMAC_SHA1_80 inline:([\w+/]+)/)[1];
this.init();
}
OutboundCallSession.prototype.init = function () {
var _this = this;
init() {
// wait for user to answer the call
var answerHandler = function (message) {
if (message.headers.CSeq !== _this.sipMessage.headers.CSeq) {
const answerHandler = (message) => {
if (message.headers.CSeq !== this.sipMessage.headers.CSeq) {
return;
}
if (message.subject.startsWith("SIP/2.0 486")) {
_this.softphone.off("message", answerHandler);
_this.emit("busy");
_this.dispose();
this.softphone.off("message", answerHandler);
this.emit("busy");
this.dispose();
return;
}
if (message.subject.startsWith("SIP/2.0 200")) {
_this.softphone.off("message", answerHandler);
_this.emit("answered");
var ackMessage = new index_2.RequestMessage("ACK ".concat((0, utils_1.extractAddress)(_this.remotePeer), " SIP/2.0"), {
"Call-ID": _this.callId,
From: _this.localPeer,
To: _this.remotePeer,
Via: _this.sipMessage.headers.Via,
CSeq: _this.sipMessage.headers.CSeq.replace(" INVITE", " ACK"),
this.softphone.off("message", answerHandler);
this.emit("answered");
const ackMessage = new index_2.RequestMessage(`ACK ${(0, utils_1.extractAddress)(this.remotePeer)} SIP/2.0`, {
"Call-ID": this.callId,
From: this.localPeer,
To: this.remotePeer,
Via: this.sipMessage.headers.Via,
CSeq: this.sipMessage.headers.CSeq.replace(" INVITE", " ACK"),
});
_this.softphone.send(ackMessage);
this.softphone.send(ackMessage);
}
};
this.softphone.on("message", answerHandler);
this.once("answered", function () { return _this.startLocalServices(); });
};
OutboundCallSession.prototype.cancel = function () {
return __awaiter(this, void 0, void 0, function () {
var requestMessage;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
requestMessage = new index_2.RequestMessage("CANCEL ".concat((0, utils_1.extractAddress)(this.remotePeer), " SIP/2.0"), {
"Call-ID": this.callId,
From: this.localPeer,
To: (0, utils_1.withoutTag)(this.remotePeer),
Via: this.sipMessage.headers.Via,
CSeq: this.sipMessage.headers.CSeq.replace(" INVITE", " CANCEL"),
});
return [4 /*yield*/, this.softphone.send(requestMessage)];
case 1:
_a.sent();
return [2 /*return*/];
}
});
this.once("answered", () => this.startLocalServices());
}
async cancel() {
const requestMessage = new index_2.RequestMessage(`CANCEL ${(0, utils_1.extractAddress)(this.remotePeer)} SIP/2.0`, {
"Call-ID": this.callId,
From: this.localPeer,
To: (0, utils_1.withoutTag)(this.remotePeer),
Via: this.sipMessage.headers.Via,
CSeq: this.sipMessage.headers.CSeq.replace(" INVITE", " CANCEL"),
});
};
return OutboundCallSession;
}(index_1.default));
await this.softphone.send(requestMessage);
}
}
exports.default = OutboundCallSession;
"use strict";
var __extends = (this && this.__extends) || (function () {
var extendStatics = function (d, b) {
extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
return extendStatics(d, b);
};
return function (d, b) {
if (typeof b !== "function" && b !== null)
throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
var __importDefault = (this && this.__importDefault) || function (mod) {

@@ -21,42 +6,38 @@ return (mod && mod.__esModule) ? mod : { "default": mod };

Object.defineProperty(exports, "__esModule", { value: true });
var node_events_1 = __importDefault(require("node:events"));
var node_buffer_1 = require("node:buffer");
var werift_rtp_1 = require("werift-rtp");
var Streamer = /** @class */ (function (_super) {
__extends(Streamer, _super);
function Streamer(callSesstion, buffer) {
var _this = _super.call(this) || this;
_this.paused = false;
_this.callSession = callSesstion;
_this.buffer = buffer;
_this.originalBuffer = buffer;
return _this;
const node_events_1 = __importDefault(require("node:events"));
const node_buffer_1 = require("node:buffer");
const werift_rtp_1 = require("werift-rtp");
class Streamer extends node_events_1.default {
paused = false;
callSession;
buffer;
originalBuffer;
constructor(callSesstion, buffer) {
super();
this.callSession = callSesstion;
this.buffer = buffer;
this.originalBuffer = buffer;
}
Streamer.prototype.start = function () {
start() {
this.buffer = this.originalBuffer;
this.paused = false;
this.sendPacket();
};
Streamer.prototype.stop = function () {
}
stop() {
this.buffer = node_buffer_1.Buffer.alloc(0);
};
Streamer.prototype.pause = function () {
}
pause() {
this.paused = true;
};
Streamer.prototype.resume = function () {
}
resume() {
this.paused = false;
this.sendPacket();
};
Object.defineProperty(Streamer.prototype, "finished", {
get: function () {
return this.buffer.length < this.callSession.softphone.codec.packetSize;
},
enumerable: false,
configurable: true
});
Streamer.prototype.sendPacket = function () {
var _this = this;
}
get finished() {
return this.buffer.length < this.callSession.softphone.codec.packetSize;
}
sendPacket() {
if (!this.callSession.disposed && !this.paused && !this.finished) {
var temp = this.callSession.encoder.encode(this.buffer.subarray(0, this.callSession.softphone.codec.packetSize));
var rtpPacket = new werift_rtp_1.RtpPacket(new werift_rtp_1.RtpHeader({
const temp = this.callSession.encoder.encode(this.buffer.subarray(0, this.callSession.softphone.codec.packetSize));
const rtpPacket = new werift_rtp_1.RtpPacket(new werift_rtp_1.RtpHeader({
version: 2,

@@ -90,8 +71,7 @@ padding: false,

else {
setTimeout(function () { return _this.sendPacket(); }, 20);
setTimeout(() => this.sendPacket(), 20);
}
}
};
return Streamer;
}(node_events_1.default));
}
}
exports.default = Streamer;
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var node_buffer_1 = require("node:buffer");
var opus_1 = require("@evan/opus");
var Codec = /** @class */ (function () {
function Codec(name) {
const node_buffer_1 = require("node:buffer");
const opus_1 = require("@evan/opus");
class Codec {
id;
name;
packetSize;
timestampInterval;
createEncoder;
createDecoder;
constructor(name) {
this.name = name;
switch (name) {
case "OPUS/16000": {
this.createEncoder = function () {
var encoder = new opus_1.Encoder({ channels: 1, sample_rate: 16000 });
return { encode: function (pcm) { return node_buffer_1.Buffer.from(encoder.encode(pcm)); } };
this.createEncoder = () => {
const encoder = new opus_1.Encoder({ channels: 1, sample_rate: 16000 });
return { encode: (pcm) => node_buffer_1.Buffer.from(encoder.encode(pcm)) };
};
this.createDecoder = function () {
var decoder = new opus_1.Decoder({ channels: 1, sample_rate: 16000 });
this.createDecoder = () => {
const decoder = new opus_1.Decoder({ channels: 1, sample_rate: 16000 });
return {
decode: function (opus) { return node_buffer_1.Buffer.from(decoder.decode(opus)); },
decode: (opus) => node_buffer_1.Buffer.from(decoder.decode(opus)),
};

@@ -26,10 +32,10 @@ };

case "OPUS/48000/2": {
this.createEncoder = function () {
var encoder = new opus_1.Encoder({ channels: 2, sample_rate: 48000 });
return { encode: function (pcm) { return node_buffer_1.Buffer.from(encoder.encode(pcm)); } };
this.createEncoder = () => {
const encoder = new opus_1.Encoder({ channels: 2, sample_rate: 48000 });
return { encode: (pcm) => node_buffer_1.Buffer.from(encoder.encode(pcm)) };
};
this.createDecoder = function () {
var decoder = new opus_1.Decoder({ channels: 2, sample_rate: 48000 });
this.createDecoder = () => {
const decoder = new opus_1.Decoder({ channels: 2, sample_rate: 48000 });
return {
decode: function (opus) { return node_buffer_1.Buffer.from(decoder.decode(opus)); },
decode: (opus) => node_buffer_1.Buffer.from(decoder.decode(opus)),
};

@@ -43,7 +49,7 @@ };

case "PCMU/8000": {
this.createEncoder = function () {
return { encode: function (pcm) { return pcm; } };
this.createEncoder = () => {
return { encode: (pcm) => pcm };
};
this.createDecoder = function () {
return { decode: function (audio) { return audio; } };
this.createDecoder = () => {
return { decode: (audio) => audio };
};

@@ -56,8 +62,7 @@ this.id = 0;

default: {
throw new Error("unsupported codec: ".concat(name));
throw new Error(`unsupported codec: ${name}`);
}
}
}
return Codec;
}());
}
exports.default = Codec;
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var node_buffer_1 = require("node:buffer");
var DTMF = /** @class */ (function () {
function DTMF() {
}
DTMF.phoneChars = [
const node_buffer_1 = require("node:buffer");
class DTMF {
static phoneChars = [
"0",

@@ -21,3 +19,3 @@ "1",

];
DTMF.payloads = [
static payloads = [
0x00060000,

@@ -30,10 +28,10 @@ 0x000600a0,

];
DTMF.charToPayloads = function (char) {
var index = DTMF.phoneChars.indexOf(char[0]);
static charToPayloads = (char) => {
const index = DTMF.phoneChars.indexOf(char[0]);
if (index === -1) {
throw new Error("invalid phone char");
}
return DTMF.payloads.map(function (payload) {
var temp = payload + index * 0x01000000;
var buffer = node_buffer_1.Buffer.alloc(4);
return DTMF.payloads.map((payload) => {
const temp = payload + index * 0x01000000;
const buffer = node_buffer_1.Buffer.alloc(4);
buffer.writeIntBE(temp, 0, 4);

@@ -43,9 +41,8 @@ return buffer;

};
DTMF.payloadToChar = function (payload) {
var intBE = payload.readIntBE(0, 4);
var index = (intBE - 0x00060000) / 0x01000000;
static payloadToChar = (payload) => {
const intBE = payload.readIntBE(0, 4);
const index = (intBE - 0x00060000) / 0x01000000;
return DTMF.phoneChars[index];
};
return DTMF;
}());
}
exports.default = DTMF;
"use strict";
var __extends = (this && this.__extends) || (function () {
var extendStatics = function (d, b) {
extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
return extendStatics(d, b);
};
return function (d, b) {
if (typeof b !== "function" && b !== null)
throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __generator = (this && this.__generator) || function (thisArg, body) {
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
function verb(n) { return function (v) { return step([n, v]); }; }
function step(op) {
if (f) throw new TypeError("Generator is already executing.");
while (g && (g = 0, op[0] && (_ = 0)), _) try {
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
if (y = 0, t) op = [op[0] & 2, t.value];
switch (op[0]) {
case 0: case 1: t = op; break;
case 4: _.label++; return { value: op[1], done: false };
case 5: _.label++; y = op[1]; op = [0]; continue;
case 7: op = _.ops.pop(); _.trys.pop(); continue;
default:
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
if (t[2]) _.ops.pop();
_.trys.pop(); continue;
}
op = body.call(thisArg, _);
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
}
};
var __importDefault = (this && this.__importDefault) || function (mod) {

@@ -57,36 +6,37 @@ return (mod && mod.__esModule) ? mod : { "default": mod };

Object.defineProperty(exports, "__esModule", { value: true });
var node_events_1 = __importDefault(require("node:events"));
var node_tls_1 = __importDefault(require("node:tls"));
var wait_for_async_1 = __importDefault(require("wait-for-async"));
var inbound_1 = __importDefault(require("./call-session/inbound"));
var outbound_1 = __importDefault(require("./call-session/outbound"));
var index_1 = require("./sip-message/index");
var utils_1 = require("./utils");
var codec_1 = __importDefault(require("./codec"));
var Softphone = /** @class */ (function (_super) {
__extends(Softphone, _super);
function Softphone(sipInfo) {
var _this = _super.call(this) || this;
_this.fakeDomain = (0, utils_1.uuid)() + ".invalid";
_this.fakeEmail = (0, utils_1.uuid)() + "@" + _this.fakeDomain;
_this.connected = false;
_this.instanceId = (0, utils_1.uuid)();
_this.registerCallId = (0, utils_1.uuid)();
const node_events_1 = __importDefault(require("node:events"));
const node_tls_1 = __importDefault(require("node:tls"));
const wait_for_async_1 = __importDefault(require("wait-for-async"));
const inbound_1 = __importDefault(require("./call-session/inbound"));
const outbound_1 = __importDefault(require("./call-session/outbound"));
const index_1 = require("./sip-message/index");
const utils_1 = require("./utils");
const codec_1 = __importDefault(require("./codec"));
class Softphone extends node_events_1.default {
sipInfo;
client;
codec;
fakeDomain = (0, utils_1.uuid)() + ".invalid";
fakeEmail = (0, utils_1.uuid)() + "@" + this.fakeDomain;
intervalHandle;
connected = false;
constructor(sipInfo) {
super();
if (sipInfo.codec === undefined) {
sipInfo.codec = "OPUS/16000";
}
_this.codec = new codec_1.default(sipInfo.codec);
_this.sipInfo = sipInfo;
if (_this.sipInfo.domain === undefined) {
_this.sipInfo.domain = "sip.ringcentral.com";
this.codec = new codec_1.default(sipInfo.codec);
this.sipInfo = sipInfo;
if (this.sipInfo.domain === undefined) {
this.sipInfo.domain = "sip.ringcentral.com";
}
if (_this.sipInfo.outboundProxy === undefined) {
_this.sipInfo.outboundProxy = "sip10.ringcentral.com:5096";
if (this.sipInfo.outboundProxy === undefined) {
this.sipInfo.outboundProxy = "sip10.ringcentral.com:5096";
}
var tokens = _this.sipInfo.outboundProxy.split(":");
_this.client = node_tls_1.default.connect({ host: tokens[0], port: parseInt(tokens[1], 10) }, function () {
_this.connected = true;
const tokens = this.sipInfo.outboundProxy.split(":");
this.client = node_tls_1.default.connect({ host: tokens[0], port: parseInt(tokens[1], 10) }, () => {
this.connected = true;
});
var cache = "";
_this.client.on("data", function (data) {
let cache = "";
this.client.on("data", (data) => {
cache += data.toString("utf-8");

@@ -97,7 +47,7 @@ if (!cache.endsWith("\r\n")) {

// received two empty body messages
var tempMessages = cache
const tempMessages = cache
.split("\r\nContent-Length: 0\r\n\r\n")
.filter(function (message) { return message.trim() !== ""; });
.filter((message) => message.trim() !== "");
cache = "";
for (var i = 0; i < tempMessages.length; i++) {
for (let i = 0; i < tempMessages.length; i++) {
if (!tempMessages[i].includes("Content-Length: ")) {

@@ -107,92 +57,68 @@ tempMessages[i] = tempMessages[i] + "\r\nContent-Length: 0";

}
for (var _i = 0, tempMessages_1 = tempMessages; _i < tempMessages_1.length; _i++) {
var message = tempMessages_1[_i];
_this.emit("message", index_1.InboundMessage.fromString(message));
for (const message of tempMessages) {
this.emit("message", index_1.InboundMessage.fromString(message));
}
});
return _this;
}
Softphone.prototype.register = function () {
return __awaiter(this, void 0, void 0, function () {
var sipRegister;
var _this = this;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
if (!!this.connected) return [3 /*break*/, 2];
return [4 /*yield*/, (0, wait_for_async_1.default)({ interval: 100, condition: function () { return _this.connected; } })];
case 1:
_a.sent();
_a.label = 2;
case 2:
sipRegister = function () { return __awaiter(_this, void 0, void 0, function () {
var fromTag, requestMessage, inboundMessage, wwwAuth, nonce, newMessage;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
fromTag = (0, utils_1.uuid)();
requestMessage = new index_1.RequestMessage("REGISTER sip:".concat(this.sipInfo.domain, " SIP/2.0"), {
Via: "SIP/2.0/TLS ".concat(this.client.localAddress, ":").concat(this.client.localPort, ";rport;branch=").concat((0, utils_1.branch)(), ";alias"),
Route: "<sip:".concat(this.sipInfo.outboundProxy, ";transport=tls;lr>"),
"Max-Forwards": "70",
From: "<sip:".concat(this.sipInfo.username, "@").concat(this.sipInfo.domain, ">;tag=").concat(fromTag),
To: "<sip:".concat(this.sipInfo.username, "@").concat(this.sipInfo.domain, ">"),
"Call-ID": this.registerCallId,
Supported: "outbound, path",
Contact: "<sip:".concat(this.sipInfo.username, "@").concat(this.client.localAddress, ":").concat(this.client.localPort, ";transport=TLS;ob>;reg-id=1;+sip.instance=\"<urn:uuid:").concat(this.instanceId, ">\""),
Expires: 300,
Allow: "PRACK, INVITE, ACK, BYE, CANCEL, UPDATE, INFO, SUBSCRIBE, NOTIFY, REFER, MESSAGE, OPTIONS",
});
return [4 /*yield*/, this.send(requestMessage, true)];
case 1:
inboundMessage = _a.sent();
if (inboundMessage.subject.startsWith("SIP/2.0 200 ")) {
// sometimes the server will return 200 OK directly
return [2 /*return*/];
}
wwwAuth = inboundMessage.headers["Www-Authenticate"] ||
inboundMessage.headers["WWW-Authenticate"];
nonce = wwwAuth.match(/, nonce="(.+?)"/)[1];
newMessage = requestMessage.fork();
newMessage.headers.Authorization = (0, utils_1.generateAuthorization)(this.sipInfo, nonce, "REGISTER");
this.send(newMessage);
return [2 /*return*/];
}
});
}); };
sipRegister();
this.intervalHandle = setInterval(function () {
sipRegister();
}, 3 * 60 * 1000);
this.on("message", function (inboundMessage) {
if (!inboundMessage.subject.startsWith("INVITE sip:")) {
return;
}
var outboundMessage = new index_1.OutboundMessage("SIP/2.0 100 Trying", {
Via: inboundMessage.headers.Via,
"Call-ID": inboundMessage.headers["Call-ID"],
From: inboundMessage.headers.From,
To: inboundMessage.headers.To,
CSeq: inboundMessage.headers.CSeq,
"Content-Length": "0",
});
_this.send(outboundMessage);
_this.emit("invite", inboundMessage);
});
return [2 /*return*/];
}
instanceId = (0, utils_1.uuid)();
registerCallId = (0, utils_1.uuid)();
async register() {
if (!this.connected) {
await (0, wait_for_async_1.default)({ interval: 100, condition: () => this.connected });
}
const sipRegister = async () => {
const fromTag = (0, utils_1.uuid)();
const requestMessage = new index_1.RequestMessage(`REGISTER sip:${this.sipInfo.domain} SIP/2.0`, {
Via: `SIP/2.0/TLS ${this.client.localAddress}:${this.client.localPort};rport;branch=${(0, utils_1.branch)()};alias`,
Route: `<sip:${this.sipInfo.outboundProxy};transport=tls;lr>`,
"Max-Forwards": "70",
From: `<sip:${this.sipInfo.username}@${this.sipInfo.domain}>;tag=${fromTag}`,
To: `<sip:${this.sipInfo.username}@${this.sipInfo.domain}>`,
"Call-ID": this.registerCallId,
Supported: "outbound, path",
Contact: `<sip:${this.sipInfo.username}@${this.client.localAddress}:${this.client.localPort};transport=TLS;ob>;reg-id=1;+sip.instance="<urn:uuid:${this.instanceId}>"`,
Expires: 300,
Allow: "PRACK, INVITE, ACK, BYE, CANCEL, UPDATE, INFO, SUBSCRIBE, NOTIFY, REFER, MESSAGE, OPTIONS",
});
const inboundMessage = await this.send(requestMessage, true);
if (inboundMessage.subject.startsWith("SIP/2.0 200 ")) {
// sometimes the server will return 200 OK directly
return;
}
const wwwAuth = inboundMessage.headers["Www-Authenticate"] ||
inboundMessage.headers["WWW-Authenticate"];
const nonce = wwwAuth.match(/, nonce="(.+?)"/)[1];
const newMessage = requestMessage.fork();
newMessage.headers.Authorization = (0, utils_1.generateAuthorization)(this.sipInfo, nonce, "REGISTER");
this.send(newMessage);
};
sipRegister();
this.intervalHandle = setInterval(() => {
sipRegister();
}, 3 * 60 * 1000);
this.on("message", (inboundMessage) => {
if (!inboundMessage.subject.startsWith("INVITE sip:")) {
return;
}
const outboundMessage = new index_1.OutboundMessage("SIP/2.0 100 Trying", {
Via: inboundMessage.headers.Via,
"Call-ID": inboundMessage.headers["Call-ID"],
From: inboundMessage.headers.From,
To: inboundMessage.headers.To,
CSeq: inboundMessage.headers.CSeq,
"Content-Length": "0",
});
this.send(outboundMessage);
this.emit("invite", inboundMessage);
});
};
Softphone.prototype.enableDebugMode = function () {
this.on("message", function (message) {
return console.log("Receiving...(".concat(new Date(), ")\n") + message.toString());
});
var tlsWrite = this.client.write.bind(this.client);
this.client.write = function (message) {
console.log("Sending...(".concat(new Date(), ")\n") + message);
}
enableDebugMode() {
this.on("message", (message) => console.log(`Receiving...(${new Date()})\n` + message.toString()));
const tlsWrite = this.client.write.bind(this.client);
this.client.write = (message) => {
console.log(`Sending...(${new Date()})\n` + message);
return tlsWrite(message);
};
};
Softphone.prototype.revoke = function () {
}
revoke() {
clearInterval(this.intervalHandle);

@@ -202,14 +128,12 @@ this.removeAllListeners();

this.client.destroy();
};
Softphone.prototype.send = function (message, waitForReply) {
var _this = this;
if (waitForReply === void 0) { waitForReply = false; }
}
send(message, waitForReply = false) {
this.client.write(message.toString());
if (!waitForReply) {
return new Promise(function (resolve) {
return new Promise((resolve) => {
resolve(undefined);
});
}
return new Promise(function (resolve) {
var messageListerner = function (inboundMessage) {
return new Promise((resolve) => {
const messageListerner = (inboundMessage) => {
if (inboundMessage.headers.CSeq !== message.headers.CSeq) {

@@ -221,77 +145,55 @@ return;

}
_this.off("message", messageListerner);
this.off("message", messageListerner);
resolve(inboundMessage);
};
_this.on("message", messageListerner);
this.on("message", messageListerner);
});
};
Softphone.prototype.answer = function (inviteMessage) {
return __awaiter(this, void 0, void 0, function () {
var inboundCallSession;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
inboundCallSession = new inbound_1.default(this, inviteMessage);
return [4 /*yield*/, inboundCallSession.answer()];
case 1:
_a.sent();
return [2 /*return*/, inboundCallSession];
}
});
});
};
}
async answer(inviteMessage) {
const inboundCallSession = new inbound_1.default(this, inviteMessage);
await inboundCallSession.answer();
return inboundCallSession;
}
// decline an inbound call
Softphone.prototype.decline = function (inviteMessage) {
return __awaiter(this, void 0, void 0, function () {
var newMessage;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
newMessage = new index_1.ResponseMessage(inviteMessage, 603);
return [4 /*yield*/, this.send(newMessage)];
case 1:
_a.sent();
return [2 /*return*/];
}
});
});
};
Softphone.prototype.call = function (callee) {
return __awaiter(this, void 0, void 0, function () {
var offerSDP, inviteMessage, inboundMessage, proxyAuthenticate, nonce, newMessage, progressMessage;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
offerSDP = "\nv=0\no=- ".concat(Date.now(), " 0 IN IP4 ").concat(this.client.localAddress, "\ns=rc-softphone-ts\nc=IN IP4 ").concat(this.client.localAddress, "\nt=0 0\nm=audio ").concat((0, utils_1.randomInt)(), " RTP/SAVP ").concat(this.codec.id, " 101\na=rtpmap:").concat(this.codec.id, " ").concat(this.codec.name, "\na=rtpmap:101 telephone-event/8000\na=fmtp:101 0-15\na=sendrecv\na=crypto:1 AES_CM_128_HMAC_SHA1_80 inline:").concat(utils_1.localKey, "\n ").trim();
inviteMessage = new index_1.RequestMessage("INVITE sip:".concat(callee, " SIP/2.0"), {
Via: "SIP/2.0/TLS ".concat(this.client.localAddress, ":").concat(this.client.localPort, ";rport;branch=").concat((0, utils_1.branch)(), ";alias"),
"Max-Forwards": 70,
From: "<sip:".concat(this.sipInfo.username, "@").concat(this.sipInfo.domain, ">;tag=").concat((0, utils_1.uuid)()),
To: "<sip:".concat(callee, "@sip.ringcentral.com>"),
Contact: " <sip:".concat(this.sipInfo.username, "@").concat(this.client.localAddress, ":").concat(this.client.localPort, ";transport=TLS;ob>"),
"Call-ID": (0, utils_1.uuid)(),
Route: "<sip:".concat(this.sipInfo.outboundProxy, ";transport=tls;lr>"),
Allow: "PRACK, INVITE, ACK, BYE, CANCEL, UPDATE, INFO, SUBSCRIBE, NOTIFY, REFER, MESSAGE, OPTIONS",
Supported: "replaces, 100rel, timer, norefersub",
"Session-Expires": 1800,
"Min-SE": 90,
"Content-Type": "application/sdp",
}, offerSDP);
return [4 /*yield*/, this.send(inviteMessage, true)];
case 1:
inboundMessage = _a.sent();
proxyAuthenticate = inboundMessage.headers["Proxy-Authenticate"];
nonce = proxyAuthenticate.match(/, nonce="(.+?)"/)[1];
newMessage = inviteMessage.fork();
newMessage.headers["Proxy-Authorization"] = (0, utils_1.generateAuthorization)(this.sipInfo, nonce, "INVITE");
return [4 /*yield*/, this.send(newMessage, true)];
case 2:
progressMessage = _a.sent();
return [2 /*return*/, new outbound_1.default(this, progressMessage)];
}
});
});
};
return Softphone;
}(node_events_1.default));
async decline(inviteMessage) {
const newMessage = new index_1.ResponseMessage(inviteMessage, 603);
await this.send(newMessage);
}
async call(callee) {
const offerSDP = `
v=0
o=- ${Date.now()} 0 IN IP4 ${this.client.localAddress}
s=rc-softphone-ts
c=IN IP4 ${this.client.localAddress}
t=0 0
m=audio ${(0, utils_1.randomInt)()} RTP/SAVP ${this.codec.id} 101
a=rtpmap:${this.codec.id} ${this.codec.name}
a=rtpmap:101 telephone-event/8000
a=fmtp:101 0-15
a=sendrecv
a=crypto:1 AES_CM_128_HMAC_SHA1_80 inline:${utils_1.localKey}
`.trim();
const inviteMessage = new index_1.RequestMessage(`INVITE sip:${callee} SIP/2.0`, {
Via: `SIP/2.0/TLS ${this.client.localAddress}:${this.client.localPort};rport;branch=${(0, utils_1.branch)()};alias`,
"Max-Forwards": 70,
From: `<sip:${this.sipInfo.username}@${this.sipInfo.domain}>;tag=${(0, utils_1.uuid)()}`,
To: `<sip:${callee}@sip.ringcentral.com>`,
Contact: ` <sip:${this.sipInfo.username}@${this.client.localAddress}:${this.client.localPort};transport=TLS;ob>`,
"Call-ID": (0, utils_1.uuid)(),
Route: `<sip:${this.sipInfo.outboundProxy};transport=tls;lr>`,
Allow: `PRACK, INVITE, ACK, BYE, CANCEL, UPDATE, INFO, SUBSCRIBE, NOTIFY, REFER, MESSAGE, OPTIONS`,
Supported: `replaces, 100rel, timer, norefersub`,
"Session-Expires": 1800,
"Min-SE": 90,
"Content-Type": "application/sdp",
}, offerSDP);
const inboundMessage = await this.send(inviteMessage, true);
const proxyAuthenticate = inboundMessage.headers["Proxy-Authenticate"];
const nonce = proxyAuthenticate.match(/, nonce="(.+?)"/)[1];
const newMessage = inviteMessage.fork();
newMessage.headers["Proxy-Authorization"] = (0, utils_1.generateAuthorization)(this.sipInfo, nonce, "INVITE");
const progressMessage = await this.send(newMessage, true);
return new outbound_1.default(this, progressMessage);
}
}
exports.default = Softphone;
"use strict";
var __extends = (this && this.__extends) || (function () {
var extendStatics = function (d, b) {
extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
return extendStatics(d, b);
};
return function (d, b) {
if (typeof b !== "function" && b !== null)
throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
var __importDefault = (this && this.__importDefault) || function (mod) {

@@ -21,16 +6,12 @@ return (mod && mod.__esModule) ? mod : { "default": mod };

Object.defineProperty(exports, "__esModule", { value: true });
var utils_1 = require("../../utils");
var sip_message_1 = __importDefault(require("../sip-message"));
var InboundMessage = /** @class */ (function (_super) {
__extends(InboundMessage, _super);
function InboundMessage() {
return _super !== null && _super.apply(this, arguments) || this;
}
InboundMessage.fromString = function (str) {
var sipMessage = new sip_message_1.default();
var _a = str.split("\r\n\r\n"), init = _a[0], body = _a.slice(1);
const utils_1 = require("../../utils");
const sip_message_1 = __importDefault(require("../sip-message"));
class InboundMessage extends sip_message_1.default {
static fromString(str) {
const sipMessage = new sip_message_1.default();
const [init, ...body] = str.split("\r\n\r\n");
sipMessage.body = body.join("\r\n\r\n");
var _b = init.split("\r\n"), subject = _b[0], headers = _b.slice(1);
const [subject, ...headers] = init.split("\r\n");
sipMessage.subject = subject;
sipMessage.headers = Object.fromEntries(headers.map(function (line) { return line.split(": "); }));
sipMessage.headers = Object.fromEntries(headers.map((line) => line.split(": ")));
if (sipMessage.headers.To && !sipMessage.headers.To.includes(";tag=")) {

@@ -40,5 +21,4 @@ sipMessage.headers.To += ";tag=" + (0, utils_1.uuid)(); // generate local tag

return sipMessage;
};
return InboundMessage;
}(sip_message_1.default));
}
}
exports.default = InboundMessage;
"use strict";
var __extends = (this && this.__extends) || (function () {
var extendStatics = function (d, b) {
extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
return extendStatics(d, b);
};
return function (d, b) {
if (typeof b !== "function" && b !== null)
throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
var __importDefault = (this && this.__importDefault) || function (mod) {

@@ -21,16 +6,10 @@ return (mod && mod.__esModule) ? mod : { "default": mod };

Object.defineProperty(exports, "__esModule", { value: true });
var sip_message_1 = __importDefault(require("../sip-message"));
var OutboundMessage = /** @class */ (function (_super) {
__extends(OutboundMessage, _super);
function OutboundMessage(subject, headers, body) {
if (subject === void 0) { subject = ""; }
if (headers === void 0) { headers = {}; }
if (body === void 0) { body = ""; }
var _this = _super.call(this, subject, headers, body) || this;
_this.headers["Content-Length"] = _this.body.length.toString();
_this.headers["User-Agent"] = "ringcentral-softphone-ts";
return _this;
const sip_message_1 = __importDefault(require("../sip-message"));
class OutboundMessage extends sip_message_1.default {
constructor(subject = "", headers = {}, body = "") {
super(subject, headers, body);
this.headers["Content-Length"] = this.body.length.toString();
this.headers["User-Agent"] = "ringcentral-softphone-ts";
}
return OutboundMessage;
}(sip_message_1.default));
}
exports.default = OutboundMessage;
"use strict";
var __extends = (this && this.__extends) || (function () {
var extendStatics = function (d, b) {
extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
return extendStatics(d, b);
};
return function (d, b) {
if (typeof b !== "function" && b !== null)
throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
var __assign = (this && this.__assign) || function () {
__assign = Object.assign || function(t) {
for (var s, i = 1, n = arguments.length; i < n; i++) {
s = arguments[i];
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
t[p] = s[p];
}
return t;
};
return __assign.apply(this, arguments);
};
var __importDefault = (this && this.__importDefault) || function (mod) {

@@ -32,30 +6,24 @@ return (mod && mod.__esModule) ? mod : { "default": mod };

Object.defineProperty(exports, "__esModule", { value: true });
var index_1 = __importDefault(require("./index"));
var utils_1 = require("../../utils");
var cseq = Math.floor(Math.random() * 10000);
var RequestMessage = /** @class */ (function (_super) {
__extends(RequestMessage, _super);
function RequestMessage(subject, headers, body) {
if (subject === void 0) { subject = ""; }
if (headers === void 0) { headers = {}; }
if (body === void 0) { body = ""; }
var _this = _super.call(this, subject, headers, body) || this;
if (_this.headers.CSeq === undefined) {
_this.newCseq();
const index_1 = __importDefault(require("./index"));
const utils_1 = require("../../utils");
let cseq = Math.floor(Math.random() * 10000);
class RequestMessage extends index_1.default {
constructor(subject = "", headers = {}, body = "") {
super(subject, headers, body);
if (this.headers.CSeq === undefined) {
this.newCseq();
}
return _this;
}
RequestMessage.prototype.newCseq = function () {
this.headers.CSeq = "".concat(++cseq, " ").concat(this.subject.split(" ")[0]);
};
RequestMessage.prototype.fork = function () {
var newMessage = new RequestMessage(this.subject, __assign({}, this.headers), this.body);
newCseq() {
this.headers.CSeq = `${++cseq} ${this.subject.split(" ")[0]}`;
}
fork() {
const newMessage = new RequestMessage(this.subject, { ...this.headers }, this.body);
newMessage.newCseq();
if (newMessage.headers.Via) {
newMessage.headers.Via = newMessage.headers.Via.replace(/;branch=.+?$/, ";branch=".concat((0, utils_1.branch)()));
newMessage.headers.Via = newMessage.headers.Via.replace(/;branch=.+?$/, `;branch=${(0, utils_1.branch)()}`);
}
return newMessage;
};
return RequestMessage;
}(index_1.default));
}
}
exports.default = RequestMessage;
"use strict";
var __extends = (this && this.__extends) || (function () {
var extendStatics = function (d, b) {
extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
return extendStatics(d, b);
};
return function (d, b) {
if (typeof b !== "function" && b !== null)
throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
var __assign = (this && this.__assign) || function () {
__assign = Object.assign || function(t) {
for (var s, i = 1, n = arguments.length; i < n; i++) {
s = arguments[i];
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
t[p] = s[p];
}
return t;
};
return __assign.apply(this, arguments);
};
var __importDefault = (this && this.__importDefault) || function (mod) {

@@ -32,22 +6,16 @@ return (mod && mod.__esModule) ? mod : { "default": mod };

Object.defineProperty(exports, "__esModule", { value: true });
var index_1 = __importDefault(require("./index"));
var response_codes_1 = __importDefault(require("../response-codes"));
var ResponseMessage = /** @class */ (function (_super) {
__extends(ResponseMessage, _super);
function ResponseMessage(inboundMessage, responseCode, headers, body) {
if (headers === void 0) { headers = {}; }
if (body === void 0) { body = ""; }
var _this = _super.call(this, undefined, __assign({}, headers), body) || this;
_this.subject = "SIP/2.0 ".concat(responseCode, " ").concat(response_codes_1.default[responseCode]);
var keys = ["Via", "From", "To", "Call-ID", "CSeq"];
for (var _i = 0, keys_1 = keys; _i < keys_1.length; _i++) {
var key = keys_1[_i];
const index_1 = __importDefault(require("./index"));
const response_codes_1 = __importDefault(require("../response-codes"));
class ResponseMessage extends index_1.default {
constructor(inboundMessage, responseCode, headers = {}, body = "") {
super(undefined, { ...headers }, body);
this.subject = `SIP/2.0 ${responseCode} ${response_codes_1.default[responseCode]}`;
const keys = ["Via", "From", "To", "Call-ID", "CSeq"];
for (const key of keys) {
if (inboundMessage.headers[key]) {
_this.headers[key] = inboundMessage.headers[key];
this.headers[key] = inboundMessage.headers[key];
}
}
return _this;
}
return ResponseMessage;
}(index_1.default));
}
exports.default = ResponseMessage;
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
// Ref: https://en.wikipedia.org/wiki/List_of_SIP_response_codes'
var responseCodes = {
const responseCodes = {
100: "Trying",

@@ -6,0 +6,0 @@ 180: "Ringing",

"use strict";
var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
if (ar || !(i in from)) {
if (!ar) ar = Array.prototype.slice.call(from, 0, i);
ar[i] = from[i];
}
}
return to.concat(ar || Array.prototype.slice.call(from));
};
Object.defineProperty(exports, "__esModule", { value: true });
var SipMessage = /** @class */ (function () {
function SipMessage(subject, headers, body) {
if (subject === void 0) { subject = ""; }
if (headers === void 0) { headers = {}; }
if (body === void 0) { body = ""; }
class SipMessage {
subject;
headers;
body;
constructor(subject = "", headers = {}, body = "") {
this.subject = subject;

@@ -27,14 +18,12 @@ this.headers = headers;

}
SipMessage.prototype.toString = function () {
var _this = this;
var r = __spreadArray(__spreadArray([
this.subject
], Object.keys(this.headers).map(function (key) { return "".concat(key, ": ").concat(_this.headers[key]); }), true), [
toString() {
const r = [
this.subject,
...Object.keys(this.headers).map((key) => `${key}: ${this.headers[key]}`),
"",
this.body,
], false).join("\r\n");
].join("\r\n");
return r;
};
return SipMessage;
}());
}
}
exports.default = SipMessage;

@@ -7,37 +7,35 @@ "use strict";

exports.localKey = exports.extractAddress = exports.withoutTag = exports.randomInt = exports.branch = exports.uuid = exports.generateAuthorization = void 0;
var node_crypto_1 = __importDefault(require("node:crypto"));
var md5 = function (s) { return node_crypto_1.default.createHash("md5").update(s).digest("hex"); };
var generateResponse = function (sipInfo, endpoint, nonce) {
var ha1 = md5("".concat(sipInfo.authorizationId, ":").concat(sipInfo.domain, ":").concat(sipInfo.password));
var ha2 = md5(endpoint);
var response = md5("".concat(ha1, ":").concat(nonce, ":").concat(ha2));
const node_crypto_1 = __importDefault(require("node:crypto"));
const md5 = (s) => node_crypto_1.default.createHash("md5").update(s).digest("hex");
const generateResponse = (sipInfo, endpoint, nonce) => {
const ha1 = md5(`${sipInfo.authorizationId}:${sipInfo.domain}:${sipInfo.password}`);
const ha2 = md5(endpoint);
const response = md5(`${ha1}:${nonce}:${ha2}`);
return response;
};
var generateAuthorization = function (sipInfo, nonce, method) {
var authObj = {
const generateAuthorization = (sipInfo, nonce, method) => {
const authObj = {
"Digest algorithm": "MD5",
username: sipInfo.authorizationId,
realm: sipInfo.domain,
nonce: nonce,
uri: "sip:".concat(sipInfo.domain),
response: generateResponse(sipInfo, "".concat(method, ":sip:").concat(sipInfo.domain), nonce),
nonce,
uri: `sip:${sipInfo.domain}`,
response: generateResponse(sipInfo, `${method}:sip:${sipInfo.domain}`, nonce),
};
return Object.keys(authObj)
.map(function (key) { return "".concat(key, "=\"").concat(authObj[key], "\""); })
.map((key) => `${key}="${authObj[key]}"`)
.join(", ");
};
exports.generateAuthorization = generateAuthorization;
var uuid = function () { return node_crypto_1.default.randomUUID(); };
const uuid = () => node_crypto_1.default.randomUUID();
exports.uuid = uuid;
var branch = function () { return "z9hG4bK-" + (0, exports.uuid)(); };
const branch = () => "z9hG4bK-" + (0, exports.uuid)();
exports.branch = branch;
var randomInt = function () {
return Math.floor(Math.random() * (65535 - 1024 + 1)) + 1024;
};
const randomInt = () => Math.floor(Math.random() * (65535 - 1024 + 1)) + 1024;
exports.randomInt = randomInt;
var withoutTag = function (s) { return s.replace(/;tag=.*$/, ""); };
const withoutTag = (s) => s.replace(/;tag=.*$/, "");
exports.withoutTag = withoutTag;
var extractAddress = function (s) { var _a; return (_a = s.match(/<(sip:.+?)>/)) === null || _a === void 0 ? void 0 : _a[1]; };
const extractAddress = (s) => s.match(/<(sip:.+?)>/)?.[1];
exports.extractAddress = extractAddress;
var keyAndSalt = node_crypto_1.default.randomBytes(30);
const keyAndSalt = node_crypto_1.default.randomBytes(30);
exports.localKey = keyAndSalt.toString("base64").replace(/=+$/, "");

@@ -1,95 +0,42 @@

var __extends = (this && this.__extends) || (function () {
var extendStatics = function (d, b) {
extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
return extendStatics(d, b);
};
return function (d, b) {
if (typeof b !== "function" && b !== null)
throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __generator = (this && this.__generator) || function (thisArg, body) {
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
function verb(n) { return function (v) { return step([n, v]); }; }
function step(op) {
if (f) throw new TypeError("Generator is already executing.");
while (g && (g = 0, op[0] && (_ = 0)), _) try {
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
if (y = 0, t) op = [op[0] & 2, t.value];
switch (op[0]) {
case 0: case 1: t = op; break;
case 4: _.label++; return { value: op[1], done: false };
case 5: _.label++; y = op[1]; op = [0]; continue;
case 7: op = _.ops.pop(); _.trys.pop(); continue;
default:
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
if (t[2]) _.ops.pop();
_.trys.pop(); continue;
}
op = body.call(thisArg, _);
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
}
};
import CallSession from "./index";
import { OutboundMessage } from "../sip-message/index";
import { localKey, randomInt } from "../utils";
var InboundCallSession = /** @class */ (function (_super) {
__extends(InboundCallSession, _super);
function InboundCallSession(softphone, inviteMessage) {
var _this = _super.call(this, softphone, inviteMessage) || this;
_this.localPeer = inviteMessage.headers.To;
_this.remotePeer = inviteMessage.headers.From;
_this.remoteKey = inviteMessage.body.match(/AES_CM_128_HMAC_SHA1_80 inline:([\w+/]+)/)[1];
return _this;
class InboundCallSession extends CallSession {
constructor(softphone, inviteMessage) {
super(softphone, inviteMessage);
this.localPeer = inviteMessage.headers.To;
this.remotePeer = inviteMessage.headers.From;
this.remoteKey = inviteMessage.body.match(/AES_CM_128_HMAC_SHA1_80 inline:([\w+/]+)/)[1];
}
InboundCallSession.prototype.answer = function () {
return __awaiter(this, void 0, void 0, function () {
var answerSDP, newMessage;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
answerSDP = "\nv=0\no=- ".concat(Date.now(), " 0 IN IP4 ").concat(this.softphone.client.localAddress, "\ns=rc-softphone-ts\nc=IN IP4 ").concat(this.softphone.client.localAddress, "\nt=0 0\nm=audio ").concat(randomInt(), " RTP/SAVP ").concat(this.softphone.codec.id, " 101\na=rtpmap:").concat(this.softphone.codec.id, " ").concat(this.softphone.codec.name, "\na=rtpmap:101 telephone-event/8000\na=fmtp:101 0-15\na=sendrecv\na=crypto:1 AES_CM_128_HMAC_SHA1_80 inline:").concat(localKey, "\n").trim();
newMessage = new OutboundMessage("SIP/2.0 200 OK", {
Via: this.sipMessage.headers.Via,
"Call-ID": this.sipMessage.headers["Call-ID"],
From: this.sipMessage.headers.From,
To: this.sipMessage.headers.To,
CSeq: this.sipMessage.headers.CSeq,
Contact: "<sip:".concat(this.softphone.sipInfo.username, "@").concat(this.softphone.client.localAddress, ":").concat(this.softphone.client.localPort, ";transport=TLS;ob>"),
Allow: "PRACK, INVITE, ACK, BYE, CANCEL, UPDATE, INFO, SUBSCRIBE, NOTIFY, REFER, MESSAGE, OPTIONS",
Supported: "replaces, 100rel, timer, norefersub",
"Session-Expires": "14400;refresher=uac",
Require: "timer",
"Content-Type": "application/sdp",
}, answerSDP);
return [4 /*yield*/, this.softphone.send(newMessage)];
case 1:
_a.sent();
this.startLocalServices();
return [2 /*return*/];
}
});
});
};
return InboundCallSession;
}(CallSession));
async answer() {
const answerSDP = `
v=0
o=- ${Date.now()} 0 IN IP4 ${this.softphone.client.localAddress}
s=rc-softphone-ts
c=IN IP4 ${this.softphone.client.localAddress}
t=0 0
m=audio ${randomInt()} RTP/SAVP ${this.softphone.codec.id} 101
a=rtpmap:${this.softphone.codec.id} ${this.softphone.codec.name}
a=rtpmap:101 telephone-event/8000
a=fmtp:101 0-15
a=sendrecv
a=crypto:1 AES_CM_128_HMAC_SHA1_80 inline:${localKey}
`.trim();
const newMessage = new OutboundMessage("SIP/2.0 200 OK", {
Via: this.sipMessage.headers.Via,
"Call-ID": this.sipMessage.headers["Call-ID"],
From: this.sipMessage.headers.From,
To: this.sipMessage.headers.To,
CSeq: this.sipMessage.headers.CSeq,
Contact: `<sip:${this.softphone.sipInfo.username}@${this.softphone.client.localAddress}:${this.softphone.client.localPort};transport=TLS;ob>`,
Allow: "PRACK, INVITE, ACK, BYE, CANCEL, UPDATE, INFO, SUBSCRIBE, NOTIFY, REFER, MESSAGE, OPTIONS",
Supported: "replaces, 100rel, timer, norefersub",
"Session-Expires": "14400;refresher=uac",
Require: "timer",
"Content-Type": "application/sdp",
}, answerSDP);
await this.softphone.send(newMessage);
this.startLocalServices();
}
}
export default InboundCallSession;

@@ -1,52 +0,1 @@

var __extends = (this && this.__extends) || (function () {
var extendStatics = function (d, b) {
extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
return extendStatics(d, b);
};
return function (d, b) {
if (typeof b !== "function" && b !== null)
throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __generator = (this && this.__generator) || function (thisArg, body) {
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
function verb(n) { return function (v) { return step([n, v]); }; }
function step(op) {
if (f) throw new TypeError("Generator is already executing.");
while (g && (g = 0, op[0] && (_ = 0)), _) try {
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
if (y = 0, t) op = [op[0] & 2, t.value];
switch (op[0]) {
case 0: case 1: t = op; break;
case 4: _.label++; return { value: op[1], done: false };
case 5: _.label++; y = op[1]; op = [0]; continue;
case 7: op = _.ops.pop(); _.trys.pop(); continue;
default:
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
if (t[2]) _.ops.pop();
_.trys.pop(); continue;
}
op = body.call(thisArg, _);
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
}
};
import dgram from "node:dgram";

@@ -60,70 +9,59 @@ import EventEmitter from "node:events";

import Streamer from "./streamer";
var CallSession = /** @class */ (function (_super) {
__extends(CallSession, _super);
function CallSession(softphone, sipMessage) {
var _this = _super.call(this) || this;
_this.disposed = false;
// for audio streaming
_this.ssrc = randomInt();
_this.sequenceNumber = randomInt();
_this.timestamp = randomInt();
_this.softphone = softphone;
_this.encoder = softphone.codec.createEncoder();
_this.decoder = softphone.codec.createDecoder();
_this.sipMessage = sipMessage;
_this.remoteIP = _this.sipMessage.body.match(/c=IN IP4 ([\d.]+)/)[1];
_this.remotePort = parseInt(_this.sipMessage.body.match(/m=audio (\d+) /)[1], 10);
return _this;
class CallSession extends EventEmitter {
softphone;
sipMessage;
socket;
localPeer;
remotePeer;
remoteIP;
remotePort;
disposed = false;
srtpSession;
encoder;
decoder;
// for audio streaming
ssrc = randomInt();
sequenceNumber = randomInt();
timestamp = randomInt();
constructor(softphone, sipMessage) {
super();
this.softphone = softphone;
this.encoder = softphone.codec.createEncoder();
this.decoder = softphone.codec.createDecoder();
this.sipMessage = sipMessage;
this.remoteIP = this.sipMessage.body.match(/c=IN IP4 ([\d.]+)/)[1];
this.remotePort = parseInt(this.sipMessage.body.match(/m=audio (\d+) /)[1], 10);
}
Object.defineProperty(CallSession.prototype, "remoteKey", {
set: function (key) {
var localKeyBuffer = Buffer.from(localKey, "base64");
var remoteKeyBuffer = Buffer.from(key, "base64");
this.srtpSession = new SrtpSession({
profile: 0x0001,
keys: {
localMasterKey: localKeyBuffer.subarray(0, 16),
localMasterSalt: localKeyBuffer.subarray(16, 30),
remoteMasterKey: remoteKeyBuffer.subarray(0, 16),
remoteMasterSalt: remoteKeyBuffer.subarray(16, 30),
},
});
},
enumerable: false,
configurable: true
});
Object.defineProperty(CallSession.prototype, "callId", {
get: function () {
return this.sipMessage.headers["Call-ID"];
},
enumerable: false,
configurable: true
});
CallSession.prototype.send = function (data) {
set remoteKey(key) {
const localKeyBuffer = Buffer.from(localKey, "base64");
const remoteKeyBuffer = Buffer.from(key, "base64");
this.srtpSession = new SrtpSession({
profile: 0x0001,
keys: {
localMasterKey: localKeyBuffer.subarray(0, 16),
localMasterSalt: localKeyBuffer.subarray(16, 30),
remoteMasterKey: remoteKeyBuffer.subarray(0, 16),
remoteMasterSalt: remoteKeyBuffer.subarray(16, 30),
},
});
}
get callId() {
return this.sipMessage.headers["Call-ID"];
}
send(data) {
this.socket.send(data, this.remotePort, this.remoteIP);
};
CallSession.prototype.hangup = function () {
return __awaiter(this, void 0, void 0, function () {
var requestMessage;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
requestMessage = new RequestMessage("BYE sip:".concat(this.softphone.sipInfo.domain, " SIP/2.0"), {
"Call-ID": this.callId,
From: this.localPeer,
To: this.remotePeer,
Via: "SIP/2.0/TLS ".concat(this.softphone.fakeDomain, ";branch=").concat(branch()),
});
return [4 /*yield*/, this.softphone.send(requestMessage)];
case 1:
_a.sent();
return [2 /*return*/];
}
});
}
async hangup() {
const requestMessage = new RequestMessage(`BYE sip:${this.softphone.sipInfo.domain} SIP/2.0`, {
"Call-ID": this.callId,
From: this.localPeer,
To: this.remotePeer,
Via: `SIP/2.0/TLS ${this.softphone.fakeDomain};branch=${branch()}`,
});
};
CallSession.prototype.sendDTMF = function (char) {
var timestamp = Math.floor(Date.now() / 1000);
var sequenceNumber = timestamp % 65536;
var rtpHeader = new RtpHeader({
await this.softphone.send(requestMessage);
}
sendDTMF(char) {
const timestamp = Math.floor(Date.now() / 1000);
let sequenceNumber = timestamp % 65536;
const rtpHeader = new RtpHeader({
version: 2,

@@ -136,4 +74,4 @@ padding: false,

payloadType: 101,
sequenceNumber: sequenceNumber,
timestamp: timestamp,
sequenceNumber,
timestamp,
ssrc: randomInt(),

@@ -146,18 +84,17 @@ csrcLength: 0,

});
for (var _i = 0, _a = DTMF.charToPayloads(char); _i < _a.length; _i++) {
var payload = _a[_i];
for (const payload of DTMF.charToPayloads(char)) {
rtpHeader.sequenceNumber = sequenceNumber++;
var rtpPacket = new RtpPacket(rtpHeader, payload);
const rtpPacket = new RtpPacket(rtpHeader, payload);
this.send(this.srtpSession.encrypt(rtpPacket.payload, rtpPacket.header));
}
};
}
// buffer is the content of a audio file, it is supposed to be uncompressed PCM data
// The audio should be playable by command: play -t raw -b 16 -r 16000 -e signed-integer test.wav
CallSession.prototype.streamAudio = function (input) {
var streamer = new Streamer(this, input);
streamAudio(input) {
const streamer = new Streamer(this, input);
streamer.start();
return streamer;
};
}
// send a single rtp packet
CallSession.prototype.sendPacket = function (rtpPacket) {
sendPacket(rtpPacket) {
if (this.disposed) {

@@ -167,17 +104,16 @@ return;

this.send(this.srtpSession.encrypt(rtpPacket.payload, rtpPacket.header));
};
CallSession.prototype.startLocalServices = function () {
var _this = this;
}
startLocalServices() {
this.socket = dgram.createSocket("udp4");
this.socket.on("message", function (message) {
var rtpPacket = RtpPacket.deSerialize(_this.srtpSession.decrypt(message));
_this.emit("rtpPacket", rtpPacket);
this.socket.on("message", (message) => {
const rtpPacket = RtpPacket.deSerialize(this.srtpSession.decrypt(message));
this.emit("rtpPacket", rtpPacket);
if (rtpPacket.header.payloadType === 101) {
_this.emit("dtmfPacket", rtpPacket);
var char = DTMF.payloadToChar(rtpPacket.payload);
this.emit("dtmfPacket", rtpPacket);
const char = DTMF.payloadToChar(rtpPacket.payload);
if (char) {
_this.emit("dtmf", char);
this.emit("dtmf", char);
}
}
else if (rtpPacket.header.payloadType === _this.softphone.codec.id) {
else if (rtpPacket.header.payloadType === this.softphone.codec.id) {
if (rtpPacket.payload.length === 4 &&

@@ -195,6 +131,6 @@ rtpPacket.payload[0] >= 0x00 &&

try {
rtpPacket.payload = _this.decoder.decode(rtpPacket.payload);
_this.emit("audioPacket", rtpPacket);
rtpPacket.payload = this.decoder.decode(rtpPacket.payload);
this.emit("audioPacket", rtpPacket);
}
catch (_a) {
catch {
console.error("opus decode failed", rtpPacket);

@@ -209,29 +145,27 @@ }

this.send("hello");
var byeHandler = function (inboundMessage) {
if (inboundMessage.headers["Call-ID"] !== _this.callId) {
const byeHandler = (inboundMessage) => {
if (inboundMessage.headers["Call-ID"] !== this.callId) {
return;
}
if (inboundMessage.headers.CSeq.endsWith(" BYE")) {
_this.softphone.off("message", byeHandler);
_this.dispose();
this.softphone.off("message", byeHandler);
this.dispose();
}
};
this.softphone.on("message", byeHandler);
};
CallSession.prototype.dispose = function () {
var _a, _b;
}
dispose() {
this.disposed = true;
this.emit("disposed");
this.removeAllListeners();
(_a = this.socket) === null || _a === void 0 ? void 0 : _a.removeAllListeners();
(_b = this.socket) === null || _b === void 0 ? void 0 : _b.close();
};
CallSession.prototype.transfer = function (transferTo) {
var _this = this;
var requestMessage = new RequestMessage("REFER sip:".concat(this.softphone.sipInfo.username, "@").concat(this.softphone.sipInfo.outboundProxy, ";transport=tls SIP/2.0"), {
Via: "SIP/2.0/TLS ".concat(this.softphone.client.localAddress, ":").concat(this.softphone.client.localPort, ";rport;branch=").concat(branch(), ";alias"),
this.socket?.removeAllListeners();
this.socket?.close();
}
transfer(transferTo) {
const requestMessage = new RequestMessage(`REFER sip:${this.softphone.sipInfo.username}@${this.softphone.sipInfo.outboundProxy};transport=tls SIP/2.0`, {
Via: `SIP/2.0/TLS ${this.softphone.client.localAddress}:${this.softphone.client.localPort};rport;branch=${branch()};alias`,
"Max-Forwards": 70,
From: this.localPeer,
To: this.remotePeer,
Contact: "<sip:".concat(this.softphone.sipInfo.username, "@").concat(this.softphone.client.localAddress, ":").concat(this.softphone.client.localPort, ";transport=TLS;ob>"),
Contact: `<sip:${this.softphone.sipInfo.username}@${this.softphone.client.localAddress}:${this.softphone.client.localPort};transport=TLS;ob>`,
"Call-ID": this.callId,

@@ -243,21 +177,20 @@ Event: "refer",

"Allow-Events": "presence, message-summary, refer",
"Refer-To": "sip:".concat(transferTo, "@").concat(this.softphone.sipInfo.domain),
"Referred-By": "<sip:".concat(this.softphone.sipInfo.username, "@").concat(this.softphone.sipInfo.domain, ">"),
"Refer-To": `sip:${transferTo}@${this.softphone.sipInfo.domain}`,
"Referred-By": `<sip:${this.softphone.sipInfo.username}@${this.softphone.sipInfo.domain}>`,
});
this.softphone.send(requestMessage);
// reply to those NOTIFY messages
var notifyHandler = function (inboundMessage) {
const notifyHandler = (inboundMessage) => {
if (!inboundMessage.subject.startsWith("NOTIFY ")) {
return;
}
var responseMessage = new ResponseMessage(inboundMessage, 200);
_this.softphone.send(responseMessage);
const responseMessage = new ResponseMessage(inboundMessage, 200);
this.softphone.send(responseMessage);
if (inboundMessage.body.trim() === "SIP/2.0 200 OK") {
_this.softphone.off("message", notifyHandler);
this.softphone.off("message", notifyHandler);
}
};
this.softphone.on("message", notifyHandler);
};
return CallSession;
}(EventEmitter));
}
}
export default CallSession;

@@ -1,117 +0,51 @@

var __extends = (this && this.__extends) || (function () {
var extendStatics = function (d, b) {
extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
return extendStatics(d, b);
};
return function (d, b) {
if (typeof b !== "function" && b !== null)
throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __generator = (this && this.__generator) || function (thisArg, body) {
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
function verb(n) { return function (v) { return step([n, v]); }; }
function step(op) {
if (f) throw new TypeError("Generator is already executing.");
while (g && (g = 0, op[0] && (_ = 0)), _) try {
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
if (y = 0, t) op = [op[0] & 2, t.value];
switch (op[0]) {
case 0: case 1: t = op; break;
case 4: _.label++; return { value: op[1], done: false };
case 5: _.label++; y = op[1]; op = [0]; continue;
case 7: op = _.ops.pop(); _.trys.pop(); continue;
default:
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
if (t[2]) _.ops.pop();
_.trys.pop(); continue;
}
op = body.call(thisArg, _);
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
}
};
import CallSession from "./index";
import { RequestMessage } from "../sip-message/index";
import { extractAddress, withoutTag } from "../utils";
var OutboundCallSession = /** @class */ (function (_super) {
__extends(OutboundCallSession, _super);
function OutboundCallSession(softphone, answerMessage) {
var _this = _super.call(this, softphone, answerMessage) || this;
_this.localPeer = answerMessage.headers.From;
_this.remotePeer = answerMessage.headers.To;
_this.remoteKey = answerMessage.body.match(/AES_CM_128_HMAC_SHA1_80 inline:([\w+/]+)/)[1];
_this.init();
return _this;
class OutboundCallSession extends CallSession {
constructor(softphone, answerMessage) {
super(softphone, answerMessage);
this.localPeer = answerMessage.headers.From;
this.remotePeer = answerMessage.headers.To;
this.remoteKey = answerMessage.body.match(/AES_CM_128_HMAC_SHA1_80 inline:([\w+/]+)/)[1];
this.init();
}
OutboundCallSession.prototype.init = function () {
var _this = this;
init() {
// wait for user to answer the call
var answerHandler = function (message) {
if (message.headers.CSeq !== _this.sipMessage.headers.CSeq) {
const answerHandler = (message) => {
if (message.headers.CSeq !== this.sipMessage.headers.CSeq) {
return;
}
if (message.subject.startsWith("SIP/2.0 486")) {
_this.softphone.off("message", answerHandler);
_this.emit("busy");
_this.dispose();
this.softphone.off("message", answerHandler);
this.emit("busy");
this.dispose();
return;
}
if (message.subject.startsWith("SIP/2.0 200")) {
_this.softphone.off("message", answerHandler);
_this.emit("answered");
var ackMessage = new RequestMessage("ACK ".concat(extractAddress(_this.remotePeer), " SIP/2.0"), {
"Call-ID": _this.callId,
From: _this.localPeer,
To: _this.remotePeer,
Via: _this.sipMessage.headers.Via,
CSeq: _this.sipMessage.headers.CSeq.replace(" INVITE", " ACK"),
this.softphone.off("message", answerHandler);
this.emit("answered");
const ackMessage = new RequestMessage(`ACK ${extractAddress(this.remotePeer)} SIP/2.0`, {
"Call-ID": this.callId,
From: this.localPeer,
To: this.remotePeer,
Via: this.sipMessage.headers.Via,
CSeq: this.sipMessage.headers.CSeq.replace(" INVITE", " ACK"),
});
_this.softphone.send(ackMessage);
this.softphone.send(ackMessage);
}
};
this.softphone.on("message", answerHandler);
this.once("answered", function () { return _this.startLocalServices(); });
};
OutboundCallSession.prototype.cancel = function () {
return __awaiter(this, void 0, void 0, function () {
var requestMessage;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
requestMessage = new RequestMessage("CANCEL ".concat(extractAddress(this.remotePeer), " SIP/2.0"), {
"Call-ID": this.callId,
From: this.localPeer,
To: withoutTag(this.remotePeer),
Via: this.sipMessage.headers.Via,
CSeq: this.sipMessage.headers.CSeq.replace(" INVITE", " CANCEL"),
});
return [4 /*yield*/, this.softphone.send(requestMessage)];
case 1:
_a.sent();
return [2 /*return*/];
}
});
this.once("answered", () => this.startLocalServices());
}
async cancel() {
const requestMessage = new RequestMessage(`CANCEL ${extractAddress(this.remotePeer)} SIP/2.0`, {
"Call-ID": this.callId,
From: this.localPeer,
To: withoutTag(this.remotePeer),
Via: this.sipMessage.headers.Via,
CSeq: this.sipMessage.headers.CSeq.replace(" INVITE", " CANCEL"),
});
};
return OutboundCallSession;
}(CallSession));
await this.softphone.send(requestMessage);
}
}
export default OutboundCallSession;

@@ -1,56 +0,37 @@

var __extends = (this && this.__extends) || (function () {
var extendStatics = function (d, b) {
extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
return extendStatics(d, b);
};
return function (d, b) {
if (typeof b !== "function" && b !== null)
throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
import EventEmitter from "node:events";
import { Buffer } from "node:buffer";
import { RtpHeader, RtpPacket } from "werift-rtp";
var Streamer = /** @class */ (function (_super) {
__extends(Streamer, _super);
function Streamer(callSesstion, buffer) {
var _this = _super.call(this) || this;
_this.paused = false;
_this.callSession = callSesstion;
_this.buffer = buffer;
_this.originalBuffer = buffer;
return _this;
class Streamer extends EventEmitter {
paused = false;
callSession;
buffer;
originalBuffer;
constructor(callSesstion, buffer) {
super();
this.callSession = callSesstion;
this.buffer = buffer;
this.originalBuffer = buffer;
}
Streamer.prototype.start = function () {
start() {
this.buffer = this.originalBuffer;
this.paused = false;
this.sendPacket();
};
Streamer.prototype.stop = function () {
}
stop() {
this.buffer = Buffer.alloc(0);
};
Streamer.prototype.pause = function () {
}
pause() {
this.paused = true;
};
Streamer.prototype.resume = function () {
}
resume() {
this.paused = false;
this.sendPacket();
};
Object.defineProperty(Streamer.prototype, "finished", {
get: function () {
return this.buffer.length < this.callSession.softphone.codec.packetSize;
},
enumerable: false,
configurable: true
});
Streamer.prototype.sendPacket = function () {
var _this = this;
}
get finished() {
return this.buffer.length < this.callSession.softphone.codec.packetSize;
}
sendPacket() {
if (!this.callSession.disposed && !this.paused && !this.finished) {
var temp = this.callSession.encoder.encode(this.buffer.subarray(0, this.callSession.softphone.codec.packetSize));
var rtpPacket = new RtpPacket(new RtpHeader({
const temp = this.callSession.encoder.encode(this.buffer.subarray(0, this.callSession.softphone.codec.packetSize));
const rtpPacket = new RtpPacket(new RtpHeader({
version: 2,

@@ -84,8 +65,7 @@ padding: false,

else {
setTimeout(function () { return _this.sendPacket(); }, 20);
setTimeout(() => this.sendPacket(), 20);
}
}
};
return Streamer;
}(EventEmitter));
}
}
export default Streamer;
import { Buffer } from "node:buffer";
import { Decoder, Encoder } from "@evan/opus";
var Codec = /** @class */ (function () {
function Codec(name) {
class Codec {
id;
name;
packetSize;
timestampInterval;
createEncoder;
createDecoder;
constructor(name) {
this.name = name;
switch (name) {
case "OPUS/16000": {
this.createEncoder = function () {
var encoder = new Encoder({ channels: 1, sample_rate: 16000 });
return { encode: function (pcm) { return Buffer.from(encoder.encode(pcm)); } };
this.createEncoder = () => {
const encoder = new Encoder({ channels: 1, sample_rate: 16000 });
return { encode: (pcm) => Buffer.from(encoder.encode(pcm)) };
};
this.createDecoder = function () {
var decoder = new Decoder({ channels: 1, sample_rate: 16000 });
this.createDecoder = () => {
const decoder = new Decoder({ channels: 1, sample_rate: 16000 });
return {
decode: function (opus) { return Buffer.from(decoder.decode(opus)); },
decode: (opus) => Buffer.from(decoder.decode(opus)),
};

@@ -24,10 +30,10 @@ };

case "OPUS/48000/2": {
this.createEncoder = function () {
var encoder = new Encoder({ channels: 2, sample_rate: 48000 });
return { encode: function (pcm) { return Buffer.from(encoder.encode(pcm)); } };
this.createEncoder = () => {
const encoder = new Encoder({ channels: 2, sample_rate: 48000 });
return { encode: (pcm) => Buffer.from(encoder.encode(pcm)) };
};
this.createDecoder = function () {
var decoder = new Decoder({ channels: 2, sample_rate: 48000 });
this.createDecoder = () => {
const decoder = new Decoder({ channels: 2, sample_rate: 48000 });
return {
decode: function (opus) { return Buffer.from(decoder.decode(opus)); },
decode: (opus) => Buffer.from(decoder.decode(opus)),
};

@@ -41,7 +47,7 @@ };

case "PCMU/8000": {
this.createEncoder = function () {
return { encode: function (pcm) { return pcm; } };
this.createEncoder = () => {
return { encode: (pcm) => pcm };
};
this.createDecoder = function () {
return { decode: function (audio) { return audio; } };
this.createDecoder = () => {
return { decode: (audio) => audio };
};

@@ -54,8 +60,7 @@ this.id = 0;

default: {
throw new Error("unsupported codec: ".concat(name));
throw new Error(`unsupported codec: ${name}`);
}
}
}
return Codec;
}());
}
export default Codec;
import { Buffer } from "node:buffer";
var DTMF = /** @class */ (function () {
function DTMF() {
}
DTMF.phoneChars = [
class DTMF {
static phoneChars = [
"0",

@@ -19,3 +17,3 @@ "1",

];
DTMF.payloads = [
static payloads = [
0x00060000,

@@ -28,10 +26,10 @@ 0x000600a0,

];
DTMF.charToPayloads = function (char) {
var index = DTMF.phoneChars.indexOf(char[0]);
static charToPayloads = (char) => {
const index = DTMF.phoneChars.indexOf(char[0]);
if (index === -1) {
throw new Error("invalid phone char");
}
return DTMF.payloads.map(function (payload) {
var temp = payload + index * 0x01000000;
var buffer = Buffer.alloc(4);
return DTMF.payloads.map((payload) => {
const temp = payload + index * 0x01000000;
const buffer = Buffer.alloc(4);
buffer.writeIntBE(temp, 0, 4);

@@ -41,9 +39,8 @@ return buffer;

};
DTMF.payloadToChar = function (payload) {
var intBE = payload.readIntBE(0, 4);
var index = (intBE - 0x00060000) / 0x01000000;
static payloadToChar = (payload) => {
const intBE = payload.readIntBE(0, 4);
const index = (intBE - 0x00060000) / 0x01000000;
return DTMF.phoneChars[index];
};
return DTMF;
}());
}
export default DTMF;

@@ -1,52 +0,1 @@

var __extends = (this && this.__extends) || (function () {
var extendStatics = function (d, b) {
extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
return extendStatics(d, b);
};
return function (d, b) {
if (typeof b !== "function" && b !== null)
throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __generator = (this && this.__generator) || function (thisArg, body) {
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
function verb(n) { return function (v) { return step([n, v]); }; }
function step(op) {
if (f) throw new TypeError("Generator is already executing.");
while (g && (g = 0, op[0] && (_ = 0)), _) try {
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
if (y = 0, t) op = [op[0] & 2, t.value];
switch (op[0]) {
case 0: case 1: t = op; break;
case 4: _.label++; return { value: op[1], done: false };
case 5: _.label++; y = op[1]; op = [0]; continue;
case 7: op = _.ops.pop(); _.trys.pop(); continue;
default:
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
if (t[2]) _.ops.pop();
_.trys.pop(); continue;
}
op = body.call(thisArg, _);
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
}
};
import EventEmitter from "node:events";

@@ -60,28 +9,29 @@ import tls from "node:tls";

import Codec from "./codec";
var Softphone = /** @class */ (function (_super) {
__extends(Softphone, _super);
function Softphone(sipInfo) {
var _this = _super.call(this) || this;
_this.fakeDomain = uuid() + ".invalid";
_this.fakeEmail = uuid() + "@" + _this.fakeDomain;
_this.connected = false;
_this.instanceId = uuid();
_this.registerCallId = uuid();
class Softphone extends EventEmitter {
sipInfo;
client;
codec;
fakeDomain = uuid() + ".invalid";
fakeEmail = uuid() + "@" + this.fakeDomain;
intervalHandle;
connected = false;
constructor(sipInfo) {
super();
if (sipInfo.codec === undefined) {
sipInfo.codec = "OPUS/16000";
}
_this.codec = new Codec(sipInfo.codec);
_this.sipInfo = sipInfo;
if (_this.sipInfo.domain === undefined) {
_this.sipInfo.domain = "sip.ringcentral.com";
this.codec = new Codec(sipInfo.codec);
this.sipInfo = sipInfo;
if (this.sipInfo.domain === undefined) {
this.sipInfo.domain = "sip.ringcentral.com";
}
if (_this.sipInfo.outboundProxy === undefined) {
_this.sipInfo.outboundProxy = "sip10.ringcentral.com:5096";
if (this.sipInfo.outboundProxy === undefined) {
this.sipInfo.outboundProxy = "sip10.ringcentral.com:5096";
}
var tokens = _this.sipInfo.outboundProxy.split(":");
_this.client = tls.connect({ host: tokens[0], port: parseInt(tokens[1], 10) }, function () {
_this.connected = true;
const tokens = this.sipInfo.outboundProxy.split(":");
this.client = tls.connect({ host: tokens[0], port: parseInt(tokens[1], 10) }, () => {
this.connected = true;
});
var cache = "";
_this.client.on("data", function (data) {
let cache = "";
this.client.on("data", (data) => {
cache += data.toString("utf-8");

@@ -92,7 +42,7 @@ if (!cache.endsWith("\r\n")) {

// received two empty body messages
var tempMessages = cache
const tempMessages = cache
.split("\r\nContent-Length: 0\r\n\r\n")
.filter(function (message) { return message.trim() !== ""; });
.filter((message) => message.trim() !== "");
cache = "";
for (var i = 0; i < tempMessages.length; i++) {
for (let i = 0; i < tempMessages.length; i++) {
if (!tempMessages[i].includes("Content-Length: ")) {

@@ -102,92 +52,68 @@ tempMessages[i] = tempMessages[i] + "\r\nContent-Length: 0";

}
for (var _i = 0, tempMessages_1 = tempMessages; _i < tempMessages_1.length; _i++) {
var message = tempMessages_1[_i];
_this.emit("message", InboundMessage.fromString(message));
for (const message of tempMessages) {
this.emit("message", InboundMessage.fromString(message));
}
});
return _this;
}
Softphone.prototype.register = function () {
return __awaiter(this, void 0, void 0, function () {
var sipRegister;
var _this = this;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
if (!!this.connected) return [3 /*break*/, 2];
return [4 /*yield*/, waitFor({ interval: 100, condition: function () { return _this.connected; } })];
case 1:
_a.sent();
_a.label = 2;
case 2:
sipRegister = function () { return __awaiter(_this, void 0, void 0, function () {
var fromTag, requestMessage, inboundMessage, wwwAuth, nonce, newMessage;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
fromTag = uuid();
requestMessage = new RequestMessage("REGISTER sip:".concat(this.sipInfo.domain, " SIP/2.0"), {
Via: "SIP/2.0/TLS ".concat(this.client.localAddress, ":").concat(this.client.localPort, ";rport;branch=").concat(branch(), ";alias"),
Route: "<sip:".concat(this.sipInfo.outboundProxy, ";transport=tls;lr>"),
"Max-Forwards": "70",
From: "<sip:".concat(this.sipInfo.username, "@").concat(this.sipInfo.domain, ">;tag=").concat(fromTag),
To: "<sip:".concat(this.sipInfo.username, "@").concat(this.sipInfo.domain, ">"),
"Call-ID": this.registerCallId,
Supported: "outbound, path",
Contact: "<sip:".concat(this.sipInfo.username, "@").concat(this.client.localAddress, ":").concat(this.client.localPort, ";transport=TLS;ob>;reg-id=1;+sip.instance=\"<urn:uuid:").concat(this.instanceId, ">\""),
Expires: 300,
Allow: "PRACK, INVITE, ACK, BYE, CANCEL, UPDATE, INFO, SUBSCRIBE, NOTIFY, REFER, MESSAGE, OPTIONS",
});
return [4 /*yield*/, this.send(requestMessage, true)];
case 1:
inboundMessage = _a.sent();
if (inboundMessage.subject.startsWith("SIP/2.0 200 ")) {
// sometimes the server will return 200 OK directly
return [2 /*return*/];
}
wwwAuth = inboundMessage.headers["Www-Authenticate"] ||
inboundMessage.headers["WWW-Authenticate"];
nonce = wwwAuth.match(/, nonce="(.+?)"/)[1];
newMessage = requestMessage.fork();
newMessage.headers.Authorization = generateAuthorization(this.sipInfo, nonce, "REGISTER");
this.send(newMessage);
return [2 /*return*/];
}
});
}); };
sipRegister();
this.intervalHandle = setInterval(function () {
sipRegister();
}, 3 * 60 * 1000);
this.on("message", function (inboundMessage) {
if (!inboundMessage.subject.startsWith("INVITE sip:")) {
return;
}
var outboundMessage = new OutboundMessage("SIP/2.0 100 Trying", {
Via: inboundMessage.headers.Via,
"Call-ID": inboundMessage.headers["Call-ID"],
From: inboundMessage.headers.From,
To: inboundMessage.headers.To,
CSeq: inboundMessage.headers.CSeq,
"Content-Length": "0",
});
_this.send(outboundMessage);
_this.emit("invite", inboundMessage);
});
return [2 /*return*/];
}
instanceId = uuid();
registerCallId = uuid();
async register() {
if (!this.connected) {
await waitFor({ interval: 100, condition: () => this.connected });
}
const sipRegister = async () => {
const fromTag = uuid();
const requestMessage = new RequestMessage(`REGISTER sip:${this.sipInfo.domain} SIP/2.0`, {
Via: `SIP/2.0/TLS ${this.client.localAddress}:${this.client.localPort};rport;branch=${branch()};alias`,
Route: `<sip:${this.sipInfo.outboundProxy};transport=tls;lr>`,
"Max-Forwards": "70",
From: `<sip:${this.sipInfo.username}@${this.sipInfo.domain}>;tag=${fromTag}`,
To: `<sip:${this.sipInfo.username}@${this.sipInfo.domain}>`,
"Call-ID": this.registerCallId,
Supported: "outbound, path",
Contact: `<sip:${this.sipInfo.username}@${this.client.localAddress}:${this.client.localPort};transport=TLS;ob>;reg-id=1;+sip.instance="<urn:uuid:${this.instanceId}>"`,
Expires: 300,
Allow: "PRACK, INVITE, ACK, BYE, CANCEL, UPDATE, INFO, SUBSCRIBE, NOTIFY, REFER, MESSAGE, OPTIONS",
});
const inboundMessage = await this.send(requestMessage, true);
if (inboundMessage.subject.startsWith("SIP/2.0 200 ")) {
// sometimes the server will return 200 OK directly
return;
}
const wwwAuth = inboundMessage.headers["Www-Authenticate"] ||
inboundMessage.headers["WWW-Authenticate"];
const nonce = wwwAuth.match(/, nonce="(.+?)"/)[1];
const newMessage = requestMessage.fork();
newMessage.headers.Authorization = generateAuthorization(this.sipInfo, nonce, "REGISTER");
this.send(newMessage);
};
sipRegister();
this.intervalHandle = setInterval(() => {
sipRegister();
}, 3 * 60 * 1000);
this.on("message", (inboundMessage) => {
if (!inboundMessage.subject.startsWith("INVITE sip:")) {
return;
}
const outboundMessage = new OutboundMessage("SIP/2.0 100 Trying", {
Via: inboundMessage.headers.Via,
"Call-ID": inboundMessage.headers["Call-ID"],
From: inboundMessage.headers.From,
To: inboundMessage.headers.To,
CSeq: inboundMessage.headers.CSeq,
"Content-Length": "0",
});
this.send(outboundMessage);
this.emit("invite", inboundMessage);
});
};
Softphone.prototype.enableDebugMode = function () {
this.on("message", function (message) {
return console.log("Receiving...(".concat(new Date(), ")\n") + message.toString());
});
var tlsWrite = this.client.write.bind(this.client);
this.client.write = function (message) {
console.log("Sending...(".concat(new Date(), ")\n") + message);
}
enableDebugMode() {
this.on("message", (message) => console.log(`Receiving...(${new Date()})\n` + message.toString()));
const tlsWrite = this.client.write.bind(this.client);
this.client.write = (message) => {
console.log(`Sending...(${new Date()})\n` + message);
return tlsWrite(message);
};
};
Softphone.prototype.revoke = function () {
}
revoke() {
clearInterval(this.intervalHandle);

@@ -197,14 +123,12 @@ this.removeAllListeners();

this.client.destroy();
};
Softphone.prototype.send = function (message, waitForReply) {
var _this = this;
if (waitForReply === void 0) { waitForReply = false; }
}
send(message, waitForReply = false) {
this.client.write(message.toString());
if (!waitForReply) {
return new Promise(function (resolve) {
return new Promise((resolve) => {
resolve(undefined);
});
}
return new Promise(function (resolve) {
var messageListerner = function (inboundMessage) {
return new Promise((resolve) => {
const messageListerner = (inboundMessage) => {
if (inboundMessage.headers.CSeq !== message.headers.CSeq) {

@@ -216,77 +140,55 @@ return;

}
_this.off("message", messageListerner);
this.off("message", messageListerner);
resolve(inboundMessage);
};
_this.on("message", messageListerner);
this.on("message", messageListerner);
});
};
Softphone.prototype.answer = function (inviteMessage) {
return __awaiter(this, void 0, void 0, function () {
var inboundCallSession;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
inboundCallSession = new InboundCallSession(this, inviteMessage);
return [4 /*yield*/, inboundCallSession.answer()];
case 1:
_a.sent();
return [2 /*return*/, inboundCallSession];
}
});
});
};
}
async answer(inviteMessage) {
const inboundCallSession = new InboundCallSession(this, inviteMessage);
await inboundCallSession.answer();
return inboundCallSession;
}
// decline an inbound call
Softphone.prototype.decline = function (inviteMessage) {
return __awaiter(this, void 0, void 0, function () {
var newMessage;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
newMessage = new ResponseMessage(inviteMessage, 603);
return [4 /*yield*/, this.send(newMessage)];
case 1:
_a.sent();
return [2 /*return*/];
}
});
});
};
Softphone.prototype.call = function (callee) {
return __awaiter(this, void 0, void 0, function () {
var offerSDP, inviteMessage, inboundMessage, proxyAuthenticate, nonce, newMessage, progressMessage;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
offerSDP = "\nv=0\no=- ".concat(Date.now(), " 0 IN IP4 ").concat(this.client.localAddress, "\ns=rc-softphone-ts\nc=IN IP4 ").concat(this.client.localAddress, "\nt=0 0\nm=audio ").concat(randomInt(), " RTP/SAVP ").concat(this.codec.id, " 101\na=rtpmap:").concat(this.codec.id, " ").concat(this.codec.name, "\na=rtpmap:101 telephone-event/8000\na=fmtp:101 0-15\na=sendrecv\na=crypto:1 AES_CM_128_HMAC_SHA1_80 inline:").concat(localKey, "\n ").trim();
inviteMessage = new RequestMessage("INVITE sip:".concat(callee, " SIP/2.0"), {
Via: "SIP/2.0/TLS ".concat(this.client.localAddress, ":").concat(this.client.localPort, ";rport;branch=").concat(branch(), ";alias"),
"Max-Forwards": 70,
From: "<sip:".concat(this.sipInfo.username, "@").concat(this.sipInfo.domain, ">;tag=").concat(uuid()),
To: "<sip:".concat(callee, "@sip.ringcentral.com>"),
Contact: " <sip:".concat(this.sipInfo.username, "@").concat(this.client.localAddress, ":").concat(this.client.localPort, ";transport=TLS;ob>"),
"Call-ID": uuid(),
Route: "<sip:".concat(this.sipInfo.outboundProxy, ";transport=tls;lr>"),
Allow: "PRACK, INVITE, ACK, BYE, CANCEL, UPDATE, INFO, SUBSCRIBE, NOTIFY, REFER, MESSAGE, OPTIONS",
Supported: "replaces, 100rel, timer, norefersub",
"Session-Expires": 1800,
"Min-SE": 90,
"Content-Type": "application/sdp",
}, offerSDP);
return [4 /*yield*/, this.send(inviteMessage, true)];
case 1:
inboundMessage = _a.sent();
proxyAuthenticate = inboundMessage.headers["Proxy-Authenticate"];
nonce = proxyAuthenticate.match(/, nonce="(.+?)"/)[1];
newMessage = inviteMessage.fork();
newMessage.headers["Proxy-Authorization"] = generateAuthorization(this.sipInfo, nonce, "INVITE");
return [4 /*yield*/, this.send(newMessage, true)];
case 2:
progressMessage = _a.sent();
return [2 /*return*/, new OutboundCallSession(this, progressMessage)];
}
});
});
};
return Softphone;
}(EventEmitter));
async decline(inviteMessage) {
const newMessage = new ResponseMessage(inviteMessage, 603);
await this.send(newMessage);
}
async call(callee) {
const offerSDP = `
v=0
o=- ${Date.now()} 0 IN IP4 ${this.client.localAddress}
s=rc-softphone-ts
c=IN IP4 ${this.client.localAddress}
t=0 0
m=audio ${randomInt()} RTP/SAVP ${this.codec.id} 101
a=rtpmap:${this.codec.id} ${this.codec.name}
a=rtpmap:101 telephone-event/8000
a=fmtp:101 0-15
a=sendrecv
a=crypto:1 AES_CM_128_HMAC_SHA1_80 inline:${localKey}
`.trim();
const inviteMessage = new RequestMessage(`INVITE sip:${callee} SIP/2.0`, {
Via: `SIP/2.0/TLS ${this.client.localAddress}:${this.client.localPort};rport;branch=${branch()};alias`,
"Max-Forwards": 70,
From: `<sip:${this.sipInfo.username}@${this.sipInfo.domain}>;tag=${uuid()}`,
To: `<sip:${callee}@sip.ringcentral.com>`,
Contact: ` <sip:${this.sipInfo.username}@${this.client.localAddress}:${this.client.localPort};transport=TLS;ob>`,
"Call-ID": uuid(),
Route: `<sip:${this.sipInfo.outboundProxy};transport=tls;lr>`,
Allow: `PRACK, INVITE, ACK, BYE, CANCEL, UPDATE, INFO, SUBSCRIBE, NOTIFY, REFER, MESSAGE, OPTIONS`,
Supported: `replaces, 100rel, timer, norefersub`,
"Session-Expires": 1800,
"Min-SE": 90,
"Content-Type": "application/sdp",
}, offerSDP);
const inboundMessage = await this.send(inviteMessage, true);
const proxyAuthenticate = inboundMessage.headers["Proxy-Authenticate"];
const nonce = proxyAuthenticate.match(/, nonce="(.+?)"/)[1];
const newMessage = inviteMessage.fork();
newMessage.headers["Proxy-Authorization"] = generateAuthorization(this.sipInfo, nonce, "INVITE");
const progressMessage = await this.send(newMessage, true);
return new OutboundCallSession(this, progressMessage);
}
}
export default Softphone;

@@ -1,30 +0,11 @@

var __extends = (this && this.__extends) || (function () {
var extendStatics = function (d, b) {
extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
return extendStatics(d, b);
};
return function (d, b) {
if (typeof b !== "function" && b !== null)
throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
import { uuid } from "../../utils";
import SipMessage from "../sip-message";
var InboundMessage = /** @class */ (function (_super) {
__extends(InboundMessage, _super);
function InboundMessage() {
return _super !== null && _super.apply(this, arguments) || this;
}
InboundMessage.fromString = function (str) {
var sipMessage = new SipMessage();
var _a = str.split("\r\n\r\n"), init = _a[0], body = _a.slice(1);
class InboundMessage extends SipMessage {
static fromString(str) {
const sipMessage = new SipMessage();
const [init, ...body] = str.split("\r\n\r\n");
sipMessage.body = body.join("\r\n\r\n");
var _b = init.split("\r\n"), subject = _b[0], headers = _b.slice(1);
const [subject, ...headers] = init.split("\r\n");
sipMessage.subject = subject;
sipMessage.headers = Object.fromEntries(headers.map(function (line) { return line.split(": "); }));
sipMessage.headers = Object.fromEntries(headers.map((line) => line.split(": ")));
if (sipMessage.headers.To && !sipMessage.headers.To.includes(";tag=")) {

@@ -34,5 +15,4 @@ sipMessage.headers.To += ";tag=" + uuid(); // generate local tag

return sipMessage;
};
return InboundMessage;
}(SipMessage));
}
}
export default InboundMessage;

@@ -1,30 +0,9 @@

var __extends = (this && this.__extends) || (function () {
var extendStatics = function (d, b) {
extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
return extendStatics(d, b);
};
return function (d, b) {
if (typeof b !== "function" && b !== null)
throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
import SipMessage from "../sip-message";
var OutboundMessage = /** @class */ (function (_super) {
__extends(OutboundMessage, _super);
function OutboundMessage(subject, headers, body) {
if (subject === void 0) { subject = ""; }
if (headers === void 0) { headers = {}; }
if (body === void 0) { body = ""; }
var _this = _super.call(this, subject, headers, body) || this;
_this.headers["Content-Length"] = _this.body.length.toString();
_this.headers["User-Agent"] = "ringcentral-softphone-ts";
return _this;
class OutboundMessage extends SipMessage {
constructor(subject = "", headers = {}, body = "") {
super(subject, headers, body);
this.headers["Content-Length"] = this.body.length.toString();
this.headers["User-Agent"] = "ringcentral-softphone-ts";
}
return OutboundMessage;
}(SipMessage));
}
export default OutboundMessage;

@@ -1,55 +0,23 @@

var __extends = (this && this.__extends) || (function () {
var extendStatics = function (d, b) {
extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
return extendStatics(d, b);
};
return function (d, b) {
if (typeof b !== "function" && b !== null)
throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
var __assign = (this && this.__assign) || function () {
__assign = Object.assign || function(t) {
for (var s, i = 1, n = arguments.length; i < n; i++) {
s = arguments[i];
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
t[p] = s[p];
}
return t;
};
return __assign.apply(this, arguments);
};
import OutboundMessage from "./index";
import { branch } from "../../utils";
var cseq = Math.floor(Math.random() * 10000);
var RequestMessage = /** @class */ (function (_super) {
__extends(RequestMessage, _super);
function RequestMessage(subject, headers, body) {
if (subject === void 0) { subject = ""; }
if (headers === void 0) { headers = {}; }
if (body === void 0) { body = ""; }
var _this = _super.call(this, subject, headers, body) || this;
if (_this.headers.CSeq === undefined) {
_this.newCseq();
let cseq = Math.floor(Math.random() * 10000);
class RequestMessage extends OutboundMessage {
constructor(subject = "", headers = {}, body = "") {
super(subject, headers, body);
if (this.headers.CSeq === undefined) {
this.newCseq();
}
return _this;
}
RequestMessage.prototype.newCseq = function () {
this.headers.CSeq = "".concat(++cseq, " ").concat(this.subject.split(" ")[0]);
};
RequestMessage.prototype.fork = function () {
var newMessage = new RequestMessage(this.subject, __assign({}, this.headers), this.body);
newCseq() {
this.headers.CSeq = `${++cseq} ${this.subject.split(" ")[0]}`;
}
fork() {
const newMessage = new RequestMessage(this.subject, { ...this.headers }, this.body);
newMessage.newCseq();
if (newMessage.headers.Via) {
newMessage.headers.Via = newMessage.headers.Via.replace(/;branch=.+?$/, ";branch=".concat(branch()));
newMessage.headers.Via = newMessage.headers.Via.replace(/;branch=.+?$/, `;branch=${branch()}`);
}
return newMessage;
};
return RequestMessage;
}(OutboundMessage));
}
}
export default RequestMessage;

@@ -1,47 +0,15 @@

var __extends = (this && this.__extends) || (function () {
var extendStatics = function (d, b) {
extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
return extendStatics(d, b);
};
return function (d, b) {
if (typeof b !== "function" && b !== null)
throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
var __assign = (this && this.__assign) || function () {
__assign = Object.assign || function(t) {
for (var s, i = 1, n = arguments.length; i < n; i++) {
s = arguments[i];
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
t[p] = s[p];
}
return t;
};
return __assign.apply(this, arguments);
};
import OutboundMessage from "./index";
import responseCodes from "../response-codes";
var ResponseMessage = /** @class */ (function (_super) {
__extends(ResponseMessage, _super);
function ResponseMessage(inboundMessage, responseCode, headers, body) {
if (headers === void 0) { headers = {}; }
if (body === void 0) { body = ""; }
var _this = _super.call(this, undefined, __assign({}, headers), body) || this;
_this.subject = "SIP/2.0 ".concat(responseCode, " ").concat(responseCodes[responseCode]);
var keys = ["Via", "From", "To", "Call-ID", "CSeq"];
for (var _i = 0, keys_1 = keys; _i < keys_1.length; _i++) {
var key = keys_1[_i];
class ResponseMessage extends OutboundMessage {
constructor(inboundMessage, responseCode, headers = {}, body = "") {
super(undefined, { ...headers }, body);
this.subject = `SIP/2.0 ${responseCode} ${responseCodes[responseCode]}`;
const keys = ["Via", "From", "To", "Call-ID", "CSeq"];
for (const key of keys) {
if (inboundMessage.headers[key]) {
_this.headers[key] = inboundMessage.headers[key];
this.headers[key] = inboundMessage.headers[key];
}
}
return _this;
}
return ResponseMessage;
}(OutboundMessage));
}
export default ResponseMessage;
// Ref: https://en.wikipedia.org/wiki/List_of_SIP_response_codes'
var responseCodes = {
const responseCodes = {
100: "Trying",

@@ -4,0 +4,0 @@ 180: "Ringing",

@@ -1,15 +0,6 @@

var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
if (ar || !(i in from)) {
if (!ar) ar = Array.prototype.slice.call(from, 0, i);
ar[i] = from[i];
}
}
return to.concat(ar || Array.prototype.slice.call(from));
};
var SipMessage = /** @class */ (function () {
function SipMessage(subject, headers, body) {
if (subject === void 0) { subject = ""; }
if (headers === void 0) { headers = {}; }
if (body === void 0) { body = ""; }
class SipMessage {
subject;
headers;
body;
constructor(subject = "", headers = {}, body = "") {
this.subject = subject;

@@ -25,14 +16,12 @@ this.headers = headers;

}
SipMessage.prototype.toString = function () {
var _this = this;
var r = __spreadArray(__spreadArray([
this.subject
], Object.keys(this.headers).map(function (key) { return "".concat(key, ": ").concat(_this.headers[key]); }), true), [
toString() {
const r = [
this.subject,
...Object.keys(this.headers).map((key) => `${key}: ${this.headers[key]}`),
"",
this.body,
], false).join("\r\n");
].join("\r\n");
return r;
};
return SipMessage;
}());
}
}
export default SipMessage;
import crypto from "node:crypto";
var md5 = function (s) { return crypto.createHash("md5").update(s).digest("hex"); };
var generateResponse = function (sipInfo, endpoint, nonce) {
var ha1 = md5("".concat(sipInfo.authorizationId, ":").concat(sipInfo.domain, ":").concat(sipInfo.password));
var ha2 = md5(endpoint);
var response = md5("".concat(ha1, ":").concat(nonce, ":").concat(ha2));
const md5 = (s) => crypto.createHash("md5").update(s).digest("hex");
const generateResponse = (sipInfo, endpoint, nonce) => {
const ha1 = md5(`${sipInfo.authorizationId}:${sipInfo.domain}:${sipInfo.password}`);
const ha2 = md5(endpoint);
const response = md5(`${ha1}:${nonce}:${ha2}`);
return response;
};
export var generateAuthorization = function (sipInfo, nonce, method) {
var authObj = {
export const generateAuthorization = (sipInfo, nonce, method) => {
const authObj = {
"Digest algorithm": "MD5",
username: sipInfo.authorizationId,
realm: sipInfo.domain,
nonce: nonce,
uri: "sip:".concat(sipInfo.domain),
response: generateResponse(sipInfo, "".concat(method, ":sip:").concat(sipInfo.domain), nonce),
nonce,
uri: `sip:${sipInfo.domain}`,
response: generateResponse(sipInfo, `${method}:sip:${sipInfo.domain}`, nonce),
};
return Object.keys(authObj)
.map(function (key) { return "".concat(key, "=\"").concat(authObj[key], "\""); })
.map((key) => `${key}="${authObj[key]}"`)
.join(", ");
};
export var uuid = function () { return crypto.randomUUID(); };
export var branch = function () { return "z9hG4bK-" + uuid(); };
export var randomInt = function () {
return Math.floor(Math.random() * (65535 - 1024 + 1)) + 1024;
};
export var withoutTag = function (s) { return s.replace(/;tag=.*$/, ""); };
export var extractAddress = function (s) { var _a; return (_a = s.match(/<(sip:.+?)>/)) === null || _a === void 0 ? void 0 : _a[1]; };
var keyAndSalt = crypto.randomBytes(30);
export var localKey = keyAndSalt.toString("base64").replace(/=+$/, "");
export const uuid = () => crypto.randomUUID();
export const branch = () => "z9hG4bK-" + uuid();
export const randomInt = () => Math.floor(Math.random() * (65535 - 1024 + 1)) + 1024;
export const withoutTag = (s) => s.replace(/;tag=.*$/, "");
export const extractAddress = (s) => s.match(/<(sip:.+?)>/)?.[1];
const keyAndSalt = crypto.randomBytes(30);
export const localKey = keyAndSalt.toString("base64").replace(/=+$/, "");
{
"name": "ringcentral-softphone",
"version": "1.1.2",
"version": "1.1.3",
"homepage": "https://github.com/ringcentral/ringcentral-softphone-ts",

@@ -9,2 +9,5 @@ "license": "MIT",

"main": "dist/cjs/index.js",
"files": [
"dist"
],
"exports": {

@@ -25,3 +28,3 @@ ".": {

"out": "rm -rf *.wav && tsx -r dotenv-override-true/config demos/outbound-call.ts",
"build": "tsc -p tsconfig.cjs.json && tsc -p tsconfig.esm.json",
"build": "tsc -p tsconfig.esm.json && tsc -p tsconfig.cjs.json",
"prepublishOnly": "yarn build",

@@ -35,5 +38,2 @@ "postpublish": "rm -rf dist"

},
"files": [
"dist"
],
"devDependencies": {

@@ -40,0 +40,0 @@ "@types/node": "^22.12.0",

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc