diff --git a/lib/channel/opts.js b/lib/channel/opts.js index e79e5e1a..f3a0dc49 100644 --- a/lib/channel/opts.js +++ b/lib/channel/opts.js @@ -1,6 +1,7 @@ var ChannelModule = require("./module"); var Config = require("../config"); var Utilities = require("../utilities"); +var url = require("url"); function OptionsModule(channel) { ChannelModule.apply(this, arguments); @@ -143,11 +144,48 @@ OptionsModule.prototype.handleSetOptions = function (user, data) { } if ("externalcss" in data && user.account.effectiveRank >= 3) { - this.opts.externalcss = (""+data.externalcss).substring(0, 255); + var link = (""+data.externalcss).substring(0, 255); + try { + var data = url.parse(link); + if (!data.protocol || !data.protocol.match(/^(https?|ftp):$/)) { + throw "Unacceptable protocol " + data.protocol; + } else if (!data.host) { + throw "URL is missing host"; + } else { + link = data.href; + } + } catch (e) { + user.socket.emit("errorMsg", { + msg: "Invalid URL for external CSS: " + e, + alert: true + }); + return; + } + + this.opts.externalcss = link; } if ("externaljs" in data && user.account.effectiveRank >= 3) { - this.opts.externaljs = (""+data.externaljs).substring(0, 255); + var link = (""+data.externaljs).substring(0, 255); + + try { + var data = url.parse(link); + if (!data.protocol || !data.protocol.match(/^(https?|ftp)$/)) { + throw "Unacceptable protocol " + data.protocol; + } else if (!data.host) { + throw "URL is missing host"; + } else { + link = data.href; + } + } catch (e) { + user.socket.emit("errorMsg", { + msg: "Invalid URL for external JS: " + e, + alert: true + }); + return; + } + + this.opts.externaljs = link; } if ("chat_antiflood" in data) { diff --git a/lib/channel/playlist.js b/lib/channel/playlist.js index efb3b38d..bf4a17d7 100644 --- a/lib/channel/playlist.js +++ b/lib/channel/playlist.js @@ -270,16 +270,16 @@ PlaylistModule.prototype.sendChangeMedia = function (users) { if (users === this.channel.users) { this.channel.broadcastAll("setCurrent", uid); this.channel.broadcastAll("changeMedia", update); + + var m = this.current.media; + this.channel.logger.log("[playlist] Now playing: " + m.title + + " (" + m.type + ":" + m.id + ")"); } else { users.forEach(function (u) { u.socket.emit("setCurrent", uid); u.socket.emit("changeMedia", update); }); } - - var m = this.current.media; - this.channel.logger.log("[playlist] Now playing: " + m.title + - " (" + m.type + ":" + m.id + ")"); }; PlaylistModule.prototype.sendMediaUpdate = function (users) { diff --git a/templates/channel.jade b/templates/channel.jade index 560fe69d..baa6a826 100644 --- a/templates/channel.jade +++ b/templates/channel.jade @@ -158,6 +158,7 @@ html(lang="en") li: a(href="#us-general", data-toggle="tab") General li: a(href="#us-playback", data-toggle="tab") Playback li: a(href="#us-chat", data-toggle="tab") Chat + li: a(href="#us-scriptcontrol", data-toggle="tab") Script Access li: a(href="#us-mod", data-toggle="tab", style="") Moderator .modal-body .tab-content @@ -165,6 +166,7 @@ html(lang="en") mixin us-general() mixin us-playback() mixin us-chat() + mixin us-scripts() mixin us-mod() .modal-footer button.btn.btn-primary(type="button", data-dismiss="modal", onclick="javascript:saveUserOptions()") Save diff --git a/templates/useroptions.jade b/templates/useroptions.jade index 4fc72004..c91b4e3a 100644 --- a/templates/useroptions.jade +++ b/templates/useroptions.jade @@ -52,6 +52,17 @@ mixin us-general p#us-conninfo.text-info Connection Information: .clear +mixin us-scripts + #us-scriptcontrol.tab-pane + h4 Script Access + table.table + thead + tr + th Channel + th Type + th Preference + th Clear + mixin us-playback #us-playback.tab-pane h4 Playback Preferences diff --git a/www/css/cytube.css b/www/css/cytube.css index c7111481..048bef51 100644 --- a/www/css/cytube.css +++ b/www/css/cytube.css @@ -562,3 +562,15 @@ body.chatOnly .pm-panel, body.chatOnly .pm-panel-placeholder { .chat-shadow { text-decoration: line-through; } + +#chanjs-allow-prompt { + text-align: center; +} + +#chanjs-allow-prompt-buttons { + margin-top: 10px; +} + +#chanjs-allow-prompt-buttons button:first-child { + margin-right: 5px; +} diff --git a/www/js/callbacks.js b/www/js/callbacks.js index 3d296bbe..d18af9f6 100644 --- a/www/js/callbacks.js +++ b/www/js/callbacks.js @@ -250,6 +250,7 @@ Callbacks = { document.title = opts.pagetitle; PAGETITLE = opts.pagetitle; $("#chanexternalcss").remove(); + if(opts.externalcss.trim() != "" && !USEROPTS.ignore_channelcss) { $("") .attr("rel", "stylesheet") @@ -257,10 +258,14 @@ Callbacks = { .attr("id", "chanexternalcss") .appendTo($("head")); } - if(opts.externaljs.trim() != "" && !USEROPTS.ignore_channeljs) { - if(opts.externaljs != CHANNEL.opts.externaljs) { - $.getScript(opts.externaljs); - } + + if(opts.externaljs.trim() != "" && !USEROPTS.ignore_channeljs && + opts.externaljs !== CHANNEL.opts.externaljs) { + checkScriptAccess(opts.externaljs, "external", function (pref) { + if (pref === "ALLOW") { + $.getScript(opts.externaljs); + } + }); } CHANNEL.opts = opts; @@ -294,10 +299,26 @@ Callbacks = { $("#jstext").val(data.js); if(data.js && !USEROPTS.ignore_channeljs) { - $("