httpServ.js 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351
  1. var http = require("http")
  2. ,https = require("https")
  3. ,clientSession = require("client-sessions")
  4. ,Url = require("./url.js").Url
  5. ,config = require("../config.js")
  6. ,sessionManager = require("./session.js").SessionManager
  7. ,Slack = require("./slack.js").Slack
  8. ,slackManager = require("./slackManager.js").SlackManager
  9. ,FaviconWriter = require("./faviconWriter.js").FaviconWriter;
  10. function Server(port) {
  11. var ctx = this;
  12. this.httpServ = http.createServer(function(req, res) {
  13. res.ended = false;
  14. res.on('end', () => {
  15. res.ended = true;
  16. });
  17. ctx.onRequest(req, res);
  18. });
  19. this.httpServ.listen(port, ctx.onListen);
  20. }
  21. Server.parseCookies = function(req) {
  22. var cookies = req.headers.cookie
  23. ,cookieObj = null;
  24. if (cookies) {
  25. cookieObj = {};
  26. cookies = cookies.split(";");
  27. for (var cookieIndex =0, nbCookies =cookies.length; cookieIndex < nbCookies; cookieIndex++) {
  28. var cookieParts = cookies[cookieIndex].indexOf("=")
  29. ,cookieKey = null
  30. ,cookieValue = null;
  31. if (cookieParts === -1) {
  32. cookieKey = cookies[cookieIndex].trim();
  33. } else {
  34. cookieKey = cookies[cookieIndex].substr(0, cookieParts).trim();
  35. cookieValue = cookies[cookieIndex].substr(cookieParts +1).trim();
  36. }
  37. cookieObj[cookieKey] = cookieValue;
  38. }
  39. }
  40. return cookieObj;
  41. };
  42. Server.stringifyCookies = function(cookies) {
  43. var cookieString = "";
  44. for (var cookieKey in cookies) {
  45. cookieString += cookieString;
  46. if (cookies[cookieString] !== null) {
  47. cookieString += "=";
  48. }
  49. cookieString += "; ";
  50. }
  51. return cookieString;
  52. };
  53. Server.prototype.onListen = function() {
  54. console.log("onListen");
  55. };
  56. function redirectToSlackAuth(res) {
  57. res.writeHeader("302", {
  58. Location: "https://slack.com/oauth/authorize"
  59. +"?client_id=" +config.clientId
  60. +"&scope=" +slackManager.getScope().join(",")
  61. +"&redirect_uri=" +config.rootUrl
  62. });
  63. res.end();
  64. }
  65. Server.prototype.onRequest = function(req, res) {
  66. req.reqT = Date.now();
  67. req.cookies = Server.parseCookies(req);
  68. req.session = sessionManager.forRequest(req);
  69. req.urlObj = new Url(req.url);
  70. if (!req.session || !req.session.slackToken) {
  71. if (req.urlObj.queryTokens.code) {
  72. Slack.getOauthToken(req.urlObj.queryTokens.code, config.rootUrl, (token) => {
  73. if (token) {
  74. req.session = sessionManager.lazyForRequest(req);
  75. req.session.setSlackToken(req.reqT, token);
  76. res.writeHeader("302", {
  77. Location: config.rootUrl
  78. ,"Set-Cookie": "sessID="+req.session.sessId
  79. });
  80. sessionManager.saveSession(req.session);
  81. res.end();
  82. } else {
  83. redirectToSlackAuth(res);
  84. }
  85. });
  86. } else {
  87. redirectToSlackAuth(res);
  88. }
  89. } else if (req.urlObj.isPublic()) {
  90. if (!config.isDebug)
  91. res.setHeader('Cache-Control', 'private, max-age=' +15 * 60);
  92. res.setHeader('Content-Length', req.urlObj.serve.stat.size);
  93. res.writeHeader("200");
  94. req.urlObj.getReadStream().pipe(res, { end: true });
  95. sessionManager.saveSession(req.session);
  96. return; // async pipe will close when finished
  97. } else if (req.urlObj.match(["favicon.png"])) {
  98. if (!config.isDebug)
  99. res.setHeader('Cache-Control', 'private, max-age=' +15 * 60);
  100. var unreadHi = (req.urlObj.queryTokens.h && req.urlObj.queryTokens.h[0]) ? parseInt(req.urlObj.queryTokens.h[0], 10) : 0
  101. ,unread = (req.urlObj.queryTokens.m && req.urlObj.queryTokens.m[0]) ? parseInt(req.urlObj.queryTokens.m[0], 10) : 0;
  102. FaviconWriter.write(unreadHi, unread, (buf) => {
  103. res.writeHeader("200");
  104. res.end(buf);
  105. });
  106. sessionManager.saveSession(req.session);
  107. return;
  108. } else {
  109. // Api / dynamic content
  110. var apiSuccess = false;
  111. res.chatContext = slackManager.lazyGet(req.session, req.reqT);
  112. if (req.urlObj.match(["api", "hist"])) {
  113. if (!req.urlObj.queryTokens.room) {
  114. res.writeHeader("400", "Bad request");
  115. } else {
  116. var allFound = true;
  117. req.urlObj.queryTokens.room.forEach(function(targetId) {
  118. if (!res.chatContext.data.channels[targetId]) {
  119. allFound = false;
  120. }
  121. res.chatContext.fetchHistory(res.chatContext.data.channels[targetId]);
  122. });
  123. if (allFound)
  124. res.writeHeader("204", "No Content");
  125. else
  126. res.writeHeader("404", "Channel not found");
  127. }
  128. sessionManager.saveSession(req.session);
  129. res.end();
  130. } else if (req.urlObj.match(["api", "typing"])) {
  131. if (!req.urlObj.queryTokens.room) {
  132. res.writeHeader("400", "Bad request");
  133. res.end();
  134. } else {
  135. var chan = res.chatContext.data.channels[req.urlObj.queryTokens.room[0]];
  136. if (!chan) {
  137. res.writeHeader("404", "Chan not found");
  138. } else {
  139. res.chatContext.sendTyping(chan);
  140. res.writeHeader("204", "No Content");
  141. }
  142. res.end();
  143. }
  144. } else if (req.urlObj.match(["api", "cmd"])) {
  145. if (!req.urlObj.queryTokens.room ||
  146. !req.urlObj.queryTokens.cmd) {
  147. res.writeHeader("400", "Bad request");
  148. res.end();
  149. } else {
  150. var chan = res.chatContext.data.channels[req.urlObj.queryTokens.room[0]]
  151. ,cmd = res.chatContext.data.commands.data['/' +req.urlObj.queryTokens.cmd[0]];
  152. if (!chan) {
  153. res.writeHeader("404", "Chan not found");
  154. } else if (!cmd) {
  155. res.writeHeader("404", "No such command");
  156. } else {
  157. var args = req.urlObj.queryTokens.args ? req.urlObj.queryTokens.args[0] : "";
  158. if (args === true)
  159. args = "";
  160. res.chatContext.sendCommand(chan, cmd, args);
  161. res.writeHeader("204", "No Content");
  162. }
  163. res.end();
  164. }
  165. } else if (req.urlObj.match(["api", "markread"])) {
  166. if (!req.urlObj.queryTokens.room || !req.urlObj.queryTokens.ts) {
  167. res.writeHeader("400", "Bad request");
  168. res.end();
  169. } else {
  170. var chan = res.chatContext.data.channels[req.urlObj.queryTokens.room[0]]
  171. ,ts = parseFloat(req.urlObj.queryTokens.ts[0]);
  172. if (!chan)
  173. res.writeHeader("404", "Chan Not Found");
  174. else if (isNaN(ts))
  175. res.writeHeader("400", "Invalid date");
  176. else
  177. res.chatContext.markRead(chan, ts);
  178. res.end();
  179. }
  180. sessionManager.saveSession(req.session);
  181. } else if (req.urlObj.match(["api", "avatar"])) {
  182. if (!req.urlObj.queryTokens.user) {
  183. res.writeHeader("400", "Bad request");
  184. res.end();
  185. } else {
  186. var user = res.chatContext.data.users[req.urlObj.queryTokens.user[0]];
  187. if (!user) {
  188. res.writeHeader("404", "User Not Found");
  189. res.end();
  190. } else {
  191. var url = user.icons.small;
  192. if (url.substr(0, 7) === "http://")
  193. http.get(url, (d) => {
  194. d.pipe(res, { end: true });
  195. });
  196. else if (url.substr(0, 8) === "https://")
  197. https.get(url, (d) => {
  198. d.pipe(res, { end: true });
  199. });
  200. }
  201. }
  202. sessionManager.saveSession(req.session);
  203. } else if (req.urlObj.match(["api", "msg"])) {
  204. if (req.method === 'POST') {
  205. if (!req.urlObj.queryTokens.room || !req.urlObj.queryTokens.text) {
  206. res.writeHeader("400", "Bad request");
  207. } else {
  208. var chan = res.chatContext.data.channels[req.urlObj.queryTokens.room[0]];
  209. if (chan) {
  210. var attachments = null;
  211. if (req.urlObj.queryTokens.attachments) {
  212. try { attachments = JSON.parse(decodeURIComponent(req.urlObj.queryTokens.attachments[0])); }
  213. catch (e) {}
  214. }
  215. if (req.urlObj.queryTokens.me)
  216. res.chatContext.sendMeMsg(chan, req.urlObj.queryTokens.text);
  217. else
  218. res.chatContext.sendMsg(chan, req.urlObj.queryTokens.text, attachments);
  219. res.writeHeader("204", "No Content");
  220. } else {
  221. res.writeHeader("404", "Channel not found");
  222. }
  223. }
  224. } else if (req.method === "DELETE") {
  225. if (!req.urlObj.queryTokens.room || !req.urlObj.queryTokens.ts) {
  226. res.writeHeader("400", "Bad request");
  227. } else {
  228. var chan = res.chatContext.data.channels[req.urlObj.queryTokens.room[0]];
  229. if (chan) {
  230. res.chatContext.removeMsg(chan, req.urlObj.queryTokens.ts[0]);
  231. res.writeHeader("204", "No Content");
  232. } else {
  233. res.writeHeader("404", "Channel not found");
  234. }
  235. }
  236. } else if (req.method === "PUT") {
  237. if (!req.urlObj.queryTokens.room || !req.urlObj.queryTokens.ts || !req.urlObj.queryTokens.text) {
  238. res.writeHeader("400", "Bad request");
  239. } else {
  240. var chan = res.chatContext.data.channels[req.urlObj.queryTokens.room[0]];
  241. if (chan) {
  242. res.chatContext.editMsg(chan, req.urlObj.queryTokens.ts[0], req.urlObj.queryTokens.text);
  243. res.writeHeader("204", "No Content");
  244. } else {
  245. res.writeHeader("404", "Channel not found");
  246. }
  247. }
  248. } else {
  249. res.writeHeader("400", "Bad request");
  250. }
  251. sessionManager.saveSession(req.session);
  252. res.end();
  253. } else if (req.urlObj.match(["api", "reaction"])) {
  254. var chanId = req.urlObj.queryTokens["room"] ? req.urlObj.queryTokens["room"][0] : undefined
  255. ,msgId = req.urlObj.queryTokens["msg"] ? req.urlObj.queryTokens["msg"][0] : undefined
  256. ,reaction = req.urlObj.queryTokens["reaction"] ? req.urlObj.queryTokens["reaction"][0] : undefined;
  257. if (chanId && msgId && reaction) {
  258. var chan = res.chatContext.data.channels[chanId];
  259. if (!chan) {
  260. res.writeHeader("404", "Channel Not Found");
  261. } else if (req.method === 'POST') {
  262. res.writeHeader("204", "No Content");
  263. res.chatContext.addReaction(chan, msgId, reaction);
  264. } else if (req.method === 'DELETE') {
  265. res.writeHeader("204", "No Content");
  266. res.chatContext.removeReaction(chan, msgId, reaction);
  267. } else {
  268. res.writeHeader("405", "Method not allowed");
  269. }
  270. } else {
  271. res.writeHeader("400", "Missing Parameter");
  272. }
  273. sessionManager.saveSession(req.session);
  274. res.end();
  275. } else if (req.urlObj.match(["api", "file"])) {
  276. sessionManager.saveSession(req.session);
  277. if (req.urlObj.queryTokens["room"]) {
  278. var chan = res.chatContext.data.channels[req.urlObj.queryTokens["room"][0]];
  279. if (chan) {
  280. var uploadRequest = res.chatContext.openUploadFileStream(chan, req.headers["content-type"], (errorMsg) => {
  281. if (!errorMsg)
  282. res.writeHeader("204", "No Content");
  283. else
  284. res.writeHeader("500", errorMsg);
  285. res.end();
  286. });
  287. req.on('end', () => {
  288. uploadRequest.end();
  289. });
  290. req.pipe(uploadRequest);
  291. } else {
  292. res.writeHeader("404", "Channel Not Found");
  293. res.end();
  294. }
  295. } else {
  296. res.writeHeader("400", "Bad Request");
  297. res.end();
  298. }
  299. } else if (req.urlObj.match(["api", "attachmentAction"])) {
  300. sessionManager.saveSession(req.session);
  301. res.chatContext.sendAction(req.headers["content-type"], req, (result) => {
  302. res.end(result);
  303. });
  304. } else if (req.urlObj.match(["api"])) {
  305. res.chatContext.onRequest(
  306. (req.urlObj.queryTokens.v ? req.urlObj.queryTokens.v[0] : 0) || 0
  307. , (slack, newData) => {
  308. if (!res.ended) {
  309. try {
  310. if (!slack.connected) {
  311. res.writeHeader("403", {
  312. "Content-Type": "application/json"
  313. });
  314. res.end(slack.error);
  315. } else {
  316. res.writeHeader("200", {
  317. "Content-Type": "application/json"
  318. });
  319. res.end(JSON.stringify(newData));
  320. }
  321. } catch (e) {}
  322. }
  323. sessionManager.saveSession(req.session);
  324. });
  325. } else {
  326. console.log(JSON.stringify(req.session));
  327. console.log(JSON.stringify(req.urlObj));
  328. console.log(JSON.stringify(req.cookies));
  329. res.writeHeader("404", "Not Found");
  330. res.end();
  331. }
  332. }
  333. };
  334. module.exports.HttpServ = Server;