├── README
├── bwpacksniffer.js
└── xsi-events_monitor.js
/README:
--------------------------------------------------------------------------------
1 | This is a Node.js server that sniffes xsi-events from/to Broadsoft BW.
--------------------------------------------------------------------------------
/bwpacksniffer.js:
--------------------------------------------------------------------------------
1 | //*********************** variables and objects used in the proxy ******************
2 | //no final, o object credentials sera um array porque vai ter varias ZD's apps conectando no proxy
3 | var credentials = [{
4 | //url: 'xsp1.pbxl.net', <-- no need as it will be fixed for all apps
5 | username: 'BWS_Test.zentestuser1@pbxl.net',
6 | password: 'Borras123',
7 | appId: '',
8 | callhalf: '',
9 | subscriptionId: '',
10 | channelId: '', //probably will be necessary in the very near future....
11 | zddomain: '',
12 | },
13 | {
14 | //url: 'xsp1.pbxl.net', <-- no need as it will be fixed for all apps
15 | username: 'BWS_Test.zentestuser2@pbxl.net',
16 | password: 'Borras123',
17 | appId: '',
18 | callhalf: '',
19 | subscriptionId: '',
20 | channelId: '', //probably will be necessary in the very near future....
21 | zddomain: '',
22 | }];
23 |
24 | //this array stores all the BW groups from all clients. A subscription must be opened for each one
25 | var BW_groups = ['PBXL_Test',];
26 |
27 | //this object stores data related to BW for each ZD's app. These data are unique to the application
28 | //as there is only one channel and subscription, that will receive all events from BW related to
29 | //all opened calls
30 | var bwconnection = {
31 | applicationId: 'bweventsniffer',
32 | channelSetId: 'bweventsnifferchannelset',
33 | channelId: '',
34 | heartbeatIntervalId: '', //TODO: this variable is global as there is only 1 streaming http
35 | subscriptionId: '',
36 | callhalf: '',
37 | };
38 |
39 | //**************** global constants to be used by all ZD's apps *********************
40 | var HEARTBEAT_INTERVAL = 15000;
41 | var BW_URL = 'xsp1.pbxl.net';
42 | var ZD_URL = 'pbxltest.zendesk.com';
43 | var EVENT_CLOSE = '';
44 | var CHANNEL_CLOSE = '';
45 | var HEARTBEAT_CLOSE = '';
46 |
47 | //****************** required objects and libs ***********************
48 | var express = require('express');
49 | var http = require('http');
50 | var path = require('path');
51 | //var https = require('https'); //the main http session(streaming http)
52 | var app = express();
53 | var parseString = require('xml2js').parseString;
54 | var DOMParser = require('xmldom').DOMParser;
55 | var fs = require('fs');
56 | var Log = require('log');
57 | var log = new Log('debug', fs.createWriteStream('snifferlog.txt', {'flags':'a'}));
58 |
59 | //******************* setup the proxy ***********************
60 | // all environments
61 | app.set('port', process.env.PORT || 3000);
62 | app.use(express.favicon());
63 | app.use(express.logger('dev'));
64 | app.use(express.bodyParser());
65 | app.use(express.methodOverride());
66 |
67 | app.use(express.static(path.join(__dirname, 'public')));
68 |
69 | app.use(app.router);
70 |
71 | // development only
72 | if ('development' == app.get('env')) {
73 | app.use(express.errorHandler());
74 | }
75 |
76 | //******************* commands from client(ZD app or test web page) ************************
77 | app.all('*', function(req, res, next){
78 | res.header("Access-Control-Allow-Origin", "https://pbxltest.zendesk.com");
79 | res.header("Access-Control-Allow-Headers", "X-Requested-With, Access-Control-Allow-Credentials, Authorization");
80 | res.header("Access-Control-Allow-Credentials", true);
81 | next();
82 | });
83 |
84 | app.all("/log_in/", function(req, res){
85 | console.log("/register_user/ received " + req.query);
86 | log.info(", username: " + req.param('username') +
87 | ", password: " + req.param('password') +
88 | ", appId: " + req.param('appid') +
89 | ", zddomain: " + req.param('zedomain') + '\r\n');
90 |
91 | res.writeHead(200, {'Content-Type': 'text/plain'});
92 | res.end();
93 | for(var index in credentials){
94 | if(credentials[index].username === req.param('username')){
95 | credentials[index].appId = req.param('appid');
96 | credentials[index].zddomain = req.param('zddomain');
97 | sendSignedInToZdApp(credentials[index].zddomain, credentials[index].appId, 'signedIn');
98 | }else{ //this user is not registered so, send back an error message to check the credentials
99 | sendSignInErrorToZD(req.param('zddomain'), req.param('appid'));
100 | }
101 | }
102 | });
103 |
104 | app.all("/make_call/", function(req, res){
105 | console.log("/make_call/ received " + req.query);
106 | res.send("Received your request to make a call to " + req.param('destination') + ". Now, wait a bit!");
107 | makeCall(req.param('destination'),req.param('username'));
108 | });
109 |
110 | app.all('/accept_call', function(req, res){
111 | console.log("/accept_call/ received" + req.query);
112 | res.send("Answering the call...");
113 | acceptCall(req.param('username'));
114 | });
115 |
116 | app.all('/disconnect_call/', function(req, res){
117 | console.log("/disconnect_call/ received" + req.query);
118 | res.send("Disconnecting call...")
119 | disconnectCall(req.param('username'));
120 | });
121 |
122 | app.all('/reject_call/', function(req, res){
123 | console.log("/reject_call/ received" + req.query);
124 | res.send("Rejecting call...")
125 | rejectCall(); //TODO: implement rejectCall();
126 | });
127 |
128 | //********************** event processing work functions *************************
129 | getName = function(){
130 | console.log("INFO: getName ->");
131 | var options = {
132 | host: BW_URL,
133 | path: "/com.broadsoft.xsi-actions/v2.0/user/" + credentials.username + "/profile",
134 | method: 'GET',
135 | auth: credentials.username + ':' + credentials.password
136 | };
137 | var http = require('http');
138 | var req = http.request(options, function(res) {
139 | log.info("< response from BW: " + res.statusCode + '\r\n');
140 | if(res.statusCode === 200){
141 | requestChannel();
142 | }else{
143 | sendSignInErrorToZD(credentials.appId);
144 | }
145 | });
146 |
147 | req.on('error', function(e) {
148 | log.info('problem with request: ' + e.message + '\r\n');
149 | });
150 |
151 | req.end();
152 | log.info('> GET ' + BW_URL + "/com.broadsoft.xsi-actions/v2.0/user/" + credentials.username + "/profile \r\n");
153 | };
154 |
155 | //TODO: probably, will have to open one channel for each user...
156 | //if so, the function must receive the username and password.
157 | requestChannel = function(){
158 | console.log("INFO: requestChannel ->");
159 | var options = {
160 | host: BW_URL,
161 | path: "/com.broadsoft.async/com.broadsoft.xsi-events/v2.0/channel",
162 | method: 'POST',
163 | //auth: username + ':' + password,
164 | auth: 'jp_zentest@pbxl.net:Borras123',
165 | headers: {'Content-Type': 'text/xml'}
166 | };
167 | var req = http.request(options, function(res){
168 | log.info("<- response from BW: " + res.statusCode + '\r\n');
169 | if(res.statusCode != 200){
170 | console.log("Error in requestChannel. Response status is " + res.statusCode);
171 | console.log("Will try again...");
172 | log.error("<- response from BW: " + res.statusCode + '\r\n');
173 | log.info("Will try again...");
174 | requestChannel();
175 | }
176 | res.setEncoding('utf8');
177 | var resbody = "";
178 | res.on('data', function (chunk) {
179 | console.log(chunk + '\r\n' + '\r\n');
180 | log.info("< " + chunk + '\r\n');
181 | resbody += chunk;
182 | if(resbody.indexOf(EVENT_CLOSE) >= 0 || resbody.indexOf(CHANNEL_CLOSE) >= 0 || resbody.indexOf(HEARTBEAT_CLOSE) >= 0){
183 | parseChunk(resbody);
184 | resbody = ""; //prepares to receive a new event, if any!
185 | }else if(resbody.indexOf('= 0){
186 | //do nothing here as it is only answer from the heartbeat command
187 | }
188 | });
189 | res.on('end',function(){
190 | /*console.log(" response data: " + resbody);
191 | parseString(resbody, function (err, result) {
192 | console.log(" Response's body: " + result);
193 | });*/
194 | });
195 | res.on('error', function(e){
196 | console.log("Error on requestChannel. Status is " + e.status);
197 | console.log("Error message: " + e.message);
198 | console.log("Will try again...");
199 | requestChannel();
200 | });
201 | });
202 |
203 | req.on('error', function(e) {
204 | console.log('problem with requestChannel request: ' + e.message);
205 | });
206 | console.log("channelSetId in requestChannel function is " + bwconnection.channelSetId);
207 | var xml_data = '';
208 | xml_data = xml_data + '';
209 | xml_data = xml_data + '' + bwconnection.channelSetId + '';
210 | xml_data = xml_data + '1';
211 | xml_data = xml_data + '100';
212 | xml_data = xml_data + '3600';
213 | xml_data = xml_data + '' + bwconnection.applicationId + '';
214 | xml_data = xml_data + '';
215 |
216 | req.write(xml_data);
217 | req.end();
218 | log.info('> POST ' + BW_URL + '/com.broadsoft.async/com.broadsoft.xsi-events/v2.0/channel \r\n' + xml_data + '\r\n');
219 | };
220 |
221 | startHeartbeat = function(channelid){
222 | console.log("INFO: startHeartbeat ->");
223 | var options = {
224 | host: BW_URL,
225 | path: '/com.broadsoft.xsi-events/v2.0/channel/' + channelid + "/heartbeat",
226 | method: 'PUT',
227 | //auth: username + ':' + password
228 | auth: 'jp_zentest@pbxl.net:Borras123' //the correct is the above!!!
229 | };
230 | var http = require('http');
231 | var req = http.request(options, function(res) {
232 | log.info("< response from BW on heartbeat: " + res.statusCode + '\r\n');
233 | });
234 |
235 | req.on('error', function(e) {
236 | console.log('problem with heartbeat request: ' + e.message);
237 | });
238 |
239 | req.end();
240 | log.info('> PUT ' + BW_URL + '/com.broadsoft.xsi-events/v2.0/channel/' + bwconnection.channelId + "/heartbeat \r\n");
241 |
242 | heartbeatIntervalId = setTimeout(function(){
243 | if(bwconnection.channelId != ''){
244 | startHeartbeat();
245 | }else{
246 | //if there is no channel, then start again from requesting a new one
247 | requestChannel();
248 | }
249 | }, HEARTBEAT_INTERVAL);
250 | };
251 |
252 | //need to make a subscription for each user registered
253 | eventSubscription = function(event, username, password){
254 | console.log("INFO: eventSubscription to " + username + " ->");
255 | var options = {
256 | host: BW_URL,
257 | //path: "/com.broadsoft.xsi-events/v2.0/user/" + username,
258 | path: "/com.broadsoft.xsi-events/v2.0/serviceprovider/PBXL%20Inc./group/PBXL_Test",
259 | method: 'POST',
260 | auth: 'jp_zentest@pbxl.net:Borras123',
261 | headers: {'Content-Type': 'text/xml'}
262 | };
263 | var http = require('http');
264 | var req = http.request(options, function(res){
265 | console.log("<- response from BW: " + res.statusCode);
266 | res.setEncoding('utf8');
267 | var resbody = "";
268 | res.on('data', function (chunk) {
269 | log.info('< ' + chunk + '\r\n');
270 | });
271 | res.on('end',function(){
272 | //TODO
273 | });
274 |
275 | });
276 |
277 | req.on('error', function(e) {
278 | console.log('problem with request: ' + e.message);
279 | });
280 |
281 | console.log("channelSetId in eventSubscription function is " + bwconnection.channelSetId);
282 | var xml_data = '';
283 | xml_data = xml_data + '';
284 | xml_data = xml_data + "" + event + "";
285 | xml_data = xml_data + "3600";
286 | xml_data = xml_data + "" + bwconnection.channelSetId + "";
287 | xml_data = xml_data + '' + bwconnection.applicationId + '';
288 | xml_data = xml_data + "";
289 |
290 | req.write(xml_data);
291 | req.end();
292 | log.info('> POST ' + BW_URL + "/com.broadsoft.xsi-events/v2.0/serviceprovider/PBXL%20Inc./group/PBXL_Test" + '\r\n' + xml_data + '\r\n');
293 | };
294 |
295 | sendResponseEvent = function(eventId){
296 | console.log("INFO: sendResponseEvent ->");
297 | var options = {
298 | host: BW_URL,
299 | path: "/com.broadsoft.xsi-events/v2.0/channel/eventresponse",
300 | method: 'POST',
301 | auth: 'jp_zentest@pbxl.net:Borras123',
302 | //auth: username + ':' + password,
303 | headers: {'Content-Type': 'text/xml'}
304 | };
305 | var http = require('http');
306 | var req = http.request(options, function(res){
307 | //console.log("<- response from BW: " + res.statusCode);
308 | });
309 |
310 | req.on('error', function(e) {
311 | console.log(' problem with sendResponseEvent request: ' + e.message);
312 | });
313 |
314 | var xml_data = '';
315 | xml_data = xml_data + "";
316 | xml_data = xml_data + "" + eventId + "";
317 | xml_data = xml_data + "200";
318 | xml_data = xml_data + "OK";
319 | xml_data = xml_data + "";
320 |
321 | req.write(xml_data);
322 | req.end();
323 |
324 | log.info('> POST ' + BW_URL + "/com.broadsoft.xsi-events/v2.0/channel/eventresponse \r\n" + xml_data + '\r\n');
325 | };
326 |
327 | parseChunk = function(chunk){ //chunk is already string
328 | //now, look for what kind of event we received
329 | if(chunk.indexOf('= 0){ //= 0){
337 | //TODO: for now do nothing as it is only answer from heartbeat
338 | }else if(chunk.indexOf('ChannelTerminatedEvent') >= 0){
339 | console.log("WARNING: ChannelTerminatedEvent <-");
340 | bwconnection.channelId = '';
341 | }else if(chunk.indexOf('= 0){//xsi:Event received. Now see if it is channel disconnection
342 | //for every xsi:Event, needs to send event Response
343 | var xmldoc = new DOMParser().parseFromString(chunk,'text/xml');
344 | var eventid = xmldoc.getElementsByTagName('xsi:eventID').item(0).firstChild.nodeValue;
345 | sendResponseEvent(eventid);
346 | var userid = xmldoc.getElementsByTagName('xsi:userId').item(0).firstChild.nodeValue;
347 | var targetid = xmldoc.getElementsByTagName('xsi:targetId').item(0).firstChild.nodeValue;
348 | var remoteparty = xmldoc.getElementsByTagName('xsi:address').item(0).firstChild.nodeValue.substring(5);
349 | var eventType = xmldoc.getElementsByTagName('xsi:eventData').item(0).getAttribute('xsi1:type').trim();
350 | eventType = eventType.substring(4);//string off the prefix "xsi:" from the eventType
351 | switch(eventType){
352 | case 'CallReceivedEvent':
353 | var countrycode = xmldoc.getElementsByTagName('xsi:address').item(0).getAttribute('countryCode');
354 | if(remoteparty.indexOf(countrycode) >= 0){
355 | remoteparty = remoteparty.replace(countrycode, '0');
356 | }
357 | console.log("INFO: CallReceived(from: " + remoteparty + " to: " + targetid + ") <-");
358 | break;
359 | case 'CallAnsweredEvent':
360 | console.log("INFO: CallAnsweredEvent(userid: " + remoteparty + ") <-");
361 | break;
362 | case 'CallReleasedEvent':
363 | console.log("INFO: CallReleasedEvent(userid: " + remoteparty + ") <-");
364 | break;
365 | case 'CallUpdatedEvent':
366 | console.log("INFO: CallUpdatedEvent <-");
367 | break;
368 | case 'CallSubscriptionEvent':
369 | console.log("INFO: CallSubscriptionEvent <-");
370 | break;
371 | case 'CallOriginatedEvent':
372 | console.log("INFO: CallOriginatedEvent(from: " + targetid + " to: " + remoteparty + ") <-");
373 | break;
374 | case 'CallRetrievedEvent':
375 | console.log("INFO: CallRetrievedEvent(userid: " + remoteparty + ") <-");
376 | break;
377 | case 'CallHeldEvent':
378 | console.log('INFO: CallHelEvent <-');
379 | break;
380 | case 'CallTransferredEvent':
381 | console.log('INFO: CallTransferredEvent <-');
382 | break;
383 | case 'CallRedirectedEvent':
384 | console.log('INFO: CallRedirectedEvent');
385 | break;
386 | case 'CallHeldEvent':
387 | case 'DoNotDisturbEvent':
388 | case 'CallForwardingAlwaysEvent':
389 | case 'RemoteOfficeEvent':
390 | break;
391 | default:
392 | }
393 | }
394 | };
395 |
396 | //**************** listen for incoming events ***********************
397 | requestChannel();
--------------------------------------------------------------------------------
/xsi-events_monitor.js:
--------------------------------------------------------------------------------
1 | //*********************** variables and objects used in the proxy ******************
2 | //no final, o object credentials sera um array porque vai ter varias ZD's apps conectando no proxy
3 | var credentials = [{
4 | username: 'BWS_Test.zentestuser1@pbxl.net',
5 | password: 'Borras123',
6 | },
7 | {
8 | username: 'BWS_Test.zentestuser2@pbxl.net',
9 | password: 'Borras123',
10 | }
11 | ];
12 |
13 | //this array stores all the BW groups from all clients. A subscription must be opened for each one
14 | var BW_groups = ['PBXL_Test',];
15 |
16 | //this object stores data related to BW for each ZD's app. These data are unique to the application
17 | //as there is only one channel and subscription, that will receive all events from BW related to
18 | //all opened calls
19 | var bwconnection = {
20 | applicationId: 'xsisnifferchannel',
21 | channelSetId: 'xsisnifferchannelset',
22 | channelId: '',
23 | heartbeatIntervalId: '',
24 | channelUpdateIntervalId: '',
25 | subscriptionId: '',
26 | subscriptionUpdateIntervalId: '',
27 | callhalf: '',
28 | groupadmin: 'jp_zentest@pbxl.net',
29 | groupadminpassword: 'Borras123',
30 | serviceprovider: 'PBXL%20Inc.',
31 | groupId: 'PBXL_Test',
32 | };
33 |
34 | //**************** global constants to be used by all ZD's apps *********************
35 | var HEARTBEAT_INTERVAL = 15000;
36 | var CHANNEL_UPDATE_INTERVAL = 1800000;
37 | var SUBSCRIPTION_UPDATE_INTERVAL = 1600000;
38 | var BW_URL = 'xsp1.pbxl.net';
39 | var SUBSCRIPTION_CLOSE = '';
40 | var EVENT_CLOSE = '';
41 | var CHANNEL_CLOSE = '';
42 | var HEARTBEAT_CLOSE = '';
43 |
44 | //****************** required objects and libs ***********************
45 | require('monitor').start();
46 | var express = require('express');
47 | var http = require('http'); //http object used to connect the proxy with BW server
48 | var path = require('path');
49 | var app = express();
50 | var parseString = require('xml2js').parseString;
51 | var DOMParser = require('xmldom').DOMParser;
52 | var fs = require('fs');
53 | var Log = require('log');
54 | var log = new Log('debug', fs.createWriteStream('xsi-events-monitor_log.txt', {'flags':'a'}));
55 |
56 | //******************* setup the proxy ***********************
57 | // all environments
58 | app.set('port', process.env.PORT || 5000);
59 | app.set('views', __dirname + '/views');
60 | app.engine('html', require('ejs').renderFile);
61 | app.use(express.favicon());
62 | app.use(express.logger('dev'));
63 | app.use(express.bodyParser());
64 | app.use(express.methodOverride());
65 |
66 | app.use(express.static(path.join(__dirname, 'public')));
67 |
68 | app.use(app.router);
69 |
70 | // development only
71 | if ('development' == app.get('env')) {
72 | app.use(express.errorHandler());
73 | }
74 |
75 | //******************* commands from client(ZD app or test web page) ************************
76 | //TODO: the Access-Control is not working -> have to check it
77 | app.all('*', function(req, res, next){
78 | //res.header("Access-Control-Allow-Origin", "https://pbxltest.zendesk.com");
79 | //res.header("Access-Control-Allow-Origin", "https://ap.salesforce.com");
80 | res.header("Access-Control-Allow-Origin", "*");
81 | res.header("Access-Control-Allow-Headers", "X-Requested-With, Access-Control-Allow-Credentials, Authorization");
82 | res.header("Access-Control-Allow-Credentials", true);
83 | next();
84 | });
85 |
86 | app.all('/', function(req, res, next){
87 | console.log("/ received " + req.query);
88 | log.info('<- / received ');
89 | res.send('nothing here for you...');
90 | });
91 |
92 | //connect to be used for some web app to receive and display xsi events
93 | app.post("/connect/", function(req, res){
94 | var username = req.param('username');
95 | console.log("<- /connect/ called from user " + username);
96 | log.info('<- /connect/ called from user ' + username);
97 | for(var index in credentials){
98 | if(credentials[index].username == username){
99 | console.log("found user " + username + " in credentials and will now connect it");
100 | credentials[index].appId = res;
101 | var connectresponse = '';
102 | connectresponse += 'ConnectResponse';
103 | connectresponse += '';
104 | console.log("-> ConnectResponse to SFDC(" + username + ")");
105 | log.info("-> ConnectResponse to SFDC(" + username + ")");
106 | res.setHeader('Content-Type', 'text/xml; charset=UTF-8');
107 | res.setHeader('Transfer-Encoding', 'chunked');
108 | res.write(connectresponse, 'utf8');
109 | }else{
110 | console.log("Username " + username + " not in credentials");
111 | }
112 | }
113 | });
114 |
115 | //********************** event processing work functions *************************
116 | requestChannel = function(){
117 | console.log("-> INFO: requestChannel");
118 | var options = {
119 | host: BW_URL,
120 | path: "/com.broadsoft.async/com.broadsoft.xsi-events/v2.0/channel",
121 | method: 'POST',
122 | auth: bwconnection.groupadmin + ':' + bwconnection.groupadminpassword,
123 | headers: {'Content-Type': 'text/xml'}
124 | };
125 | var req = http.request(options, function(res){
126 | if(res.statusCode != 200 && res.statusCode != 401 && res.statusCode != 403){//not auth problem
127 | console.log("Error in requestChannel. Response status is " + res.statusCode);
128 | log.error("<- response from BW: " + res.statusCode + '\r\n');
129 | console.log("Will try again in 5 secs...");
130 | log.info("Will try again in 5 secs...");
131 | setTimeout(function(){
132 | requestChannel();
133 | },5000);
134 | }
135 | res.setEncoding('utf8');
136 | var resbuffer = '';
137 | var index = 0;
138 | var resbody = "";
139 | res.on('data', function (chunk) {
140 | resbody += chunk;
141 | if(resbody.indexOf(EVENT_CLOSE) >= 0 || resbody.indexOf(CHANNEL_CLOSE) >= 0 ||
142 | resbody.indexOf(HEARTBEAT_CLOSE) >= 0 || resbody.indexOf(SUBSCRIPTION_CLOSE) >= 0){
143 | log.info("<- " + chunk + '\r\n');
144 | console.log(chunk);
145 | parseChunk(resbody);
146 | resbody = ""; //prepares to receive a new event, if any!
147 | }else if(resbody.indexOf('= 0){
148 | //do nothing here as it is only answer from the heartbeat command
149 | }else{
150 | resbuffer += chunk;
151 | }
152 | });
153 | res.on('error', function(e){
154 | console.log("Error on requestChannel. Status is " + e.status);
155 | console.log("Error message: " + e.message);
156 | console.log("Will try again...");
157 | requestChannel();
158 | });
159 | res.on('close', function(e){
160 | console.log("ERROR: Main connection closed.");
161 | log.error("Main connection closed.");
162 | });
163 | });
164 |
165 | req.on('error', function(e) {
166 | console.log('problem with requestChannel request: ' + e.message);
167 | });
168 | console.log("channelSetId in requestChannel function is " + bwconnection.channelSetId);
169 | var xml_data = '';
170 | xml_data = xml_data + '';
171 | xml_data = xml_data + '' + bwconnection.channelSetId + '';
172 | xml_data = xml_data + '1';
173 | xml_data = xml_data + '100';
174 | xml_data = xml_data + '3600';
175 | xml_data = xml_data + '' + bwconnection.applicationId + '';
176 | xml_data = xml_data + '';
177 |
178 | req.write(xml_data);
179 | req.end();
180 | log.info('-> POST ' + BW_URL + '/com.broadsoft.async/com.broadsoft.xsi-events/v2.0/channel \r\n' + xml_data + '\r\n');
181 | };
182 |
183 | updateChannel = function(){
184 | console.log("-> INFO: updateChannel ID " + bwconnection.channelId);
185 | log.info("-> updateChannel ID " + bwconnection.channelId);
186 | var options = {
187 | host: BW_URL,
188 | path: "/com.broadsoft.xsi-events/v2.0/channel/" + bwconnection.channelId,
189 | method: 'PUT',
190 | auth: bwconnection.groupadmin + ':' + bwconnection.groupadminpassword,
191 | headers: {'Content-Type': 'text/xml'}
192 | };
193 | var http = require('http');
194 | var req = http.request(options, function(res){
195 | if(res.statusCode != 200){
196 | console.log("Error in updateChannel. Response status is " + res.statusCode);
197 | log.error("<- response from BW: " + res.statusCode + '\r\n');
198 | }
199 | });
200 | req.on('error', function(e) {
201 | console.log('problem with updateChannel request: ' + e.message);
202 | });
203 | console.log("channelSetId in requestChannel function is " + bwconnection.channelSetId);
204 | var xml_data = '';
205 | xml_data = xml_data + '';
206 | xml_data = xml_data + '3800';
207 | xml_data = xml_data + '';
208 |
209 | req.write(xml_data);
210 | req.end();
211 | log.info('-> POST ' + BW_URL + '/com.broadsoft.xsi-events/v2.0/channel/' + bwconnection.channelId + '\r\n' + xml_data + '\r\n');
212 | };
213 |
214 | startHeartbeat = function(){
215 | if(bwconnection.channelId != ''){
216 | console.log("-> INFO: startHeartbeat");
217 | var options = {
218 | host: BW_URL,
219 | path: '/com.broadsoft.xsi-events/v2.0/channel/' + bwconnection.channelId + "/heartbeat",
220 | method: 'PUT',
221 | auth: bwconnection.groupadmin + ':' + bwconnection.groupadminpassword,
222 | };
223 | var http = require('http');
224 | var req = http.request(options, function(res) {
225 | if(res.statusCode != 200){//some problems happened with the channel. Open a new one
226 | log.error("<- response from BW on heartbeat: " + res.statusCode + '\r\n');
227 | }
228 | log.info("<- response from BW on heartbeat: " + res.statusCode + '\r\n');
229 | });
230 |
231 | req.on('error', function(e) {
232 | console.log('problem with heartbeat request: ' + e.message);
233 | });
234 |
235 | req.end();
236 | log.info('-> PUT ' + BW_URL + '/com.broadsoft.xsi-events/v2.0/channel/' + bwconnection.channelId + "/heartbeat \r\n");
237 | }else{
238 | console.log("WARNING: now heartbeat sent as there is no channel openned");
239 | log.warning("WARNING: now heartbeat sent as there is no channel openned");
240 | }
241 | };
242 |
243 | //need to make a subscription for each user registered
244 | eventSubscription = function(event){
245 | console.log("-> INFO: eventSubscription");
246 | var options = {
247 | host: BW_URL,
248 | path: "/com.broadsoft.xsi-events/v2.0/serviceprovider/" + bwconnection.serviceprovider +
249 | "/group/" + bwconnection.groupId,
250 | method: 'POST',
251 | auth: bwconnection.groupadmin + ':' + bwconnection.groupadminpassword,
252 | headers: {'Content-Type': 'text/xml'}
253 | };
254 | var http = require('http');
255 | var req = http.request(options, function(res){
256 | if(res.statusCode != 200){
257 | console.log("ERROR: <- Subscription response from BW: " + res.statusCode);
258 | log.error("<- Subscription response from BW: " + res.statusCode);
259 | }else{
260 | res.setEncoding('utf8');
261 | res.on('data', function(response){
262 | console.log("<- Subscription Response: " + response + '\r\n');
263 | log.info("<- Subscription Response: " + response + '\r\n');
264 | var xmldoc = new DOMParser().parseFromString(response,'text/xml');
265 | bwconnection.subscriptionId = xmldoc.getElementsByTagName('subscriptionId').item(0).firstChild.nodeValue;
266 | bwconnection.subscriptionUpdateIntervalId = setInterval(updateSubscription, SUBSCRIPTION_UPDATE_INTERVAL);
267 | })
268 | }
269 | });
270 |
271 | req.on('error', function(e) {
272 | console.log('problem with request: ' + e.message);
273 | });
274 |
275 | console.log("channelSEtId in eventSubscription function is " + bwconnection.channelSetId);
276 | var xml_data = '';
277 | xml_data = xml_data + '';
278 | xml_data = xml_data + "" + event + "";
279 | xml_data = xml_data + "3600";
280 | xml_data = xml_data + "" + bwconnection.channelSetId + "";
281 | xml_data = xml_data + '' + bwconnection.applicationId + '';
282 | xml_data = xml_data + "";
283 |
284 | req.write(xml_data);
285 | req.end();
286 | log.info('-> POST ' + BW_URL + "/com.broadsoft.xsi-events/v2.0/serviceprovider/" + bwconnection.serviceprovider + "/group/" + bwconnection.groupId + '\r\n' + xml_data + '\r\n');
287 | };
288 |
289 | updateSubscription = function(){
290 | console.log("-> INFO: updateSubscription ID " + bwconnection.subscriptionId);
291 | log.info("-> updateSubscription ID " + bwconnection.subscriptionId);
292 | var options = {
293 | host: BW_URL,
294 | path: "/com.broadsoft.xsi-events/v2.0/subscription/" + bwconnection.subscriptionId ,
295 | method: 'PUT',
296 | auth: bwconnection.groupadmin + ':' + bwconnection.groupadminpassword,
297 | headers: {'Content-Type': 'text/xml'}
298 | };
299 | var http = require('http');
300 | var req = http.request(options, function(res){
301 | if(res.statusCode != 200){
302 | console.log("<- Subscription Update response from BW: " + res.statusCode);
303 | log.error("<- Subscription Update response from BW: " + res.statusCode);
304 | }
305 | });
306 |
307 | req.on('error', function(e) {
308 | console.log('problem with request: ' + e.message);
309 | });
310 |
311 | var xml_data = '';
312 | xml_data = xml_data + '';
313 | xml_data = xml_data + "3800";
314 | xml_data = xml_data + "";
315 |
316 | req.write(xml_data);
317 | req.end();
318 | log.info('-> POST ' + BW_URL + "/com.broadsoft.xsi-events/v2.0/subscription/" + bwconnection.subscriptionId + '\r\n' + xml_data + '\r\n');
319 | };
320 |
321 | userSubscription = function(username, password, event, callback){
322 | console.log("-> INFO: userSubscription");
323 | var options = {
324 | host: BW_URL,
325 | path: "/com.broadsoft.xsi-events/v2.0/user/" + username,
326 | method: 'POST',
327 | auth: 'jp_zentest@pbxl.net' + ':' + 'Borras123',
328 | headers: {'Content-Type': 'text/xml'}
329 | };
330 | var http = require('http');
331 | var req = http.request(options, function(res){
332 | if(res.statusCode != 200){
333 | console.log("ERROR: <- Subscription response from BW: " + res.statusCode);
334 | log.error("<- Subscription response from BW: " + res.statusCode);
335 | }else{
336 | res.setEncoding('utf8');
337 | res.on('data', function(response){
338 | console.log("<- Subscription Response: " + response + '\r\n');
339 | log.info("<- Subscription Response: " + response + '\r\n');
340 | //var xmldoc = new DOMParser().parseFromString(response,'text/xml');
341 | //bwconnection.subscriptionId = xmldoc.getElementsByTagName('subscriptionId').item(0).firstChild.nodeValue;
342 | //bwconnection.subscriptionUpdateIntervalId = setInterval(updateSubscription, SUBSCRIPTION_UPDATE_INTERVAL);
343 | })
344 | }
345 | callback(res.statusCode);
346 | });
347 |
348 | req.on('error', function(e) {
349 | console.log('problem with request: ' + e.message);
350 | });
351 |
352 | console.log("channelSEtId in eventSubscription function is " + bwconnection.channelSetId);
353 | var xml_data = '';
354 | xml_data = xml_data + '';
355 | xml_data = xml_data + "" + event + "";
356 | xml_data = xml_data + "3600";
357 | xml_data = xml_data + "" + bwconnection.channelSetId + "";
358 | xml_data = xml_data + '' + bwconnection.applicationId + '';
359 | xml_data = xml_data + "";
360 |
361 | req.write(xml_data);
362 | req.end();
363 | log.info('-> POST ' + BW_URL + "/com.broadsoft.xsi-events/v2.0/user/" + username + '\r\n' + xml_data + '\r\n');
364 | };
365 |
366 | sendResponseEvent = function(eventId){
367 | console.log("-> INFO: sendResponseEvent");
368 | var options = {
369 | host: BW_URL,
370 | path: "/com.broadsoft.xsi-events/v2.0/channel/eventresponse",
371 | method: 'POST',
372 | auth: bwconnection.groupadmin + ':' + bwconnection.groupadminpassword,
373 | headers: {'Content-Type': 'text/xml'}
374 | };
375 | var http = require('http');
376 | var req = http.request(options, function(res){
377 | if(res.statusCode != 200){
378 | console.log("<- response from BW: " + res.statusCode);
379 | log.error("<- response from BW: " + res.statusCode);
380 | }
381 | });
382 |
383 | req.on('error', function(e) {
384 | console.log('problem with sendResponseEvent request: ' + e.message);
385 | });
386 |
387 | var xml_data = '';
388 | xml_data = xml_data + "";
389 | xml_data = xml_data + "" + eventId + "";
390 | xml_data = xml_data + "200";
391 | xml_data = xml_data + "OK";
392 | xml_data = xml_data + "";
393 |
394 | req.write(xml_data);
395 | req.end();
396 |
397 | log.info('-> POST ' + BW_URL + "/com.broadsoft.xsi-events/v2.0/channel/eventresponse \r\n" + xml_data + '\r\n');
398 | };
399 |
400 | parseChunk = function(chunk){ //chunk is already string
401 | //now, look for what kind of event we received
402 | if(chunk.indexOf('= 0){ //= 0){
424 | //TODO: for now do nothing as it is only answer from heartbeat
425 | }else if(chunk.indexOf('ChannelTerminatedEvent') >= 0){
426 | console.log("WARNING: ChannelTerminatedEvent <-");
427 | log.warning("ChannelTerminatedEvent <-");
428 | bwconnection.channelId = '';
429 | requestChannel();
430 | }else if(chunk.indexOf('SubscriptionTerminatedEvent') >= 0){//will open a new subscription
431 | console.log('WARNING: SubscriptionTerminatedEvent <-');
432 | log.warning('SubscriptionTerminatedEvent <-');
433 | bwconnection.subscriptionId = '';
434 | }else if(chunk.indexOf('= 0){//xsi:Event received. Now see if it is channel disconnection
435 | //for every xsi:Event, needs to send event Response
436 | try{
437 | var xmldoc = new DOMParser().parseFromString(chunk,'text/xml');
438 | var eventid = xmldoc.getElementsByTagName('xsi:eventID').item(0).firstChild.nodeValue;
439 | sendResponseEvent(eventid);
440 | }catch(error){
441 | //TODO: for now, do nothing as it means that some event does not contains the
442 | //searched node
443 | }
444 | for(var index in credentials){
445 | var responseobj = credentials[index].appId;
446 | try{
447 | responseobj.write(chunk);
448 | }
449 | catch(error){}
450 | }
451 | }
452 | };
453 |
454 | //**************** listen for incoming events ***********************
455 | var opts = {key: fs.readFileSync('key.pem'), cert: fs.readFileSync('cert.pem')};
456 |
457 | http.createServer(app).listen(app.get('port'), function() {
458 | console.log('Express server listening on port ' + app.get('port'));
459 | });
460 |
461 | /*mainhttps = require('https'); //the https object to connect the client with the proxy
462 | mainhttps.createServer(opts, app).listen(app.get('port'), function(){
463 | console.log('Express HTTPS server listening on port ' + app.get('port'));
464 | });*/
465 | //**************** start the server by registering a channel in BW ************
466 | requestChannel();
--------------------------------------------------------------------------------