apiController.js 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382
  1. /* jshint esversion: 6 */
  2. const config = require("../../config.js"),
  3. http = require("http"),
  4. https = require("https"),
  5. sessionManager = require("../session.js").SessionManager,
  6. accountConfigManager = require("../models/accountConfig.js").accountConfigManager;
  7. function recursiveGet(url, cb, redirectLoop) {
  8. var getFnc = http.get;
  9. if (url.substr(0, 8) === "https://")
  10. getFnc = https.get;
  11. getFnc(url, (d) => {
  12. if (d.statusCode >= 300 && d.statusCode < 400 && d.headers["location"]) {
  13. if (!redirectLoop)
  14. redirectLoop = [];
  15. if (redirectLoop.indexOf(d.headers["location"]) === -1 && redirectLoop.length < 5) {
  16. redirectLoop.push(d.headers["location"]);
  17. recursiveGet(d.headers["location"], cb, redirectLoop);
  18. return;
  19. }
  20. }
  21. cb(d);
  22. });
  23. };
  24. module.exports.ApiController = {
  25. onRequest: function(req, res, srv) {
  26. if (req.urlObj.match(["api", "logout"])) {
  27. if (req.method === 'POST') {
  28. sessionManager.closeSession(req);
  29. } else {
  30. res.writeHeader("400", "Bad request");
  31. sessionManager.saveSession(req.session);
  32. res.end();
  33. }
  34. } else if (req.urlObj.match(["api", "hist"])) {
  35. if (!req.urlObj.queryTokens.room) {
  36. res.writeHeader("400", "Bad request");
  37. sessionManager.saveSession(req.session);
  38. res.end();
  39. } else {
  40. let ctx = res.chatContext.getChannelContext(req.urlObj.queryTokens.room[0]);
  41. if (ctx) {
  42. ctx.fetchHistory(ctx.getChatContext().channels[req.urlObj.queryTokens.room[0]], (hist) => {
  43. var histArray = [];
  44. hist.forEach((i) => {
  45. histArray.push(i.toStatic());
  46. });
  47. res.writeHeader(200, {"Content-Type":"application/json"});
  48. res.end(JSON.stringify(histArray));
  49. });
  50. } else {
  51. res.writeHeader("404", "Channel not found");
  52. res.end();
  53. }
  54. }
  55. } else if (req.urlObj.match(["api", "typing"])) {
  56. if (!req.urlObj.queryTokens.room) {
  57. res.writeHeader("400", "Bad request");
  58. res.end();
  59. } else {
  60. let ctx = res.chatContext.getChannelContext(req.urlObj.queryTokens.room[0]);
  61. if (!ctx) {
  62. res.writeHeader("404", "Chan not found");
  63. } else {
  64. ctx.sendTyping(ctx.getChatContext().channels[req.urlObj.queryTokens.room[0]]);
  65. res.writeHeader("204", "No Content");
  66. }
  67. res.end();
  68. }
  69. } else if (req.urlObj.match(["api", "cmd"])) {
  70. if (!req.urlObj.queryTokens.room ||
  71. !req.urlObj.queryTokens.cmd) {
  72. res.writeHeader("400", "Bad request");
  73. res.end();
  74. } else {
  75. let ctx = res.chatContext.getChannelContext(req.urlObj.queryTokens.room[0]),
  76. cmd = ctx ? ctx.getChatContext().commands.data['/' +req.urlObj.queryTokens.cmd[0]] : null;
  77. if (!ctx) {
  78. res.writeHeader("404", "Chan not found");
  79. } else if (!cmd) {
  80. res.writeHeader("404", "No such command");
  81. } else {
  82. let args = req.urlObj.queryTokens.args ? req.urlObj.queryTokens.args[0] : "";
  83. if (args === true)
  84. args = "";
  85. ctx.sendCommand(ctx.getChatContext().channels[req.urlObj.queryTokens.room[0]], cmd, args);
  86. res.writeHeader("204", "No Content");
  87. }
  88. res.end();
  89. }
  90. } else if (req.urlObj.match(["api", "markread"])) {
  91. if (!req.urlObj.queryTokens.room || !req.urlObj.queryTokens.id || !req.urlObj.queryTokens.ts) {
  92. res.writeHeader("400", "Bad request");
  93. res.end();
  94. } else {
  95. let ctx = res.chatContext.getChannelContext(req.urlObj.queryTokens.room[0]),
  96. id = req.urlObj.queryTokens.id[0],
  97. ts = req.urlObj.queryTokens.ts[0];
  98. if (!ctx) {
  99. res.writeHeader("404", "Chan Not Found");
  100. } else {
  101. ctx.markRead(ctx.getChatContext().channels[req.urlObj.queryTokens.room[0]], id, ts);
  102. res.writeHeader("204", "No Content");
  103. }
  104. res.end();
  105. }
  106. sessionManager.saveSession(req.session);
  107. } else if (req.urlObj.match(["api", "avatar"])) {
  108. if (!req.urlObj.queryTokens.user) {
  109. res.writeHeader("400", "Bad request");
  110. res.end();
  111. } else {
  112. let user = res.chatContext.getUser(req.urlObj.queryTokens.user[0]);
  113. if (!user) {
  114. res.writeHeader("404", "User Not Found");
  115. res.end();
  116. } else {
  117. let url = (req.urlObj.queryTokens.size && req.urlObj.queryTokens.size[0] === 'l') ? user.getLargeIcon() : user.getSmallIcon();
  118. if (!config.isDebug)
  119. res.setHeader('Cache-Control', 'private, max-age=' +60 * 60); // 1 hour cache for avatars
  120. recursiveGet(url, (d) => {
  121. d.pipe(res, { end: true });
  122. });
  123. }
  124. }
  125. sessionManager.saveSession(req.session);
  126. } else if (req.urlObj.match(["api", "msg"])) {
  127. if (req.method === 'POST') {
  128. if (!req.urlObj.queryTokens.room || !req.urlObj.queryTokens.text) {
  129. res.writeHeader("400", "Bad request");
  130. } else {
  131. let ctx = res.chatContext.getChannelContext(req.urlObj.queryTokens.room[0]);
  132. if (ctx) {
  133. let attachments = null;
  134. if (req.urlObj.queryTokens.attachments) {
  135. try { attachments = JSON.parse(decodeURIComponent(req.urlObj.queryTokens.attachments[0])); }
  136. catch (e) {}
  137. }
  138. if (req.urlObj.queryTokens.me)
  139. ctx.sendMeMsg(ctx.getChatContext().channels[req.urlObj.queryTokens.room[0]], req.urlObj.queryTokens.text);
  140. else
  141. ctx.sendMsg(ctx.getChatContext().channels[req.urlObj.queryTokens.room[0]], req.urlObj.queryTokens.text, attachments);
  142. res.writeHeader("204", "No Content");
  143. } else {
  144. res.writeHeader("404", "Channel not found");
  145. }
  146. }
  147. } else if (req.method === "DELETE") {
  148. if (!req.urlObj.queryTokens.room || !req.urlObj.queryTokens.ts) {
  149. res.writeHeader("400", "Bad request");
  150. } else {
  151. let ctx = res.chatContext.getChannelContext(req.urlObj.queryTokens.room[0]);
  152. if (ctx) {
  153. ctx.removeMsg(ctx.getChatContext().channels[req.urlObj.queryTokens.room[0]], req.urlObj.queryTokens.ts[0]);
  154. res.writeHeader("204", "No Content");
  155. } else {
  156. res.writeHeader("404", "Channel not found");
  157. }
  158. }
  159. } else if (req.method === "PUT") {
  160. if (!req.urlObj.queryTokens.room || !req.urlObj.queryTokens.ts || !req.urlObj.queryTokens.text) {
  161. res.writeHeader("400", "Bad request");
  162. } else {
  163. let ctx = res.chatContext.getChannelContext(req.urlObj.queryTokens.room[0]);
  164. if (ctx) {
  165. ctx.editMsg(ctx.getChatContext().channels[req.urlObj.queryTokens.room[0]], req.urlObj.queryTokens.ts[0], req.urlObj.queryTokens.text);
  166. res.writeHeader("204", "No Content");
  167. } else {
  168. res.writeHeader("404", "Channel not found");
  169. }
  170. }
  171. } else {
  172. res.writeHeader("400", "Bad request");
  173. }
  174. sessionManager.saveSession(req.session);
  175. res.end();
  176. } else if (req.urlObj.match(["api", "reaction"])) {
  177. const chanId = req.urlObj.queryTokens.room ? req.urlObj.queryTokens.room[0] : undefined,
  178. msgId = req.urlObj.queryTokens.msg ? req.urlObj.queryTokens.msg[0] : undefined,
  179. reaction = req.urlObj.queryTokens.reaction ? req.urlObj.queryTokens.reaction[0] : undefined;
  180. if (chanId && msgId && reaction) {
  181. let ctx = res.chatContext.getChannelContext(chanId);
  182. if (!ctx) {
  183. res.writeHeader("404", "Channel Not Found");
  184. } else if (req.method === 'POST') {
  185. res.writeHeader("204", "No Content");
  186. ctx.addReaction(ctx.getChatContext().channels[chanId], msgId, reaction);
  187. } else if (req.method === 'DELETE') {
  188. res.writeHeader("204", "No Content");
  189. ctx.removeReaction(ctx.getChatContext().channels[chanId], msgId, reaction);
  190. } else {
  191. res.writeHeader("405", "Method not allowed");
  192. }
  193. } else {
  194. res.writeHeader("400", "Missing Parameter");
  195. }
  196. sessionManager.saveSession(req.session);
  197. res.end();
  198. } else if (req.urlObj.match(["api", "file"])) {
  199. sessionManager.saveSession(req.session);
  200. if (req.urlObj.queryTokens.room) {
  201. let ctx = res.chatContext.getChannelContext(req.urlObj.queryTokens.room[0]);
  202. if (ctx) {
  203. let uploadRequest = ctx.openUploadFileStream(ctx.getChatContext().channels[req.urlObj.queryTokens.room[0]], req.headers["content-type"], (errorMsg) => {
  204. if (!errorMsg)
  205. res.writeHeader("204", "No Content");
  206. else
  207. res.writeHeader("500", errorMsg);
  208. res.end();
  209. });
  210. req.on('end', () => {
  211. uploadRequest.end();
  212. });
  213. req.pipe(uploadRequest);
  214. } else {
  215. res.writeHeader("404", "Channel Not Found");
  216. res.end();
  217. }
  218. } else {
  219. res.writeHeader("400", "Bad Request");
  220. res.end();
  221. }
  222. } else if (req.urlObj.match(["api", "attachmentAction"])) {
  223. if (!req.urlObj.queryTokens.serviceId) {
  224. res.writeHeader("400", "Bad Request");
  225. res.end();
  226. } else {
  227. let serviceId = req.urlObj.queryTokens.serviceId[0],
  228. ctx = res.chatContext.getUserContext(serviceId);
  229. if (ctx) {
  230. let body = [];
  231. req.on('data', (chunk) => { body.push(chunk); });
  232. req.once('end', ()=> {
  233. let payload;
  234. try {
  235. payload = JSON.parse(Buffer.concat(body).toString());
  236. } catch(e) {
  237. res.writeHeader("400", "Bad Request");
  238. res.end();
  239. return;
  240. }
  241. if (!ctx.sendAction(serviceId, payload, (result) => {
  242. if (result !== false) {
  243. res.writeHeader(200);
  244. res.end(JSON.stringify(result));
  245. } else {
  246. res.writeHeader(503, "Service unavailable");
  247. res.end();
  248. }
  249. })) {
  250. // Error re-interpreting payload
  251. res.writeHeader("400", "Bad Request");
  252. res.end();
  253. }
  254. });
  255. } else {
  256. res.writeHeader("404", "Service not found");
  257. res.end();
  258. }
  259. }
  260. sessionManager.saveSession(req.session);
  261. } else if (req.urlObj.match(["api", "starChannel"])) {
  262. if (req.urlObj.queryTokens.room) {
  263. var ctx = res.chatContext.getChannelContext(req.urlObj.queryTokens.room[0]);
  264. if (!ctx) {
  265. res.writeHeader("404", "Not Found");
  266. res.end();
  267. } else if (!ctx.getChatContext().capacities.starChannel) {
  268. res.writeHeader("400", "Bad Request");
  269. res.end();
  270. } else {
  271. ctx.starChannel(ctx.getChatContext().channels[req.urlObj.queryTokens.room[0]]);
  272. res.writeHeader("204", "No Content");
  273. res.end();
  274. }
  275. } else {
  276. res.writeHeader("400", "Bad Request");
  277. res.end();
  278. }
  279. sessionManager.saveSession(req.session);
  280. } else if (req.urlObj.match(["api", "unstarChannel"])) {
  281. if (req.urlObj.queryTokens.room) {
  282. var ctx = res.chatContext.getChannelContext(req.urlObj.queryTokens.room[0]);
  283. if (!ctx) {
  284. res.writeHeader("404", "Not Found");
  285. res.end();
  286. } else if (!ctx.getChatContext().capacities.starChannel) {
  287. res.writeHeader("400", "Bad Request");
  288. res.end();
  289. } else {
  290. ctx.unstarChannel(ctx.getChatContext().channels[req.urlObj.queryTokens.room[0]]);
  291. res.writeHeader("204", "No Content");
  292. res.end();
  293. }
  294. } else {
  295. res.writeHeader("400", "Bad Request");
  296. res.end();
  297. }
  298. sessionManager.saveSession(req.session);
  299. } else if (req.urlObj.match(["api", "starMsg"])) {
  300. if (req.urlObj.queryTokens.room && req.urlObj.queryTokens.msgId) {
  301. var ctx = res.chatContext.getChannelContext(req.urlObj.queryTokens.room[0]);
  302. if (!ctx) {
  303. res.writeHeader("404", "Not Found");
  304. res.end();
  305. } else if (!ctx.getChatContext().capacities.starMsg) {
  306. res.writeHeader("400", "Bad Request");
  307. res.end();
  308. } else if (req.method === 'POST') {
  309. ctx.starMsg(ctx.getChatContext().channels[req.urlObj.queryTokens.room[0]], req.urlObj.queryTokens.msgId[0]);
  310. res.writeHeader("204", "No Content");
  311. res.end();
  312. } else if (req.method === 'DELETE') {
  313. ctx.unstarMsg(ctx.getChatContext().channels[req.urlObj.queryTokens.room[0]], req.urlObj.queryTokens.msgId[0]);
  314. res.writeHeader("204", "No Content");
  315. res.end();
  316. } else {
  317. res.writeHeader("400", "Bad Request");
  318. res.end();
  319. }
  320. } else {
  321. res.writeHeader("400", "Bad Request");
  322. res.end();
  323. }
  324. } else if (req.urlObj.match(["api", "pinMsg"])) {
  325. if (req.urlObj.queryTokens.room && req.urlObj.queryTokens.msgId) {
  326. var ctx = res.chatContext.getChannelContext(req.urlObj.queryTokens.room[0]);
  327. if (!ctx) {
  328. res.writeHeader("404", "Not Found");
  329. res.end();
  330. } else if (!ctx.getChatContext().capacities.starMsg) {
  331. res.writeHeader("400", "Bad Request");
  332. res.end();
  333. } else if (req.method === 'POST') {
  334. ctx.pinMsg(ctx.getChatContext().channels[req.urlObj.queryTokens.room[0]], req.urlObj.queryTokens.msgId[0]);
  335. res.writeHeader("204", "No Content");
  336. res.end();
  337. } else if (req.method === 'DELETE') {
  338. ctx.unpinMsg(ctx.getChatContext().channels[req.urlObj.queryTokens.room[0]], req.urlObj.queryTokens.msgId[0]);
  339. res.writeHeader("204", "No Content");
  340. res.end();
  341. } else {
  342. res.writeHeader("400", "Bad Request");
  343. res.end();
  344. }
  345. } else {
  346. res.writeHeader("400", "Bad Request");
  347. res.end();
  348. }
  349. } else if (req.urlObj.match(["api"])) {
  350. res.chatContext.poll(
  351. (req.urlObj.queryTokens.v ? parseInt(req.urlObj.queryTokens.v[0], 10) : 0) || 0, req.reqT, (newData) => {
  352. if (!res.ended) {
  353. try {
  354. res.writeHeader("200", {
  355. "Content-Type": "application/json"
  356. });
  357. res.end(JSON.stringify(newData));
  358. } catch (e) {}
  359. }
  360. sessionManager.saveSession(req.session);
  361. }, (knownVersion, cb) => {
  362. accountConfigManager.fromAccountIdAndVersion(req.account.id, knownVersion, cb);
  363. });
  364. } else {
  365. srv.execTemplate(require('../template/_404.js'), req, res);
  366. }
  367. }
  368. };