1 if(typeof(socketbug) === 'undefined') 2 { 3 /** 4 * Socketbug - Web Socket Remote Debugging 5 * 6 * Copyright (c) 2011 Manifest Interactive, LLC 7 * 8 * Licensed under the LGPL v3 licenses. 9 * 10 * @version v0.2.1 ( 7/4/2011 ) 11 * 12 * @author <a href="http://www.socketbug.com">Website</a> 13 * @author <a href="http://www.vimeo.com/user7532036/videos">Video Tutorials ( HD )</a> 14 * @author <a href="http://www.twitter.com/socketbug_dev">Twitter</a> 15 * @author <a href="http://github.com/manifestinteractive/socketbug">Source Code</a> 16 * @author <a href="http://socketbug.userecho.com">Support & Feature Requests</a> 17 * 18 * @namespace Socketbug Server 19 */ 20 socketbug = function() 21 { 22 /** 23 * Socketbug Configuration 24 * 25 * @type Object 26 * 27 * @example <b>_socketbug._config._server_port</b> ( Number ): <b>8080</b> 28 * 29 * This is your Socketbugs Server Port Number Where Socket.IO is Running 30 * 31 * @example <b>_socketbug._config._authorized_only._groups</b> ( Boolean ): <b>true</b> 32 * 33 * Should Group Authentication Be Required? If this is set to true 34 * then all connections will have their Group ID authenticated on 35 * whatever server you specify below 36 * 37 * @example <b>_socketbug._config._authorized_only._applications</b> ( Boolean ): <b>true</b> 38 * 39 * Should Application Authentication Be Required? If this is set to 40 * true then all connections will have their Application ID 41 * authenticated on whatever server you specify below 42 * 43 * @example <b>_socketbug._config._authorize._host</b> ( String ): <b>www.mywebsite.com</b> 44 * 45 * Host URL ( NOTE: do not add http:// or a trailing / ) to 46 * Authentication Script. This can be any server. It does not 47 * have to be on the same server that Socketbug is installed on. 48 * 49 * @example <b>_socketbug._config._authorize._port</b> ( Number ): <b>80</b> 50 * 51 * Port Number for your Authentication Script 52 * 53 * @example <b>_socketbug._config._authorize._path</b> ( String ): <b>/path/to/my/auth.script</b> 54 * 55 * Path from root of Authentication Script. Basically, this 56 * gets appended to _socketbug._config._authorize._host to 57 * make a complete URL. ( NOTE: This should start with a / ) 58 * 59 * @example <b>_socketbug._config._encryption_salt</b> ( String ): <b>Go0dp4$sW0rd</b> 60 * 61 * SALT you would like to use for MD5 Hashing. This is the SAME SALT 62 * you will use throughout your Socketbug Communications. 63 * 64 * @example <b>_socketbug._groups</b> ( Object ) 65 * 66 * Object to Store Socketbug Groups. You will not need to 67 * mess with this, it's just for storing connection 68 * info on Socketbug. 69 * 70 * @example <b>_socketbug._applications</b> ( Object ) 71 * 72 * Object to Store Socketbug Applications. You will not 73 * need to mess with this, it's just for storing connection 74 * info on Socketbug. 75 * 76 * @example <b>_socketbug._clients</b> ( Object ) 77 * 78 * Object to Store Socketbug Clients. You will not need 79 * to mess with this, it's just for storing connection 80 * info on Socketbug. 81 */ 82 var _socketbug = 83 { 84 '_config': 85 { 86 '_server_port': 8080, 87 '_authorized_only': 88 { 89 '_groups': true, 90 '_applications': true 91 }, 92 '_authorize': 93 { 94 '_host': 'localhost', 95 '_port': 80, 96 '_path': '/socketbug/client/example/auth/validate.php' 97 }, 98 '_encryption_salt': 'Ch4ng3^M3' 99 }, 100 '_groups': {}, 101 '_applications': {}, 102 '_clients': {} 103 }; 104 105 /* ================================================== */ 106 /* Should Not Need to Change Anything Below Here */ 107 /* ================================================== */ 108 109 /** Basic Functionality to Manage GUID's */ 110 var GUID={is_valid:function(value){if(typeof(value)=='undefined'){return false;}if(value.length!==36){return false;}rGx=new RegExp("\\b(?:[A-F0-9]{8})(?:-[A-F0-9]{4}){3}-(?:[A-F0-9]{12})\\b");return (rGx.exec(value)!=null);},create:function(){if(arguments.length==1&&this.is_valid(arguments[0])){value=arguments[0];return value;}var res=[];var hv;var rgx=new RegExp("[2345]");for(var i=0;i<8;i++){hv=(((1+Math.random())*0x10000)|0).toString(16).substring(1);if(rgx.exec(i.toString())!=null){if(i==3){hv="6"+hv.substr(1,3);}res.push("-");}res.push(hv.toUpperCase());}value=res.join('');return value;}}; 111 112 /** Node.js Requirements */ 113 var http = require('http'), 114 crypto = require('crypto'); 115 116 /** Setup Socket.io to Listen to Server */ 117 var io = require('socket.io').listen(_socketbug._config._server_port); 118 119 /** Configure Socket.IO */ 120 io.configure('production', function () { 121 io.set('polling duration', 20); 122 io.set('log level', 1); 123 }); 124 io.configure('development', function () { 125 io.set('polling duration', 60); 126 io.set('log level', 3); 127 }); 128 io.configure(function () { 129 io.set('polling duration', 60); 130 io.set('log level', 2); 131 }); 132 133 /** Build a Socket to Isolate the Application Layer */ 134 var sb_manager = io 135 .of('/sb_manager') 136 .on('connection', function (socket) 137 { 138 /** 139 * @function disconnect 140 * 141 * Do some House Keeping to Clean Up Stored Data 142 */ 143 socket.on('disconnect', function () 144 { 145 /** Manage Groups on Client Disconnect */ 146 if(_socketbug._groups) 147 { 148 try 149 { 150 /** Determine Group Client belongs to */ 151 var my_group = _socketbug._clients[socket.id].group.id; 152 153 /** Keep track of Clients in the Group */ 154 var group_clients = 0; 155 156 /** Check if Client Group Exists on Socketbug */ 157 if(typeof(_socketbug._applications[my_application]) != 'undefined') 158 { 159 /** Loop through Group Clients and remove Disconnecting Client */ 160 for(var i=0; i<_socketbug._groups[my_group].clients.length; i++) 161 { 162 /** This is the Client that's Disconnecting */ 163 if(_socketbug._groups[my_group].clients[i] == socket.id) 164 { 165 /** Remove this Client from the Group */ 166 _socketbug._groups[my_group].clients.splice(i, 1); 167 } 168 /** There are other Clients in the Group */ 169 else 170 { 171 group_clients++; 172 } 173 } 174 175 /** Delete the Group if all Connected Clients from Socketbug are Gone */ 176 if(group_clients == 0) 177 { 178 delete _socketbug._groups[my_group]; 179 } 180 } 181 } 182 catch(error) 183 { 184 console.error(error); 185 } 186 } 187 188 /** Manage Applications on Client Disconnect */ 189 if(_socketbug._applications) 190 { 191 try 192 { 193 /** Determine Application Client belongs to */ 194 var my_application = _socketbug._clients[socket.id].application.id; 195 196 /** Keep track of Clients in the Application */ 197 var application_clients = 0; 198 199 /** Check if Client Application Exists on Socketbug */ 200 if(typeof(_socketbug._applications[my_application]) != 'undefined') 201 { 202 /** Loop through Application Clients and remove Disconnecting Client */ 203 for(var i=0; i<_socketbug._applications[my_application].clients.length; i++) 204 { 205 /** This is the Client that's Disconnecting */ 206 if(_socketbug._applications[my_application].clients[i] == socket.id) 207 { 208 /** Remove this Client from the Application */ 209 _socketbug._applications[my_application].clients.splice(i, 1); 210 } 211 /** There are other Clients in the Application */ 212 else 213 { 214 application_clients++; 215 } 216 } 217 218 /** Delete the Application if all Connected Clients from Socketbug are Gone */ 219 if(application_clients == 0) 220 { 221 delete _socketbug._applications[my_application]; 222 } 223 } 224 } 225 catch(error) 226 { 227 console.error(error); 228 } 229 } 230 231 /** Manage Clients on Disconnect */ 232 if(_socketbug._clients) 233 { 234 try 235 { 236 delete _socketbug._clients[socket.id]; 237 } 238 catch(error) 239 { 240 console.error(error); 241 } 242 } 243 }); 244 245 /** 246 * @function connection_manager 247 * 248 * Manage Group Connections 249 * 250 * @param {Object} group Group Data 251 * @param {Object} application Application Data 252 * @param {Object} client Client Data 253 * @param {Function} callback Callback Handler 254 */ 255 socket.on('connection_manager', function (group, application, client, callback) 256 { 257 var check_auth = false; 258 259 /* Add Client to Client List */ 260 _socketbug._clients[socket.id] = { 261 'client': { 262 'id': socket.id, 263 'name': '' 264 }, 265 'group': group, 266 'application': application, 267 'authenticated': { 268 'group': null, 269 'application': null, 270 } 271 }; 272 273 /** Check if this Group is Already Connected to Socketbug */ 274 if (_socketbug._groups[group.id]) 275 { 276 sb_manager.socket(socket.id).emit('manager_response', 'Group Already Connected and Approved for Socketbug Use', 'log'); 277 } 278 else if(_socketbug._config._authorized_only._groups) 279 { 280 sb_manager.socket(socket.id).emit('manager_response', 'Socketbug Server Group Authorization Required', 'warn'); 281 check_auth = true; 282 } 283 284 /** Check if this Application is Already Connected to Socketbug */ 285 if (_socketbug._applications[application.id]) 286 { 287 sb_manager.socket(socket.id).emit('manager_response', 'Application Already Connected and Approved for Socketbug Use'); 288 } 289 else if(_socketbug._config._authorized_only._applications) 290 { 291 sb_manager.socket(socket.id).emit('manager_response', 'Socketbug Server Application Authorization Required', 'warn'); 292 check_auth = true; 293 } 294 295 if(check_auth) 296 { 297 sb_manager.socket(socket.id).emit('manager_response', 'Checking Authorization...', 'info'); 298 299 var path = _socketbug._config._authorize._path + '?cb='; 300 path += (_socketbug._config._authorized_only._groups) ? '&group_id='+group.id:''; 301 path += (_socketbug._config._authorized_only._applications) ? '&application_id='+application.id:''; 302 303 /** Setup Options for Socketbug Authorization for Group ID's */ 304 var options = 305 { 306 host: _socketbug._config._authorize._host, 307 port: _socketbug._config._authorize._port, 308 path: path 309 }; 310 311 /** Establish Connection and Make request */ 312 http.get(options, function(response) 313 { 314 /** Create Variable to Store Returned JSON */ 315 var data = ''; 316 317 /** Build the Response in Pieces Should the JSON be Large */ 318 response.on('data', function(chunk) 319 { 320 data += chunk; 321 }); 322 323 /** Start Validation now that JSON is done Loading */ 324 response.on('end', function() 325 { 326 try 327 { 328 /** Build Expected Response */ 329 var _expected = (_socketbug._config._authorized_only._groups) ? crypto.createHash('md5').update(_socketbug._config._encryption_salt + 'Valid Group ID: ' + group.id).digest("hex") : ''; 330 _expected += (_socketbug._config._authorized_only._applications) ? crypto.createHash('md5').update(_socketbug._config._encryption_salt + 'Valid Application ID: ' + application.id).digest("hex") : ''; 331 332 /** Convert JSON String to Javascript Object */ 333 var json = JSON.parse(data); 334 335 /** Verify that the JSON Response Matched Exactly what was Expected */ 336 if(json.response === _expected) 337 { 338 authorize_group(socket, group); 339 authorize_application(socket, application); 340 } 341 /** JSON Resonse Did NOT Match */ 342 else 343 { 344 if(_socketbug._config._authorized_only._groups && json.group.valid) 345 { 346 authorize_group(socket, group); 347 } 348 else 349 { 350 /** Add this Group to the Unauthorized Group List */ 351 _socketbug._clients[socket.id].authenticated.group = false; 352 353 /** Communicate Unsuccesful Connection */ 354 sb_manager.socket(socket.id).emit('manager_response', 'Group NOT Approved for Socketbug Use', 'error'); 355 } 356 357 if(_socketbug._config._authorized_only._applications && json.application.valid) 358 { 359 authorize_application(socket, application); 360 } 361 else 362 { 363 /** Add this Group to the Unauthorized Group List */ 364 _socketbug._clients[socket.id].authenticated.application = false; 365 366 /* Communicate Unsuccesful Connection */ 367 sb_manager.socket(socket.id).emit('manager_response', 'Application NOT Approved for Socketbug Use', 'error'); 368 } 369 } 370 371 /** Send Authentication Failures */ 372 authorization(socket.id); 373 } 374 catch(message) 375 { 376 /** Communicate Unsuccesful Connection */ 377 //socket.emit('manager_response', message, 'error'); 378 } 379 }); 380 }); 381 } 382 else 383 { 384 authorize_group(socket, group); 385 authorize_application(socket, application); 386 } 387 388 /** Execute Callback Handler */ 389 if(typeof(callback) == 'function') 390 { 391 callback(socket.id); 392 } 393 394 }); 395 } 396 ); 397 398 /** Build a Socket to Isolate the Application Layer */ 399 var sb_application = io 400 .of('/sb_application') 401 .on('connection', function (socket) 402 { 403 /** 404 * @function execute_js 405 * 406 * Capture Console's Remote Javascript Exection Request 407 * 408 * @param {String} javascript Javascript to Execute 409 * @param {Function} callback Callback Handler 410 */ 411 socket.on('execute_js', function (javascript, callback) 412 { 413 try 414 { 415 /** Fetch Clients Application ID */ 416 var my_application = _socketbug._clients[socket.id].application.id; 417 418 /** Loop through Clients in Matching Application to Send Debug Message */ 419 for(var i=0; i<_socketbug._applications[my_application].clients.length; i++) 420 { 421 /** Fetch Client ID for Other Connected Users */ 422 var app_client = _socketbug._applications[my_application].clients[i]; 423 424 /** See if Client is Authorized */ 425 if(authorization(app_client)) 426 { 427 /** Send to Specific Client */ 428 sb_application.socket(app_client).emit('execute_js', javascript); 429 430 /** Execute Callback Handler */ 431 if(typeof(callback) == 'function') 432 { 433 callback(); 434 } 435 } 436 } 437 } 438 catch(error) 439 { 440 console.error(error); 441 } 442 }); 443 444 /** 445 * @function view_source 446 * 447 * Capture Applications Remote View Source Response 448 * 449 * @param {Function} callback Callback Handler 450 */ 451 socket.on('view_source', function (callback) 452 { 453 try 454 { 455 /** Fetch Clients Application ID */ 456 var my_application = _socketbug._clients[socket.id].application.id; 457 458 /** Loop through Clients in Matching Application to Send Debug Message */ 459 for(var i=0; i<_socketbug._applications[my_application].clients.length; i++) 460 { 461 /** Fetch Client ID for Other Connected Users */ 462 var app_client = _socketbug._applications[my_application].clients[i]; 463 464 /** See if Client is Authorized */ 465 if(authorization(app_client) && app_client != socket.id) 466 { 467 /** Send to Specific Client */ 468 sb_application.socket(app_client).emit('fetch_source'); 469 470 /** Execute Callback Handler */ 471 if(typeof(callback) == 'function') 472 { 473 callback(); 474 } 475 } 476 } 477 } 478 catch(error) 479 { 480 console.error(error); 481 } 482 }); 483 } 484 ); 485 486 /** Build a Socket to Isolate the Application Layer */ 487 var sb_console = io 488 .of('/sb_console') 489 .on('connection', function (socket) 490 { 491 /** 492 * @function debug 493 * 494 * Capture Client Message Event 495 * 496 * @param {String} level Debug Level 497 * @param {Object} data Data to Debug 498 * @param {Function} callback Callback Handler 499 */ 500 socket.on('debug', function (level, data, callback) 501 { 502 try 503 { 504 /** Fetch Clients Application ID */ 505 var my_application = _socketbug._clients[socket.id].application.id; 506 507 /** Loop through Clients in Matching Application to Send Debug Message */ 508 for(var i=0; i<_socketbug._applications[my_application].clients.length; i++) 509 { 510 /** Fetch Client ID for Other Connected Users */ 511 var app_client = _socketbug._applications[my_application].clients[i]; 512 513 /** See if Client is Authorized */ 514 if(authorization(app_client)) 515 { 516 /** Send to Specific Client */ 517 sb_console.socket(app_client).emit('application_debug', level, data[0]); 518 519 /** Execute Callback Handler */ 520 if(typeof(callback) == 'function') 521 { 522 callback(); 523 } 524 } 525 } 526 } 527 catch(error) 528 { 529 console.error('Error Authenticating Remote Debug Message'); 530 console.error(error); 531 } 532 }); 533 534 /** 535 * @function view_source 536 * 537 * Capture Console's Remote View Source Request 538 * 539 * @param {String} source_code 540 */ 541 socket.on('view_source', function (source_code) 542 { 543 try 544 { 545 /** Fetch Clients Application ID */ 546 var my_application = _socketbug._clients[socket.id].application.id; 547 548 /** Loop through Clients in Matching Application to Send Debug Message */ 549 for(var i=0; i<_socketbug._applications[my_application].clients.length; i++) 550 { 551 /** Fetch Client ID for Other Connected Users */ 552 var app_client = _socketbug._applications[my_application].clients[i]; 553 554 /** See if Client is Authorized */ 555 if(authorization(app_client) && app_client != socket.id) 556 { 557 /** Send to Specific Client */ 558 sb_console.socket(app_client).emit('view_source', source_code); 559 } 560 } 561 } 562 catch(error) 563 { 564 console.error(error); 565 } 566 }); 567 } 568 ); 569 570 /** 571 * Check Authorization of Session ID 572 * 573 * @param {String} session_id This is the Socket.IO Session ID for Socketbug 574 * 575 * @return Boolean 576 */ 577 function authorization(session_id) 578 { 579 /** Check if Either Group or Application Authentication is False. It is NULL by default and will only be FALSE if authentication failed. */ 580 if(_socketbug._clients[session_id].authenticated.application !== false && _socketbug._clients[session_id].authenticated.group !== false) 581 { 582 return true; 583 } 584 /** Authorization Failed */ 585 else 586 { 587 sb_manager.socket(session_id).emit('authentication_failed', _socketbug._clients[session_id].authenticated.application, _socketbug._clients[session_id].authenticated.group); 588 589 return false; 590 } 591 } 592 593 /** 594 * Check Authorization of Group ID 595 * 596 * @param {Object} socket This is the Socket used by Socketbug 597 * @param {String} group This is the Group ID for Socketbug 598 */ 599 function authorize_group(socket, group) 600 { 601 /** Set Flag to Indicate if Group is Connected on Socketbug */ 602 var group_found = false; 603 604 /** Loop through Connected Groups */ 605 for(var group_id in _socketbug._groups) 606 { 607 if(group_id == group.id) 608 { 609 group_found = true; 610 } 611 } 612 613 /** This is a New Group */ 614 if( !group_found) 615 { 616 _socketbug._groups[group.id] = { 617 618 /** Add Group Data */ 619 'data': { 620 'id': group.id, 621 'name': group.name 622 }, 623 624 /** Add Client to Group */ 625 'clients': [socket.id] 626 }; 627 } 628 /** Add New Client to Existing Group */ 629 else 630 { 631 _socketbug._groups[group.id].clients.push(socket.id); 632 } 633 634 /** Tell Socketbug that Client can Communicate with Group */ 635 _socketbug._clients[socket.id].authenticated.group = true; 636 637 sb_manager.socket(socket.id).emit('manager_response', 'Group Authorized for Socketbug Use', 'info'); 638 } 639 640 /** 641 * Check Authorization of Application ID 642 * 643 * @param {Object} socket This is the Socket used by Socketbug 644 * @param {String} application This is the Application ID for Socketbug 645 */ 646 function authorize_application(socket, application) 647 { 648 /** Set Flag to Indicate if Application is Connected on Socketbug */ 649 var application_found = false; 650 651 /** Loop through Connected Applications */ 652 for(var application_id in _socketbug._applications) 653 { 654 if(application_id == application.id) 655 { 656 application_found = true; 657 } 658 } 659 660 /** This is a New Application */ 661 if( !application_found) 662 { 663 _socketbug._applications[application.id] = { 664 665 /** Add Application Data */ 666 'data': { 667 'id': application.id, 668 'name': application.name 669 }, 670 671 /** Add Client to Application */ 672 'clients': [socket.id] 673 }; 674 } 675 /** Add New Client to Existing Application */ 676 else 677 { 678 _socketbug._applications[application.id].clients.push(socket.id); 679 } 680 681 /** Tell Socketbug that Client can Communicate with Application */ 682 _socketbug._clients[socket.id].authenticated.application = true; 683 684 sb_manager.socket(socket.id).emit('manager_response', 'Application Authorized for Socketbug Use', 'info'); 685 } 686 }(); 687 }