drachtio-modesl
Version:
FreeSWITCH ESL Node.js Implementation
287 lines (241 loc) • 9.42 kB
JavaScript
var encodeXml = require('../util/xml').encodeXml;
//can be (type {string}[, subclass {string}])
//or (headers {object/map}[, body {string}])
var Event = module.exports = function(type, subclass) {
this.headers = [];
this.hPtr = null;
//case where we actually have type/subclass
if(typeof type === 'string') {
this.type = type;
this.subclass = subclass;
this.body = '';
this.addHeader('Event-Name', type);
if(subclass) this.addHeader('Event-Subclass', subclass);
}
//case were we have headers/body
else if(typeof type === 'object') {
this.type = type['Event-Name'];
this.subclass = type['Event-Subclass'];
this.body = subclass || type._body || '';
var self = this;
Object.keys(type).forEach(function(key) {
if(key === '_body') return;
self.addHeader(key, type[key]);
});
}
//case where we get no params
else {
this.type = '';
this.subclass = null;
this.body = '';
}
};
Event.PRIORITY = {
LOW: 'LOW',
NORMAL: 'NORMAL',
HIGH: 'HIGH'
};
//Turns an event into colon-separated 'name: value'
// pairs similar to a sip/email packet
// (the way it looks on '/events plain all').
Event.prototype.serialize = function(format) {
format = format || 'plain';
var data;
switch(format) {
case 'json':
/*
{
"Event-Name": "CUSTOM",
"Core-UUID": "8b192020-7368-4498-9b11-cbe10f48a784",
"FreeSWITCH-Hostname": "smsdev",
"FreeSWITCH-Switchname": "smsdev",
"FreeSWITCH-IPv4": "10.1.12.115",
"FreeSWITCH-IPv6": "::1",
"Event-Date-Local": "2012-09-25 14:22:37",
"Event-Date-GMT": "Tue, 25 Sep 2012 18:22:37 GMT",
"Event-Date-Timestamp": "1348597357036551",
"Event-Calling-File": "switch_cpp.cpp",
"Event-Calling-Function": "Event",
"Event-Calling-Line-Number": "262",
"Event-Sequence": "11027",
"Event-Subclass": "SMS::SEND_MESSAGE",
"proto": "sip",
"dest_proto": "sip",
"from": "9515529832",
"from_full": "9515529832",
"to": "internal/8507585138@sms-proxy-01.bandwidthclec.com",
"subject": "PATLive Testing",
"type": "text/plain",
"hint": "the hint",
"replying": "true",
"Content-Length": "23",
"_body": "Hello from Chad Engler!"
}
*/
this.addHeader('Content-Length', Buffer.byteLength(this.body, 'utf8'));
data = this.headers.reduce(function (d, header) {
d[header.name] = header.value;
return d;
}, {});
if (this.body) {
data._body = this.body;
}
return JSON.stringify(data, null, 2);
case 'plain':
/*
Event-Name: CUSTOM
Core-UUID: 8b192020-7368-4498-9b11-cbe10f48a784
FreeSWITCH-Hostname: smsdev
FreeSWITCH-Switchname: smsdev
FreeSWITCH-IPv4: 10.1.12.115
FreeSWITCH-IPv6: %3A%3A1
Event-Date-Local: 2012-09-25%2014%3A21%3A56
Event-Date-GMT: Tue,%2025%20Sep%202012%2018%3A21%3A56%20GMT
Event-Date-Timestamp: 1348597316736546
Event-Calling-File: switch_cpp.cpp
Event-Calling-Function: Event
Event-Calling-Line-Number: 262
Event-Sequence: 11021
Event-Subclass: SMS%3A%3ASEND_MESSAGE
proto: sip
dest_proto: sip
from: 9515529832
from_full: 9515529832
to: internal/8507585138%40sms-proxy-01.bandwidthclec.com
subject: PATLive%20Testing
type: text/plain
hint: the%20hint
replying: true
Content-Length: 23
*/
this.addHeader('Content-Length', Buffer.byteLength(this.body, 'utf8'));
data = this.headers.reduce(function(d, header) {
d += header.name + ': ' + header.value + '\n';
return d;
}, '');
if (this.body) {
//url encode newlines in the body
data += '\n' + this.body;
}
return data;
case 'xml':
/*
<event>
<headers>
<Event-Name>CUSTOM</Event-Name>
<Core-UUID>8b192020-7368-4498-9b11-cbe10f48a784</Core-UUID>
<FreeSWITCH-Hostname>smsdev</FreeSWITCH-Hostname>
<FreeSWITCH-Switchname>smsdev</FreeSWITCH-Switchname>
<FreeSWITCH-IPv4>10.1.12.115</FreeSWITCH-IPv4>
<FreeSWITCH-IPv6>%3A%3A1</FreeSWITCH-IPv6>
<Event-Date-Local>2012-09-25%2014%3A26%3A17</Event-Date-Local>
<Event-Date-GMT>Tue,%2025%20Sep%202012%2018%3A26%3A17%20GMT</Event-Date-GMT>
<Event-Date-Timestamp>1348597577616542</Event-Date-Timestamp>
<Event-Calling-File>switch_cpp.cpp</Event-Calling-File>
<Event-Calling-Function>Event</Event-Calling-Function>
<Event-Calling-Line-Number>262</Event-Calling-Line-Number>
<Event-Sequence>11057</Event-Sequence>
<Event-Subclass>SMS%3A%3ASEND_MESSAGE</Event-Subclass>
<proto>sip</proto>
<dest_proto>sip</dest_proto>
<from>9515529832</from>
<from_full>9515529832</from_full>
<to>internal/8507585138%40sms-proxy-01.bandwidthclec.com</to>
<subject>PATLive%20Testing</subject>
<type>text/plain</type>
<hint>the%20hint</hint>
<replying>true</replying>
</headers>
<Content-Length>23</Content-Length>
<body>Hello from Chad Engler!</body>
</event>
*/
var xmlEncodedBody = encodeXml(this.body);
this.addHeader('Content-Length', Buffer.byteLength(xmlEncodedBody, 'utf8'));
data = '<event>\n';
//add headers
data += ' <headers>\n';
this.headers.forEach(function(header) {
data += ' <' + header.name + '>';
data += typeof header.value === 'string' ? encodeXml(header.value) : header.value;
data += '</' + header.name + '>\n';
});
data += ' </headers>\n';
//add body
if(xmlEncodedBody) {
data += ' <body>' + xmlEncodedBody + '</body>\n';
}
data += '</event>';
return data;
}
};
//Sets the priority of an event to $number in case it's fired.
//'NORMAL', 'LOW', 'HIGH', 'INVALID'
Event.prototype.setPriority = function(priority) {
this.addHeader('priority', priority);
};
//Gets the header with the key of $header_name from an event object.
Event.prototype.getHeader = function(name) {
var h = this._findHeader(name);
return (h ? h.value : null);
};
//Gets the body of an event object.
Event.prototype.getBody = function() {
return this.body;
};
//Gets the event type of an event object.
Event.prototype.getType = function() {
return this.type;
};
//Add $value to the body of an event object.
// This can be called multiple times for the same event object.
Event.prototype.addBody = function(value) {
return this.body += value;
};
//Add a header with key = $header_name and value = $value
// to an event object. This can be called multiple times
// for the same event object.
Event.prototype.addHeader = function(name, value) {
var h = this._findHeader(name);
if(h) h.value = value;
else this.headers.push({ name: name, value: value });
return value;
};
//Delete the header with key $header_name from an event object.
Event.prototype.delHeader = function(name) {
var i = this._findHeaderIndex(name);
return (i === null ? null : this.headers.splice(i, 1));
};
//Sets the pointer to the first header in an event object,
// and returns it's key name. This must be called before nextHeader is called.
Event.prototype.firstHeader = function() {
this.hPtr = 0;
return this.headers[0].name;
};
//Moves the pointer to the next header in an event object,
// and returns it's key name. firstHeader must be called
// before this method to set the pointer. If you're already
// on the last header when this method is called, then it will return NULL.
Event.prototype.nextHeader = function() {
//if no firstHeader called yet
if(this.hPtr === null)
return null;
//if reached end
if(this.hPtr === (this.headers.length - 1)) {
this.hPtr = null;
return null;
}
//increment and return
return this.headers[++this.hPtr].name;
};
Event.prototype._findHeaderIndex = function(name) {
for(var i = 0, len = this.headers.length; i < len; ++i) {
if(this.headers[i].name === name)
return i;
}
return null;
};
Event.prototype._findHeader = function(name) {
var i = this._findHeaderIndex(name);
return (i === null ? null : this.headers[i]);
};