var http = require("http") ,https = require("https") ,clientSession = require("client-sessions") ,Url = require("./url.js").Url ,config = require("../config.js") ,sessionManager = require("./session.js").SessionManager ,Slack = require("./slack.js").Slack ,slackManager = require("./slackManager.js").SlackManager ,FaviconWriter = require("./faviconWriter.js").FaviconWriter; function Server(port) { var ctx = this; this.httpServ = http.createServer(function(req, res) { res.ended = false; res.on('end', () => { res.ended = true; }); ctx.onRequest(req, res); }); this.httpServ.listen(port, ctx.onListen); } Server.parseCookies = function(req) { var cookies = req.headers.cookie ,cookieObj = null; if (cookies) { cookieObj = {}; cookies = cookies.split(";"); for (var cookieIndex =0, nbCookies =cookies.length; cookieIndex < nbCookies; cookieIndex++) { var cookieParts = cookies[cookieIndex].indexOf("=") ,cookieKey = null ,cookieValue = null; if (cookieParts === -1) { cookieKey = cookies[cookieIndex].trim(); } else { cookieKey = cookies[cookieIndex].substr(0, cookieParts).trim(); cookieValue = cookies[cookieIndex].substr(cookieParts +1).trim(); } cookieObj[cookieKey] = cookieValue; } } return cookieObj; }; Server.stringifyCookies = function(cookies) { var cookieString = ""; for (var cookieKey in cookies) { cookieString += cookieString; if (cookies[cookieString] !== null) { cookieString += "="; } cookieString += "; "; } return cookieString; }; Server.prototype.onListen = function() { console.log("onListen"); }; function redirectToSlackAuth(res) { res.writeHeader("302", { Location: "https://slack.com/oauth/authorize" +"?client_id=" +config.clientId +"&scope=" +slackManager.getScope().join(",") +"&redirect_uri=" +config.rootUrl }); res.end(); } Server.prototype.onRequest = function(req, res) { req.reqT = Date.now(); req.cookies = Server.parseCookies(req); req.session = sessionManager.forRequest(req); req.urlObj = new Url(req.url); if (!req.session || !req.session.slackToken) { if (req.urlObj.queryTokens.code) { Slack.getOauthToken(req.urlObj.queryTokens.code, config.rootUrl, (token) => { if (token) { req.session = sessionManager.lazyForRequest(req); req.session.setSlackToken(req.reqT, token); res.writeHeader("302", { Location: config.rootUrl ,"Set-Cookie": "sessID="+req.session.sessId }); sessionManager.saveSession(req.session); res.end(); } else { redirectToSlackAuth(res); } }); } else { redirectToSlackAuth(res); } } else if (req.urlObj.isPublic()) { if (!config.isDebug) res.setHeader('Cache-Control', 'private, max-age=' +15 * 60); res.setHeader('Content-Length', req.urlObj.serve.stat.size); res.writeHeader("200"); req.urlObj.getReadStream().pipe(res, { end: true }); sessionManager.saveSession(req.session); return; // async pipe will close when finished } else if (req.urlObj.match(["favicon.png"])) { if (!config.isDebug) res.setHeader('Cache-Control', 'private, max-age=' +15 * 60); var unreadHi = (req.urlObj.queryTokens.h && req.urlObj.queryTokens.h[0]) ? parseInt(req.urlObj.queryTokens.h[0], 10) : 0 ,unread = (req.urlObj.queryTokens.m && req.urlObj.queryTokens.m[0]) ? parseInt(req.urlObj.queryTokens.m[0], 10) : 0; FaviconWriter.write(unreadHi, unread, (buf) => { res.writeHeader("200"); res.end(buf); }); sessionManager.saveSession(req.session); return; } else { // Api / dynamic content var apiSuccess = false; res.chatContext = slackManager.lazyGet(req.session, req.reqT); if (req.urlObj.match(["api", "hist"])) { if (!req.urlObj.queryTokens.room) { res.writeHeader("400", "Bad request"); } else { var allFound = true; req.urlObj.queryTokens.room.forEach(function(targetId) { if (!res.chatContext.data.channels[targetId]) { allFound = false; } res.chatContext.fetchHistory(res.chatContext.data.channels[targetId]); }); if (allFound) res.writeHeader("204", "No Content"); else res.writeHeader("404", "Channel not found"); } sessionManager.saveSession(req.session); res.end(); } else if (req.urlObj.match(["api", "typing"])) { if (!req.urlObj.queryTokens.room) { res.writeHeader("400", "Bad request"); res.end(); } else { var chan = res.chatContext.data.channels[req.urlObj.queryTokens.room[0]]; if (!chan) { res.writeHeader("404", "Chan not found"); } else { res.chatContext.sendTyping(chan); res.writeHeader("204", "No Content"); } res.end(); } } else if (req.urlObj.match(["api", "cmd"])) { if (!req.urlObj.queryTokens.room || !req.urlObj.queryTokens.cmd) { res.writeHeader("400", "Bad request"); res.end(); } else { var chan = res.chatContext.data.channels[req.urlObj.queryTokens.room[0]] ,cmd = res.chatContext.data.commands.data['/' +req.urlObj.queryTokens.cmd[0]]; if (!chan) { res.writeHeader("404", "Chan not found"); } else if (!cmd) { res.writeHeader("404", "No such command"); } else { var args = req.urlObj.queryTokens.args ? req.urlObj.queryTokens.args[0] : ""; if (args === true) args = ""; res.chatContext.sendCommand(chan, cmd, args); res.writeHeader("204", "No Content"); } res.end(); } } else if (req.urlObj.match(["api", "markread"])) { if (!req.urlObj.queryTokens.room || !req.urlObj.queryTokens.ts) { res.writeHeader("400", "Bad request"); res.end(); } else { var chan = res.chatContext.data.channels[req.urlObj.queryTokens.room[0]] ,ts = parseFloat(req.urlObj.queryTokens.ts[0]); if (!chan) res.writeHeader("404", "Chan Not Found"); else if (isNaN(ts)) res.writeHeader("400", "Invalid date"); else res.chatContext.markRead(chan, ts); res.end(); } sessionManager.saveSession(req.session); } else if (req.urlObj.match(["api", "avatar"])) { if (!req.urlObj.queryTokens.user) { res.writeHeader("400", "Bad request"); res.end(); } else { var user = res.chatContext.data.users[req.urlObj.queryTokens.user[0]]; if (!user) { res.writeHeader("404", "User Not Found"); res.end(); } else { var url = user.icons.small; if (url.substr(0, 7) === "http://") http.get(url, (d) => { d.pipe(res, { end: true }); }); else if (url.substr(0, 8) === "https://") https.get(url, (d) => { d.pipe(res, { end: true }); }); } } sessionManager.saveSession(req.session); } else if (req.urlObj.match(["api", "msg"])) { if (req.method === 'POST') { if (!req.urlObj.queryTokens.room || !req.urlObj.queryTokens.text) { res.writeHeader("400", "Bad request"); } else { var chan = res.chatContext.data.channels[req.urlObj.queryTokens.room[0]]; if (chan) { var attachments = null; if (req.urlObj.queryTokens.attachments) { try { attachments = JSON.parse(decodeURIComponent(req.urlObj.queryTokens.attachments[0])); } catch (e) {} } if (req.urlObj.queryTokens.me) res.chatContext.sendMeMsg(chan, req.urlObj.queryTokens.text); else res.chatContext.sendMsg(chan, req.urlObj.queryTokens.text, attachments); res.writeHeader("204", "No Content"); } else { res.writeHeader("404", "Channel not found"); } } } else if (req.method === "DELETE") { if (!req.urlObj.queryTokens.room || !req.urlObj.queryTokens.ts) { res.writeHeader("400", "Bad request"); } else { var chan = res.chatContext.data.channels[req.urlObj.queryTokens.room[0]]; if (chan) { res.chatContext.removeMsg(chan, req.urlObj.queryTokens.ts[0]); res.writeHeader("204", "No Content"); } else { res.writeHeader("404", "Channel not found"); } } } else if (req.method === "PUT") { if (!req.urlObj.queryTokens.room || !req.urlObj.queryTokens.ts || !req.urlObj.queryTokens.text) { res.writeHeader("400", "Bad request"); } else { var chan = res.chatContext.data.channels[req.urlObj.queryTokens.room[0]]; if (chan) { res.chatContext.editMsg(chan, req.urlObj.queryTokens.ts[0], req.urlObj.queryTokens.text); res.writeHeader("204", "No Content"); } else { res.writeHeader("404", "Channel not found"); } } } else { res.writeHeader("400", "Bad request"); } sessionManager.saveSession(req.session); res.end(); } else if (req.urlObj.match(["api", "reaction"])) { var chanId = req.urlObj.queryTokens["room"] ? req.urlObj.queryTokens["room"][0] : undefined ,msgId = req.urlObj.queryTokens["msg"] ? req.urlObj.queryTokens["msg"][0] : undefined ,reaction = req.urlObj.queryTokens["reaction"] ? req.urlObj.queryTokens["reaction"][0] : undefined; if (chanId && msgId && reaction) { var chan = res.chatContext.data.channels[chanId]; if (!chan) { res.writeHeader("404", "Channel Not Found"); } else if (req.method === 'POST') { res.writeHeader("204", "No Content"); res.chatContext.addReaction(chan, msgId, reaction); } else if (req.method === 'DELETE') { res.writeHeader("204", "No Content"); res.chatContext.removeReaction(chan, msgId, reaction); } else { res.writeHeader("405", "Method not allowed"); } } else { res.writeHeader("400", "Missing Parameter"); } sessionManager.saveSession(req.session); res.end(); } else if (req.urlObj.match(["api", "file"])) { sessionManager.saveSession(req.session); if (req.urlObj.queryTokens["room"]) { var chan = res.chatContext.data.channels[req.urlObj.queryTokens["room"][0]]; if (chan) { var uploadRequest = res.chatContext.openUploadFileStream(chan, req.headers["content-type"], (errorMsg) => { if (!errorMsg) res.writeHeader("204", "No Content"); else res.writeHeader("500", errorMsg); res.end(); }); req.on('end', () => { uploadRequest.end(); }); req.pipe(uploadRequest); } else { res.writeHeader("404", "Channel Not Found"); res.end(); } } else { res.writeHeader("400", "Bad Request"); res.end(); } } else if (req.urlObj.match(["api", "attachmentAction"])) { sessionManager.saveSession(req.session); res.chatContext.sendAction(req.headers["content-type"], req, (result) => { res.end(result); }); } else if (req.urlObj.match(["api"])) { res.chatContext.onRequest( (req.urlObj.queryTokens.v ? req.urlObj.queryTokens.v[0] : 0) || 0 , (slack, newData) => { if (!res.ended) { try { if (!slack.connected) { res.writeHeader("403", { "Content-Type": "application/json" }); res.end(slack.error); } else { res.writeHeader("200", { "Content-Type": "application/json" }); res.end(JSON.stringify(newData)); } } catch (e) {} } sessionManager.saveSession(req.session); }); } else { console.log(JSON.stringify(req.session)); console.log(JSON.stringify(req.urlObj)); console.log(JSON.stringify(req.cookies)); res.writeHeader("404", "Not Found"); res.end(); } } }; module.exports.HttpServ = Server;