diff --git a/config/users.json b/config/users.json new file mode 100644 index 0000000..12c286c --- /dev/null +++ b/config/users.json @@ -0,0 +1,3 @@ +{ + "dominic": "test" +} diff --git a/js/config.js b/js/config.js index e591ad6..7e1f138 100644 --- a/js/config.js +++ b/js/config.js @@ -33,6 +33,14 @@ function loadConfigFile(relPath) { } } +/** + * Answer true if the config file is ready, else false + */ +exports.isReady = function() { + if(config) return true; + else return false; +}; + /** * Fetch a property from the configuration * @param {String} prop diff --git a/js/db_interface.js b/js/db_interface.js index 2d7b6b1..0cd199b 100644 --- a/js/db_interface.js +++ b/js/db_interface.js @@ -39,6 +39,8 @@ exports = module.exports = function(args) { /** * ### encrypt + * this is used to decrypt + * @param {String} plainText */ function encrypt(plainText) { if(!plainText) return null; @@ -56,13 +58,13 @@ function encrypt(plainText) { /** * ### decrypt */ -function decrypt(crypticText) { +function decrypt(crypticText, id) { if(!crypticText) return null; try { var deciph = crypto.createDecipher('aes-256-cbc', crypto_key); return deciph.update(crypticText, 'base64', 'utf8') + deciph.final('utf8'); } catch (err) { - log.error('DB', 'in decrypting: ' + err); + log.error('DB', 'in decrypting "' + id + '": ' + err); return null; } } @@ -179,7 +181,9 @@ exports.storeActionModuleAuth = function(id, data) { * @param {function} callback the callback to receive the answer (err, obj) */ exports.getActionModuleAuth = function(id, callback) { - if(callback && db) db.get('action_module_' + id + '_auth', function(err, txt) { callback(err, decrypt(txt)); }); + if(callback && db) db.get('action_module_' + id + '_auth', function(id) { + return function(err, txt) { callback(err, decrypt(txt, 'action_module_' + id + '_auth')); }; + }(id)); }; // ## Event Modules @@ -235,7 +239,9 @@ exports.storeEventModuleAuth = function(id, data) { // @param {String} id the module id // @param {function} callback the callback to receive the answer (err, obj) exports.getEventModuleAuth = function(id, callback) { - if(callback) db.get('event_module_' + id +'_auth', function(err, txt) { callback(err, decrypt(txt)); }); + if(callback) db.get('event_module_' + id +'_auth', function(id) { + return function(err, txt) { callback(err, decrypt(txt, 'event_module_' + id + '_auth')); }; + }(id)); }; // ## Rules diff --git a/js/engine.js b/js/engine.js index 2d6c531..20036a3 100644 --- a/js/engine.js +++ b/js/engine.js @@ -132,7 +132,7 @@ function checkEvent(evt) { for(var rn in listRules) { //TODO this needs to get depth safe, not only data but eventually also // on one level above (eventid and other meta) - if(listRules[rn].event === evt.event && validConditions(evt.data, listRules[rn])) { + if(listRules[rn].event === evt.event && validConditions(evt.payload, listRules[rn])) { log.print('EN', 'Rule "' + rn + '" fired'); actions = actions.concat(listRules[rn].actions); } @@ -167,7 +167,7 @@ function invokeAction(evt, action) { var srvc = listActionModules[arrModule[0]]; if(srvc && srvc[arrModule[1]]) { //FIXME preprocessing not only on data - preprocessActionArguments(evt.data, action.arguments, actionargs); + preprocessActionArguments(evt.payload, action.arguments, actionargs); try { if(srvc[arrModule[1]]) srvc[arrModule[1]](actionargs); } catch(err) { diff --git a/js/eventpoller.js b/js/eventpoller.js index 56fe3c4..c9a5e88 100644 --- a/js/eventpoller.js +++ b/js/eventpoller.js @@ -126,7 +126,7 @@ function checkRemotes() { process.send({ event: p, eventid: 'polled_' + eId++, - data: obj + payload: obj }); } }; diff --git a/js/http_listener.js b/js/http_listener.js index 3df7a65..bb93e43 100644 --- a/js/http_listener.js +++ b/js/http_listener.js @@ -1,5 +1,8 @@ -// # HTTP Listener -// Isso +// HTTP Listener +// ============= +// +// Handles the HTTP requests to the server at the port specified by the [config](config.html) file. + 'use strict'; var path = require('path'), @@ -10,12 +13,18 @@ var path = require('path'), log = require('./logging'), sess_sec = '#C[>;j`@".TXm2TA;A2Tg)', db_port, http_port, server, - adminHandler, eventHandler; + eventHandler, userHandler; +/* + * The module needs to be called as a function to initialize it. + * After that it fetches the http\_port, db\_port & sess\_sec properties + * from the configuration file. + */ exports = module.exports = function(args) { args = args || {}; log(args); var config = require('./config')(args); + userHandler = require('./user_handler')(args); db_port = config.getDBPort(), sess_sec = config.getSessionSecret(), http_port = config.getHttpPort(); @@ -23,110 +32,78 @@ exports = module.exports = function(args) { }; exports.addHandlers = function(funcAdminHandler, funcEvtHandler) { - if(!funcEvtHandler) { - log.error('HL', 'ERROR: either port or eventHandler function not defined!'); + if(!funcAdminHandler || !funcEvtHandler) { + log.error('HL', 'ERROR: either adminHandler or eventHandler function not defined!'); return; } - adminHandler = funcAdminHandler; + userHandler.addHandler(funcAdminHandler); eventHandler = funcEvtHandler; - -//FIXME this whole webserver requires clean approach together with session handling all over the engine. -//One entry point, from then collecting response contents and one exit point that sends it! - + // Add cookie support for session handling. app.use(express.cookieParser()); - app.use('/doc/', express.static(path.resolve(__dirname, '..', 'webpages', 'doc'))); - app.use('/mobile/', express.static(path.resolve(__dirname, '..', 'webpages', 'mobile'))); - app.use('/rulesforge/', express.static(path.resolve(__dirname, '..', 'webpages', 'rulesforge'))); - app.get('/admin', onAdminCommand); - app.post('/pushEvents', onPushEvent); - if(db_port) { - app.use(express.session({ - store: new RedisStore({ - host: 'localhost', - port: db_port, - db: 2 - // , - // pass: 'RedisPASS' - }), - // FIXME use a secret from config - secret: sess_sec - })); - log.print('HL', 'Added redis DB as session backbone'); - } else { - app.use(express.session({secret: sess_sec})); - log.print('HL', 'no session backbone'); - } - if(http_port) server = app.listen(http_port); // inbound event channel - else log.error('HL', new Error('No HTTP port found!?')); -}; - -function answerHandler(r) { - var response = r, hasBeenAnswered = false; - function postAnswer(msg) { - if(!hasBeenAnswered) { - response.write(msg); - response.end(); - hasBeenAnswered = true; - } - } - return { - answerSuccess: function(msg) { - if(!hasBeenAnswered) response.writeHead(200, { "Content-Type": "text/plain" }); - postAnswer(msg); - }, - answerError: function(msg) { - if(!hasBeenAnswered) response.writeHead(400, { "Content-Type": "text/plain" }); - postAnswer(msg); - }, - isAnswered: function() { return hasBeenAnswered; } - }; -}; - -/** - * Handles correct event posts, replies thank you. - */ -function answerSuccess(resp, msg){ - resp.writeHead(200, { "Content-Type": "text/plain" }); - resp.write(msg); - resp.end(); -} - -/** - * Handles erroneous requests. - * @param {Object} msg the error message to be returned - */ -function answerError(resp, msg) { - resp.writeHead(400, { "Content-Type": "text/plain" }); - resp.write(msg); - resp.end(); -} - -//FIXME this answer handling is a very ugly hack, improve! -function onAdminCommand(request, response) { - var q = request.query; - log.print('HL', 'Received admin request: ' + request.originalUrl); - if(q.cmd) { - adminHandler(q, answerHandler(response)); - // answerSuccess(response, 'Thank you, we try our best!'); - } else answerError(response, 'I\'m not sure about what you want from me...'); -} + app.use(express.session({secret: sess_sec})); + log.print('HL', 'no session backbone'); + // ^ TODO figure out why redis backbone doesn't work. eventually the db pass has to be set in the DB? + // } session information seems to be stored in DB but not retrieved correctly + // } if(db_port) { + // } app.use(express.session({ + // } store: new RedisStore({ + // } host: 'localhost', + // } port: db_port, + // } db: 2 + // } , + // } pass: null + // } }), + // } secret: sess_sec + // } })); + // } log.print('HL', 'Added redis DB as session backbone'); + // } } else { + // } app.use(express.session({secret: sess_sec})); + // } log.print('HL', 'no session backbone'); + // } } + + // Redirect the requests to the appropriate handler. + app.use('/doc/', express.static(path.resolve(__dirname, '..', 'webpages', 'doc'))); + // app.get('/mobile', userHandler.handleRequest); + app.get('/rulesforge', userHandler.handleRequest); + app.use('/mobile/', express.static(path.resolve(__dirname, '..', 'webpages', 'mobile'))); + // } app.use('/rulesforge/', express.static(path.resolve(__dirname, '..', 'webpages', 'rulesforge'))); + app.get('/admin', userHandler.handleRequest); + app.post('/push_event', onPushEvent); + if(http_port) server = app.listen(http_port); // inbound event channel + else log.error('HL', new Error('No HTTP port found!? Nothing to listen on!...')); +}; + /** - * If a request is made to the server, this function is used to handle it. + * If a post request reaches the server, this function handles it and treats the request as a possible event. */ -function onPushEvent(request, response) { +function onPushEvent(req, resp) { var body = ''; - request.on('data', function (data) { body += data; }); - request.on('end', function () { + req.on('data', function (data) { body += data; }); + req.on('end', function () { var obj = qs.parse(body); /* If required event properties are present we process the event */ if(obj && obj.event && obj.eventid){ - answerSuccess(response, 'Thank you for the event (' + obj.event + '[' + obj.eventid + '])!'); + resp.writeHead(200, { "Content-Type": "text/plain" }); + resp.write('Thank you for the event (' + obj.event + '[' + obj.eventid + '])!'); + resp.end(); eventHandler(obj); - } else answerError(response, 'Your event was missing important parameters!'); + } else { + resp.writeHead(400, { "Content-Type": "text/plain" }); + resp.write('Your event was missing important parameters!'); + resp.end(); + } + resp.end(); }); } +exports.loadUsers = function() { + var users = JSON.parse(require('fs').readFileSync(path.resolve(__dirname, '..', relPath))); + for(var name in users) { + + } +}; + exports.shutDown = function() { log.print('HL', 'Shutting down HTTP listener'); process.exit(); // This is a bit brute force... diff --git a/js/server.js b/js/server.js index 5459830..9eb698a 100644 --- a/js/server.js +++ b/js/server.js @@ -24,8 +24,7 @@ dog's back. */ //FIXME server should be started via command line arguments http_port and logging level to allow proper testing -var fs = require('fs'), - path = require('path'), +var path = require('path'), log = require('./logging'), procCmds = { 'die': function() { shutDown(); } @@ -36,7 +35,10 @@ var fs = require('fs'), function init() { log.print('RS', 'STARTING SERVER'); - + if(!require('./config').isReady()) { + log.error('RS', 'Config file not ready!'); + return; + } if(process.argv.length > 2) { args.logType = parseInt(process.argv[2]) || 0 ; log(args); @@ -56,12 +58,13 @@ function init() { 'loadactions': mm.loadActionModulesFromFS, 'loadevent': mm.loadEventModuleFromFS, 'loadevents': mm.loadEventModulesFromFS, + 'loadusers': http_listener.loadUsers, 'shutdown': shutDown }; log.print('RS', 'Initialzing engine'); engine.addDBLinkAndLoadActionsAndRules(db); log.print('RS', 'Initialzing http listener'); - http_listener.addHandlers(handleAdminCommands, engine.pushEvent); + http_listener.addHandlers(db, handleAdminCommands, engine.pushEvent); log.print('RS', 'Initialzing module manager'); mm.addHandlers(db, engine.loadActionModule, engine.addRule); //FIXME load actions and events, then rules, do this here, visible for everybody on the first glance @@ -82,6 +85,7 @@ function handleAdminCommands(args, answHandler) { }, 2000); } + function shutDown(args, answHandler) { if(answHandler) answHandler.answerSuccess('Goodbye!'); log.print('RS', 'Received shut down command!'); diff --git a/js/user_handler.js b/js/user_handler.js new file mode 100644 index 0000000..183fe3d --- /dev/null +++ b/js/user_handler.js @@ -0,0 +1,61 @@ + +var log = require('./logging'), + db = require('./db_interface'), adminHandler; + +exports = module.exports = function(args) { + args = args || {}; + log(args); + db(args); + return module.exports; +}; + +exports.addHandler = function(adminHandl) { + adminHandler = adminHandl; +}; + +exports.handleRequest = function(req, resp) { + req.on('end', function () { + resp.end(); + }); + if(req.session && req.session.lastPage) resp.send('You visited last: ' + req.session.lastPage); + else resp.send('You are new!'); + // resp.end(); + log.print('UH', 'last: '+ req.session.lastPage); + req.session.lastPage = req.originalUrl; + log.print('UH', 'last: '+ req.session.lastPage); + log.print('UH', 'retrieved req: '+ req.originalUrl); + // console.log(req); +}; + +function answerHandler(r) { + var response = r, hasBeenAnswered = false; + function postAnswer(msg) { + if(!hasBeenAnswered) { + response.write(msg); + response.end(); + hasBeenAnswered = true; + } + } + return { + answerSuccess: function(msg) { + if(!hasBeenAnswered) response.writeHead(200, { "Content-Type": "text/plain" }); + postAnswer(msg); + }, + answerError: function(msg) { + if(!hasBeenAnswered) response.writeHead(400, { "Content-Type": "text/plain" }); + postAnswer(msg); + }, + isAnswered: function() { return hasBeenAnswered; } + }; +}; + +//FIXME this answer handling is a very ugly hack, improve! +function onAdminCommand(request, response) { + var q = request.query; + log.print('HL', 'Received admin request: ' + request.originalUrl); + if(q.cmd) { + adminHandler(q, answerHandler(response)); + // answerSuccess(response, 'Thank you, we try our best!'); + } else answerError(response, 'I\'m not sure about what you want from me...'); +} + diff --git a/webpages/mobile/index.html b/webpages/mobile/index.html index a63b156..df649b8 100644 --- a/webpages/mobile/index.html +++ b/webpages/mobile/index.html @@ -1,8 +1,53 @@