mirror of https://github.com/calzoneman/sync.git
Require user permission to run channel js
This commit is contained in:
parent
3661ab1fd9
commit
e87ddb473b
|
@ -1,6 +1,7 @@
|
||||||
var ChannelModule = require("./module");
|
var ChannelModule = require("./module");
|
||||||
var Config = require("../config");
|
var Config = require("../config");
|
||||||
var Utilities = require("../utilities");
|
var Utilities = require("../utilities");
|
||||||
|
var url = require("url");
|
||||||
|
|
||||||
function OptionsModule(channel) {
|
function OptionsModule(channel) {
|
||||||
ChannelModule.apply(this, arguments);
|
ChannelModule.apply(this, arguments);
|
||||||
|
@ -143,11 +144,48 @@ OptionsModule.prototype.handleSetOptions = function (user, data) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if ("externalcss" in data && user.account.effectiveRank >= 3) {
|
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) {
|
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) {
|
if ("chat_antiflood" in data) {
|
||||||
|
|
|
@ -270,16 +270,16 @@ PlaylistModule.prototype.sendChangeMedia = function (users) {
|
||||||
if (users === this.channel.users) {
|
if (users === this.channel.users) {
|
||||||
this.channel.broadcastAll("setCurrent", uid);
|
this.channel.broadcastAll("setCurrent", uid);
|
||||||
this.channel.broadcastAll("changeMedia", update);
|
this.channel.broadcastAll("changeMedia", update);
|
||||||
|
|
||||||
|
var m = this.current.media;
|
||||||
|
this.channel.logger.log("[playlist] Now playing: " + m.title +
|
||||||
|
" (" + m.type + ":" + m.id + ")");
|
||||||
} else {
|
} else {
|
||||||
users.forEach(function (u) {
|
users.forEach(function (u) {
|
||||||
u.socket.emit("setCurrent", uid);
|
u.socket.emit("setCurrent", uid);
|
||||||
u.socket.emit("changeMedia", update);
|
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) {
|
PlaylistModule.prototype.sendMediaUpdate = function (users) {
|
||||||
|
|
|
@ -158,6 +158,7 @@ html(lang="en")
|
||||||
li: a(href="#us-general", data-toggle="tab") General
|
li: a(href="#us-general", data-toggle="tab") General
|
||||||
li: a(href="#us-playback", data-toggle="tab") Playback
|
li: a(href="#us-playback", data-toggle="tab") Playback
|
||||||
li: a(href="#us-chat", data-toggle="tab") Chat
|
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
|
li: a(href="#us-mod", data-toggle="tab", style="") Moderator
|
||||||
.modal-body
|
.modal-body
|
||||||
.tab-content
|
.tab-content
|
||||||
|
@ -165,6 +166,7 @@ html(lang="en")
|
||||||
mixin us-general()
|
mixin us-general()
|
||||||
mixin us-playback()
|
mixin us-playback()
|
||||||
mixin us-chat()
|
mixin us-chat()
|
||||||
|
mixin us-scripts()
|
||||||
mixin us-mod()
|
mixin us-mod()
|
||||||
.modal-footer
|
.modal-footer
|
||||||
button.btn.btn-primary(type="button", data-dismiss="modal", onclick="javascript:saveUserOptions()") Save
|
button.btn.btn-primary(type="button", data-dismiss="modal", onclick="javascript:saveUserOptions()") Save
|
||||||
|
|
|
@ -52,6 +52,17 @@ mixin us-general
|
||||||
p#us-conninfo.text-info <strong>Connection Information: </strong>
|
p#us-conninfo.text-info <strong>Connection Information: </strong>
|
||||||
.clear
|
.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
|
mixin us-playback
|
||||||
#us-playback.tab-pane
|
#us-playback.tab-pane
|
||||||
h4 Playback Preferences
|
h4 Playback Preferences
|
||||||
|
|
|
@ -562,3 +562,15 @@ body.chatOnly .pm-panel, body.chatOnly .pm-panel-placeholder {
|
||||||
.chat-shadow {
|
.chat-shadow {
|
||||||
text-decoration: line-through;
|
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;
|
||||||
|
}
|
||||||
|
|
|
@ -250,6 +250,7 @@ Callbacks = {
|
||||||
document.title = opts.pagetitle;
|
document.title = opts.pagetitle;
|
||||||
PAGETITLE = opts.pagetitle;
|
PAGETITLE = opts.pagetitle;
|
||||||
$("#chanexternalcss").remove();
|
$("#chanexternalcss").remove();
|
||||||
|
|
||||||
if(opts.externalcss.trim() != "" && !USEROPTS.ignore_channelcss) {
|
if(opts.externalcss.trim() != "" && !USEROPTS.ignore_channelcss) {
|
||||||
$("<link/>")
|
$("<link/>")
|
||||||
.attr("rel", "stylesheet")
|
.attr("rel", "stylesheet")
|
||||||
|
@ -257,10 +258,14 @@ Callbacks = {
|
||||||
.attr("id", "chanexternalcss")
|
.attr("id", "chanexternalcss")
|
||||||
.appendTo($("head"));
|
.appendTo($("head"));
|
||||||
}
|
}
|
||||||
if(opts.externaljs.trim() != "" && !USEROPTS.ignore_channeljs) {
|
|
||||||
if(opts.externaljs != CHANNEL.opts.externaljs) {
|
if(opts.externaljs.trim() != "" && !USEROPTS.ignore_channeljs &&
|
||||||
$.getScript(opts.externaljs);
|
opts.externaljs !== CHANNEL.opts.externaljs) {
|
||||||
}
|
checkScriptAccess(opts.externaljs, "external", function (pref) {
|
||||||
|
if (pref === "ALLOW") {
|
||||||
|
$.getScript(opts.externaljs);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
CHANNEL.opts = opts;
|
CHANNEL.opts = opts;
|
||||||
|
@ -294,10 +299,26 @@ Callbacks = {
|
||||||
$("#jstext").val(data.js);
|
$("#jstext").val(data.js);
|
||||||
|
|
||||||
if(data.js && !USEROPTS.ignore_channeljs) {
|
if(data.js && !USEROPTS.ignore_channeljs) {
|
||||||
$("<script/>").attr("type", "text/javascript")
|
var src = data.js
|
||||||
.attr("id", "chanjs")
|
.replace(/&/g, "&")
|
||||||
.text(data.js)
|
.replace(/</g, "<")
|
||||||
.appendTo($("body"));
|
.replace(/>/g, ">")
|
||||||
|
.replace(/\n/g, "<br>")
|
||||||
|
.replace(/\t/g, " ")
|
||||||
|
.replace(/ /g, " ");
|
||||||
|
src = encodeURIComponent(src);
|
||||||
|
|
||||||
|
var viewsource = "data:text/html, <body style='font: 9pt monospace;" +
|
||||||
|
"max-width:60rem;margin:0 auto;padding:4rem;'>" +
|
||||||
|
src + "</body>";
|
||||||
|
checkScriptAccess(viewsource, "embedded", function (pref) {
|
||||||
|
if (pref === "ALLOW") {
|
||||||
|
$("<script/>").attr("type", "text/javascript")
|
||||||
|
.attr("id", "chanjs")
|
||||||
|
.text(data.js)
|
||||||
|
.appendTo($("body"));
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -76,10 +76,15 @@ var FILTER_TO = 0;
|
||||||
var NO_STORAGE = typeof localStorage == "undefined" || localStorage === null;
|
var NO_STORAGE = typeof localStorage == "undefined" || localStorage === null;
|
||||||
|
|
||||||
function getOpt(k) {
|
function getOpt(k) {
|
||||||
return NO_STORAGE ? readCookie(k) : localStorage.getItem(k);
|
var v = NO_STORAGE ? readCookie(k) : localStorage.getItem(k);
|
||||||
|
try {
|
||||||
|
v = JSON.parse(v);
|
||||||
|
} catch (e) { }
|
||||||
|
return v;
|
||||||
}
|
}
|
||||||
|
|
||||||
function setOpt(k, v) {
|
function setOpt(k, v) {
|
||||||
|
v = JSON.stringify(v);
|
||||||
NO_STORAGE ? createCookie(k, v, 1000) : localStorage.setItem(k, v);
|
NO_STORAGE ? createCookie(k, v, 1000) : localStorage.setItem(k, v);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -91,9 +96,9 @@ function getOrDefault(k, def) {
|
||||||
return true;
|
return true;
|
||||||
if(v === "false")
|
if(v === "false")
|
||||||
return false;
|
return false;
|
||||||
if(v.match(/^[0-9]+$/))
|
if(v.match && v.match(/^[0-9]+$/))
|
||||||
return parseInt(v);
|
return parseInt(v);
|
||||||
if(v.match(/^[0-9\.]+$/))
|
if(v.match && v.match(/^[0-9\.]+$/))
|
||||||
return parseFloat(v);
|
return parseFloat(v);
|
||||||
return v;
|
return v;
|
||||||
}
|
}
|
||||||
|
@ -163,6 +168,8 @@ var VOLUME = parseFloat(getOrDefault("volume", 1));
|
||||||
var NO_WEBSOCKETS = USEROPTS.altsocket;
|
var NO_WEBSOCKETS = USEROPTS.altsocket;
|
||||||
var NO_VIMEO = Boolean(location.host.match("cytu.be"));
|
var NO_VIMEO = Boolean(location.host.match("cytu.be"));
|
||||||
|
|
||||||
|
var JSPREF = getOpt("channel_js_pref") || {};
|
||||||
|
|
||||||
var Rank = {
|
var Rank = {
|
||||||
Guest: 0,
|
Guest: 0,
|
||||||
Member: 1,
|
Member: 1,
|
||||||
|
|
139
www/js/util.js
139
www/js/util.js
|
@ -589,17 +589,6 @@ function showUserOptions() {
|
||||||
$("#us-layout").val(USEROPTS.layout);
|
$("#us-layout").val(USEROPTS.layout);
|
||||||
$("#us-no-channelcss").prop("checked", USEROPTS.ignore_channelcss);
|
$("#us-no-channelcss").prop("checked", USEROPTS.ignore_channelcss);
|
||||||
$("#us-no-channeljs").prop("checked", USEROPTS.ignore_channeljs);
|
$("#us-no-channeljs").prop("checked", USEROPTS.ignore_channeljs);
|
||||||
/*
|
|
||||||
if (!ALLOW_SSL) {
|
|
||||||
$("#us-ssl").prop("checked", false);
|
|
||||||
$("#us-ssl").attr("disabled", true);
|
|
||||||
$("#us-ssl").attr("title", "This server has not enabled SSL");
|
|
||||||
} else {
|
|
||||||
$("#us-ssl").prop("checked", USEROPTS.secure_connection);
|
|
||||||
$("#us-ssl").attr("disabled", false);
|
|
||||||
$("#us-ssl").attr("title", "");
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
var conninfo = "<strong>Connection Information: </strong>" +
|
var conninfo = "<strong>Connection Information: </strong>" +
|
||||||
"Connected to <code>" + IO_URL + "</code> (";
|
"Connected to <code>" + IO_URL + "</code> (";
|
||||||
if (IO_V6) {
|
if (IO_V6) {
|
||||||
|
@ -638,6 +627,8 @@ function showUserOptions() {
|
||||||
$("#us-joinmessage").prop("checked", USEROPTS.joinmessage);
|
$("#us-joinmessage").prop("checked", USEROPTS.joinmessage);
|
||||||
$("#us-shadowchat").prop("checked", USEROPTS.show_shadowchat);
|
$("#us-shadowchat").prop("checked", USEROPTS.show_shadowchat);
|
||||||
|
|
||||||
|
formatScriptAccessPrefs();
|
||||||
|
|
||||||
$("a[href='#us-general']").click();
|
$("a[href='#us-general']").click();
|
||||||
$("#useroptions").modal();
|
$("#useroptions").modal();
|
||||||
}
|
}
|
||||||
|
@ -1845,14 +1836,13 @@ function chatDialog(div) {
|
||||||
"z-index": "auto",
|
"z-index": "auto",
|
||||||
position: "absolute"
|
position: "absolute"
|
||||||
})
|
})
|
||||||
.appendTo($("body"));
|
.appendTo($("#chatwrap"));
|
||||||
|
|
||||||
div.appendTo(parent);
|
div.appendTo(parent);
|
||||||
var cw = $("#chatwrap").width();
|
var cw = $("#chatwrap").width();
|
||||||
var ch = $("#chatwrap").height();
|
var ch = $("#chatwrap").height();
|
||||||
var cp = $("#chatwrap").offset();
|
var x = cw/2 - parent.width()/2;
|
||||||
var x = cp.left + cw/2 - parent.width()/2;
|
var y = ch/2 - parent.height()/2;
|
||||||
var y = cp.top + ch/2 - parent.height()/2;
|
|
||||||
parent.css("left", x + "px");
|
parent.css("left", x + "px");
|
||||||
parent.css("top", y + "px");
|
parent.css("top", y + "px");
|
||||||
return parent;
|
return parent;
|
||||||
|
@ -2563,3 +2553,122 @@ function fallbackRaw(data) {
|
||||||
|
|
||||||
handleMediaUpdate(data);
|
handleMediaUpdate(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function checkScriptAccess(source, type, cb) {
|
||||||
|
var pref = JSPREF[CHANNEL.name.toLowerCase() + "_" + type];
|
||||||
|
if (pref === "ALLOW") {
|
||||||
|
return cb("ALLOW");
|
||||||
|
} else if (pref !== "DENY") {
|
||||||
|
var div = $("#chanjs-allow-prompt");
|
||||||
|
if (div.length > 0) {
|
||||||
|
setTimeout(function () {
|
||||||
|
checkScriptAccess(source, type, cb);
|
||||||
|
}, 500);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
div = $("<div/>").attr("id", "chanjs-allow-prompt");
|
||||||
|
var close = $("<button/>").addClass("close pull-right")
|
||||||
|
.html("×")
|
||||||
|
.appendTo(div);
|
||||||
|
var form = $("<form/>")
|
||||||
|
.attr("id", "chanjs-allow-prompt")
|
||||||
|
.attr("style", "text-align: center")
|
||||||
|
.appendTo(div);
|
||||||
|
form.append("<span>This channel is requesting permission to run a 3rd-party " +
|
||||||
|
"script for additional features. <strong>Only " +
|
||||||
|
"run scripts from channels you trust.</strong></span><br>");
|
||||||
|
$("<a/>").attr("href", source)
|
||||||
|
.attr("target", "_blank")
|
||||||
|
.text(type === "embedded" ? "view embedded script" : source)
|
||||||
|
.appendTo(form);
|
||||||
|
form.append("<div id='chanjs-allow-prompt-buttons'>" +
|
||||||
|
"<button id='chanjs-allow' class='btn btn-xs btn-danger'>Allow</button>" +
|
||||||
|
"<button id='chanjs-deny' class='btn btn-xs btn-danger'>Deny</button>" +
|
||||||
|
"</div>");
|
||||||
|
form.append("<div class='checkbox'><label><input type='checkbox' " +
|
||||||
|
"id='chanjs-save-pref'/>Remember my choice for this channel" +
|
||||||
|
"</label></div>");
|
||||||
|
var dialog = chatDialog(div);
|
||||||
|
|
||||||
|
close.click(function () {
|
||||||
|
dialog.remove();
|
||||||
|
/* Implicit denial of script access */
|
||||||
|
cb("DENY");
|
||||||
|
});
|
||||||
|
|
||||||
|
$("#chanjs-allow").click(function () {
|
||||||
|
var save = $("#chanjs-save-pref").is(":checked");
|
||||||
|
dialog.remove();
|
||||||
|
if (save) {
|
||||||
|
JSPREF[CHANNEL.name.toLowerCase() + "_" + type] = "ALLOW";
|
||||||
|
setOpt("channel_js_pref", JSPREF);
|
||||||
|
}
|
||||||
|
cb("ALLOW");
|
||||||
|
});
|
||||||
|
|
||||||
|
$("#chanjs-deny").click(function () {
|
||||||
|
var save = $("#chanjs-save-pref").is(":checked");
|
||||||
|
dialog.remove();
|
||||||
|
if (save) {
|
||||||
|
JSPREF[CHANNEL.name.toLowerCase() + "_" + type] = "DENY";
|
||||||
|
setOpt("channel_js_pref", JSPREF);
|
||||||
|
}
|
||||||
|
cb("DENY");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatScriptAccessPrefs() {
|
||||||
|
var tbl = $("#us-scriptcontrol table");
|
||||||
|
tbl.find("tbody").remove();
|
||||||
|
|
||||||
|
var channels = Object.keys(JSPREF).sort();
|
||||||
|
channels.forEach(function (channel) {
|
||||||
|
var parts = channel.split("_");
|
||||||
|
if (!parts[1].match(/^(external|embedded)$/)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var pref = JSPREF[channel];
|
||||||
|
var tr = $("<tr/>").appendTo(tbl);
|
||||||
|
$("<td/>").text(parts[0]).appendTo(tr);
|
||||||
|
$("<td/>").text(parts[1]).appendTo(tr);
|
||||||
|
|
||||||
|
var pref_td = $("<td/>").appendTo(tr);
|
||||||
|
var allow_label = $("<label/>").addClass("radio-inline")
|
||||||
|
.text("Allow").appendTo(pref_td);
|
||||||
|
var allow = $("<input/>").attr("type", "radio")
|
||||||
|
.prop("checked", pref === "ALLOW").
|
||||||
|
prependTo(allow_label);
|
||||||
|
allow.change(function () {
|
||||||
|
if (allow.is(":checked")) {
|
||||||
|
JSPREF[channel] = "ALLOW";
|
||||||
|
setOpt("channel_js_pref", JSPREF);
|
||||||
|
deny.prop("checked", false);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
var deny_label = $("<label/>").addClass("radio-inline")
|
||||||
|
.text("Deny").appendTo(pref_td);
|
||||||
|
var deny = $("<input/>").attr("type", "radio")
|
||||||
|
.prop("checked", pref === "DENY").
|
||||||
|
prependTo(deny_label);
|
||||||
|
deny.change(function () {
|
||||||
|
if (deny.is(":checked")) {
|
||||||
|
JSPREF[channel] = "DENY";
|
||||||
|
setOpt("channel_js_pref", JSPREF);
|
||||||
|
allow.prop("checked", false);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
var clearpref = $("<button/>").addClass("btn btn-sm btn-danger")
|
||||||
|
.text("Clear Preference")
|
||||||
|
.appendTo($("<td/>").appendTo(tr))
|
||||||
|
.click(function () {
|
||||||
|
delete JSPREF[channel];
|
||||||
|
setOpt("channel_js_pref", JSPREF);
|
||||||
|
tr.remove();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue