var express = require("express"); var router = express.Router(); var path = require("path"); var mongojs = require("mongojs"); var ObjectId = mongojs.ObjectId; var token_db = mongojs("tokens"); var cookieParser = require("cookie-parser"); var db = require(pathThumbnails + "/handlers/db.js"); var allowed_key; try { allowed_key = require(pathThumbnails + "/config/allowed_api.js"); } catch (e) { allowed_key = ["***"]; console.log( "(!) Missing file - /config/allowed_api.js Have a look at /config/allowed_api.example.js." ); } var crypto = require("crypto"); var List = require(pathThumbnails + "/handlers/list.js"); var Functions = require(pathThumbnails + "/handlers/functions.js"); var Frontpage = require(pathThumbnails + "/handlers/frontpage.js"); var Search = require(pathThumbnails + "/handlers/search.js"); var uniqid = require("uniqid"); var Filter = require("bad-words"); var filter = new Filter({ placeHolder: "x" }); var paginate = require("mongojs-paginate"); var _exports = { router: router, sIO: {} }; var projects = require(pathThumbnails + "/handlers/aggregates.js"); var error = { not_found: { youtube: { status: 404, error: "Couldn't find a song like that on YouTube.", success: false, results: [] }, local: { status: 404, error: "Couldn't find a song like that in the channel", success: false, results: [] }, list: { status: 404, error: "The list doesn't exist", success: false, results: [] } }, not_authenticated: { status: 403, error: "Wrong adminpassword or userpassword.", success: false, results: [] }, formatting: { status: 400, error: "Malformed request parameters.", success: false, results: [] }, conflicting: { status: 409, error: "That element already exists.", success: false, results: [] }, wrong_token: { status: 400, error: "You're using a faulty token. Try getting a new token, or send the request without the token.", success: false, results: [] }, tooMany: { status: 429, error: "You're doing too many requests, check header-field Retry-After for the wait-time left.", success: false, results: [] }, settings: { status: 409, error: "The channel doesn't have strict skipping enabled.", success: false, results: [] }, already_skip: { status: 206, error: false, success: true, results: [] }, more_skip_needed: { status: 202, error: false, success: true, results: [] }, no_error: { status: 200, error: false, success: true, results: [] } }; router.use(function(req, res, next) { next(); // make sure we go to the next routes and don't stop here }); router.route("/api/help").get(function(req, res) { res.redirect("https://github.com/zoff-music/zoff/blob/master/server/REST.md"); return; }); router.route("/api/frontpages").get(function(req, res) { res.header("Access-Control-Allow-Origin", "*"); res.header( "Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept" ); res.header({ "Content-Type": "application/json" }); var ip = req.headers["x-forwarded-for"] || req.connection.remoteAddress; var guid = Functions.hash_pass( req.get("User-Agent") + ip + req.headers["accept-language"] ); checkTimeout(guid, res, false, "GET", function() { Frontpage.get_frontpage_lists(function(err, docs) { //db.collection("frontpage_lists").find({frontpage: true, count: {$gt: 0}}, function(err, docs) { db.collection("connected_users").find({ _id: "total_users" }, function( err, tot ) { var to_return = error.no_error; to_return.results = { channels: docs, viewers: tot[0].total_users.length }; res.status(200).send(to_return); return; }); }); }); }); router.route("/api/frontpages").post(function(req, res) { res.header("Access-Control-Allow-Origin", "*"); res.header( "Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept" ); res.header({ "Content-Type": "application/json" }); var ip = req.headers["x-forwarded-for"] || req.connection.remoteAddress; var guid = Functions.hash_pass( req.get("User-Agent") + ip + req.headers["accept-language"] ); var token = ""; if (req.body.hasOwnProperty("token")) { token = req.body.token; } var ip = req.headers["x-forwarded-for"] || req.connection.remoteAddress; var guid = Functions.hash_pass( req.get("User-Agent") + ip + req.headers["accept-language"] ); token_db .collection("api_token") .find({ token: token }, function(err, token_docs) { var authorized = false; var origin; try { origin = req.headers.referer.split("/")[2]; } catch (e) { origin = ""; } if ( token_docs.length == 1 && token_docs[0].token == token && (token_docs[0].origin == "*" || origin.indexOf(token_docs[0].origin) > -1) ) { authorized = true; } checkOveruseApiToken(authorized, token_docs, res, function() { checkTimeout(guid, res, authorized, "POST", function() { Frontpage.get_frontpage_lists(function(err, docs) { //db.collection("frontpage_lists").find({frontpage: true, count: {$gt: 0}}, function(err, docs) { db.collection("connected_users").find( { _id: "total_users" }, function(err, tot) { var to_return = error.no_error; to_return.results = { channels: docs, viewers: tot[0].total_users.length }; res.status(200).send(to_return); return; } ); }); }); }); }); }); router.route("/api/generate_name").get(function(req, res) { Functions.generate_channel_name(res); }); router.route("/api/list/:channel_name/:video_id").delete(function(req, res) { res.header("Access-Control-Allow-Origin", "*"); res.header( "Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept" ); res.header({ "Content-Type": "application/json" }); if ( !req.body.hasOwnProperty("adminpass") || !req.body.hasOwnProperty("userpass") || !req.params.hasOwnProperty("channel_name") || !req.params.hasOwnProperty("video_id") ) { var result = { adminpass: { expected: "string", got: req.body.hasOwnProperty("adminpass") ? typeof req.body.adminpass : undefined }, userpass: { expected: "string", got: req.body.hasOwnProperty("userpass") ? typeof req.body.userpass : undefined } }; var to_send = error.formatting; to_send.results = [result]; res.status(400).send(to_send); return; } var token = ""; if (req.body.hasOwnProperty("token")) { token = req.body.token; } try { var ip = req.headers["x-forwarded-for"] || req.connection.remoteAddress; var guid = Functions.hash_pass( req.get("User-Agent") + ip + req.headers["accept-language"] ); var adminpass = req.body.adminpass == "" ? "" : Functions.hash_pass( crypto .createHash("sha256") .update(req.body.adminpass, "utf8") .digest("hex") ); req.body.userpass = req.body.userpass == "" ? "" : crypto .createHash("sha256") .update(req.body.userpass, "utf8") .digest("base64"); var userpass = req.body.userpass; var channel_name = cleanChannelName(req.params.channel_name); var video_id = req.params.video_id; if (typeof userpass != "string" || typeof adminpass != "string") { throw "Wrong format"; } } catch (e) { var result = { adminpass: { expected: "string", got: req.body.hasOwnProperty("adminpass") ? typeof req.body.adminpass : undefined }, userpass: { expected: "string", got: req.body.hasOwnProperty("userpass") ? typeof req.body.userpass : undefined } }; var to_send = error.formatting; to_send.results = [result]; res.status(400).send(to_send); return; } var cookie = req.cookies._uI; Functions.getSessionAdminUser(cookie, channel_name, function(_u, _a) { if (req.body.adminpass == "") { adminpass = Functions.hash_pass(_a); } if (req.body.userpass == "") { userpass = _u; } token_db .collection("api_token") .find({ token: token }, function(err, token_docs) { var authorized = false; var origin; try { origin = req.headers.referer.split("/")[2]; } catch (e) { origin = ""; } if ( token_docs.length == 1 && token_docs[0].token == token && (token_docs[0].origin == "*" || origin.indexOf(token_docs[0].origin) > -1) ) { authorized = true; } checkOveruseApiToken(authorized, token_docs, res, function() { checkTimeout(guid, res, authorized, "DELETE", function() { if (token != "" && !authorized) { //updateTimeout(guid, res, authorized, "DELETE", function(err, docs) { res.status(400).send(error.wrong_token); return; //}); } else { validateLogin( adminpass, userpass, channel_name, "delete", res, function(exists) { if (!exists) { res.status(404).send(error.not_found.list); return; } db.collection(channel_name).find( { id: video_id, now_playing: false }, function(err, docs) { if (docs.length == 0) { res.status(404).send(error.not_found.local); return; } var dont_increment = false; if (docs[0]) { if (docs[0].type == "suggested") { dont_increment = true; } db.collection(channel_name).remove( { id: video_id }, function(err, docs) { if (authorized) { incrementToken(token); } io.to(channel_name).emit("channel", { type: "deleted", value: video_id }); if (!dont_increment) { db.collection("frontpage_lists").update( { _id: channel_name, count: { $gt: 0 } }, { $inc: { count: -1 }, $set: { accessed: Functions.get_time() } }, { upsert: true }, function(err, docs) { //updateTimeout(guid, res, authorized, "DELETE", function(err, docs) { res.status(200).send(error.no_error); return; //}); } ); } else { //updateTimeout(guid, res, authorized, "DELETE", function(err, docs) { res.status(200).send(error.no_error); return; //}); } } ); } } ); } ); } }); }); }); }); }); router.route("/api/skip/:channel_name").post(function(req, res) { res.header("Access-Control-Allow-Origin", "*"); res.header( "Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept" ); res.header({ "Content-Type": "application/json" }); var api_key = req.body.api_key; var guid = req.body.chat_name; var channel_name = cleanChannelName(req.params.channel_name); var userpass = ""; if (req.body.userpass && req.body.userpass != "") userpass = crypto .createHash("sha256") .update(Functions.decrypt_string(req.body.userpass)) .digest("base64"); if (allowed_key.indexOf(api_key) > -1 || allowed_key.indexOf("***") > -1) { db.collection(channel_name + "_settings").find({ id: "config" }, function( err, settings ) { if (settings.length == 0) { res.status(404).send(error.not_found.list); return; } settings = settings[0]; if (!settings.strictSkip) { res.status(409).send(error.settings); return; } if (settings.userpass == "" || settings.userpass == userpass) { if ( settings.skips.length + 1 >= settings.strictSkipNumber && !Functions.contains(settings.skips, guid) ) { Functions.checkTimeout( "skip", 1, channel_name, channel_name, false, true, undefined, function() { db.collection(channel_name).find({ now_playing: true }, function( err, np ) { if (np.length != 1) { res.status(404).send(error.not_found.list); return; } List.change_song( channel_name, false, np[0].id, [settings], function() { res.status(200).send(error.no_error); return; } ); _exports.sIO.to(channel_name).emit("chat", { from: guid, icon: false, msg: " skipped via API." }); }); }, "", function() { res.status(429).send(error.tooMany); return; } ); } else if (!Functions.contains(settings.skips, guid)) { db.collection(channel_name + "_settings").update( { id: "config" }, { $push: { skips: guid } }, function(err, d) { var to_skip = settings.strictSkipNumber - settings.skips.length - 1; _exports.sIO .to(channel_name) .emit("chat", { from: guid, msg: " voted to skip via API." }); // VOTED TO SKIP var to_send = error.more_skip_needed; to_send.results = [to_skip]; res.status(202).send(to_send); return; } ); } else { //ALREADY SKIP res.status(206).send(error.already_skip); return; } } else { // NOT AUTHENTICATED res.status(403).send(error.not_authenticated); return; } }); } else { // WRONG API KEY var toSend = error.not_authenticated; toSend.status = 406; res.status(406).send(toSend); return; } }); router.route("/api/conf/:channel_name").put(function(req, res) { res.header("Access-Control-Allow-Origin", "*"); res.header( "Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept" ); res.header({ "Content-Type": "application/json" }); try { if ( !req.body.hasOwnProperty("adminpass") || !req.body.hasOwnProperty("userpass") || !req.params.hasOwnProperty("channel_name") || !req.body.hasOwnProperty("vote") || !req.body.hasOwnProperty("addsongs") || !req.body.hasOwnProperty("longsongs") || !req.body.hasOwnProperty("frontpage") || !req.body.hasOwnProperty("allvideos") || !req.body.hasOwnProperty("skip") || !req.body.hasOwnProperty("shuffle") || !req.body.hasOwnProperty("userpass_changed") ) { throw "Wrong format"; } var token = ""; if (req.body.hasOwnProperty("token")) { token = req.body.token; } var ip = req.headers["x-forwarded-for"] || req.connection.remoteAddress; var guid = Functions.hash_pass( req.get("User-Agent") + ip + req.headers["accept-language"] ); var adminpass = req.body.adminpass == "" ? "" : Functions.hash_pass( crypto .createHash("sha256") .update(req.body.adminpass, "utf8") .digest("hex") ); req.body.userpass = req.body.userpass == "" ? "" : crypto .createHash("sha256") .update(req.body.userpass, "utf8") .digest("base64"); var userpass = req.body.userpass; var voting = req.body.vote; var addsongs = req.body.addsongs; var longsongs = req.body.longsongs; var frontpage = req.body.frontpage; var allvideos = req.body.allvideos; var removeplay = req.body.removeplay; var skipping = req.body.skip; var shuffling = req.body.shuffle; var userpass_changed = req.body.userpass_changed; var channel_name = cleanChannelName(req.params.channel_name); if ( typeof userpass != "string" || typeof adminpass != "string" || typeof voting != "boolean" || typeof addsongs != "boolean" || typeof longsongs != "boolean" || typeof frontpage != "boolean" || typeof allvideos != "boolean" || typeof removeplay != "boolean" || typeof skipping != "boolean" || typeof shuffling != "boolean" || typeof userpass_changed != "boolean" ) { throw "Wrong format"; } } catch (e) { var result = { adminpass: { expected: "string", got: req.body.hasOwnProperty("adminpass") ? typeof req.body.adminpass : undefined }, userpass: { expected: "string", got: req.body.hasOwnProperty("userpass") ? typeof req.body.userpass : undefined }, vote: { expected: "boolean", got: req.body.hasOwnProperty("vote") ? typeof req.body.vote : undefined }, addsongs: { expected: "boolean", got: req.body.hasOwnProperty("addsongs") ? typeof req.body.addsongs : undefined }, longsongs: { expected: "boolean", got: req.body.hasOwnProperty("longsongs") ? typeof req.body.longsongs : undefined }, frontpage: { expected: "boolean", got: req.body.hasOwnProperty("frontpage") ? typeof req.body.frontpage : undefined }, skip: { expected: "boolean", got: req.body.hasOwnProperty("skip") ? typeof req.body.skip : undefined }, shuffle: { expected: "boolean", got: req.body.hasOwnProperty("shuffle") ? typeof req.body.shuffle : undefined }, userpass_changed: { expected: "boolean", got: req.body.hasOwnProperty("userpass_changed") ? typeof req.body.userpass_changed : undefined } }; var to_send = error.formatting; to_send.results = [result]; res.status(400).send(to_send); return; } var cookie = req.cookies._uI; Functions.getSessionAdminUser(cookie, channel_name, function(_u, _a) { if (req.body.adminpass == "") { adminpass = Functions.hash_pass(_a); } if (req.body.userpass == "") { userpass = _u; } token_db .collection("api_token") .find({ token: token }, function(err, token_docs) { var authorized = false; var origin; try { origin = req.headers.referer.split("/")[2]; } catch (e) { origin = ""; } if ( token_docs.length == 1 && token_docs[0].token == token && (token_docs[0].origin == "*" || origin.indexOf(token_docs[0].origin) > -1) ) { authorized = true; } checkOveruseApiToken(authorized, token_docs, res, function() { checkTimeout(guid, res, authorized, "CONFIG", function() { if (token != "" && !authorized) { //updateTimeout(guid, res, authorized, "CONFIG", function(err, docs) { res.status(400).send(error.wrong_token); return; //}); } else { validateLogin( adminpass, userpass, channel_name, "config", res, function(exists, conf) { if (!exists && conf.length == 0) { res.status(404).send(error.not_found.list); return; } if ( (!userpass_changed && frontpage) || (userpass_changed && userpass == "") ) { userpass = ""; } else if (userpass_changed && userpass != "") { frontpage = false; } var description = ""; var obj = { addsongs: addsongs, allvideos: allvideos, frontpage: frontpage, skip: skipping, vote: voting, removeplay: removeplay, shuffle: shuffling, longsongs: longsongs, adminpass: adminpass, desc: description }; if (userpass_changed) { obj["userpass"] = userpass; } else if (frontpage) { obj["userpass"] = ""; } db.collection(channel_name + "_settings").update( { views: { $exists: true } }, { $set: obj }, function(err, docs) { if (obj.adminpass !== "") obj.adminpass = true; if (obj.hasOwnProperty("userpass") && obj.userpass != "") obj.userpass = true; else obj.userpass = false; io.to(channel_name).emit("conf", [obj]); db.collection("frontpage_lists").update( { _id: channel_name }, { $set: { frontpage: frontpage, accessed: Functions.get_time() } }, { upsert: true }, function(err, docs) { if (authorized) { incrementToken(token); } //updateTimeout(guid, res, authorized, "CONFIG", function(err, docs) { var to_return = error.no_error; to_return.results = [obj]; res.status(200).send(to_return); return; //}); } ); } ); } ); } }); }); }); }); }); router.route("/api/list/:channel_name/:video_id").put(function(req, res) { res.header("Access-Control-Allow-Origin", "*"); res.header( "Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept" ); res.header({ "Content-Type": "application/json" }); try { if ( !req.body.hasOwnProperty("adminpass") || !req.body.hasOwnProperty("userpass") || !req.params.hasOwnProperty("channel_name") || !req.params.hasOwnProperty("video_id") ) { throw "Wrong format"; } var token = ""; if (req.body.hasOwnProperty("token")) { token = req.body.token; } var ip = req.headers["x-forwarded-for"] || req.connection.remoteAddress; var guid = Functions.hash_pass( req.get("User-Agent") + ip + req.headers["accept-language"] ); var adminpass = req.body.adminpass == "" ? "" : Functions.hash_pass( crypto .createHash("sha256") .update(req.body.adminpass, "utf8") .digest("hex") ); req.body.userpass = req.body.userpass == "" ? "" : crypto .createHash("sha256") .update(req.body.userpass, "utf8") .digest("base64"); var userpass = req.body.userpass; var channel_name = cleanChannelName(req.params.channel_name); var video_id = req.params.video_id; if (typeof userpass != "string" || typeof adminpass != "string") { throw "Wrong format"; } } catch (e) { var result = { adminpass: { expected: "string", got: req.body.hasOwnProperty("adminpass") ? typeof req.body.adminpass : undefined }, userpass: { expected: "string", got: req.body.hasOwnProperty("userpass") ? typeof req.body.userpass : undefined } }; var to_send = error.formatting; to_send.results = [result]; res.status(400).send(to_send); return; } var cookie = req.cookies._uI; Functions.getSessionAdminUser(cookie, channel_name, function(_u, _a) { if (req.body.adminpass == "") { adminpass = Functions.hash_pass(_a); } if (req.body.userpass == "") { userpass = _u; } token_db .collection("api_token") .find({ token: token }, function(err, token_docs) { var authorized = false; var origin; try { origin = req.headers.referer.split("/")[2]; } catch (e) { origin = ""; } if ( token_docs.length == 1 && token_docs[0].token == token && (token_docs[0].origin == "*" || origin.indexOf(token_docs[0].origin) > -1) ) { authorized = true; } checkOveruseApiToken(authorized, token_docs, res, function() { checkTimeout(guid, res, authorized, "PUT", function() { if (token != "" && !authorized) { //updateTimeout(guid, res, authorized, "PUT", function(err, docs) { res.status(400).send(error.wrong_token); return; //}); } else { validateLogin( adminpass, userpass, channel_name, "vote", res, function(exists) { if (!exists) { res.status(404).send(error.not_found.list); return; } db.collection(channel_name).find( { id: video_id, now_playing: false }, function(err, song) { if ( song.length == 0 || (song.hasOwnProperty("type") && song.type == "suggested") ) { res.status(404).send(error.not_found.local); return; } else if (song[0].guids.indexOf(guid) > -1) { res.status(409).send(error.conflicting); return; } else { song[0].votes += 1; song[0].guids.push(guid); db.collection(channel_name).update( { id: video_id }, { $inc: { votes: 1 }, $set: { added: Functions.get_time(), type: "video" }, $push: { guids: guid } }, function(err, success) { if (authorized) { incrementToken(token); } io.to(channel_name).emit("channel", { type: "vote", value: video_id, time: Functions.get_time() }); List.getNextSong( channel_name, undefined, function() { //updateTimeout(guid, res, authorized, "PUT", function(err, docs) { var to_return = error.no_error; to_return.results = song; res.status(200).send(to_return); return; //}); } ); } ); } } ); } ); } }); }); }); }); }); router.route("/api/list/:channel_name/__np__").post(function(req, res) { res.header("Access-Control-Allow-Origin", "*"); res.header( "Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept" ); res.header({ "Content-Type": "application/json" }); try { var ip = req.headers["x-forwarded-for"] || req.connection.remoteAddress; var guid = Functions.hash_pass( req.get("User-Agent") + ip + req.headers["accept-language"] ); var channel_name = cleanChannelName(req.params.channel_name); var userpass; if (req.body.hasOwnProperty("userpass")) { req.body.userpass = req.body.userpass == "" ? "" : crypto .createHash("sha256") .update(req.body.userpass, "utf8") .digest("base64"); userpass = req.body.userpass; } else { userpass = ""; } var token = ""; if (req.body.hasOwnProperty("token")) { token = req.body.token; } } catch (e) { var result = { userpass: { expected: "string", got: req.body.hasOwnProperty("userpass") ? typeof req.body.userpass : undefined } }; var to_send = error.formatting; to_send.results = [result]; res.status(400).send(to_send); return; } var cookie = req.cookies._uI; Functions.getSessionAdminUser(cookie, channel_name, function(_u, _a) { if (req.body.userpass == "") { //userpass = Functions.hash_pass(Functions.hash_pass(Functions.decrypt_string(_u))) userpass = _u; } token_db .collection("api_token") .find({ token: token }, function(err, token_docs) { var authorized = false; var origin; try { origin = req.headers.referer.split("/")[2]; } catch (e) { origin = ""; } if ( token_docs.length == 1 && token_docs[0].token == token && (token_docs[0].origin == "*" || origin.indexOf(token_docs[0].origin) > -1) ) { authorized = true; } checkOveruseApiToken(authorized, token_docs, res, function() { checkTimeout(guid, res, authorized, "POST", function() { if (token != "" && !authorized) { //updateTimeout(guid, res, authorized, "POST", function(err, docs) { res.status(400).send(error.wrong_token); return; //}); } else { db.collection(channel_name).find( { now_playing: true }, projects.toShowChannel, function(err, list) { if (list.length > 0) { db.collection(channel_name + "_settings").find( { id: "config" }, function(err, conf) { if (authorized) { incrementToken(token); } if (conf.length == 0) { res.status(404).send(error.not_found.list); return; } else if ( conf[0].userpass != userpass && conf[0].userpass != "" && conf[0].userpass != undefined ) { res.status(403).send(error.not_authenticated); return; } //updateTimeout(guid, res, authorized, "POST", function(err, docs) { var to_return = error.no_error; if (list[0].source == undefined) { list[0].source = "youtube"; } if (list[0].thumbnail == undefined) { list[0].thumbnail = "https://img.youtube.com/vi/" + list[0].id + "/mqdefault.jpg"; } to_return.results = list; res.status(200).send(to_return); return; //}); } ); } else { res.status(404).send(error.not_found.list); return; } } ); } }); }); }); }); }); router.route("/api/search/:channel_name/").post(function(req, res) { res.header("Access-Control-Allow-Origin", "*"); res.header( "Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept" ); res.header({ "Content-Type": "application/json" }); try { var ip = req.headers["x-forwarded-for"] || req.connection.remoteAddress; var guid = Functions.hash_pass( req.get("User-Agent") + ip + req.headers["accept-language"] ); var channel_name = cleanChannelName(req.params.channel_name); var userpass; if (req.body.hasOwnProperty("userpass")) { req.body.userpass = req.body.userpass == "" ? "" : crypto .createHash("sha256") .update(req.body.userpass, "utf8") .digest("base64"); userpass = req.body.userpass; } else { userpass = ""; } var page = 1; if (req.body.hasOwnProperty("page") && req.body.page > 0) { page = req.body.page; } var searchQuery = ""; var searchByCategory = true; if (req.body.hasOwnProperty("type")) { searchByCategory = req.body.type == "category"; } if (req.body.searchQuery == undefined || req.body.searchQuery == "") { var to_send = error.formatting; to_send.results = [result]; res.status(400).send(to_send); return; } searchQuery = req.body.searchQuery.toLowerCase(); var token = ""; if (req.body.hasOwnProperty("token")) { token = req.body.token; } } catch (e) { var result = { userpass: { expected: "string", got: req.body.hasOwnProperty("userpass") ? typeof req.body.userpass : undefined } }; var to_send = error.formatting; to_send.results = [result]; res.status(400).send(to_send); return; } var cookie = req.cookies._uI; Functions.getSessionAdminUser(cookie, channel_name, function(_u, _a) { if (req.body.userpass == "") { //userpass = Functions.hash_pass(Functions.hash_pass(Functions.decrypt_string(_u))) userpass = _u; } token_db .collection("api_token") .find({ token: token }, function(err, token_docs) { var authorized = false; var origin; try { origin = req.headers.referer.split("/")[2]; } catch (e) { origin = ""; } if ( token_docs.length == 1 && token_docs[0].token == token && (token_docs[0].origin == "*" || origin.indexOf(token_docs[0].origin) > -1) ) { authorized = true; } checkOveruseApiToken(authorized, token_docs, res, function() { checkTimeout(guid, res, authorized, "POST", function() { db.collection(channel_name + "_settings").find( { id: "config" }, function(err, conf) { if (authorized) { incrementToken(token); } if (conf.length == 0) { res.status(404).send(error.not_found.list); return; } else if ( conf[0].userpass != userpass && conf[0].userpass != "" && conf[0].userpass != undefined ) { res.status(403).send(error.not_authenticated); return; } var querySubObject = { $regex: ".*" + searchQuery + ".*" }; var queryObject = {}; if (searchByCategory) { queryObject.tags = querySubObject; } else { queryObject.title = querySubObject; queryObject.title.$regex = searchQuery; queryObject.title.$options = "i"; } var query = db.collection(channel_name).find(queryObject); paginate(query, { limit: 30, page: page }, function( err, result ) { if (result.items.length == 0) { res.status(404).send(error.not_found.local); return; } var to_return = error.no_error; to_return.results = {}; if (result.hasNext) { to_return.results.next = result.page + 1; } if (result.hasPrevious) { to_return.results.prev = result.page - 1; } to_return.results.search_results = result.items; res.status(200).send(to_return); }); } ); }); }); }); }); }); router.route("/api/list/:channel_name/:video_id").post(function(req, res) { res.header("Access-Control-Allow-Origin", "*"); res.header( "Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept" ); res.header({ "Content-Type": "application/json" }); var fetch_only = false; if (req.body.hasOwnProperty("fetch_song")) { fetch_only = true; } var token = ""; if (req.body.hasOwnProperty("token")) { token = req.body.token; } try { if ( !fetch_only && (!req.body.hasOwnProperty("adminpass") || !req.body.hasOwnProperty("userpass") || !req.params.hasOwnProperty("channel_name") || !req.params.hasOwnProperty("video_id") || !req.body.hasOwnProperty("duration") || !req.body.hasOwnProperty("start_time") || !req.body.hasOwnProperty("end_time") || !req.body.hasOwnProperty("title") || !req.body.hasOwnProperty("source")) ) { throw "Wrong format"; } var ip = req.headers["x-forwarded-for"] || req.connection.remoteAddress; var guid = Functions.hash_pass( req.get("User-Agent") + ip + req.headers["accept-language"] ); var userpass; if (req.body.hasOwnProperty("userpass")) { req.body.userpass = req.body.userpass == "" ? "" : crypto .createHash("sha256") .update(req.body.userpass, "utf8") .digest("base64"); userpass = req.body.userpass; } else { userpass = ""; } var channel_name = cleanChannelName(req.params.channel_name); var video_id = req.params.video_id; if (!fetch_only) { var adminpass = req.body.adminpass == "" ? "" : Functions.hash_pass( crypto .createHash("sha256") .update(req.body.adminpass, "utf8") .digest("hex") ); var duration = parseInt(req.body.duration); var start_time = parseInt(req.body.start_time); var end_time = parseInt(req.body.end_time); var tags = []; if (req.body.tags != undefined) tags = req.body.tags.split(","); var source = req.body.source; if (source == "soundcloud" && !req.body.hasOwnProperty("thumbnail")) { throw "Wrong format"; } if (duration != end_time - start_time) duration = end_time - start_time; var title = req.body.title; if ( typeof userpass != "string" || typeof adminpass != "string" || typeof title != "string" || isNaN(duration) || isNaN(start_time) || isNaN(end_time) ) { throw "Wrong format"; } } } catch (e) { var result = { adminpass: { expected: "string", got: req.body.hasOwnProperty("adminpass") ? typeof req.body.adminpass : undefined }, userpass: { expected: "string", got: req.body.hasOwnProperty("userpass") ? typeof req.body.userpass : undefined }, title: { expected: "string", got: req.body.hasOwnProperty("title") ? typeof req.body.title : undefined }, start_time: { expected: "number or string that can be cast to int", got: !req.body.hasOwnProperty("start_time") ? undefined : isNaN(req.body.start_time) ? "uncastable string" : typeof req.body.start_time }, end_time: { expected: "number or string that can be cast to int", got: !req.body.hasOwnProperty("end_time") ? undefined : isNaN(req.body.end_time) ? "uncastable string" : typeof req.body.end_time }, duration: { expected: "number or string that can be cast to int", got: !req.body.hasOwnProperty("duration") ? undefined : isNaN(req.body.duration) ? "uncastable string" : typeof req.body.duration } }; var to_send = error.formatting; to_send.results = [result]; res.status(400).send(to_send); return; } var cookie = req.cookies._uI; Functions.getSessionAdminUser(cookie, channel_name, function(_u, _a) { if (req.body.adminpass == "") { adminpass = Functions.hash_pass(_a); } if (req.body.userpass == "") { userpass = _u; } token_db .collection("api_token") .find({ token: token }, function(err, token_docs) { var authorized = false; var origin; try { origin = req.headers.referer.split("/")[2]; } catch (e) { origin = ""; } if ( token_docs.length == 1 && token_docs[0].token == token && (token_docs[0].origin == "*" || origin.indexOf(token_docs[0].origin) > -1) ) { authorized = true; } checkOveruseApiToken(authorized, token_docs, res, function() { checkTimeout(guid, res, authorized, "POST", function() { if (token != "" && !authorized) { //updateTimeout(guid, res, authorized, "POST", function(err, docs) { res.status(400).send(error.wrong_token); return; //}); } else { var type = fetch_only ? "fetch_song" : "add"; validateLogin( adminpass, userpass, channel_name, type, res, function(exists, conf, authenticated) { db.collection(channel_name).find({ id: video_id }, function( err, result ) { if (result.length == 0 || result[0].type == "suggested") { var song_type = authenticated ? "video" : "suggested"; if (fetch_only && result.length == 0) { res.status(404).send(error.not_found.local); return; } db.collection(channel_name).find( { now_playing: true }, function(err, now_playing) { var set_np = false; if (now_playing.length == 0 && authenticated) { set_np = true; } var new_song = { tags: tags, added: Functions.get_time(), guids: [guid], id: video_id, now_playing: set_np, title: title, votes: 1, duration: duration, start: parseInt(start_time), end: parseInt(end_time), type: song_type, source: source }; var runFunction = Search.get_correct_info; if (source == "soundcloud") { if ( req.body.thumbnail.indexOf( "https://i1.sndcdn.com" ) > -1 || req.body.thumbnail.indexOf( "https://w1.sndcdn.com" ) > -1 ) { new_song.thumbnail = req.body.thumbnail; } else { new_song.thumbnail = "https://img.youtube.com/vi/404_notfound/mqdefault.jpg"; } runFunction = function( new_song, foo_2, foo_3, callback ) { callback(new_song, true); }; } else if (source == "youtube") new_song.thumbnail = "https://img.youtube.com/vi/" + new_song.id + "/mqdefault.jpg"; runFunction(new_song, channel_name, false, function( element, found ) { if (!found) { res.status(404).send(error.not_found.youtube); return; } new_song = element; db.collection("frontpage_lists").find( { _id: channel_name }, function(err, count) { var create_frontpage_lists = false; if (count.length == 0) { create_frontpage_lists = true; } if (!exists) { var configs = { addsongs: false, adminpass: "", allvideos: true, frontpage: true, longsongs: false, removeplay: false, shuffle: true, skip: false, skips: [], startTime: Functions.get_time(), views: [], vote: false, desc: "" }; db.collection( channel_name + "_settings" ).insert(configs, function(err, docs) { io.to(channel_name).emit("conf", configs); }); } db.collection(channel_name).update( { id: new_song.id }, new_song, { upsert: true }, function(err, success) { if (authorized) { incrementToken(token); } if (create_frontpage_lists) { db.collection("frontpage_lists").update( { _id: channel_name, count: authenticated ? 1 : 0, frontpage: true, accessed: Functions.get_time(), viewers: 1 }, { upsert: true }, function(err, docs) { if (authenticated) { io.to(channel_name).emit( "channel", { type: "added", value: new_song } ); } else { io.to(channel_name).emit( "suggested", new_song ); } postEnd( channel_name, configs, new_song, guid, res, authenticated, authorized ); } ); } else if (set_np) { var thumbnail = req.body.thumbnail != undefined ? req.body.thumbnail : undefined; Frontpage.update_frontpage( channel_name, video_id, title, thumbnail, source, function() { io.to(channel_name).emit("np", { np: [new_song], conf: [conf] }); postEnd( channel_name, configs, new_song, guid, res, authenticated, authorized ); } ); } else { db.collection("frontpage_lists").update( { _id: channel_name }, { $inc: { count: authenticated ? 1 : 0 } }, function(err, docs) { if (authenticated) { io.to(channel_name).emit( "channel", { type: "added", value: new_song } ); } else { io.to(channel_name).emit( "suggested", new_song ); } postEnd( channel_name, configs, new_song, guid, res, authenticated, authorized ); } ); } } ); } ); }); } ); } else if (fetch_only) { var to_return = error.no_error; to_return.results = result; res.status(200).send(to_return); return; } else { res.status(409).send(error.conflicting); return; } }); } ); } }); }); }); }); }); router.route("/api/list/:channel_name").get(function(req, res) { res.header("Access-Control-Allow-Origin", "*"); res.header( "Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept" ); res.header({ "Content-Type": "application/json" }); var channel_name = cleanChannelName(req.params.channel_name); db.collection(channel_name).aggregate( [ { $match: { type: { $ne: "suggested" } } }, { $project: projects.project_object }, { $sort: { now_playing: -1, votes: -1, added: 1, title: 1 } } ], function(err, docs) { var ip = req.headers["x-forwarded-for"] || req.connection.remoteAddress; var guid = Functions.hash_pass( req.get("User-Agent") + ip + req.headers["accept-language"] ); //db.collection(channel_name).find({views: {$exists: false}}, projects.toShowChannel, function(err, docs) { checkTimeout(guid, res, false, "GET", function() { if (docs.length > 0) { db.collection(channel_name + "_settings").find( { id: "config" }, function(err, conf) { if (conf.length == 0) { res.status(404).send(error.not_found.list); return; } else if ( conf[0].userpass != "" && conf[0].userpass != undefined ) { res.status(403).send(error.not_authenticated); return; } var to_return = error.no_error; to_return.results = docs; res.status(200).send(to_return); } ); } else { res.status(404).send(error.not_found.list); } }); } ); }); router.route("/api/list/:channel_name/:video_id").get(function(req, res) { res.header("Access-Control-Allow-Origin", "*"); res.header( "Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept" ); res.header({ "Content-Type": "application/json" }); var ip = req.headers["x-forwarded-for"] || req.connection.remoteAddress; var guid = Functions.hash_pass( req.get("User-Agent") + ip + req.headers["accept-language"] ); checkTimeout(guid, res, false, "GET", function() { var channel_name = cleanChannelName(req.params.channel_name); var video_id = req.params.video_id; var searchQuery = { id: video_id }; if (video_id == "__np__") { searchQuery = { now_playing: true }; } db.collection(channel_name).find( searchQuery, projects.toShowChannel, function(err, docs) { db.collection(channel_name + "_settings").find( { id: "config" }, function(err, conf) { if (conf.length == 0) { res.status(404).send(error.not_found.list); return; } else if ( conf[0].userpass != "" && conf[0].userpass != undefined ) { res.status(403).send(error.not_authenticated); return; } if (docs.length == 0) { res.status(404).send(error.not_found.local); return; } var to_return = error.no_error; if (docs[0].source == undefined) { docs[0].source = "youtube"; } if (docs[0].thumbnail == undefined) { docs[0].thumbnail = "https://img.youtube.com/vi/" + docs[0].id + "/mqdefault.jpg"; } to_return.results = docs; res.status(200).send(to_return); return; } ); } ); }); }); router.route("/api/conf/:channel_name").get(function(req, res) { res.header("Access-Control-Allow-Origin", "*"); res.header( "Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept" ); res.header({ "Content-Type": "application/json" }); var ip = req.headers["x-forwarded-for"] || req.connection.remoteAddress; var guid = Functions.hash_pass( req.get("User-Agent") + ip + req.headers["accept-language"] ); checkTimeout(guid, res, false, "GET", function() { var channel_name = cleanChannelName(req.params.channel_name); db.collection(channel_name + "_settings").aggregate( [ { $match: { id: "config" } }, { $project: projects.toShowConfig } ], function(err, docs) { if ( docs.length > 0 && (docs[0].userpass == "" || docs[0].userpass == undefined) ) { var conf = docs[0]; if (conf.adminpass != "") { conf.adminpass = true; } else { conf.adminpass = false; } if (conf.userpass != "" && conf.userpass != undefined) { conf.userpass = true; } else { conf.userpass = false; } var to_return = error.no_error; to_return.results = [conf]; res.status(200).send(to_return); } else if ( docs.length > 0 && docs[0].userpass != "" && docs[0].userpass != undefined ) { res.status(403).send(error.not_authenticated); return; } else { res.status(404).send(error.not_found.list); return; } } ); }); }); router.route("/api/conf/:channel_name").post(function(req, res) { res.header("Access-Control-Allow-Origin", "*"); res.header( "Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept" ); res.header({ "Content-Type": "application/json" }); try { var token = ""; if (req.body.hasOwnProperty("token")) { token = req.body.token; } var ip = req.headers["x-forwarded-for"] || req.connection.remoteAddress; var guid = Functions.hash_pass( req.get("User-Agent") + ip + req.headers["accept-language"] ); var channel_name = cleanChannelName(req.params.channel_name); var userpass; if (req.body.hasOwnProperty("userpass")) { req.body.userpass = req.body.userpass == "" ? "" : crypto .createHash("sha256") .update(req.body.userpass, "utf8") .digest("base64"); userpass = req.body.userpass; } else { userpass = ""; } } catch (e) { var result = { userpass: { expected: "string", got: req.body.hasOwnProperty("userpass") ? typeof req.body.userpass : undefined } }; var to_send = error.formatting; to_send.results = [result]; res.status(400).send(to_send); return; } var cookie = req.cookies._uI; Functions.getSessionAdminUser(cookie, channel_name, function(_u, _a) { if (req.body.userpass == "") { userpass = crypto .createHash("sha256") .update(Functions.decrypt_string("", _u), "utf8") .digest("base64"); } token_db .collection("api_token") .find({ token: token }, function(err, token_docs) { var authorized = false; var origin; try { origin = req.headers.referer.split("/")[2]; } catch (e) { origin = ""; } if ( token_docs.length == 1 && token_docs[0].token == token && (token_docs[0].origin == "*" || origin.indexOf(token_docs[0].origin) > -1) ) { authorized = true; } checkOveruseApiToken(authorized, token_docs, res, function() { checkTimeout(guid, res, authorized, "POST", function() { if (token != "" && !authorized) { //updateTimeout(guid, res, authorized, "DELETE", function(err, docs) { res.status(400).send(error.wrong_token); return; //}); } else { db.collection(channel_name + "_settings").aggregate( [ { $match: { id: "config" } }, { $project: projects.toShowConfig } ], function(err, docs) { if (docs.length > 0 && docs[0].userpass == userpass) { var conf = docs[0]; if (conf.adminpass != "") { conf.adminpass = true; } else { conf.adminpass = false; } if (conf.userpass != "") { conf.userpass = true; } else { conf.userpass = false; } if (authorized) { incrementToken(token); } //updateTimeout(guid, res, authorized, "POST", function(err, docs) { var to_return = error.no_error; to_return.results = [conf]; res.status(200).send(to_return); //}); } else if (docs.length > 0 && docs[0].userpass != userpass) { res.status(403).send(error.not_authenticated); return; } else { res.status(404).send(error.not_found.list); return; } } ); } }); }); }); }); }); function checkOveruseApiToken(authorized, token_docs, res, callback) { if (!authorized || (authorized && token_docs[0].limit == 0)) { callback(); return; } db.collection("timeout_api").find({ guid: token_docs[0].token }, function( e, doc ) { if (doc.length == 1) { var this_doc = doc[0]; var date = new Date(this_doc.createdAt); date.setSeconds(date.getSeconds() + 1); var now = new Date(); var retry_in = (date.getTime() - now.getTime()) / 1000; if (this_doc.used >= token_docs[0].limit && retry_in > 0) { res.header({ "Retry-After": retry_in }); res.status(429).send(error.tooMany); return; } else { var updateElement = {}; if (retry_in <= 0) { updateElement["$set"] = { createdAt: new Date(), used: 1 }; } else { updateElement["$inc"] = { used: 1 }; } db.collection("timeout_api").update( { guid: token_docs[0].token }, updateElement, function(e, d) { callback(); } ); } } else { db.collection("timeout_api").update( { guid: token_docs[0].token }, { $set: { guid: token_docs[0].token, createdAt: new Date(), type: "ALL" }, $inc: { used: 1 } }, { upsert: true }, function(e, d) { callback(); } ); } }); } router.route("/api/list/:channel_name").post(function(req, res) { res.header("Access-Control-Allow-Origin", "*"); res.header( "Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept" ); res.header({ "Content-Type": "application/json" }); try { var token = ""; if (req.body.hasOwnProperty("token")) { token = req.body.token; } var ip = req.headers["x-forwarded-for"] || req.connection.remoteAddress; var guid = Functions.hash_pass( req.get("User-Agent") + ip + req.headers["accept-language"] ); var channel_name = cleanChannelName(req.params.channel_name); var userpass; if (req.body.hasOwnProperty("userpass")) { req.body.userpass = req.body.userpass == "" ? "" : crypto .createHash("sha256") .update(req.body.userpass, "utf8") .digest("base64"); userpass = req.body.userpass; } else { userpass = ""; } /*if(typeof(userpass) != "string") { throw "Wrong format"; }*/ } catch (e) { var result = { userpass: { expected: "string", got: req.body.hasOwnProperty("userpass") ? typeof req.body.userpass : undefined } }; var to_send = error.formatting; to_send.results = [result]; res.status(400).send(to_send); return; } var cookie = req.cookies._uI; Functions.getSessionAdminUser(cookie, channel_name, function(_u, _a) { if (req.body.userpass == "") { //userpass = Functions.hash_pass(Functions.hash_pass(Functions.decrypt_string(_u))) userpass = _u; } token_db .collection("api_token") .find({ token: token }, function(err, token_docs) { var authorized = false; var origin; try { origin = req.headers.referer.split("/")[2]; } catch (e) { origin = ""; } if ( token_docs.length == 1 && token_docs[0].token == token && (token_docs[0].origin == "*" || origin.indexOf(token_docs[0].origin) > -1) ) { authorized = true; } checkOveruseApiToken(authorized, token_docs, res, function() { checkTimeout(guid, res, authorized, "POST", function() { if (token != "" && !authorized) { //updateTimeout(guid, res, authorized, "POST", function(err, docs) { res.status(400).send(error.wrong_token); return; //}); } else { db.collection(channel_name).aggregate( [ { $match: { type: { $ne: "suggested" } } }, { $project: projects.project_object }, { $sort: { now_playing: -1, votes: -1, added: 1, title: 1 } } ], function(err, list) { //db.collection(channel_name).find({views: {$exists: false}}, projects.toShowChannel, function(err, list) { if (list.length > 0) { db.collection(channel_name + "_settings").find( { id: "config" }, function(err, conf) { if (conf.length == 0) { res.status(404).send(error.not_found.list); return; } else if ( conf[0].userpass != userpass && conf[0].userpass != "" && conf[0].userpass != undefined ) { res.status(403).send(error.not_authenticated); return; } if (authorized) { incrementToken(token); } //updateTimeout(guid, res, authorized, "POST", function(err, docs) { var to_return = error.no_error; to_return.results = list; res.status(200).send(to_return); return; //}); } ); } else { res.status(404).send(error.not_found.list); return; } } ); } }); }); }); }); }); function incrementToken(token) { token_db .collection("api_token") .update({ token: token }, { $inc: { usage: 1 } }, function(err, doc) {}); } router.route("/api/color").post(function(req, res) { try { var origin = req .get("origin") .replace("https://", "") .replace("http://", ""); var allowed = [ "client.localhost", "localhost", "zoff.me", "client.zoff.me", "zoff.no", "client.zoff.no" ]; if (allowed.indexOf(origin) < 0) { throw "Wrong origin"; } } catch (e) { res.sendStatus(403); return; } if (!req.body.hasOwnProperty("id") || typeof req.body.id != "string") { res.sendStatus(400); return; } List.sendColor(false, undefined, req.body.id, true, res); }); router.route("/api/imageblob").post(function(req, res) { var Jimp = require("jimp"); try { var origin = req .get("origin") .replace("https://", "") .replace("http://", ""); var allowed = [ "client.localhost", "localhost", "zoff.me", "client.zoff.me", "zoff.no", "client.zoff.no" ]; if (allowed.indexOf(origin) < 0) { throw "Wrong origin"; } } catch (e) { res.sendStatus(403); return; } Jimp.read( "https://img.youtube.com/vi/" + req.body.id + "/mqdefault.jpg", function(err, image) { if (err) { console.log(err); res.sendStatus(404); return; } image .blur(50) .write( path.join( pathThumbnails, "/public/assets/images/thumbnails/" + req.body.id + ".jpg" ), function(e, r) { res.status(200).send(req.body.id + ".jpg"); return; } ); } ); }); try { var nodemailer = require("nodemailer"); var mailconfig = require(path.join(__dirname, "../../config/mailconfig.js")); var recaptcha_config = require(path.join( __dirname, "../../config/recaptcha.js" )); var Recaptcha = require("express-recaptcha"); var RECAPTCHA_SITE_KEY = recaptcha_config.site; var RECAPTCHA_SECRET_KEY = recaptcha_config.key; var recaptcha = new Recaptcha(RECAPTCHA_SITE_KEY, RECAPTCHA_SECRET_KEY); router .route("/api/apply") .post(recaptcha.middleware.verify, function(req, res) { if (req.body.email == "" || req.body.email == undefined) { res.send("failed"); return; } if (req.recaptcha.error == null) { var origin = "*"; if (req.body.origin != undefined && req.body.origin != "") origin = req.body.origin; var name = req.body.email; var id = crypto .createHash("sha256") .update(uniqid()) .digest("base64"); var uniqid_link = crypto .createHash("sha256") .update(uniqid()) .digest("hex"); token_db .collection("api_token") .find({ name: name }, function(err, results_find) { var token = ""; if (results_find.length > 0) { token = results_find[0].token; } token_db .collection("api_links") .find({ token: token }, function(e, d) { if ( results_find.length == 0 || (d.length == 0 && results_find.length > 0 && !results_find[0].active) ) { token_db.collection("api_token").insert( { name: name, origin: origin, token: id, usage: 0, active: false, limit: 20 }, function(err, docs) { createApiLink(req, res, uniqid_link, id, name); } ); } else { createApiLink(req, res, uniqid_link, token, name); } }); }); } else { res.send("failed"); return; } }); function createApiLink(req, res, uniqid_link, id, name) { token_db .collection("api_links") .insert({ id: uniqid_link, token: id, createdAt: new Date() }, function( err, docs ) { let transporter = nodemailer.createTransport(mailconfig); transporter.verify(function(error, success) { if (error) { token_db .collection("api_links") .remove({ id: uniqid_link }, function(e, d) { res.send("failed"); return; }); } else { var subject = "ZOFF: API-key"; var message = "Hello,

Thanks for signing up for the API, here is your key: https://zoff.me/api/apply/" + uniqid_link + "

This link will expire in 1 day, so please write it down.

zoff-logo"; var msg = { from: mailconfig.from, to: name, subject: subject, text: message, html: message }; transporter.sendMail(msg, (error, info) => { if (error) { res.send("failed"); transporter.close(); return; } res.status(200).send("success"); transporter.close(); return; }); } }); }); } router .route("/api/mail") .post(recaptcha.middleware.verify, function(req, res) { if (req.recaptcha.error == null) { let transporter = nodemailer.createTransport(mailconfig); transporter.verify(function(error, success) { if (error) { res.sendStatus(500); return; } else { var subject = "ZOFF: Contact form webpage"; if (req.body.error_report) { subject = "ZOFF: Error report"; } var from = req.body.from; var message = req.body.message; var msg = { from: mailconfig.from, to: mailconfig.to, subject: subject, text: message, html: message, replyTo: from }; transporter.sendMail(msg, (error, info) => { if (error) { res.status(500).send("failed"); transporter.close(); return; } res.status(200).send("success"); transporter.close(); }); } }); } else { res.status(500).send("failed"); return; } }); } catch (e) { console.log("Mail is not configured and wont work"); console.log( "Seems you forgot to create a mailconfig.js in /server/config/. Have a look at the mailconfig.example.js." ); router.route("/api/mail").post(function(req, res) { console.log( "Someone tried to send a mail, but the mailsystem hasn't been enabled.." ); res.status(500).send("failed"); return; }); } function updateTimeout(guid, res, authorized, type, callback) { if (authorized) { callback(null, null); return; } db.collection("timeout_api").update( { type: type, guid: guid }, { $set: { createdAt: new Date(), type: type, guid: guid } }, { upsert: true }, function(err, docs) { callback(err, docs); } ); } function checkTimeout(guid, res, authorized, type, callback) { if (authorized) { callback(); return; } db.collection("timeout_api").find( { type: type, guid: guid }, function(err, docs) { if (docs.length > 0) { var date = new Date(docs[0].createdAt); date.setSeconds(date.getSeconds() + 1); var now = new Date(); var retry_in = (date.getTime() - now.getTime()) / 1000; if (retry_in > 0) { res.header({ "Retry-After": retry_in }); var thisErrorString = JSON.stringify(error.tooMany); var thisError = JSON.parse(thisErrorString); thisError.error += " To get an API-key, visit https://zoff.me/api/apply."; res.status(429).send(thisError); return; } } var now_date = new Date(); db.collection("timeout_api").update( { type: type, guid: guid }, { $set: { createdAt: now_date, type: type, guid: guid } }, { upsert: true }, function(err, docs) { callback(); return; } ); } ); } function cleanChannelName(channel_name) { var coll = Functions.removeEmojis(channel_name).toLowerCase(); //coll = coll.replace("_", ""); //coll = encodeURIComponent(coll).replace(/\W/g, ''); coll = Functions.encodeChannelName(channel_name); coll = filter.clean(coll); return coll; } function validateLogin(adminpass, userpass, channel_name, type, res, callback) { db.collection(channel_name + "_settings").find({ id: "config" }, function( err, conf ) { var exists = false; if ( conf.length > 0 && (conf[0].userpass == undefined || conf[0].userpass == "" || conf[0].userpass == userpass) ) { exists = true; } else if (conf.length > 0 && type == "config") { res.status(404).send(error.not_found.list); return; } else if (conf.length == 0) { res.status(404).send(error.not_found.list); return; } if ( type == "fetch_song" || (type == "add" && ((conf[0].addsongs && (conf[0].adminpass == "" || conf[0].adminpass == undefined || conf[0].adminpass == adminpass)) || !conf[0].addsongs)) || (type == "delete" && (conf[0].adminpass == "" || conf[0].adminpass == undefined || conf[0].adminpass == adminpass)) || (type == "vote" && ((conf[0].vote && (conf[0].adminpass == "" || conf[0].adminpass == undefined || conf[0].adminpass == adminpass)) || !conf[0].vote)) || (type == "config" && (conf[0].adminpass == "" || conf[0].adminpass == undefined || conf[0].adminpass == adminpass)) ) { callback(exists, conf, true); } else if (type == "add") { callback(exists, conf, false); } else { res.status(403).send(error.not_authenticated); return; } }); } function postEnd( channel_name, configs, new_song, guid, res, authenticated, authorized ) { if (configs != undefined) { io.to(channel_name).emit("conf", configs); } List.getNextSong(channel_name, undefined, function() { //updateTimeout(guid, res, authorized, "POST", function(err, docs) { var to_return = error.no_error; if (!authenticated) { to_return = error.not_authenticated; to_return.success = true; } to_return.results = [new_song]; res.status(authenticated ? 200 : 403).send(to_return); return; //}); }); } module.exports = _exports;